luci-base: cbi.js, ui.js: add custom validation callbacks, new ui widgets
Implement further widget primitives like text inputs or checkboxes and support custom validation callback functions when instantiating CBI validators. Also add support initializing ui.js widgets from the "data-ui-widget" HTML attribute. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
parent
2beb9fa16f
commit
808b9f36eb
2 changed files with 396 additions and 26 deletions
|
@ -260,11 +260,11 @@ var CBIValidatorPrototype = {
|
||||||
|
|
||||||
validate: function() {
|
validate: function() {
|
||||||
/* element is detached */
|
/* element is detached */
|
||||||
if (!findParent(this.field, 'body'))
|
if (!findParent(this.field, 'body') && !findParent(this.field, '[data-field]'))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
this.field.classList.remove('cbi-input-invalid');
|
this.field.classList.remove('cbi-input-invalid');
|
||||||
this.value = matchesElem(this.field, 'select') ? this.field.options[this.field.selectedIndex].value : this.field.value;
|
this.value = (this.field.value != null) ? this.field.value : '';
|
||||||
this.error = null;
|
this.error = null;
|
||||||
|
|
||||||
var valid;
|
var valid;
|
||||||
|
@ -274,18 +274,28 @@ var CBIValidatorPrototype = {
|
||||||
else
|
else
|
||||||
valid = this.vstack[0].apply(this, this.vstack[1]);
|
valid = this.vstack[0].apply(this, this.vstack[1]);
|
||||||
|
|
||||||
if (!valid) {
|
if (valid !== true) {
|
||||||
this.field.setAttribute('data-tooltip', _('Expecting %s').format(this.error));
|
this.field.setAttribute('data-tooltip', _('Expecting %s').format(this.error));
|
||||||
this.field.setAttribute('data-tooltip-style', 'error');
|
this.field.setAttribute('data-tooltip-style', 'error');
|
||||||
this.field.dispatchEvent(new CustomEvent('validation-failure', { bubbles: true }));
|
this.field.dispatchEvent(new CustomEvent('validation-failure', { bubbles: true }));
|
||||||
}
|
return false;
|
||||||
else {
|
|
||||||
this.field.removeAttribute('data-tooltip');
|
|
||||||
this.field.removeAttribute('data-tooltip-style');
|
|
||||||
this.field.dispatchEvent(new CustomEvent('validation-success', { bubbles: true }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return valid;
|
if (typeof(this.vfunc) == 'function')
|
||||||
|
valid = this.vfunc(this.value);
|
||||||
|
|
||||||
|
if (valid !== true) {
|
||||||
|
this.assert(false, valid);
|
||||||
|
this.field.setAttribute('data-tooltip', valid);
|
||||||
|
this.field.setAttribute('data-tooltip-style', 'error');
|
||||||
|
this.field.dispatchEvent(new CustomEvent('validation-failure', { bubbles: true }));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.field.removeAttribute('data-tooltip');
|
||||||
|
this.field.removeAttribute('data-tooltip-style');
|
||||||
|
this.field.dispatchEvent(new CustomEvent('validation-success', { bubbles: true }));
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
types: {
|
types: {
|
||||||
|
@ -600,10 +610,14 @@ var CBIValidatorPrototype = {
|
||||||
if (sibling === option)
|
if (sibling === option)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var input = sibling.querySelector('[data-type]'),
|
var values = L.dom.callClassMethod(sibling.querySelector('[data-idref]'), 'getValue');
|
||||||
values = input ? (input.getAttribute('data-is-list') ? input.value.match(/[^ \t]+/g) : [ input.value ]) : null;
|
|
||||||
|
|
||||||
if (values !== null && values.indexOf(ctx.value) !== -1)
|
if (!Array.isArray(values) && sibling.querySelector('[data-is-list]'))
|
||||||
|
values = String(values || '').match(/[^ \t]+/g) || [];
|
||||||
|
else if (!Array.isArray(values))
|
||||||
|
values = (values != null) ? [ values ] : [];
|
||||||
|
|
||||||
|
if (values.indexOf(ctx.value) != -1)
|
||||||
unique = false;
|
unique = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -619,14 +633,19 @@ var CBIValidatorPrototype = {
|
||||||
hexstring: function() {
|
hexstring: function() {
|
||||||
return this.assert(this.value.match(/^([a-f0-9][a-f0-9]|[A-F0-9][A-F0-9])+$/),
|
return this.assert(this.value.match(/^([a-f0-9][a-f0-9]|[A-F0-9][A-F0-9])+$/),
|
||||||
_('hexadecimal encoded value'));
|
_('hexadecimal encoded value'));
|
||||||
|
},
|
||||||
|
|
||||||
|
string: function() {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function CBIValidator(field, type, optional)
|
function CBIValidator(field, type, optional, vfunc)
|
||||||
{
|
{
|
||||||
this.field = field;
|
this.field = field;
|
||||||
this.optional = optional;
|
this.optional = optional;
|
||||||
|
this.vfunc = vfunc;
|
||||||
this.vstack = this.compile(type);
|
this.vstack = this.compile(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -850,6 +869,15 @@ function cbi_init() {
|
||||||
i.addEventListener('mouseout', handler);
|
i.addEventListener('mouseout', handler);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-ui-widget]').forEach(function(node) {
|
||||||
|
var args = JSON.parse(node.getAttribute('data-ui-widget') || '[]'),
|
||||||
|
widget = new (Function.prototype.bind.apply(L.ui[args[0]], args)),
|
||||||
|
markup = widget.render();
|
||||||
|
|
||||||
|
markup.addEventListener('widget-change', cbi_d_update);
|
||||||
|
node.parentNode.replaceChild(markup, node);
|
||||||
|
});
|
||||||
|
|
||||||
cbi_d_update();
|
cbi_d_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,18 @@ var UIElement = L.Class.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
isValid: function() {
|
isValid: function() {
|
||||||
return true;
|
return (this.validState !== false);
|
||||||
|
},
|
||||||
|
|
||||||
|
triggerValidation: function() {
|
||||||
|
if (typeof(this.vfunc) != 'function')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var wasValid = this.isValid();
|
||||||
|
|
||||||
|
this.vfunc();
|
||||||
|
|
||||||
|
return (wasValid != this.isValid());
|
||||||
},
|
},
|
||||||
|
|
||||||
registerEvents: function(targetNode, synevent, events) {
|
registerEvents: function(targetNode, synevent, events) {
|
||||||
|
@ -32,7 +43,28 @@ var UIElement = L.Class.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
setUpdateEvents: function(targetNode /*, ... */) {
|
setUpdateEvents: function(targetNode /*, ... */) {
|
||||||
this.registerEvents(targetNode, 'widget-update', this.varargs(arguments, 1));
|
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));
|
||||||
},
|
},
|
||||||
|
|
||||||
setChangeEvents: function(targetNode /*, ... */) {
|
setChangeEvents: function(targetNode /*, ... */) {
|
||||||
|
@ -40,13 +72,273 @@ var UIElement = L.Class.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var UITextfield = UIElement.extend({
|
||||||
|
__init__: function(value, options) {
|
||||||
|
this.value = value;
|
||||||
|
this.options = Object.assign({
|
||||||
|
optional: true,
|
||||||
|
password: false
|
||||||
|
}, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
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', {
|
||||||
|
'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);
|
||||||
|
},
|
||||||
|
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
|
||||||
|
getValue: function() {
|
||||||
|
var inputEl = this.node.childNodes[+!!this.options.password];
|
||||||
|
return inputEl.value;
|
||||||
|
},
|
||||||
|
|
||||||
|
setValue: function(value) {
|
||||||
|
var inputEl = this.node.childNodes[+!!this.options.password];
|
||||||
|
inputEl.value = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var UICheckbox = UIElement.extend({
|
||||||
|
__init__: function(value, options) {
|
||||||
|
this.value = value;
|
||||||
|
this.options = Object.assign({
|
||||||
|
value_enabled: '1',
|
||||||
|
value_disabled: '0'
|
||||||
|
}, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
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', {
|
||||||
|
'name': this.options.name,
|
||||||
|
'type': 'checkbox',
|
||||||
|
'value': this.options.value_enabled,
|
||||||
|
'checked': (this.value == this.options.value_enabled) ? '' : null
|
||||||
|
}));
|
||||||
|
|
||||||
|
return this.bind(frameEl);
|
||||||
|
},
|
||||||
|
|
||||||
|
bind: function(frameEl) {
|
||||||
|
this.node = frameEl;
|
||||||
|
|
||||||
|
this.setUpdateEvents(frameEl.lastElementChild, 'click', 'blur');
|
||||||
|
this.setChangeEvents(frameEl.lastElementChild, 'change');
|
||||||
|
|
||||||
|
L.dom.bindClassInstance(frameEl, this);
|
||||||
|
|
||||||
|
return frameEl;
|
||||||
|
},
|
||||||
|
|
||||||
|
isChecked: function() {
|
||||||
|
return this.node.lastElementChild.checked;
|
||||||
|
},
|
||||||
|
|
||||||
|
getValue: function() {
|
||||||
|
return this.isChecked()
|
||||||
|
? this.options.value_enabled
|
||||||
|
: this.options.value_disabled;
|
||||||
|
},
|
||||||
|
|
||||||
|
setValue: function(value) {
|
||||||
|
this.node.lastElementChild.checked = (value == this.options.value_enabled);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var UISelect = UIElement.extend({
|
||||||
|
__init__: function(value, choices, options) {
|
||||||
|
if (typeof(choices) != 'object')
|
||||||
|
choices = {};
|
||||||
|
|
||||||
|
if (!Array.isArray(value))
|
||||||
|
value = (value != null && value != '') ? [ value ] : [];
|
||||||
|
|
||||||
|
if (!options.multi && value.length > 1)
|
||||||
|
value.length = 1;
|
||||||
|
|
||||||
|
this.values = value;
|
||||||
|
this.choices = choices;
|
||||||
|
this.options = Object.assign({
|
||||||
|
multi: false,
|
||||||
|
widget: 'select',
|
||||||
|
orientation: 'horizontal'
|
||||||
|
}, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var frameEl,
|
||||||
|
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 = E('select', {
|
||||||
|
'id': this.options.id,
|
||||||
|
'name': this.options.name,
|
||||||
|
'size': this.options.size,
|
||||||
|
'class': 'cbi-input-select',
|
||||||
|
'multiple': this.options.multi ? '' : null
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.options.optional)
|
||||||
|
frameEl.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.appendChild(E('option', {
|
||||||
|
'value': keys[i],
|
||||||
|
'selected': (this.values.indexOf(keys[i]) > -1) ? '' : null
|
||||||
|
}, this.choices[keys[i]] || keys[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
frameEl = E('div', {
|
||||||
|
'id': this.options.id
|
||||||
|
});
|
||||||
|
|
||||||
|
var brEl = (this.options.orientation === 'horizontal') ? document.createTextNode(' ') : E('br');
|
||||||
|
|
||||||
|
for (var i = 0; i < keys.length; i++) {
|
||||||
|
frameEl.appendChild(E('label', {}, [
|
||||||
|
E('input', {
|
||||||
|
'name': this.options.id || this.options.name,
|
||||||
|
'type': this.options.multi ? 'checkbox' : 'radio',
|
||||||
|
'class': this.options.multi ? '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);
|
||||||
|
},
|
||||||
|
|
||||||
|
bind: function(frameEl) {
|
||||||
|
this.node = frameEl;
|
||||||
|
|
||||||
|
if (this.options.widget == 'select') {
|
||||||
|
this.setUpdateEvents(frameEl, 'change', 'click', 'blur');
|
||||||
|
this.setChangeEvents(frameEl, '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;
|
||||||
|
},
|
||||||
|
|
||||||
|
getValue: function() {
|
||||||
|
if (this.options.widget == 'select')
|
||||||
|
return this.node.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;
|
||||||
|
},
|
||||||
|
|
||||||
|
setValue: function(value) {
|
||||||
|
if (this.options.widget == 'select') {
|
||||||
|
if (value == null)
|
||||||
|
value = '';
|
||||||
|
|
||||||
|
for (var i = 0; i < this.node.options.length; i++)
|
||||||
|
this.node.options[i].selected = (this.node.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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var UIDropdown = UIElement.extend({
|
var UIDropdown = UIElement.extend({
|
||||||
__init__: function(value, choices, options) {
|
__init__: function(value, choices, options) {
|
||||||
if (typeof(choices) != 'object')
|
if (typeof(choices) != 'object')
|
||||||
choices = {};
|
choices = {};
|
||||||
|
|
||||||
if (!Array.isArray(value))
|
if (!Array.isArray(value))
|
||||||
this.values = (value != null) ? [ value ] : [];
|
this.values = (value != null && value != '') ? [ value ] : [];
|
||||||
else
|
else
|
||||||
this.values = value;
|
this.values = value;
|
||||||
|
|
||||||
|
@ -95,15 +387,22 @@ var UIDropdown = UIElement.extend({
|
||||||
var createEl = E('input', {
|
var createEl = E('input', {
|
||||||
'type': 'text',
|
'type': 'text',
|
||||||
'class': 'create-item-input',
|
'class': 'create-item-input',
|
||||||
|
'readonly': this.options.readonly ? '' : null,
|
||||||
|
'maxlength': this.options.maxlength,
|
||||||
'placeholder': this.options.custom_placeholder || this.options.placeholder
|
'placeholder': this.options.custom_placeholder || this.options.placeholder
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.options.datatype)
|
if (this.options.datatype)
|
||||||
L.ui.addValidator(createEl, this.options.datatype, true, 'blur', 'keyup');
|
L.ui.addValidator(createEl, this.options.datatype,
|
||||||
|
true, null, 'blur', 'keyup');
|
||||||
|
|
||||||
sb.lastElementChild.appendChild(E('li', { 'data-value': '-' }, createEl));
|
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);
|
return this.bind(sb);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -751,7 +1050,7 @@ var UIDropdown = UIElement.extend({
|
||||||
setValue: function(values) {
|
setValue: function(values) {
|
||||||
if (this.options.multi) {
|
if (this.options.multi) {
|
||||||
if (!Array.isArray(values))
|
if (!Array.isArray(values))
|
||||||
values = (values != null) ? [ values ] : [];
|
values = (values != null && values != '') ? [ values ] : [];
|
||||||
|
|
||||||
var v = {};
|
var v = {};
|
||||||
|
|
||||||
|
@ -791,9 +1090,9 @@ var UICombobox = UIDropdown.extend({
|
||||||
this.super('__init__', [ value, choices, Object.assign({
|
this.super('__init__', [ value, choices, Object.assign({
|
||||||
select_placeholder: _('-- Please choose --'),
|
select_placeholder: _('-- Please choose --'),
|
||||||
custom_placeholder: _('-- custom --'),
|
custom_placeholder: _('-- custom --'),
|
||||||
dropdown_items: 5
|
dropdown_items: 5,
|
||||||
|
sort: true
|
||||||
}, options, {
|
}, options, {
|
||||||
sort: true,
|
|
||||||
multi: false,
|
multi: false,
|
||||||
create: true,
|
create: true,
|
||||||
optional: true
|
optional: true
|
||||||
|
@ -804,7 +1103,7 @@ var UICombobox = UIDropdown.extend({
|
||||||
var UIDynamicList = UIElement.extend({
|
var UIDynamicList = UIElement.extend({
|
||||||
__init__: function(values, choices, options) {
|
__init__: function(values, choices, options) {
|
||||||
if (!Array.isArray(values))
|
if (!Array.isArray(values))
|
||||||
values = (values != null) ? [ values ] : [];
|
values = (values != null && values != '') ? [ values ] : [];
|
||||||
|
|
||||||
if (typeof(choices) != 'object')
|
if (typeof(choices) != 'object')
|
||||||
choices = null;
|
choices = null;
|
||||||
|
@ -837,7 +1136,9 @@ var UIDynamicList = UIElement.extend({
|
||||||
dl.lastElementChild.appendChild(inputEl);
|
dl.lastElementChild.appendChild(inputEl);
|
||||||
dl.lastElementChild.appendChild(E('div', { 'class': 'cbi-button cbi-button-add' }, '+'));
|
dl.lastElementChild.appendChild(E('div', { 'class': 'cbi-button cbi-button-add' }, '+'));
|
||||||
|
|
||||||
L.ui.addValidator(inputEl, this.options.datatype, true, 'blue', 'keyup');
|
if (this.options.datatype)
|
||||||
|
L.ui.addValidator(inputEl, this.options.datatype,
|
||||||
|
true, null, 'blur', 'keyup');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < this.values.length; i++)
|
for (var i = 0; i < this.values.length; i++)
|
||||||
|
@ -1012,7 +1313,7 @@ var UIDynamicList = UIElement.extend({
|
||||||
|
|
||||||
setValue: function(values) {
|
setValue: function(values) {
|
||||||
if (!Array.isArray(values))
|
if (!Array.isArray(values))
|
||||||
values = (values != null) ? [ values ] : [];
|
values = (values != null && values != '') ? [ values ] : [];
|
||||||
|
|
||||||
var items = this.node.querySelectorAll('.item');
|
var items = this.node.querySelectorAll('.item');
|
||||||
|
|
||||||
|
@ -1026,6 +1327,41 @@ var UIDynamicList = UIElement.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var UIHiddenfield = UIElement.extend({
|
||||||
|
__init__: function(value, options) {
|
||||||
|
this.value = value;
|
||||||
|
this.options = Object.assign({
|
||||||
|
|
||||||
|
}, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var hiddenEl = E('input', {
|
||||||
|
'id': this.options.id,
|
||||||
|
'type': 'hidden',
|
||||||
|
'value': this.value
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.bind(hiddenEl);
|
||||||
|
},
|
||||||
|
|
||||||
|
bind: function(hiddenEl) {
|
||||||
|
this.node = hiddenEl;
|
||||||
|
|
||||||
|
L.dom.bindClassInstance(hiddenEl, this);
|
||||||
|
|
||||||
|
return hiddenEl;
|
||||||
|
},
|
||||||
|
|
||||||
|
getValue: function() {
|
||||||
|
return this.node.value;
|
||||||
|
},
|
||||||
|
|
||||||
|
setValue: function(value) {
|
||||||
|
this.node.value = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
return L.Class.extend({
|
return L.Class.extend({
|
||||||
__init__: function() {
|
__init__: function() {
|
||||||
|
@ -1658,7 +1994,7 @@ return L.Class.extend({
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
addValidator: function(field, type, optional /*, ... */) {
|
addValidator: function(field, type, optional, vfunc /*, ... */) {
|
||||||
if (type == null)
|
if (type == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1667,19 +2003,25 @@ return L.Class.extend({
|
||||||
events.push('blur', 'keyup');
|
events.push('blur', 'keyup');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var cbiValidator = new CBIValidator(field, type, optional),
|
var cbiValidator = new CBIValidator(field, type, optional, vfunc),
|
||||||
validatorFn = cbiValidator.validate.bind(cbiValidator);
|
validatorFn = cbiValidator.validate.bind(cbiValidator);
|
||||||
|
|
||||||
for (var i = 0; i < events.length; i++)
|
for (var i = 0; i < events.length; i++)
|
||||||
field.addEventListener(events[i], validatorFn);
|
field.addEventListener(events[i], validatorFn);
|
||||||
|
|
||||||
validatorFn();
|
validatorFn();
|
||||||
|
|
||||||
|
return validatorFn;
|
||||||
}
|
}
|
||||||
catch (e) { }
|
catch (e) { }
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Widgets */
|
/* Widgets */
|
||||||
|
Textfield: UITextfield,
|
||||||
|
Checkbox: UICheckbox,
|
||||||
|
Select: UISelect,
|
||||||
Dropdown: UIDropdown,
|
Dropdown: UIDropdown,
|
||||||
DynamicList: UIDynamicList,
|
DynamicList: UIDynamicList,
|
||||||
Combobox: UICombobox
|
Combobox: UICombobox,
|
||||||
|
Hiddenfield: UIHiddenfield
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue