summaryrefslogtreecommitdiffhomepage
path: root/modules/luci-base/htdocs/luci-static/resources/cbi.js
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2018-10-20 17:56:02 +0200
committerJo-Philipp Wich <jo@mein.io>2018-11-05 11:05:12 +0100
commit7c78218339ac914f097db79c343b07ea86c7313a (patch)
tree4607442d001a841964fbbedbb23a99bfbdfe7faa /modules/luci-base/htdocs/luci-static/resources/cbi.js
parentbd614de514a7dd43a25f432dc1a9f9f66e7eec2b (diff)
luci-base, themes: rework dynlist and dropdown widgets
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'modules/luci-base/htdocs/luci-static/resources/cbi.js')
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/cbi.js418
1 files changed, 143 insertions, 275 deletions
diff --git a/modules/luci-base/htdocs/luci-static/resources/cbi.js b/modules/luci-base/htdocs/luci-static/resources/cbi.js
index 2efa02485..3ace96f32 100644
--- a/modules/luci-base/htdocs/luci-static/resources/cbi.js
+++ b/modules/luci-base/htdocs/luci-static/resources/cbi.js
@@ -794,95 +794,41 @@ function cbi_init() {
cbi_d_update();
}
-function cbi_combobox(id, values, def, man, focus) {
- var selid = "cbi.combobox." + id;
- if (document.getElementById(selid)) {
- return
- }
-
- var obj = document.getElementById(id)
- var sel = document.createElement("select");
- sel.id = selid;
- sel.index = obj.index;
- sel.classList.remove('cbi-input-text');
- sel.classList.add('cbi-input-select');
-
- if (obj.nextSibling)
- obj.parentNode.insertBefore(sel, obj.nextSibling);
- else
- obj.parentNode.appendChild(sel);
-
- var dt = obj.getAttribute('cbi_datatype');
- var op = obj.getAttribute('cbi_optional');
-
- if (!values[obj.value]) {
- if (obj.value == "") {
- var optdef = document.createElement("option");
- optdef.value = "";
- optdef.appendChild(document.createTextNode(typeof(def) === 'string' ? def : _('-- Please choose --')));
- sel.appendChild(optdef);
- }
- else {
- var opt = document.createElement("option");
- opt.value = obj.value;
- opt.selected = "selected";
- opt.appendChild(document.createTextNode(obj.value));
- sel.appendChild(opt);
- }
+function cbi_combobox_init(id, values, def, man) {
+ var obj = (typeof(id) === 'string') ? document.getElementById(id) : id;
+ var sb = E('div', {
+ 'name': obj.name,
+ 'class': 'cbi-dropdown',
+ 'display-items': 5,
+ 'optional': obj.getAttribute('data-optional'),
+ 'placeholder': _('-- Please choose --')
+ }, [ E('ul') ]);
+
+ if (!(obj.value in values) && obj.value.length) {
+ sb.lastElementChild.appendChild(E('li', {
+ 'data-value': obj.value,
+ 'selected': ''
+ }, obj.value.length ? obj.value : (def || _('-- Please choose --'))));
}
for (var i in values) {
- var opt = document.createElement("option");
- opt.value = i;
-
- if (obj.value == i)
- opt.selected = "selected";
-
- opt.appendChild(document.createTextNode(values[i]));
- sel.appendChild(opt);
- }
-
- var optman = document.createElement("option");
- optman.value = "";
- optman.appendChild(document.createTextNode(typeof(man) === 'string' ? man : _('-- custom --')));
- sel.appendChild(optman);
-
- obj.style.display = "none";
-
- if (dt)
- cbi_validate_field(sel, op == 'true', dt);
-
- sel.addEventListener("change", function() {
- if (sel.selectedIndex == sel.options.length - 1) {
- obj.style.display = "inline";
- sel.blur();
- sel.parentNode.removeChild(sel);
- obj.focus();
- }
- else {
- obj.value = sel.options[sel.selectedIndex].value;
- }
-
- try {
- cbi_d_update();
- } catch (e) {
- //Do nothing
- }
- })
-
- // Retrigger validation in select
- if (focus) {
- sel.focus();
- sel.blur();
+ sb.lastElementChild.appendChild(E('li', {
+ 'data-value': i,
+ 'selected': (i == obj.value) ? '' : null
+ }, values[i]));
}
-}
-function cbi_combobox_init(id, values, def, man) {
- var obj = (typeof(id) === 'string') ? document.getElementById(id) : id;
- obj.addEventListener("blur", function() {
- cbi_combobox(obj.id, values, def, man, true);
- });
- cbi_combobox(obj.id, values, def, man, false);
+ sb.lastElementChild.appendChild(E('li', { 'data-value': '-' }, [
+ E('input', {
+ 'type': 'text',
+ 'class': 'create-item-input',
+ 'data-type': obj.getAttribute('data-type'),
+ 'data-optional': true,
+ 'placeholder': (man || _('-- custom --'))
+ })
+ ]));
+
+ obj.parentNode.replaceChild(sb, obj);
}
function cbi_filebrowser(id, defpath) {
@@ -912,229 +858,151 @@ function cbi_browser_init(id, resource, defpath)
btn.addEventListener('click', cbi_browser_btnclick);
}
-function cbi_dynlist_init(parent, datatype, optional, choices)
-{
- var prefix = parent.getAttribute('data-prefix');
- var holder = parent.getAttribute('data-placeholder');
-
- var values;
-
- function cbi_dynlist_redraw(focus, add, del)
- {
- values = [ ];
+CBIDynamicList = {
+ 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': dl.getAttribute('data-prefix'),
+ 'value': value })]);
+
+ dl.querySelectorAll('.item, .add-item').forEach(function(item) {
+ if (exists)
+ return;
- while (parent.firstChild) {
- var n = parent.firstChild;
- var i = +n.index;
+ var hidden = item.querySelector('input[type="hidden"]');
- if (i != del) {
- if (matchesElem(n, 'input'))
- values.push(n.value || '');
- else if (matchesElem(n, 'select'))
- values[values.length-1] = n.options[n.selectedIndex].value;
- }
+ if (hidden && hidden.value === value)
+ exists = true;
+ else if (!hidden || hidden.value >= value)
+ exists = !!item.parentNode.insertBefore(new_item, item);
+ });
+ },
- parent.removeChild(n);
- }
+ removeItem: function(dl, item) {
+ var sb = dl.querySelector('.cbi-dropdown');
+ if (sb) {
+ var value = item.querySelector('input[type="hidden"]').value;
- if (add >= 0) {
- focus = add+1;
- values.splice(focus, 0, '');
- }
- else if (values.length == 0) {
- focus = 0;
- values.push('');
+ sb.querySelectorAll('ul > li').forEach(function(li) {
+ if (li.getAttribute('data-value') === value)
+ li.removeAttribute('unselectable');
+ });
}
- for (var i = 0; i < values.length; i++) {
- var t = document.createElement('input');
- t.id = prefix + '.' + (i+1);
- t.name = prefix;
- t.value = values[i];
- t.type = 'text';
- t.index = i;
- t.className = 'cbi-input-text';
-
- if (i == 0 && holder)
- t.placeholder = holder;
-
- var b = E('div', {
- class: 'cbi-button cbi-button-' + ((i+1) < values.length ? 'remove' : 'add')
- }, (i+1) < values.length ? '×' : '+');
-
- parent.appendChild(t);
- parent.appendChild(b);
-
- if (datatype == 'file')
- cbi_browser_init(t.id, null, parent.getAttribute('data-browser-path'));
-
- parent.appendChild(document.createElement('br'));
-
- if (datatype)
- cbi_validate_field(t.id, ((i+1) == values.length) || optional, datatype);
-
- if (choices) {
- cbi_combobox_init(t.id, choices, '', _('-- custom --'));
- b.index = i;
-
- b.addEventListener('keydown', cbi_dynlist_keydown);
- b.addEventListener('keypress', cbi_dynlist_keypress);
-
- if (i == focus || -i == focus)
- b.focus();
- }
- else {
- t.addEventListener('keydown', cbi_dynlist_keydown);
- t.addEventListener('keypress', cbi_dynlist_keypress);
-
- if (i == focus) {
- t.focus();
- }
- else if (-i == focus) {
- t.focus();
+ item.parentNode.removeChild(item);
+ },
- /* force cursor to end */
- var v = t.value;
- t.value = ' '
- t.value = v;
- }
- }
+ handleClick: function(ev) {
+ var dl = ev.currentTarget,
+ item = findParent(ev.target, '.item');
- b.addEventListener('click', cbi_dynlist_btnclick);
+ if (item) {
+ this.removeItem(dl, item);
}
- }
-
- function cbi_dynlist_keypress(ev)
- {
- ev = ev ? ev : window.event;
-
- var se = ev.target ? ev.target : ev.srcElement;
-
- if (se.nodeType == 3)
- se = se.parentNode;
-
- switch (ev.keyCode) {
- /* backspace, delete */
- case 8:
- case 46:
- if (se.value.length == 0) {
- if (ev.preventDefault)
- ev.preventDefault();
-
- return false;
- }
-
- return true;
-
- /* enter, arrow up, arrow down */
- case 13:
- case 38:
- case 40:
- if (ev.preventDefault)
- ev.preventDefault();
-
- return false;
+ 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 = '';
+ }
}
+ },
- return true;
- }
-
- function cbi_dynlist_keydown(ev)
- {
- ev = ev ? ev : window.event;
-
- var se = ev.target ? ev.target : ev.srcElement;
-
- if (se.nodeType == 3)
- se = se.parentNode;
-
- var prev = se.previousSibling;
- while (prev && prev.name != prefix)
- prev = prev.previousSibling;
-
- var next = se.nextSibling;
- while (next && next.name != prefix)
- next = next.nextSibling;
-
- /* advance one further in combobox case */
- if (next && next.nextSibling.name == prefix)
- next = next.nextSibling;
-
- switch (ev.keyCode) {
- /* backspace, delete */
- case 8:
- case 46:
- var del = (matchesElem(se, 'select'))
- ? true : (se.value.length == 0);
+ handleDropdownChange: function(ev) {
+ var dl = ev.currentTarget,
+ sbIn = ev.detail.instance,
+ sbEl = ev.detail.element,
+ sbVal = ev.detail.value;
- if (del) {
- if (ev.preventDefault)
- ev.preventDefault();
+ if (sbVal === null)
+ return;
- var focus = se.index;
- if (ev.keyCode == 8)
- focus = -focus+1;
+ sbIn.setValues(sbEl, null);
+ sbVal.element.setAttribute('unselectable', '');
- cbi_dynlist_redraw(focus, -1, se.index);
+ this.addItem(dl, sbVal.value, sbVal.text, true);
+ },
- return false;
- }
+ handleKeydown: function(ev) {
+ var dl = ev.currentTarget,
+ item = findParent(ev.target, '.item');
- break;
+ if (item) {
+ switch (ev.keyCode) {
+ case 8: /* backspace */
+ if (item.previousElementSibling)
+ item.previousElementSibling.focus();
- /* enter */
- case 13:
- cbi_dynlist_redraw(-1, se.index, -1);
+ this.removeItem(dl, item);
break;
- /* arrow up */
- case 38:
- if (prev)
- prev.focus();
+ 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();
+ }
- /* arrow down */
- case 40:
- if (next)
- next.focus();
-
+ ev.preventDefault();
break;
+ }
}
-
- return true;
}
+};
- function cbi_dynlist_btnclick(ev)
- {
- ev = ev ? ev : window.event;
+function cbi_dynlist_init(dl, datatype, optional, choices)
+{
+ if (!(this instanceof cbi_dynlist_init))
+ return new cbi_dynlist_init(dl, datatype, optional, choices);
+
+ dl.classList.add('cbi-dynlist');
+ dl.appendChild(E('div', { 'class': 'add-item' }, E('input', {
+ 'type': 'text',
+ 'name': 'cbi.dynlist.' + dl.getAttribute('data-prefix'),
+ 'class': 'cbi-input-text',
+ 'data-type': datatype,
+ 'data-optional': true
+ })));
+
+ if (choices)
+ cbi_combobox_init(dl.lastElementChild.lastElementChild, choices, '', _('-- custom --'));
+ else
+ dl.lastElementChild.appendChild(E('div', { 'class': 'cbi-button cbi-button-add' }, '+'));
- var se = ev.target ? ev.target : ev.srcElement;
- var input = se.previousSibling;
- while (input && input.name != prefix)
- input = input.previousSibling;
+ dl.addEventListener('click', this.handleClick.bind(this));
+ dl.addEventListener('keydown', this.handleKeydown.bind(this));
+ dl.addEventListener('cbi-dropdown-change', this.handleDropdownChange.bind(this));
- if (se.classList.contains('cbi-button-remove')) {
- input.value = '';
-
- cbi_dynlist_keydown({
- target: input,
- keyCode: 8
- });
- }
- else {
- cbi_dynlist_keydown({
- target: input,
- keyCode: 13
- });
- }
+ try {
+ var values = JSON.parse(dl.getAttribute('data-values') || '[]');
- return false;
+ if (typeof(values) === 'object' && Array.isArray(values))
+ for (var i = 0; i < values.length; i++)
+ this.addItem(dl, values[i], choices ? choices[values[i]] : null);
}
-
- cbi_dynlist_redraw(NaN, -1, -1);
+ catch (e) {}
}
+cbi_dynlist_init.prototype = CBIDynamicList;
+
function cbi_t_add(section, tab) {
var t = document.getElementById('tab.' + section + '.' + tab);