cbi.js 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339
  1. /*
  2. LuCI - Lua Configuration Interface
  3. Copyright 2008 Steven Barth <steven@midlink.org>
  4. Copyright 2008-2012 Jo-Philipp Wich <jow@openwrt.org>
  5. Licensed under the Apache License, Version 2.0 (the "License");
  6. you may not use this file except in compliance with the License.
  7. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. */
  10. var cbi_d = [];
  11. var cbi_t = [];
  12. var cbi_c = [];
  13. var cbi_validators = {
  14. 'integer': function()
  15. {
  16. return (this.match(/^-?[0-9]+$/) != null);
  17. },
  18. 'uinteger': function()
  19. {
  20. return (cbi_validators.integer.apply(this) && (this >= 0));
  21. },
  22. 'float': function()
  23. {
  24. return !isNaN(parseFloat(this));
  25. },
  26. 'ufloat': function()
  27. {
  28. return (cbi_validators['float'].apply(this) && (this >= 0));
  29. },
  30. 'ipaddr': function()
  31. {
  32. return cbi_validators.ip4addr.apply(this) ||
  33. cbi_validators.ip6addr.apply(this);
  34. },
  35. 'ip4addr': function()
  36. {
  37. if (this.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(\/(\S+))?$/))
  38. {
  39. return (RegExp.$1 >= 0) && (RegExp.$1 <= 255) &&
  40. (RegExp.$2 >= 0) && (RegExp.$2 <= 255) &&
  41. (RegExp.$3 >= 0) && (RegExp.$3 <= 255) &&
  42. (RegExp.$4 >= 0) && (RegExp.$4 <= 255) &&
  43. ((RegExp.$6.indexOf('.') < 0)
  44. ? ((RegExp.$6 >= 0) && (RegExp.$6 <= 32))
  45. : (cbi_validators.ip4addr.apply(RegExp.$6)))
  46. ;
  47. }
  48. return false;
  49. },
  50. 'ip6addr': function()
  51. {
  52. if( this.match(/^([a-fA-F0-9:.]+)(\/(\d+))?$/) )
  53. {
  54. if( !RegExp.$2 || ((RegExp.$3 >= 0) && (RegExp.$3 <= 128)) )
  55. {
  56. var addr = RegExp.$1;
  57. if( addr == '::' )
  58. {
  59. return true;
  60. }
  61. if( addr.indexOf('.') > 0 )
  62. {
  63. var off = addr.lastIndexOf(':');
  64. if( !(off && cbi_validators.ip4addr.apply(addr.substr(off+1))) )
  65. return false;
  66. addr = addr.substr(0, off) + ':0:0';
  67. }
  68. if( addr.indexOf('::') >= 0 )
  69. {
  70. var colons = 0;
  71. var fill = '0';
  72. for( var i = 1; i < (addr.length-1); i++ )
  73. if( addr.charAt(i) == ':' )
  74. colons++;
  75. if( colons > 7 )
  76. return false;
  77. for( var i = 0; i < (7 - colons); i++ )
  78. fill += ':0';
  79. if (addr.match(/^(.*?)::(.*?)$/))
  80. addr = (RegExp.$1 ? RegExp.$1 + ':' : '') + fill +
  81. (RegExp.$2 ? ':' + RegExp.$2 : '');
  82. }
  83. return (addr.match(/^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$/) != null);
  84. }
  85. }
  86. return false;
  87. },
  88. 'port': function()
  89. {
  90. return cbi_validators.integer.apply(this) &&
  91. (this >= 0) && (this <= 65535);
  92. },
  93. 'portrange': function()
  94. {
  95. if (this.match(/^(\d+)-(\d+)$/))
  96. {
  97. var p1 = RegExp.$1;
  98. var p2 = RegExp.$2;
  99. return cbi_validators.port.apply(p1) &&
  100. cbi_validators.port.apply(p2) &&
  101. (parseInt(p1) <= parseInt(p2))
  102. ;
  103. }
  104. else
  105. {
  106. return cbi_validators.port.apply(this);
  107. }
  108. },
  109. 'macaddr': function()
  110. {
  111. return (this.match(/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/) != null);
  112. },
  113. 'host': function()
  114. {
  115. return cbi_validators.hostname.apply(this) ||
  116. cbi_validators.ipaddr.apply(this);
  117. },
  118. 'hostname': function()
  119. {
  120. if (this.length <= 253)
  121. return (this.match(/^[a-zA-Z0-9]+$/) != null ||
  122. (this.match(/^[a-zA-Z0-9_][a-zA-Z0-9_\-.]*[a-zA-Z0-9]$/) &&
  123. this.match(/[^0-9.]/)));
  124. return false;
  125. },
  126. 'network': function()
  127. {
  128. return cbi_validators.uciname.apply(this) ||
  129. cbi_validators.host.apply(this);
  130. },
  131. 'wpakey': function()
  132. {
  133. var v = this;
  134. if( v.length == 64 )
  135. return (v.match(/^[a-fA-F0-9]{64}$/) != null);
  136. else
  137. return (v.length >= 8) && (v.length <= 63);
  138. },
  139. 'wepkey': function()
  140. {
  141. var v = this;
  142. if ( v.substr(0,2) == 's:' )
  143. v = v.substr(2);
  144. if( (v.length == 10) || (v.length == 26) )
  145. return (v.match(/^[a-fA-F0-9]{10,26}$/) != null);
  146. else
  147. return (v.length == 5) || (v.length == 13);
  148. },
  149. 'uciname': function()
  150. {
  151. return (this.match(/^[a-zA-Z0-9_]+$/) != null);
  152. },
  153. 'range': function(min, max)
  154. {
  155. var val = parseFloat(this);
  156. if (!isNaN(min) && !isNaN(max) && !isNaN(val))
  157. return ((val >= min) && (val <= max));
  158. return false;
  159. },
  160. 'min': function(min)
  161. {
  162. var val = parseFloat(this);
  163. if (!isNaN(min) && !isNaN(val))
  164. return (val >= min);
  165. return false;
  166. },
  167. 'max': function(max)
  168. {
  169. var val = parseFloat(this);
  170. if (!isNaN(max) && !isNaN(val))
  171. return (val <= max);
  172. return false;
  173. },
  174. 'rangelength': function(min, max)
  175. {
  176. var val = '' + this;
  177. if (!isNaN(min) && !isNaN(max))
  178. return ((val.length >= min) && (val.length <= max));
  179. return false;
  180. },
  181. 'minlength': function(min)
  182. {
  183. var val = '' + this;
  184. if (!isNaN(min))
  185. return (val.length >= min);
  186. return false;
  187. },
  188. 'maxlength': function(max)
  189. {
  190. var val = '' + this;
  191. if (!isNaN(max))
  192. return (val.length <= max);
  193. return false;
  194. },
  195. 'or': function()
  196. {
  197. for (var i = 0; i < arguments.length; i += 2)
  198. {
  199. if (typeof arguments[i] != 'function')
  200. {
  201. if (arguments[i] == this)
  202. return true;
  203. i--;
  204. }
  205. else if (arguments[i].apply(this, arguments[i+1]))
  206. {
  207. return true;
  208. }
  209. }
  210. return false;
  211. },
  212. 'and': function()
  213. {
  214. for (var i = 0; i < arguments.length; i += 2)
  215. {
  216. if (typeof arguments[i] != 'function')
  217. {
  218. if (arguments[i] != this)
  219. return false;
  220. i--;
  221. }
  222. else if (!arguments[i].apply(this, arguments[i+1]))
  223. {
  224. return false;
  225. }
  226. }
  227. return true;
  228. },
  229. 'neg': function()
  230. {
  231. return cbi_validators.or.apply(
  232. this.replace(/^[ \t]*![ \t]*/, ''), arguments);
  233. },
  234. 'list': function(subvalidator, subargs)
  235. {
  236. if (typeof subvalidator != 'function')
  237. return false;
  238. var tokens = this.match(/[^ \t]+/g);
  239. for (var i = 0; i < tokens.length; i++)
  240. if (!subvalidator.apply(tokens[i], subargs))
  241. return false;
  242. return true;
  243. },
  244. 'phonedigit': function()
  245. {
  246. return (this.match(/^[0-9\*#!\.]+$/) != null);
  247. }
  248. };
  249. function cbi_d_add(field, dep, next) {
  250. var obj = document.getElementById(field);
  251. if (obj) {
  252. var entry
  253. for (var i=0; i<cbi_d.length; i++) {
  254. if (cbi_d[i].id == field) {
  255. entry = cbi_d[i];
  256. break;
  257. }
  258. }
  259. if (!entry) {
  260. entry = {
  261. "node": obj,
  262. "id": field,
  263. "parent": obj.parentNode.id,
  264. "next": next,
  265. "deps": []
  266. };
  267. cbi_d.unshift(entry);
  268. }
  269. entry.deps.push(dep)
  270. }
  271. }
  272. function cbi_d_checkvalue(target, ref) {
  273. var t = document.getElementById(target);
  274. var value;
  275. if (!t) {
  276. var tl = document.getElementsByName(target);
  277. if( tl.length > 0 && (tl[0].type == 'radio' || tl[0].type == 'checkbox'))
  278. for( var i = 0; i < tl.length; i++ )
  279. if( tl[i].checked ) {
  280. value = tl[i].value;
  281. break;
  282. }
  283. value = value ? value : "";
  284. } else if (!t.value) {
  285. value = "";
  286. } else {
  287. value = t.value;
  288. if (t.type == "checkbox") {
  289. value = t.checked ? value : "";
  290. }
  291. }
  292. return (value == ref)
  293. }
  294. function cbi_d_check(deps) {
  295. var reverse;
  296. var def = false;
  297. for (var i=0; i<deps.length; i++) {
  298. var istat = true;
  299. reverse = false;
  300. for (var j in deps[i]) {
  301. if (j == "!reverse") {
  302. reverse = true;
  303. } else if (j == "!default") {
  304. def = true;
  305. istat = false;
  306. } else {
  307. istat = (istat && cbi_d_checkvalue(j, deps[i][j]))
  308. }
  309. }
  310. if (istat) {
  311. return !reverse;
  312. }
  313. }
  314. return def;
  315. }
  316. function cbi_d_update() {
  317. var state = false;
  318. for (var i=0; i<cbi_d.length; i++) {
  319. var entry = cbi_d[i];
  320. var next = document.getElementById(entry.next)
  321. var node = document.getElementById(entry.id)
  322. var parent = document.getElementById(entry.parent)
  323. if (node && node.parentNode && !cbi_d_check(entry.deps)) {
  324. node.parentNode.removeChild(node);
  325. state = true;
  326. if( entry.parent )
  327. cbi_c[entry.parent]--;
  328. } else if ((!node || !node.parentNode) && cbi_d_check(entry.deps)) {
  329. if (!next) {
  330. parent.appendChild(entry.node);
  331. } else {
  332. next.parentNode.insertBefore(entry.node, next);
  333. }
  334. state = true;
  335. if( entry.parent )
  336. cbi_c[entry.parent]++;
  337. }
  338. }
  339. if (entry && entry.parent) {
  340. if (!cbi_t_update())
  341. cbi_tag_last(parent);
  342. }
  343. if (state) {
  344. cbi_d_update();
  345. }
  346. }
  347. function cbi_bind(obj, type, callback, mode) {
  348. if (!obj.addEventListener) {
  349. obj.attachEvent('on' + type,
  350. function(){
  351. var e = window.event;
  352. if (!e.target && e.srcElement)
  353. e.target = e.srcElement;
  354. return !!callback(e);
  355. }
  356. );
  357. } else {
  358. obj.addEventListener(type, callback, !!mode);
  359. }
  360. return obj;
  361. }
  362. function cbi_combobox(id, values, def, man) {
  363. var selid = "cbi.combobox." + id;
  364. if (document.getElementById(selid)) {
  365. return
  366. }
  367. var obj = document.getElementById(id)
  368. var sel = document.createElement("select");
  369. sel.id = selid;
  370. sel.className = obj.className.replace(/cbi-input-text/, 'cbi-input-select');
  371. if (obj.nextSibling) {
  372. obj.parentNode.insertBefore(sel, obj.nextSibling);
  373. } else {
  374. obj.parentNode.appendChild(sel);
  375. }
  376. var dt = obj.getAttribute('cbi_datatype');
  377. var op = obj.getAttribute('cbi_optional');
  378. if (dt)
  379. cbi_validate_field(sel, op == 'true', dt);
  380. if (!values[obj.value]) {
  381. if (obj.value == "") {
  382. var optdef = document.createElement("option");
  383. optdef.value = "";
  384. optdef.appendChild(document.createTextNode(def));
  385. sel.appendChild(optdef);
  386. } else {
  387. var opt = document.createElement("option");
  388. opt.value = obj.value;
  389. opt.selected = "selected";
  390. opt.appendChild(document.createTextNode(obj.value));
  391. sel.appendChild(opt);
  392. }
  393. }
  394. for (var i in values) {
  395. var opt = document.createElement("option");
  396. opt.value = i;
  397. if (obj.value == i) {
  398. opt.selected = "selected";
  399. }
  400. opt.appendChild(document.createTextNode(values[i]));
  401. sel.appendChild(opt);
  402. }
  403. var optman = document.createElement("option");
  404. optman.value = "";
  405. optman.appendChild(document.createTextNode(man));
  406. sel.appendChild(optman);
  407. obj.style.display = "none";
  408. cbi_bind(sel, "change", function() {
  409. if (sel.selectedIndex == sel.options.length - 1) {
  410. obj.style.display = "inline";
  411. sel.parentNode.removeChild(sel);
  412. obj.focus();
  413. } else {
  414. obj.value = sel.options[sel.selectedIndex].value;
  415. }
  416. try {
  417. cbi_d_update();
  418. } catch (e) {
  419. //Do nothing
  420. }
  421. })
  422. // Retrigger validation in select
  423. sel.focus();
  424. sel.blur();
  425. }
  426. function cbi_combobox_init(id, values, def, man) {
  427. var obj = document.getElementById(id);
  428. cbi_bind(obj, "blur", function() {
  429. cbi_combobox(id, values, def, man)
  430. });
  431. cbi_combobox(id, values, def, man);
  432. }
  433. function cbi_filebrowser(id, url, defpath) {
  434. var field = document.getElementById(id);
  435. var browser = window.open(
  436. url + ( field.value || defpath || '' ) + '?field=' + id,
  437. "luci_filebrowser", "width=300,height=400,left=100,top=200,scrollbars=yes"
  438. );
  439. browser.focus();
  440. }
  441. function cbi_browser_init(id, respath, url, defpath)
  442. {
  443. function cbi_browser_btnclick(e) {
  444. cbi_filebrowser(id, url, defpath);
  445. return false;
  446. }
  447. var field = document.getElementById(id);
  448. var btn = document.createElement('img');
  449. btn.className = 'cbi-image-button';
  450. btn.src = respath + '/cbi/folder.gif';
  451. field.parentNode.insertBefore(btn, field.nextSibling);
  452. cbi_bind(btn, 'click', cbi_browser_btnclick);
  453. }
  454. function cbi_dynlist_init(name, respath, datatype, optional, choices)
  455. {
  456. var input0 = document.getElementsByName(name)[0];
  457. var prefix = input0.name;
  458. var parent = input0.parentNode;
  459. var holder = input0.placeholder;
  460. var values;
  461. function cbi_dynlist_redraw(focus, add, del)
  462. {
  463. values = [ ];
  464. while (parent.firstChild)
  465. {
  466. var n = parent.firstChild;
  467. var i = parseInt(n.index);
  468. if (i != del)
  469. {
  470. if (n.nodeName.toLowerCase() == 'input')
  471. values.push(n.value || '');
  472. else if (n.nodeName.toLowerCase() == 'select')
  473. values[values.length-1] = n.options[n.selectedIndex].value;
  474. }
  475. parent.removeChild(n);
  476. }
  477. if (add >= 0)
  478. {
  479. focus = add+1;
  480. values.splice(focus, 0, '');
  481. }
  482. else if (values.length == 0)
  483. {
  484. focus = 0;
  485. values.push('');
  486. }
  487. for (var i = 0; i < values.length; i++)
  488. {
  489. var t = document.createElement('input');
  490. t.id = prefix + '.' + (i+1);
  491. t.name = prefix;
  492. t.value = values[i];
  493. t.type = 'text';
  494. t.index = i;
  495. t.className = 'cbi-input-text';
  496. if (i == 0 && holder)
  497. {
  498. t.placeholder = holder;
  499. }
  500. var b = document.createElement('img');
  501. b.src = respath + ((i+1) < values.length ? '/cbi/remove.gif' : '/cbi/add.gif');
  502. b.className = 'cbi-image-button';
  503. parent.appendChild(t);
  504. parent.appendChild(b);
  505. parent.appendChild(document.createElement('br'));
  506. if (datatype)
  507. {
  508. cbi_validate_field(t.id, ((i+1) == values.length) || optional, datatype);
  509. }
  510. if (choices)
  511. {
  512. cbi_combobox_init(t.id, choices[0], '', choices[1]);
  513. t.nextSibling.index = i;
  514. cbi_bind(t.nextSibling, 'keydown', cbi_dynlist_keydown);
  515. cbi_bind(t.nextSibling, 'keypress', cbi_dynlist_keypress);
  516. if (i == focus || -i == focus)
  517. t.nextSibling.focus();
  518. }
  519. else
  520. {
  521. cbi_bind(t, 'keydown', cbi_dynlist_keydown);
  522. cbi_bind(t, 'keypress', cbi_dynlist_keypress);
  523. if (i == focus)
  524. {
  525. t.focus();
  526. }
  527. else if (-i == focus)
  528. {
  529. t.focus();
  530. /* force cursor to end */
  531. var v = t.value;
  532. t.value = ' '
  533. t.value = v;
  534. }
  535. }
  536. cbi_bind(b, 'click', cbi_dynlist_btnclick);
  537. }
  538. }
  539. function cbi_dynlist_keypress(ev)
  540. {
  541. ev = ev ? ev : window.event;
  542. var se = ev.target ? ev.target : ev.srcElement;
  543. if (se.nodeType == 3)
  544. se = se.parentNode;
  545. switch (ev.keyCode)
  546. {
  547. /* backspace, delete */
  548. case 8:
  549. case 46:
  550. if (se.value.length == 0)
  551. {
  552. if (ev.preventDefault)
  553. ev.preventDefault();
  554. return false;
  555. }
  556. return true;
  557. /* enter, arrow up, arrow down */
  558. case 13:
  559. case 38:
  560. case 40:
  561. if (ev.preventDefault)
  562. ev.preventDefault();
  563. return false;
  564. }
  565. return true;
  566. }
  567. function cbi_dynlist_keydown(ev)
  568. {
  569. ev = ev ? ev : window.event;
  570. var se = ev.target ? ev.target : ev.srcElement;
  571. if (se.nodeType == 3)
  572. se = se.parentNode;
  573. var prev = se.previousSibling;
  574. while (prev && prev.name != name)
  575. prev = prev.previousSibling;
  576. var next = se.nextSibling;
  577. while (next && next.name != name)
  578. next = next.nextSibling;
  579. /* advance one further in combobox case */
  580. if (next && next.nextSibling.name == name)
  581. next = next.nextSibling;
  582. switch (ev.keyCode)
  583. {
  584. /* backspace, delete */
  585. case 8:
  586. case 46:
  587. var del = (se.nodeName.toLowerCase() == 'select')
  588. ? true : (se.value.length == 0);
  589. if (del)
  590. {
  591. if (ev.preventDefault)
  592. ev.preventDefault();
  593. var focus = se.index;
  594. if (ev.keyCode == 8)
  595. focus = -focus+1;
  596. cbi_dynlist_redraw(focus, -1, se.index);
  597. return false;
  598. }
  599. break;
  600. /* enter */
  601. case 13:
  602. cbi_dynlist_redraw(-1, se.index, -1);
  603. break;
  604. /* arrow up */
  605. case 38:
  606. if (prev)
  607. prev.focus();
  608. break;
  609. /* arrow down */
  610. case 40:
  611. if (next)
  612. next.focus();
  613. break;
  614. }
  615. return true;
  616. }
  617. function cbi_dynlist_btnclick(ev)
  618. {
  619. ev = ev ? ev : window.event;
  620. var se = ev.target ? ev.target : ev.srcElement;
  621. if (se.src.indexOf('remove') > -1)
  622. {
  623. se.previousSibling.value = '';
  624. cbi_dynlist_keydown({
  625. target: se.previousSibling,
  626. keyCode: 8
  627. });
  628. }
  629. else
  630. {
  631. cbi_dynlist_keydown({
  632. target: se.previousSibling,
  633. keyCode: 13
  634. });
  635. }
  636. return false;
  637. }
  638. cbi_dynlist_redraw(NaN, -1, -1);
  639. }
  640. //Hijacks the CBI form to send via XHR (requires Prototype)
  641. function cbi_hijack_forms(layer, win, fail, load) {
  642. var forms = layer.getElementsByTagName('form');
  643. for (var i=0; i<forms.length; i++) {
  644. $(forms[i]).observe('submit', function(event) {
  645. // Prevent the form from also submitting the regular way
  646. event.stop();
  647. // Submit via XHR
  648. event.element().request({
  649. onSuccess: win,
  650. onFailure: fail
  651. });
  652. if (load) {
  653. load();
  654. }
  655. });
  656. }
  657. }
  658. function cbi_t_add(section, tab) {
  659. var t = document.getElementById('tab.' + section + '.' + tab);
  660. var c = document.getElementById('container.' + section + '.' + tab);
  661. if( t && c ) {
  662. cbi_t[section] = (cbi_t[section] || [ ]);
  663. cbi_t[section][tab] = { 'tab': t, 'container': c, 'cid': c.id };
  664. }
  665. }
  666. function cbi_t_switch(section, tab) {
  667. if( cbi_t[section] && cbi_t[section][tab] ) {
  668. var o = cbi_t[section][tab];
  669. var h = document.getElementById('tab.' + section);
  670. for( var tid in cbi_t[section] ) {
  671. var o2 = cbi_t[section][tid];
  672. if( o.tab.id != o2.tab.id ) {
  673. o2.tab.className = o2.tab.className.replace(/(^| )cbi-tab( |$)/, " cbi-tab-disabled ");
  674. o2.container.style.display = 'none';
  675. }
  676. else {
  677. if(h) h.value = tab;
  678. o2.tab.className = o2.tab.className.replace(/(^| )cbi-tab-disabled( |$)/, " cbi-tab ");
  679. o2.container.style.display = 'block';
  680. }
  681. }
  682. }
  683. return false
  684. }
  685. function cbi_t_update() {
  686. var hl_tabs = [ ];
  687. var updated = false;
  688. for( var sid in cbi_t )
  689. for( var tid in cbi_t[sid] )
  690. {
  691. if( cbi_c[cbi_t[sid][tid].cid] == 0 ) {
  692. cbi_t[sid][tid].tab.style.display = 'none';
  693. }
  694. else if( cbi_t[sid][tid].tab && cbi_t[sid][tid].tab.style.display == 'none' ) {
  695. cbi_t[sid][tid].tab.style.display = '';
  696. var t = cbi_t[sid][tid].tab;
  697. t.className += ' cbi-tab-highlighted';
  698. hl_tabs.push(t);
  699. }
  700. cbi_tag_last(cbi_t[sid][tid].container);
  701. updated = true;
  702. }
  703. if( hl_tabs.length > 0 )
  704. window.setTimeout(function() {
  705. for( var i = 0; i < hl_tabs.length; i++ )
  706. hl_tabs[i].className = hl_tabs[i].className.replace(/ cbi-tab-highlighted/g, '');
  707. }, 750);
  708. return updated;
  709. }
  710. function cbi_validate_form(form, errmsg)
  711. {
  712. /* if triggered by a section removal or addition, don't validate */
  713. if( form.cbi_state == 'add-section' || form.cbi_state == 'del-section' )
  714. return true;
  715. if( form.cbi_validators )
  716. {
  717. for( var i = 0; i < form.cbi_validators.length; i++ )
  718. {
  719. var validator = form.cbi_validators[i];
  720. if( !validator() && errmsg )
  721. {
  722. alert(errmsg);
  723. return false;
  724. }
  725. }
  726. }
  727. return true;
  728. }
  729. function cbi_validate_reset(form)
  730. {
  731. window.setTimeout(
  732. function() { cbi_validate_form(form, null) }, 100
  733. );
  734. return true;
  735. }
  736. function cbi_validate_compile(code)
  737. {
  738. var pos = 0;
  739. var esc = false;
  740. var depth = 0;
  741. var stack = [ ];
  742. code += ',';
  743. for (var i = 0; i < code.length; i++)
  744. {
  745. if (esc)
  746. {
  747. esc = false;
  748. continue;
  749. }
  750. switch (code.charCodeAt(i))
  751. {
  752. case 92:
  753. esc = true;
  754. break;
  755. case 40:
  756. case 44:
  757. if (depth <= 0)
  758. {
  759. if (pos < i)
  760. {
  761. var label = code.substring(pos, i);
  762. label = label.replace(/\\(.)/g, '$1');
  763. label = label.replace(/^[ \t]+/g, '');
  764. label = label.replace(/[ \t]+$/g, '');
  765. if (label && !isNaN(label))
  766. {
  767. stack.push(parseFloat(label));
  768. }
  769. else if (label.match(/^(['"]).*\1$/))
  770. {
  771. stack.push(label.replace(/^(['"])(.*)\1$/, '$2'));
  772. }
  773. else if (typeof cbi_validators[label] == 'function')
  774. {
  775. stack.push(cbi_validators[label]);
  776. stack.push(null);
  777. }
  778. else
  779. {
  780. throw "Syntax error, unhandled token '"+label+"'";
  781. }
  782. }
  783. pos = i+1;
  784. }
  785. depth += (code.charCodeAt(i) == 40);
  786. break;
  787. case 41:
  788. if (--depth <= 0)
  789. {
  790. if (typeof stack[stack.length-2] != 'function')
  791. throw "Syntax error, argument list follows non-function";
  792. stack[stack.length-1] =
  793. arguments.callee(code.substring(pos, i));
  794. pos = i+1;
  795. }
  796. break;
  797. }
  798. }
  799. return stack;
  800. }
  801. function cbi_validate_field(cbid, optional, type)
  802. {
  803. var field = (typeof cbid == "string") ? document.getElementById(cbid) : cbid;
  804. var vstack; try { vstack = cbi_validate_compile(type); } catch(e) { };
  805. if (field && vstack && typeof vstack[0] == "function")
  806. {
  807. var validator = function()
  808. {
  809. // is not detached
  810. if( field.form )
  811. {
  812. field.className = field.className.replace(/ cbi-input-invalid/g, '');
  813. // validate value
  814. var value = (field.options && field.options.selectedIndex > -1)
  815. ? field.options[field.options.selectedIndex].value : field.value;
  816. if (!(((value.length == 0) && optional) || vstack[0].apply(value, vstack[1])))
  817. {
  818. // invalid
  819. field.className += ' cbi-input-invalid';
  820. return false;
  821. }
  822. }
  823. return true;
  824. };
  825. if( ! field.form.cbi_validators )
  826. field.form.cbi_validators = [ ];
  827. field.form.cbi_validators.push(validator);
  828. cbi_bind(field, "blur", validator);
  829. cbi_bind(field, "keyup", validator);
  830. if (field.nodeName == 'SELECT')
  831. {
  832. cbi_bind(field, "change", validator);
  833. cbi_bind(field, "click", validator);
  834. }
  835. field.setAttribute("cbi_validate", validator);
  836. field.setAttribute("cbi_datatype", type);
  837. field.setAttribute("cbi_optional", (!!optional).toString());
  838. validator();
  839. var fcbox = document.getElementById('cbi.combobox.' + field.id);
  840. if (fcbox)
  841. cbi_validate_field(fcbox, optional, type);
  842. }
  843. }
  844. function cbi_row_swap(elem, up, store)
  845. {
  846. var tr = elem.parentNode;
  847. while (tr && tr.nodeName.toLowerCase() != 'tr')
  848. tr = tr.parentNode;
  849. if (!tr)
  850. return false;
  851. var table = tr.parentNode;
  852. while (table && table.nodeName.toLowerCase() != 'table')
  853. table = table.parentNode;
  854. if (!table)
  855. return false;
  856. var s = up ? 3 : 2;
  857. var e = up ? table.rows.length : table.rows.length - 1;
  858. for (var idx = s; idx < e; idx++)
  859. {
  860. if (table.rows[idx] == tr)
  861. {
  862. if (up)
  863. tr.parentNode.insertBefore(table.rows[idx], table.rows[idx-1]);
  864. else
  865. tr.parentNode.insertBefore(table.rows[idx+1], table.rows[idx]);
  866. break;
  867. }
  868. }
  869. var ids = [ ];
  870. for (idx = 2; idx < table.rows.length; idx++)
  871. {
  872. table.rows[idx].className = table.rows[idx].className.replace(
  873. /cbi-rowstyle-[12]/, 'cbi-rowstyle-' + (1 + (idx % 2))
  874. );
  875. if (table.rows[idx].id && table.rows[idx].id.match(/-([^\-]+)$/) )
  876. ids.push(RegExp.$1);
  877. }
  878. var input = document.getElementById(store);
  879. if (input)
  880. input.value = ids.join(' ');
  881. return false;
  882. }
  883. function cbi_tag_last(container)
  884. {
  885. var last;
  886. for (var i = 0; i < container.childNodes.length; i++)
  887. {
  888. var c = container.childNodes[i];
  889. if (c.nodeType == 1 && c.nodeName.toLowerCase() == 'div')
  890. {
  891. c.className = c.className.replace(/ cbi-value-last$/, '');
  892. last = c;
  893. }
  894. }
  895. if (last)
  896. {
  897. last.className += ' cbi-value-last';
  898. }
  899. }
  900. String.prototype.serialize = function()
  901. {
  902. var o = this;
  903. switch(typeof(o))
  904. {
  905. case 'object':
  906. // null
  907. if( o == null )
  908. {
  909. return 'null';
  910. }
  911. // array
  912. else if( o.length )
  913. {
  914. var i, s = '';
  915. for( var i = 0; i < o.length; i++ )
  916. s += (s ? ', ' : '') + String.serialize(o[i]);
  917. return '[ ' + s + ' ]';
  918. }
  919. // object
  920. else
  921. {
  922. var k, s = '';
  923. for( k in o )
  924. s += (s ? ', ' : '') + k + ': ' + String.serialize(o[k]);
  925. return '{ ' + s + ' }';
  926. }
  927. break;
  928. case 'string':
  929. // complex string
  930. if( o.match(/[^a-zA-Z0-9_,.: -]/) )
  931. return 'decodeURIComponent("' + encodeURIComponent(o) + '")';
  932. // simple string
  933. else
  934. return '"' + o + '"';
  935. break;
  936. default:
  937. return o.toString();
  938. }
  939. }
  940. String.prototype.format = function()
  941. {
  942. if (!RegExp)
  943. return;
  944. var html_esc = [/&/g, '&#38;', /"/g, '&#34;', /'/g, '&#39;', /</g, '&#60;', />/g, '&#62;'];
  945. var quot_esc = [/"/g, '&#34;', /'/g, '&#39;'];
  946. function esc(s, r) {
  947. for( var i = 0; i < r.length; i += 2 )
  948. s = s.replace(r[i], r[i+1]);
  949. return s;
  950. }
  951. var str = this;
  952. var out = '';
  953. var re = /^(([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X|q|h|j|t|m))/;
  954. var a = b = [], numSubstitutions = 0, numMatches = 0;
  955. while( a = re.exec(str) )
  956. {
  957. var m = a[1];
  958. var leftpart = a[2], pPad = a[3], pJustify = a[4], pMinLength = a[5];
  959. var pPrecision = a[6], pType = a[7];
  960. numMatches++;
  961. if (pType == '%')
  962. {
  963. subst = '%';
  964. }
  965. else
  966. {
  967. if (numSubstitutions < arguments.length)
  968. {
  969. var param = arguments[numSubstitutions++];
  970. var pad = '';
  971. if (pPad && pPad.substr(0,1) == "'")
  972. pad = leftpart.substr(1,1);
  973. else if (pPad)
  974. pad = pPad;
  975. var justifyRight = true;
  976. if (pJustify && pJustify === "-")
  977. justifyRight = false;
  978. var minLength = -1;
  979. if (pMinLength)
  980. minLength = parseInt(pMinLength);
  981. var precision = -1;
  982. if (pPrecision && pType == 'f')
  983. precision = parseInt(pPrecision.substring(1));
  984. var subst = param;
  985. switch(pType)
  986. {
  987. case 'b':
  988. subst = (parseInt(param) || 0).toString(2);
  989. break;
  990. case 'c':
  991. subst = String.fromCharCode(parseInt(param) || 0);
  992. break;
  993. case 'd':
  994. subst = (parseInt(param) || 0);
  995. break;
  996. case 'u':
  997. subst = Math.abs(parseInt(param) || 0);
  998. break;
  999. case 'f':
  1000. subst = (precision > -1)
  1001. ? ((parseFloat(param) || 0.0)).toFixed(precision)
  1002. : (parseFloat(param) || 0.0);
  1003. break;
  1004. case 'o':
  1005. subst = (parseInt(param) || 0).toString(8);
  1006. break;
  1007. case 's':
  1008. subst = param;
  1009. break;
  1010. case 'x':
  1011. subst = ('' + (parseInt(param) || 0).toString(16)).toLowerCase();
  1012. break;
  1013. case 'X':
  1014. subst = ('' + (parseInt(param) || 0).toString(16)).toUpperCase();
  1015. break;
  1016. case 'h':
  1017. subst = esc(param, html_esc);
  1018. break;
  1019. case 'q':
  1020. subst = esc(param, quot_esc);
  1021. break;
  1022. case 'j':
  1023. subst = String.serialize(param);
  1024. break;
  1025. case 't':
  1026. var td = 0;
  1027. var th = 0;
  1028. var tm = 0;
  1029. var ts = (param || 0);
  1030. if (ts > 60) {
  1031. tm = Math.floor(ts / 60);
  1032. ts = (ts % 60);
  1033. }
  1034. if (tm > 60) {
  1035. th = Math.floor(tm / 60);
  1036. tm = (tm % 60);
  1037. }
  1038. if (th > 24) {
  1039. td = Math.floor(th / 24);
  1040. th = (th % 24);
  1041. }
  1042. subst = (td > 0)
  1043. ? String.format('%dd %dh %dm %ds', td, th, tm, ts)
  1044. : String.format('%dh %dm %ds', th, tm, ts);
  1045. break;
  1046. case 'm':
  1047. var mf = pMinLength ? parseInt(pMinLength) : 1000;
  1048. var pr = pPrecision ? Math.floor(10*parseFloat('0'+pPrecision)) : 2;
  1049. var i = 0;
  1050. var val = parseFloat(param || 0);
  1051. var units = [ '', 'K', 'M', 'G', 'T', 'P', 'E' ];
  1052. for (i = 0; (i < units.length) && (val > mf); i++)
  1053. val /= mf;
  1054. subst = val.toFixed(pr) + ' ' + units[i];
  1055. break;
  1056. }
  1057. }
  1058. }
  1059. out += leftpart + subst;
  1060. str = str.substr(m.length);
  1061. }
  1062. return out + str;
  1063. }
  1064. String.prototype.nobr = function()
  1065. {
  1066. return this.replace(/[\s\n]+/g, '&#160;');
  1067. }
  1068. String.serialize = function()
  1069. {
  1070. var a = [ ];
  1071. for (var i = 1; i < arguments.length; i++)
  1072. a.push(arguments[i]);
  1073. return ''.serialize.apply(arguments[0], a);
  1074. }
  1075. String.format = function()
  1076. {
  1077. var a = [ ];
  1078. for (var i = 1; i < arguments.length; i++)
  1079. a.push(arguments[i]);
  1080. return ''.format.apply(arguments[0], a);
  1081. }
  1082. String.nobr = function()
  1083. {
  1084. var a = [ ];
  1085. for (var i = 1; i < arguments.length; i++)
  1086. a.push(arguments[i]);
  1087. return ''.nobr.apply(arguments[0], a);
  1088. }