gluon/package/gluon-web-model/javascript/gluon-web-model.js

549 lines
12 KiB
JavaScript
Raw Normal View History

/*
SPDX-License-Identifier: Apache-2.0
SPDX-FileCopyrightText: 2008, Steven Barth <steven@midlink.org>
SPDX-FileCopyrightText: 2008-2012, Jo-Philipp Wich <jow@openwrt.org>
SPDX-FileCopyrightText: 2017, Matthias Schiffer <mschiffer@universe-factory.net>
SPDX-FileCopyrightText: 2023, Leonardo Mörlein <me@irrelefant.net>
*/
/*
Build using:
uglifyjs javascript/gluon-web-model.js -o javascript/gluon-web-model.min.js -c -m --ie
*/
(function() {
var dep_entries = {};
function Int(x) {
return (/^-?\d+$/.test(x) ? +x : NaN);
}
function Dec(x) {
return (/^-?\d*\.?\d+?$/.test(x) ? +x : NaN);
}
var validators = {
'integer': function() {
return !isNaN(Int(this));
},
'uinteger': function() {
return (Int(this) >= 0);
},
'float': function() {
return !isNaN(Dec(this));
},
'ufloat': function() {
return (Dec(this) >= 0);
},
'ipaddr': function() {
return validators.ip4addr.apply(this) ||
validators.ip6addr.apply(this);
},
'ip4addr': function() {
var match;
if ((match = this.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/))) {
return (match[1] >= 0) && (match[1] <= 255) &&
2022-12-18 17:23:34 +00:00
(match[2] >= 0) && (match[2] <= 255) &&
(match[3] >= 0) && (match[3] <= 255) &&
(match[4] >= 0) && (match[4] <= 255);
}
return false;
},
'ip6addr': function() {
if (this.indexOf('::') < 0)
return (this.match(/^(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}$/i) != null);
if (
(this.indexOf(':::') >= 0) || this.match(/::.+::/) ||
this.match(/^:[^:]/) || this.match(/[^:]:$/)
)
return false;
if (this.match(/^(?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}$/i))
return true;
if (this.match(/^(?:[a-f0-9]{1,4}:){7}:$/i))
return true;
if (this.match(/^:(?::[a-f0-9]{1,4}){7}$/i))
return true;
return false;
},
'wpakey': function() {
var v = this;
if (v.length == 64)
return (v.match(/^[a-f0-9]{64}$/i) != null);
else
return (v.length >= 8) && (v.length <= 63);
},
'range': function(min, max) {
var val = Dec(this);
return (val >= +min && val <= +max);
},
'min': function(min) {
return (Dec(this) >= +min);
},
'max': function(max) {
return (Dec(this) <= +max);
},
'irange': function(min, max) {
var val = Int(this);
return (val >= +min && val <= +max);
},
'imin': function(min) {
return (Int(this) >= +min);
},
'imax': function(max) {
return (Int(this) <= +max);
},
'minlength': function(min) {
return ((''+this).length >= +min);
},
'maxlength': function(max) {
return ((''+this).length <= +max);
},
};
function compile(type) {
var v, match;
if ((match = type.match(/^([^\(]+)\(([^,]+),([^\)]+)\)$/)) && (v = validators[match[1]]) !== undefined) {
return function() {
return v.apply(this, [match[2], match[3]]);
}
} else if ((match = type.match(/^([^\(]+)\(([^,\)]+)\)$/)) && (v = validators[match[1]]) !== undefined) {
return function() {
return v.apply(this, [match[2]]);
}
} else {
return validators[type];
}
}
function checkvalue(target, ref) {
var t = document.getElementById(target);
var value;
if (t) {
if (t.type == "checkbox") {
value = t.checked;
} else if (t.value) {
value = t.value;
} else {
value = "";
}
return (value == ref);
} else {
t = document.getElementById(target + '.' + ref);
if (t)
return (t.type == "radio" && t.checked);
}
return false;
}
function check(deps) {
for (var i=0; i < deps.length; i++) {
var stat = true;
for (var j in deps[i]) {
stat = (stat && checkvalue(j, deps[i][j]));
}
if (stat)
return true;
}
return false;
}
function update() {
window.dispatchEvent(new Event('gluon-update'));
var state = false;
for (var id in dep_entries) {
var entry = dep_entries[id];
var node = document.getElementById(id);
var parent = document.getElementById(entry.parent);
if (node && node.parentNode && !check(entry.deps)) {
node.parentNode.removeChild(node);
node.dispatchEvent(new Event('gluon-hide'));
state = true;
} else if (parent && (!node || !node.parentNode) && check(entry.deps)) {
var next = undefined;
for (next = parent.firstChild; next; next = next.nextSibling) {
if (next.getAttribute && parseInt(next.getAttribute('data-index'), 10) > entry.index) {
break;
}
}
if (!next) {
parent.appendChild(entry.node);
} else {
parent.insertBefore(entry.node, next);
}
entry.node.dispatchEvent(new Event('gluon-show'));
state = true;
}
// hide optionals widget if no choices remaining
if (parent && parent.parentNode && parent.getAttribute('data-optionals'))
parent.parentNode.style.display = (parent.options.length <= 1) ? 'none' : '';
}
var nodes = document.querySelectorAll('[data-exclusive-with]');
for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
var exclusive_with = JSON.parse(node.getAttribute('data-exclusive-with'));
node.disabled = false;
for (var list_item of exclusive_with) {
var el = document.getElementById(node.name + '.' + list_item);
node.disabled ||= el.checked;
}
if (node.disabled)
node.checked = false;
}
if (state) {
update();
}
}
function bind(obj, type, callback, mode) {
if (!obj.addEventListener) {
obj.attachEvent('on' + type,
function() {
var e = window.event;
if (!e.target && e.srcElement)
e.target = e.srcElement;
return !!callback(e);
}
);
} else {
obj.addEventListener(type, callback, !!mode);
}
return obj;
}
function init_dynlist(parent, attr) {
var prefix = attr.prefix;
function dynlist_redraw(focus, add, del) {
var values = [];
while (parent.firstChild) {
var n = parent.firstChild;
var i = +n.index;
if (i != del) {
if (n.nodeName.toLowerCase() == 'input')
values.push(n.value || '');
else if (n.nodeName.toLowerCase() == 'select')
values[values.length-1] = n.options[n.selectedIndex].value;
}
parent.removeChild(n);
}
if (add >= 0) {
focus = add + 1;
values.splice(add, 0, '');
} else if (!attr.optional && values.length == 0) {
values.push('');
}
for (var i = 1; i <= values.length; i++) {
var t = document.createElement('input');
t.id = prefix + '.' + i;
t.name = prefix;
t.value = values[i-1];
t.type = 'text';
t.index = i;
if (attr.size)
t.size = attr.size;
if (attr.placeholder)
t.placeholder = attr.placeholder;
parent.appendChild(t);
if (attr.type)
validate_field(t, false, attr.type);
bind(t, 'keydown', dynlist_keydown);
bind(t, 'keypress', dynlist_keypress);
if (i == focus) {
t.focus();
} else if (-i == focus) {
t.focus();
/* force cursor to end */
var v = t.value;
t.value = ' '
t.value = v;
}
if (attr.optional || values.length > 1) {
var b = document.createElement('span');
b.className = 'gluon-remove';
parent.appendChild(b);
bind(b, 'click', dynlist_btnclick(false));
parent.appendChild(document.createElement('br'));
}
}
var b = document.createElement('span');
b.className = 'gluon-add';
parent.appendChild(b);
bind(b, 'click', dynlist_btnclick(true));
}
function 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;
}
return true;
}
function dynlist_keydown(ev) {
ev = ev ? ev : window.event;
var se = ev.target ? ev.target : ev.srcElement;
var index = 0;
var prev, next;
if (se) {
if (se.nodeType == 3)
se = se.parentNode;
index = se.index;
prev = se.previousSibling;
while (prev && prev.name != prefix)
prev = prev.previousSibling;
next = se.nextSibling;
while (next && next.name != prefix)
next = next.nextSibling;
}
switch (ev.keyCode) {
/* backspace, delete */
case 8:
case 46:
var del = (se.nodeName.toLowerCase() == 'select')
? true : (se.value.length == 0);
if (del) {
if (ev.preventDefault)
ev.preventDefault();
var focus = se.index;
if (ev.keyCode == 8)
focus = -focus+1;
dynlist_redraw(focus, -1, index);
return false;
}
break;
/* enter */
case 13:
dynlist_redraw(-1, index, -1);
break;
/* arrow up */
case 38:
if (prev)
prev.focus();
break;
/* arrow down */
case 40:
if (next)
next.focus();
break;
}
return true;
}
function dynlist_btnclick(add) {
return function(ev) {
ev = ev ? ev : window.event;
var se = ev.target ? ev.target : ev.srcElement;
var input = se.previousSibling;
while (input && input.name != prefix) {
input = input.previousSibling;
}
if (add) {
dynlist_keydown({
target: input,
keyCode: 13
});
} else {
input.value = '';
dynlist_keydown({
target: input,
keyCode: 8
});
}
return false;
}
}
dynlist_redraw(NaN, -1, -1);
}
function validate_field(field, optional, type) {
var check = compile(type);
if (!check)
return;
var validator = function() {
if (!field.form)
return;
field.className = field.className.replace(/ gluon-input-invalid/g, '');
var value = (field.options && field.options.selectedIndex > -1)
? field.options[field.options.selectedIndex].value : field.value;
if (!(((value.length == 0) && optional) || check.apply(value)))
field.className += ' gluon-input-invalid';
};
bind(field, "blur", validator);
bind(field, "keyup", validator);
bind(field, "gluon-revalidate", validator);
if (field.nodeName.toLowerCase() == 'select') {
bind(field, "change", validator);
bind(field, "click", validator);
}
validator();
}
function add(obj, dep, index) {
var entry = dep_entries[obj.id];
if (!entry) {
entry = {
"node": obj,
"parent": obj.parentNode.id,
"deps": [],
"index": index
};
dep_entries[obj.id] = entry;
}
entry.deps.push(dep)
}
(function() {
var nodes;
nodes = document.querySelectorAll('[data-depends]');
for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
var index = parseInt(node.getAttribute('data-index'), 10);
var depends = JSON.parse(node.getAttribute('data-depends'));
if (!isNaN(index) && depends.length > 0) {
for (var alt = 0; alt < depends.length; alt++) {
add(node, depends[alt], index);
}
}
}
nodes = document.querySelectorAll('[data-update]');
for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
var events = node.getAttribute('data-update').split(' ');
for (var j = 0, event; (event = events[j]) !== undefined; j++) {
bind(node, event, function () {setTimeout(update, 0);});
}
}
nodes = document.querySelectorAll('[data-type]');
for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
validate_field(node, node.getAttribute('data-optional') === 'true',
node.getAttribute('data-type'));
}
nodes = document.querySelectorAll('[data-dynlist]');
for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
var attr = JSON.parse(node.getAttribute('data-dynlist'));
init_dynlist(node, attr);
}
update();
})();
})();