From 2b64fd6c6c8f48b56e84fee3e5c4c51971b82210 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Tue, 31 Mar 2020 21:30:38 +0200 Subject: [PATCH] docs: update js api docs Signed-off-by: Jo-Philipp Wich --- docs/jsapi/LuCI.Class.html | 610 ++- docs/jsapi/LuCI.Headers.html | 610 ++- docs/jsapi/LuCI.Network.Device.html | 656 ++- docs/jsapi/LuCI.Network.Hosts.html | 628 ++- docs/jsapi/LuCI.Network.Protocol.html | 970 +++- docs/jsapi/LuCI.Network.WifiDevice.html | 638 ++- docs/jsapi/LuCI.Network.WifiNetwork.html | 686 ++- docs/jsapi/LuCI.Network.html | 642 ++- docs/jsapi/LuCI.Poll.html | 610 ++- docs/jsapi/LuCI.Request.html | 610 ++- docs/jsapi/LuCI.Request.poll.html | 610 ++- docs/jsapi/LuCI.Response.html | 610 ++- docs/jsapi/LuCI.XHR.html | 624 ++- docs/jsapi/LuCI.dom.html | 620 ++- docs/jsapi/LuCI.fs.html | 610 ++- docs/jsapi/LuCI.html | 613 ++- docs/jsapi/LuCI.rpc.html | 610 ++- docs/jsapi/LuCI.uci.html | 610 ++- docs/jsapi/LuCI.ui.AbstractElement.html | 3086 +++++++++++ docs/jsapi/LuCI.ui.Checkbox.html | 3233 ++++++++++++ docs/jsapi/LuCI.ui.ComboButton.html | 3263 ++++++++++++ docs/jsapi/LuCI.ui.Combobox.html | 3166 ++++++++++++ docs/jsapi/LuCI.ui.Dropdown.html | 3964 ++++++++++++++ docs/jsapi/LuCI.ui.DynamicList.html | 3341 ++++++++++++ docs/jsapi/LuCI.ui.FileUpload.html | 3190 ++++++++++++ docs/jsapi/LuCI.ui.Hiddenfield.html | 2944 +++++++++++ docs/jsapi/LuCI.ui.Select.html | 3293 ++++++++++++ docs/jsapi/LuCI.ui.Textarea.html | 3242 ++++++++++++ docs/jsapi/LuCI.ui.Textfield.html | 3170 ++++++++++++ docs/jsapi/LuCI.ui.changes.html | 2354 +++++++++ docs/jsapi/LuCI.ui.html | 4292 ++++++++++++++++ docs/jsapi/LuCI.ui.tabs.html | 2101 ++++++++ docs/jsapi/LuCI.view.html | 624 ++- docs/jsapi/fs.js.html | 610 ++- docs/jsapi/index.html | 610 ++- docs/jsapi/luci.js.html | 619 ++- docs/jsapi/network.js.html | 709 ++- docs/jsapi/rpc.js.html | 610 ++- docs/jsapi/uci.js.html | 610 ++- docs/jsapi/ui.js.html | 5994 ++++++++++++++++++++++ 40 files changed, 66364 insertions(+), 228 deletions(-) create mode 100644 docs/jsapi/LuCI.ui.AbstractElement.html create mode 100644 docs/jsapi/LuCI.ui.Checkbox.html create mode 100644 docs/jsapi/LuCI.ui.ComboButton.html create mode 100644 docs/jsapi/LuCI.ui.Combobox.html create mode 100644 docs/jsapi/LuCI.ui.Dropdown.html create mode 100644 docs/jsapi/LuCI.ui.DynamicList.html create mode 100644 docs/jsapi/LuCI.ui.FileUpload.html create mode 100644 docs/jsapi/LuCI.ui.Hiddenfield.html create mode 100644 docs/jsapi/LuCI.ui.Select.html create mode 100644 docs/jsapi/LuCI.ui.Textarea.html create mode 100644 docs/jsapi/LuCI.ui.Textfield.html create mode 100644 docs/jsapi/LuCI.ui.changes.html create mode 100644 docs/jsapi/LuCI.ui.html create mode 100644 docs/jsapi/LuCI.ui.tabs.html create mode 100644 docs/jsapi/ui.js.html diff --git a/docs/jsapi/LuCI.Class.html b/docs/jsapi/LuCI.Class.html index e1efc4a9a..cc34d3319 100644 --- a/docs/jsapi/LuCI.Class.html +++ b/docs/jsapi/LuCI.Class.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -2326,7 +2934,7 @@ and the values extracted from the args array beginning with
    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.Headers.html b/docs/jsapi/LuCI.Headers.html index d19075b5e..b86cc17b0 100644 --- a/docs/jsapi/LuCI.Headers.html +++ b/docs/jsapi/LuCI.Headers.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1499,7 +2107,7 @@ Note: Header-Names are case-insensitive.

    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.Network.Device.html b/docs/jsapi/LuCI.Network.Device.html index eec9069b9..3ce2664cc 100644 --- a/docs/jsapi/LuCI.Network.Device.html +++ b/docs/jsapi/LuCI.Network.Device.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1111,7 +1719,7 @@ device and allows querying device details such as packet statistics or MTU.

    < @@ -1196,7 +1804,7 @@ device and allows querying device details such as packet statistics or MTU.

    < @@ -1300,7 +1908,7 @@ device is not a Linux bridge. @@ -1401,7 +2009,7 @@ enabled, else false. @@ -1502,7 +2110,7 @@ for non-wifi devices or operation mode and ssid for wifi ones. @@ -1602,7 +2210,7 @@ for non-wifi devices or operation mode and ssid for wifi ones. @@ -1702,7 +2310,7 @@ for non-wifi devices or operation mode and ssid for wifi ones. @@ -1806,7 +2414,7 @@ e.g. for non-ethernet tunnel devices. @@ -1906,7 +2514,7 @@ e.g. for non-ethernet tunnel devices. @@ -2006,7 +2614,7 @@ e.g. for non-ethernet tunnel devices. @@ -2111,7 +2719,7 @@ assigned to any logical interface. @@ -2212,7 +2820,7 @@ logical interfaces this device is assigned to. @@ -2317,7 +2925,7 @@ a Linux bridge. @@ -2417,7 +3025,7 @@ a Linux bridge. @@ -2517,7 +3125,7 @@ a Linux bridge. @@ -2618,7 +3226,7 @@ the operation mode and SSID for wifi devices. @@ -2718,7 +3326,7 @@ the operation mode and SSID for wifi devices. @@ -2818,7 +3426,7 @@ the operation mode and SSID for wifi devices. @@ -2927,7 +3535,7 @@ the operation mode and SSID for wifi devices. @@ -3028,7 +3636,7 @@ the operation mode and SSID for wifi devices. @@ -3133,7 +3741,7 @@ is not a wireless device. @@ -3234,7 +3842,7 @@ else false. @@ -3335,7 +3943,7 @@ else false. @@ -3444,7 +4052,7 @@ when it is down or absent.
    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.Network.Hosts.html b/docs/jsapi/LuCI.Network.Hosts.html index 9af4dec9f..b6138acc7 100644 --- a/docs/jsapi/LuCI.Network.Hosts.html +++ b/docs/jsapi/LuCI.Network.Hosts.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1112,7 +1720,7 @@ host information by different criteria.

    @@ -1197,7 +1805,7 @@ host information by different criteria.

    @@ -1347,7 +1955,7 @@ the corresponding host. @@ -1497,7 +2105,7 @@ the corresponding host. @@ -1647,7 +2255,7 @@ the corresponding host. @@ -1797,7 +2405,7 @@ the corresponding host. @@ -1947,7 +2555,7 @@ the corresponding host. @@ -2097,7 +2705,7 @@ the corresponding host. @@ -2247,7 +2855,7 @@ the corresponding host. @@ -2425,7 +3033,7 @@ is used as hint.
    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.Network.Protocol.html b/docs/jsapi/LuCI.Network.Protocol.html index d6dd78cb8..c104314ec 100644 --- a/docs/jsapi/LuCI.Network.Protocol.html +++ b/docs/jsapi/LuCI.Network.Protocol.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -970,31 +974,635 @@
  • createSID
  • -
  • get
  • +
  • get
  • + +
  • get_first
  • + +
  • load
  • + +
  • move
  • + +
  • remove
  • + +
  • resolveSID
  • + +
  • save
  • + +
  • sections
  • + +
  • set
  • + +
  • set_first
  • + +
  • unload
  • + +
  • unset
  • + +
  • unset_first
  • + + + + + +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + + +
  • +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1112,7 +1720,7 @@ well as methods for enumerating related wireless networks.

    @@ -1197,7 +1805,7 @@ well as methods for enumerating related wireless networks.

    @@ -1352,7 +1960,7 @@ were invalid. @@ -1504,7 +2112,7 @@ with this wireless radio device. @@ -1656,7 +2264,7 @@ not found. @@ -1767,7 +2375,7 @@ known mode values are: @@ -1877,7 +2485,7 @@ known mode values are: @@ -1977,7 +2585,7 @@ known mode values are: @@ -2079,7 +2687,7 @@ for the wireless phy. @@ -2181,7 +2789,7 @@ describing the networks found in the vincinity. @@ -2332,7 +2940,7 @@ this radio device. @@ -2434,7 +3042,7 @@ radio device. @@ -2536,7 +3144,7 @@ UCI configuration. @@ -2637,7 +3245,7 @@ runtime state.

    @@ -2791,7 +3399,7 @@ configuration.

    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.Network.WifiNetwork.html b/docs/jsapi/LuCI.Network.WifiNetwork.html index 4b4b7e0bc..e43432ba7 100644 --- a/docs/jsapi/LuCI.Network.WifiNetwork.html +++ b/docs/jsapi/LuCI.Network.WifiNetwork.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1113,7 +1721,7 @@ such networks in parallel.

    @@ -1198,7 +1806,7 @@ such networks in parallel.

    @@ -1469,7 +2077,7 @@ are passed. @@ -1621,7 +2229,7 @@ not found. @@ -1722,7 +2330,7 @@ information. @@ -1823,7 +2431,7 @@ encryption state could not be found in ubus runtime information. - network.js, line 3576 + network.js, line 3617 @@ -1936,7 +2544,7 @@ state. Possible returned values are: @@ -2038,7 +2646,7 @@ translated string.

    @@ -2139,7 +2747,7 @@ information. @@ -2240,7 +2848,7 @@ with this network. @@ -2346,7 +2954,7 @@ is not available. @@ -2449,7 +3057,7 @@ is not available. @@ -2553,7 +3161,7 @@ or null if it cannot be determined. @@ -2654,7 +3262,7 @@ information or 00 if it cannot be determined. @@ -2755,7 +3363,7 @@ device associted with this wireless network. @@ -2860,7 +3468,7 @@ available. @@ -2963,7 +3571,7 @@ name, depending on which information is available. @@ -3066,7 +3674,7 @@ radio and network index numbers.

    @@ -3171,7 +3779,7 @@ associated network device, e.g. when not configured or up. @@ -3275,7 +3883,7 @@ is not in mesh mode. @@ -3382,7 +3990,7 @@ is not in mesh mode. @@ -3482,7 +4090,7 @@ is not in mesh mode. @@ -3587,7 +4195,7 @@ interface. @@ -3688,7 +4296,7 @@ attached to.

    @@ -3789,7 +4397,7 @@ logical interfaces this wireless network is attached to. @@ -3890,7 +4498,7 @@ information or 0 if it cannot be determined. @@ -3992,7 +4600,7 @@ internal network ID, depending on which information is available. @@ -4096,7 +4704,7 @@ information or null if it cannot be determined. @@ -4199,7 +4807,7 @@ noise and signal (SNR), divided by 5. @@ -4301,7 +4909,7 @@ by ubus runtime state. @@ -4405,7 +5013,7 @@ in mesh mode. @@ -4509,7 +5117,7 @@ in mesh mode. @@ -4613,7 +5221,7 @@ cannot be determined. @@ -4718,7 +5326,7 @@ found. @@ -4822,7 +5430,7 @@ or null if it cannot be determined. @@ -4923,7 +5531,7 @@ deauthenticating clients, otherwise false. @@ -5025,7 +5633,7 @@ UCI configuration. @@ -5130,7 +5738,7 @@ instance.

    @@ -5284,7 +5892,7 @@ configuration.

    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.Network.html b/docs/jsapi/LuCI.Network.html index 4dd84f333..67a6d1b98 100644 --- a/docs/jsapi/LuCI.Network.html +++ b/docs/jsapi/LuCI.Network.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1404,7 +2012,7 @@ existed. @@ -1702,7 +2310,7 @@ references to it were successfully deleted from the configuration or @@ -2104,7 +2712,7 @@ or WPA3 SAE (CCMP).

    @@ -2252,7 +2860,7 @@ be found. @@ -2353,7 +2961,7 @@ instances describing the network devices found on the system. @@ -2454,7 +3062,7 @@ instances describing the network devices found on the system. @@ -2558,7 +3166,7 @@ class instance describing the found hosts.

    @@ -3270,7 +3878,7 @@ backend classes.

    @@ -3374,7 +3982,7 @@ the layout. @@ -3477,7 +4085,7 @@ instances describing the found IPv6 default route interfaces. @@ -3580,7 +4188,7 @@ instances describing the found default route interfaces. @@ -3729,7 +4337,7 @@ be found. @@ -3832,7 +4440,7 @@ the configuration. @@ -3983,7 +4591,7 @@ be found. @@ -4086,7 +4694,7 @@ are found. @@ -5094,7 +5702,7 @@ be passed to Class.extend().< @@ -7139,7 +7747,7 @@ conjunction with quality to calculate a quality percentage.

    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) diff --git a/docs/jsapi/LuCI.Poll.html b/docs/jsapi/LuCI.Poll.html index ef5aba59f..0952abd55 100644 --- a/docs/jsapi/LuCI.Poll.html +++ b/docs/jsapi/LuCI.Poll.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1882,7 +2490,7 @@ run to begin with.
    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.Request.html b/docs/jsapi/LuCI.Request.html index 177b8c186..b3cbe7cbf 100644 --- a/docs/jsapi/LuCI.Request.html +++ b/docs/jsapi/LuCI.Request.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -2817,7 +3425,7 @@ instances as sole argument during the HTTP request transfer.

    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.Request.poll.html b/docs/jsapi/LuCI.Request.poll.html index 8865bf20f..7a993ab57 100644 --- a/docs/jsapi/LuCI.Request.poll.html +++ b/docs/jsapi/LuCI.Request.poll.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -2001,7 +2609,7 @@ else null.

    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.Response.html b/docs/jsapi/LuCI.Response.html index 5627139ec..a861348b3 100644 --- a/docs/jsapi/LuCI.Response.html +++ b/docs/jsapi/LuCI.Response.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1959,7 +2567,7 @@ using String() and treated as response text.

    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.XHR.html b/docs/jsapi/LuCI.XHR.html index 4d1670b7b..4516d9695 100644 --- a/docs/jsapi/LuCI.XHR.html +++ b/docs/jsapi/LuCI.XHR.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1114,7 +1722,7 @@ request handling.

    @@ -1201,7 +1809,7 @@ request handling.

    @@ -1275,7 +1883,7 @@ request handling.

    @@ -1378,7 +1986,7 @@ already completed. @@ -1455,7 +2063,7 @@ finishes or timed out.

    @@ -1698,7 +2306,7 @@ finishes or timed out.

    @@ -1941,7 +2549,7 @@ finishes or timed out.

    @@ -2051,7 +2659,7 @@ when invoked.

    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.dom.html b/docs/jsapi/LuCI.dom.html index 539e07683..abf438601 100644 --- a/docs/jsapi/LuCI.dom.html +++ b/docs/jsapi/LuCI.dom.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1595,7 +2203,7 @@ call implicitely turning it into a string.

    @@ -1796,7 +2404,7 @@ a valid Class instance.

    @@ -2860,7 +3468,7 @@ be found. @@ -3010,7 +3618,7 @@ class could be found on the node itself or any of its parents. @@ -3728,7 +4336,7 @@ the first div element node.

    @@ -3882,7 +4490,7 @@ ignored, else not.

    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.fs.html b/docs/jsapi/LuCI.fs.html index c370890be..36169d6a7 100644 --- a/docs/jsapi/LuCI.fs.html +++ b/docs/jsapi/LuCI.fs.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -3471,7 +4079,7 @@ the failure reason.
    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.html b/docs/jsapi/LuCI.html index 2d15fc039..5bf4e3262 100644 --- a/docs/jsapi/LuCI.html +++ b/docs/jsapi/LuCI.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1253,6 +1861,9 @@ accessible using the global L variable.

    uci
    +
    ui
    +
    +
    view
    @@ -5184,7 +5795,7 @@ else null.

    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.rpc.html b/docs/jsapi/LuCI.rpc.html index 35d319848..2b421cdc9 100644 --- a/docs/jsapi/LuCI.rpc.html +++ b/docs/jsapi/LuCI.rpc.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -3229,7 +3837,7 @@ to the expect and filter declarations.
    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.uci.html b/docs/jsapi/LuCI.uci.html index a38f932f2..3db3c5576 100644 --- a/docs/jsapi/LuCI.uci.html +++ b/docs/jsapi/LuCI.uci.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -4777,7 +5385,7 @@ associated name as arguments.

    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/LuCI.ui.AbstractElement.html b/docs/jsapi/LuCI.ui.AbstractElement.html new file mode 100644 index 000000000..e727a3a41 --- /dev/null +++ b/docs/jsapi/LuCI.ui.AbstractElement.html @@ -0,0 +1,3086 @@ + + + + + Class: AbstractElement + + + + + + + + + + + + + + + + + +
    + + +
    +

    Class: AbstractElement

    + + + + +
    + +
    +

    + LuCI.ui. + + AbstractElement +

    + +

    The AbstractElement class serves as abstract base for the different widgets +implemented by LuCI.ui. It provides the common logic for getting and +setting values, for checking the validity state and for wiring up required +events.

    +

    UI widget instances are usually not supposed to be created by view code +directly, instead they're implicitely created by LuCI.form when +instantiating CBI forms.

    +

    This class is automatically instantiated as part of LuCI.ui. To use it +in views, use 'require ui' and refer to ui.AbstractElement. To import +it in external JavaScript, use L.require("ui").then(...) and access the +AbstractElement property of the class instance value.

    + +
    + +
    +
    + + + + +
    +
    +

    + + new LuCI.ui.AbstractElement() +

    + + +
    + ui.js, line 12 +
    + +
    + + +
    +
    + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + + +

    Methods

    + +
    + +
    +
    +

    + + getValue(){string|Array.<string>|null} +

    + + +
    + ui.js, line 71 +
    + +
    + + +
    +
    + + +
    +

    Read the current value of the input widget.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + string + | + + Array.<string> + | + + null + + + The current value of the input element. For simple inputs like text +fields or selects, the return value type will be a - possibly empty - +string. Complex widgets such as DynamicList instances may result in +an array of strings or null for unset values.
    + + + + +
    + + + +
    +
    +

    + + isValid(){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Check whether the current input value is valid.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true if the current input value is valid or false if it does +not meet the validation constraints.
    + + + + +
    + + + +
    +
    +

    + + registerEvents(targetNode, synevent, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Dispatch a custom (synthetic) event in response to received events.

    +

    Sets up event handlers on the given target DOM node for the given event +names that dispatch a custom event of the given type to the widget root +DOM node.

    +

    The primary purpose of this function is to set up a series of custom +uniform standard events such as widget-update, validation-success, +validation-failure etc. which are triggered by various different +widget specific native DOM events.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + +

    Specifies the DOM node on which the native event listeners should be +registered.

    synevent + + +string + + + + + +

    The name of the custom event to dispatch to the widget root DOM node.

    events + + +Array.<string> + + + + + +

    The native DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + render(){Node} +

    + + + + +
    + + +
    +
    + + +
    +

    Render the widget, setup event listeners and return resulting markup.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Node + + + Returns a DOM Node or DocumentFragment containing the rendered +widget markup.
    + + + + +
    + + + +
    +
    +

    + + setChangeEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may change the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to change completely, such as +change events in a select menu. In contrast to update events, such +change events will not trigger input value validation but they may cause +field dependencies to get re-evaluated and will mark the input widget +as dirty.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + setUpdateEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may update the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to update, such as keyup or +onclick events. In contrast to change events, such update events will +trigger input value validation.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + setValue(value) +

    + + +
    + ui.js, line 89 +
    + +
    + + +
    +
    + + +
    +

    Set the current value of the input widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    value + + +string +| + +Array.<string> +| + +null + + + + + +

    The value to set the input element to. For simple inputs like text +fields or selects, the value should be a - possibly empty - string. +Complex widgets such as DynamicList instances may accept string array +or null values.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + triggerValidation() +

    + + + + +
    + + +
    +
    + + +
    +

    Force validation of the current input value.

    +

    Usually input validation is automatically triggered by various DOM events +bound to the input widget. In some cases it is required though to manually +trigger validation runs, e.g. when programmatically altering values.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + +
    + + + +

    Type Definitions

    + +
    + +
    +
    +

    LuCI.ui.AbstractElement.InitOptionsObject

    +
    + + +
    +
    + + + +
    + + +
    Properties:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeArgumentDefaultDescription
    id + + +string + + + + + + <optional>
    + + + +
    + +

    Specifies the widget ID to use. It will be used as HTML id attribute +on the toplevel widget DOM node.

    name + + +string + + + + + + <optional>
    + + + +
    + +

    Specifies the widget name which is set as HTML name attribute on the +corresponding <input> element.

    optional + + +boolean + + + + + + <optional>
    + + + +
    + + true + +

    Specifies whether the input field allows empty values.

    datatype + + +string + + + + + + <optional>
    + + + +
    + + string + +

    An expression describing the input data validation constraints. +It defaults to string which will allow any value. +SeeLuCI.validation for details on the expression format.

    validator + + +function + + + + + + <optional>
    + + + +
    + +

    Specifies a custom validator function which is invoked after the +standard validation constraints are checked. The function should return +true to accept the given input value. Any other return value type is +converted to a string and treated as validation error message.

    + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + +
    + + + + + +
    + +
    + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/jsapi/LuCI.ui.Checkbox.html b/docs/jsapi/LuCI.ui.Checkbox.html new file mode 100644 index 000000000..79fa47f08 --- /dev/null +++ b/docs/jsapi/LuCI.ui.Checkbox.html @@ -0,0 +1,3233 @@ + + + + + Class: Checkbox + + + + + + + + + + + + + + + + + +
    + + +
    +

    Class: Checkbox

    + + + + +
    + +
    +

    + LuCI.ui. + + Checkbox +

    + +

    The Checkbox class implements a simple checkbox input field.

    +

    UI widget instances are usually not supposed to be created by view code +directly, instead they're implicitely created by LuCI.form when +instantiating CBI forms.

    +

    This class is automatically instantiated as part of LuCI.ui. To use it +in views, use 'require ui' and refer to ui.Checkbox. To import it in +external JavaScript, use L.require("ui").then(...) and access the +Checkbox property of the class instance value.

    + +
    + +
    +
    + + + + +
    +
    +

    + + new LuCI.ui.Checkbox(value, options) +

    + + + + +
    + + +
    +
    + + +
    +

    Instantiate a checkbox widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    value + + +string + + + + + + null + + + + + optional + + + + + +

    The initial input value.

    options + + +LuCI.ui.Checkbox.InitOptions + + + + + + + + + optional + + + + + +

    Object describing the widget specific options to initialize the input.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + +

    Methods

    + +
    + +
    +
    +

    + + inherited + + getValue(){string|Array.<string>|null} +

    + + + + +
    + + +
    +
    + + +
    +

    Read the current value of the input widget.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + string + | + + Array.<string> + | + + null + + + The current value of the input element. For simple inputs like text +fields or selects, the return value type will be a - possibly empty - +string. Complex widgets such as DynamicList instances may result in +an array of strings or null for unset values.
    + + + + +
    + + + +
    +
    +

    + + isChecked(){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Test whether the checkbox is currently checked.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true when the checkbox is currently checked, otherwise false.
    + + + + +
    + + + +
    +
    +

    + + inherited + + isValid(){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Check whether the current input value is valid.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true if the current input value is valid or false if it does +not meet the validation constraints.
    + + + + +
    + + + +
    +
    +

    + + inherited + + registerEvents(targetNode, synevent, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Dispatch a custom (synthetic) event in response to received events.

    +

    Sets up event handlers on the given target DOM node for the given event +names that dispatch a custom event of the given type to the widget root +DOM node.

    +

    The primary purpose of this function is to set up a series of custom +uniform standard events such as widget-update, validation-success, +validation-failure etc. which are triggered by various different +widget specific native DOM events.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + +

    Specifies the DOM node on which the native event listeners should be +registered.

    synevent + + +string + + + + + +

    The name of the custom event to dispatch to the widget root DOM node.

    events + + +Array.<string> + + + + + +

    The native DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + render(){Node} +

    + + + + +
    + + +
    +
    + + +
    +

    Render the widget, setup event listeners and return resulting markup.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Node + + + Returns a DOM Node or DocumentFragment containing the rendered +widget markup.
    + + + + +
    + + + +
    +
    +

    + + inherited + + setChangeEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may change the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to change completely, such as +change events in a select menu. In contrast to update events, such +change events will not trigger input value validation but they may cause +field dependencies to get re-evaluated and will mark the input widget +as dirty.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setUpdateEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may update the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to update, such as keyup or +onclick events. In contrast to change events, such update events will +trigger input value validation.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setValue(value) +

    + + + + +
    + + +
    +
    + + +
    +

    Set the current value of the input widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    value + + +string +| + +Array.<string> +| + +null + + + + + +

    The value to set the input element to. For simple inputs like text +fields or selects, the value should be a - possibly empty - string. +Complex widgets such as DynamicList instances may accept string array +or null values.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + triggerValidation() +

    + + + + +
    + + +
    +
    + + +
    +

    Force validation of the current input value.

    +

    Usually input validation is automatically triggered by various DOM events +bound to the input widget. In some cases it is required though to manually +trigger validation runs, e.g. when programmatically altering values.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + +
    + + + +

    Type Definitions

    + +
    + +
    +
    +

    LuCI.ui.Checkbox.InitOptionsLuCI.ui.AbstractElement.InitOptions

    +
    + + +
    +
    + +
    +

    In addition to the AbstractElement.InitOptions +the following properties are recognized:

    +
    + + + +
    + + +
    Properties:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeArgumentDefaultDescription
    value_enabled + + +string + + + + + + <optional>
    + + + +
    + + 1 + +

    Specifies the value corresponding to a checked checkbox.

    value_disabled + + +string + + + + + + <optional>
    + + + +
    + + 0 + +

    Specifies the value corresponding to an unchecked checkbox.

    hiddenname + + +string + + + + + + <optional>
    + + + +
    + +

    Specifies the HTML name attribute of the hidden input backing the +checkbox. This is a legacy property existing for compatibility reasons, +it is required for HTML based form submissions.

    + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + +
    + + + + + +
    + +
    + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/jsapi/LuCI.ui.ComboButton.html b/docs/jsapi/LuCI.ui.ComboButton.html new file mode 100644 index 000000000..3b54b1765 --- /dev/null +++ b/docs/jsapi/LuCI.ui.ComboButton.html @@ -0,0 +1,3263 @@ + + + + + Class: ComboButton + + + + + + + + + + + + + + + + + +
    + + +
    +

    Class: ComboButton

    + + + + +
    + +
    +

    + LuCI.ui. + + ComboButton +

    + +

    The ComboButton class implements a button element which can be expanded +into a dropdown to chose from a set of different action choices.

    +

    UI widget instances are usually not supposed to be created by view code +directly, instead they're implicitely created by LuCI.form when +instantiating CBI forms.

    +

    This class is automatically instantiated as part of LuCI.ui. To use it +in views, use 'require ui' and refer to ui.ComboButton. To import it in +external JavaScript, use L.require("ui").then(...) and access the +ComboButton property of the class instance value.

    + +
    + +
    +
    + + + + +
    +
    +

    + + new LuCI.ui.ComboButton(value, choices, options) +

    + + + + +
    + + +
    +
    + + +
    +

    Instantiate a combo button widget offering multiple action choices.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    value + + +string +| + +Array.<string> + + + + + + null + + + + + optional + + + + + +

    The initial input value(s).

    choices + + +Object.<string, *> + + + + + + + + + + + + +

    Object containing the selectable choices of the widget. The object keys +serve as values for the different choices while the values are used as +choice labels.

    options + + +LuCI.ui.ComboButton.InitOptions + + + + + + + + + optional + + + + + +

    Object describing the widget specific options to initialize the button.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + +

    Methods

    + +
    + +
    +
    +

    + + inherited + + addChoices(values, labels) +

    + + + + +
    + + +
    +
    + + +
    +

    Add new choices to the dropdown menu.

    +

    This function adds further choices to an existing dropdown menu, +ignoring choice values which are already present.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    values + + +Array.<string> + + + + + +

    The choice values to add to the dropdown widget.

    labels + + +Object.<string, *> + + + + + +

    The choice label values to use when adding dropdown choices. If no +label is found for a particular choice value, the value itself is used +as label text. Choice labels may be any valid value accepted by +LuCI.dom#content.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + clearChoices(reset_value) +

    + + + + +
    + + +
    +
    + + +
    +

    Remove all existing choices from the dropdown menu.

    +

    This function removes all preexisting dropdown choices from the widget, +keeping only choices currently being selected unless reset_values is +given, in which case all choices and deselected and removed.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    reset_value + + +boolean + + + + + + false + + + + + optional + + + + + +

    If set to true, deselect and remove selected choices as well instead +of keeping them.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + closeAllDropdowns() +

    + + + + +
    + + +
    +
    + + +
    +

    Close all open dropdown widgets in the current document.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + isValid(){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Check whether the current input value is valid.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true if the current input value is valid or false if it does +not meet the validation constraints.
    + + + + +
    + + + +
    +
    +

    + + inherited + + registerEvents(targetNode, synevent, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Dispatch a custom (synthetic) event in response to received events.

    +

    Sets up event handlers on the given target DOM node for the given event +names that dispatch a custom event of the given type to the widget root +DOM node.

    +

    The primary purpose of this function is to set up a series of custom +uniform standard events such as widget-update, validation-success, +validation-failure etc. which are triggered by various different +widget specific native DOM events.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + +

    Specifies the DOM node on which the native event listeners should be +registered.

    synevent + + +string + + + + + +

    The name of the custom event to dispatch to the widget root DOM node.

    events + + +Array.<string> + + + + + +

    The native DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setChangeEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may change the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to change completely, such as +change events in a select menu. In contrast to update events, such +change events will not trigger input value validation but they may cause +field dependencies to get re-evaluated and will mark the input widget +as dirty.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setUpdateEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may update the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to update, such as keyup or +onclick events. In contrast to change events, such update events will +trigger input value validation.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + triggerValidation() +

    + + + + +
    + + +
    +
    + + +
    +

    Force validation of the current input value.

    +

    Usually input validation is automatically triggered by various DOM events +bound to the input widget. In some cases it is required though to manually +trigger validation runs, e.g. when programmatically altering values.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + +
    + + + +

    Type Definitions

    + +
    + +
    +
    +

    LuCI.ui.ComboButton.InitOptionsLuCI.ui.Dropdown.InitOptions

    +
    + + +
    +
    + +
    +

    ComboButtons support the same properties as +Dropdown.InitOptions but enforce +specific values for some properties and add aditional button specific +properties.

    +
    + + + +
    + + +
    Properties:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeArgumentDefaultDescription
    multiple + + +boolean + + + + + + + + + + false + +

    Since ComboButtons never allow selecting multiple actions, this property +is forcibly set to false.

    create + + +boolean + + + + + + + + + + false + +

    Since ComboButtons never allow creating custom choices, this property +is forcibly set to false.

    optional + + +boolean + + + + + + + + + + false + +

    Since ComboButtons must always select one action, this property is +forcibly set to false.

    classes + + +Object.<string, string> + + + + + + <optional>
    + + + +
    + +

    Specifies a mapping of choice values to CSS class names. If an action +choice is selected by the user and if a corresponding entry exists in +the classes object, the class names corresponding to the selected +value are set on the button element.

    +

    This is useful to apply different button styles, such as colors, to the +combined button depending on the selected action.

    click + + +function + + + + + + <optional>
    + + + +
    + +

    Specifies a handler function to invoke when the user clicks the button. +This function will be called with the button DOM node as this context +and receive the DOM click event as first as well as the selected action +choice value as second argument.

    + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + +
    + + + + + +
    + +
    + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/jsapi/LuCI.ui.Combobox.html b/docs/jsapi/LuCI.ui.Combobox.html new file mode 100644 index 000000000..a281bd644 --- /dev/null +++ b/docs/jsapi/LuCI.ui.Combobox.html @@ -0,0 +1,3166 @@ + + + + + Class: Combobox + + + + + + + + + + + + + + + + + +
    + + +
    +

    Class: Combobox

    + + + + +
    + +
    +

    + LuCI.ui. + + Combobox +

    + +

    The Combobox class implements a rich, stylable dropdown menu which allows +to enter custom values. Historically, comboboxes used to be a dedicated +widget type in LuCI but nowadays they are direct aliases of dropdown widgets +with a set of enforced default properties for easier instantiation.

    +

    UI widget instances are usually not supposed to be created by view code +directly, instead they're implicitely created by LuCI.form when +instantiating CBI forms.

    +

    This class is automatically instantiated as part of LuCI.ui. To use it +in views, use 'require ui' and refer to ui.Combobox. To import it in +external JavaScript, use L.require("ui").then(...) and access the +Combobox property of the class instance value.

    + +
    + +
    +
    + + + + +
    +
    +

    + + new LuCI.ui.Combobox(value, choices, options) +

    + + + + +
    + + +
    +
    + + +
    +

    Instantiate a rich dropdown choice widget allowing custom values.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    value + + +string +| + +Array.<string> + + + + + + null + + + + + optional + + + + + +

    The initial input value(s).

    choices + + +Object.<string, *> + + + + + + + + + + + + +

    Object containing the selectable choices of the widget. The object keys +serve as values for the different choices while the values are used as +choice labels.

    options + + +LuCI.ui.Combobox.InitOptions + + + + + + + + + optional + + + + + +

    Object describing the widget specific options to initialize the dropdown.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + +

    Methods

    + +
    + +
    +
    +

    + + inherited + + addChoices(values, labels) +

    + + + + +
    + + +
    +
    + + +
    +

    Add new choices to the dropdown menu.

    +

    This function adds further choices to an existing dropdown menu, +ignoring choice values which are already present.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    values + + +Array.<string> + + + + + +

    The choice values to add to the dropdown widget.

    labels + + +Object.<string, *> + + + + + +

    The choice label values to use when adding dropdown choices. If no +label is found for a particular choice value, the value itself is used +as label text. Choice labels may be any valid value accepted by +LuCI.dom#content.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + clearChoices(reset_value) +

    + + + + +
    + + +
    +
    + + +
    +

    Remove all existing choices from the dropdown menu.

    +

    This function removes all preexisting dropdown choices from the widget, +keeping only choices currently being selected unless reset_values is +given, in which case all choices and deselected and removed.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    reset_value + + +boolean + + + + + + false + + + + + optional + + + + + +

    If set to true, deselect and remove selected choices as well instead +of keeping them.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + closeAllDropdowns() +

    + + + + +
    + + +
    +
    + + +
    +

    Close all open dropdown widgets in the current document.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + isValid(){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Check whether the current input value is valid.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true if the current input value is valid or false if it does +not meet the validation constraints.
    + + + + +
    + + + +
    +
    +

    + + inherited + + registerEvents(targetNode, synevent, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Dispatch a custom (synthetic) event in response to received events.

    +

    Sets up event handlers on the given target DOM node for the given event +names that dispatch a custom event of the given type to the widget root +DOM node.

    +

    The primary purpose of this function is to set up a series of custom +uniform standard events such as widget-update, validation-success, +validation-failure etc. which are triggered by various different +widget specific native DOM events.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + +

    Specifies the DOM node on which the native event listeners should be +registered.

    synevent + + +string + + + + + +

    The name of the custom event to dispatch to the widget root DOM node.

    events + + +Array.<string> + + + + + +

    The native DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setChangeEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may change the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to change completely, such as +change events in a select menu. In contrast to update events, such +change events will not trigger input value validation but they may cause +field dependencies to get re-evaluated and will mark the input widget +as dirty.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setUpdateEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may update the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to update, such as keyup or +onclick events. In contrast to change events, such update events will +trigger input value validation.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + triggerValidation() +

    + + + + +
    + + +
    +
    + + +
    +

    Force validation of the current input value.

    +

    Usually input validation is automatically triggered by various DOM events +bound to the input widget. In some cases it is required though to manually +trigger validation runs, e.g. when programmatically altering values.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + +
    + + + +

    Type Definitions

    + +
    + +
    +
    +

    LuCI.ui.Combobox.InitOptionsLuCI.ui.Dropdown.InitOptions

    +
    + + +
    +
    + +
    +

    Comboboxes support the same properties as +Dropdown.InitOptions but enforce +specific values for the following properties:

    +
    + + + +
    + + +
    Properties:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    multiple + + +boolean + + + + + + false + +

    Since Comboboxes never allow selecting multiple values, this property +is forcibly set to false.

    create + + +boolean + + + + + + true + +

    Since Comboboxes always allow custom choice values, this property is +forcibly set to true.

    optional + + +boolean + + + + + + true + +

    Since Comboboxes are always optional, this property is forcibly set to +true.

    + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + +
    + + + + + +
    + +
    + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/jsapi/LuCI.ui.Dropdown.html b/docs/jsapi/LuCI.ui.Dropdown.html new file mode 100644 index 000000000..319c10f69 --- /dev/null +++ b/docs/jsapi/LuCI.ui.Dropdown.html @@ -0,0 +1,3964 @@ + + + + + Class: Dropdown + + + + + + + + + + + + + + + + + +
    + + +
    +

    Class: Dropdown

    + + + + +
    + +
    +

    + LuCI.ui. + + Dropdown +

    + +

    The Dropdown class implements a rich, stylable dropdown menu which +supports non-text choice labels.

    +

    UI widget instances are usually not supposed to be created by view code +directly, instead they're implicitely created by LuCI.form when +instantiating CBI forms.

    +

    This class is automatically instantiated as part of LuCI.ui. To use it +in views, use 'require ui' and refer to ui.Dropdown. To import it in +external JavaScript, use L.require("ui").then(...) and access the +Dropdown property of the class instance value.

    + +
    + +
    +
    + + + + +
    +
    + + + + + +
    + + +
    +
    + + +
    +

    Instantiate a rich dropdown choice widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    value + + +string +| + +Array.<string> + + + + + + null + + + + + optional + + + + + +

    The initial input value(s).

    choices + + +Object.<string, *> + + + + + + + + + + + + +

    Object containing the selectable choices of the widget. The object keys +serve as values for the different choices while the values are used as +choice labels.

    options + + +LuCI.ui.Dropdown.InitOptions + + + + + + + + + optional + + + + + +

    Object describing the widget specific options to initialize the dropdown.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + +

    Methods

    + +
    + +
    +
    +

    + + addChoices(values, labels) +

    + + + + +
    + + +
    +
    + + +
    +

    Add new choices to the dropdown menu.

    +

    This function adds further choices to an existing dropdown menu, +ignoring choice values which are already present.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    values + + +Array.<string> + + + + + +

    The choice values to add to the dropdown widget.

    labels + + +Object.<string, *> + + + + + +

    The choice label values to use when adding dropdown choices. If no +label is found for a particular choice value, the value itself is used +as label text. Choice labels may be any valid value accepted by +LuCI.dom#content.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + clearChoices(reset_value) +

    + + + + +
    + + +
    +
    + + +
    +

    Remove all existing choices from the dropdown menu.

    +

    This function removes all preexisting dropdown choices from the widget, +keeping only choices currently being selected unless reset_values is +given, in which case all choices and deselected and removed.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    reset_value + + +boolean + + + + + + false + + + + + optional + + + + + +

    If set to true, deselect and remove selected choices as well instead +of keeping them.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + closeAllDropdowns() +

    + + + + +
    + + +
    +
    + + +
    +

    Close all open dropdown widgets in the current document.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + getValue(){string|Array.<string>|null} +

    + + + + +
    + + +
    +
    + + +
    +

    Read the current value of the input widget.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + string + | + + Array.<string> + | + + null + + + The current value of the input element. For simple inputs like text +fields or selects, the return value type will be a - possibly empty - +string. Complex widgets such as DynamicList instances may result in +an array of strings or null for unset values.
    + + + + +
    + + + +
    +
    +

    + + inherited + + isValid(){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Check whether the current input value is valid.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true if the current input value is valid or false if it does +not meet the validation constraints.
    + + + + +
    + + + +
    +
    +

    + + inherited + + registerEvents(targetNode, synevent, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Dispatch a custom (synthetic) event in response to received events.

    +

    Sets up event handlers on the given target DOM node for the given event +names that dispatch a custom event of the given type to the widget root +DOM node.

    +

    The primary purpose of this function is to set up a series of custom +uniform standard events such as widget-update, validation-success, +validation-failure etc. which are triggered by various different +widget specific native DOM events.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + +

    Specifies the DOM node on which the native event listeners should be +registered.

    synevent + + +string + + + + + +

    The name of the custom event to dispatch to the widget root DOM node.

    events + + +Array.<string> + + + + + +

    The native DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + render(){Node} +

    + + + + +
    + + +
    +
    + + +
    +

    Render the widget, setup event listeners and return resulting markup.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Node + + + Returns a DOM Node or DocumentFragment containing the rendered +widget markup.
    + + + + +
    + + + +
    +
    +

    + + inherited + + setChangeEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may change the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to change completely, such as +change events in a select menu. In contrast to update events, such +change events will not trigger input value validation but they may cause +field dependencies to get re-evaluated and will mark the input widget +as dirty.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setUpdateEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may update the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to update, such as keyup or +onclick events. In contrast to change events, such update events will +trigger input value validation.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setValue(value) +

    + + + + +
    + + +
    +
    + + +
    +

    Set the current value of the input widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    value + + +string +| + +Array.<string> +| + +null + + + + + +

    The value to set the input element to. For simple inputs like text +fields or selects, the value should be a - possibly empty - string. +Complex widgets such as DynamicList instances may accept string array +or null values.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + triggerValidation() +

    + + + + +
    + + +
    +
    + + +
    +

    Force validation of the current input value.

    +

    Usually input validation is automatically triggered by various DOM events +bound to the input widget. In some cases it is required though to manually +trigger validation runs, e.g. when programmatically altering values.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + +
    + + + +

    Type Definitions

    + +
    + +
    +
    +

    LuCI.ui.Dropdown.InitOptionsLuCI.ui.AbstractElement.InitOptions

    +
    + + +
    +
    + +
    +

    In addition to the AbstractElement.InitOptions +the following properties are recognized:

    +
    + + + +
    + + +
    Properties:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeArgumentDefaultDescription
    optional + + +boolean + + + + + + <optional>
    + + + +
    + + true + +

    Specifies whether the dropdown selection is optional. In contrast to +other widgets, the optional constraint of dropdowns works differently; +instead of marking the widget invalid on empty values when set to false, +the user is not allowed to deselect all choices.

    +

    For single value dropdowns that means that no empty "please select" +choice is offered and for multi value dropdowns, the last selected choice +may not be deselected without selecting another choice first.

    multiple + + +boolean + + + + + + <optional>
    + + + +
    + +

    Specifies whether multiple choice values may be selected. It defaults +to true when an array is passed as input value to the constructor.

    sort + + +boolean +| + +Array.<string> + + + + + + <optional>
    + + + +
    + + false + +

    Specifies if and how to sort choice values. If set to true, the choice +values will be sorted alphabetically. If set to an array of strings, the +choice sort order is derived from the array.

    select_placeholder + + +string + + + + + + <optional>
    + + + +
    + + -- Please choose -- + +

    Specifies a placeholder text which is displayed when no choice is +selected yet.

    custom_placeholder + + +string + + + + + + <optional>
    + + + +
    + + -- custom -- + +

    Specifies a placeholder text which is displayed in the text input +field allowing to enter custom choice values. Only applicable if the +create option is set to true.

    create + + +boolean + + + + + + <optional>
    + + + +
    + + false + +

    Specifies whether custom choices may be entered into the dropdown +widget.

    create_query + + +string + + + + + + <optional>
    + + + +
    + + .create-item-input + +

    Specifies a CSS selector expression used to find the input element +which is used to enter custom choice values. This should not normally +be used except by widgets derived from the Dropdown class.

    create_template + + +string + + + + + + <optional>
    + + + +
    + + script[type="item-template"] + +

    Specifies a CSS selector expression used to find an HTML element +serving as template for newly added custom choice values.

    +

    Any {{value}} placeholder string within the template elements text +content will be replaced by the user supplied choice value, the +resulting string is parsed as HTML and appended to the end of the +choice list. The template markup may specify one HTML element with a +data-label-placeholder attribute which is replaced by a matching +label value from the choices object or with the user supplied value +itself in case choices contains no matching choice label.

    +

    If the template element is not found or if no create_template selector +expression is specified, the default markup for newly created elements is +<li data-value="{{value}}"><span data-label-placeholder="true" /></li>.

    create_markup + + +string + + + + + + <optional>
    + + + +
    + +

    This property allows specifying the markup for custom choices directly +instead of referring to a template element through CSS selectors.

    +

    Apart from that it works exactly like create_template.

    display_items + + +number + + + + + + <optional>
    + + + +
    + + 3 + +

    Specifies the maximum amount of choice labels that should be shown in +collapsed dropdown state before further selected choices are cut off.

    +

    Only applicable when multiple is true.

    dropdown_items + + +number + + + + + + <optional>
    + + + +
    + + -1 + +

    Specifies the maximum amount of choices that should be shown when the +dropdown is open. If the amount of available choices exceeds this number, +the dropdown area must be scrolled to reach further items.

    +

    If set to -1, the dropdown menu will attempt to show all choice values +and only resort to scrolling if the amount of choices exceeds the available +screen space above and below the dropdown widget.

    placeholder + + +string + + + + + + <optional>
    + + + +
    + +

    This property serves as a shortcut to set both select_placeholder and +custom_placeholder. Either of these properties will fallback to +placeholder if not specified.

    readonly + + +boolean + + + + + + <optional>
    + + + +
    + + false + +

    Specifies whether the custom choice input field should be rendered +readonly. Only applicable when create is true.

    maxlength + + +number + + + + + + <optional>
    + + + +
    + +

    Specifies the HTML maxlength attribute to set on the custom choice +<input> element. Note that this a legacy property that exists for +compatibility reasons. It is usually better to maxlength(N) validation +expression. Only applicable when create is true.

    + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + +
    + + + + + +
    + +
    + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/jsapi/LuCI.ui.DynamicList.html b/docs/jsapi/LuCI.ui.DynamicList.html new file mode 100644 index 000000000..cd991386c --- /dev/null +++ b/docs/jsapi/LuCI.ui.DynamicList.html @@ -0,0 +1,3341 @@ + + + + + Class: DynamicList + + + + + + + + + + + + + + + + + +
    + + +
    +

    Class: DynamicList

    + + + + +
    + +
    +

    + LuCI.ui. + + DynamicList +

    + +

    The DynamicList class implements a widget which allows the user to specify +an arbitrary amount of input values, either from free formed text input or +from a set of predefined choices.

    +

    UI widget instances are usually not supposed to be created by view code +directly, instead they're implicitely created by LuCI.form when +instantiating CBI forms.

    +

    This class is automatically instantiated as part of LuCI.ui. To use it +in views, use 'require ui' and refer to ui.DynamicList. To import it in +external JavaScript, use L.require("ui").then(...) and access the +DynamicList property of the class instance value.

    + +
    + +
    +
    + + + + +
    +
    +

    + + new LuCI.ui.DynamicList(value, choices, options) +

    + + + + +
    + + +
    +
    + + +
    +

    Instantiate a dynamic list widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    value + + +string +| + +Array.<string> + + + + + + null + + + + + optional + + + + + +

    The initial input value(s).

    choices + + +Object.<string, *> + + + + + + + + + optional + + + + + +

    Object containing the selectable choices of the widget. The object keys +serve as values for the different choices while the values are used as +choice labels. If omitted, no default choices are presented to the user, +instead a plain text input field is rendered allowing the user to add +arbitrary values to the dynamic list.

    options + + +LuCI.ui.DynamicList.InitOptions + + + + + + + + + optional + + + + + +

    Object describing the widget specific options to initialize the dynamic list.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + +

    Methods

    + +
    + +
    +
    +

    + + addChoices(values, labels) +

    + + + + +
    + + +
    +
    + + +
    +

    Add new suggested choices to the dynamic list.

    +

    This function adds further choices to an existing dynamic list, +ignoring choice values which are already present.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    values + + +Array.<string> + + + + + +

    The choice values to add to the dynamic lists suggestion dropdown.

    labels + + +Object.<string, *> + + + + + +

    The choice label values to use when adding suggested choices. If no +label is found for a particular choice value, the value itself is used +as label text. Choice labels may be any valid value accepted by +LuCI.dom#content.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + clearChoices() +

    + + + + +
    + + +
    +
    + + +
    +

    Remove all existing choices from the dynamic list.

    +

    This function removes all preexisting suggested choices from the widget.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + getValue(){string|Array.<string>|null} +

    + + + + +
    + + +
    +
    + + +
    +

    Read the current value of the input widget.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + string + | + + Array.<string> + | + + null + + + The current value of the input element. For simple inputs like text +fields or selects, the return value type will be a - possibly empty - +string. Complex widgets such as DynamicList instances may result in +an array of strings or null for unset values.
    + + + + +
    + + + +
    +
    +

    + + inherited + + isValid(){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Check whether the current input value is valid.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true if the current input value is valid or false if it does +not meet the validation constraints.
    + + + + +
    + + + +
    +
    +

    + + inherited + + registerEvents(targetNode, synevent, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Dispatch a custom (synthetic) event in response to received events.

    +

    Sets up event handlers on the given target DOM node for the given event +names that dispatch a custom event of the given type to the widget root +DOM node.

    +

    The primary purpose of this function is to set up a series of custom +uniform standard events such as widget-update, validation-success, +validation-failure etc. which are triggered by various different +widget specific native DOM events.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + +

    Specifies the DOM node on which the native event listeners should be +registered.

    synevent + + +string + + + + + +

    The name of the custom event to dispatch to the widget root DOM node.

    events + + +Array.<string> + + + + + +

    The native DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + render(){Node} +

    + + + + +
    + + +
    +
    + + +
    +

    Render the widget, setup event listeners and return resulting markup.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Node + + + Returns a DOM Node or DocumentFragment containing the rendered +widget markup.
    + + + + +
    + + + +
    +
    +

    + + inherited + + setChangeEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may change the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to change completely, such as +change events in a select menu. In contrast to update events, such +change events will not trigger input value validation but they may cause +field dependencies to get re-evaluated and will mark the input widget +as dirty.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setUpdateEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may update the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to update, such as keyup or +onclick events. In contrast to change events, such update events will +trigger input value validation.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setValue(value) +

    + + + + +
    + + +
    +
    + + +
    +

    Set the current value of the input widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    value + + +string +| + +Array.<string> +| + +null + + + + + +

    The value to set the input element to. For simple inputs like text +fields or selects, the value should be a - possibly empty - string. +Complex widgets such as DynamicList instances may accept string array +or null values.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + triggerValidation() +

    + + + + +
    + + +
    +
    + + +
    +

    Force validation of the current input value.

    +

    Usually input validation is automatically triggered by various DOM events +bound to the input widget. In some cases it is required though to manually +trigger validation runs, e.g. when programmatically altering values.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + +
    + + + +

    Type Definitions

    + +
    + +
    +
    +

    LuCI.ui.DynamicList.InitOptionsLuCI.ui.Dropdown.InitOptions

    +
    + + +
    +
    + +
    +

    In case choices are passed to the dynamic list contructor, the widget +supports the same properties as Dropdown.InitOptions +but enforces specific values for some dropdown properties.

    +
    + + + +
    + + +
    Properties:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    multiple + + +boolean + + + + + + false + +

    Since dynamic lists never allow selecting multiple choices when adding +another list item, this property is forcibly set to false.

    optional + + +boolean + + + + + + true + +

    Since dynamic lists use an embedded dropdown to present a list of +predefined choice values, the dropdown must be made optional to allow +it to remain unselected.

    + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + +
    + + + + + +
    + +
    + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/jsapi/LuCI.ui.FileUpload.html b/docs/jsapi/LuCI.ui.FileUpload.html new file mode 100644 index 000000000..9fbf9709e --- /dev/null +++ b/docs/jsapi/LuCI.ui.FileUpload.html @@ -0,0 +1,3190 @@ + + + + + Class: FileUpload + + + + + + + + + + + + + + + + + +
    + + +
    +

    Class: FileUpload

    + + + + +
    + +
    +

    + LuCI.ui. + + FileUpload +

    + +

    The FileUpload class implements a widget which allows the user to upload, +browse, select and delete files beneath a predefined remote directory.

    +

    UI widget instances are usually not supposed to be created by view code +directly, instead they're implicitely created by LuCI.form when +instantiating CBI forms.

    +

    This class is automatically instantiated as part of LuCI.ui. To use it +in views, use 'require ui' and refer to ui.FileUpload. To import it in +external JavaScript, use L.require("ui").then(...) and access the +FileUpload property of the class instance value.

    + +
    + +
    +
    + + + + +
    +
    +

    + + new LuCI.ui.FileUpload(value, options) +

    + + + + +
    + + +
    +
    + + +
    +

    Instantiate a file upload widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    value + + +string +| + +Array.<string> + + + + + + null + + + + + optional + + + + + +

    The initial input value.

    options + + +LuCI.ui.DynamicList.InitOptions + + + + + + + + + optional + + + + + +

    Object describing the widget specific options to initialize the file +upload control.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + +

    Methods

    + +
    + +
    +
    +

    + + inherited + + getValue(){string|Array.<string>|null} +

    + + + + +
    + + +
    +
    + + +
    +

    Read the current value of the input widget.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + string + | + + Array.<string> + | + + null + + + The current value of the input element. For simple inputs like text +fields or selects, the return value type will be a - possibly empty - +string. Complex widgets such as DynamicList instances may result in +an array of strings or null for unset values.
    + + + + +
    + + + +
    +
    +

    + + inherited + + isValid(){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Check whether the current input value is valid.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true if the current input value is valid or false if it does +not meet the validation constraints.
    + + + + +
    + + + +
    +
    +

    + + inherited + + registerEvents(targetNode, synevent, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Dispatch a custom (synthetic) event in response to received events.

    +

    Sets up event handlers on the given target DOM node for the given event +names that dispatch a custom event of the given type to the widget root +DOM node.

    +

    The primary purpose of this function is to set up a series of custom +uniform standard events such as widget-update, validation-success, +validation-failure etc. which are triggered by various different +widget specific native DOM events.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + +

    Specifies the DOM node on which the native event listeners should be +registered.

    synevent + + +string + + + + + +

    The name of the custom event to dispatch to the widget root DOM node.

    events + + +Array.<string> + + + + + +

    The native DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + render(){Node} +

    + + + + +
    + + +
    +
    + + +
    +

    Render the widget, setup event listeners and return resulting markup.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Node + + + Returns a DOM Node or DocumentFragment containing the rendered +widget markup.
    + + + + +
    + + + +
    +
    +

    + + inherited + + setChangeEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may change the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to change completely, such as +change events in a select menu. In contrast to update events, such +change events will not trigger input value validation but they may cause +field dependencies to get re-evaluated and will mark the input widget +as dirty.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setUpdateEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may update the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to update, such as keyup or +onclick events. In contrast to change events, such update events will +trigger input value validation.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setValue(value) +

    + + + + +
    + + +
    +
    + + +
    +

    Set the current value of the input widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    value + + +string +| + +Array.<string> +| + +null + + + + + +

    The value to set the input element to. For simple inputs like text +fields or selects, the value should be a - possibly empty - string. +Complex widgets such as DynamicList instances may accept string array +or null values.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + triggerValidation() +

    + + + + +
    + + +
    +
    + + +
    +

    Force validation of the current input value.

    +

    Usually input validation is automatically triggered by various DOM events +bound to the input widget. In some cases it is required though to manually +trigger validation runs, e.g. when programmatically altering values.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + +
    + + + +

    Type Definitions

    + +
    + +
    +
    +

    LuCI.ui.FileUpload.InitOptionsLuCI.ui.AbstractElement.InitOptions

    +
    + + +
    +
    + +
    +

    In addition to the AbstractElement.InitOptions +the following properties are recognized:

    +
    + + + +
    + + +
    Properties:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeArgumentDefaultDescription
    show_hidden + + +boolean + + + + + + <optional>
    + + + +
    + + false + +

    Specifies whether hidden files should be displayed when browsing remote +files. Note that this is not a security feature, hidden files are always +present in the remote file listings received, this option merely controls +whether they're displayed or not.

    enable_upload + + +boolean + + + + + + <optional>
    + + + +
    + + true + +

    Specifies whether the widget allows the user to upload files. If set to +false, only existing files may be selected. Note that this is not a +security feature. Whether file upload requests are accepted remotely +depends on the ACL setup for the current session. This option merely +controls whether the upload controls are rendered or not.

    enable_remove + + +boolean + + + + + + <optional>
    + + + +
    + + true + +

    Specifies whether the widget allows the user to delete remove files. +If set to false, existing files may not be removed. Note that this is +not a security feature. Whether file delete requests are accepted +remotely depends on the ACL setup for the current session. This option +merely controls whether the file remove controls are rendered or not.

    root_directory + + +string + + + + + + <optional>
    + + + +
    + + /etc/luci-uploads + +

    Specifies the remote directory the upload and file browsing actions take +place in. Browsing to directories outside of the root directory is +prevented by the widget. Note that this is not a security feature. +Whether remote directories are browseable or not solely depends on the +ACL setup for the current session.

    + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + +
    + + + + + +
    + +
    + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/jsapi/LuCI.ui.Hiddenfield.html b/docs/jsapi/LuCI.ui.Hiddenfield.html new file mode 100644 index 000000000..40fe47f8c --- /dev/null +++ b/docs/jsapi/LuCI.ui.Hiddenfield.html @@ -0,0 +1,2944 @@ + + + + + Class: Hiddenfield + + + + + + + + + + + + + + + + + +
    + + +
    +

    Class: Hiddenfield

    + + + + +
    + +
    +

    + LuCI.ui. + + Hiddenfield +

    + +

    The Hiddenfield class implements an HTML <input type="hidden"> field +which allows to store form data without exposing it to the user.

    +

    UI widget instances are usually not supposed to be created by view code +directly, instead they're implicitely created by LuCI.form when +instantiating CBI forms.

    +

    This class is automatically instantiated as part of LuCI.ui. To use it +in views, use 'require ui' and refer to ui.Hiddenfield. To import it in +external JavaScript, use L.require("ui").then(...) and access the +Hiddenfield property of the class instance value.

    + +
    + +
    +
    + + + + +
    +
    +

    + + new LuCI.ui.Hiddenfield(value, options) +

    + + + + +
    + + +
    +
    + + +
    +

    Instantiate a hidden input field widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    value + + +string +| + +Array.<string> + + + + + + null + + + + + optional + + + + + +

    The initial input value.

    options + + +LuCI.ui.AbstractElement.InitOptions + + + + + + + + + optional + + + + + +

    Object describing the widget specific options to initialize the hidden input.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + +

    Methods

    + +
    + +
    +
    +

    + + inherited + + getValue(){string|Array.<string>|null} +

    + + + + +
    + + +
    +
    + + +
    +

    Read the current value of the input widget.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + string + | + + Array.<string> + | + + null + + + The current value of the input element. For simple inputs like text +fields or selects, the return value type will be a - possibly empty - +string. Complex widgets such as DynamicList instances may result in +an array of strings or null for unset values.
    + + + + +
    + + + +
    +
    +

    + + inherited + + isValid(){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Check whether the current input value is valid.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true if the current input value is valid or false if it does +not meet the validation constraints.
    + + + + +
    + + + +
    +
    +

    + + inherited + + registerEvents(targetNode, synevent, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Dispatch a custom (synthetic) event in response to received events.

    +

    Sets up event handlers on the given target DOM node for the given event +names that dispatch a custom event of the given type to the widget root +DOM node.

    +

    The primary purpose of this function is to set up a series of custom +uniform standard events such as widget-update, validation-success, +validation-failure etc. which are triggered by various different +widget specific native DOM events.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + +

    Specifies the DOM node on which the native event listeners should be +registered.

    synevent + + +string + + + + + +

    The name of the custom event to dispatch to the widget root DOM node.

    events + + +Array.<string> + + + + + +

    The native DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + render(){Node} +

    + + + + +
    + + +
    +
    + + +
    +

    Render the widget, setup event listeners and return resulting markup.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Node + + + Returns a DOM Node or DocumentFragment containing the rendered +widget markup.
    + + + + +
    + + + +
    +
    +

    + + inherited + + setChangeEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may change the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to change completely, such as +change events in a select menu. In contrast to update events, such +change events will not trigger input value validation but they may cause +field dependencies to get re-evaluated and will mark the input widget +as dirty.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setUpdateEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may update the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to update, such as keyup or +onclick events. In contrast to change events, such update events will +trigger input value validation.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setValue(value) +

    + + + + +
    + + +
    +
    + + +
    +

    Set the current value of the input widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    value + + +string +| + +Array.<string> +| + +null + + + + + +

    The value to set the input element to. For simple inputs like text +fields or selects, the value should be a - possibly empty - string. +Complex widgets such as DynamicList instances may accept string array +or null values.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + triggerValidation() +

    + + + + +
    + + +
    +
    + + +
    +

    Force validation of the current input value.

    +

    Usually input validation is automatically triggered by various DOM events +bound to the input widget. In some cases it is required though to manually +trigger validation runs, e.g. when programmatically altering values.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + +
    + + + + + + + +
    + +
    + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/jsapi/LuCI.ui.Select.html b/docs/jsapi/LuCI.ui.Select.html new file mode 100644 index 000000000..79e72cead --- /dev/null +++ b/docs/jsapi/LuCI.ui.Select.html @@ -0,0 +1,3293 @@ + + + + + Class: Select + + + + + + + + + + + + + + + + + +
    + + +
    +

    Class: Select

    + + + + +
    + +
    +

    + LuCI.ui. + + Select +

    + +

    The Select class implements either a traditional HTML <select> element +or a group of checkboxes or radio buttons, depending on whether multiple +values are enabled or not.

    +

    UI widget instances are usually not supposed to be created by view code +directly, instead they're implicitely created by LuCI.form when +instantiating CBI forms.

    +

    This class is automatically instantiated as part of LuCI.ui. To use it +in views, use 'require ui' and refer to ui.Select. To import it in +external JavaScript, use L.require("ui").then(...) and access the +Select property of the class instance value.

    + +
    + +
    +
    + + + + +
    +
    +

    + + new LuCI.ui.Select(value, choices, options) +

    + + + + +
    + + +
    +
    + + +
    +

    Instantiate a select dropdown or checkbox/radiobutton group.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    value + + +string +| + +Array.<string> + + + + + + null + + + + + optional + + + + + +

    The initial input value(s).

    choices + + +Object.<string, string> + + + + + + + + + + + + +

    Object containing the selectable choices of the widget. The object keys +serve as values for the different choices while the values are used as +choice labels.

    options + + +LuCI.ui.Select.InitOptions + + + + + + + + + optional + + + + + +

    Object describing the widget specific options to initialize the inputs.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + +

    Methods

    + +
    + +
    +
    +

    + + inherited + + getValue(){string|Array.<string>|null} +

    + + + + +
    + + +
    +
    + + +
    +

    Read the current value of the input widget.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + string + | + + Array.<string> + | + + null + + + The current value of the input element. For simple inputs like text +fields or selects, the return value type will be a - possibly empty - +string. Complex widgets such as DynamicList instances may result in +an array of strings or null for unset values.
    + + + + +
    + + + +
    +
    +

    + + inherited + + isValid(){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Check whether the current input value is valid.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true if the current input value is valid or false if it does +not meet the validation constraints.
    + + + + +
    + + + +
    +
    +

    + + inherited + + registerEvents(targetNode, synevent, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Dispatch a custom (synthetic) event in response to received events.

    +

    Sets up event handlers on the given target DOM node for the given event +names that dispatch a custom event of the given type to the widget root +DOM node.

    +

    The primary purpose of this function is to set up a series of custom +uniform standard events such as widget-update, validation-success, +validation-failure etc. which are triggered by various different +widget specific native DOM events.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + +

    Specifies the DOM node on which the native event listeners should be +registered.

    synevent + + +string + + + + + +

    The name of the custom event to dispatch to the widget root DOM node.

    events + + +Array.<string> + + + + + +

    The native DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + render(){Node} +

    + + + + +
    + + +
    +
    + + +
    +

    Render the widget, setup event listeners and return resulting markup.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Node + + + Returns a DOM Node or DocumentFragment containing the rendered +widget markup.
    + + + + +
    + + + +
    +
    +

    + + inherited + + setChangeEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may change the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to change completely, such as +change events in a select menu. In contrast to update events, such +change events will not trigger input value validation but they may cause +field dependencies to get re-evaluated and will mark the input widget +as dirty.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setUpdateEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may update the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to update, such as keyup or +onclick events. In contrast to change events, such update events will +trigger input value validation.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setValue(value) +

    + + + + +
    + + +
    +
    + + +
    +

    Set the current value of the input widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    value + + +string +| + +Array.<string> +| + +null + + + + + +

    The value to set the input element to. For simple inputs like text +fields or selects, the value should be a - possibly empty - string. +Complex widgets such as DynamicList instances may accept string array +or null values.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + triggerValidation() +

    + + + + +
    + + +
    +
    + + +
    +

    Force validation of the current input value.

    +

    Usually input validation is automatically triggered by various DOM events +bound to the input widget. In some cases it is required though to manually +trigger validation runs, e.g. when programmatically altering values.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + +
    + + + +

    Type Definitions

    + +
    + +
    +
    +

    LuCI.ui.Select.InitOptionsLuCI.ui.AbstractElement.InitOptions

    +
    + + +
    +
    + +
    +

    In addition to the AbstractElement.InitOptions +the following properties are recognized:

    +
    + + + +
    + + +
    Properties:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeArgumentDefaultDescription
    multiple + + +boolean + + + + + + <optional>
    + + + +
    + + false + +

    Specifies whether multiple choice values may be selected.

    widget + + +string + + + + + + <optional>
    + + + +
    + + select + +

    Specifies the kind of widget to render. May be either select or +individual. When set to select an HTML <select> element will be +used, otherwise a group of checkbox or radio button elements is created, +depending on the value of the multiple option.

    orientation + + +string + + + + + + <optional>
    + + + +
    + + horizontal + +

    Specifies whether checkbox / radio button groups should be rendered +in a horizontal or vertical manner. Does not apply to the select +widget type.

    sort + + +boolean +| + +Array.<string> + + + + + + <optional>
    + + + +
    + + false + +

    Specifies if and how to sort choice values. If set to true, the choice +values will be sorted alphabetically. If set to an array of strings, the +choice sort order is derived from the array.

    size + + +number + + + + + + <optional>
    + + + +
    + +

    Specifies the HTML size attribute to set on the <select> element. +Only applicable to the select widget type.

    placeholder + + +string + + + + + + <optional>
    + + + +
    + + -- Please choose -- + +

    Specifies a placeholder text which is displayed when no choice is +selected yet. Only applicable to the select widget type.

    + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + +
    + + + + + +
    + +
    + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/jsapi/LuCI.ui.Textarea.html b/docs/jsapi/LuCI.ui.Textarea.html new file mode 100644 index 000000000..2503a1568 --- /dev/null +++ b/docs/jsapi/LuCI.ui.Textarea.html @@ -0,0 +1,3242 @@ + + + + + Class: Textarea + + + + + + + + + + + + + + + + + +
    + + +
    +

    Class: Textarea

    + + + + +
    + +
    +

    + LuCI.ui. + + Textarea +

    + +

    The Textarea class implements a multiline text area input field.

    +

    UI widget instances are usually not supposed to be created by view code +directly, instead they're implicitely created by LuCI.form when +instantiating CBI forms.

    +

    This class is automatically instantiated as part of LuCI.ui. To use it +in views, use 'require ui' and refer to ui.Textarea. To import it in +external JavaScript, use L.require("ui").then(...) and access the +Textarea property of the class instance value.

    + +
    + +
    +
    + + + + +
    +
    +

    + + new LuCI.ui.Textarea(value, options) +

    + + + + +
    + + +
    +
    + + +
    +

    Instantiate a textarea widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    value + + +string + + + + + + null + + + + + optional + + + + + +

    The initial input value.

    options + + +LuCI.ui.Textarea.InitOptions + + + + + + + + + optional + + + + + +

    Object describing the widget specific options to initialize the input.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + +

    Methods

    + +
    + +
    +
    +

    + + inherited + + getValue(){string|Array.<string>|null} +

    + + + + +
    + + +
    +
    + + +
    +

    Read the current value of the input widget.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + string + | + + Array.<string> + | + + null + + + The current value of the input element. For simple inputs like text +fields or selects, the return value type will be a - possibly empty - +string. Complex widgets such as DynamicList instances may result in +an array of strings or null for unset values.
    + + + + +
    + + + +
    +
    +

    + + inherited + + isValid(){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Check whether the current input value is valid.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true if the current input value is valid or false if it does +not meet the validation constraints.
    + + + + +
    + + + +
    +
    +

    + + inherited + + registerEvents(targetNode, synevent, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Dispatch a custom (synthetic) event in response to received events.

    +

    Sets up event handlers on the given target DOM node for the given event +names that dispatch a custom event of the given type to the widget root +DOM node.

    +

    The primary purpose of this function is to set up a series of custom +uniform standard events such as widget-update, validation-success, +validation-failure etc. which are triggered by various different +widget specific native DOM events.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + +

    Specifies the DOM node on which the native event listeners should be +registered.

    synevent + + +string + + + + + +

    The name of the custom event to dispatch to the widget root DOM node.

    events + + +Array.<string> + + + + + +

    The native DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + render(){Node} +

    + + + + +
    + + +
    +
    + + +
    +

    Render the widget, setup event listeners and return resulting markup.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Node + + + Returns a DOM Node or DocumentFragment containing the rendered +widget markup.
    + + + + +
    + + + +
    +
    +

    + + inherited + + setChangeEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may change the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to change completely, such as +change events in a select menu. In contrast to update events, such +change events will not trigger input value validation but they may cause +field dependencies to get re-evaluated and will mark the input widget +as dirty.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setUpdateEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may update the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to update, such as keyup or +onclick events. In contrast to change events, such update events will +trigger input value validation.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setValue(value) +

    + + + + +
    + + +
    +
    + + +
    +

    Set the current value of the input widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    value + + +string +| + +Array.<string> +| + +null + + + + + +

    The value to set the input element to. For simple inputs like text +fields or selects, the value should be a - possibly empty - string. +Complex widgets such as DynamicList instances may accept string array +or null values.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + triggerValidation() +

    + + + + +
    + + +
    +
    + + +
    +

    Force validation of the current input value.

    +

    Usually input validation is automatically triggered by various DOM events +bound to the input widget. In some cases it is required though to manually +trigger validation runs, e.g. when programmatically altering values.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + +
    + + + +

    Type Definitions

    + +
    + +
    +
    +

    LuCI.ui.Textarea.InitOptionsLuCI.ui.AbstractElement.InitOptions

    +
    + + +
    +
    + +
    +

    In addition to the AbstractElement.InitOptions +the following properties are recognized:

    +
    + + + +
    + + +
    Properties:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeArgumentDefaultDescription
    readonly + + +boolean + + + + + + <optional>
    + + + +
    + + false + +

    Specifies whether the input widget should be rendered readonly.

    placeholder + + +string + + + + + + <optional>
    + + + +
    + +

    Specifies the HTML placeholder attribute which is displayed when the +corresponding <textarea> element is empty.

    monospace + + +boolean + + + + + + <optional>
    + + + +
    + + false + +

    Specifies whether a monospace font should be forced for the textarea +contents.

    cols + + +number + + + + + + <optional>
    + + + +
    + +

    Specifies the HTML cols attribute to set on the corresponding +<textarea> element.

    rows + + +number + + + + + + <optional>
    + + + +
    + +

    Specifies the HTML rows attribute to set on the corresponding +<textarea> element.

    wrap + + +boolean + + + + + + <optional>
    + + + +
    + + false + +

    Specifies whether the HTML wrap attribute should be set.

    + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + +
    + + + + + +
    + +
    + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/jsapi/LuCI.ui.Textfield.html b/docs/jsapi/LuCI.ui.Textfield.html new file mode 100644 index 000000000..e5e2aeb8c --- /dev/null +++ b/docs/jsapi/LuCI.ui.Textfield.html @@ -0,0 +1,3170 @@ + + + + + Class: Textfield + + + + + + + + + + + + + + + + + +
    + + +
    +

    Class: Textfield

    + + + + +
    + +
    +

    + LuCI.ui. + + Textfield +

    + +

    The Textfield class implements a standard single line text input field.

    +

    UI widget instances are usually not supposed to be created by view code +directly, instead they're implicitely created by LuCI.form when +instantiating CBI forms.

    +

    This class is automatically instantiated as part of LuCI.ui. To use it +in views, use 'require ui' and refer to ui.Textfield. To import it in +external JavaScript, use L.require("ui").then(...) and access the +Textfield property of the class instance value.

    + +
    + +
    +
    + + + + +
    +
    +

    + + new LuCI.ui.Textfield(value, options) +

    + + + + +
    + + +
    +
    + + +
    +

    Instantiate a text input widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    value + + +string + + + + + + null + + + + + optional + + + + + +

    The initial input value.

    options + + +LuCI.ui.Textfield.InitOptions + + + + + + + + + optional + + + + + +

    Object describing the widget specific options to initialize the input.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + +
    + + +

    Extends

    + + + + + + + + + + + + + + + +

    Methods

    + +
    + +
    +
    +

    + + inherited + + getValue(){string|Array.<string>|null} +

    + + + + +
    + + +
    +
    + + +
    +

    Read the current value of the input widget.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + string + | + + Array.<string> + | + + null + + + The current value of the input element. For simple inputs like text +fields or selects, the return value type will be a - possibly empty - +string. Complex widgets such as DynamicList instances may result in +an array of strings or null for unset values.
    + + + + +
    + + + +
    +
    +

    + + inherited + + isValid(){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Check whether the current input value is valid.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true if the current input value is valid or false if it does +not meet the validation constraints.
    + + + + +
    + + + +
    +
    +

    + + inherited + + registerEvents(targetNode, synevent, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Dispatch a custom (synthetic) event in response to received events.

    +

    Sets up event handlers on the given target DOM node for the given event +names that dispatch a custom event of the given type to the widget root +DOM node.

    +

    The primary purpose of this function is to set up a series of custom +uniform standard events such as widget-update, validation-success, +validation-failure etc. which are triggered by various different +widget specific native DOM events.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + +

    Specifies the DOM node on which the native event listeners should be +registered.

    synevent + + +string + + + + + +

    The name of the custom event to dispatch to the widget root DOM node.

    events + + +Array.<string> + + + + + +

    The native DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + render(){Node} +

    + + + + +
    + + +
    +
    + + +
    +

    Render the widget, setup event listeners and return resulting markup.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Node + + + Returns a DOM Node or DocumentFragment containing the rendered +widget markup.
    + + + + +
    + + + +
    +
    +

    + + inherited + + setChangeEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may change the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to change completely, such as +change events in a select menu. In contrast to update events, such +change events will not trigger input value validation but they may cause +field dependencies to get re-evaluated and will mark the input widget +as dirty.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setUpdateEvents(targetNode, events) +

    + + + + +
    + + +
    +
    + + +
    +

    Setup listeners for native DOM events that may update the widget value.

    +

    Sets up event handlers on the given target DOM node for the given event +names which may cause the input value to update, such as keyup or +onclick events. In contrast to change events, such update events will +trigger input value validation.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    targetNode + + +Node + + + + + + + + + + +

    Specifies the DOM node on which the event listeners should be registered.

    events + + +string + + + + + + + + + + repeatable + + +

    The DOM events for which event handlers should be registered.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + setValue(value) +

    + + + + +
    + + +
    +
    + + +
    +

    Set the current value of the input widget.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    value + + +string +| + +Array.<string> +| + +null + + + + + +

    The value to set the input element to. For simple inputs like text +fields or selects, the value should be a - possibly empty - string. +Complex widgets such as DynamicList instances may accept string array +or null values.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + inherited + + triggerValidation() +

    + + + + +
    + + +
    +
    + + +
    +

    Force validation of the current input value.

    +

    Usually input validation is automatically triggered by various DOM events +bound to the input widget. In some cases it is required though to manually +trigger validation runs, e.g. when programmatically altering values.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + +
    + + + +

    Type Definitions

    + +
    + +
    +
    +

    LuCI.ui.Textfield.InitOptionsLuCI.ui.AbstractElement.InitOptions

    +
    + + +
    +
    + +
    +

    In addition to the AbstractElement.InitOptions +the following properties are recognized:

    +
    + + + +
    + + +
    Properties:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeArgumentDefaultDescription
    password + + +boolean + + + + + + <optional>
    + + + +
    + + false + +

    Specifies whether the input should be rendered as concealed password field.

    readonly + + +boolean + + + + + + <optional>
    + + + +
    + + false + +

    Specifies whether the input widget should be rendered readonly.

    maxlength + + +number + + + + + + <optional>
    + + + +
    + +

    Specifies the HTML maxlength attribute to set on the corresponding +<input> element. Note that this a legacy property that exists for +compatibility reasons. It is usually better to maxlength(N) validation +expression.

    placeholder + + +string + + + + + + <optional>
    + + + +
    + +

    Specifies the HTML placeholder attribute which is displayed when the +corresponding <input> element is empty.

    + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + +
    + + + + + +
    + +
    + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/jsapi/LuCI.ui.changes.html b/docs/jsapi/LuCI.ui.changes.html new file mode 100644 index 000000000..d7b75c6a8 --- /dev/null +++ b/docs/jsapi/LuCI.ui.changes.html @@ -0,0 +1,2354 @@ + + + + + Class: changes + + + + + + + + + + + + + + + + + +
    + + +
    +

    Class: changes

    + + + + +
    + +
    +

    + LuCI.ui. + + changes +

    + +

    The changes class encapsulates logic for visualizing, applying, +confirming and reverting staged UCI changesets.

    +

    This class is automatically instantiated as part of LuCI.ui. To use it +in views, use 'require ui' and refer to ui.changes. To import it in +external JavaScript, use L.require("ui").then(...) and access the +changes property of the class instance value.

    + +
    + +
    +
    + + + + +
    +
    +

    + + new LuCI.ui.changes() +

    + + + + +
    + + +
    +
    + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + + +

    Methods

    + +
    + +
    +
    +

    + + apply(checked) +

    + + + + +
    + + +
    +
    + + +
    +

    Apply the staged configuration changes.

    +

    Start applying staged configuration changes and open a modal dialog +with a progress indication to prevent interaction with the view +during the apply process. The modal dialog will be automatically +closed and the current view reloaded once the apply process is +complete.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    checked + + +boolean + + + + + + false + + + + + optional + + + + + +

    Whether to perform a checked (true) configuration apply or an +unchecked (false) one. +In case of a checked apply, the configuration changes must be +confirmed within a specific time interval, otherwise the device +will begin to roll back the changes in order to restore the previous +settings.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + displayChanges() +

    + + + + +
    + + +
    +
    + + +
    +

    Display the current changelog.

    +

    Open a modal dialog visualizing the currently staged UCI changes +and offer options to revert or apply the shown changes.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + renderChangeIndicator(changes) +

    + + + + +
    + + +
    +
    + + +
    +

    Update the change count indicator.

    +

    This function updates the UCI change count indicator from the given +UCI changeset structure.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    changes + + +Object.<string, Array.<LuCI.uci.ChangeRecord>> + + + + + +

    The UCI changeset to count.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + revert() +

    + + + + +
    + + +
    +
    + + +
    +

    Revert the staged configuration changes.

    +

    Start reverting staged configuration changes and open a modal dialog +with a progress indication to prevent interaction with the view +during the revert process. The modal dialog will be automatically +closed and the current view reloaded once the revert process is +complete.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + setIndicator(numChanges) +

    + + + + +
    + + +
    +
    + + +
    +

    Set the change count indicator.

    +

    This function updates or hides the UCI change count indicator, +depending on the passed change count. When the count is greater +than 0, the change indicator is displayed or updated, otherwise it +is removed.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    numChanges + + +number + + + + + +

    The number of changes to indicate.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + +
    + + + + + + + +
    + +
    + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/jsapi/LuCI.ui.html b/docs/jsapi/LuCI.ui.html new file mode 100644 index 000000000..73feafbe9 --- /dev/null +++ b/docs/jsapi/LuCI.ui.html @@ -0,0 +1,4292 @@ + + + + + Class: ui + + + + + + + + + + + + + + + + + +
    + + +
    +

    Class: ui

    + + + + +
    + +
    +

    + LuCI. + + ui +

    + +

    Provides high level UI helper functionality. +To import the class in views, use 'require ui', to import it in +external JavaScript, use L.require("ui").then(...).

    + +
    + +
    +
    + + + + +
    +
    +

    + + new LuCI.ui() +

    + + + + +
    + + +
    +
    + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + +
    + + + + + + + + +

    Classes

    + +
    +
    AbstractElement
    +
    + +
    changes
    +
    + +
    Checkbox
    +
    + +
    Combobox
    +
    + +
    ComboButton
    +
    + +
    Dropdown
    +
    + +
    DynamicList
    +
    + +
    FileUpload
    +
    + +
    Hiddenfield
    +
    + +
    Select
    +
    + +
    tabs
    +
    + +
    Textarea
    +
    + +
    Textfield
    +
    +
    + + + + + + + +

    Methods

    + +
    + +
    +
    +

    + + addNotification(title, contents, classes){Node} +

    + + + + +
    + + +
    +
    + + +
    +

    Add a notification banner at the top of the current view.

    +

    A notification banner is an alert message usually displayed at the +top of the current view, spanning the entire availibe width. +Notification banners will stay in place until dismissed by the user. +Multiple banners may be shown at the same time.

    +

    Additional CSS class names may be passed to influence the appearence of +the banner. Valid values for the classes depend on the underlying theme.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    title + + +string + + + + + + + optional + + + + + +

    The title of the notification banner. If null, no title element +will be rendered.

    contents + + +* + + + + + + + + + + +

    The contents to add to the notification banner. This should be a DOM +node or a document fragment in most cases. The value is passed as-is +to the L.dom.content() function - refer to its documentation for +applicable values.

    classes + + +string + + + + + + + optional + + + + + repeatable + + +

    A number of extra CSS class names which are set on the notification +banner element.

    + + + +
    + + + + + + + + + + + + + + + + + + + +
    See:
    +
    +
      +
    • LuCI.dom.content
    • +
    +
    + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Node + + + Returns a DOM Node representing the notification banner element.
    + + + + +
    + + + +
    +
    +

    + + addValidator(field, type, optional, vfunc, events){function} +

    + + + + +
    + + +
    +
    + + +
    +

    Add validation constraints to an input element.

    +

    Compile the given type expression and optional validator function into +a validation function and bind it to the specified input element events.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    field + + +Node + + + + + + + + + + + + +

    The DOM input element node to bind the validation constraints to.

    type + + +string + + + + + + + + + + + + +

    The datatype specification to describe validation constraints. +Refer to the LuCI.validation class documentation for details.

    optional + + +boolean + + + + + + false + + + + + optional + + + + + +

    Specifies whether empty values are allowed (true) or not (false). +If an input element is not marked optional it must not be empty, +otherwise it will be marked as invalid.

    vfunc + + +function + + + + + + + + + optional + + + + + +

    Specifies a custom validation function which is invoked after the +other validation constraints are applied. The validation must return +true to accept the passed value. Any other return type is converted +to a string and treated as validation error message.

    events + + +string + + + + + + blur, keyup + + + + + optional + + + + + repeatable + + +

    The list of events to bind. Each received event will trigger a field +validation. If omitted, the keyup and blur events are bound by +default.

    + + + +
    + + + + + + + + + + + + + + + + + + + +
    See:
    +
    +
      +
    • LuCI.validation
    • +
    +
    + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + function + + + Returns the compiled validator function which can be used to manually +trigger field validation or to bind it to further events.
    + + + + +
    + + + +
    +
    +

    + + awaitReconnect(hosts) +

    + + + + +
    + + +
    +
    + + +
    +

    Wait for device to come back online and reconnect to it.

    +

    Poll each given hostname or IP address and navigate to it as soon as +one of the addresses becomes reachable.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    hosts + + +string + + + + + + [window.location.host] + + + + + optional + + + + + repeatable + + +

    The list of IP addresses and host names to check for reachability. +If omitted, the current value of window.location.host is used by +default.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + createHandlerFn(ctx, fn, extra_args){function|null} +

    + + + + +
    + + +
    +
    + + +
    +

    Create a pre-bound event handler function.

    +

    Generate and bind a function suitable for use in event handlers. The +generated function automatically disables the event source element +and adds an active indication to it by adding appropriate CSS classes.

    +

    It will also await any promises returned by the wrapped function and +re-enable the source element after the promises ran to completion.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    ctx + + +* + + + + + + + + + + +

    The this context to use for the wrapped function.

    fn + + +function +| + +string + + + + + + + + + + +

    Specifies the function to wrap. In case of a function value, the +function is used as-is. If a string is specified instead, it is looked +up in ctx to obtain the function to wrap. In both cases the bound +function will be invoked with ctx as this context

    extra_args + + +* + + + + + + + + + + repeatable + + +

    Any further parameter as passed as-is to the bound event handler +function in the same order as passed to createHandlerFn().

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + function + | + + null + + + Returns the pre-bound handler function which is suitable to be passed +to addEventListener(). Returns null if the given fn argument is +a string which could not be found in ctx or if ctx[fn] is not a +valid function value.
    + + + + +
    + + + +
    +
    +

    + + hideIndicator(id){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Remove an header area indicator.

    +

    This function removes the given indicator label from the header indicator +area. When the given indicator is not found, this function does nothing.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    id + + +string + + + + + +

    The ID of the indicator to remove.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true when the indicator has been removed or false when the +requested indicator was not found.
    + + + + +
    + + + +
    +
    +

    + + hideModal() +

    + + + + +
    + + +
    +
    + + +
    +

    Close the open modal overlay dialog.

    +

    This function will close an open modal dialog and restore the normal view +behaviour. It has no effect if no modal dialog is currently open.

    +

    Note that this function is stand-alone, it does not rely on this and +will not invoke other class functions so it suitable to be used as event +handler as-is without the need to bind it first.

    +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + itemlist(node, items, separators){Node} +

    + + + + +
    + + +
    +
    + + +
    +

    Formats a series of label/value pairs into list-like markup.

    +

    This function transforms a flat array of alternating label and value +elements into a list-like markup, using the values in separators as +separators and appends the resulting nodes to the given parent DOM node.

    +

    Each label is suffixed with : and wrapped into a <strong> tag, the +<strong> element and the value corresponding to the label are +subsequently wrapped into a <span class="nowrap"> element.

    +

    The resulting <span> element tuples are joined by the given separators +to form the final markup which is appened to the given parent DOM node.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    node + + +Node + + + + + + + + + + + + +

    The parent DOM node to append the markup to. Any previous child elements +will be removed.

    items + + +Array.<*> + + + + + + + + + + + + +

    An alternating array of labels and values. The label values will be +converted to plain strings, the values are used as-is and may be of +any type accepted by LuCI.dom.content().

    separators + + +* +| + +Array.<*> + + + + + + [E('br')] + + + + + optional + + + + + +

    A single value or an array of separator values to separate each +label/value pair with. The function will cycle through the separators +when joining the pairs. If omitted, the default separator is a sole HTML +<br> element. Separator values are used as-is and may be of any type +accepted by LuCI.dom.content().

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Node + + + Returns the parent DOM node the formatted markup has been added to.
    + + + + +
    + + + +
    +
    +

    + + pingDevice(proto, host){Promise.<Event>} +

    + + + + +
    + + +
    +
    + + +
    +

    Perform a device connectivity test.

    +

    Attempt to fetch a well known ressource from the remote device via HTTP +in order to test connectivity. This function is mainly useful to wait +for the router to come back online after a reboot or reconfiguration.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    proto + + +string + + + + + + http + + + + + optional + + + + + +

    The protocol to use for fetching the resource. May be either http +(the default) or https.

    host + + +string + + + + + + window.location.host + + + + + optional + + + + + +

    Override the host address to probe. By default the current host as seen +in the address bar is probed.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Promise.<Event> + + + Returns a promise resolving to a load event in case the device is +reachable or rejecting with an error event in case it is not reachable +or rejecting with null when the connectivity check timed out.
    + + + + +
    + + + +
    +
    +

    + + showIndicator(id, label, handler, style){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Display or update an header area indicator.

    +

    An indicator is a small label displayed in the header area of the screen +providing few amounts of status information such as item counts or state +toggle indicators.

    +

    Multiple indicators may be shown at the same time and indicator labels +may be made clickable to display extended information or to initiate +further actions.

    +

    Indicators can either use a default active or a less accented inactive +style which is useful for indicators representing state toggles.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    id + + +string + + + + + + + + + + + + +

    The ID of the indicator. If an indicator with the given ID already exists, +it is updated with the given label and style.

    label + + +string + + + + + + + + + + + + +

    The text to display in the indicator label.

    handler + + +function + + + + + + + + + optional + + + + + +

    A handler function to invoke when the indicator label is clicked/touched +by the user. If omitted, the indicator is not clickable/touchable.

    +

    Note that this parameter only applies to new indicators, when updating +existing labels it is ignored.

    style + + +string + + + + + + active + + + + + optional + + + + + +

    The indicator style to use. May be either active or inactive.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true when the indicator has been updated or false when no +changes were made.
    + + + + +
    + + + +
    +
    +

    + + showModal(title, contents, classes){Node} +

    + + + + +
    + + +
    +
    + + +
    +

    Display a modal overlay dialog with the specified contents.

    +

    The modal overlay dialog covers the current view preventing interaction +with the underlying view contents. Only one modal dialog instance can +be opened. Invoking showModal() while a modal dialog is already open will +replace the open dialog with a new one having the specified contents.

    +

    Additional CSS class names may be passed to influence the appearence of +the dialog. Valid values for the classes depend on the underlying theme.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    title + + +string + + + + + + + optional + + + + + +

    The title of the dialog. If null, no title element will be rendered.

    contents + + +* + + + + + + + + + + +

    The contents to add to the modal dialog. This should be a DOM node or +a document fragment in most cases. The value is passed as-is to the +L.dom.content() function - refer to its documentation for applicable +values.

    classes + + +string + + + + + + + optional + + + + + repeatable + + +

    A number of extra CSS class names which are set on the modal dialog +element.

    + + + +
    + + + + + + + + + + + + + + + + + + + +
    See:
    +
    +
      +
    • LuCI.dom.content
    • +
    +
    + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Node + + + Returns a DOM Node representing the modal dialog element.
    + + + + +
    + + + +
    +
    +

    + + uploadFile(path, progessStatusNode){Promise.<LuCI.ui.FileUploadReply>} +

    + + + + +
    + + +
    +
    + + +
    +

    Display a modal file upload prompt.

    +

    This function opens a modal dialog prompting the user to select and +upload a file to a predefined remote destination path.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    path + + +string + + + + + + + + + + +

    The remote file path to upload the local file to.

    progessStatusNode + + +Node + + + + + + + optional + + + + + +

    An optional DOM text node whose content text is set to the progress +percentage value during file upload.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Promise.<LuCI.ui.FileUploadReply> + + + Returns a promise resolving to a file upload status object on success +or rejecting with an error in case the upload failed or has been +cancelled by the user.
    + + + + +
    + +
    + + + +

    Type Definitions

    + +
    + +
    +
    +

    LuCI.ui.FileUploadReplyObject

    +
    + + +
    +
    + + + +
    + + +
    Properties:
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    name + + +string + + + +

    Name of the uploaded file without directory components

    size + + +number + + + +

    Size of the uploaded file in bytes

    checksum + + +string + + + +

    The MD5 checksum of the received file data

    sha256sum + + +string + + + +

    The SHA256 checksum of the received file data

    + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    + +
    + + + + + +
    + +
    + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/jsapi/LuCI.ui.tabs.html b/docs/jsapi/LuCI.ui.tabs.html new file mode 100644 index 000000000..ec5f92ffb --- /dev/null +++ b/docs/jsapi/LuCI.ui.tabs.html @@ -0,0 +1,2101 @@ + + + + + Class: tabs + + + + + + + + + + + + + + + + + +
    + + +
    +

    Class: tabs

    + + + + +
    + +
    +

    + LuCI.ui. + + tabs +

    + +

    The tabs class handles tab menu groups used throughout the view area. +It takes care of setting up tab groups, tracking their state and handling +related events.

    +

    This class is automatically instantiated as part of LuCI.ui. To use it +in views, use 'require ui' and refer to ui.tabs. To import it in +external JavaScript, use L.require("ui").then(...) and access the +tabs property of the class instance value.

    + +
    + +
    +
    + + + + +
    +
    +

    + + new LuCI.ui.tabs() +

    + + + + +
    + + +
    +
    + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + +
    + + + + + + + + + + + + + + +

    Methods

    + +
    + +
    +
    +

    + + initTabGroup(panes) +

    + + + + +
    + + +
    +
    + + +
    +

    Initializes a new tab group from the given tab pane collection.

    +

    This function cycles through the given tab pane DOM nodes, extracts +their tab IDs, titles and active states, renders a corresponding +tab menu and prepends it to the tab panes common parent DOM node.

    +

    The tab menu labels will be set to the value of the data-tab-title +attribute of each corresponding pane. The last pane with the +data-tab-active attribute set to true will be selected by default.

    +

    If no pane is marked as active, the first one will be preselected.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    panes + + +Array.<Node> +| + +NodeList + + + + + +

    A collection of tab panes to build a tab group menu for. May be a +plain array of DOM nodes or a NodeList collection, such as the result +of a querySelectorAll() call or the .childNodes property of a +DOM node.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + +
    +
    +

    + + isEmptyPane(pane){boolean} +

    + + + + +
    + + +
    +
    + + +
    +

    Checks whether the given tab pane node is empty.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    pane + + +Node + + + + + +

    The tab pane to check.

    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    Returns:
    + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + boolean + + + Returns true if the pane is empty, else false.
    + + + + +
    + +
    + + + + + + + +
    + +
    + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + \ No newline at end of file diff --git a/docs/jsapi/LuCI.view.html b/docs/jsapi/LuCI.view.html index 3e300f262..200f00ea5 100644 --- a/docs/jsapi/LuCI.view.html +++ b/docs/jsapi/LuCI.view.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1111,7 +1719,7 @@ set of methods to inherit from.

    @@ -1196,7 +1804,7 @@ set of methods to inherit from.

    @@ -1309,7 +1917,7 @@ methods are overwritten with null. @@ -1473,7 +2081,7 @@ is reenabled. @@ -1637,7 +2245,7 @@ is reenabled. @@ -1803,7 +2411,7 @@ is reenabled. @@ -1913,7 +2521,7 @@ the default implementation does nothing.

    @@ -2087,7 +2695,7 @@ to a Node value.
    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/fs.js.html b/docs/jsapi/fs.js.html index 769e3edd8..292a521ba 100644 --- a/docs/jsapi/fs.js.html +++ b/docs/jsapi/fs.js.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1523,7 +2131,7 @@ return FileSystem;
    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/index.html b/docs/jsapi/index.html index c5203a4d2..6760e4bb8 100644 --- a/docs/jsapi/index.html +++ b/docs/jsapi/index.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1112,7 +1720,7 @@
    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/luci.js.html b/docs/jsapi/luci.js.html index c8bf70211..f5e0c81d0 100644 --- a/docs/jsapi/luci.js.html +++ b/docs/jsapi/luci.js.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -3640,6 +4248,9 @@ * be found. */ data: function(node, key, val) { + if (!node || !node.getAttribute) + return null; + var id = node.getAttribute('data-idref'); /* clear all data */ @@ -4067,14 +4678,14 @@ 1: [ _('Apply unchecked') ] }, { classes: { - 0: 'cbi-button cbi-button-apply important', - 1: 'cbi-button cbi-button-negative important' + 0: 'btn cbi-button cbi-button-apply important', + 1: 'btn cbi-button cbi-button-negative important' }, click: L.ui.createHandlerFn(this, 'handleSaveApply') }).render() : E([]); if (this.handleSaveApply || this.handleSave || this.handleReset) { - footer.appendChild(E('div', { 'class': 'cbi-page-actions' }, [ + footer.appendChild(E('div', { 'class': 'cbi-page-actions control-group' }, [ saveApplyBtn, ' ', this.handleSave ? E('button', { 'class': 'cbi-button cbi-button-save', @@ -4249,7 +4860,7 @@
    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/network.js.html b/docs/jsapi/network.js.html index 4a306a23a..07aca9add 100644 --- a/docs/jsapi/network.js.html +++ b/docs/jsapi/network.js.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -2064,47 +2672,52 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ { * `false` if the given network could not be found. */ deleteNetwork: function(name) { - var requireFirewall = Promise.resolve(L.require('firewall')).catch(function() {}); + var requireFirewall = Promise.resolve(L.require('firewall')).catch(function() {}), + network = this.instantiateNetwork(name); return Promise.all([ requireFirewall, initNetworkState() ]).then(function() { var uciInterface = uci.get('network', name); if (uciInterface != null && uciInterface['.type'] == 'interface') { - uci.remove('network', name); + return Promise.resolve(network ? network.deleteConfiguration() : null).then(function() { + uci.remove('network', name); - uci.sections('luci', 'ifstate', function(s) { - if (s.interface == name) - uci.remove('luci', s['.name']); - }); + uci.sections('luci', 'ifstate', function(s) { + if (s.interface == name) + uci.remove('luci', s['.name']); + }); - uci.sections('network', 'alias', function(s) { - if (s.interface == name) - uci.remove('network', s['.name']); - }); + uci.sections('network', 'alias', function(s) { + if (s.interface == name) + uci.remove('network', s['.name']); + }); - uci.sections('network', 'route', function(s) { - if (s.interface == name) - uci.remove('network', s['.name']); - }); + uci.sections('network', 'route', function(s) { + if (s.interface == name) + uci.remove('network', s['.name']); + }); - uci.sections('network', 'route6', function(s) { - if (s.interface == name) - uci.remove('network', s['.name']); - }); + uci.sections('network', 'route6', function(s) { + if (s.interface == name) + uci.remove('network', s['.name']); + }); - uci.sections('wireless', 'wifi-iface', function(s) { - var networks = L.toArray(s.network).filter(function(network) { return network != name }); + uci.sections('wireless', 'wifi-iface', function(s) { + var networks = L.toArray(s.network).filter(function(network) { return network != name }); - if (networks.length > 0) - uci.set('wireless', s['.name'], 'network', networks.join(' ')); - else - uci.unset('wireless', s['.name'], 'network'); - }); + if (networks.length > 0) + uci.set('wireless', s['.name'], 'network', networks.join(' ')); + else + uci.unset('wireless', s['.name'], 'network'); + }); - if (L.firewall) - return L.firewall.deleteNetwork(name).then(function() { return true }); + if (L.firewall) + return L.firewall.deleteNetwork(name).then(function() { return true }); - return true; + return true; + }).catch(function() { + return false; + }); } return false; @@ -3383,6 +3996,23 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ { return null; }, + /** + * Check function for the protocol handler if a new interface is createable. + * + * This function should be overwritten by protocol specific subclasses. + * + * @abstract + * + * @param {string} ifname + * The name of the interface to be created. + * + * @returns {Promise<null|string>} + * Returns `null` if new interface is createable, else returns (error) message. + */ + isCreateable: function(ifname) { + return Promise.resolve(null); + }, + /** * Checks whether the protocol functionality is installed. * @@ -3724,7 +4354,26 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ { } return false; - } + }, + + /** + * Cleanup related configuration entries. + * + * This function will be invoked if an interface is about to be removed + * from the configuration and is responsible for performing any required + * cleanup tasks, such as unsetting uci entries in related configurations. + * + * It should be overwritten by protocol specific subclasses. + * + * @abstract + * + * @returns {*|Promise<*>} + * This function may return a promise which is awaited before the rest of + * the configuration is removed. Any non-promise return value and any + * resolved promise value is ignored. If the returned promise is rejected, + * the interface removal will be aborted. + */ + deleteConfiguration: function() {} }); /** @@ -5195,7 +5844,7 @@ return Network;
    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/rpc.js.html b/docs/jsapi/rpc.js.html index c9d239b0e..983f091fb 100644 --- a/docs/jsapi/rpc.js.html +++ b/docs/jsapi/rpc.js.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -1573,7 +2181,7 @@ return L.Class.extend(/** @lends LuCI.rpc.prototype */ {
    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/uci.js.html b/docs/jsapi/uci.js.html index 8a619eed1..1f320f98a 100644 --- a/docs/jsapi/uci.js.html +++ b/docs/jsapi/uci.js.html @@ -498,6 +498,8 @@
  • containsDevice
  • +
  • deleteConfiguration
  • +
  • deleteDevice
  • get
  • @@ -556,6 +558,8 @@
  • isBridge
  • +
  • isCreateable
  • +
  • isDynamic
  • isEmpty
  • @@ -1002,6 +1006,610 @@ +
  • + + LuCI.ui + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.AbstractElement + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.changes + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Checkbox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Combobox + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.ComboButton + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Dropdown + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.DynamicList + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.FileUpload + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Hiddenfield + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Select + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.tabs + + +
      + +
    +
      + +
    +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textarea + + +
      + +
    + +
      + +
    + +
      + +
    +
  • + +
  • + + LuCI.ui.Textfield + + +
      + +
    + +
      + +
    + +
      + +
    +
  • +
  • LuCI.view @@ -2039,7 +2647,7 @@ return L.Class.extend(/** @lends LuCI.uci.prototype */ {
    - Documentation generated by JSDoc 3.6.3 on Wed Feb 12 2020 11:56:59 GMT+0100 (Central European Standard Time) + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time)
    diff --git a/docs/jsapi/ui.js.html b/docs/jsapi/ui.js.html new file mode 100644 index 000000000..96b3006f0 --- /dev/null +++ b/docs/jsapi/ui.js.html @@ -0,0 +1,5994 @@ + + + + + Source: ui.js + + + + + + + + + + + + + + + + + +
    + + +
    +

    Source: ui.js

    + + + + +
    +
    +
    'use strict';
    +'require rpc';
    +'require uci';
    +'require validation';
    +'require fs';
    +
    +var modalDiv = null,
    +    tooltipDiv = null,
    +    indicatorDiv = null,
    +    tooltipTimeout = null;
    +
    +/**
    + * @class AbstractElement
    + * @memberof LuCI.ui
    + * @hideconstructor
    + * @classdesc
    + *
    + * The `AbstractElement` class serves as abstract base for the different widgets
    + * implemented by `LuCI.ui`. It provides the common logic for getting and
    + * setting values, for checking the validity state and for wiring up required
    + * events.
    + *
    + * UI widget instances are usually not supposed to be created by view code
    + * directly, instead they're implicitely created by `LuCI.form` when
    + * instantiating CBI forms.
    + *
    + * This class is automatically instantiated as part of `LuCI.ui`. To use it
    + * in views, use `'require ui'` and refer to `ui.AbstractElement`. To import
    + * it in external JavaScript, use `L.require("ui").then(...)` and access the
    + * `AbstractElement` property of the class instance value.
    + */
    +var UIElement = L.Class.extend(/** @lends LuCI.ui.AbstractElement.prototype */ {
    +	/**
    +	 * @typedef {Object} InitOptions
    +	 * @memberof LuCI.ui.AbstractElement
    +	 *
    +	 * @property {string} [id]
    +	 * Specifies the widget ID to use. It will be used as HTML `id` attribute
    +	 * on the toplevel widget DOM node.
    +	 *
    +	 * @property {string} [name]
    +	 * Specifies the widget name which is set as HTML `name` attribute on the
    +	 * corresponding `<input>` element.
    +	 *
    +	 * @property {boolean} [optional=true]
    +	 * Specifies whether the input field allows empty values.
    +	 *
    +	 * @property {string} [datatype=string]
    +	 * An expression describing the input data validation constraints.
    +	 * It defaults to `string` which will allow any value.
    +	 * See{@link LuCI.validation} for details on the expression format.
    +	 *
    +	 * @property {function} [validator]
    +	 * Specifies a custom validator function which is invoked after the
    +	 * standard validation constraints are checked. The function should return
    +	 * `true` to accept the given input value. Any other return value type is
    +	 * converted to a string and treated as validation error message.
    +	 */
    +
    +	/**
    +	 * Read the current value of the input widget.
    +	 *
    +	 * @instance
    +	 * @memberof LuCI.ui.AbstractElement
    +	 * @returns {string|string[]|null}
    +	 * The current value of the input element. For simple inputs like text
    +	 * fields or selects, the return value type will be a - possibly empty -
    +	 * string. Complex widgets such as `DynamicList` instances may result in
    +	 * an array of strings or `null` for unset values.
    +	 */
    +	getValue: function() {
    +		if (L.dom.matches(this.node, 'select') || L.dom.matches(this.node, 'input'))
    +			return this.node.value;
    +
    +		return null;
    +	},
    +
    +	/**
    +	 * Set the current value of the input widget.
    +	 *
    +	 * @instance
    +	 * @memberof LuCI.ui.AbstractElement
    +	 * @param {string|string[]|null} value
    +	 * The value to set the input element to. For simple inputs like text
    +	 * fields or selects, the value should be a - possibly empty - string.
    +	 * Complex widgets such as `DynamicList` instances may accept string array
    +	 * or `null` values.
    +	 */
    +	setValue: function(value) {
    +		if (L.dom.matches(this.node, 'select') || L.dom.matches(this.node, 'input'))
    +			this.node.value = value;
    +	},
    +
    +	/**
    +	 * Check whether the current input value is valid.
    +	 *
    +	 * @instance
    +	 * @memberof LuCI.ui.AbstractElement
    +	 * @returns {boolean}
    +	 * Returns `true` if the current input value is valid or `false` if it does
    +	 * not meet the validation constraints.
    +	 */
    +	isValid: function() {
    +		return (this.validState !== false);
    +	},
    +
    +	/**
    +	 * Force validation of the current input value.
    +	 *
    +	 * Usually input validation is automatically triggered by various DOM events
    +	 * bound to the input widget. In some cases it is required though to manually
    +	 * trigger validation runs, e.g. when programmatically altering values.
    +	 *
    +	 * @instance
    +	 * @memberof LuCI.ui.AbstractElement
    +	 */
    +	triggerValidation: function() {
    +		if (typeof(this.vfunc) != 'function')
    +			return false;
    +
    +		var wasValid = this.isValid();
    +
    +		this.vfunc();
    +
    +		return (wasValid != this.isValid());
    +	},
    +
    +	/**
    +	 * Dispatch a custom (synthetic) event in response to received events.
    +	 *
    +	 * Sets up event handlers on the given target DOM node for the given event
    +	 * names that dispatch a custom event of the given type to the widget root
    +	 * DOM node.
    +	 *
    +	 * The primary purpose of this function is to set up a series of custom
    +	 * uniform standard events such as `widget-update`, `validation-success`,
    +	 * `validation-failure` etc. which are triggered by various different
    +	 * widget specific native DOM events.
    +	 *
    +	 * @instance
    +	 * @memberof LuCI.ui.AbstractElement
    +	 * @param {Node} targetNode
    +	 * Specifies the DOM node on which the native event listeners should be
    +	 * registered.
    +	 *
    +	 * @param {string} synevent
    +	 * The name of the custom event to dispatch to the widget root DOM node.
    +	 *
    +	 * @param {string[]} events
    +	 * The native DOM events for which event handlers should be registered.
    +	 */
    +	registerEvents: function(targetNode, synevent, events) {
    +		var dispatchFn = L.bind(function(ev) {
    +			this.node.dispatchEvent(new CustomEvent(synevent, { bubbles: true }));
    +		}, this);
    +
    +		for (var i = 0; i < events.length; i++)
    +			targetNode.addEventListener(events[i], dispatchFn);
    +	},
    +
    +	/**
    +	 * Setup listeners for native DOM events that may update the widget value.
    +	 *
    +	 * Sets up event handlers on the given target DOM node for the given event
    +	 * names which may cause the input value to update, such as `keyup` or
    +	 * `onclick` events. In contrast to change events, such update events will
    +	 * trigger input value validation.
    +	 *
    +	 * @instance
    +	 * @memberof LuCI.ui.AbstractElement
    +	 * @param {Node} targetNode
    +	 * Specifies the DOM node on which the event listeners should be registered.
    +	 *
    +	 * @param {...string} events
    +	 * The DOM events for which event handlers should be registered.
    +	 */
    +	setUpdateEvents: function(targetNode /*, ... */) {
    +		var datatype = this.options.datatype,
    +		    optional = this.options.hasOwnProperty('optional') ? this.options.optional : true,
    +		    validate = this.options.validate,
    +		    events = this.varargs(arguments, 1);
    +
    +		this.registerEvents(targetNode, 'widget-update', events);
    +
    +		if (!datatype && !validate)
    +			return;
    +
    +		this.vfunc = L.ui.addValidator.apply(L.ui, [
    +			targetNode, datatype || 'string',
    +			optional, validate
    +		].concat(events));
    +
    +		this.node.addEventListener('validation-success', L.bind(function(ev) {
    +			this.validState = true;
    +		}, this));
    +
    +		this.node.addEventListener('validation-failure', L.bind(function(ev) {
    +			this.validState = false;
    +		}, this));
    +	},
    +
    +	/**
    +	 * Setup listeners for native DOM events that may change the widget value.
    +	 *
    +	 * Sets up event handlers on the given target DOM node for the given event
    +	 * names which may cause the input value to change completely, such as
    +	 * `change` events in a select menu. In contrast to update events, such
    +	 * change events will not trigger input value validation but they may cause
    +	 * field dependencies to get re-evaluated and will mark the input widget
    +	 * as dirty.
    +	 *
    +	 * @instance
    +	 * @memberof LuCI.ui.AbstractElement
    +	 * @param {Node} targetNode
    +	 * Specifies the DOM node on which the event listeners should be registered.
    +	 *
    +	 * @param {...string} events
    +	 * The DOM events for which event handlers should be registered.
    +	 */
    +	setChangeEvents: function(targetNode /*, ... */) {
    +		var tag_changed = L.bind(function(ev) { this.setAttribute('data-changed', true) }, this.node);
    +
    +		for (var i = 1; i < arguments.length; i++)
    +			targetNode.addEventListener(arguments[i], tag_changed);
    +
    +		this.registerEvents(targetNode, 'widget-change', this.varargs(arguments, 1));
    +	},
    +
    +	/**
    +	 * Render the widget, setup event listeners and return resulting markup.
    +	 *
    +	 * @instance
    +	 * @memberof LuCI.ui.AbstractElement
    +	 *
    +	 * @returns {Node}
    +	 * Returns a DOM Node or DocumentFragment containing the rendered
    +	 * widget markup.
    +	 */
    +	render: function() {}
    +});
    +
    +/**
    + * Instantiate a text input widget.
    + *
    + * @constructor Textfield
    + * @memberof LuCI.ui
    + * @augments LuCI.ui.AbstractElement
    + *
    + * @classdesc
    + *
    + * The `Textfield` class implements a standard single line text input field.
    + *
    + * UI widget instances are usually not supposed to be created by view code
    + * directly, instead they're implicitely created by `LuCI.form` when
    + * instantiating CBI forms.
    + *
    + * This class is automatically instantiated as part of `LuCI.ui`. To use it
    + * in views, use `'require ui'` and refer to `ui.Textfield`. To import it in
    + * external JavaScript, use `L.require("ui").then(...)` and access the
    + * `Textfield` property of the class instance value.
    + *
    + * @param {string} [value=null]
    + * The initial input value.
    + *
    + * @param {LuCI.ui.Textfield.InitOptions} [options]
    + * Object describing the widget specific options to initialize the input.
    + */
    +var UITextfield = UIElement.extend(/** @lends LuCI.ui.Textfield.prototype */ {
    +	/**
    +	 * In addition to the [AbstractElement.InitOptions]{@link LuCI.ui.AbstractElement.InitOptions}
    +	 * the following properties are recognized:
    +	 *
    +	 * @typedef {LuCI.ui.AbstractElement.InitOptions} InitOptions
    +	 * @memberof LuCI.ui.Textfield
    +	 *
    +	 * @property {boolean} [password=false]
    +	 * Specifies whether the input should be rendered as concealed password field.
    +	 *
    +	 * @property {boolean} [readonly=false]
    +	 * Specifies whether the input widget should be rendered readonly.
    +	 *
    +	 * @property {number} [maxlength]
    +	 * Specifies the HTML `maxlength` attribute to set on the corresponding
    +	 * `<input>` element. Note that this a legacy property that exists for
    +	 * compatibility reasons. It is usually better to `maxlength(N)` validation
    +	 * expression.
    +	 *
    +	 * @property {string} [placeholder]
    +	 * Specifies the HTML `placeholder` attribute which is displayed when the
    +	 * corresponding `<input>` element is empty.
    +	 */
    +	__init__: function(value, options) {
    +		this.value = value;
    +		this.options = Object.assign({
    +			optional: true,
    +			password: false
    +		}, options);
    +	},
    +
    +	/** @override */
    +	render: function() {
    +		var frameEl = E('div', { 'id': this.options.id });
    +
    +		if (this.options.password) {
    +			frameEl.classList.add('nowrap');
    +			frameEl.appendChild(E('input', {
    +				'type': 'password',
    +				'style': 'position:absolute; left:-100000px',
    +				'aria-hidden': true,
    +				'tabindex': -1,
    +				'name': this.options.name ? 'password.%s'.format(this.options.name) : null
    +			}));
    +		}
    +
    +		frameEl.appendChild(E('input', {
    +			'id': this.options.id ? 'widget.' + this.options.id : null,
    +			'name': this.options.name,
    +			'type': this.options.password ? 'password' : 'text',
    +			'class': this.options.password ? 'cbi-input-password' : 'cbi-input-text',
    +			'readonly': this.options.readonly ? '' : null,
    +			'maxlength': this.options.maxlength,
    +			'placeholder': this.options.placeholder,
    +			'value': this.value,
    +		}));
    +
    +		if (this.options.password)
    +			frameEl.appendChild(E('button', {
    +				'class': 'cbi-button cbi-button-neutral',
    +				'title': _('Reveal/hide password'),
    +				'aria-label': _('Reveal/hide password'),
    +				'click': function(ev) {
    +					var e = this.previousElementSibling;
    +					e.type = (e.type === 'password') ? 'text' : 'password';
    +					ev.preventDefault();
    +				}
    +			}, '∗'));
    +
    +		return this.bind(frameEl);
    +	},
    +
    +	/** @private */
    +	bind: function(frameEl) {
    +		var inputEl = frameEl.childNodes[+!!this.options.password];
    +
    +		this.node = frameEl;
    +
    +		this.setUpdateEvents(inputEl, 'keyup', 'blur');
    +		this.setChangeEvents(inputEl, 'change');
    +
    +		L.dom.bindClassInstance(frameEl, this);
    +
    +		return frameEl;
    +	},
    +
    +	/** @override */
    +	getValue: function() {
    +		var inputEl = this.node.childNodes[+!!this.options.password];
    +		return inputEl.value;
    +	},
    +
    +	/** @override */
    +	setValue: function(value) {
    +		var inputEl = this.node.childNodes[+!!this.options.password];
    +		inputEl.value = value;
    +	}
    +});
    +
    +/**
    + * Instantiate a textarea widget.
    + *
    + * @constructor Textarea
    + * @memberof LuCI.ui
    + * @augments LuCI.ui.AbstractElement
    + *
    + * @classdesc
    + *
    + * The `Textarea` class implements a multiline text area input field.
    + *
    + * UI widget instances are usually not supposed to be created by view code
    + * directly, instead they're implicitely created by `LuCI.form` when
    + * instantiating CBI forms.
    + *
    + * This class is automatically instantiated as part of `LuCI.ui`. To use it
    + * in views, use `'require ui'` and refer to `ui.Textarea`. To import it in
    + * external JavaScript, use `L.require("ui").then(...)` and access the
    + * `Textarea` property of the class instance value.
    + *
    + * @param {string} [value=null]
    + * The initial input value.
    + *
    + * @param {LuCI.ui.Textarea.InitOptions} [options]
    + * Object describing the widget specific options to initialize the input.
    + */
    +var UITextarea = UIElement.extend(/** @lends LuCI.ui.Textarea.prototype */ {
    +	/**
    +	 * In addition to the [AbstractElement.InitOptions]{@link LuCI.ui.AbstractElement.InitOptions}
    +	 * the following properties are recognized:
    +	 *
    +	 * @typedef {LuCI.ui.AbstractElement.InitOptions} InitOptions
    +	 * @memberof LuCI.ui.Textarea
    +	 *
    +	 * @property {boolean} [readonly=false]
    +	 * Specifies whether the input widget should be rendered readonly.
    +	 *
    +	 * @property {string} [placeholder]
    +	 * Specifies the HTML `placeholder` attribute which is displayed when the
    +	 * corresponding `<textarea>` element is empty.
    +	 *
    +	 * @property {boolean} [monospace=false]
    +	 * Specifies whether a monospace font should be forced for the textarea
    +	 * contents.
    +	 *
    +	 * @property {number} [cols]
    +	 * Specifies the HTML `cols` attribute to set on the corresponding
    +	 * `<textarea>` element.
    +	 *
    +	 * @property {number} [rows]
    +	 * Specifies the HTML `rows` attribute to set on the corresponding
    +	 * `<textarea>` element.
    +	 *
    +	 * @property {boolean} [wrap=false]
    +	 * Specifies whether the HTML `wrap` attribute should be set.
    +	 */
    +	__init__: function(value, options) {
    +		this.value = value;
    +		this.options = Object.assign({
    +			optional: true,
    +			wrap: false,
    +			cols: null,
    +			rows: null
    +		}, options);
    +	},
    +
    +	/** @override */
    +	render: function() {
    +		var frameEl = E('div', { 'id': this.options.id }),
    +		    value = (this.value != null) ? String(this.value) : '';
    +
    +		frameEl.appendChild(E('textarea', {
    +			'id': this.options.id ? 'widget.' + this.options.id : null,
    +			'name': this.options.name,
    +			'class': 'cbi-input-textarea',
    +			'readonly': this.options.readonly ? '' : null,
    +			'placeholder': this.options.placeholder,
    +			'style': !this.options.cols ? 'width:100%' : null,
    +			'cols': this.options.cols,
    +			'rows': this.options.rows,
    +			'wrap': this.options.wrap ? '' : null
    +		}, [ value ]));
    +
    +		if (this.options.monospace)
    +			frameEl.firstElementChild.style.fontFamily = 'monospace';
    +
    +		return this.bind(frameEl);
    +	},
    +
    +	/** @private */
    +	bind: function(frameEl) {
    +		var inputEl = frameEl.firstElementChild;
    +
    +		this.node = frameEl;
    +
    +		this.setUpdateEvents(inputEl, 'keyup', 'blur');
    +		this.setChangeEvents(inputEl, 'change');
    +
    +		L.dom.bindClassInstance(frameEl, this);
    +
    +		return frameEl;
    +	},
    +
    +	/** @override */
    +	getValue: function() {
    +		return this.node.firstElementChild.value;
    +	},
    +
    +	/** @override */
    +	setValue: function(value) {
    +		this.node.firstElementChild.value = value;
    +	}
    +});
    +
    +/**
    + * Instantiate a checkbox widget.
    + *
    + * @constructor Checkbox
    + * @memberof LuCI.ui
    + * @augments LuCI.ui.AbstractElement
    + *
    + * @classdesc
    + *
    + * The `Checkbox` class implements a simple checkbox input field.
    + *
    + * UI widget instances are usually not supposed to be created by view code
    + * directly, instead they're implicitely created by `LuCI.form` when
    + * instantiating CBI forms.
    + *
    + * This class is automatically instantiated as part of `LuCI.ui`. To use it
    + * in views, use `'require ui'` and refer to `ui.Checkbox`. To import it in
    + * external JavaScript, use `L.require("ui").then(...)` and access the
    + * `Checkbox` property of the class instance value.
    + *
    + * @param {string} [value=null]
    + * The initial input value.
    + *
    + * @param {LuCI.ui.Checkbox.InitOptions} [options]
    + * Object describing the widget specific options to initialize the input.
    + */
    +var UICheckbox = UIElement.extend(/** @lends LuCI.ui.Checkbox.prototype */ {
    +	/**
    +	 * In addition to the [AbstractElement.InitOptions]{@link LuCI.ui.AbstractElement.InitOptions}
    +	 * the following properties are recognized:
    +	 *
    +	 * @typedef {LuCI.ui.AbstractElement.InitOptions} InitOptions
    +	 * @memberof LuCI.ui.Checkbox
    +	 *
    +	 * @property {string} [value_enabled=1]
    +	 * Specifies the value corresponding to a checked checkbox.
    +	 *
    +	 * @property {string} [value_disabled=0]
    +	 * Specifies the value corresponding to an unchecked checkbox.
    +	 *
    +	 * @property {string} [hiddenname]
    +	 * Specifies the HTML `name` attribute of the hidden input backing the
    +	 * checkbox. This is a legacy property existing for compatibility reasons,
    +	 * it is required for HTML based form submissions.
    +	 */
    +	__init__: function(value, options) {
    +		this.value = value;
    +		this.options = Object.assign({
    +			value_enabled: '1',
    +			value_disabled: '0'
    +		}, options);
    +	},
    +
    +	/** @override */
    +	render: function() {
    +		var id = 'cb%08x'.format(Math.random() * 0xffffffff);
    +		var frameEl = E('div', {
    +			'id': this.options.id,
    +			'class': 'cbi-checkbox'
    +		});
    +
    +		if (this.options.hiddenname)
    +			frameEl.appendChild(E('input', {
    +				'type': 'hidden',
    +				'name': this.options.hiddenname,
    +				'value': 1
    +			}));
    +
    +		frameEl.appendChild(E('input', {
    +			'id': id,
    +			'name': this.options.name,
    +			'type': 'checkbox',
    +			'value': this.options.value_enabled,
    +			'checked': (this.value == this.options.value_enabled) ? '' : null,
    +			'data-widget-id': this.options.id ? 'widget.' + this.options.id : null
    +		}));
    +
    +		frameEl.appendChild(E('label', { 'for': id }));
    +
    +		return this.bind(frameEl);
    +	},
    +
    +	/** @private */
    +	bind: function(frameEl) {
    +		this.node = frameEl;
    +
    +		this.setUpdateEvents(frameEl.lastElementChild.previousElementSibling, 'click', 'blur');
    +		this.setChangeEvents(frameEl.lastElementChild.previousElementSibling, 'change');
    +
    +		L.dom.bindClassInstance(frameEl, this);
    +
    +		return frameEl;
    +	},
    +
    +	/**
    +	 * Test whether the checkbox is currently checked.
    +	 *
    +	 * @instance
    +	 * @memberof LuCI.ui.Checkbox
    +	 * @returns {boolean}
    +	 * Returns `true` when the checkbox is currently checked, otherwise `false`.
    +	 */
    +	isChecked: function() {
    +		return this.node.lastElementChild.previousElementSibling.checked;
    +	},
    +
    +	/** @override */
    +	getValue: function() {
    +		return this.isChecked()
    +			? this.options.value_enabled
    +			: this.options.value_disabled;
    +	},
    +
    +	/** @override */
    +	setValue: function(value) {
    +		this.node.lastElementChild.previousElementSibling.checked = (value == this.options.value_enabled);
    +	}
    +});
    +
    +/**
    + * Instantiate a select dropdown or checkbox/radiobutton group.
    + *
    + * @constructor Select
    + * @memberof LuCI.ui
    + * @augments LuCI.ui.AbstractElement
    + *
    + * @classdesc
    + *
    + * The `Select` class implements either a traditional HTML `<select>` element
    + * or a group of checkboxes or radio buttons, depending on whether multiple
    + * values are enabled or not.
    + *
    + * UI widget instances are usually not supposed to be created by view code
    + * directly, instead they're implicitely created by `LuCI.form` when
    + * instantiating CBI forms.
    + *
    + * This class is automatically instantiated as part of `LuCI.ui`. To use it
    + * in views, use `'require ui'` and refer to `ui.Select`. To import it in
    + * external JavaScript, use `L.require("ui").then(...)` and access the
    + * `Select` property of the class instance value.
    + *
    + * @param {string|string[]} [value=null]
    + * The initial input value(s).
    + *
    + * @param {Object<string, string>} choices
    + * Object containing the selectable choices of the widget. The object keys
    + * serve as values for the different choices while the values are used as
    + * choice labels.
    + *
    + * @param {LuCI.ui.Select.InitOptions} [options]
    + * Object describing the widget specific options to initialize the inputs.
    + */
    +var UISelect = UIElement.extend(/** @lends LuCI.ui.Select.prototype */ {
    +	/**
    +	 * In addition to the [AbstractElement.InitOptions]{@link LuCI.ui.AbstractElement.InitOptions}
    +	 * the following properties are recognized:
    +	 *
    +	 * @typedef {LuCI.ui.AbstractElement.InitOptions} InitOptions
    +	 * @memberof LuCI.ui.Select
    +	 *
    +	 * @property {boolean} [multiple=false]
    +	 * Specifies whether multiple choice values may be selected.
    +	 *
    +	 * @property {string} [widget=select]
    +	 * Specifies the kind of widget to render. May be either `select` or
    +	 * `individual`. When set to `select` an HTML `<select>` element will be
    +	 * used, otherwise a group of checkbox or radio button elements is created,
    +	 * depending on the value of the `multiple` option.
    +	 *
    +	 * @property {string} [orientation=horizontal]
    +	 * Specifies whether checkbox / radio button groups should be rendered
    +	 * in a `horizontal` or `vertical` manner. Does not apply to the `select`
    +	 * widget type.
    +	 *
    +	 * @property {boolean|string[]} [sort=false]
    +	 * Specifies if and how to sort choice values. If set to `true`, the choice
    +	 * values will be sorted alphabetically. If set to an array of strings, the
    +	 * choice sort order is derived from the array.
    +	 *
    +	 * @property {number} [size]
    +	 * Specifies the HTML `size` attribute to set on the `<select>` element.
    +	 * Only applicable to the `select` widget type.
    +	 *
    +	 * @property {string} [placeholder=-- Please choose --]
    +	 * Specifies a placeholder text which is displayed when no choice is
    +	 * selected yet. Only applicable to the `select` widget type.
    +	 */
    +	__init__: function(value, choices, options) {
    +		if (!L.isObject(choices))
    +			choices = {};
    +
    +		if (!Array.isArray(value))
    +			value = (value != null && value != '') ? [ value ] : [];
    +
    +		if (!options.multiple && value.length > 1)
    +			value.length = 1;
    +
    +		this.values = value;
    +		this.choices = choices;
    +		this.options = Object.assign({
    +			multiple: false,
    +			widget: 'select',
    +			orientation: 'horizontal'
    +		}, options);
    +
    +		if (this.choices.hasOwnProperty(''))
    +			this.options.optional = true;
    +	},
    +
    +	/** @override */
    +	render: function() {
    +		var frameEl = E('div', { 'id': this.options.id }),
    +		    keys = Object.keys(this.choices);
    +
    +		if (this.options.sort === true)
    +			keys.sort();
    +		else if (Array.isArray(this.options.sort))
    +			keys = this.options.sort;
    +
    +		if (this.options.widget == 'select') {
    +			frameEl.appendChild(E('select', {
    +				'id': this.options.id ? 'widget.' + this.options.id : null,
    +				'name': this.options.name,
    +				'size': this.options.size,
    +				'class': 'cbi-input-select',
    +				'multiple': this.options.multiple ? '' : null
    +			}));
    +
    +			if (this.options.optional)
    +				frameEl.lastChild.appendChild(E('option', {
    +					'value': '',
    +					'selected': (this.values.length == 0 || this.values[0] == '') ? '' : null
    +				}, [ this.choices[''] || this.options.placeholder || _('-- Please choose --') ]));
    +
    +			for (var i = 0; i < keys.length; i++) {
    +				if (keys[i] == null || keys[i] == '')
    +					continue;
    +
    +				frameEl.lastChild.appendChild(E('option', {
    +					'value': keys[i],
    +					'selected': (this.values.indexOf(keys[i]) > -1) ? '' : null
    +				}, [ this.choices[keys[i]] || keys[i] ]));
    +			}
    +		}
    +		else {
    +			var brEl = (this.options.orientation === 'horizontal') ? document.createTextNode(' ') : E('br');
    +
    +			for (var i = 0; i < keys.length; i++) {
    +				frameEl.appendChild(E('label', {}, [
    +					E('input', {
    +						'id': this.options.id ? 'widget.' + this.options.id : null,
    +						'name': this.options.id || this.options.name,
    +						'type': this.options.multiple ? 'checkbox' : 'radio',
    +						'class': this.options.multiple ? 'cbi-input-checkbox' : 'cbi-input-radio',
    +						'value': keys[i],
    +						'checked': (this.values.indexOf(keys[i]) > -1) ? '' : null
    +					}),
    +					this.choices[keys[i]] || keys[i]
    +				]));
    +
    +				if (i + 1 == this.options.size)
    +					frameEl.appendChild(brEl);
    +			}
    +		}
    +
    +		return this.bind(frameEl);
    +	},
    +
    +	/** @private */
    +	bind: function(frameEl) {
    +		this.node = frameEl;
    +
    +		if (this.options.widget == 'select') {
    +			this.setUpdateEvents(frameEl.firstChild, 'change', 'click', 'blur');
    +			this.setChangeEvents(frameEl.firstChild, 'change');
    +		}
    +		else {
    +			var radioEls = frameEl.querySelectorAll('input[type="radio"]');
    +			for (var i = 0; i < radioEls.length; i++) {
    +				this.setUpdateEvents(radioEls[i], 'change', 'click', 'blur');
    +				this.setChangeEvents(radioEls[i], 'change', 'click', 'blur');
    +			}
    +		}
    +
    +		L.dom.bindClassInstance(frameEl, this);
    +
    +		return frameEl;
    +	},
    +
    +	/** @override */
    +	getValue: function() {
    +		if (this.options.widget == 'select')
    +			return this.node.firstChild.value;
    +
    +		var radioEls = frameEl.querySelectorAll('input[type="radio"]');
    +		for (var i = 0; i < radioEls.length; i++)
    +			if (radioEls[i].checked)
    +				return radioEls[i].value;
    +
    +		return null;
    +	},
    +
    +	/** @override */
    +	setValue: function(value) {
    +		if (this.options.widget == 'select') {
    +			if (value == null)
    +				value = '';
    +
    +			for (var i = 0; i < this.node.firstChild.options.length; i++)
    +				this.node.firstChild.options[i].selected = (this.node.firstChild.options[i].value == value);
    +
    +			return;
    +		}
    +
    +		var radioEls = frameEl.querySelectorAll('input[type="radio"]');
    +		for (var i = 0; i < radioEls.length; i++)
    +			radioEls[i].checked = (radioEls[i].value == value);
    +	}
    +});
    +
    +/**
    + * Instantiate a rich dropdown choice widget.
    + *
    + * @constructor Dropdown
    + * @memberof LuCI.ui
    + * @augments LuCI.ui.AbstractElement
    + *
    + * @classdesc
    + *
    + * The `Dropdown` class implements a rich, stylable dropdown menu which
    + * supports non-text choice labels.
    + *
    + * UI widget instances are usually not supposed to be created by view code
    + * directly, instead they're implicitely created by `LuCI.form` when
    + * instantiating CBI forms.
    + *
    + * This class is automatically instantiated as part of `LuCI.ui`. To use it
    + * in views, use `'require ui'` and refer to `ui.Dropdown`. To import it in
    + * external JavaScript, use `L.require("ui").then(...)` and access the
    + * `Dropdown` property of the class instance value.
    + *
    + * @param {string|string[]} [value=null]
    + * The initial input value(s).
    + *
    + * @param {Object<string, *>} choices
    + * Object containing the selectable choices of the widget. The object keys
    + * serve as values for the different choices while the values are used as
    + * choice labels.
    + *
    + * @param {LuCI.ui.Dropdown.InitOptions} [options]
    + * Object describing the widget specific options to initialize the dropdown.
    + */
    +var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
    +	/**
    +	 * In addition to the [AbstractElement.InitOptions]{@link LuCI.ui.AbstractElement.InitOptions}
    +	 * the following properties are recognized:
    +	 *
    +	 * @typedef {LuCI.ui.AbstractElement.InitOptions} InitOptions
    +	 * @memberof LuCI.ui.Dropdown
    +	 *
    +	 * @property {boolean} [optional=true]
    +	 * Specifies whether the dropdown selection is optional. In contrast to
    +	 * other widgets, the `optional` constraint of dropdowns works differently;
    +	 * instead of marking the widget invalid on empty values when set to `false`,
    +	 * the user is not allowed to deselect all choices.
    +	 *
    +	 * For single value dropdowns that means that no empty "please select"
    +	 * choice is offered and for multi value dropdowns, the last selected choice
    +	 * may not be deselected without selecting another choice first.
    +	 *
    +	 * @property {boolean} [multiple]
    +	 * Specifies whether multiple choice values may be selected. It defaults
    +	 * to `true` when an array is passed as input value to the constructor.
    +	 *
    +	 * @property {boolean|string[]} [sort=false]
    +	 * Specifies if and how to sort choice values. If set to `true`, the choice
    +	 * values will be sorted alphabetically. If set to an array of strings, the
    +	 * choice sort order is derived from the array.
    +	 *
    +	 * @property {string} [select_placeholder=-- Please choose --]
    +	 * Specifies a placeholder text which is displayed when no choice is
    +	 * selected yet.
    +	 *
    +	 * @property {string} [custom_placeholder=-- custom --]
    +	 * Specifies a placeholder text which is displayed in the text input
    +	 * field allowing to enter custom choice values. Only applicable if the
    +	 * `create` option is set to `true`.
    +	 *
    +	 * @property {boolean} [create=false]
    +	 * Specifies whether custom choices may be entered into the dropdown
    +	 * widget.
    +	 *
    +	 * @property {string} [create_query=.create-item-input]
    +	 * Specifies a CSS selector expression used to find the input element
    +	 * which is used to enter custom choice values. This should not normally
    +	 * be used except by widgets derived from the Dropdown class.
    +	 *
    +	 * @property {string} [create_template=script[type="item-template"]]
    +	 * Specifies a CSS selector expression used to find an HTML element
    +	 * serving as template for newly added custom choice values.
    +	 *
    +	 * Any `{{value}}` placeholder string within the template elements text
    +	 * content will be replaced by the user supplied choice value, the
    +	 * resulting string is parsed as HTML and appended to the end of the
    +	 * choice list. The template markup may specify one HTML element with a
    +	 * `data-label-placeholder` attribute which is replaced by a matching
    +	 * label value from the `choices` object or with the user supplied value
    +	 * itself in case `choices` contains no matching choice label.
    +	 *
    +	 * If the template element is not found or if no `create_template` selector
    +	 * expression is specified, the default markup for newly created elements is
    +	 * `<li data-value="{{value}}"><span data-label-placeholder="true" /></li>`.
    +	 *
    +	 * @property {string} [create_markup]
    +	 * This property allows specifying the markup for custom choices directly
    +	 * instead of referring to a template element through CSS selectors.
    +	 *
    +	 * Apart from that it works exactly like `create_template`.
    +	 *
    +	 * @property {number} [display_items=3]
    +	 * Specifies the maximum amount of choice labels that should be shown in
    +	 * collapsed dropdown state before further selected choices are cut off.
    +	 *
    +	 * Only applicable when `multiple` is `true`.
    +	 *
    +	 * @property {number} [dropdown_items=-1]
    +	 * Specifies the maximum amount of choices that should be shown when the
    +	 * dropdown is open. If the amount of available choices exceeds this number,
    +	 * the dropdown area must be scrolled to reach further items.
    +	 *
    +	 * If set to `-1`, the dropdown menu will attempt to show all choice values
    +	 * and only resort to scrolling if the amount of choices exceeds the available
    +	 * screen space above and below the dropdown widget.
    +	 *
    +	 * @property {string} [placeholder]
    +	 * This property serves as a shortcut to set both `select_placeholder` and
    +	 * `custom_placeholder`. Either of these properties will fallback to
    +	 * `placeholder` if not specified.
    +	 *
    +	 * @property {boolean} [readonly=false]
    +	 * Specifies whether the custom choice input field should be rendered
    +	 * readonly. Only applicable when `create` is `true`.
    +	 *
    +	 * @property {number} [maxlength]
    +	 * Specifies the HTML `maxlength` attribute to set on the custom choice
    +	 * `<input>` element. Note that this a legacy property that exists for
    +	 * compatibility reasons. It is usually better to `maxlength(N)` validation
    +	 * expression. Only applicable when `create` is `true`.
    +	 */
    +	__init__: function(value, choices, options) {
    +		if (typeof(choices) != 'object')
    +			choices = {};
    +
    +		if (!Array.isArray(value))
    +			this.values = (value != null && value != '') ? [ value ] : [];
    +		else
    +			this.values = value;
    +
    +		this.choices = choices;
    +		this.options = Object.assign({
    +			sort:               true,
    +			multiple:           Array.isArray(value),
    +			optional:           true,
    +			select_placeholder: _('-- Please choose --'),
    +			custom_placeholder: _('-- custom --'),
    +			display_items:      3,
    +			dropdown_items:     -1,
    +			create:             false,
    +			create_query:       '.create-item-input',
    +			create_template:    'script[type="item-template"]'
    +		}, options);
    +	},
    +
    +	/** @override */
    +	render: function() {
    +		var sb = E('div', {
    +			'id': this.options.id,
    +			'class': 'cbi-dropdown',
    +			'multiple': this.options.multiple ? '' : null,
    +			'optional': this.options.optional ? '' : null,
    +		}, E('ul'));
    +
    +		var keys = Object.keys(this.choices);
    +
    +		if (this.options.sort === true)
    +			keys.sort();
    +		else if (Array.isArray(this.options.sort))
    +			keys = this.options.sort;
    +
    +		if (this.options.create)
    +			for (var i = 0; i < this.values.length; i++)
    +				if (!this.choices.hasOwnProperty(this.values[i]))
    +					keys.push(this.values[i]);
    +
    +		for (var i = 0; i < keys.length; i++) {
    +			var label = this.choices[keys[i]];
    +
    +			if (L.dom.elem(label))
    +				label = label.cloneNode(true);
    +
    +			sb.lastElementChild.appendChild(E('li', {
    +				'data-value': keys[i],
    +				'selected': (this.values.indexOf(keys[i]) > -1) ? '' : null
    +			}, [ label || keys[i] ]));
    +		}
    +
    +		if (this.options.create) {
    +			var createEl = E('input', {
    +				'type': 'text',
    +				'class': 'create-item-input',
    +				'readonly': this.options.readonly ? '' : null,
    +				'maxlength': this.options.maxlength,
    +				'placeholder': this.options.custom_placeholder || this.options.placeholder
    +			});
    +
    +			if (this.options.datatype || this.options.validate)
    +				L.ui.addValidator(createEl, this.options.datatype || 'string',
    +				                  true, this.options.validate, 'blur', 'keyup');
    +
    +			sb.lastElementChild.appendChild(E('li', { 'data-value': '-' }, createEl));
    +		}
    +
    +		if (this.options.create_markup)
    +			sb.appendChild(E('script', { type: 'item-template' },
    +				this.options.create_markup));
    +
    +		return this.bind(sb);
    +	},
    +
    +	/** @private */
    +	bind: function(sb) {
    +		var o = this.options;
    +
    +		o.multiple = sb.hasAttribute('multiple');
    +		o.optional = sb.hasAttribute('optional');
    +		o.placeholder = sb.getAttribute('placeholder') || o.placeholder;
    +		o.display_items = parseInt(sb.getAttribute('display-items') || o.display_items);
    +		o.dropdown_items = parseInt(sb.getAttribute('dropdown-items') || o.dropdown_items);
    +		o.create_query = sb.getAttribute('item-create') || o.create_query;
    +		o.create_template = sb.getAttribute('item-template') || o.create_template;
    +
    +		var ul = sb.querySelector('ul'),
    +		    more = sb.appendChild(E('span', { class: 'more', tabindex: -1 }, '···')),
    +		    open = sb.appendChild(E('span', { class: 'open', tabindex: -1 }, '▾')),
    +		    canary = sb.appendChild(E('div')),
    +		    create = sb.querySelector(this.options.create_query),
    +		    ndisplay = this.options.display_items,
    +		    n = 0;
    +
    +		if (this.options.multiple) {
    +			var items = ul.querySelectorAll('li');
    +
    +			for (var i = 0; i < items.length; i++) {
    +				this.transformItem(sb, items[i]);
    +
    +				if (items[i].hasAttribute('selected') && ndisplay-- > 0)
    +					items[i].setAttribute('display', n++);
    +			}
    +		}
    +		else {
    +			if (this.options.optional && !ul.querySelector('li[data-value=""]')) {
    +				var placeholder = E('li', { placeholder: '' },
    +					this.options.select_placeholder || this.options.placeholder);
    +
    +				ul.firstChild
    +					? ul.insertBefore(placeholder, ul.firstChild)
    +					: ul.appendChild(placeholder);
    +			}
    +
    +			var items = ul.querySelectorAll('li'),
    +			    sel = sb.querySelectorAll('[selected]');
    +
    +			sel.forEach(function(s) {
    +				s.removeAttribute('selected');
    +			});
    +
    +			var s = sel[0] || items[0];
    +			if (s) {
    +				s.setAttribute('selected', '');
    +				s.setAttribute('display', n++);
    +			}
    +
    +			ndisplay--;
    +		}
    +
    +		this.saveValues(sb, ul);
    +
    +		ul.setAttribute('tabindex', -1);
    +		sb.setAttribute('tabindex', 0);
    +
    +		if (ndisplay < 0)
    +			sb.setAttribute('more', '')
    +		else
    +			sb.removeAttribute('more');
    +
    +		if (ndisplay == this.options.display_items)
    +			sb.setAttribute('empty', '')
    +		else
    +			sb.removeAttribute('empty');
    +
    +		L.dom.content(more, (ndisplay == this.options.display_items)
    +			? (this.options.select_placeholder || this.options.placeholder) : '···');
    +
    +
    +		sb.addEventListener('click', this.handleClick.bind(this));
    +		sb.addEventListener('keydown', this.handleKeydown.bind(this));
    +		sb.addEventListener('cbi-dropdown-close', this.handleDropdownClose.bind(this));
    +		sb.addEventListener('cbi-dropdown-select', this.handleDropdownSelect.bind(this));
    +
    +		if ('ontouchstart' in window) {
    +			sb.addEventListener('touchstart', function(ev) { ev.stopPropagation(); });
    +			window.addEventListener('touchstart', this.closeAllDropdowns);
    +		}
    +		else {
    +			sb.addEventListener('mouseover', this.handleMouseover.bind(this));
    +			sb.addEventListener('focus', this.handleFocus.bind(this));
    +
    +			canary.addEventListener('focus', this.handleCanaryFocus.bind(this));
    +
    +			window.addEventListener('mouseover', this.setFocus);
    +			window.addEventListener('click', this.closeAllDropdowns);
    +		}
    +
    +		if (create) {
    +			create.addEventListener('keydown', this.handleCreateKeydown.bind(this));
    +			create.addEventListener('focus', this.handleCreateFocus.bind(this));
    +			create.addEventListener('blur', this.handleCreateBlur.bind(this));
    +
    +			var li = findParent(create, 'li');
    +
    +			li.setAttribute('unselectable', '');
    +			li.addEventListener('click', this.handleCreateClick.bind(this));
    +		}
    +
    +		this.node = sb;
    +
    +		this.setUpdateEvents(sb, 'cbi-dropdown-open', 'cbi-dropdown-close');
    +		this.setChangeEvents(sb, 'cbi-dropdown-change', 'cbi-dropdown-close');
    +
    +		L.dom.bindClassInstance(sb, this);
    +
    +		return sb;
    +	},
    +
    +	/** @private */
    +	openDropdown: function(sb) {
    +		var st = window.getComputedStyle(sb, null),
    +		    ul = sb.querySelector('ul'),
    +		    li = ul.querySelectorAll('li'),
    +		    fl = findParent(sb, '.cbi-value-field'),
    +		    sel = ul.querySelector('[selected]'),
    +		    rect = sb.getBoundingClientRect(),
    +		    items = Math.min(this.options.dropdown_items, li.length);
    +
    +		document.querySelectorAll('.cbi-dropdown[open]').forEach(function(s) {
    +			s.dispatchEvent(new CustomEvent('cbi-dropdown-close', {}));
    +		});
    +
    +		sb.setAttribute('open', '');
    +
    +		var pv = ul.cloneNode(true);
    +		    pv.classList.add('preview');
    +
    +		if (fl)
    +			fl.classList.add('cbi-dropdown-open');
    +
    +		if ('ontouchstart' in window) {
    +			var vpWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
    +			    vpHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0),
    +			    start = null;
    +
    +			ul.style.top = sb.offsetHeight + 'px';
    +			ul.style.left = -rect.left + 'px';
    +			ul.style.right = (rect.right - vpWidth) + 'px';
    +			ul.style.maxHeight = (vpHeight * 0.5) + 'px';
    +			ul.style.WebkitOverflowScrolling = 'touch';
    +
    +			function getScrollParent(element) {
    +				var parent = element,
    +				    style = getComputedStyle(element),
    +				    excludeStaticParent = (style.position === 'absolute');
    +
    +				if (style.position === 'fixed')
    +					return document.body;
    +
    +				while ((parent = parent.parentElement) != null) {
    +					style = getComputedStyle(parent);
    +
    +					if (excludeStaticParent && style.position === 'static')
    +						continue;
    +
    +					if (/(auto|scroll)/.test(style.overflow + style.overflowY + style.overflowX))
    +						return parent;
    +				}
    +
    +				return document.body;
    +			}
    +
    +			var scrollParent = getScrollParent(sb),
    +			    scrollFrom = scrollParent.scrollTop,
    +			    scrollTo = scrollFrom + rect.top - vpHeight * 0.5;
    +
    +			var scrollStep = function(timestamp) {
    +				if (!start) {
    +					start = timestamp;
    +					ul.scrollTop = sel ? Math.max(sel.offsetTop - sel.offsetHeight, 0) : 0;
    +				}
    +
    +				var duration = Math.max(timestamp - start, 1);
    +				if (duration < 100) {
    +					scrollParent.scrollTop = scrollFrom + (scrollTo - scrollFrom) * (duration / 100);
    +					window.requestAnimationFrame(scrollStep);
    +				}
    +				else {
    +					scrollParent.scrollTop = scrollTo;
    +				}
    +			};
    +
    +			window.requestAnimationFrame(scrollStep);
    +		}
    +		else {
    +			ul.style.maxHeight = '1px';
    +			ul.style.top = ul.style.bottom = '';
    +
    +			window.requestAnimationFrame(function() {
    +				var itemHeight = li[Math.max(0, li.length - 2)].getBoundingClientRect().height,
    +				    fullHeight = 0,
    +				    spaceAbove = rect.top,
    +				    spaceBelow = window.innerHeight - rect.height - rect.top;
    +
    +				for (var i = 0; i < (items == -1 ? li.length : items); i++)
    +					fullHeight += li[i].getBoundingClientRect().height;
    +
    +				if (fullHeight <= spaceBelow) {
    +					ul.style.top = rect.height + 'px';
    +					ul.style.maxHeight = spaceBelow + 'px';
    +				}
    +				else if (fullHeight <= spaceAbove) {
    +					ul.style.bottom = rect.height + 'px';
    +					ul.style.maxHeight = spaceAbove + 'px';
    +				}
    +				else if (spaceBelow >= spaceAbove) {
    +					ul.style.top = rect.height + 'px';
    +					ul.style.maxHeight = (spaceBelow - (spaceBelow % itemHeight)) + 'px';
    +				}
    +				else {
    +					ul.style.bottom = rect.height + 'px';
    +					ul.style.maxHeight = (spaceAbove - (spaceAbove % itemHeight)) + 'px';
    +				}
    +
    +				ul.scrollTop = sel ? Math.max(sel.offsetTop - sel.offsetHeight, 0) : 0;
    +			});
    +		}
    +
    +		var cboxes = ul.querySelectorAll('[selected] input[type="checkbox"]');
    +		for (var i = 0; i < cboxes.length; i++) {
    +			cboxes[i].checked = true;
    +			cboxes[i].disabled = (cboxes.length == 1 && !this.options.optional);
    +		};
    +
    +		ul.classList.add('dropdown');
    +
    +		sb.insertBefore(pv, ul.nextElementSibling);
    +
    +		li.forEach(function(l) {
    +			l.setAttribute('tabindex', 0);
    +		});
    +
    +		sb.lastElementChild.setAttribute('tabindex', 0);
    +
    +		this.setFocus(sb, sel || li[0], true);
    +	},
    +
    +	/** @private */
    +	closeDropdown: function(sb, no_focus) {
    +		if (!sb.hasAttribute('open'))
    +			return;
    +
    +		var pv = sb.querySelector('ul.preview'),
    +		    ul = sb.querySelector('ul.dropdown'),
    +		    li = ul.querySelectorAll('li'),
    +		    fl = findParent(sb, '.cbi-value-field');
    +
    +		li.forEach(function(l) { l.removeAttribute('tabindex'); });
    +		sb.lastElementChild.removeAttribute('tabindex');
    +
    +		sb.removeChild(pv);
    +		sb.removeAttribute('open');
    +		sb.style.width = sb.style.height = '';
    +
    +		ul.classList.remove('dropdown');
    +		ul.style.top = ul.style.bottom = ul.style.maxHeight = '';
    +
    +		if (fl)
    +			fl.classList.remove('cbi-dropdown-open');
    +
    +		if (!no_focus)
    +			this.setFocus(sb, sb);
    +
    +		this.saveValues(sb, ul);
    +	},
    +
    +	/** @private */
    +	toggleItem: function(sb, li, force_state) {
    +		if (li.hasAttribute('unselectable'))
    +			return;
    +
    +		if (this.options.multiple) {
    +			var cbox = li.querySelector('input[type="checkbox"]'),
    +			    items = li.parentNode.querySelectorAll('li'),
    +			    label = sb.querySelector('ul.preview'),
    +			    sel = li.parentNode.querySelectorAll('[selected]').length,
    +			    more = sb.querySelector('.more'),
    +			    ndisplay = this.options.display_items,
    +			    n = 0;
    +
    +			if (li.hasAttribute('selected')) {
    +				if (force_state !== true) {
    +					if (sel > 1 || this.options.optional) {
    +						li.removeAttribute('selected');
    +						cbox.checked = cbox.disabled = false;
    +						sel--;
    +					}
    +					else {
    +						cbox.disabled = true;
    +					}
    +				}
    +			}
    +			else {
    +				if (force_state !== false) {
    +					li.setAttribute('selected', '');
    +					cbox.checked = true;
    +					cbox.disabled = false;
    +					sel++;
    +				}
    +			}
    +
    +			while (label && label.firstElementChild)
    +				label.removeChild(label.firstElementChild);
    +
    +			for (var i = 0; i < items.length; i++) {
    +				items[i].removeAttribute('display');
    +				if (items[i].hasAttribute('selected')) {
    +					if (ndisplay-- > 0) {
    +						items[i].setAttribute('display', n++);
    +						if (label)
    +							label.appendChild(items[i].cloneNode(true));
    +					}
    +					var c = items[i].querySelector('input[type="checkbox"]');
    +					if (c)
    +						c.disabled = (sel == 1 && !this.options.optional);
    +				}
    +			}
    +
    +			if (ndisplay < 0)
    +				sb.setAttribute('more', '');
    +			else
    +				sb.removeAttribute('more');
    +
    +			if (ndisplay === this.options.display_items)
    +				sb.setAttribute('empty', '');
    +			else
    +				sb.removeAttribute('empty');
    +
    +			L.dom.content(more, (ndisplay === this.options.display_items)
    +				? (this.options.select_placeholder || this.options.placeholder) : '···');
    +		}
    +		else {
    +			var sel = li.parentNode.querySelector('[selected]');
    +			if (sel) {
    +				sel.removeAttribute('display');
    +				sel.removeAttribute('selected');
    +			}
    +
    +			li.setAttribute('display', 0);
    +			li.setAttribute('selected', '');
    +
    +			this.closeDropdown(sb, true);
    +		}
    +
    +		this.saveValues(sb, li.parentNode);
    +	},
    +
    +	/** @private */
    +	transformItem: function(sb, li) {
    +		var cbox = E('form', {}, E('input', { type: 'checkbox', tabindex: -1, onclick: 'event.preventDefault()' })),
    +		    label = E('label');
    +
    +		while (li.firstChild)
    +			label.appendChild(li.firstChild);
    +
    +		li.appendChild(cbox);
    +		li.appendChild(label);
    +	},
    +
    +	/** @private */
    +	saveValues: function(sb, ul) {
    +		var sel = ul.querySelectorAll('li[selected]'),
    +		    div = sb.lastElementChild,
    +		    name = this.options.name,
    +		    strval = '',
    +		    values = [];
    +
    +		while (div.lastElementChild)
    +			div.removeChild(div.lastElementChild);
    +
    +		sel.forEach(function (s) {
    +			if (s.hasAttribute('placeholder'))
    +				return;
    +
    +			var v = {
    +				text: s.innerText,
    +				value: s.hasAttribute('data-value') ? s.getAttribute('data-value') : s.innerText,
    +				element: s
    +			};
    +
    +			div.appendChild(E('input', {
    +				type: 'hidden',
    +				name: name,
    +				value: v.value
    +			}));
    +
    +			values.push(v);
    +
    +			strval += strval.length ? ' ' + v.value : v.value;
    +		});
    +
    +		var detail = {
    +			instance: this,
    +			element: sb
    +		};
    +
    +		if (this.options.multiple)
    +			detail.values = values;
    +		else
    +			detail.value = values.length ? values[0] : null;
    +
    +		sb.value = strval;
    +
    +		sb.dispatchEvent(new CustomEvent('cbi-dropdown-change', {
    +			bubbles: true,
    +			detail: detail
    +		}));
    +	},
    +
    +	/** @private */
    +	setValues: function(sb, values) {
    +		var ul = sb.querySelector('ul');
    +
    +		if (this.options.create) {
    +			for (var value in values) {
    +				this.createItems(sb, value);
    +
    +				if (!this.options.multiple)
    +					break;
    +			}
    +		}
    +
    +		if (this.options.multiple) {
    +			var lis = ul.querySelectorAll('li[data-value]');
    +			for (var i = 0; i < lis.length; i++) {
    +				var value = lis[i].getAttribute('data-value');
    +				if (values === null || !(value in values))
    +					this.toggleItem(sb, lis[i], false);
    +				else
    +					this.toggleItem(sb, lis[i], true);
    +			}
    +		}
    +		else {
    +			var ph = ul.querySelector('li[placeholder]');
    +			if (ph)
    +				this.toggleItem(sb, ph);
    +
    +			var lis = ul.querySelectorAll('li[data-value]');
    +			for (var i = 0; i < lis.length; i++) {
    +				var value = lis[i].getAttribute('data-value');
    +				if (values !== null && (value in values))
    +					this.toggleItem(sb, lis[i]);
    +			}
    +		}
    +	},
    +
    +	/** @private */
    +	setFocus: function(sb, elem, scroll) {
    +		if (sb && sb.hasAttribute && sb.hasAttribute('locked-in'))
    +			return;
    +
    +		if (sb.target && findParent(sb.target, 'ul.dropdown'))
    +			return;
    +
    +		document.querySelectorAll('.focus').forEach(function(e) {
    +			if (!matchesElem(e, 'input')) {
    +				e.classList.remove('focus');
    +				e.blur();
    +			}
    +		});
    +
    +		if (elem) {
    +			elem.focus();
    +			elem.classList.add('focus');
    +
    +			if (scroll)
    +				elem.parentNode.scrollTop = elem.offsetTop - elem.parentNode.offsetTop;
    +		}
    +	},
    +
    +	/** @private */
    +	createChoiceElement: function(sb, value, label) {
    +		var tpl = sb.querySelector(this.options.create_template),
    +		    markup = null;
    +
    +		if (tpl)
    +			markup = (tpl.textContent || tpl.innerHTML || tpl.firstChild.data).replace(/^<!--|-->$/, '').trim();
    +		else
    +			markup = '<li data-value="{{value}}"><span data-label-placeholder="true" /></li>';
    +
    +		var new_item = E(markup.replace(/{{value}}/g, '%h'.format(value))),
    +		    placeholder = new_item.querySelector('[data-label-placeholder]');
    +
    +		if (placeholder) {
    +			var content = E('span', {}, label || this.choices[value] || [ value ]);
    +
    +			while (content.firstChild)
    +				placeholder.parentNode.insertBefore(content.firstChild, placeholder);
    +
    +			placeholder.parentNode.removeChild(placeholder);
    +		}
    +
    +		if (this.options.multiple)
    +			this.transformItem(sb, new_item);
    +
    +		return new_item;
    +	},
    +
    +	/** @private */
    +	createItems: function(sb, value) {
    +		var sbox = this,
    +		    val = (value || '').trim(),
    +		    ul = sb.querySelector('ul');
    +
    +		if (!sbox.options.multiple)
    +			val = val.length ? [ val ] : [];
    +		else
    +			val = val.length ? val.split(/\s+/) : [];
    +
    +		val.forEach(function(item) {
    +			var new_item = null;
    +
    +			ul.childNodes.forEach(function(li) {
    +				if (li.getAttribute && li.getAttribute('data-value') === item)
    +					new_item = li;
    +			});
    +
    +			if (!new_item) {
    +				new_item = sbox.createChoiceElement(sb, item);
    +
    +				if (!sbox.options.multiple) {
    +					var old = ul.querySelector('li[created]');
    +					if (old)
    +						ul.removeChild(old);
    +
    +					new_item.setAttribute('created', '');
    +				}
    +
    +				new_item = ul.insertBefore(new_item, ul.lastElementChild);
    +			}
    +
    +			sbox.toggleItem(sb, new_item, true);
    +			sbox.setFocus(sb, new_item, true);
    +		});
    +	},
    +
    +	/**
    +	 * Remove all existing choices from the dropdown menu.
    +	 *
    +	 * This function removes all preexisting dropdown choices from the widget,
    +	 * keeping only choices currently being selected unless `reset_values` is
    +	 * given, in which case all choices and deselected and removed.
    +	 *
    +	 * @instance
    +	 * @memberof LuCI.ui.Dropdown
    +	 * @param {boolean} [reset_value=false]
    +	 * If set to `true`, deselect and remove selected choices as well instead
    +	 * of keeping them.
    +	 */
    +	clearChoices: function(reset_value) {
    +		var ul = this.node.querySelector('ul'),
    +		    lis = ul ? ul.querySelectorAll('li[data-value]') : [],
    +		    len = lis.length - (this.options.create ? 1 : 0),
    +		    val = reset_value ? null : this.getValue();
    +
    +		for (var i = 0; i < len; i++) {
    +			var lival = lis[i].getAttribute('data-value');
    +			if (val == null ||
    +				(!this.options.multiple && val != lival) ||
    +				(this.options.multiple && val.indexOf(lival) == -1))
    +				ul.removeChild(lis[i]);
    +		}
    +
    +		if (reset_value)
    +			this.setValues(this.node, {});
    +	},
    +
    +	/**
    +	 * Add new choices to the dropdown menu.
    +	 *
    +	 * This function adds further choices to an existing dropdown menu,
    +	 * ignoring choice values which are already present.
    +	 *
    +	 * @instance
    +	 * @memberof LuCI.ui.Dropdown
    +	 * @param {string[]} values
    +	 * The choice values to add to the dropdown widget.
    +	 *
    +	 * @param {Object<string, *>} labels
    +	 * The choice label values to use when adding dropdown choices. If no
    +	 * label is found for a particular choice value, the value itself is used
    +	 * as label text. Choice labels may be any valid value accepted by
    +	 * {@link LuCI.dom#content}.
    +	 */
    +	addChoices: function(values, labels) {
    +		var sb = this.node,
    +		    ul = sb.querySelector('ul'),
    +		    lis = ul ? ul.querySelectorAll('li[data-value]') : [];
    +
    +		if (!Array.isArray(values))
    +			values = L.toArray(values);
    +
    +		if (!L.isObject(labels))
    +			labels = {};
    +
    +		for (var i = 0; i < values.length; i++) {
    +			var found = false;
    +
    +			for (var j = 0; j < lis.length; j++) {
    +				if (lis[j].getAttribute('data-value') === values[i]) {
    +					found = true;
    +					break;
    +				}
    +			}
    +
    +			if (found)
    +				continue;
    +
    +			ul.insertBefore(
    +				this.createChoiceElement(sb, values[i], labels[values[i]]),
    +				ul.lastElementChild);
    +		}
    +	},
    +
    +	/**
    +	 * Close all open dropdown widgets in the current document.
    +	 */
    +	closeAllDropdowns: function() {
    +		document.querySelectorAll('.cbi-dropdown[open]').forEach(function(s) {
    +			s.dispatchEvent(new CustomEvent('cbi-dropdown-close', {}));
    +		});
    +	},
    +
    +	/** @private */
    +	handleClick: function(ev) {
    +		var sb = ev.currentTarget;
    +
    +		if (!sb.hasAttribute('open')) {
    +			if (!matchesElem(ev.target, 'input'))
    +				this.openDropdown(sb);
    +		}
    +		else {
    +			var li = findParent(ev.target, 'li');
    +			if (li && li.parentNode.classList.contains('dropdown'))
    +				this.toggleItem(sb, li);
    +			else if (li && li.parentNode.classList.contains('preview'))
    +				this.closeDropdown(sb);
    +			else if (matchesElem(ev.target, 'span.open, span.more'))
    +				this.closeDropdown(sb);
    +		}
    +
    +		ev.preventDefault();
    +		ev.stopPropagation();
    +	},
    +
    +	/** @private */
    +	handleKeydown: function(ev) {
    +		var sb = ev.currentTarget;
    +
    +		if (matchesElem(ev.target, 'input'))
    +			return;
    +
    +		if (!sb.hasAttribute('open')) {
    +			switch (ev.keyCode) {
    +			case 37:
    +			case 38:
    +			case 39:
    +			case 40:
    +				this.openDropdown(sb);
    +				ev.preventDefault();
    +			}
    +		}
    +		else {
    +			var active = findParent(document.activeElement, 'li');
    +
    +			switch (ev.keyCode) {
    +			case 27:
    +				this.closeDropdown(sb);
    +				break;
    +
    +			case 13:
    +				if (active) {
    +					if (!active.hasAttribute('selected'))
    +						this.toggleItem(sb, active);
    +					this.closeDropdown(sb);
    +					ev.preventDefault();
    +				}
    +				break;
    +
    +			case 32:
    +				if (active) {
    +					this.toggleItem(sb, active);
    +					ev.preventDefault();
    +				}
    +				break;
    +
    +			case 38:
    +				if (active && active.previousElementSibling) {
    +					this.setFocus(sb, active.previousElementSibling);
    +					ev.preventDefault();
    +				}
    +				break;
    +
    +			case 40:
    +				if (active && active.nextElementSibling) {
    +					this.setFocus(sb, active.nextElementSibling);
    +					ev.preventDefault();
    +				}
    +				break;
    +			}
    +		}
    +	},
    +
    +	/** @private */
    +	handleDropdownClose: function(ev) {
    +		var sb = ev.currentTarget;
    +
    +		this.closeDropdown(sb, true);
    +	},
    +
    +	/** @private */
    +	handleDropdownSelect: function(ev) {
    +		var sb = ev.currentTarget,
    +		    li = findParent(ev.target, 'li');
    +
    +		if (!li)
    +			return;
    +
    +		this.toggleItem(sb, li);
    +		this.closeDropdown(sb, true);
    +	},
    +
    +	/** @private */
    +	handleMouseover: function(ev) {
    +		var sb = ev.currentTarget;
    +
    +		if (!sb.hasAttribute('open'))
    +			return;
    +
    +		var li = findParent(ev.target, 'li');
    +
    +		if (li && li.parentNode.classList.contains('dropdown'))
    +			this.setFocus(sb, li);
    +	},
    +
    +	/** @private */
    +	handleFocus: function(ev) {
    +		var sb = ev.currentTarget;
    +
    +		document.querySelectorAll('.cbi-dropdown[open]').forEach(function(s) {
    +			if (s !== sb || sb.hasAttribute('open'))
    +				s.dispatchEvent(new CustomEvent('cbi-dropdown-close', {}));
    +		});
    +	},
    +
    +	/** @private */
    +	handleCanaryFocus: function(ev) {
    +		this.closeDropdown(ev.currentTarget.parentNode);
    +	},
    +
    +	/** @private */
    +	handleCreateKeydown: function(ev) {
    +		var input = ev.currentTarget,
    +		    sb = findParent(input, '.cbi-dropdown');
    +
    +		switch (ev.keyCode) {
    +		case 13:
    +			ev.preventDefault();
    +
    +			if (input.classList.contains('cbi-input-invalid'))
    +				return;
    +
    +			this.createItems(sb, input.value);
    +			input.value = '';
    +			input.blur();
    +			break;
    +		}
    +	},
    +
    +	/** @private */
    +	handleCreateFocus: function(ev) {
    +		var input = ev.currentTarget,
    +		    cbox = findParent(input, 'li').querySelector('input[type="checkbox"]'),
    +		    sb = findParent(input, '.cbi-dropdown');
    +
    +		if (cbox)
    +			cbox.checked = true;
    +
    +		sb.setAttribute('locked-in', '');
    +	},
    +
    +	/** @private */
    +	handleCreateBlur: function(ev) {
    +		var input = ev.currentTarget,
    +		    cbox = findParent(input, 'li').querySelector('input[type="checkbox"]'),
    +		    sb = findParent(input, '.cbi-dropdown');
    +
    +		if (cbox)
    +			cbox.checked = false;
    +
    +		sb.removeAttribute('locked-in');
    +	},
    +
    +	/** @private */
    +	handleCreateClick: function(ev) {
    +		ev.currentTarget.querySelector(this.options.create_query).focus();
    +	},
    +
    +	/** @override */
    +	setValue: function(values) {
    +		if (this.options.multiple) {
    +			if (!Array.isArray(values))
    +				values = (values != null && values != '') ? [ values ] : [];
    +
    +			var v = {};
    +
    +			for (var i = 0; i < values.length; i++)
    +				v[values[i]] = true;
    +
    +			this.setValues(this.node, v);
    +		}
    +		else {
    +			var v = {};
    +
    +			if (values != null) {
    +				if (Array.isArray(values))
    +					v[values[0]] = true;
    +				else
    +					v[values] = true;
    +			}
    +
    +			this.setValues(this.node, v);
    +		}
    +	},
    +
    +	/** @override */
    +	getValue: function() {
    +		var div = this.node.lastElementChild,
    +		    h = div.querySelectorAll('input[type="hidden"]'),
    +			v = [];
    +
    +		for (var i = 0; i < h.length; i++)
    +			v.push(h[i].value);
    +
    +		return this.options.multiple ? v : v[0];
    +	}
    +});
    +
    +/**
    + * Instantiate a rich dropdown choice widget allowing custom values.
    + *
    + * @constructor Combobox
    + * @memberof LuCI.ui
    + * @augments LuCI.ui.Dropdown
    + *
    + * @classdesc
    + *
    + * The `Combobox` class implements a rich, stylable dropdown menu which allows
    + * to enter custom values. Historically, comboboxes used to be a dedicated
    + * widget type in LuCI but nowadays they are direct aliases of dropdown widgets
    + * with a set of enforced default properties for easier instantiation.
    + *
    + * UI widget instances are usually not supposed to be created by view code
    + * directly, instead they're implicitely created by `LuCI.form` when
    + * instantiating CBI forms.
    + *
    + * This class is automatically instantiated as part of `LuCI.ui`. To use it
    + * in views, use `'require ui'` and refer to `ui.Combobox`. To import it in
    + * external JavaScript, use `L.require("ui").then(...)` and access the
    + * `Combobox` property of the class instance value.
    + *
    + * @param {string|string[]} [value=null]
    + * The initial input value(s).
    + *
    + * @param {Object<string, *>} choices
    + * Object containing the selectable choices of the widget. The object keys
    + * serve as values for the different choices while the values are used as
    + * choice labels.
    + *
    + * @param {LuCI.ui.Combobox.InitOptions} [options]
    + * Object describing the widget specific options to initialize the dropdown.
    + */
    +var UICombobox = UIDropdown.extend(/** @lends LuCI.ui.Combobox.prototype */ {
    +	/**
    +	 * Comboboxes support the same properties as
    +	 * [Dropdown.InitOptions]{@link LuCI.ui.Dropdown.InitOptions} but enforce
    +	 * specific values for the following properties:
    +	 *
    +	 * @typedef {LuCI.ui.Dropdown.InitOptions} InitOptions
    +	 * @memberof LuCI.ui.Combobox
    +	 *
    +	 * @property {boolean} multiple=false
    +	 * Since Comboboxes never allow selecting multiple values, this property
    +	 * is forcibly set to `false`.
    +	 *
    +	 * @property {boolean} create=true
    +	 * Since Comboboxes always allow custom choice values, this property is
    +	 * forcibly set to `true`.
    +	 *
    +	 * @property {boolean} optional=true
    +	 * Since Comboboxes are always optional, this property is forcibly set to
    +	 * `true`.
    +	 */
    +	__init__: function(value, choices, options) {
    +		this.super('__init__', [ value, choices, Object.assign({
    +			select_placeholder: _('-- Please choose --'),
    +			custom_placeholder: _('-- custom --'),
    +			dropdown_items: -1,
    +			sort: true
    +		}, options, {
    +			multiple: false,
    +			create: true,
    +			optional: true
    +		}) ]);
    +	}
    +});
    +
    +/**
    + * Instantiate a combo button widget offering multiple action choices.
    + *
    + * @constructor ComboButton
    + * @memberof LuCI.ui
    + * @augments LuCI.ui.Dropdown
    + *
    + * @classdesc
    + *
    + * The `ComboButton` class implements a button element which can be expanded
    + * into a dropdown to chose from a set of different action choices.
    + *
    + * UI widget instances are usually not supposed to be created by view code
    + * directly, instead they're implicitely created by `LuCI.form` when
    + * instantiating CBI forms.
    + *
    + * This class is automatically instantiated as part of `LuCI.ui`. To use it
    + * in views, use `'require ui'` and refer to `ui.ComboButton`. To import it in
    + * external JavaScript, use `L.require("ui").then(...)` and access the
    + * `ComboButton` property of the class instance value.
    + *
    + * @param {string|string[]} [value=null]
    + * The initial input value(s).
    + *
    + * @param {Object<string, *>} choices
    + * Object containing the selectable choices of the widget. The object keys
    + * serve as values for the different choices while the values are used as
    + * choice labels.
    + *
    + * @param {LuCI.ui.ComboButton.InitOptions} [options]
    + * Object describing the widget specific options to initialize the button.
    + */
    +var UIComboButton = UIDropdown.extend(/** @lends LuCI.ui.ComboButton.prototype */ {
    +	/**
    +	 * ComboButtons support the same properties as
    +	 * [Dropdown.InitOptions]{@link LuCI.ui.Dropdown.InitOptions} but enforce
    +	 * specific values for some properties and add aditional button specific
    +	 * properties.
    +	 *
    +	 * @typedef {LuCI.ui.Dropdown.InitOptions} InitOptions
    +	 * @memberof LuCI.ui.ComboButton
    +	 *
    +	 * @property {boolean} multiple=false
    +	 * Since ComboButtons never allow selecting multiple actions, this property
    +	 * is forcibly set to `false`.
    +	 *
    +	 * @property {boolean} create=false
    +	 * Since ComboButtons never allow creating custom choices, this property
    +	 * is forcibly set to `false`.
    +	 *
    +	 * @property {boolean} optional=false
    +	 * Since ComboButtons must always select one action, this property is
    +	 * forcibly set to `false`.
    +	 *
    +	 * @property {Object<string, string>} [classes]
    +	 * Specifies a mapping of choice values to CSS class names. If an action
    +	 * choice is selected by the user and if a corresponding entry exists in
    +	 * the `classes` object, the class names corresponding to the selected
    +	 * value are set on the button element.
    +	 *
    +	 * This is useful to apply different button styles, such as colors, to the
    +	 * combined button depending on the selected action.
    +	 *
    +	 * @property {function} [click]
    +	 * Specifies a handler function to invoke when the user clicks the button.
    +	 * This function will be called with the button DOM node as `this` context
    +	 * and receive the DOM click event as first as well as the selected action
    +	 * choice value as second argument.
    +	 */
    +	__init__: function(value, choices, options) {
    +		this.super('__init__', [ value, choices, Object.assign({
    +			sort: true
    +		}, options, {
    +			multiple: false,
    +			create: false,
    +			optional: false
    +		}) ]);
    +	},
    +
    +	/** @override */
    +	render: function(/* ... */) {
    +		var node = UIDropdown.prototype.render.apply(this, arguments),
    +		    val = this.getValue();
    +
    +		if (L.isObject(this.options.classes) && this.options.classes.hasOwnProperty(val))
    +			node.setAttribute('class', 'cbi-dropdown ' + this.options.classes[val]);
    +
    +		return node;
    +	},
    +
    +	/** @private */
    +	handleClick: function(ev) {
    +		var sb = ev.currentTarget,
    +		    t = ev.target;
    +
    +		if (sb.hasAttribute('open') || L.dom.matches(t, '.cbi-dropdown > span.open'))
    +			return UIDropdown.prototype.handleClick.apply(this, arguments);
    +
    +		if (this.options.click)
    +			return this.options.click.call(sb, ev, this.getValue());
    +	},
    +
    +	/** @private */
    +	toggleItem: function(sb /*, ... */) {
    +		var rv = UIDropdown.prototype.toggleItem.apply(this, arguments),
    +		    val = this.getValue();
    +
    +		if (L.isObject(this.options.classes) && this.options.classes.hasOwnProperty(val))
    +			sb.setAttribute('class', 'cbi-dropdown ' + this.options.classes[val]);
    +		else
    +			sb.setAttribute('class', 'cbi-dropdown');
    +
    +		return rv;
    +	}
    +});
    +
    +/**
    + * Instantiate a dynamic list widget.
    + *
    + * @constructor DynamicList
    + * @memberof LuCI.ui
    + * @augments LuCI.ui.AbstractElement
    + *
    + * @classdesc
    + *
    + * The `DynamicList` class implements a widget which allows the user to specify
    + * an arbitrary amount of input values, either from free formed text input or
    + * from a set of predefined choices.
    + *
    + * UI widget instances are usually not supposed to be created by view code
    + * directly, instead they're implicitely created by `LuCI.form` when
    + * instantiating CBI forms.
    + *
    + * This class is automatically instantiated as part of `LuCI.ui`. To use it
    + * in views, use `'require ui'` and refer to `ui.DynamicList`. To import it in
    + * external JavaScript, use `L.require("ui").then(...)` and access the
    + * `DynamicList` property of the class instance value.
    + *
    + * @param {string|string[]} [value=null]
    + * The initial input value(s).
    + *
    + * @param {Object<string, *>} [choices]
    + * Object containing the selectable choices of the widget. The object keys
    + * serve as values for the different choices while the values are used as
    + * choice labels. If omitted, no default choices are presented to the user,
    + * instead a plain text input field is rendered allowing the user to add
    + * arbitrary values to the dynamic list.
    + *
    + * @param {LuCI.ui.DynamicList.InitOptions} [options]
    + * Object describing the widget specific options to initialize the dynamic list.
    + */
    +var UIDynamicList = UIElement.extend(/** @lends LuCI.ui.DynamicList.prototype */ {
    +	/**
    +	 * In case choices are passed to the dynamic list contructor, the widget
    +	 * supports the same properties as [Dropdown.InitOptions]{@link LuCI.ui.Dropdown.InitOptions}
    +	 * but enforces specific values for some dropdown properties.
    +	 *
    +	 * @typedef {LuCI.ui.Dropdown.InitOptions} InitOptions
    +	 * @memberof LuCI.ui.DynamicList
    +	 *
    +	 * @property {boolean} multiple=false
    +	 * Since dynamic lists never allow selecting multiple choices when adding
    +	 * another list item, this property is forcibly set to `false`.
    +	 *
    +	 * @property {boolean} optional=true
    +	 * Since dynamic lists use an embedded dropdown to present a list of
    +	 * predefined choice values, the dropdown must be made optional to allow
    +	 * it to remain unselected.
    +	 */
    +	__init__: function(values, choices, options) {
    +		if (!Array.isArray(values))
    +			values = (values != null && values != '') ? [ values ] : [];
    +
    +		if (typeof(choices) != 'object')
    +			choices = null;
    +
    +		this.values = values;
    +		this.choices = choices;
    +		this.options = Object.assign({}, options, {
    +			multiple: false,
    +			optional: true
    +		});
    +	},
    +
    +	/** @override */
    +	render: function() {
    +		var dl = E('div', {
    +			'id': this.options.id,
    +			'class': 'cbi-dynlist'
    +		}, E('div', { 'class': 'add-item' }));
    +
    +		if (this.choices) {
    +			if (this.options.placeholder != null)
    +				this.options.select_placeholder = this.options.placeholder;
    +
    +			var cbox = new UICombobox(null, this.choices, this.options);
    +
    +			dl.lastElementChild.appendChild(cbox.render());
    +		}
    +		else {
    +			var inputEl = E('input', {
    +				'id': this.options.id ? 'widget.' + this.options.id : null,
    +				'type': 'text',
    +				'class': 'cbi-input-text',
    +				'placeholder': this.options.placeholder
    +			});
    +
    +			dl.lastElementChild.appendChild(inputEl);
    +			dl.lastElementChild.appendChild(E('div', { 'class': 'btn cbi-button cbi-button-add' }, '+'));
    +
    +			if (this.options.datatype || this.options.validate)
    +				L.ui.addValidator(inputEl, this.options.datatype || 'string',
    +				                  true, this.options.validate, 'blur', 'keyup');
    +		}
    +
    +		for (var i = 0; i < this.values.length; i++) {
    +			var label = this.choices ? this.choices[this.values[i]] : null;
    +
    +			if (L.dom.elem(label))
    +				label = label.cloneNode(true);
    +
    +			this.addItem(dl, this.values[i], label);
    +		}
    +
    +		return this.bind(dl);
    +	},
    +
    +	/** @private */
    +	bind: function(dl) {
    +		dl.addEventListener('click', L.bind(this.handleClick, this));
    +		dl.addEventListener('keydown', L.bind(this.handleKeydown, this));
    +		dl.addEventListener('cbi-dropdown-change', L.bind(this.handleDropdownChange, this));
    +
    +		this.node = dl;
    +
    +		this.setUpdateEvents(dl, 'cbi-dynlist-change');
    +		this.setChangeEvents(dl, 'cbi-dynlist-change');
    +
    +		L.dom.bindClassInstance(dl, this);
    +
    +		return dl;
    +	},
    +
    +	/** @private */
    +	addItem: function(dl, value, text, flash) {
    +		var exists = false,
    +		    new_item = E('div', { 'class': flash ? 'item flash' : 'item', 'tabindex': 0 }, [
    +				E('span', {}, [ text || value ]),
    +				E('input', {
    +					'type': 'hidden',
    +					'name': this.options.name,
    +					'value': value })]);
    +
    +		dl.querySelectorAll('.item').forEach(function(item) {
    +			if (exists)
    +				return;
    +
    +			var hidden = item.querySelector('input[type="hidden"]');
    +
    +			if (hidden && hidden.parentNode !== item)
    +				hidden = null;
    +
    +			if (hidden && hidden.value === value)
    +				exists = true;
    +		});
    +
    +		if (!exists) {
    +			var ai = dl.querySelector('.add-item');
    +			ai.parentNode.insertBefore(new_item, ai);
    +		}
    +
    +		dl.dispatchEvent(new CustomEvent('cbi-dynlist-change', {
    +			bubbles: true,
    +			detail: {
    +				instance: this,
    +				element: dl,
    +				value: value,
    +				add: true
    +			}
    +		}));
    +	},
    +
    +	/** @private */
    +	removeItem: function(dl, item) {
    +		var value = item.querySelector('input[type="hidden"]').value;
    +		var sb = dl.querySelector('.cbi-dropdown');
    +		if (sb)
    +			sb.querySelectorAll('ul > li').forEach(function(li) {
    +				if (li.getAttribute('data-value') === value) {
    +					if (li.hasAttribute('dynlistcustom'))
    +						li.parentNode.removeChild(li);
    +					else
    +						li.removeAttribute('unselectable');
    +				}
    +			});
    +
    +		item.parentNode.removeChild(item);
    +
    +		dl.dispatchEvent(new CustomEvent('cbi-dynlist-change', {
    +			bubbles: true,
    +			detail: {
    +				instance: this,
    +				element: dl,
    +				value: value,
    +				remove: true
    +			}
    +		}));
    +	},
    +
    +	/** @private */
    +	handleClick: function(ev) {
    +		var dl = ev.currentTarget,
    +		    item = findParent(ev.target, '.item');
    +
    +		if (item) {
    +			this.removeItem(dl, item);
    +		}
    +		else if (matchesElem(ev.target, '.cbi-button-add')) {
    +			var input = ev.target.previousElementSibling;
    +			if (input.value.length && !input.classList.contains('cbi-input-invalid')) {
    +				this.addItem(dl, input.value, null, true);
    +				input.value = '';
    +			}
    +		}
    +	},
    +
    +	/** @private */
    +	handleDropdownChange: function(ev) {
    +		var dl = ev.currentTarget,
    +		    sbIn = ev.detail.instance,
    +		    sbEl = ev.detail.element,
    +		    sbVal = ev.detail.value;
    +
    +		if (sbVal === null)
    +			return;
    +
    +		sbIn.setValues(sbEl, null);
    +		sbVal.element.setAttribute('unselectable', '');
    +
    +		if (sbVal.element.hasAttribute('created')) {
    +			sbVal.element.removeAttribute('created');
    +			sbVal.element.setAttribute('dynlistcustom', '');
    +		}
    +
    +		var label = sbVal.text;
    +
    +		if (sbVal.element) {
    +			label = E([]);
    +
    +			for (var i = 0; i < sbVal.element.childNodes.length; i++)
    +				label.appendChild(sbVal.element.childNodes[i].cloneNode(true));
    +		}
    +
    +		this.addItem(dl, sbVal.value, label, true);
    +	},
    +
    +	/** @private */
    +	handleKeydown: function(ev) {
    +		var dl = ev.currentTarget,
    +		    item = findParent(ev.target, '.item');
    +
    +		if (item) {
    +			switch (ev.keyCode) {
    +			case 8: /* backspace */
    +				if (item.previousElementSibling)
    +					item.previousElementSibling.focus();
    +
    +				this.removeItem(dl, item);
    +				break;
    +
    +			case 46: /* delete */
    +				if (item.nextElementSibling) {
    +					if (item.nextElementSibling.classList.contains('item'))
    +						item.nextElementSibling.focus();
    +					else
    +						item.nextElementSibling.firstElementChild.focus();
    +				}
    +
    +				this.removeItem(dl, item);
    +				break;
    +			}
    +		}
    +		else if (matchesElem(ev.target, '.cbi-input-text')) {
    +			switch (ev.keyCode) {
    +			case 13: /* enter */
    +				if (ev.target.value.length && !ev.target.classList.contains('cbi-input-invalid')) {
    +					this.addItem(dl, ev.target.value, null, true);
    +					ev.target.value = '';
    +					ev.target.blur();
    +					ev.target.focus();
    +				}
    +
    +				ev.preventDefault();
    +				break;
    +			}
    +		}
    +	},
    +
    +	/** @override */
    +	getValue: function() {
    +		var items = this.node.querySelectorAll('.item > input[type="hidden"]'),
    +		    input = this.node.querySelector('.add-item > input[type="text"]'),
    +		    v = [];
    +
    +		for (var i = 0; i < items.length; i++)
    +			v.push(items[i].value);
    +
    +		if (input && input.value != null && input.value.match(/\S/) &&
    +		    input.classList.contains('cbi-input-invalid') == false &&
    +		    v.filter(function(s) { return s == input.value }).length == 0)
    +			v.push(input.value);
    +
    +		return v;
    +	},
    +
    +	/** @override */
    +	setValue: function(values) {
    +		if (!Array.isArray(values))
    +			values = (values != null && values != '') ? [ values ] : [];
    +
    +		var items = this.node.querySelectorAll('.item');
    +
    +		for (var i = 0; i < items.length; i++)
    +			if (items[i].parentNode === this.node)
    +				this.removeItem(this.node, items[i]);
    +
    +		for (var i = 0; i < values.length; i++)
    +			this.addItem(this.node, values[i],
    +				this.choices ? this.choices[values[i]] : null);
    +	},
    +
    +	/**
    +	 * Add new suggested choices to the dynamic list.
    +	 *
    +	 * This function adds further choices to an existing dynamic list,
    +	 * ignoring choice values which are already present.
    +	 *
    +	 * @instance
    +	 * @memberof LuCI.ui.DynamicList
    +	 * @param {string[]} values
    +	 * The choice values to add to the dynamic lists suggestion dropdown.
    +	 *
    +	 * @param {Object<string, *>} labels
    +	 * The choice label values to use when adding suggested choices. If no
    +	 * label is found for a particular choice value, the value itself is used
    +	 * as label text. Choice labels may be any valid value accepted by
    +	 * {@link LuCI.dom#content}.
    +	 */
    +	addChoices: function(values, labels) {
    +		var dl = this.node.lastElementChild.firstElementChild;
    +		L.dom.callClassMethod(dl, 'addChoices', values, labels);
    +	},
    +
    +	/**
    +	 * Remove all existing choices from the dynamic list.
    +	 *
    +	 * This function removes all preexisting suggested choices from the widget.
    +	 *
    +	 * @instance
    +	 * @memberof LuCI.ui.DynamicList
    +	 */
    +	clearChoices: function() {
    +		var dl = this.node.lastElementChild.firstElementChild;
    +		L.dom.callClassMethod(dl, 'clearChoices');
    +	}
    +});
    +
    +/**
    + * Instantiate a hidden input field widget.
    + *
    + * @constructor Hiddenfield
    + * @memberof LuCI.ui
    + * @augments LuCI.ui.AbstractElement
    + *
    + * @classdesc
    + *
    + * The `Hiddenfield` class implements an HTML `<input type="hidden">` field
    + * which allows to store form data without exposing it to the user.
    + *
    + * UI widget instances are usually not supposed to be created by view code
    + * directly, instead they're implicitely created by `LuCI.form` when
    + * instantiating CBI forms.
    + *
    + * This class is automatically instantiated as part of `LuCI.ui`. To use it
    + * in views, use `'require ui'` and refer to `ui.Hiddenfield`. To import it in
    + * external JavaScript, use `L.require("ui").then(...)` and access the
    + * `Hiddenfield` property of the class instance value.
    + *
    + * @param {string|string[]} [value=null]
    + * The initial input value.
    + *
    + * @param {LuCI.ui.AbstractElement.InitOptions} [options]
    + * Object describing the widget specific options to initialize the hidden input.
    + */
    +var UIHiddenfield = UIElement.extend(/** @lends LuCI.ui.Hiddenfield.prototype */ {
    +	__init__: function(value, options) {
    +		this.value = value;
    +		this.options = Object.assign({
    +
    +		}, options);
    +	},
    +
    +	/** @override */
    +	render: function() {
    +		var hiddenEl = E('input', {
    +			'id': this.options.id,
    +			'type': 'hidden',
    +			'value': this.value
    +		});
    +
    +		return this.bind(hiddenEl);
    +	},
    +
    +	/** @private */
    +	bind: function(hiddenEl) {
    +		this.node = hiddenEl;
    +
    +		L.dom.bindClassInstance(hiddenEl, this);
    +
    +		return hiddenEl;
    +	},
    +
    +	/** @override */
    +	getValue: function() {
    +		return this.node.value;
    +	},
    +
    +	/** @override */
    +	setValue: function(value) {
    +		this.node.value = value;
    +	}
    +});
    +
    +/**
    + * Instantiate a file upload widget.
    + *
    + * @constructor FileUpload
    + * @memberof LuCI.ui
    + * @augments LuCI.ui.AbstractElement
    + *
    + * @classdesc
    + *
    + * The `FileUpload` class implements a widget which allows the user to upload,
    + * browse, select and delete files beneath a predefined remote directory.
    + *
    + * UI widget instances are usually not supposed to be created by view code
    + * directly, instead they're implicitely created by `LuCI.form` when
    + * instantiating CBI forms.
    + *
    + * This class is automatically instantiated as part of `LuCI.ui`. To use it
    + * in views, use `'require ui'` and refer to `ui.FileUpload`. To import it in
    + * external JavaScript, use `L.require("ui").then(...)` and access the
    + * `FileUpload` property of the class instance value.
    + *
    + * @param {string|string[]} [value=null]
    + * The initial input value.
    + *
    + * @param {LuCI.ui.DynamicList.InitOptions} [options]
    + * Object describing the widget specific options to initialize the file
    + * upload control.
    + */
    +var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
    +	/**
    +	 * In addition to the [AbstractElement.InitOptions]{@link LuCI.ui.AbstractElement.InitOptions}
    +	 * the following properties are recognized:
    +	 *
    +	 * @typedef {LuCI.ui.AbstractElement.InitOptions} InitOptions
    +	 * @memberof LuCI.ui.FileUpload
    +	 *
    +	 * @property {boolean} [show_hidden=false]
    +	 * Specifies whether hidden files should be displayed when browsing remote
    +	 * files. Note that this is not a security feature, hidden files are always
    +	 * present in the remote file listings received, this option merely controls
    +	 * whether they're displayed or not.
    +	 *
    +	 * @property {boolean} [enable_upload=true]
    +	 * Specifies whether the widget allows the user to upload files. If set to
    +	 * `false`, only existing files may be selected. Note that this is not a
    +	 * security feature. Whether file upload requests are accepted remotely
    +	 * depends on the ACL setup for the current session. This option merely
    +	 * controls whether the upload controls are rendered or not.
    +	 *
    +	 * @property {boolean} [enable_remove=true]
    +	 * Specifies whether the widget allows the user to delete remove files.
    +	 * If set to `false`, existing files may not be removed. Note that this is
    +	 * not a security feature. Whether file delete requests are accepted
    +	 * remotely depends on the ACL setup for the current session. This option
    +	 * merely controls whether the file remove controls are rendered or not.
    +	 *
    +	 * @property {string} [root_directory=/etc/luci-uploads]
    +	 * Specifies the remote directory the upload and file browsing actions take
    +	 * place in. Browsing to directories outside of the root directory is
    +	 * prevented by the widget. Note that this is not a security feature.
    +	 * Whether remote directories are browseable or not solely depends on the
    +	 * ACL setup for the current session.
    +	 */
    +	__init__: function(value, options) {
    +		this.value = value;
    +		this.options = Object.assign({
    +			show_hidden: false,
    +			enable_upload: true,
    +			enable_remove: true,
    +			root_directory: '/etc/luci-uploads'
    +		}, options);
    +	},
    +
    +	/** @private */
    +	bind: function(browserEl) {
    +		this.node = browserEl;
    +
    +		this.setUpdateEvents(browserEl, 'cbi-fileupload-select', 'cbi-fileupload-cancel');
    +		this.setChangeEvents(browserEl, 'cbi-fileupload-select', 'cbi-fileupload-cancel');
    +
    +		L.dom.bindClassInstance(browserEl, this);
    +
    +		return browserEl;
    +	},
    +
    +	/** @override */
    +	render: function() {
    +		return L.resolveDefault(this.value != null ? fs.stat(this.value) : null).then(L.bind(function(stat) {
    +			var label;
    +
    +			if (L.isObject(stat) && stat.type != 'directory')
    +				this.stat = stat;
    +
    +			if (this.stat != null)
    +				label = [ this.iconForType(this.stat.type), ' %s (%1000mB)'.format(this.truncatePath(this.stat.path), this.stat.size) ];
    +			else if (this.value != null)
    +				label = [ this.iconForType('file'), ' %s (%s)'.format(this.truncatePath(this.value), _('File not accessible')) ];
    +			else
    +				label = [ _('Select file…') ];
    +
    +			return this.bind(E('div', { 'id': this.options.id }, [
    +				E('button', {
    +					'class': 'btn',
    +					'click': L.ui.createHandlerFn(this, 'handleFileBrowser')
    +				}, label),
    +				E('div', {
    +					'class': 'cbi-filebrowser'
    +				}),
    +				E('input', {
    +					'type': 'hidden',
    +					'name': this.options.name,
    +					'value': this.value
    +				})
    +			]));
    +		}, this));
    +	},
    +
    +	/** @private */
    +	truncatePath: function(path) {
    +		if (path.length > 50)
    +			path = path.substring(0, 25) + '…' + path.substring(path.length - 25);
    +
    +		return path;
    +	},
    +
    +	/** @private */
    +	iconForType: function(type) {
    +		switch (type) {
    +		case 'symlink':
    +			return E('img', {
    +				'src': L.resource('cbi/link.gif'),
    +				'title': _('Symbolic link'),
    +				'class': 'middle'
    +			});
    +
    +		case 'directory':
    +			return E('img', {
    +				'src': L.resource('cbi/folder.gif'),
    +				'title': _('Directory'),
    +				'class': 'middle'
    +			});
    +
    +		default:
    +			return E('img', {
    +				'src': L.resource('cbi/file.gif'),
    +				'title': _('File'),
    +				'class': 'middle'
    +			});
    +		}
    +	},
    +
    +	/** @private */
    +	canonicalizePath: function(path) {
    +		return path.replace(/\/{2,}/, '/')
    +			.replace(/\/\.(\/|$)/g, '/')
    +			.replace(/[^\/]+\/\.\.(\/|$)/g, '/')
    +			.replace(/\/$/, '');
    +	},
    +
    +	/** @private */
    +	splitPath: function(path) {
    +		var croot = this.canonicalizePath(this.options.root_directory || '/'),
    +		    cpath = this.canonicalizePath(path || '/');
    +
    +		if (cpath.length <= croot.length)
    +			return [ croot ];
    +
    +		if (cpath.charAt(croot.length) != '/')
    +			return [ croot ];
    +
    +		var parts = cpath.substring(croot.length + 1).split(/\//);
    +
    +		parts.unshift(croot);
    +
    +		return parts;
    +	},
    +
    +	/** @private */
    +	handleUpload: function(path, list, ev) {
    +		var form = ev.target.parentNode,
    +		    fileinput = form.querySelector('input[type="file"]'),
    +		    nameinput = form.querySelector('input[type="text"]'),
    +		    filename = (nameinput.value != null ? nameinput.value : '').trim();
    +
    +		ev.preventDefault();
    +
    +		if (filename == '' || filename.match(/\//) || fileinput.files[0] == null)
    +			return;
    +
    +		var existing = list.filter(function(e) { return e.name == filename })[0];
    +
    +		if (existing != null && existing.type == 'directory')
    +			return alert(_('A directory with the same name already exists.'));
    +		else if (existing != null && !confirm(_('Overwrite existing file "%s" ?').format(filename)))
    +			return;
    +
    +		var data = new FormData();
    +
    +		data.append('sessionid', L.env.sessionid);
    +		data.append('filename', path + '/' + filename);
    +		data.append('filedata', fileinput.files[0]);
    +
    +		return L.Request.post(L.env.cgi_base + '/cgi-upload', data, {
    +			progress: L.bind(function(btn, ev) {
    +				btn.firstChild.data = '%.2f%%'.format((ev.loaded / ev.total) * 100);
    +			}, this, ev.target)
    +		}).then(L.bind(function(path, ev, res) {
    +			var reply = res.json();
    +
    +			if (L.isObject(reply) && reply.failure)
    +				alert(_('Upload request failed: %s').format(reply.message));
    +
    +			return this.handleSelect(path, null, ev);
    +		}, this, path, ev));
    +	},
    +
    +	/** @private */
    +	handleDelete: function(path, fileStat, ev) {
    +		var parent = path.replace(/\/[^\/]+$/, '') || '/',
    +		    name = path.replace(/^.+\//, ''),
    +		    msg;
    +
    +		ev.preventDefault();
    +
    +		if (fileStat.type == 'directory')
    +			msg = _('Do you really want to recursively delete the directory "%s" ?').format(name);
    +		else
    +			msg = _('Do you really want to delete "%s" ?').format(name);
    +
    +		if (confirm(msg)) {
    +			var button = this.node.firstElementChild,
    +			    hidden = this.node.lastElementChild;
    +
    +			if (path == hidden.value) {
    +				L.dom.content(button, _('Select file…'));
    +				hidden.value = '';
    +			}
    +
    +			return fs.remove(path).then(L.bind(function(parent, ev) {
    +				return this.handleSelect(parent, null, ev);
    +			}, this, parent, ev)).catch(function(err) {
    +				alert(_('Delete request failed: %s').format(err.message));
    +			});
    +		}
    +	},
    +
    +	/** @private */
    +	renderUpload: function(path, list) {
    +		if (!this.options.enable_upload)
    +			return E([]);
    +
    +		return E([
    +			E('a', {
    +				'href': '#',
    +				'class': 'btn cbi-button-positive',
    +				'click': function(ev) {
    +					var uploadForm = ev.target.nextElementSibling,
    +					    fileInput = uploadForm.querySelector('input[type="file"]');
    +
    +					ev.target.style.display = 'none';
    +					uploadForm.style.display = '';
    +					fileInput.click();
    +				}
    +			}, _('Upload file…')),
    +			E('div', { 'class': 'upload', 'style': 'display:none' }, [
    +				E('input', {
    +					'type': 'file',
    +					'style': 'display:none',
    +					'change': function(ev) {
    +						var nameinput = ev.target.parentNode.querySelector('input[type="text"]'),
    +						    uploadbtn = ev.target.parentNode.querySelector('button.cbi-button-save');
    +
    +						nameinput.value = ev.target.value.replace(/^.+[\/\\]/, '');
    +						uploadbtn.disabled = false;
    +					}
    +				}),
    +				E('button', {
    +					'class': 'btn',
    +					'click': function(ev) {
    +						ev.preventDefault();
    +						ev.target.previousElementSibling.click();
    +					}
    +				}, [ _('Browse…') ]),
    +				E('div', {}, E('input', { 'type': 'text', 'placeholder': _('Filename') })),
    +				E('button', {
    +					'class': 'btn cbi-button-save',
    +					'click': L.ui.createHandlerFn(this, 'handleUpload', path, list),
    +					'disabled': true
    +				}, [ _('Upload file') ])
    +			])
    +		]);
    +	},
    +
    +	/** @private */
    +	renderListing: function(container, path, list) {
    +		var breadcrumb = E('p'),
    +		    rows = E('ul');
    +
    +		list.sort(function(a, b) {
    +			var isDirA = (a.type == 'directory'),
    +			    isDirB = (b.type == 'directory');
    +
    +			if (isDirA != isDirB)
    +				return isDirA < isDirB;
    +
    +			return a.name > b.name;
    +		});
    +
    +		for (var i = 0; i < list.length; i++) {
    +			if (!this.options.show_hidden && list[i].name.charAt(0) == '.')
    +				continue;
    +
    +			var entrypath = this.canonicalizePath(path + '/' + list[i].name),
    +			    selected = (entrypath == this.node.lastElementChild.value),
    +			    mtime = new Date(list[i].mtime * 1000);
    +
    +			rows.appendChild(E('li', [
    +				E('div', { 'class': 'name' }, [
    +					this.iconForType(list[i].type),
    +					' ',
    +					E('a', {
    +						'href': '#',
    +						'style': selected ? 'font-weight:bold' : null,
    +						'click': L.ui.createHandlerFn(this, 'handleSelect',
    +							entrypath, list[i].type != 'directory' ? list[i] : null)
    +					}, '%h'.format(list[i].name))
    +				]),
    +				E('div', { 'class': 'mtime hide-xs' }, [
    +					' %04d-%02d-%02d %02d:%02d:%02d '.format(
    +						mtime.getFullYear(),
    +						mtime.getMonth() + 1,
    +						mtime.getDate(),
    +						mtime.getHours(),
    +						mtime.getMinutes(),
    +						mtime.getSeconds())
    +				]),
    +				E('div', [
    +					selected ? E('button', {
    +						'class': 'btn',
    +						'click': L.ui.createHandlerFn(this, 'handleReset')
    +					}, [ _('Deselect') ]) : '',
    +					this.options.enable_remove ? E('button', {
    +						'class': 'btn cbi-button-negative',
    +						'click': L.ui.createHandlerFn(this, 'handleDelete', entrypath, list[i])
    +					}, [ _('Delete') ]) : ''
    +				])
    +			]));
    +		}
    +
    +		if (!rows.firstElementChild)
    +			rows.appendChild(E('em', _('No entries in this directory')));
    +
    +		var dirs = this.splitPath(path),
    +		    cur = '';
    +
    +		for (var i = 0; i < dirs.length; i++) {
    +			cur = cur ? cur + '/' + dirs[i] : dirs[i];
    +			L.dom.append(breadcrumb, [
    +				i ? ' » ' : '',
    +				E('a', {
    +					'href': '#',
    +					'click': L.ui.createHandlerFn(this, 'handleSelect', cur || '/', null)
    +				}, dirs[i] != '' ? '%h'.format(dirs[i]) : E('em', '(root)')),
    +			]);
    +		}
    +
    +		L.dom.content(container, [
    +			breadcrumb,
    +			rows,
    +			E('div', { 'class': 'right' }, [
    +				this.renderUpload(path, list),
    +				E('a', {
    +					'href': '#',
    +					'class': 'btn',
    +					'click': L.ui.createHandlerFn(this, 'handleCancel')
    +				}, _('Cancel'))
    +			]),
    +		]);
    +	},
    +
    +	/** @private */
    +	handleCancel: function(ev) {
    +		var button = this.node.firstElementChild,
    +		    browser = button.nextElementSibling;
    +
    +		browser.classList.remove('open');
    +		button.style.display = '';
    +
    +		this.node.dispatchEvent(new CustomEvent('cbi-fileupload-cancel', {}));
    +
    +		ev.preventDefault();
    +	},
    +
    +	/** @private */
    +	handleReset: function(ev) {
    +		var button = this.node.firstElementChild,
    +		    hidden = this.node.lastElementChild;
    +
    +		hidden.value = '';
    +		L.dom.content(button, _('Select file…'));
    +
    +		this.handleCancel(ev);
    +	},
    +
    +	/** @private */
    +	handleSelect: function(path, fileStat, ev) {
    +		var browser = L.dom.parent(ev.target, '.cbi-filebrowser'),
    +		    ul = browser.querySelector('ul');
    +
    +		if (fileStat == null) {
    +			L.dom.content(ul, E('em', { 'class': 'spinning' }, _('Loading directory contents…')));
    +			L.resolveDefault(fs.list(path), []).then(L.bind(this.renderListing, this, browser, path));
    +		}
    +		else {
    +			var button = this.node.firstElementChild,
    +			    hidden = this.node.lastElementChild;
    +
    +			path = this.canonicalizePath(path);
    +
    +			L.dom.content(button, [
    +				this.iconForType(fileStat.type),
    +				' %s (%1000mB)'.format(this.truncatePath(path), fileStat.size)
    +			]);
    +
    +			browser.classList.remove('open');
    +			button.style.display = '';
    +			hidden.value = path;
    +
    +			this.stat = Object.assign({ path: path }, fileStat);
    +			this.node.dispatchEvent(new CustomEvent('cbi-fileupload-select', { detail: this.stat }));
    +		}
    +	},
    +
    +	/** @private */
    +	handleFileBrowser: function(ev) {
    +		var button = ev.target,
    +		    browser = button.nextElementSibling,
    +		    path = this.stat ? this.stat.path.replace(/\/[^\/]+$/, '') : (this.options.initial_directory || this.options.root_directory);
    +
    +		if (path.indexOf(this.options.root_directory) != 0)
    +			path = this.options.root_directory;
    +
    +		ev.preventDefault();
    +
    +		return L.resolveDefault(fs.list(path), []).then(L.bind(function(button, browser, path, list) {
    +			document.querySelectorAll('.cbi-filebrowser.open').forEach(function(browserEl) {
    +				L.dom.findClassInstance(browserEl).handleCancel(ev);
    +			});
    +
    +			button.style.display = 'none';
    +			browser.classList.add('open');
    +
    +			return this.renderListing(browser, path, list);
    +		}, this, button, browser, path));
    +	},
    +
    +	/** @override */
    +	getValue: function() {
    +		return this.node.lastElementChild.value;
    +	},
    +
    +	/** @override */
    +	setValue: function(value) {
    +		this.node.lastElementChild.value = value;
    +	}
    +});
    +
    +/**
    + * @class ui
    + * @memberof LuCI
    + * @hideconstructor
    + * @classdesc
    + *
    + * Provides high level UI helper functionality.
    + * To import the class in views, use `'require ui'`, to import it in
    + * external JavaScript, use `L.require("ui").then(...)`.
    + */
    +return L.Class.extend(/** @lends LuCI.ui.prototype */ {
    +	__init__: function() {
    +		modalDiv = document.body.appendChild(
    +			L.dom.create('div', { id: 'modal_overlay' },
    +				L.dom.create('div', { class: 'modal', role: 'dialog', 'aria-modal': true })));
    +
    +		tooltipDiv = document.body.appendChild(
    +			L.dom.create('div', { class: 'cbi-tooltip' }));
    +
    +		/* setup old aliases */
    +		L.showModal = this.showModal;
    +		L.hideModal = this.hideModal;
    +		L.showTooltip = this.showTooltip;
    +		L.hideTooltip = this.hideTooltip;
    +		L.itemlist = this.itemlist;
    +
    +		document.addEventListener('mouseover', this.showTooltip.bind(this), true);
    +		document.addEventListener('mouseout', this.hideTooltip.bind(this), true);
    +		document.addEventListener('focus', this.showTooltip.bind(this), true);
    +		document.addEventListener('blur', this.hideTooltip.bind(this), true);
    +
    +		document.addEventListener('luci-loaded', this.tabs.init.bind(this.tabs));
    +		document.addEventListener('luci-loaded', this.changes.init.bind(this.changes));
    +		document.addEventListener('uci-loaded', this.changes.init.bind(this.changes));
    +	},
    +
    +	/**
    +	 * Display a modal overlay dialog with the specified contents.
    +	 *
    +	 * The modal overlay dialog covers the current view preventing interaction
    +	 * with the underlying view contents. Only one modal dialog instance can
    +	 * be opened. Invoking showModal() while a modal dialog is already open will
    +	 * replace the open dialog with a new one having the specified contents.
    +	 *
    +	 * Additional CSS class names may be passed to influence the appearence of
    +	 * the dialog. Valid values for the classes depend on the underlying theme.
    +	 *
    +	 * @see LuCI.dom.content
    +	 *
    +	 * @param {string} [title]
    +	 * The title of the dialog. If `null`, no title element will be rendered.
    +	 *
    +	 * @param {*} contents
    +	 * The contents to add to the modal dialog. This should be a DOM node or
    +	 * a document fragment in most cases. The value is passed as-is to the
    +	 * `L.dom.content()` function - refer to its documentation for applicable
    +	 * values.
    +	 *
    +	 * @param {...string} [classes]
    +	 * A number of extra CSS class names which are set on the modal dialog
    +	 * element.
    +	 *
    +	 * @returns {Node}
    +	 * Returns a DOM Node representing the modal dialog element.
    +	 */
    +	showModal: function(title, children /* , ... */) {
    +		var dlg = modalDiv.firstElementChild;
    +
    +		dlg.setAttribute('class', 'modal');
    +
    +		for (var i = 2; i < arguments.length; i++)
    +			dlg.classList.add(arguments[i]);
    +
    +		L.dom.content(dlg, L.dom.create('h4', {}, title));
    +		L.dom.append(dlg, children);
    +
    +		document.body.classList.add('modal-overlay-active');
    +
    +		return dlg;
    +	},
    +
    +	/**
    +	 * Close the open modal overlay dialog.
    +	 *
    +	 * This function will close an open modal dialog and restore the normal view
    +	 * behaviour. It has no effect if no modal dialog is currently open.
    +	 *
    +	 * Note that this function is stand-alone, it does not rely on `this` and
    +	 * will not invoke other class functions so it suitable to be used as event
    +	 * handler as-is without the need to bind it first.
    +	 */
    +	hideModal: function() {
    +		document.body.classList.remove('modal-overlay-active');
    +	},
    +
    +	/** @private */
    +	showTooltip: function(ev) {
    +		var target = findParent(ev.target, '[data-tooltip]');
    +
    +		if (!target)
    +			return;
    +
    +		if (tooltipTimeout !== null) {
    +			window.clearTimeout(tooltipTimeout);
    +			tooltipTimeout = null;
    +		}
    +
    +		var rect = target.getBoundingClientRect(),
    +		    x = rect.left              + window.pageXOffset,
    +		    y = rect.top + rect.height + window.pageYOffset;
    +
    +		tooltipDiv.className = 'cbi-tooltip';
    +		tooltipDiv.innerHTML = '▲ ';
    +		tooltipDiv.firstChild.data += target.getAttribute('data-tooltip');
    +
    +		if (target.hasAttribute('data-tooltip-style'))
    +			tooltipDiv.classList.add(target.getAttribute('data-tooltip-style'));
    +
    +		if ((y + tooltipDiv.offsetHeight) > (window.innerHeight + window.pageYOffset)) {
    +			y -= (tooltipDiv.offsetHeight + target.offsetHeight);
    +			tooltipDiv.firstChild.data = '▼ ' + tooltipDiv.firstChild.data.substr(2);
    +		}
    +
    +		tooltipDiv.style.top = y + 'px';
    +		tooltipDiv.style.left = x + 'px';
    +		tooltipDiv.style.opacity = 1;
    +
    +		tooltipDiv.dispatchEvent(new CustomEvent('tooltip-open', {
    +			bubbles: true,
    +			detail: { target: target }
    +		}));
    +	},
    +
    +	/** @private */
    +	hideTooltip: function(ev) {
    +		if (ev.target === tooltipDiv || ev.relatedTarget === tooltipDiv ||
    +		    tooltipDiv.contains(ev.target) || tooltipDiv.contains(ev.relatedTarget))
    +			return;
    +
    +		if (tooltipTimeout !== null) {
    +			window.clearTimeout(tooltipTimeout);
    +			tooltipTimeout = null;
    +		}
    +
    +		tooltipDiv.style.opacity = 0;
    +		tooltipTimeout = window.setTimeout(function() { tooltipDiv.removeAttribute('style'); }, 250);
    +
    +		tooltipDiv.dispatchEvent(new CustomEvent('tooltip-close', { bubbles: true }));
    +	},
    +
    +	/**
    +	 * Add a notification banner at the top of the current view.
    +	 *
    +	 * A notification banner is an alert message usually displayed at the
    +	 * top of the current view, spanning the entire availibe width.
    +	 * Notification banners will stay in place until dismissed by the user.
    +	 * Multiple banners may be shown at the same time.
    +	 *
    +	 * Additional CSS class names may be passed to influence the appearence of
    +	 * the banner. Valid values for the classes depend on the underlying theme.
    +	 *
    +	 * @see LuCI.dom.content
    +	 *
    +	 * @param {string} [title]
    +	 * The title of the notification banner. If `null`, no title element
    +	 * will be rendered.
    +	 *
    +	 * @param {*} contents
    +	 * The contents to add to the notification banner. This should be a DOM
    +	 * node or a document fragment in most cases. The value is passed as-is
    +	 * to the `L.dom.content()` function - refer to its documentation for
    +	 * applicable values.
    +	 *
    +	 * @param {...string} [classes]
    +	 * A number of extra CSS class names which are set on the notification
    +	 * banner element.
    +	 *
    +	 * @returns {Node}
    +	 * Returns a DOM Node representing the notification banner element.
    +	 */
    +	addNotification: function(title, children /*, ... */) {
    +		var mc = document.querySelector('#maincontent') || document.body;
    +		var msg = E('div', {
    +			'class': 'alert-message fade-in',
    +			'style': 'display:flex',
    +			'transitionend': function(ev) {
    +				var node = ev.currentTarget;
    +				if (node.parentNode && node.classList.contains('fade-out'))
    +					node.parentNode.removeChild(node);
    +			}
    +		}, [
    +			E('div', { 'style': 'flex:10' }),
    +			E('div', { 'style': 'flex:1 1 auto; display:flex' }, [
    +				E('button', {
    +					'class': 'btn',
    +					'style': 'margin-left:auto; margin-top:auto',
    +					'click': function(ev) {
    +						L.dom.parent(ev.target, '.alert-message').classList.add('fade-out');
    +					},
    +
    +				}, [ _('Dismiss') ])
    +			])
    +		]);
    +
    +		if (title != null)
    +			L.dom.append(msg.firstElementChild, E('h4', {}, title));
    +
    +		L.dom.append(msg.firstElementChild, children);
    +
    +		for (var i = 2; i < arguments.length; i++)
    +			msg.classList.add(arguments[i]);
    +
    +		mc.insertBefore(msg, mc.firstElementChild);
    +
    +		return msg;
    +	},
    +
    +	/**
    +	 * Display or update an header area indicator.
    +	 *
    +	 * An indicator is a small label displayed in the header area of the screen
    +	 * providing few amounts of status information such as item counts or state
    +	 * toggle indicators.
    +	 *
    +	 * Multiple indicators may be shown at the same time and indicator labels
    +	 * may be made clickable to display extended information or to initiate
    +	 * further actions.
    +	 *
    +	 * Indicators can either use a default `active` or a less accented `inactive`
    +	 * style which is useful for indicators representing state toggles.
    +	 *
    +	 * @param {string} id
    +	 * The ID of the indicator. If an indicator with the given ID already exists,
    +	 * it is updated with the given label and style.
    +	 *
    +	 * @param {string} label
    +	 * The text to display in the indicator label.
    +	 *
    +	 * @param {function} [handler]
    +	 * A handler function to invoke when the indicator label is clicked/touched
    +	 * by the user. If omitted, the indicator is not clickable/touchable.
    +	 *
    +	 * Note that this parameter only applies to new indicators, when updating
    +	 * existing labels it is ignored.
    +	 *
    +	 * @param {string} [style=active]
    +	 * The indicator style to use. May be either `active` or `inactive`.
    +	 *
    +	 * @returns {boolean}
    +	 * Returns `true` when the indicator has been updated or `false` when no
    +	 * changes were made.
    +	 */
    +	showIndicator: function(id, label, handler, style) {
    +		if (indicatorDiv == null) {
    +			indicatorDiv = document.body.querySelector('#indicators');
    +
    +			if (indicatorDiv == null)
    +				return false;
    +		}
    +
    +		var handlerFn = (typeof(handler) == 'function') ? handler : null,
    +		    indicatorElem = indicatorDiv.querySelector('span[data-indicator="%s"]'.format(id)) ||
    +			indicatorDiv.appendChild(E('span', {
    +				'data-indicator': id,
    +				'data-clickable': handlerFn ? true : null,
    +				'click': handlerFn
    +			}, ['']));
    +
    +		if (label == indicatorElem.firstChild.data && style == indicatorElem.getAttribute('data-style'))
    +			return false;
    +
    +		indicatorElem.firstChild.data = label;
    +		indicatorElem.setAttribute('data-style', (style == 'inactive') ? 'inactive' : 'active');
    +		return true;
    +	},
    +
    +	/**
    +	 * Remove an header area indicator.
    +	 *
    +	 * This function removes the given indicator label from the header indicator
    +	 * area. When the given indicator is not found, this function does nothing.
    +	 *
    +	 * @param {string} id
    +	 * The ID of the indicator to remove.
    +	 *
    +	 * @returns {boolean}
    +	 * Returns `true` when the indicator has been removed or `false` when the
    +	 * requested indicator was not found.
    +	 */
    +	hideIndicator: function(id) {
    +		var indicatorElem = indicatorDiv ? indicatorDiv.querySelector('span[data-indicator="%s"]'.format(id)) : null;
    +
    +		if (indicatorElem == null)
    +			return false;
    +
    +		indicatorDiv.removeChild(indicatorElem);
    +		return true;
    +	},
    +
    +	/**
    +	 * Formats a series of label/value pairs into list-like markup.
    +	 *
    +	 * This function transforms a flat array of alternating label and value
    +	 * elements into a list-like markup, using the values in `separators` as
    +	 * separators and appends the resulting nodes to the given parent DOM node.
    +	 *
    +	 * Each label is suffixed with `: ` and wrapped into a `<strong>` tag, the
    +	 * `<strong>` element and the value corresponding to the label are
    +	 * subsequently wrapped into a `<span class="nowrap">` element.
    +	 *
    +	 * The resulting `<span>` element tuples are joined by the given separators
    +	 * to form the final markup which is appened to the given parent DOM node.
    +	 *
    +	 * @param {Node} node
    +	 * The parent DOM node to append the markup to. Any previous child elements
    +	 * will be removed.
    +	 *
    +	 * @param {Array<*>} items
    +	 * An alternating array of labels and values. The label values will be
    +	 * converted to plain strings, the values are used as-is and may be of
    +	 * any type accepted by `LuCI.dom.content()`.
    +	 *
    +	 * @param {*|Array<*>} [separators=[E('br')]]
    +	 * A single value or an array of separator values to separate each
    +	 * label/value pair with. The function will cycle through the separators
    +	 * when joining the pairs. If omitted, the default separator is a sole HTML
    +	 * `<br>` element. Separator values are used as-is and may be of any type
    +	 * accepted by `LuCI.dom.content()`.
    +	 *
    +	 * @returns {Node}
    +	 * Returns the parent DOM node the formatted markup has been added to.
    +	 */
    +	itemlist: function(node, items, separators) {
    +		var children = [];
    +
    +		if (!Array.isArray(separators))
    +			separators = [ separators || E('br') ];
    +
    +		for (var i = 0; i < items.length; i += 2) {
    +			if (items[i+1] !== null && items[i+1] !== undefined) {
    +				var sep = separators[(i/2) % separators.length],
    +				    cld = [];
    +
    +				children.push(E('span', { class: 'nowrap' }, [
    +					items[i] ? E('strong', items[i] + ': ') : '',
    +					items[i+1]
    +				]));
    +
    +				if ((i+2) < items.length)
    +					children.push(L.dom.elem(sep) ? sep.cloneNode(true) : sep);
    +			}
    +		}
    +
    +		L.dom.content(node, children);
    +
    +		return node;
    +	},
    +
    +	/**
    +	 * @class
    +	 * @memberof LuCI.ui
    +	 * @hideconstructor
    +	 * @classdesc
    +	 *
    +	 * The `tabs` class handles tab menu groups used throughout the view area.
    +	 * It takes care of setting up tab groups, tracking their state and handling
    +	 * related events.
    +	 *
    +	 * This class is automatically instantiated as part of `LuCI.ui`. To use it
    +	 * in views, use `'require ui'` and refer to `ui.tabs`. To import it in
    +	 * external JavaScript, use `L.require("ui").then(...)` and access the
    +	 * `tabs` property of the class instance value.
    +	 */
    +	tabs: L.Class.singleton(/* @lends LuCI.ui.tabs.prototype */ {
    +		/** @private */
    +		init: function() {
    +			var groups = [], prevGroup = null, currGroup = null;
    +
    +			document.querySelectorAll('[data-tab]').forEach(function(tab) {
    +				var parent = tab.parentNode;
    +
    +				if (L.dom.matches(tab, 'li') && L.dom.matches(parent, 'ul.cbi-tabmenu'))
    +					return;
    +
    +				if (!parent.hasAttribute('data-tab-group'))
    +					parent.setAttribute('data-tab-group', groups.length);
    +
    +				currGroup = +parent.getAttribute('data-tab-group');
    +
    +				if (currGroup !== prevGroup) {
    +					prevGroup = currGroup;
    +
    +					if (!groups[currGroup])
    +						groups[currGroup] = [];
    +				}
    +
    +				groups[currGroup].push(tab);
    +			});
    +
    +			for (var i = 0; i < groups.length; i++)
    +				this.initTabGroup(groups[i]);
    +
    +			document.addEventListener('dependency-update', this.updateTabs.bind(this));
    +
    +			this.updateTabs();
    +		},
    +
    +		/**
    +		 * Initializes a new tab group from the given tab pane collection.
    +		 *
    +		 * This function cycles through the given tab pane DOM nodes, extracts
    +		 * their tab IDs, titles and active states, renders a corresponding
    +		 * tab menu and prepends it to the tab panes common parent DOM node.
    +		 *
    +		 * The tab menu labels will be set to the value of the `data-tab-title`
    +		 * attribute of each corresponding pane. The last pane with the
    +		 * `data-tab-active` attribute set to `true` will be selected by default.
    +		 *
    +		 * If no pane is marked as active, the first one will be preselected.
    +		 *
    +		 * @instance
    +		 * @memberof LuCI.ui.tabs
    +		 * @param {Array<Node>|NodeList} panes
    +		 * A collection of tab panes to build a tab group menu for. May be a
    +		 * plain array of DOM nodes or a NodeList collection, such as the result
    +		 * of a `querySelectorAll()` call or the `.childNodes` property of a
    +		 * DOM node.
    +		 */
    +		initTabGroup: function(panes) {
    +			if (typeof(panes) != 'object' || !('length' in panes) || panes.length === 0)
    +				return;
    +
    +			var menu = E('ul', { 'class': 'cbi-tabmenu' }),
    +			    group = panes[0].parentNode,
    +			    groupId = +group.getAttribute('data-tab-group'),
    +			    selected = null;
    +
    +			if (group.getAttribute('data-initialized') === 'true')
    +				return;
    +
    +			for (var i = 0, pane; pane = panes[i]; i++) {
    +				var name = pane.getAttribute('data-tab'),
    +				    title = pane.getAttribute('data-tab-title'),
    +				    active = pane.getAttribute('data-tab-active') === 'true';
    +
    +				menu.appendChild(E('li', {
    +					'style': this.isEmptyPane(pane) ? 'display:none' : null,
    +					'class': active ? 'cbi-tab' : 'cbi-tab-disabled',
    +					'data-tab': name
    +				}, E('a', {
    +					'href': '#',
    +					'click': this.switchTab.bind(this)
    +				}, title)));
    +
    +				if (active)
    +					selected = i;
    +			}
    +
    +			group.parentNode.insertBefore(menu, group);
    +			group.setAttribute('data-initialized', true);
    +
    +			if (selected === null) {
    +				selected = this.getActiveTabId(panes[0]);
    +
    +				if (selected < 0 || selected >= panes.length || this.isEmptyPane(panes[selected])) {
    +					for (var i = 0; i < panes.length; i++) {
    +						if (!this.isEmptyPane(panes[i])) {
    +							selected = i;
    +							break;
    +						}
    +					}
    +				}
    +
    +				menu.childNodes[selected].classList.add('cbi-tab');
    +				menu.childNodes[selected].classList.remove('cbi-tab-disabled');
    +				panes[selected].setAttribute('data-tab-active', 'true');
    +
    +				this.setActiveTabId(panes[selected], selected);
    +			}
    +
    +			panes[selected].dispatchEvent(new CustomEvent('cbi-tab-active', {
    +				detail: { tab: panes[selected].getAttribute('data-tab') }
    +			}));
    +
    +			this.updateTabs(group);
    +		},
    +
    +		/**
    +		 * Checks whether the given tab pane node is empty.
    +		 *
    +		 * @instance
    +		 * @memberof LuCI.ui.tabs
    +		 * @param {Node} pane
    +		 * The tab pane to check.
    +		 *
    +		 * @returns {boolean}
    +		 * Returns `true` if the pane is empty, else `false`.
    +		 */
    +		isEmptyPane: function(pane) {
    +			return L.dom.isEmpty(pane, function(n) { return n.classList.contains('cbi-tab-descr') });
    +		},
    +
    +		/** @private */
    +		getPathForPane: function(pane) {
    +			var path = [], node = null;
    +
    +			for (node = pane ? pane.parentNode : null;
    +			     node != null && node.hasAttribute != null;
    +			     node = node.parentNode)
    +			{
    +				if (node.hasAttribute('data-tab'))
    +					path.unshift(node.getAttribute('data-tab'));
    +				else if (node.hasAttribute('data-section-id'))
    +					path.unshift(node.getAttribute('data-section-id'));
    +			}
    +
    +			return path.join('/');
    +		},
    +
    +		/** @private */
    +		getActiveTabState: function() {
    +			var page = document.body.getAttribute('data-page');
    +
    +			try {
    +				var val = JSON.parse(window.sessionStorage.getItem('tab'));
    +				if (val.page === page && L.isObject(val.paths))
    +					return val;
    +			}
    +			catch(e) {}
    +
    +			window.sessionStorage.removeItem('tab');
    +			return { page: page, paths: {} };
    +		},
    +
    +		/** @private */
    +		getActiveTabId: function(pane) {
    +			var path = this.getPathForPane(pane);
    +			return +this.getActiveTabState().paths[path] || 0;
    +		},
    +
    +		/** @private */
    +		setActiveTabId: function(pane, tabIndex) {
    +			var path = this.getPathForPane(pane);
    +
    +			try {
    +				var state = this.getActiveTabState();
    +				    state.paths[path] = tabIndex;
    +
    +			    window.sessionStorage.setItem('tab', JSON.stringify(state));
    +			}
    +			catch (e) { return false; }
    +
    +			return true;
    +		},
    +
    +		/** @private */
    +		updateTabs: function(ev, root) {
    +			(root || document).querySelectorAll('[data-tab-title]').forEach(L.bind(function(pane) {
    +				var menu = pane.parentNode.previousElementSibling,
    +				    tab = menu ? menu.querySelector('[data-tab="%s"]'.format(pane.getAttribute('data-tab'))) : null,
    +				    n_errors = pane.querySelectorAll('.cbi-input-invalid').length;
    +
    +				if (!menu || !tab)
    +					return;
    +
    +				if (this.isEmptyPane(pane)) {
    +					tab.style.display = 'none';
    +					tab.classList.remove('flash');
    +				}
    +				else if (tab.style.display === 'none') {
    +					tab.style.display = '';
    +					requestAnimationFrame(function() { tab.classList.add('flash') });
    +				}
    +
    +				if (n_errors) {
    +					tab.setAttribute('data-errors', n_errors);
    +					tab.setAttribute('data-tooltip', _('%d invalid field(s)').format(n_errors));
    +					tab.setAttribute('data-tooltip-style', 'error');
    +				}
    +				else {
    +					tab.removeAttribute('data-errors');
    +					tab.removeAttribute('data-tooltip');
    +				}
    +			}, this));
    +		},
    +
    +		/** @private */
    +		switchTab: function(ev) {
    +			var tab = ev.target.parentNode,
    +			    name = tab.getAttribute('data-tab'),
    +			    menu = tab.parentNode,
    +			    group = menu.nextElementSibling,
    +			    groupId = +group.getAttribute('data-tab-group'),
    +			    index = 0;
    +
    +			ev.preventDefault();
    +
    +			if (!tab.classList.contains('cbi-tab-disabled'))
    +				return;
    +
    +			menu.querySelectorAll('[data-tab]').forEach(function(tab) {
    +				tab.classList.remove('cbi-tab');
    +				tab.classList.remove('cbi-tab-disabled');
    +				tab.classList.add(
    +					tab.getAttribute('data-tab') === name ? 'cbi-tab' : 'cbi-tab-disabled');
    +			});
    +
    +			group.childNodes.forEach(function(pane) {
    +				if (L.dom.matches(pane, '[data-tab]')) {
    +					if (pane.getAttribute('data-tab') === name) {
    +						pane.setAttribute('data-tab-active', 'true');
    +						pane.dispatchEvent(new CustomEvent('cbi-tab-active', { detail: { tab: name } }));
    +						L.ui.tabs.setActiveTabId(pane, index);
    +					}
    +					else {
    +						pane.setAttribute('data-tab-active', 'false');
    +					}
    +
    +					index++;
    +				}
    +			});
    +		}
    +	}),
    +
    +	/**
    +	 * @typedef {Object} FileUploadReply
    +	 * @memberof LuCI.ui
    +
    +	 * @property {string} name - Name of the uploaded file without directory components
    +	 * @property {number} size - Size of the uploaded file in bytes
    +	 * @property {string} checksum - The MD5 checksum of the received file data
    +	 * @property {string} sha256sum - The SHA256 checksum of the received file data
    +	 */
    +
    +	/**
    +	 * Display a modal file upload prompt.
    +	 *
    +	 * This function opens a modal dialog prompting the user to select and
    +	 * upload a file to a predefined remote destination path.
    +	 *
    +	 * @param {string} path
    +	 * The remote file path to upload the local file to.
    +	 *
    +	 * @param {Node} [progessStatusNode]
    +	 * An optional DOM text node whose content text is set to the progress
    +	 * percentage value during file upload.
    +	 *
    +	 * @returns {Promise<LuCI.ui.FileUploadReply>}
    +	 * Returns a promise resolving to a file upload status object on success
    +	 * or rejecting with an error in case the upload failed or has been
    +	 * cancelled by the user.
    +	 */
    +	uploadFile: function(path, progressStatusNode) {
    +		return new Promise(function(resolveFn, rejectFn) {
    +			L.ui.showModal(_('Uploading file…'), [
    +				E('p', _('Please select the file to upload.')),
    +				E('div', { 'style': 'display:flex' }, [
    +					E('div', { 'class': 'left', 'style': 'flex:1' }, [
    +						E('input', {
    +							type: 'file',
    +							style: 'display:none',
    +							change: function(ev) {
    +								var modal = L.dom.parent(ev.target, '.modal'),
    +								    body = modal.querySelector('p'),
    +								    upload = modal.querySelector('.cbi-button-action.important'),
    +								    file = ev.currentTarget.files[0];
    +
    +								if (file == null)
    +									return;
    +
    +								L.dom.content(body, [
    +									E('ul', {}, [
    +										E('li', {}, [ '%s: %s'.format(_('Name'), file.name.replace(/^.*[\\\/]/, '')) ]),
    +										E('li', {}, [ '%s: %1024mB'.format(_('Size'), file.size) ])
    +									])
    +								]);
    +
    +								upload.disabled = false;
    +								upload.focus();
    +							}
    +						}),
    +						E('button', {
    +							'class': 'btn',
    +							'click': function(ev) {
    +								ev.target.previousElementSibling.click();
    +							}
    +						}, [ _('Browse…') ])
    +					]),
    +					E('div', { 'class': 'right', 'style': 'flex:1' }, [
    +						E('button', {
    +							'class': 'btn',
    +							'click': function() {
    +								L.ui.hideModal();
    +								rejectFn(new Error('Upload has been cancelled'));
    +							}
    +						}, [ _('Cancel') ]),
    +						' ',
    +						E('button', {
    +							'class': 'btn cbi-button-action important',
    +							'disabled': true,
    +							'click': function(ev) {
    +								var input = L.dom.parent(ev.target, '.modal').querySelector('input[type="file"]');
    +
    +								if (!input.files[0])
    +									return;
    +
    +								var progress = E('div', { 'class': 'cbi-progressbar', 'title': '0%' }, E('div', { 'style': 'width:0' }));
    +
    +								L.ui.showModal(_('Uploading file…'), [ progress ]);
    +
    +								var data = new FormData();
    +
    +								data.append('sessionid', rpc.getSessionID());
    +								data.append('filename', path);
    +								data.append('filedata', input.files[0]);
    +
    +								var filename = input.files[0].name;
    +
    +								L.Request.post(L.env.cgi_base + '/cgi-upload', data, {
    +									timeout: 0,
    +									progress: function(pev) {
    +										var percent = (pev.loaded / pev.total) * 100;
    +
    +										if (progressStatusNode)
    +											progressStatusNode.data = '%.2f%%'.format(percent);
    +
    +										progress.setAttribute('title', '%.2f%%'.format(percent));
    +										progress.firstElementChild.style.width = '%.2f%%'.format(percent);
    +									}
    +								}).then(function(res) {
    +									var reply = res.json();
    +
    +									L.ui.hideModal();
    +
    +									if (L.isObject(reply) && reply.failure) {
    +										L.ui.addNotification(null, E('p', _('Upload request failed: %s').format(reply.message)));
    +										rejectFn(new Error(reply.failure));
    +									}
    +									else {
    +										reply.name = filename;
    +										resolveFn(reply);
    +									}
    +								}, function(err) {
    +									L.ui.hideModal();
    +									rejectFn(err);
    +								});
    +							}
    +						}, [ _('Upload') ])
    +					])
    +				])
    +			]);
    +		});
    +	},
    +
    +	/**
    +	 * Perform a device connectivity test.
    +	 *
    +	 * Attempt to fetch a well known ressource from the remote device via HTTP
    +	 * in order to test connectivity. This function is mainly useful to wait
    +	 * for the router to come back online after a reboot or reconfiguration.
    +	 *
    +	 * @param {string} [proto=http]
    +	 * The protocol to use for fetching the resource. May be either `http`
    +	 * (the default) or `https`.
    +	 *
    +	 * @param {string} [host=window.location.host]
    +	 * Override the host address to probe. By default the current host as seen
    +	 * in the address bar is probed.
    +	 *
    +	 * @returns {Promise<Event>}
    +	 * Returns a promise resolving to a `load` event in case the device is
    +	 * reachable or rejecting with an `error` event in case it is not reachable
    +	 * or rejecting with `null` when the connectivity check timed out.
    +	 */
    +	pingDevice: function(proto, ipaddr) {
    +		var target = '%s://%s%s?%s'.format(proto || 'http', ipaddr || window.location.host, L.resource('icons/loading.gif'), Math.random());
    +
    +		return new Promise(function(resolveFn, rejectFn) {
    +			var img = new Image();
    +
    +			img.onload = resolveFn;
    +			img.onerror = rejectFn;
    +
    +			window.setTimeout(rejectFn, 1000);
    +
    +			img.src = target;
    +		});
    +	},
    +
    +	/**
    +	 * Wait for device to come back online and reconnect to it.
    +	 *
    +	 * Poll each given hostname or IP address and navigate to it as soon as
    +	 * one of the addresses becomes reachable.
    +	 *
    +	 * @param {...string} [hosts=[window.location.host]]
    +	 * The list of IP addresses and host names to check for reachability.
    +	 * If omitted, the current value of `window.location.host` is used by
    +	 * default.
    +	 */
    +	awaitReconnect: function(/* ... */) {
    +		var ipaddrs = arguments.length ? arguments : [ window.location.host ];
    +
    +		window.setTimeout(L.bind(function() {
    +			L.Poll.add(L.bind(function() {
    +				var tasks = [], reachable = false;
    +
    +				for (var i = 0; i < 2; i++)
    +					for (var j = 0; j < ipaddrs.length; j++)
    +						tasks.push(this.pingDevice(i ? 'https' : 'http', ipaddrs[j])
    +							.then(function(ev) { reachable = ev.target.src.replace(/^(https?:\/\/[^\/]+).*$/, '$1/') }, function() {}));
    +
    +				return Promise.all(tasks).then(function() {
    +					if (reachable) {
    +						L.Poll.stop();
    +						window.location = reachable;
    +					}
    +				});
    +			}, this));
    +		}, this), 5000);
    +	},
    +
    +	/**
    +	 * @class
    +	 * @memberof LuCI.ui
    +	 * @hideconstructor
    +	 * @classdesc
    +	 *
    +	 * The `changes` class encapsulates logic for visualizing, applying,
    +	 * confirming and reverting staged UCI changesets.
    +	 *
    +	 * This class is automatically instantiated as part of `LuCI.ui`. To use it
    +	 * in views, use `'require ui'` and refer to `ui.changes`. To import it in
    +	 * external JavaScript, use `L.require("ui").then(...)` and access the
    +	 * `changes` property of the class instance value.
    +	 */
    +	changes: L.Class.singleton(/* @lends LuCI.ui.changes.prototype */ {
    +		init: function() {
    +			if (!L.env.sessionid)
    +				return;
    +
    +			return uci.changes().then(L.bind(this.renderChangeIndicator, this));
    +		},
    +
    +		/**
    +		 * Set the change count indicator.
    +		 *
    +		 * This function updates or hides the UCI change count indicator,
    +		 * depending on the passed change count. When the count is greater
    +		 * than 0, the change indicator is displayed or updated, otherwise it
    +		 * is removed.
    +		 *
    +		 * @instance
    +		 * @memberof LuCI.ui.changes
    +		 * @param {number} numChanges
    +		 * The number of changes to indicate.
    +		 */
    +		setIndicator: function(n) {
    +			var i = document.querySelector('.uci_change_indicator');
    +			if (i == null) {
    +				var poll = document.getElementById('xhr_poll_status');
    +				i = poll.parentNode.insertBefore(E('a', {
    +					'href': '#',
    +					'class': 'uci_change_indicator label notice',
    +					'click': L.bind(this.displayChanges, this)
    +				}), poll);
    +			}
    +
    +			if (n > 0) {
    +				L.dom.content(i, [ _('Unsaved Changes'), ': ', n ]);
    +				i.classList.add('flash');
    +				i.style.display = '';
    +				document.dispatchEvent(new CustomEvent('uci-new-changes'));
    +			}
    +			else {
    +				i.classList.remove('flash');
    +				i.style.display = 'none';
    +				document.dispatchEvent(new CustomEvent('uci-clear-changes'));
    +			}
    +		},
    +
    +		/**
    +		 * Update the change count indicator.
    +		 *
    +		 * This function updates the UCI change count indicator from the given
    +		 * UCI changeset structure.
    +		 *
    +		 * @instance
    +		 * @memberof LuCI.ui.changes
    +		 * @param {Object<string, Array<LuCI.uci.ChangeRecord>>} changes
    +		 * The UCI changeset to count.
    +		 */
    +		renderChangeIndicator: function(changes) {
    +			var n_changes = 0;
    +
    +			for (var config in changes)
    +				if (changes.hasOwnProperty(config))
    +					n_changes += changes[config].length;
    +
    +			this.changes = changes;
    +			this.setIndicator(n_changes);
    +		},
    +
    +		/** @private */
    +		changeTemplates: {
    +			'add-3':      '<ins>uci add %0 <strong>%3</strong> # =%2</ins>',
    +			'set-3':      '<ins>uci set %0.<strong>%2</strong>=%3</ins>',
    +			'set-4':      '<var><ins>uci set %0.%2.%3=<strong>%4</strong></ins></var>',
    +			'remove-2':   '<del>uci del %0.<strong>%2</strong></del>',
    +			'remove-3':   '<var><del>uci del %0.%2.<strong>%3</strong></del></var>',
    +			'order-3':    '<var>uci reorder %0.%2=<strong>%3</strong></var>',
    +			'list-add-4': '<var><ins>uci add_list %0.%2.%3=<strong>%4</strong></ins></var>',
    +			'list-del-4': '<var><del>uci del_list %0.%2.%3=<strong>%4</strong></del></var>',
    +			'rename-3':   '<var>uci rename %0.%2=<strong>%3</strong></var>',
    +			'rename-4':   '<var>uci rename %0.%2.%3=<strong>%4</strong></var>'
    +		},
    +
    +		/**
    +		 * Display the current changelog.
    +		 *
    +		 * Open a modal dialog visualizing the currently staged UCI changes
    +		 * and offer options to revert or apply the shown changes.
    +		 *
    +		 * @instance
    +		 * @memberof LuCI.ui.changes
    +		 */
    +		displayChanges: function() {
    +			var list = E('div', { 'class': 'uci-change-list' }),
    +			    dlg = L.ui.showModal(_('Configuration') + ' / ' + _('Changes'), [
    +				E('div', { 'class': 'cbi-section' }, [
    +					E('strong', _('Legend:')),
    +					E('div', { 'class': 'uci-change-legend' }, [
    +						E('div', { 'class': 'uci-change-legend-label' }, [
    +							E('ins', '&#160;'), ' ', _('Section added') ]),
    +						E('div', { 'class': 'uci-change-legend-label' }, [
    +							E('del', '&#160;'), ' ', _('Section removed') ]),
    +						E('div', { 'class': 'uci-change-legend-label' }, [
    +							E('var', {}, E('ins', '&#160;')), ' ', _('Option changed') ]),
    +						E('div', { 'class': 'uci-change-legend-label' }, [
    +							E('var', {}, E('del', '&#160;')), ' ', _('Option removed') ])]),
    +					E('br'), list,
    +					E('div', { 'class': 'right' }, [
    +						E('button', {
    +							'class': 'btn',
    +							'click': L.ui.hideModal
    +						}, [ _('Dismiss') ]), ' ',
    +						E('button', {
    +							'class': 'cbi-button cbi-button-positive important',
    +							'click': L.bind(this.apply, this, true)
    +						}, [ _('Save & Apply') ]), ' ',
    +						E('button', {
    +							'class': 'cbi-button cbi-button-reset',
    +							'click': L.bind(this.revert, this)
    +						}, [ _('Revert') ])])])
    +			]);
    +
    +			for (var config in this.changes) {
    +				if (!this.changes.hasOwnProperty(config))
    +					continue;
    +
    +				list.appendChild(E('h5', '# /etc/config/%s'.format(config)));
    +
    +				for (var i = 0, added = null; i < this.changes[config].length; i++) {
    +					var chg = this.changes[config][i],
    +					    tpl = this.changeTemplates['%s-%d'.format(chg[0], chg.length)];
    +
    +					list.appendChild(E(tpl.replace(/%([01234])/g, function(m0, m1) {
    +						switch (+m1) {
    +						case 0:
    +							return config;
    +
    +						case 2:
    +							if (added != null && chg[1] == added[0])
    +								return '@' + added[1] + '[-1]';
    +							else
    +								return chg[1];
    +
    +						case 4:
    +							return "'%h'".format(chg[3].replace(/'/g, "'\"'\"'"));
    +
    +						default:
    +							return chg[m1-1];
    +						}
    +					})));
    +
    +					if (chg[0] == 'add')
    +						added = [ chg[1], chg[2] ];
    +				}
    +			}
    +
    +			list.appendChild(E('br'));
    +			dlg.classList.add('uci-dialog');
    +		},
    +
    +		/** @private */
    +		displayStatus: function(type, content) {
    +			if (type) {
    +				var message = L.ui.showModal('', '');
    +
    +				message.classList.add('alert-message');
    +				DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/));
    +
    +				if (content)
    +					L.dom.content(message, content);
    +
    +				if (!this.was_polling) {
    +					this.was_polling = L.Request.poll.active();
    +					L.Request.poll.stop();
    +				}
    +			}
    +			else {
    +				L.ui.hideModal();
    +
    +				if (this.was_polling)
    +					L.Request.poll.start();
    +			}
    +		},
    +
    +		/** @private */
    +		rollback: function(checked) {
    +			if (checked) {
    +				this.displayStatus('warning spinning',
    +					E('p', _('Failed to confirm apply within %ds, waiting for rollback…')
    +						.format(L.env.apply_rollback)));
    +
    +				var call = function(r, data, duration) {
    +					if (r.status === 204) {
    +						L.ui.changes.displayStatus('warning', [
    +							E('h4', _('Configuration changes have been rolled back!')),
    +							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)),
    +							E('div', { 'class': 'right' }, [
    +								E('button', {
    +									'class': 'btn',
    +									'click': L.bind(L.ui.changes.displayStatus, L.ui.changes, false)
    +								}, [ _('Dismiss') ]), ' ',
    +								E('button', {
    +									'class': 'btn cbi-button-action important',
    +									'click': L.bind(L.ui.changes.revert, L.ui.changes)
    +								}, [ _('Revert changes') ]), ' ',
    +								E('button', {
    +									'class': 'btn cbi-button-negative important',
    +									'click': L.bind(L.ui.changes.apply, L.ui.changes, false)
    +								}, [ _('Apply unchecked') ])
    +							])
    +						]);
    +
    +						return;
    +					}
    +
    +					var delay = isNaN(duration) ? 0 : Math.max(1000 - duration, 0);
    +					window.setTimeout(function() {
    +						L.Request.request(L.url('admin/uci/confirm'), {
    +							method: 'post',
    +							timeout: L.env.apply_timeout * 1000,
    +							query: { sid: L.env.sessionid, token: L.env.token }
    +						}).then(call);
    +					}, delay);
    +				};
    +
    +				call({ status: 0 });
    +			}
    +			else {
    +				this.displayStatus('warning', [
    +					E('h4', _('Device unreachable!')),
    +					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.'))
    +				]);
    +			}
    +		},
    +
    +		/** @private */
    +		confirm: function(checked, deadline, override_token) {
    +			var tt;
    +			var ts = Date.now();
    +
    +			this.displayStatus('notice');
    +
    +			if (override_token)
    +				this.confirm_auth = { token: override_token };
    +
    +			var call = function(r, data, duration) {
    +				if (Date.now() >= deadline) {
    +					window.clearTimeout(tt);
    +					L.ui.changes.rollback(checked);
    +					return;
    +				}
    +				else if (r && (r.status === 200 || r.status === 204)) {
    +					document.dispatchEvent(new CustomEvent('uci-applied'));
    +
    +					L.ui.changes.setIndicator(0);
    +					L.ui.changes.displayStatus('notice',
    +						E('p', _('Configuration changes applied.')));
    +
    +					window.clearTimeout(tt);
    +					window.setTimeout(function() {
    +						//L.ui.changes.displayStatus(false);
    +						window.location = window.location.href.split('#')[0];
    +					}, L.env.apply_display * 1000);
    +
    +					return;
    +				}
    +
    +				var delay = isNaN(duration) ? 0 : Math.max(1000 - duration, 0);
    +				window.setTimeout(function() {
    +					L.Request.request(L.url('admin/uci/confirm'), {
    +						method: 'post',
    +						timeout: L.env.apply_timeout * 1000,
    +						query: L.ui.changes.confirm_auth
    +					}).then(call, call);
    +				}, delay);
    +			};
    +
    +			var tick = function() {
    +				var now = Date.now();
    +
    +				L.ui.changes.displayStatus('notice spinning',
    +					E('p', _('Applying configuration changes… %ds')
    +						.format(Math.max(Math.floor((deadline - Date.now()) / 1000), 0))));
    +
    +				if (now >= deadline)
    +					return;
    +
    +				tt = window.setTimeout(tick, 1000 - (now - ts));
    +				ts = now;
    +			};
    +
    +			tick();
    +
    +			/* wait a few seconds for the settings to become effective */
    +			window.setTimeout(call, Math.max(L.env.apply_holdoff * 1000 - ((ts + L.env.apply_rollback * 1000) - deadline), 1));
    +		},
    +
    +		/**
    +		 * Apply the staged configuration changes.
    +		 *
    +		 * Start applying staged configuration changes and open a modal dialog
    +		 * with a progress indication to prevent interaction with the view
    +		 * during the apply process. The modal dialog will be automatically
    +		 * closed and the current view reloaded once the apply process is
    +		 * complete.
    +		 *
    +		 * @instance
    +		 * @memberof LuCI.ui.changes
    +		 * @param {boolean} [checked=false]
    +		 * Whether to perform a checked (`true`) configuration apply or an
    +		 * unchecked (`false`) one.
    +
    +		 * In case of a checked apply, the configuration changes must be
    +		 * confirmed within a specific time interval, otherwise the device
    +		 * will begin to roll back the changes in order to restore the previous
    +		 * settings.
    +		 */
    +		apply: function(checked) {
    +			this.displayStatus('notice spinning',
    +				E('p', _('Starting configuration apply…')));
    +
    +			L.Request.request(L.url('admin/uci', checked ? 'apply_rollback' : 'apply_unchecked'), {
    +				method: 'post',
    +				query: { sid: L.env.sessionid, token: L.env.token }
    +			}).then(function(r) {
    +				if (r.status === (checked ? 200 : 204)) {
    +					var tok = null; try { tok = r.json(); } catch(e) {}
    +					if (checked && tok !== null && typeof(tok) === 'object' && typeof(tok.token) === 'string')
    +						L.ui.changes.confirm_auth = tok;
    +
    +					L.ui.changes.confirm(checked, Date.now() + L.env.apply_rollback * 1000);
    +				}
    +				else if (checked && r.status === 204) {
    +					L.ui.changes.displayStatus('notice',
    +						E('p', _('There are no changes to apply')));
    +
    +					window.setTimeout(function() {
    +						L.ui.changes.displayStatus(false);
    +					}, L.env.apply_display * 1000);
    +				}
    +				else {
    +					L.ui.changes.displayStatus('warning',
    +						E('p', _('Apply request failed with status <code>%h</code>')
    +							.format(r.responseText || r.statusText || r.status)));
    +
    +					window.setTimeout(function() {
    +						L.ui.changes.displayStatus(false);
    +					}, L.env.apply_display * 1000);
    +				}
    +			});
    +		},
    +
    +		/**
    +		 * Revert the staged configuration changes.
    +		 *
    +		 * Start reverting staged configuration changes and open a modal dialog
    +		 * with a progress indication to prevent interaction with the view
    +		 * during the revert process. The modal dialog will be automatically
    +		 * closed and the current view reloaded once the revert process is
    +		 * complete.
    +		 *
    +		 * @instance
    +		 * @memberof LuCI.ui.changes
    +		 */
    +		revert: function() {
    +			this.displayStatus('notice spinning',
    +				E('p', _('Reverting configuration…')));
    +
    +			L.Request.request(L.url('admin/uci/revert'), {
    +				method: 'post',
    +				query: { sid: L.env.sessionid, token: L.env.token }
    +			}).then(function(r) {
    +				if (r.status === 200) {
    +					document.dispatchEvent(new CustomEvent('uci-reverted'));
    +
    +					L.ui.changes.setIndicator(0);
    +					L.ui.changes.displayStatus('notice',
    +						E('p', _('Changes have been reverted.')));
    +
    +					window.setTimeout(function() {
    +						//L.ui.changes.displayStatus(false);
    +						window.location = window.location.href.split('#')[0];
    +					}, L.env.apply_display * 1000);
    +				}
    +				else {
    +					L.ui.changes.displayStatus('warning',
    +						E('p', _('Revert request failed with status <code>%h</code>')
    +							.format(r.statusText || r.status)));
    +
    +					window.setTimeout(function() {
    +						L.ui.changes.displayStatus(false);
    +					}, L.env.apply_display * 1000);
    +				}
    +			});
    +		}
    +	}),
    +
    +	/**
    +	 * Add validation constraints to an input element.
    +	 *
    +	 * Compile the given type expression and optional validator function into
    +	 * a validation function and bind it to the specified input element events.
    +	 *
    +	 * @param {Node} field
    +	 * The DOM input element node to bind the validation constraints to.
    +	 *
    +	 * @param {string} type
    +	 * The datatype specification to describe validation constraints.
    +	 * Refer to the `LuCI.validation` class documentation for details.
    +	 *
    +	 * @param {boolean} [optional=false]
    +	 * Specifies whether empty values are allowed (`true`) or not (`false`).
    +	 * If an input element is not marked optional it must not be empty,
    +	 * otherwise it will be marked as invalid.
    +	 *
    +	 * @param {function} [vfunc]
    +	 * Specifies a custom validation function which is invoked after the
    +	 * other validation constraints are applied. The validation must return
    +	 * `true` to accept the passed value. Any other return type is converted
    +	 * to a string and treated as validation error message.
    +	 *
    +	 * @param {...string} [events=blur, keyup]
    +	 * The list of events to bind. Each received event will trigger a field
    +	 * validation. If omitted, the `keyup` and `blur` events are bound by
    +	 * default.
    +	 *
    +	 * @returns {function}
    +	 * Returns the compiled validator function which can be used to manually
    +	 * trigger field validation or to bind it to further events.
    +	 *
    +	 * @see LuCI.validation
    +	 */
    +	addValidator: function(field, type, optional, vfunc /*, ... */) {
    +		if (type == null)
    +			return;
    +
    +		var events = this.varargs(arguments, 3);
    +		if (events.length == 0)
    +			events.push('blur', 'keyup');
    +
    +		try {
    +			var cbiValidator = L.validation.create(field, type, optional, vfunc),
    +			    validatorFn = cbiValidator.validate.bind(cbiValidator);
    +
    +			for (var i = 0; i < events.length; i++)
    +				field.addEventListener(events[i], validatorFn);
    +
    +			validatorFn();
    +
    +			return validatorFn;
    +		}
    +		catch (e) { }
    +	},
    +
    +	/**
    +	 * Create a pre-bound event handler function.
    +	 *
    +	 * Generate and bind a function suitable for use in event handlers. The
    +	 * generated function automatically disables the event source element
    +	 * and adds an active indication to it by adding appropriate CSS classes.
    +	 *
    +	 * It will also await any promises returned by the wrapped function and
    +	 * re-enable the source element after the promises ran to completion.
    +	 *
    +	 * @param {*} ctx
    +	 * The `this` context to use for the wrapped function.
    +	 *
    +	 * @param {function|string} fn
    +	 * Specifies the function to wrap. In case of a function value, the
    +	 * function is used as-is. If a string is specified instead, it is looked
    +	 * up in `ctx` to obtain the function to wrap. In both cases the bound
    +	 * function will be invoked with `ctx` as `this` context
    +	 *
    +	 * @param {...*} extra_args
    +	 * Any further parameter as passed as-is to the bound event handler
    +	 * function in the same order as passed to `createHandlerFn()`.
    +	 *
    +	 * @returns {function|null}
    +	 * Returns the pre-bound handler function which is suitable to be passed
    +	 * to `addEventListener()`. Returns `null` if the given `fn` argument is
    +	 * a string which could not be found in `ctx` or if `ctx[fn]` is not a
    +	 * valid function value.
    +	 */
    +	createHandlerFn: function(ctx, fn /*, ... */) {
    +		if (typeof(fn) == 'string')
    +			fn = ctx[fn];
    +
    +		if (typeof(fn) != 'function')
    +			return null;
    +
    +		var arg_offset = arguments.length - 2;
    +
    +		return Function.prototype.bind.apply(function() {
    +			var t = arguments[arg_offset].currentTarget;
    +
    +			t.classList.add('spinning');
    +			t.disabled = true;
    +
    +			if (t.blur)
    +				t.blur();
    +
    +			Promise.resolve(fn.apply(ctx, arguments)).finally(function() {
    +				t.classList.remove('spinning');
    +				t.disabled = false;
    +			});
    +		}, this.varargs(arguments, 2, ctx));
    +	},
    +
    +	AbstractElement: UIElement,
    +
    +	/* Widgets */
    +	Textfield: UITextfield,
    +	Textarea: UITextarea,
    +	Checkbox: UICheckbox,
    +	Select: UISelect,
    +	Dropdown: UIDropdown,
    +	DynamicList: UIDynamicList,
    +	Combobox: UICombobox,
    +	ComboButton: UIComboButton,
    +	Hiddenfield: UIHiddenfield,
    +	FileUpload: UIFileUpload
    +});
    +
    +
    +
    + + + + + + + + +
    + Documentation generated by JSDoc 3.6.3 on Tue Mar 31 2020 21:30:33 GMT+0200 (Central European Summer Time) +
    +
    +
    + + + + -- 2.25.1