libs/web: rework DynamicList widget
authorJo-Philipp Wich <jow@openwrt.org>
Mon, 25 Oct 2010 17:04:03 +0000 (17:04 +0000)
committerJo-Philipp Wich <jow@openwrt.org>
Mon, 25 Oct 2010 17:04:03 +0000 (17:04 +0000)
libs/web/htdocs/luci-static/resources/cbi.js
libs/web/luasrc/cbi.lua
libs/web/luasrc/view/cbi/dynlist.htm
libs/web/luasrc/view/cbi/full_valuefooter.htm

index 74a2576..d5355f9 100644 (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();
        }
index 5fa992d..17ca18c 100644 (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
 
 
index 826e2e6..9ca7534 100644 (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 %>
+       <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 -%>
        <% 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>
+               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 -%>
-<% if i <= #vals then %><br />
-<% end end %>
-<% 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>
+       <% 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%>
index c069b7f..18b2f2d 100644 (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">