libs/web: rework DynamicList widget

This commit is contained in:
Jo-Philipp Wich 2010-10-25 17:04:03 +00:00
parent eecec8b0f2
commit 70706cf388
4 changed files with 223 additions and 58 deletions

View file

@ -265,22 +265,19 @@ function cbi_d_update() {
}
function cbi_bind(obj, type, callback, mode) {
if (typeof mode == "undefined") {
mode = false;
}
if (!obj.addEventListener) {
ieCallback = function(){
var e = window.event;
if (!e.target && e.srcElement) {
e.target = e.srcElement;
};
e.target['_eCB' + type + callback] = callback;
e.target['_eCB' + type + callback](e);
e.target['_eCB' + type + callback] = null;
};
obj.attachEvent('on' + type, ieCallback);
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);
obj.addEventListener(type, callback, !!mode);
}
return obj;
}
@ -345,7 +342,9 @@ function cbi_combobox(id, values, def, man) {
obj.focus();
} else {
obj.value = sel.options[sel.selectedIndex].value;
sel.className = (!obj.validate || obj.validate())
var vld = obj.getAttribute("cbi_validate");
sel.className = (!vld || vld())
? 'cbi-input-select' : 'cbi-input-select cbi-input-invalid';
}
@ -375,6 +374,152 @@ function cbi_filebrowser(id, url, defpath) {
browser.focus();
}
function cbi_dynlist_init(name)
{
function cbi_dynlist_renumber(e)
{
var count = 1;
var childs = e.parentNode.childNodes;
for( var i = 0; i < childs.length; i++ )
if( childs[i].name == name )
childs[i].id = name + '.' + (count++);
e.focus();
}
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;
}
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 != name)
prev = prev.previousSibling;
var next = se.nextSibling;
while (next && next.name != name)
next = next.nextSibling;
switch (ev.keyCode)
{
/* backspace, delete */
case 8:
case 46:
var jump = (ev.keyCode == 8)
? (prev || next) : (next || prev);
if (se.value.length == 0 && jump)
{
se.parentNode.removeChild(se.nextSibling);
se.parentNode.removeChild(se);
cbi_dynlist_renumber(jump);
if (ev.preventDefault)
ev.preventDefault();
return false;
}
break;
/* enter */
case 13:
var n = document.createElement('input');
n.name = se.name;
n.type = se.type;
cbi_bind(n, 'keydown', cbi_dynlist_keydown);
cbi_bind(n, 'keypress', cbi_dynlist_keypress);
if (next)
{
se.parentNode.insertBefore(n, next);
se.parentNode.insertBefore(document.createElement('br'), next);
}
else
{
se.parentNode.appendChild(n);
se.parentNode.appendChild(document.createElement('br'));
}
var dt = se.getAttribute('cbi_datatype');
var op = se.getAttribute('cbi_optional') == 'true';
if (dt)
cbi_validate_field(n, op, dt);
cbi_dynlist_renumber(n);
break;
/* arrow up */
case 38:
if (prev)
prev.focus();
break;
/* arrow down */
case 40:
if (next)
next.focus();
break;
}
return true;
}
var inputs = document.getElementsByName(name);
for( var i = 0; i < inputs.length; i++ )
{
cbi_bind(inputs[i], 'keydown', cbi_dynlist_keydown);
cbi_bind(inputs[i], 'keypress', cbi_dynlist_keypress);
}
}
//Hijacks the CBI form to send via XHR (requires Prototype)
function cbi_hijack_forms(layer, win, fail, load) {
var forms = layer.getElementsByTagName('form');
@ -484,7 +629,7 @@ function cbi_validate_reset(form)
function cbi_validate_field(cbid, optional, type)
{
var field = document.getElementById(cbid);
var field = (typeof cbid == "string") ? document.getElementById(cbid) : cbid;
var vldcb = cbi_validators[type];
if( field && vldcb )
@ -513,7 +658,13 @@ function cbi_validate_field(cbid, optional, type)
field.form.cbi_validators = [ ];
field.form.cbi_validators.push(validator);
field.onblur = field.onkeyup = field.validate = validator;
cbi_bind(field, "blur", validator);
cbi_bind(field, "keyup", validator);
field.setAttribute("cbi_validate", validator);
field.setAttribute("cbi_datatype", type);
field.setAttribute("cbi_optional", (!!optional).toString());
validator();
}

View file

@ -1641,25 +1641,48 @@ function DynamicList.value(self, key, val)
table.insert(self.vallist, tostring(val))
end
function DynamicList.write(self, ...)
self.map.proceed = true
return AbstractValue.write(self, ...)
function DynamicList.write(self, section, value)
if self.cast == "string" and type(value) == "table" then
value = table.concat(value, " ")
elseif self.cast == "table" and type(value) == "string" then
local x, t = { }
for x in value:gmatch("%S+") do
t[#t+1] = x
end
value = t
end
return AbstractValue.write(self, section, value)
end
function DynamicList.cfgvalue(self, section)
local value = AbstractValue.cfgvalue(self, section)
if type(value) == "string" then
local x
local t = { }
for x in value:gmatch("%S+") do
t[#t+1] = x
end
value = t
end
return value
end
function DynamicList.formvalue(self, section)
local value = AbstractValue.formvalue(self, section)
value = (type(value) == "table") and value or {value}
local valid = {}
for i, v in ipairs(value) do
if v and #v > 0
and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
table.insert(valid, v)
if type(value) == "string" then
local x
local t = { }
for x in value:gmatch("%S+") do
t[#t+1] = x
end
value = t
end
return valid
return value
end

View file

@ -13,40 +13,31 @@ $Id$
-%>
<%+cbi/valueheader%>
<div>
<%
local vals = self:cfgvalue(section) or {}
for i=1, #vals + 1 do
local val = vals[i]
%>
<input class="cbi-input-text" value="<%=pcdata(val)%>" onchange="cbi_d_update(this.id)" type="text"<%= attr("id", cbid .. "." .. i) .. attr("name", cbid) .. ifattr(self.size, "size")%> />
<% if i <= #vals then %>
<input class="cbi-input-image" type="image" value="<%:Delete%>" name="cbi.rle.<%=section .. "." .. self.option .. "." .. i%>" alt="<%:Delete%>" title="<%:Delete%>" src="<%=resource%>/cbi/remove.gif" />
<% else %>
<input class="cbi-input-image" type="image" value="<%:Add%>" name="cbi.ale.<%=section .. "." .. self.option%>" alt="<%:Add%>" title="<%:Add%>" src="<%=resource%>/cbi/add.gif" />
<% end %>
<% if #self.keylist > 0 then -%>
<script type="text/javascript">
cbi_combobox_init('<%=cbid .. "." .. i%>', {
<%-
for i, k in ipairs(self.keylist) do
-%>
<%-=string.format("%q", k) .. ":" .. string.format("%q", self.vallist[i])-%>
<%-if i<#self.keylist then-%>,<%-end-%>
<%-
end
-%>
}, '<%- if not self.rmempty and not self.optional then -%>
<%-:cbi_select-%>
<%- end -%>', '<%: -- custom -- %>');
</script>
<% end -%>
<% if i <= #vals then %><br />
<% end end %>
<input class="cbi-input-text" value="<%=pcdata(val)%>" onchange="cbi_d_update(this.id)" type="text"<%= attr("id", cbid .. "." .. i) .. attr("name", cbid) .. ifattr(self.size, "size")%> /><br />
<% end %>
</div>
<script type="text/javascript">
cbi_dynlist_init('<%=cbid%>');
<% if self.datatype then -%>
<script type="text/javascript">
<% for i=1, #vals + 1 do -%>
cbi_validate_field('<%=cbid%>.<%=i%>', <%=tostring(self.optional == true or i > #vals)%>, '<%=self.datatype%>');
<%- end %>
</script>
<% if #self.keylist > 0 then -%>
cbi_combobox_init('<%=cbid .. "." .. i%>', {
<%- for i, k in ipairs(self.keylist) do -%>
<%-=string.format("%q", k) .. ":" .. string.format("%q", self.vallist[i])-%>
<%-if i<#self.keylist then-%>,<%-end-%>
<%- end -%>
}, '<%- if not self.rmempty and not self.optional then -%>
<%-:cbi_select-%>
<%- end -%>', '<%: -- custom -- %>');
<% end -%>
<% for i=1, #vals + 1 do -%>
cbi_validate_field('<%=cbid%>.<%=i%>', <%=tostring(self.optional == true or i > #vals)%>, '<%=self.datatype%>');
<%- end %>
<% end -%>
</script>
<%+cbi/valuefooter%>

View file

@ -14,7 +14,7 @@ $Id$
-%>
<% if self.description and #self.description > 0 then -%>
<% if not luci.util.instanceof( self, luci.cbi.Flag ) or self.orientation == "horizontal" then -%>
<% if not luci.util.instanceof(self, luci.cbi.DynamicList) and (not luci.util.instanceof(self, luci.cbi.Flag) or self.orientation == "horizontal") then -%>
<br />
<%- end %>
<div class="cbi-value-description">