X-Git-Url: https://git.archive.openwrt.org/?a=blobdiff_plain;f=libs%2Fweb%2Fhtdocs%2Fluci-static%2Fresources%2Fcbi.js;h=05e15d27a06f6e06dbbe7fdab4ea314cdcc1030c;hb=3812f2908731c6ff5e59aec4e0c899dbff35c469;hp=4af6e58d9b5a234268db37bf62f82bd6e1313aff;hpb=ede4aca4b95c9e664e4830fd43c54b627b122538;p=project%2Fluci.git diff --git a/libs/web/htdocs/luci-static/resources/cbi.js b/libs/web/htdocs/luci-static/resources/cbi.js index 4af6e58d9..05e15d27a 100644 --- a/libs/web/htdocs/luci-static/resources/cbi.js +++ b/libs/web/htdocs/luci-static/resources/cbi.js @@ -2,7 +2,7 @@ LuCI - Lua Configuration Interface Copyright 2008 Steven Barth - Copyright 2008-2010 Jo-Philipp Wich + Copyright 2008-2011 Jo-Philipp Wich Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -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); @@ -34,13 +44,15 @@ var cbi_validators = { 'ip4addr': function(v) { - if( v.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)(\/(\d+))?$/) ) + if (v.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(\/(\S+))?$/)) { return (RegExp.$1 >= 0) && (RegExp.$1 <= 255) && (RegExp.$2 >= 0) && (RegExp.$2 <= 255) && (RegExp.$3 >= 0) && (RegExp.$3 <= 255) && (RegExp.$4 >= 0) && (RegExp.$4 <= 255) && - (!RegExp.$5 || ((RegExp.$6 >= 0) && (RegExp.$6 <= 32))) + ((RegExp.$6.indexOf('.') < 0) + ? ((RegExp.$6 >= 0) && (RegExp.$6 <= 32)) + : (cbi_validators.ip4addr(RegExp.$6))) ; } @@ -70,34 +82,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); } } @@ -139,7 +144,17 @@ var cbi_validators = { 'hostname': function(v) { - return (v.match(/^[a-zA-Z_][a-zA-Z0-9_\-.]*$/) != null); + if (v.length <= 253) + return (v.match(/^[a-zA-Z]+$/) != null || + (v.match(/^[a-zA-Z0-9][a-zA-Z0-9\-.]*[a-zA-Z0-9]$/) && + v.match(/[^0-9.]/))); + + return false; + }, + + 'network': function(v) + { + return cbi_validators.uciname(v) || cbi_validators.host(v); }, 'wpakey': function(v) @@ -161,6 +176,70 @@ 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; + }, + + 'min': function(v, args) + { + var min = parseInt(args[0]); + var val = parseInt(v); + + if (!isNaN(min) && !isNaN(val)) + return (val >= min); + + return false; + }, + + 'max': function(v, args) + { + var max = parseInt(args[0]); + var val = parseInt(v); + + if (!isNaN(max) && !isNaN(val)) + return (val <= max); + + return false; + }, + + 'neg': function(v, args) + { + if (args[0] && typeof cbi_validators[args[0]] == "function") + return cbi_validators[args[0]](v.replace(/^\s*!\s*/, '')); + + return false; + }, + + 'list': function(v, args) + { + var cb = cbi_validators[args[0] || 'string']; + if (typeof cb == "function") + { + var cbargs = args.slice(1); + var values = v.match(/[^\s]+/g); + + for (var i = 0; i < values.length; i++) + if (!cb(values[i], cbargs)) + return false; + + return true; + } + + return false; + } }; @@ -264,8 +343,9 @@ function cbi_d_update() { } } - if (entry.parent) { - cbi_t_update(); + if (entry && entry.parent) { + if (!cbi_t_update()) + cbi_tag_last(parent); } if (state) { @@ -274,22 +354,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 +379,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 +435,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 +463,260 @@ function cbi_filebrowser(id, url, defpath) { browser.focus(); } +function cbi_browser_init(id, respath, url, defpath) +{ + function cbi_browser_btnclick(e) { + cbi_filebrowser(id, url, defpath); + return false; + } + + var field = document.getElementById(id); + + var btn = document.createElement('img'); + btn.className = 'cbi-image-button'; + btn.src = respath + '/cbi/folder.gif'; + field.parentNode.insertBefore(btn, field.nextSibling); + + cbi_bind(btn, 'click', cbi_browser_btnclick); +} + +function cbi_dynlist_init(name, respath, datatype, optional, choices) +{ + var input0 = document.getElementsByName(name)[0]; + var prefix = input0.name; + var parent = input0.parentNode; + var holder = input0.placeholder; + + var values; + + function cbi_dynlist_redraw(focus, add, del) + { + values = [ ]; + + while (parent.firstChild) + { + var n = parent.firstChild; + var i = parseInt(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(focus, 0, ''); + } + else if (values.length == 0) + { + focus = 0; + values.push(''); + } + + for (var i = 0; i < values.length; i++) + { + var t = document.createElement('input'); + t.id = prefix + '.' + (i+1); + t.name = prefix; + t.value = values[i]; + t.type = 'text'; + t.index = i; + t.className = 'cbi-input-text'; + + if (i == 0 && holder) + { + t.placeholder = holder; + } + + var b = document.createElement('img'); + b.src = respath + ((i+1) < values.length ? '/cbi/remove.gif' : '/cbi/add.gif'); + b.className = 'cbi-image-button'; + + parent.appendChild(t); + parent.appendChild(b); + parent.appendChild(document.createElement('br')); + + if (datatype) + { + cbi_validate_field(t.id, ((i+1) == values.length) || optional, datatype); + } + + if (choices) + { + cbi_combobox_init(t.id, choices[0], '', choices[1]); + t.nextSibling.index = i; + + cbi_bind(t.nextSibling, 'keydown', cbi_dynlist_keydown); + cbi_bind(t.nextSibling, 'keypress', cbi_dynlist_keypress); + + if (i == focus || -i == focus) + t.nextSibling.focus(); + } + else + { + cbi_bind(t, 'keydown', cbi_dynlist_keydown); + cbi_bind(t, 'keypress', cbi_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; + } + } + + cbi_bind(b, 'click', cbi_dynlist_btnclick); + } + } + + 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; + + /* advance one further in combobox case */ + if (next && next.nextSibling.name == name) + 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; + + cbi_dynlist_redraw(focus, -1, se.index); + + return false; + } + + break; + + /* enter */ + case 13: + cbi_dynlist_redraw(-1, se.index, -1); + 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; + } + + cbi_dynlist_redraw(-1, -1, -1); +} + //Hijacks the CBI form to send via XHR (requires Prototype) function cbi_hijack_forms(layer, win, fail, load) { var forms = layer.getElementsByTagName('form'); @@ -437,8 +770,12 @@ function cbi_t_switch(section, tab) { } function cbi_t_update() { + var hl_tabs = [ ]; + var updated = false; + for( var sid in cbi_t ) for( var tid in cbi_t[sid] ) + { if( cbi_c[cbi_t[sid][tid].cid] == 0 ) { cbi_t[sid][tid].tab.style.display = 'none'; } @@ -446,14 +783,30 @@ 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); } + + cbi_tag_last(cbi_t[sid][tid].container); + updated = true; + } + + 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); + + return updated; } 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 +834,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 +855,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 +873,308 @@ 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; +} + +function cbi_tag_last(container) +{ + var last; + + for (var i = 0; i < container.childNodes.length; i++) + { + var c = container.childNodes[i]; + if (c.nodeType == 1 && c.nodeName.toLowerCase() == 'div') + { + c.className = c.className.replace(/ cbi-value-last$/, ''); + last = c; + } + } + + if (last) + { + last.className += ' cbi-value-last'; } } +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|m))/; + 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; + + case 'm': + var mf = pMinLength ? parseInt(pMinLength) : 1000; + var pr = pPrecision ? Math.floor(10*parseFloat('0'+pPrecision)) : 2; + + var i = 0; + var val = parseFloat(param || 0); + var units = [ '', 'K', 'M', 'G', 'T', 'P', 'E' ]; + + for (i = 0; (i < units.length) && (val > mf); i++) + val /= mf; + + subst = val.toFixed(pr) + ' ' + units[i]; + break; + } + } + } + + out += leftpart + subst; + str = str.substr(m.length); + } + + return out + str; + }