Changeset 8156
- Timestamp:
- 01/09/12 00:33:47 (17 months ago)
- Location:
- luci/trunk/libs/web
- Files:
-
- 3 modified
-
htdocs/luci-static/resources/cbi.js (modified) (14 diffs)
-
luasrc/cbi.lua (modified) (2 diffs)
-
luasrc/cbi/datatypes.lua (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
luci/trunk/libs/web/htdocs/luci-static/resources/cbi.js
r8155 r8156 3 3 4 4 Copyright 2008 Steven Barth <steven@midlink.org> 5 Copyright 2008-201 1Jo-Philipp Wich <xm@subsignal.org>5 Copyright 2008-2012 Jo-Philipp Wich <xm@subsignal.org> 6 6 7 7 Licensed under the Apache License, Version 2.0 (the "License"); … … 18 18 var cbi_validators = { 19 19 20 'integer': function(v) 21 { 22 return (v.match(/^-?[0-9]+$/) != null); 23 }, 24 25 'uinteger': function(v) 26 { 27 return (cbi_validators.integer(v) && (v >= 0)); 28 }, 29 30 'float': function(v) 31 { 32 return !isNaN(parseFloat(v)); 33 }, 34 35 'ufloat': function(v) 36 { 37 return (cbi_validators['float'](v) && (v >= 0)); 38 }, 39 40 'ipaddr': function(v) 41 { 42 return cbi_validators.ip4addr(v) || cbi_validators.ip6addr(v); 43 }, 44 45 'ip4addr': function(v) 46 { 47 if (v.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(\/(\S+))?$/)) 20 'integer': function() 21 { 22 return (this.match(/^-?[0-9]+$/) != null); 23 }, 24 25 'uinteger': function() 26 { 27 return (cbi_validators.integer.apply(this) && (this >= 0)); 28 }, 29 30 'float': function() 31 { 32 return !isNaN(parseFloat(this)); 33 }, 34 35 'ufloat': function() 36 { 37 return (cbi_validators['float'].apply(this) && (this >= 0)); 38 }, 39 40 'ipaddr': function() 41 { 42 return cbi_validators.ip4addr.apply(this) || 43 cbi_validators.ip6addr.apply(this); 44 }, 45 46 'ip4addr': function() 47 { 48 if (this.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(\/(\S+))?$/)) 48 49 { 49 50 return (RegExp.$1 >= 0) && (RegExp.$1 <= 255) && … … 53 54 ((RegExp.$6.indexOf('.') < 0) 54 55 ? ((RegExp.$6 >= 0) && (RegExp.$6 <= 32)) 55 : (cbi_validators.ip4addr (RegExp.$6)))56 : (cbi_validators.ip4addr.apply(RegExp.$6))) 56 57 ; 57 58 } … … 60 61 }, 61 62 62 'ip6addr': function( v)63 { 64 if( v.match(/^([a-fA-F0-9:.]+)(\/(\d+))?$/) )63 'ip6addr': function() 64 { 65 if( this.match(/^([a-fA-F0-9:.]+)(\/(\d+))?$/) ) 65 66 { 66 67 if( !RegExp.$2 || ((RegExp.$3 >= 0) && (RegExp.$3 <= 128)) ) … … 77 78 var off = addr.lastIndexOf(':'); 78 79 79 if( !(off && cbi_validators.ip4addr (addr.substr(off+1))) )80 if( !(off && cbi_validators.ip4addr.apply(addr.substr(off+1))) ) 80 81 return false; 81 82 … … 110 111 }, 111 112 112 'port': function(v) 113 { 114 return cbi_validators.integer(v) && (v >= 0) && (v <= 65535); 115 }, 116 117 'portrange': function(v) 118 { 119 if( v.match(/^(\d+)-(\d+)$/) ) 113 'port': function() 114 { 115 return cbi_validators.integer.apply(this) && 116 (this >= 0) && (this <= 65535); 117 }, 118 119 'portrange': function() 120 { 121 if (this.match(/^(\d+)-(\d+)$/)) 120 122 { 121 123 var p1 = RegExp.$1; 122 124 var p2 = RegExp.$2; 123 125 124 return cbi_validators.port (p1) &&125 cbi_validators.port (p2) &&126 return cbi_validators.port.apply(p1) && 127 cbi_validators.port.apply(p2) && 126 128 (parseInt(p1) <= parseInt(p2)) 127 129 ; … … 129 131 else 130 132 { 131 return cbi_validators.port(v); 132 } 133 }, 134 135 'macaddr': function(v) 136 { 137 return (v.match(/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/) != null); 138 }, 139 140 'host': function(v) 141 { 142 return cbi_validators.hostname(v) || cbi_validators.ipaddr(v); 143 }, 144 145 'hostname': function(v) 146 { 147 if (v.length <= 253) 148 return (v.match(/^[a-zA-Z]+$/) != null || 149 (v.match(/^[a-zA-Z0-9][a-zA-Z0-9\-.]*[a-zA-Z0-9]$/) && 150 v.match(/[^0-9.]/))); 133 return cbi_validators.port.apply(this); 134 } 135 }, 136 137 'macaddr': function() 138 { 139 return (this.match(/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/) != null); 140 }, 141 142 'host': function() 143 { 144 return cbi_validators.hostname.apply(this) || 145 cbi_validators.ipaddr.apply(this); 146 }, 147 148 'hostname': function() 149 { 150 if (this.length <= 253) 151 return (this.match(/^[a-zA-Z]+$/) != null || 152 (this.match(/^[a-zA-Z0-9][a-zA-Z0-9\-.]*[a-zA-Z0-9]$/) && 153 this.match(/[^0-9.]/))); 151 154 152 155 return false; 153 156 }, 154 157 155 'network': function(v) 156 { 157 return cbi_validators.uciname(v) || cbi_validators.host(v); 158 }, 159 160 'wpakey': function(v) 161 { 158 'network': function() 159 { 160 return cbi_validators.uciname.apply(this) || 161 cbi_validators.host.apply(this); 162 }, 163 164 'wpakey': function() 165 { 166 var v = this; 167 162 168 if( v.length == 64 ) 163 169 return (v.match(/^[a-fA-F0-9]{64}$/) != null); … … 166 172 }, 167 173 168 'wepkey': function(v) 169 { 170 if( v.substr(0,2) == 's:' ) 174 'wepkey': function() 175 { 176 var v = this; 177 178 if ( v.substr(0,2) == 's:' ) 171 179 v = v.substr(2); 172 180 … … 177 185 }, 178 186 179 'uciname': function(v) 180 { 181 return (v.match(/^[a-zA-Z0-9_]+$/) != null); 182 }, 183 184 'range': function(v, args) 185 { 186 var min = parseInt(args[0]); 187 var max = parseInt(args[1]); 188 var val = parseInt(v); 189 187 'uciname': function() 188 { 189 return (this.match(/^[a-zA-Z0-9_]+$/) != null); 190 }, 191 192 'range': function(min, max) 193 { 194 var val = parseFloat(this); 190 195 if (!isNaN(min) && !isNaN(max) && !isNaN(val)) 191 196 return ((val >= min) && (val <= max)); … … 194 199 }, 195 200 196 'min': function(v, args) 197 { 198 var min = parseInt(args[0]); 199 var val = parseInt(v); 200 201 'min': function(min) 202 { 203 var val = parseFloat(this); 201 204 if (!isNaN(min) && !isNaN(val)) 202 205 return (val >= min); … … 205 208 }, 206 209 207 'max': function(v, args) 208 { 209 var max = parseInt(args[0]); 210 var val = parseInt(v); 211 210 'max': function(max) 211 { 212 var val = parseFloat(this); 212 213 if (!isNaN(max) && !isNaN(val)) 213 214 return (val <= max); … … 216 217 }, 217 218 218 'neg': function(v, args) 219 { 220 if (args[0] && typeof cbi_validators[args[0]] == "function") 221 return cbi_validators[args[0]](v.replace(/^\s*!\s*/, '')); 222 219 'or': function() 220 { 221 for (var i = 0; i < arguments.length; i += 2) 222 { 223 if (typeof arguments[i] != 'function') 224 { 225 if (arguments[i] == this) 226 return true; 227 i--; 228 } 229 else if (arguments[i].apply(this, arguments[i+1])) 230 { 231 return true; 232 } 233 } 223 234 return false; 224 235 }, 225 236 226 'list': function(v, args) 227 { 228 var cb = cbi_validators[args[0] || 'string']; 229 if (typeof cb == "function") 230 { 231 var cbargs = args.slice(1); 232 var values = v.match(/[^\s]+/g); 233 234 for (var i = 0; i < values.length; i++) 235 if (!cb(values[i], cbargs)) 237 'and': function() 238 { 239 for (var i = 0; i < arguments.length; i += 2) 240 { 241 if (typeof arguments[i] != 'function') 242 { 243 if (arguments[i] != this) 236 244 return false; 237 238 return true; 239 } 240 241 return false; 245 i--; 246 } 247 else if (!arguments[i].apply(this, arguments[i+1])) 248 { 249 return false; 250 } 251 } 252 return true; 253 }, 254 255 'neg': function() 256 { 257 return cbi_validators.or.apply( 258 this.replace(/^[ \t]*![ \t]*/, ''), arguments); 259 }, 260 261 'list': function(subvalidator, subargs) 262 { 263 if (typeof subvalidator != 'function') 264 return false; 265 266 var tokens = this.match(/[^ \t]+/g); 267 for (var i = 0; i < tokens.length; i++) 268 if (!subvalidator.apply(tokens[i], subargs)) 269 return false; 270 271 return true; 242 272 } 243 273 }; … … 833 863 } 834 864 865 function cbi_validate_compile(code) 866 { 867 var pos = 0; 868 var esc = false; 869 var depth = 0; 870 var stack = [ ]; 871 872 code += ','; 873 874 for (var i = 0; i < code.length; i++) 875 { 876 if (esc) 877 { 878 esc = false; 879 continue; 880 } 881 882 switch (code.charCodeAt(i)) 883 { 884 case 92: 885 esc = true; 886 break; 887 888 case 40: 889 case 44: 890 if (depth <= 0) 891 { 892 if (pos < i) 893 { 894 var label = code.substring(pos, i); 895 label = label.replace(/\\(.)/g, '$1'); 896 label = label.replace(/^[ \t]+/g, ''); 897 label = label.replace(/[ \t]+$/g, ''); 898 899 if (label && !isNaN(label)) 900 { 901 stack.push(parseFloat(label)); 902 } 903 else if (label.match(/^(['"]).*\1$/)) 904 { 905 stack.push(label.replace(/^(['"])(.*)\1$/, '$2')); 906 } 907 else if (typeof cbi_validators[label] == 'function') 908 { 909 stack.push(cbi_validators[label]); 910 stack.push(null); 911 } 912 else 913 { 914 throw "Syntax error, unhandled token '"+label+"'"; 915 } 916 } 917 pos = i+1; 918 } 919 depth += (code.charCodeAt(i) == 40); 920 break; 921 922 case 41: 923 if (--depth <= 0) 924 { 925 if (typeof stack[stack.length-2] != 'function') 926 throw "Syntax error, argument list follows non-function"; 927 928 stack[stack.length-1] = 929 arguments.callee(code.substring(pos, i)); 930 931 pos = i+1; 932 } 933 break; 934 } 935 } 936 937 return stack; 938 } 939 835 940 function cbi_validate_field(cbid, optional, type) 836 941 { 837 942 var field = (typeof cbid == "string") ? document.getElementById(cbid) : cbid; 838 var vargs; 839 840 if( type.match(/^(\w+)\(([^\(\)]+)\)/) ) 841 { 842 type = RegExp.$1; 843 vargs = RegExp.$2.split(/\s*,\s*/); 844 } 845 846 var vldcb = cbi_validators[type]; 847 848 if( field && vldcb ) 943 var vstack; try { vstack = cbi_validate_compile(type); } catch(e) { }; 944 945 if (field && vstack && typeof vstack[0] == "function") 849 946 { 850 947 var validator = function() … … 859 956 ? field.options[field.options.selectedIndex].value : field.value; 860 957 861 if ( !(((value.length == 0) && optional) || vldcb(value, vargs)))958 if (!(((value.length == 0) && optional) || vstack[0].apply(value, vstack[1]))) 862 959 { 863 960 // invalid -
luci/trunk/libs/web/luasrc/cbi.lua
r8124 r8156 153 153 end 154 154 155 -- 156 -- Compile a datatype specification into a parse tree for evaluation later on 157 -- 158 local cdt_cache = { } 159 160 function compile_datatype(code) 161 local i 162 local pos = 0 163 local esc = false 164 local depth = 0 165 local stack = { } 166 167 for i = 1, #code+1 do 168 local byte = code:byte(i) or 44 169 if esc then 170 esc = false 171 elseif byte == 92 then 172 esc = true 173 elseif byte == 40 or byte == 44 then 174 if depth <= 0 then 175 if pos < i then 176 local label = code:sub(pos, i-1) 177 :gsub("\\(.)", "%1") 178 :gsub("^%s+", "") 179 :gsub("%s+$", "") 180 181 if #label > 0 and tonumber(label) then 182 stack[#stack+1] = tonumber(label) 183 elseif label:match("^'.+'$") or label:match('^".+"$') then 184 stack[#stack+1] = label:gsub("[\"'](.+)[\"']", "%1") 185 elseif type(datatypes[label]) == "function" then 186 stack[#stack+1] = datatypes[label] 187 stack[#stack+1] = { } 188 else 189 error("Datatype error, bad token %q" % label) 190 end 191 end 192 pos = i + 1 193 end 194 depth = depth + (byte == 40 and 1 or 0) 195 elseif byte == 41 then 196 depth = depth - 1 197 if depth <= 0 then 198 if type(stack[#stack-1]) ~= "function" then 199 error("Datatype error, argument list follows non-function") 200 end 201 stack[#stack] = compile_datatype(code:sub(pos, i-1)) 202 pos = i + 1 203 end 204 end 205 end 206 207 return stack 208 end 209 210 function verify_datatype(dt, value) 211 if dt and #dt > 0 then 212 if not cdt_cache[dt] then 213 local c = compile_datatype(dt) 214 if c and type(c[1]) == "function" then 215 cdt_cache[dt] = c 216 else 217 error("Datatype error, not a function expression") 218 end 219 end 220 if cdt_cache[dt] then 221 return cdt_cache[dt][1](value, unpack(cdt_cache[dt][2])) 222 end 223 end 224 return true 225 end 226 155 227 156 228 -- Node pseudo abstract class … … 1357 1429 function AbstractValue.validate(self, value) 1358 1430 if self.datatype and value then 1359 local args = { } 1360 local dt, ar = self.datatype:match("^(%w+)%(([^%(%)]+)%)") 1361 1362 if dt and ar then 1363 local a 1364 for a in ar:gmatch("[^%s,]+") do 1365 args[#args+1] = a 1366 end 1367 else 1368 dt = self.datatype 1369 end 1370 1371 if dt and datatypes[dt] then 1372 if type(value) == "table" then 1373 local v 1374 for _, v in ipairs(value) do 1375 if v and #v > 0 and not datatypes[dt](v, unpack(args)) then 1376 return nil 1377 end 1378 end 1379 else 1380 if not datatypes[dt](value, unpack(args)) then 1431 if type(value) == "table" then 1432 local v 1433 for _, v in ipairs(value) do 1434 if v and #v > 0 and not verify_datatype(self.datatype, v) then 1435 error('F') 1381 1436 return nil 1382 1437 end 1438 end 1439 else 1440 if not verify_datatype(self.datatype, value) then 1441 error('F') 1442 return nil 1383 1443 end 1384 1444 end -
luci/trunk/libs/web/luasrc/cbi/datatypes.lua
r8155 r8156 18 18 local math = require "math" 19 19 local util = require "luci.util" 20 local tonumber, type = tonumber, type20 local tonumber, type, unpack, select = tonumber, type, unpack, select 21 21 22 22 23 23 module "luci.cbi.datatypes" 24 24 25 26 _M['or'] = function(v, ...) 27 local i 28 for i = 1, select('#', ...), 2 do 29 local f = select(i, ...) 30 local a = select(i+1, ...) 31 if type(f) ~= "function" then 32 print("COMP", f, v) 33 if f == v then 34 return true 35 end 36 i = i - 1 37 elseif f(v, unpack(a)) then 38 return true 39 end 40 end 41 return false 42 end 43 44 _M['and'] = function(v, ...) 45 local i 46 for i = 1, select('#', ...), 2 do 47 local f = select(i, ...) 48 local a = select(i+1, ...) 49 if type(f) ~= "function" then 50 if f ~= v then 51 return false 52 end 53 i = i - 1 54 elseif not f(v, unpack(a)) then 55 return false 56 end 57 end 58 return true 59 end 60 61 function neg(v, ...) 62 return _M['or'](v:gsub("^%s*!%s*", ""), ...) 63 end 64 65 function list(v, subvalidator, subargs) 66 if type(subvalidator) ~= "function" then 67 return false 68 end 69 local token 70 for token in v:gmatch("%S+") do 71 if not subvalidator(token, unpack(subargs)) then 72 return false 73 end 74 end 75 return true 76 end 25 77 26 78 function bool(val) … … 255 307 return false 256 308 end 257 258 function neg(val, what)259 if what and type(_M[what]) == "function" then260 return _M[what](val:gsub("^%s*!%s*", ""))261 end262 263 return false264 end265 266 function list(val, what, ...)267 if type(val) == "string" and what and type(_M[what]) == "function" then268 for val in val:gmatch("%S+") do269 if not _M[what](val, ...) then270 return false271 end272 end273 274 return true275 end276 277 return false278 end
