X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fluci.git;a=blobdiff_plain;f=libs%2Fweb%2Fhtdocs%2Fluci-static%2Fresources%2Fcbi.js;h=d12d050a8ae8f084daafed7a13be6376bad91c1d;hp=4af6e58d9b5a234268db37bf62f82bd6e1313aff;hb=408a58f6f6b0d419bc6b81eb93f32c701e19118a;hpb=ede4aca4b95c9e664e4830fd43c54b627b122538 diff --git a/libs/web/htdocs/luci-static/resources/cbi.js b/libs/web/htdocs/luci-static/resources/cbi.js index 4af6e58d9..d12d050a8 100644 --- a/libs/web/htdocs/luci-static/resources/cbi.js +++ b/libs/web/htdocs/luci-static/resources/cbi.js @@ -27,6 +27,16 @@ var cbi_validators = { return (cbi_validators.integer(v) && (v >= 0)); }, + 'float': function(v) + { + return !isNaN(parseFloat(v)); + }, + + 'ufloat': function(v) + { + return (cbi_validators['float'](v) && (v >= 0)); + }, + 'ipaddr': function(v) { return cbi_validators.ip4addr(v) || cbi_validators.ip6addr(v); @@ -70,34 +80,27 @@ var cbi_validators = { addr = addr.substr(0, off) + ':0:0'; } - if( addr.indexOf('::') < 0 ) + if( addr.indexOf('::') >= 0 ) { - return (addr.match(/^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$/) != null); - } + var colons = 0; + var fill = '0'; - var fields = 0; + for( var i = 1; i < (addr.length-1); i++ ) + if( addr.charAt(i) == ':' ) + colons++; - for( var i = 0, last = 0, comp = false; i <= addr.length; i++ ) - { - if( (addr.charAt(i) == ':') || (i == addr.length) ) - { - if( (i == last) && !comp ) - { - comp = true; - } - else - { - var f = addr.substring(last, i); - if( !(f && f.match(/^[a-fA-F0-9]{1,4}$/)) ) - return false; - } - - fields++; - last = i + 1; - } + if( colons > 7 ) + return false; + + for( var i = 0; i < (7 - colons); i++ ) + fill += ':0'; + + if (addr.match(/^(.*?)::(.*?)$/)) + addr = (RegExp.$1 ? RegExp.$1 + ':' : '') + fill + + (RegExp.$2 ? ':' + RegExp.$2 : ''); } - return (fields == 8); + return (addr.match(/^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$/) != null); } } @@ -138,8 +141,8 @@ var cbi_validators = { }, 'hostname': function(v) - { - return (v.match(/^[a-zA-Z_][a-zA-Z0-9_\-.]*$/) != null); + { if ( v.length <= 24 ) + return (v.match(/^[a-zA-Z0-9][a-zA-Z0-9\-.]*[a-zA-Z0-9]$/) != null); }, 'wpakey': function(v) @@ -161,6 +164,22 @@ var cbi_validators = { return (v.length == 5) || (v.length == 13); }, + 'uciname': function(v) + { + return (v.match(/^[a-zA-Z0-9_]+$/) != null); + }, + + 'range': function(v, args) + { + var min = parseInt(args[0]); + var max = parseInt(args[1]); + var val = parseInt(v); + + if (!isNaN(min) && !isNaN(max) && !isNaN(val)) + return ((val >= min) && (val <= max)); + + return false; + } }; @@ -264,7 +283,7 @@ function cbi_d_update() { } } - if (entry.parent) { + if (entry && entry.parent) { cbi_t_update(); } @@ -274,22 +293,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; } @@ -302,17 +318,21 @@ function cbi_combobox(id, values, def, man) { var obj = document.getElementById(id) var sel = document.createElement("select"); - sel.id = selid; - sel.className = 'cbi-input-select'; - if (obj.className && obj.className.match(/cbi-input-invalid/)) { - sel.className += ' cbi-input-invalid'; - } + sel.id = selid; + sel.className = 'cbi-input-select'; + if (obj.nextSibling) { obj.parentNode.insertBefore(sel, obj.nextSibling); } else { obj.parentNode.appendChild(sel); } + var dt = obj.getAttribute('cbi_datatype'); + var op = obj.getAttribute('cbi_optional'); + + if (dt) + cbi_validate_field(sel, op == 'true', dt); + if (!values[obj.value]) { if (obj.value == "") { var optdef = document.createElement("option"); @@ -354,8 +374,6 @@ function cbi_combobox(id, values, def, man) { obj.focus(); } else { obj.value = sel.options[sel.selectedIndex].value; - sel.className = (!obj.validate || obj.validate()) - ? 'cbi-input-select' : 'cbi-input-select cbi-input-invalid'; } try { @@ -384,6 +402,203 @@ function cbi_filebrowser(id, url, defpath) { browser.focus(); } +function cbi_dynlist_init(name, respath) +{ + function cbi_dynlist_renumber(e) + { + /* in a perfect world, we could just getElementsByName() - but not if + * MSIE is involved... */ + var inputs = [ ]; // = document.getElementsByName(name); + for (var i = 0; i < e.parentNode.childNodes.length; i++) + if (e.parentNode.childNodes[i].name == name) + inputs.push(e.parentNode.childNodes[i]); + + for (var i = 0; i < inputs.length; i++) + { + inputs[i].id = name + '.' + (i + 1); + inputs[i].nextSibling.src = respath + ( + (i+1) < inputs.length ? '/cbi/remove.gif' : '/cbi/add.gif' + ); + } + + 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.nextSibling); + se.parentNode.removeChild(se.nextSibling); + se.parentNode.removeChild(se); + + cbi_dynlist_renumber(jump); + + if (ev.preventDefault) + ev.preventDefault(); + + /* IE Quirk, needs double focus somehow */ + jump.focus(); + + return false; + } + + break; + + /* enter */ + case 13: + var n = document.createElement('input'); + n.name = se.name; + n.type = se.type; + + var b = document.createElement('img'); + + cbi_bind(n, 'keydown', cbi_dynlist_keydown); + cbi_bind(n, 'keypress', cbi_dynlist_keypress); + cbi_bind(b, 'click', cbi_dynlist_btnclick); + + if (next) + { + se.parentNode.insertBefore(n, next); + se.parentNode.insertBefore(b, next); + se.parentNode.insertBefore(document.createElement('br'), next); + } + else + { + se.parentNode.appendChild(n); + se.parentNode.appendChild(b); + 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; + } + + function cbi_dynlist_btnclick(ev) + { + ev = ev ? ev : window.event; + + var se = ev.target ? ev.target : ev.srcElement; + + if (se.src.indexOf('remove') > -1) + { + se.previousSibling.value = ''; + + cbi_dynlist_keydown({ + target: se.previousSibling, + keyCode: 8 + }); + } + else + { + cbi_dynlist_keydown({ + target: se.previousSibling, + keyCode: 13 + }); + } + + return false; + } + + var inputs = document.getElementsByName(name); + for( var i = 0; i < inputs.length; i++ ) + { + var btn = document.createElement('img'); + btn.src = respath + ( + (i+1) < inputs.length ? '/cbi/remove.gif' : '/cbi/add.gif' + ); + + inputs[i].parentNode.insertBefore(btn, inputs[i].nextSibling); + + cbi_bind(inputs[i], 'keydown', cbi_dynlist_keydown); + cbi_bind(inputs[i], 'keypress', cbi_dynlist_keypress); + cbi_bind(btn, 'click', cbi_dynlist_btnclick); + } +} + //Hijacks the CBI form to send via XHR (requires Prototype) function cbi_hijack_forms(layer, win, fail, load) { var forms = layer.getElementsByTagName('form'); @@ -437,6 +652,8 @@ function cbi_t_switch(section, tab) { } function cbi_t_update() { + var hl_tabs = [ ]; + for( var sid in cbi_t ) for( var tid in cbi_t[sid] ) if( cbi_c[cbi_t[sid][tid].cid] == 0 ) { @@ -446,14 +663,24 @@ function cbi_t_update() { cbi_t[sid][tid].tab.style.display = ''; var t = cbi_t[sid][tid].tab; - window.setTimeout(function() { t.className = t.className.replace(/ cbi-tab-highlighted/g, '') }, 750); - cbi_t[sid][tid].tab.className += ' cbi-tab-highlighted'; + t.className += ' cbi-tab-highlighted'; + hl_tabs.push(t); } + + if( hl_tabs.length > 0 ) + window.setTimeout(function() { + for( var i = 0; i < hl_tabs.length; i++ ) + hl_tabs[i].className = hl_tabs[i].className.replace(/ cbi-tab-highlighted/g, ''); + }, 750); } function cbi_validate_form(form, errmsg) { + /* if triggered by a section removal or addition, don't validate */ + if( form.cbi_state == 'add-section' || form.cbi_state == 'del-section' ) + return true; + if( form.cbi_validators ) { for( var i = 0; i < form.cbi_validators.length; i++ ) @@ -481,12 +708,20 @@ 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 vargs; + + if( type.match(/^(\w+)\(([^\(\)]+)\)/) ) + { + type = RegExp.$1; + vargs = RegExp.$2.split(/\s*,\s*/); + } + var vldcb = cbi_validators[type]; if( field && vldcb ) { - var validator = function(reset) + var validator = function() { // is not detached if( field.form ) @@ -494,8 +729,10 @@ function cbi_validate_field(cbid, optional, type) field.className = field.className.replace(/ cbi-input-invalid/g, ''); // validate value - var value = (field.options) ? field.options[field.options.selectedIndex].value : field.value; - if( !(((value.length == 0) && optional) || vldcb(value)) ) + var value = (field.options && field.options.selectedIndex > -1) + ? field.options[field.options.selectedIndex].value : field.value; + + if( !(((value.length == 0) && optional) || vldcb(value, vargs)) ) { // invalid field.className += ' cbi-input-invalid'; @@ -510,9 +747,274 @@ 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); + + if (field.nodeName == 'SELECT') + { + cbi_bind(field, "change", validator); + cbi_bind(field, "click", validator); + } + + field.setAttribute("cbi_validate", validator); + field.setAttribute("cbi_datatype", type); + field.setAttribute("cbi_optional", (!!optional).toString()); validator(); + + var fcbox = document.getElementById('cbi.combobox.' + field.id); + if (fcbox) + cbi_validate_field(fcbox, optional, type); } } +function cbi_row_swap(elem, up, store) +{ + var tr = elem.parentNode; + while (tr && tr.nodeName.toLowerCase() != 'tr') + tr = tr.parentNode; + + if (!tr) + return false; + + var table = tr.parentNode; + while (table && table.nodeName.toLowerCase() != 'table') + table = table.parentNode; + + if (!table) + return false; + + var s = up ? 3 : 2; + var e = up ? table.rows.length : table.rows.length - 1; + + for (var idx = s; idx < e; idx++) + { + if (table.rows[idx] == tr) + { + if (up) + tr.parentNode.insertBefore(table.rows[idx], table.rows[idx-1]); + else + tr.parentNode.insertBefore(table.rows[idx+1], table.rows[idx]); + + break; + } + } + + var ids = [ ]; + for (idx = 2; idx < table.rows.length; idx++) + { + table.rows[idx].className = table.rows[idx].className.replace( + /cbi-rowstyle-[12]/, 'cbi-rowstyle-' + (1 + (idx % 2)) + ); + + if (table.rows[idx].id && table.rows[idx].id.match(/-([^\-]+)$/) ) + ids.push(RegExp.$1); + } + + var input = document.getElementById(store); + if (input) + input.value = ids.join(' '); + + return false; +} + +if( ! String.serialize ) + String.serialize = function(o) + { + switch(typeof(o)) + { + case 'object': + // null + if( o == null ) + { + return 'null'; + } + + // array + else if( o.length ) + { + var i, s = ''; + + for( var i = 0; i < o.length; i++ ) + s += (s ? ', ' : '') + String.serialize(o[i]); + + return '[ ' + s + ' ]'; + } + + // object + else + { + var k, s = ''; + + for( k in o ) + s += (s ? ', ' : '') + k + ': ' + String.serialize(o[k]); + + return '{ ' + s + ' }'; + } + + break; + + case 'string': + // complex string + if( o.match(/[^a-zA-Z0-9_,.: -]/) ) + return 'decodeURIComponent("' + encodeURIComponent(o) + '")'; + + // simple string + else + return '"' + o + '"'; + + break; + + default: + return o.toString(); + } + } + + +if( ! String.format ) + String.format = function() + { + if (!arguments || arguments.length < 1 || !RegExp) + return; + + var html_esc = [/&/g, '&', /"/g, '"', /'/g, ''', //g, '>']; + var quot_esc = [/"/g, '"', /'/g, ''']; + + function esc(s, r) { + for( var i = 0; i < r.length; i += 2 ) + s = s.replace(r[i], r[i+1]); + return s; + } + + var str = arguments[0]; + var out = ''; + var re = /^(([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X|q|h|j|t))/; + var a = b = [], numSubstitutions = 0, numMatches = 0; + + while( a = re.exec(str) ) + { + var m = a[1]; + var leftpart = a[2], pPad = a[3], pJustify = a[4], pMinLength = a[5]; + var pPrecision = a[6], pType = a[7]; + + numMatches++; + + if (pType == '%') + { + subst = '%'; + } + else + { + if (numSubstitutions++ < arguments.length) + { + var param = arguments[numSubstitutions]; + + var pad = ''; + if (pPad && pPad.substr(0,1) == "'") + pad = leftpart.substr(1,1); + else if (pPad) + pad = pPad; + + var justifyRight = true; + if (pJustify && pJustify === "-") + justifyRight = false; + + var minLength = -1; + if (pMinLength) + minLength = parseInt(pMinLength); + + var precision = -1; + if (pPrecision && pType == 'f') + precision = parseInt(pPrecision.substring(1)); + + var subst = param; + + switch(pType) + { + case 'b': + subst = (parseInt(param) || 0).toString(2); + break; + + case 'c': + subst = String.fromCharCode(parseInt(param) || 0); + break; + + case 'd': + subst = (parseInt(param) || 0); + break; + + case 'u': + subst = Math.abs(parseInt(param) || 0); + break; + + case 'f': + subst = (precision > -1) + ? ((parseFloat(param) || 0.0)).toFixed(precision) + : (parseFloat(param) || 0.0); + break; + + case 'o': + subst = (parseInt(param) || 0).toString(8); + break; + + case 's': + subst = param; + break; + + case 'x': + subst = ('' + (parseInt(param) || 0).toString(16)).toLowerCase(); + break; + + case 'X': + subst = ('' + (parseInt(param) || 0).toString(16)).toUpperCase(); + break; + + case 'h': + subst = esc(param, html_esc); + break; + + case 'q': + subst = esc(param, quot_esc); + break; + + case 'j': + subst = String.serialize(param); + break; + + case 't': + var td = 0; + var th = 0; + var tm = 0; + var ts = (param || 0); + + if (ts > 60) { + tm = Math.floor(ts / 60); + ts = (ts % 60); + } + + if (tm > 60) { + th = Math.floor(tm / 60); + tm = (tm % 60); + } + + if (th > 24) { + td = Math.floor(th / 24); + th = (th % 24); + } + + subst = (td > 0) + ? String.format('%dd %dh %dm %ds', td, th, tm, ts) + : String.format('%dh %dm %ds', th, tm, ts); + + break; + } + } + } + + out += leftpart + subst; + str = str.substr(m.length); + } + + return out + str; + }