d61010f0e06c1850731094f147af2f6c267642e6
[project/luci.git] / docs / jsapi / uci.js.html
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="utf-8">
5 <title>JSDoc: Source: uci.js</title>
6
7 <script src="scripts/prettify/prettify.js"> </script>
8 <script src="scripts/prettify/lang-css.js"> </script>
9 <!--[if lt IE 9]>
10 <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
11 <![endif]-->
12 <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
13 <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
14 </head>
15
16 <body>
17
18 <div id="main">
19
20 <h1 class="page-title">Source: uci.js</h1>
21
22
23
24
25
26
27 <section>
28 <article>
29 <pre class="prettyprint source linenums"><code>'use strict';
30 'require rpc';
31
32 /**
33 * @class uci
34 * @memberof LuCI
35 * @hideconstructor
36 * @classdesc
37 *
38 * The `LuCI.uci` class utilizes {@link LuCI.rpc} to declare low level
39 * remote UCI `ubus` procedures and implements a local caching and data
40 * manipulation layer on top to allow for synchroneous operations on
41 * UCI configuration data.
42 */
43 return L.Class.extend(/** @lends LuCI.uci.prototype */ {
44 __init__: function() {
45 this.state = {
46 newidx: 0,
47 values: { },
48 creates: { },
49 changes: { },
50 deletes: { },
51 reorder: { }
52 };
53
54 this.loaded = {};
55 },
56
57 callLoad: rpc.declare({
58 object: 'uci',
59 method: 'get',
60 params: [ 'config' ],
61 expect: { values: { } }
62 }),
63
64
65 callOrder: rpc.declare({
66 object: 'uci',
67 method: 'order',
68 params: [ 'config', 'sections' ]
69 }),
70
71 callAdd: rpc.declare({
72 object: 'uci',
73 method: 'add',
74 params: [ 'config', 'type', 'name', 'values' ],
75 expect: { section: '' }
76 }),
77
78 callSet: rpc.declare({
79 object: 'uci',
80 method: 'set',
81 params: [ 'config', 'section', 'values' ]
82 }),
83
84 callDelete: rpc.declare({
85 object: 'uci',
86 method: 'delete',
87 params: [ 'config', 'section', 'options' ]
88 }),
89
90 callApply: rpc.declare({
91 object: 'uci',
92 method: 'apply',
93 params: [ 'timeout', 'rollback' ]
94 }),
95
96 callConfirm: rpc.declare({
97 object: 'uci',
98 method: 'confirm'
99 }),
100
101
102 /**
103 * Generates a new, unique section ID for the given configuration.
104 *
105 * Note that the generated ID is temporary, it will get replaced by an
106 * identifier in the form `cfgXXXXXX` once the configuration is saved
107 * by the remote `ubus` UCI api.
108 *
109 * @param {string} config
110 * The configuration to generate the new section ID for.
111 *
112 * @returns {string}
113 * A newly generated, unique section ID in the form `newXXXXXX`
114 * where `X` denotes a hexadecimal digit.
115 */
116 createSID: function(conf) {
117 var v = this.state.values,
118 n = this.state.creates,
119 sid;
120
121 do {
122 sid = "new%06x".format(Math.random() * 0xFFFFFF);
123 } while ((n[conf] &amp;&amp; n[conf][sid]) || (v[conf] &amp;&amp; v[conf][sid]));
124
125 return sid;
126 },
127
128 /**
129 * Resolves a given section ID in extended notation to the internal
130 * section ID value.
131 *
132 * @param {string} config
133 * The configuration to resolve the section ID for.
134 *
135 * @param {string} sid
136 * The section ID to resolve. If the ID is in the form `@typename[#]`,
137 * it will get resolved to an internal anonymous ID in the forms
138 * `cfgXXXXXX`/`newXXXXXX` or to the name of a section in case it points
139 * to a named section. When the given ID is not in extended notation,
140 * it will be returned as-is.
141 *
142 * @returns {string|null}
143 * Returns the resolved section ID or the original given ID if it was
144 * not in extended notation. Returns `null` when an extended ID could
145 * not be resolved to existing section ID.
146 */
147 resolveSID: function(conf, sid) {
148 if (typeof(sid) != 'string')
149 return sid;
150
151 var m = /^@([a-zA-Z0-9_-]+)\[(-?[0-9]+)\]$/.exec(sid);
152
153 if (m) {
154 var type = m[1],
155 pos = +m[2],
156 sections = this.sections(conf, type),
157 section = sections[pos >= 0 ? pos : sections.length + pos];
158
159 return section ? section['.name'] : null;
160 }
161
162 return sid;
163 },
164
165 /* private */
166 reorderSections: function() {
167 var v = this.state.values,
168 n = this.state.creates,
169 r = this.state.reorder,
170 tasks = [];
171
172 if (Object.keys(r).length === 0)
173 return Promise.resolve();
174
175 /*
176 gather all created and existing sections, sort them according
177 to their index value and issue an uci order call
178 */
179 for (var c in r) {
180 var o = [ ];
181
182 if (n[c])
183 for (var s in n[c])
184 o.push(n[c][s]);
185
186 for (var s in v[c])
187 o.push(v[c][s]);
188
189 if (o.length > 0) {
190 o.sort(function(a, b) {
191 return (a['.index'] - b['.index']);
192 });
193
194 var sids = [ ];
195
196 for (var i = 0; i &lt; o.length; i++)
197 sids.push(o[i]['.name']);
198
199 tasks.push(this.callOrder(c, sids));
200 }
201 }
202
203 this.state.reorder = { };
204 return Promise.all(tasks);
205 },
206
207 /* private */
208 loadPackage: function(packageName) {
209 if (this.loaded[packageName] == null)
210 return (this.loaded[packageName] = this.callLoad(packageName));
211
212 return Promise.resolve(this.loaded[packageName]);
213 },
214
215 /**
216 * Loads the given UCI configurations from the remote `ubus` api.
217 *
218 * Loaded configurations are cached and only loaded once. Subsequent
219 * load operations of the same configurations will return the cached
220 * data.
221 *
222 * To force reloading a configuration, it has to be unloaded with
223 * {@link LuCI.uci#unload uci.unload()} first.
224 *
225 * @param {string|string[]} config
226 * The name of the configuration or an array of configuration
227 * names to load.
228 *
229 * @returns {Promise&lt;string[]>}
230 * Returns a promise resolving to the names of the configurations
231 * that have been successfully loaded.
232 */
233 load: function(packages) {
234 var self = this,
235 pkgs = [ ],
236 tasks = [];
237
238 if (!Array.isArray(packages))
239 packages = [ packages ];
240
241 for (var i = 0; i &lt; packages.length; i++)
242 if (!self.state.values[packages[i]]) {
243 pkgs.push(packages[i]);
244 tasks.push(self.loadPackage(packages[i]));
245 }
246
247 return Promise.all(tasks).then(function(responses) {
248 for (var i = 0; i &lt; responses.length; i++)
249 self.state.values[pkgs[i]] = responses[i];
250
251 if (responses.length)
252 document.dispatchEvent(new CustomEvent('uci-loaded'));
253
254 return pkgs;
255 });
256 },
257
258 /**
259 * Unloads the given UCI configurations from the local cache.
260 *
261 * @param {string|string[]} config
262 * The name of the configuration or an array of configuration
263 * names to unload.
264 */
265 unload: function(packages) {
266 if (!Array.isArray(packages))
267 packages = [ packages ];
268
269 for (var i = 0; i &lt; packages.length; i++) {
270 delete this.state.values[packages[i]];
271 delete this.state.creates[packages[i]];
272 delete this.state.changes[packages[i]];
273 delete this.state.deletes[packages[i]];
274
275 delete this.loaded[packages[i]];
276 }
277 },
278
279 /**
280 * Adds a new section of the given type to the given configuration,
281 * optionally named according to the given name.
282 *
283 * @param {string} config
284 * The name of the configuration to add the section to.
285 *
286 * @param {string} type
287 * The type of the section to add.
288 *
289 * @param {string} [name]
290 * The name of the section to add. If the name is omitted, an anonymous
291 * section will be added instead.
292 *
293 * @returns {string}
294 * Returns the section ID of the newly added section which is equivalent
295 * to the given name for non-anonymous sections.
296 */
297 add: function(conf, type, name) {
298 var n = this.state.creates,
299 sid = name || this.createSID(conf);
300
301 if (!n[conf])
302 n[conf] = { };
303
304 n[conf][sid] = {
305 '.type': type,
306 '.name': sid,
307 '.create': name,
308 '.anonymous': !name,
309 '.index': 1000 + this.state.newidx++
310 };
311
312 return sid;
313 },
314
315 /**
316 * Removes the section with the given ID from the given configuration.
317 *
318 * @param {string} config
319 * The name of the configuration to remove the section from.
320 *
321 * @param {string} sid
322 * The ID of the section to remove.
323 */
324 remove: function(conf, sid) {
325 var n = this.state.creates,
326 c = this.state.changes,
327 d = this.state.deletes;
328
329 /* requested deletion of a just created section */
330 if (n[conf] &amp;&amp; n[conf][sid]) {
331 delete n[conf][sid];
332 }
333 else {
334 if (c[conf])
335 delete c[conf][sid];
336
337 if (!d[conf])
338 d[conf] = { };
339
340 d[conf][sid] = true;
341 }
342 },
343
344 /**
345 * A section object represents the options and their corresponding values
346 * enclosed within a configuration section, as well as some additional
347 * meta data such as sort indexes and internal ID.
348 *
349 * Any internal metadata fields are prefixed with a dot which is isn't
350 * an allowed character for normal option names.
351 *
352 * @typedef {Object&lt;string, boolean|number|string|string[]>} SectionObject
353 * @memberof LuCI.uci
354 *
355 * @property {boolean} .anonymous
356 * The `.anonymous` property specifies whether the configuration is
357 * anonymous (`true`) or named (`false`).
358 *
359 * @property {number} .index
360 * The `.index` property specifes the sort order of the section.
361 *
362 * @property {string} .name
363 * The `.name` property holds the name of the section object. It may be
364 * either an anonymous ID in the form `cfgXXXXXX` or `newXXXXXX` with `X`
365 * being a hexadecimal digit or a string holding the name of the section.
366 *
367 * @property {string} .type
368 * The `.type` property contains the type of the corresponding uci
369 * section.
370 *
371 * @property {string|string[]} *
372 * A section object may contain an arbitrary number of further properties
373 * representing the uci option enclosed in the section.
374 *
375 * All option property names will be in the form `[A-Za-z0-9_]+` and
376 * either contain a string value or an array of strings, in case the
377 * underlying option is an UCI list.
378 */
379
380 /**
381 * The sections callback is invoked for each section found within
382 * the given configuration and receives the section object and its
383 * associated name as arguments.
384 *
385 * @callback LuCI.uci~sectionsFn
386 *
387 * @param {LuCI.uci.SectionObject} section
388 * The section object.
389 *
390 * @param {string} sid
391 * The name or ID of the section.
392 */
393
394 /**
395 * Enumerates the sections of the given configuration, optionally
396 * filtered by type.
397 *
398 * @param {string} config
399 * The name of the configuration to enumerate the sections for.
400 *
401 * @param {string} [type]
402 * Enumerate only sections of the given type. If omitted, enumerate
403 * all sections.
404 *
405 * @param {LuCI.uci~sectionsFn} [cb]
406 * An optional callback to invoke for each enumerated section.
407 *
408 * @returns {Array&lt;LuCI.uci.SectionObject>}
409 * Returns a sorted array of the section objects within the given
410 * configuration, filtered by type of a type has been specified.
411 */
412 sections: function(conf, type, cb) {
413 var sa = [ ],
414 v = this.state.values[conf],
415 n = this.state.creates[conf],
416 c = this.state.changes[conf],
417 d = this.state.deletes[conf];
418
419 if (!v)
420 return sa;
421
422 for (var s in v)
423 if (!d || d[s] !== true)
424 if (!type || v[s]['.type'] == type)
425 sa.push(Object.assign({ }, v[s], c ? c[s] : undefined));
426
427 if (n)
428 for (var s in n)
429 if (!type || n[s]['.type'] == type)
430 sa.push(Object.assign({ }, n[s]));
431
432 sa.sort(function(a, b) {
433 return a['.index'] - b['.index'];
434 });
435
436 for (var i = 0; i &lt; sa.length; i++)
437 sa[i]['.index'] = i;
438
439 if (typeof(cb) == 'function')
440 for (var i = 0; i &lt; sa.length; i++)
441 cb.call(this, sa[i], sa[i]['.name']);
442
443 return sa;
444 },
445
446 /**
447 * Gets the value of the given option within the specified section
448 * of the given configuration or the entire section object if the
449 * option name is omitted.
450 *
451 * @param {string} config
452 * The name of the configuration to read the value from.
453 *
454 * @param {string} sid
455 * The name or ID of the section to read.
456 *
457 * @param {string} [option]
458 * The option name to read the value from. If the option name is
459 * omitted or `null`, the entire section is returned instead.
460 *
461 * @returns {null|string|string[]|LuCI.uci.SectionObject}
462 * - Returns a string containing the option value in case of a
463 * plain UCI option.
464 * - Returns an array of strings containing the option values in
465 * case of `option` pointing to an UCI list.
466 * - Returns a {@link LuCI.uci.SectionObject section object} if
467 * the `option` argument has been omitted or is `null`.
468 * - Returns `null` if the config, section or option has not been
469 * found or if the corresponding configuration is not loaded.
470 */
471 get: function(conf, sid, opt) {
472 var v = this.state.values,
473 n = this.state.creates,
474 c = this.state.changes,
475 d = this.state.deletes;
476
477 sid = this.resolveSID(conf, sid);
478
479 if (sid == null)
480 return null;
481
482 /* requested option in a just created section */
483 if (n[conf] &amp;&amp; n[conf][sid]) {
484 if (!n[conf])
485 return undefined;
486
487 if (opt == null)
488 return n[conf][sid];
489
490 return n[conf][sid][opt];
491 }
492
493 /* requested an option value */
494 if (opt != null) {
495 /* check whether option was deleted */
496 if (d[conf] &amp;&amp; d[conf][sid]) {
497 if (d[conf][sid] === true)
498 return undefined;
499
500 for (var i = 0; i &lt; d[conf][sid].length; i++)
501 if (d[conf][sid][i] == opt)
502 return undefined;
503 }
504
505 /* check whether option was changed */
506 if (c[conf] &amp;&amp; c[conf][sid] &amp;&amp; c[conf][sid][opt] != null)
507 return c[conf][sid][opt];
508
509 /* return base value */
510 if (v[conf] &amp;&amp; v[conf][sid])
511 return v[conf][sid][opt];
512
513 return undefined;
514 }
515
516 /* requested an entire section */
517 if (v[conf])
518 return v[conf][sid];
519
520 return undefined;
521 },
522
523 /**
524 * Sets the value of the given option within the specified section
525 * of the given configuration.
526 *
527 * If either config, section or option is null, or if `option` begins
528 * with a dot, the function will do nothing.
529 *
530 * @param {string} config
531 * The name of the configuration to set the option value in.
532 *
533 * @param {string} sid
534 * The name or ID of the section to set the option value in.
535 *
536 * @param {string} option
537 * The option name to set the value for.
538 *
539 * @param {null|string|string[]} value
540 * The option value to set. If the value is `null` or an empty string,
541 * the option will be removed, otherwise it will be set or overwritten
542 * with the given value.
543 */
544 set: function(conf, sid, opt, val) {
545 var v = this.state.values,
546 n = this.state.creates,
547 c = this.state.changes,
548 d = this.state.deletes;
549
550 sid = this.resolveSID(conf, sid);
551
552 if (sid == null || opt == null || opt.charAt(0) == '.')
553 return;
554
555 if (n[conf] &amp;&amp; n[conf][sid]) {
556 if (val != null)
557 n[conf][sid][opt] = val;
558 else
559 delete n[conf][sid][opt];
560 }
561 else if (val != null &amp;&amp; val !== '') {
562 /* do not set within deleted section */
563 if (d[conf] &amp;&amp; d[conf][sid] === true)
564 return;
565
566 /* only set in existing sections */
567 if (!v[conf] || !v[conf][sid])
568 return;
569
570 if (!c[conf])
571 c[conf] = {};
572
573 if (!c[conf][sid])
574 c[conf][sid] = {};
575
576 /* undelete option */
577 if (d[conf] &amp;&amp; d[conf][sid])
578 d[conf][sid] = d[conf][sid].filter(function(o) { return o !== opt });
579
580 c[conf][sid][opt] = val;
581 }
582 else {
583 /* only delete in existing sections */
584 if (!(v[conf] &amp;&amp; v[conf][sid] &amp;&amp; v[conf][sid].hasOwnProperty(opt)) &amp;&amp;
585 !(c[conf] &amp;&amp; c[conf][sid] &amp;&amp; c[conf][sid].hasOwnProperty(opt)))
586 return;
587
588 if (!d[conf])
589 d[conf] = { };
590
591 if (!d[conf][sid])
592 d[conf][sid] = [ ];
593
594 if (d[conf][sid] !== true)
595 d[conf][sid].push(opt);
596 }
597 },
598
599 /**
600 * Remove the given option within the specified section of the given
601 * configuration.
602 *
603 * This function is a convenience wrapper around
604 * `uci.set(config, section, option, null)`.
605 *
606 * @param {string} config
607 * The name of the configuration to remove the option from.
608 *
609 * @param {string} sid
610 * The name or ID of the section to remove the option from.
611 *
612 * @param {string} option
613 * The name of the option to remove.
614 */
615 unset: function(conf, sid, opt) {
616 return this.set(conf, sid, opt, null);
617 },
618
619 /**
620 * Gets the value of the given option or the entire section object of
621 * the first found section of the specified type or the first found
622 * section of the entire configuration if no type is specfied.
623 *
624 * @param {string} config
625 * The name of the configuration to read the value from.
626 *
627 * @param {string} [type]
628 * The type of the first section to find. If it is `null`, the first
629 * section of the entire config is read, otherwise the first section
630 * matching the given type.
631 *
632 * @param {string} [option]
633 * The option name to read the value from. If the option name is
634 * omitted or `null`, the entire section is returned instead.
635 *
636 * @returns {null|string|string[]|LuCI.uci.SectionObject}
637 * - Returns a string containing the option value in case of a
638 * plain UCI option.
639 * - Returns an array of strings containing the option values in
640 * case of `option` pointing to an UCI list.
641 * - Returns a {@link LuCI.uci.SectionObject section object} if
642 * the `option` argument has been omitted or is `null`.
643 * - Returns `null` if the config, section or option has not been
644 * found or if the corresponding configuration is not loaded.
645 */
646 get_first: function(conf, type, opt) {
647 var sid = null;
648
649 this.sections(conf, type, function(s) {
650 if (sid == null)
651 sid = s['.name'];
652 });
653
654 return this.get(conf, sid, opt);
655 },
656
657 /**
658 * Sets the value of the given option within the first found section
659 * of the given configuration matching the specified type or within
660 * the first section of the entire config when no type has is specified.
661 *
662 * If either config, type or option is null, or if `option` begins
663 * with a dot, the function will do nothing.
664 *
665 * @param {string} config
666 * The name of the configuration to set the option value in.
667 *
668 * @param {string} [type]
669 * The type of the first section to find. If it is `null`, the first
670 * section of the entire config is written to, otherwise the first
671 * section matching the given type is used.
672 *
673 * @param {string} option
674 * The option name to set the value for.
675 *
676 * @param {null|string|string[]} value
677 * The option value to set. If the value is `null` or an empty string,
678 * the option will be removed, otherwise it will be set or overwritten
679 * with the given value.
680 */
681 set_first: function(conf, type, opt, val) {
682 var sid = null;
683
684 this.sections(conf, type, function(s) {
685 if (sid == null)
686 sid = s['.name'];
687 });
688
689 return this.set(conf, sid, opt, val);
690 },
691
692 /**
693 * Removes the given option within the first found section of the given
694 * configuration matching the specified type or within the first section
695 * of the entire config when no type has is specified.
696 *
697 * This function is a convenience wrapper around
698 * `uci.set_first(config, type, option, null)`.
699 *
700 * @param {string} config
701 * The name of the configuration to set the option value in.
702 *
703 * @param {string} [type]
704 * The type of the first section to find. If it is `null`, the first
705 * section of the entire config is written to, otherwise the first
706 * section matching the given type is used.
707 *
708 * @param {string} option
709 * The option name to set the value for.
710 */
711 unset_first: function(conf, type, opt) {
712 return this.set_first(conf, type, opt, null);
713 },
714
715 /**
716 * Move the first specified section within the given configuration
717 * before or after the second specified section.
718 *
719 * @param {string} config
720 * The configuration to move the section within.
721 *
722 * @param {string} sid1
723 * The ID of the section to move within the configuration.
724 *
725 * @param {string} [sid2]
726 * The ID of the target section for the move operation. If the
727 * `after` argument is `false` or not specified, the section named by
728 * `sid1` will be moved before this target section, if the `after`
729 * argument is `true`, the `sid1` section will be moved after this
730 * section.
731 *
732 * When the `sid2` argument is `null`, the section specified by `sid1`
733 * is moved to the end of the configuration.
734 *
735 * @param {boolean} [after=false]
736 * When `true`, the section `sid1` is moved after the section `sid2`,
737 * when `false`, the section `sid1` is moved before `sid2`.
738 *
739 * If `sid2` is null, then this parameter has no effect and the section
740 * `sid1` is moved to the end of the configuration instead.
741 *
742 * @returns {boolean}
743 * Returns `true` when the section was successfully moved, or `false`
744 * when either the section specified by `sid1` or by `sid2` is not found.
745 */
746 move: function(conf, sid1, sid2, after) {
747 var sa = this.sections(conf),
748 s1 = null, s2 = null;
749
750 sid1 = this.resolveSID(conf, sid1);
751 sid2 = this.resolveSID(conf, sid2);
752
753 for (var i = 0; i &lt; sa.length; i++) {
754 if (sa[i]['.name'] != sid1)
755 continue;
756
757 s1 = sa[i];
758 sa.splice(i, 1);
759 break;
760 }
761
762 if (s1 == null)
763 return false;
764
765 if (sid2 == null) {
766 sa.push(s1);
767 }
768 else {
769 for (var i = 0; i &lt; sa.length; i++) {
770 if (sa[i]['.name'] != sid2)
771 continue;
772
773 s2 = sa[i];
774 sa.splice(i + !!after, 0, s1);
775 break;
776 }
777
778 if (s2 == null)
779 return false;
780 }
781
782 for (var i = 0; i &lt; sa.length; i++)
783 this.get(conf, sa[i]['.name'])['.index'] = i;
784
785 this.state.reorder[conf] = true;
786
787 return true;
788 },
789
790 /**
791 * Submits all local configuration changes to the remove `ubus` api,
792 * adds, removes and reorders remote sections as needed and reloads
793 * all loaded configurations to resynchronize the local state with
794 * the remote configuration values.
795 *
796 * @returns {string[]}
797 * Returns a promise resolving to an array of configuration names which
798 * have been reloaded by the save operation.
799 */
800 save: function() {
801 var v = this.state.values,
802 n = this.state.creates,
803 c = this.state.changes,
804 d = this.state.deletes,
805 r = this.state.reorder,
806 self = this,
807 snew = [ ],
808 pkgs = { },
809 tasks = [];
810
811 if (n)
812 for (var conf in n) {
813 for (var sid in n[conf]) {
814 var r = {
815 config: conf,
816 values: { }
817 };
818
819 for (var k in n[conf][sid]) {
820 if (k == '.type')
821 r.type = n[conf][sid][k];
822 else if (k == '.create')
823 r.name = n[conf][sid][k];
824 else if (k.charAt(0) != '.')
825 r.values[k] = n[conf][sid][k];
826 }
827
828 snew.push(n[conf][sid]);
829 tasks.push(self.callAdd(r.config, r.type, r.name, r.values));
830 }
831
832 pkgs[conf] = true;
833 }
834
835 if (c)
836 for (var conf in c) {
837 for (var sid in c[conf])
838 tasks.push(self.callSet(conf, sid, c[conf][sid]));
839
840 pkgs[conf] = true;
841 }
842
843 if (d)
844 for (var conf in d) {
845 for (var sid in d[conf]) {
846 var o = d[conf][sid];
847 tasks.push(self.callDelete(conf, sid, (o === true) ? null : o));
848 }
849
850 pkgs[conf] = true;
851 }
852
853 if (r)
854 for (var conf in r)
855 pkgs[conf] = true;
856
857 return Promise.all(tasks).then(function(responses) {
858 /*
859 array "snew" holds references to the created uci sections,
860 use it to assign the returned names of the new sections
861 */
862 for (var i = 0; i &lt; snew.length; i++)
863 snew[i]['.name'] = responses[i];
864
865 return self.reorderSections();
866 }).then(function() {
867 pkgs = Object.keys(pkgs);
868
869 self.unload(pkgs);
870
871 return self.load(pkgs);
872 });
873 },
874
875 /**
876 * Instructs the remote `ubus` UCI api to commit all saved changes with
877 * rollback protection and attempts to confirm the pending commit
878 * operation to cancel the rollback timer.
879 *
880 * @param {number} [timeout=10]
881 * Override the confirmation timeout after which a rollback is triggered.
882 *
883 * @returns {Promise&lt;number>}
884 * Returns a promise resolving/rejecting with the `ubus` RPC status code.
885 */
886 apply: function(timeout) {
887 var self = this,
888 date = new Date();
889
890 if (typeof(timeout) != 'number' || timeout &lt; 1)
891 timeout = 10;
892
893 return self.callApply(timeout, true).then(function(rv) {
894 if (rv != 0)
895 return Promise.reject(rv);
896
897 var try_deadline = date.getTime() + 1000 * timeout;
898 var try_confirm = function() {
899 return self.callConfirm().then(function(rv) {
900 if (rv != 0) {
901 if (date.getTime() &lt; try_deadline)
902 window.setTimeout(try_confirm, 250);
903 else
904 return Promise.reject(rv);
905 }
906
907 return rv;
908 });
909 };
910
911 window.setTimeout(try_confirm, 1000);
912 });
913 },
914
915 /**
916 * An UCI change record is a plain array containing the change operation
917 * name as first element, the affected section ID as second argument
918 * and an optional third and fourth argument whose meanings depend on
919 * the operation.
920 *
921 * @typedef {string[]} ChangeRecord
922 * @memberof LuCI.uci
923 *
924 * @property {string} 0
925 * The operation name - may be one of `add`, `set`, `remove`, `order`,
926 * `list-add`, `list-del` or `rename`.
927 *
928 * @property {string} 1
929 * The section ID targeted by the operation.
930 *
931 * @property {string} 2
932 * The meaning of the third element depends on the operation.
933 * - For `add` it is type of the section that has been added
934 * - For `set` it either is the option name if a fourth element exists,
935 * or the type of a named section which has been added when the change
936 * entry only contains three elements.
937 * - For `remove` it contains the name of the option that has been
938 * removed.
939 * - For `order` it specifies the new sort index of the section.
940 * - For `list-add` it contains the name of the list option a new value
941 * has been added to.
942 * - For `list-del` it contains the name of the list option a value has
943 * been removed from.
944 * - For `rename` it contains the name of the option that has been
945 * renamed if a fourth element exists, else it contains the new name
946 * a section has been renamed to if the change entry only contains
947 * three elements.
948 *
949 * @property {string} 4
950 * The meaning of the fourth element depends on the operation.
951 * - For `set` it is the value an option has been set to.
952 * - For `list-add` it is the new value that has been added to a
953 * list option.
954 * - For `rename` it is the new name of an option that has been
955 * renamed.
956 */
957
958 /**
959 * Fetches uncommitted UCI changes from the remote `ubus` RPC api.
960 *
961 * @method
962 * @returns {Promise&lt;Object&lt;string, Array&lt;LuCI.uci.ChangeRecord>>>}
963 * Returns a promise resolving to an object containing the configuration
964 * names as keys and arrays of related change records as values.
965 */
966 changes: rpc.declare({
967 object: 'uci',
968 method: 'changes',
969 expect: { changes: { } }
970 })
971 });
972 </code></pre>
973 </article>
974 </section>
975
976
977
978
979 </div>
980
981 <nav>
982 <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="LuCI.html">LuCI</a></li><li><a href="LuCI.Class.html">Class</a></li><li><a href="LuCI.dom.html">dom</a></li><li><a href="LuCI.fs.html">fs</a></li><li><a href="LuCI.Headers.html">Headers</a></li><li><a href="LuCI.Network.html">Network</a></li><li><a href="LuCI.Network.Device.html">Device</a></li><li><a href="LuCI.Network.Hosts.html">Hosts</a></li><li><a href="LuCI.Network.Protocol.html">Protocol</a></li><li><a href="LuCI.Network.WifiDevice.html">WifiDevice</a></li><li><a href="LuCI.Network.WifiNetwork.html">WifiNetwork</a></li><li><a href="LuCI.Poll.html">Poll</a></li><li><a href="LuCI.Request.html">Request</a></li><li><a href="LuCI.Request.poll.html">poll</a></li><li><a href="LuCI.Response.html">Response</a></li><li><a href="LuCI.rpc.html">rpc</a></li><li><a href="LuCI.uci.html">uci</a></li><li><a href="LuCI.view.html">view</a></li><li><a href="LuCI.XHR.html">XHR</a></li></ul>
983 </nav>
984
985 <br class="clear">
986
987 <footer>
988 Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.3</a> on Tue Nov 05 2019 09:33:05 GMT+0100 (Central European Standard Time)
989 </footer>
990
991 <script> prettyPrint(); </script>
992 <script src="scripts/linenumber.js"> </script>
993 </body>
994 </html>