/* Public names: cleanEx doreDownload clrSel(?) setSel(?) srchArray * getElemIdx setInputElem(?) closeRefWin fixDivPos(?) * rptUrl(deprecated!) * Cross-file names: _rmTabs _getElemByName hasVal chkSelTree chkSelTrees * srchSelTree _setDisabled clrSelTreeForm getInputElem _addCond0 setRefWin * _setSrchResUrl confirmStr escFldCond chkCsvCols srchFixDivs _extHandler * _selTrees _selTree */ var _browser, _ver, _errMsg, _refWin, _bgColorDisabled; var _selTrees = new Array, _selTree; chkBrowser(); // (96/6/12) Blanks should include spaces, tabs and newlines. But for // Safari 2, tabs are not blanks. function _rmTabs(sel) { var opts = sel.options, s; var re = /[\t ]+$/; // With Safari, if no option is set as // // when the form is reset, the selected option is still the last selected // one. if(sel.selectedIndex <= 0 && opts.length > 0 && !opts[0].defaultSelected) opts[0].defaultSelected = true; for(i = 0; i < sel.length; i++) { s = opts[i].text; // Truncate trailing tabs. if(s.indexOf("\t") >= 0) opts[i].text = s.replace(re, ""); } } // For management and report pages, we set "action" to the page name and remove // Web arguments. function _setFormAct(f) { var pair = location.href.split('?'); f.action = pair[0]; } /* Select.prototype.tree = Select.prototype.section = Select.prototype.begPath = Select.prototype.endPath = null; */ function clrSel(sel) { var opts = sel.options; for(var i = sel.length; --i > 0; opts[i] = null); } function srchArray(vals, val) { var i, vals, val; for(i = 0; i < vals.length && vals[i] != val; i++); return i < vals.length ? i : -1; } //get the element index function getElemIdx(elem) { var i, elems = elem.form.elements; for(i = 0; elem != elems[i]; i++); return i; } function _getElemByName(name, f) { var elem, f, pair = name.split('.'); if(pair.length == 1) elem = f.elements[name]; else if(f = document.forms[pair[0]]) elem = f.elements[pair[1]]; return elem; } function hasVal(tbl, val) { var i; for(i = 0; i < tbl.length; i++) if(tbl[i] == val) return true; tbl[i] = val; return false; } function chkSelTree(sel) { var parts = sel.name.split(';'), i, pair, args = new Array, s, elems; if(parts.length > 1) { // (96/6/14) Safari does not works well with "sel.name = parts[0]". sel.setAttribute("name", s = parts[0]); // (94/7/14, 99/4/22) Note: for IE 6 and 7, the setting below is required, // but not for IE8. "elems.length" won't change. if(s.length > 0) { elems = sel.form.elements; if(!elems[s]) elems[s] = sel; } parts = parts[1].split(' '); for(i = 0; i < parts.length; i++) { pair = parts[i].split('='); args[pair[0]] = pair[1]; } } else { if(sel.tree) { if(_check) alert("Illegal way to use static pulldowns"); args['tree'] = sel.tree, args['depth'] = sel.depth; args['begPath'] = sel.begPath, args['endPath'] = sel.begPath; } else if(sel.section) { if(_check) alert("Illegal way to use dynamic multi-level pulldowns"); args['section'] = sel.section, args['depth'] = sel.depth; args['cond'] = sel.cond, args['floating'] = sel.floating; } } if(args['tree']) _selTrees[_selTrees.length] = new SelTree(sel, args['tree'], args['depth'], args['begPath'], args['endPath']); else if(args['section']) { if(self._check && args['cond']) alert('For dynamic pulldowns, attribue "cond" is deprecated. Use "colList" instead.'); _selTrees[_selTrees.length] = new SelTreeD(sel, args['section'], args['depth'], args['cond'] ? eval(args['cond']) : args['colList'], args['floating']); } else _errMsg = null; if(_errMsg) alert(_errMsg); } function chkSelTrees(f) { var i, elems = f.elements; for(i = 0; i < elems.length; i++) if(elems[i].type == "select-one") { _rmTabs(elems[i]); // ... For Safari only. chkSelTree(elems[i]); } } function srchSelTree(sel) { var i, j, node, elems; for(i = 0; i < _selTrees.length; i++) { node = _selTrees[i]; if(node.tree) { elems = node.elems; for(j = 0; j < elems.length; j++) if(elems[j] == sel) { _selTree = node; return j + 1; } } else { if(node.form == sel.form && (j = getElemIdx(sel) - node.idx) >= 0 && j < node.depth) { _selTree = node; return j + 1; } } } return -1; } function _setDisabled(elem, yes) { // (96/8/6) For IE, as TEXT or TEXTAREA elements are disabled, the // font color becomes vague and may not be changed. if(elem.type == "text" || elem.type == "textarea") elem.readOnly = yes; else elem.disabled = yes; if(_bgColorDisabled) elem.style.backgroundColor = yes ? _bgColorDisabled : _bgColorEnabled; } // Clean all select trees in form 'f'. function clrSelTreeForm(f) { var i, j, k, elems, p; for(i = 0; i < _selTrees.length; i++) { p = _selTrees[i]; if(p.tree) { elems = p.elems; // Search for the first SELECT element in form "f". for(j = 0; j < elems.length && elems[j].form != f; j++); if(j == elems.length) continue; // Don't clean the first, but clean the rests. They may be in other // forms. k = 0; while(j < elems.length) { if(k++ > 0) clrSel(elems[j]); _setDisabled(elems[j], false); p.path[++j] = null; } } else if(p.form == f && (p.colList || p.cond)) p.clrSels(); } } function getInputElem(elem) { var val = '', opt, i; switch(elem.type) { case 'text': case 'textarea': case 'hidden': case 'password': case 'file': val = elem.value; break; case 'select-multiple': case 'select-one': if(elem.selectedIndex >= 0) { opt = elem.options[elem.selectedIndex]; val = opt.value; if(val.length == 0) val = opt.text; } break; case 'checkbox': if(elem.checked) val = elem.value; break; case 'radio': elem = elem.form.elements[elem.name]; // Just a single radio button! if(elem.length == null) elem = new Array(elem); default: // array of radios if(elem.length != null) for(i = 0; i < elem.length; i++) if(elem[i].checked) { val = elem[i].value; break; } } return val; } function setInputElem(elem, val) { var i; if(elem == null || val == null) return; switch(elem.type) { case "hidden": elem.value = val; break; case "checkbox": elem.checked = val == elem.value; break; case "select-one": elem.selectedIndex = 0; setSel(elem, val); break; case "radio": // one button only elem = new Array(elem); default: if(elem.type != null) { alert('The type of input element "' + elem.name + '" is wrong!'); return; } for(i = 0; i < elem.length; i++) if(elem[i].value == val) { elem[i].checked = true; break; } } } function clean0(f, flag) { var i, elems = f.elements, elem, name, re = /^_cond[0-9]$/; if(self.cleanEx) cleanEx(f); f.reset(); // Clean file types and reset hidden types. for(i = 0; i < elems.length; i++) { elem = elems[i]; if(elem.type == "button") continue; if((name = elem.name) != "") if(name.charAt(0) != '_') _setDisabled(elem, flag == true); else if(name.match(re)) continue; switch(elem.type) { case "text": case "textarea": case "password": elem.value = ""; break; case "hidden": if(_browser == 2) // For Firefox if(typeof elem._val != "undefined") elem.value = elem._val; else if(name.length > 0 && name.charAt(0) != "_") elem.value = ""; break; case "radio": case "checkbox": elem.checked = false; break; case "select-one": elem.selectedIndex = 0; break; case "select-multiple": elem.selectedIndex = -1; break; } } clrSelTreeForm(f); } function _addCond0(f, s) { var t, i, elems = f.elements, elem; for(i = 0; i < 9 && (elem = elems['_cond' + i]); i++) { t = getInputElem(elem); if(s.length == 0) s = t; else if(t.length > 0) s += '&(' + t + ')'; } return s; } // Check the browser and its version. function chkBrowser() { // "Your browser version is " + navigator.appName + " " + navigator.appVersion" + "\n"; if(navigator.appName == "Microsoft Internet Explorer") { var parts = navigator.appVersion.split(';'); _ver = parts[1].replace(/MSIE /, ""); if(_ver >= 5.0) _browser = 1; } else if(navigator.appName == "Netscape" && (_ver = navigator.appVersion.replace(/\(.*/, "")) >= 4.0) _browser = 2; if(!_browser) alert(_browserVerStr); } // Set the corresponding option for the value 'val'. function setSel(sel, val) { var i, opts = sel.options, opt; for(i = 0; i < sel.length; i++) { opt = opts[i]; if(opt.value == val || opt.value == "" && opt.text == val) { // Note: (100.11.28) For IE8, as a SELECT-ONE element is disabled and // its size is greater than 1, "opts[i].selected = true" doesn't work! if(sel.size > 1 && _browser === 1 && sel.disabled) sel.size = 1; opts[i].selected = true; return true; } } alert("Pulldown menu '" + sel.name + "': value '" + val + "' not matched"); return false; } function setRefWin(url, attr) { if(_refWin == null || _refWin.closed) { // width=500,height=300,scrollbars,resizable,dependent,alwaysRaised=1, // status=no,menubar=no,toolbar=no _refWin = self.open(url, "_refWin", attr); if(_refWin == null) { alert('Cannot open new window!'); return; } } else _refWin.location.href = url; // _refWin.moveTo(e.screenX, top.frames[0].innerHeight+e.screenY-e.pageY+5); // _refWin.focus(); // (95/11/9) This doesn't work! } // function closeRefWin() { if(_refWin != null && !_refWin.closed) _refWin.close(); } function _setSrchResUrl(f) { function toCsv() { var url, i, s; url = location.href; i = url.indexOf('?'); if(i > 0) url = url.substr(0, i); // (93.7.21,101.8.8) For IE 5~8, "https" is out of order for download. if(_browser == 1 && _ver < 9.0) url = url.replace(/^https/, 'http'); s = url.replace(new RegExp(_doreUrl === 'dore/' ? '[^/]+$' : '[^/]+/[^/]+$'), 'dore/') + (self._multi ? 'csvm.php' : 'csv.php'); return s; } var s; if(f._goto && (s = getInputElem(f._goto))) { if(s === '_csv') s = toCsv(); } else if(f._csv && getInputElem(f._csv)) s = toCsv(); // A management page invokes the function and wants to stay there. else if(self._toList == false) return; else s = _list; f.action = s; } function connAlert(lifetime, homePage) { function connAlert0() { var time = new Date(); // "t" is the time 5 minutes later. var t = time.getTime() + 300 * 1000; var s = '('+time.getHours()+":"+time.getMinutes()+":"+time.getSeconds()+")"+ _connWarnStr; alert(s); time = new Date(); if(time.getTime() > t && homePage) location.replace(homePage); } setTimeout(connAlert0, (lifetime - 300) * 1000); } function confirmStr(s) { return s == null || confirm('[ ' + s + " ] " + _sureStr); } // Insert '\' in front of '&', '|', '(' and ')'. // If 's' contains Chinses characters defined in Unicode but not in BIG5, // they are transferred to Unicode entities like "Ӓ" by 'toUniEnt' and // backslashes are instered in front of ampersons. function escFldCond(s) { if(self.toUniEnt) s = toUniEnt(s); return s.replace(/[&|()]/g, "\\$&"); } function chkCsvCols(f) { var elems = f.elements["_colList[]"]; if(elems == null) return; if(f._csv == null) { alert('Input element named "_csv" required'); return; } if(self._colList == null) { alert('$_colList not set'); return; } var i, elem, flds = _colList.split(","), boxes = new Array; for(i = 0; i < flds.length; i++) boxes[flds[i]] = 1; for(i = 0; i < elems.length; i++) { elem = elems[i]; if(elem.type != 'checkbox') { alert('Checkbox required for "_colList[]"'); return; } if(boxes[elem.value] == null) { alert('Field "' + elem.value + '" is not in $_colList, so it may not be selected'); return; } elem.checked = true; } } function fixDivPos(id, y, x) { var div = document.getElementById(id); div.style.top = document.body.scrollTop + y; div.style.left = document.body.scrollLeft + x; } function FixDiv(div, y, x) { this.div = div; this.y = y; this.x = x; } function srchFixDivs() { function fixDivs() { var i, obj, st; for(i = 0; i < _fixDivs.length; i++) { obj = _fixDivs[i]; st = obj.div.style; st.top = document.body.scrollTop + obj.y; st.left = document.body.scrollLeft + obj.x; } } var i, divs, div; if((divs = document.getElementsByTagName('div')) != null) { _fixDivs = new Array(); for(i = 0; i < divs.length; i++) if(divs[i].className == 'fixDiv') { div = divs[i]; _fixDivs[_fixDivs.length] = new FixDiv(div, div.offsetTop, div.offsetLeft); } if(_fixDivs.length > 0) if(_browser == 1) document.body.onscroll = fixDivs; else self.onscroll = fixDivs; } } // Add JavaScript "code" into the event "handler". If "append" is true, // "code" is appended; otherwise it's inserted from the beginning. function _extHandler(handler, code, append) { var s = handler.toString(); // IE: strip "function anonymous() {}". // Firefox: strip "function onchange(event) {}". s = s.substring(s.indexOf('{')+2, s.length-2); return new Function(append ? s + code : code + s); } function _addUrlArgs(sec) { return "_path=" + _path + "&_session=" + _session + "&_section=" + sec; } function doreDownload(fld, attr, recNo, f) { if(!f) f = document._f0; if(recNo == null) recNo = f._recNo.value; setRefWin(_doreUrl + "file.php?" + _addUrlArgs(f._section.value) + "&_resVer=" + f._resVer.value + "&_field=" + fld + "&_recNo=" + recNo, attr); } function toMngRpt(page, recNo) { var f = self._tblTree ? _tblTree.form : document._f0; if(recNo != null) f._recNo.value = recNo; // else if(f._recNo.value == '') { alert('Record number unknown'); return; } f._op.value = 'f'; f.action = page; f.submit(); } function rptUrl(page, recNo) { var f = self._tblTree ? _tblTree.form : document._f0; if(recNo == null && (recNo = f._recNo.value) == '') return false; return page + '?_session=' + f._session.value + '&_section=' + f._section.value + '&_resVer=' + f._resVer.value + '&_op=f&_recNo=' + recNo; } /* function urlByPost(url) { var s = '_session=' + _session; var parts, pair, n = url.indexOf('?'); if(n > 0) { s += '&' + url.substr(n+1); url = url.substr(0, n); } document.write("
"); parts = s.split('&'); for(i = 0; i < parts.length; i++) { pair = parts[i].split('='); document.write(""); } document.write("
"); document.forms[0].submit(); } */ /* Public names: getRelaElem, getElemIdxByName, today, getDateYMD, putDateYMD, * getDateTimeYMD, putDateTimeYMD, qryDateYMD, qryDateRngYMD, setDateFlag(?), * cpSel, uniEntToChar(?), anchorQry, httpReq, getXML, someFormVals(?), * formValList */ // get the n'th element from 'elem' function getRelaElem(elem, n) { var i; var elems = elem.form.elements; for(i = 0; elem != elems[i]; i++); return elems[i+n]; } function getElemIdxByName(name, f) { var i = 0, elems; if(f == null) f = document.forms[0]; elems = f.elements; for(i = 0; i < elems.length && elems[i].name != name; i++); return i < elems.length ? i : -1; } /* function getElemByName(name, off, f) { if(f == null) f = document.forms[0]; var i = 0, elems = f.elements; for(i = 0; i < elems.length && elems[i].name != name; i++); if(off == null) off = 0; return i < elems.length && (i += off) >= 0 && i < elems.length ? elems[i] : null; } */ function todayYMD(name, flag) { alert('Function "todayYMD" was canceled!'); } /* // Fill today into three text elements following the hidden element // with name equal to 'name'. // flag: null) westen year; 1) Chinese year function todayYMD(name, flag, f) { if(f == null) f = document.forms[0]; var date = new Date(); var i, n, elems = f.elements; i = getElemIdxByName(name, f) + 1; if((n = date.getYear()) < 2000) n += 1900; elems[i].value = flag == null ? n : n - 1911; elems[i+1].value = date.getMonth() + 1; elems[i+2].value = date.getDate(); return i; } */ // Fill today into the text or hidden element with name equal to 'name'. // flag: null) westen year; 1) Chinese year function today(elem, flag) { var date = new Date(); if((n = date.getYear()) < 2000) n += 1900; if(flag != null) n -= 1911; elem.value = n + '-' + (date.getMonth() + 1) + '-' + date.getDate(); } // There is an input element(usually hidden) immediately before the // year/month/day input elements for combining the three elements into a date // elements. 'name' is the element name of the hidden element. If the hidden // element is not at the default position, give the offset 'off' from the // hidden element to the year element. // flag: null) westen year; 1) Chinese year // true) correct input; null) no input; false) wrong input function getDateYMD(name, flag, off, f) { function chkDate(y, m, d, flag) { if(y === '' && m === '' && d === '') return null; if(isNaN(y) || isNaN(m) || m < 1 || m > 12 || isNaN(d) || d < 1 || d > 31) return false; if(flag != null) y = parseInt(y) + 1911; return y + '-' + m + '-' + d; } if(f == null) f = document.forms[0]; var idx, i, y, s, elems = f.elements; idx = getElemIdxByName(name, f); i = idx + (off == null ? 1 : off); s = chkDate(elems[i].value, elems[i+1].value, elems[i+2].value, flag); if(s) elems[idx].value = s, s = true; return s; } // flag: null) westen year; 1) Chinese year function putDateYMD(name, flag, off, f) { if(f == null) f = document.forms[0]; var i, elems = f.elements, parts; i = getElemIdxByName(name, f); parts = elems[i].value.split("-"); if(parts.length == 3 && parts[0] > 0) { i += off == null ? 1 : off; elems[i].value = flag == null ? parts[0] : parts[0] - 1911; elems[i+1].value = parts[1]; elems[i+2].value = parts[2]; } } function getDateTimeYMD(name, flag, f) { if(f == null) f = document.forms[0]; var elems = f.elements; var i, h, m, s = '', err = 0; if(getDateYMD(name, flag)) { i = getElemIdxByName(name, f); h = elems[i+4].value; m = elems[i+5].value; if(h != "") if(isNaN(h) || h < 0 || h > 23) err++; else { s += ' ' + h; if(m != "") if(isNaN(m) || m < 0 || m > 59) err++; else s += ':' + m; } else if(m != "") err++; } else err++; if(err == 0) elems[i].value += s; return err == 0 ? true : false; } function putDateTimeYMD(name, flag, f) { if(f == null) f = document.forms[0]; var elems = f.elements; var i = getElemIdxByName(name, f); var pair = elems[i].value.split(" "); var parts = pair[0].split("-"); elems[i+1].value = flag ? parts[0] - 1911 : parts[0]; elems[i+2].value = parts[1]; elems[i+3].value = parts[2]; parts = pair[1].split(":"); elems[i+4].value = parts[0]; elems[i+5].value = parts[1]; } // flag: null) westen year; 1) Chinese year // date string) success; "") no input; null) wrong date. function qryDateYMD(name, flag, off, f) { if(f == null) f = document.forms[0]; var elems = f.elements; var i, idx, s, m, d; idx = getElemIdxByName(name, f); i = idx + (off == null ? 1 : off); if((s = elems[i].value) != "") { if(flag != null) s = 1911 + parseInt(s); if((m = elems[i+1].value) != "") { s += '-' + m; if((d = elems[i+2].value) != "") s += '-' + d; } else if(elems[i+2].value != "") return null; } else if(elems[i+1].value != "" || elems[i+2].value != "") return null; elems[idx].value = s; return s; } // flag: null) westen year; 1) Chinese year // Six input elements without name following the hidden element // "") OK; error message) wrong function qryDateRngYMD(name, flag, f) { if(f == null) f = document.forms[0]; var mag = "", elem; var lb = qryDateYMD(name, flag); var ub = qryDateYMD(name, flag, 4); if(lb == null || up == null) msg = _dateErrStr; else if(lb == "" && ub != "") msg = _dateRngErrStr; else if(lb != "") { elem = f.elements[name]; elem.value = lb; if(ub != "") elem.value += '~' + ub; } return msg; } function setDateFlag(d) { var i, parts; i = d.search(/\/|-|\./); if(i >= 0) { parts = d.split(d.charAt(i)); flag = 3 - parts.length; } else flag = 2; return flag; } function cpSel(sel, dest) { opt = sel.options[sel.selectedIndex]; elem = isNaN(dest) ? sel.form.elements[dest] : getRelaElem(sel, dest); elem.value = opt.value != "" ? opt.value : opt.text; } // Map Unicode entity(e.g. 〹) in "s" to characters. function uniEntToChar(s) { var t = '', x = 0, n1, n2; while((n1 = s.indexOf("&#", x)) >= 0) { n2 = s.indexOf(";", n1); if(n2 < 0) break; t += s.substr(x,n1-x) + String.fromCharCode(s.substr(n1+2,n2-n1-2)); x = n2 + 1; } return x > 0 ? t + s.substr(x) : s; } // Escape the characters &, #, + and % in 's', also for those less than the // space. It's no need to encode big5 characters under any circumstance. // As to utf-8, the situations are more complex. For IE, characters greater // than TILL need encoding with "encodeURIComponent", while the method is GET. // This is not necessary as applying AJAX with the POST method via IE, but no // harm. For Firefox and Safari, such encoding is not required, no matter using // the GET or POST(with AJAX) method. function escURL(s) { if(s.constructor != String) return s.toString(); var i, j, b, c, n = 0, t = '', min; // Do not encode big5 characters. if(self.toUniEnt) s = toUniEnt(s), min = '\uFFFF'; else min = _browser == 2 ? '\uFFFF' : '~'; for(i = b = 0; i < s.length; i++) if((c = s.charAt(i)) == '&' || c == '#' || c == '+' || c == '%' || c < ' ' || c > min) n++; else if(n > 0) { j = i - n; if(b < j) t += s.substr(b, j - b); // "encodeURI" encodes non-alphanumeric characters based on utf-8, with // the exception of: "~ / @ # $ & * ( ) _ - + = : ; ' , . ? / %". // "encodeURIComponent" is similar to "encodeURI", but the exception // becomes "~ * ( ) _ - ' .". // Note: "escape" is deprecated. It encodes all characters that are // non-alphanumeric nor one of the characters "@ * _ - + . /". Chinese // characters are encoded as %uXXXX. t += encodeURIComponent(s.substr(j, n)); b = i; n = 0; } j = i - n; if(b < j) t += s.substr(b, j - b); if(n > 0) t += encodeURIComponent(s.substr(j, n)); return t; } // anc: id attribute of anchor // url: URL for query // fld: field name used to produce the where condition = function anchorQry(anc, url, fld) { var f = document._f0, s; anc = document.getElementById(anc); url += (url.indexOf('?') > 0 ? '&' : '?') + '_session=' + f._session.value + '&_op=?' + fld + ':' + escURL(escFldCond(anc.firstChild.nodeValue)); setRefWin(url); // location.href = url; } // If function "callBack" is given, "httpReq" works asynchronously. // Note: As "argList" is a string, it should be escaped well in advance. function httpReq(url, argList, callBack) { var req, yes, method, args, key, c; if(argList.constructor != String) { args = argList; argList = c = ''; for(key in args) { argList += c + key + '=' + escURL(args[key]); c = '&'; } } if(self.XMLHttpRequest) req = new XMLHttpRequest(); else if(self.ActiveXObject) // IE req = new ActiveXObject("Microsoft.XMLHTTP"); if(!req) { _errMsg = "AJAX request: no object"; return null; } // (95/9/25) IE can send no more than 2050. (98/10/9) The maximum of // Firefox became 1400. if(yes = url.length + argList.length + 1 > 1400) method = "POST"; else { method = "GET"; url += '?' + argList; argList = null; } req.open(method, url, callBack != null); if(callBack) req.onreadystatechange = function() { // complete if(req.readyState == 4) callBack(req); } if(yes) req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); req.send(argList); // if(!callBack && req.status != 200) req = null; return req; } /* function httpReqErr(req) { alert("AJAX request fails!\nstatus: " + req.statusText + '(' + req.status + ")" // + "\nreadyState: " + req.readyState + "\nheaders: " + req.getAllResponseHeaders() ); } */ // string) receive text or XML string like "..." // null) error, receving XML string like "......" or // ".........; // DOM tree with root "B") more complicated DOM function getXML(sec, argList, url) { // Mozilla splits a text node into more text nodes, if the string is longer // than 4096. The function combines the splited nodes into one. function cmbTxtNodes(node) { var i, nodes = node.childNodes, nd, p; if(!nodes) return; for(i = 0; i < nodes.length; i++) if((nd = nodes[i]).nodeType == 3) { // text if(p) { p.nodeValue += nd.nodeValue; node.removeChild(nd); i--; } else p = nd; } else p = null; for(i = 0; i < nodes.length; i++) cmbTxtNodes(nodes[i]); } var s, args, key, dom, node, p; // var f = self._tblTree ? self._tblTree.form : document.forms[0]; if(argList.constructor === String) sec += '&'; else { args = argList; argList = ''; for(key in args) argList += '&' + key + '=' + escURL(args[key]); } if(!url) url = _doreUrl + "pulldown.php"; argList = _addUrlArgs(sec) + argList; var req = httpReq(url, argList); if(req.status == 200) if(req.responseXML && (dom = req.responseXML.documentElement)) { if(_browser == 2) cmbTxtNodes(dom); if(dom.tagName == 'R') { // from pulldown.php node = dom.lastChild; if(node.tagName == 'E') _errMsg = node.firstChild.nodeValue; else if(dom.childNodes.length != 1) _errMsg = 'Extra data from "conf.php" detected'; else { dom = dom.firstChild; // ... s = (p = dom.firstChild) ? (dom.childNodes.length == 1 && p.nodeType == 3 ? p.nodeValue : dom) : ''; } } else s = dom; } else s = req.responseText; // else _errMsg = "Fail to receive data"; else _errMsg = req.statusText; return s; } // tbl0: table name that most frequently used(form name also) // flds: field list seperated by comma(input element name list) // If the format of items of 'flds' is: .,
will override // 'tbl0'. function someFormVals(tbl0, flds) { var i, pair, fld, f, elem, s, w = '', vals = new Array(); flds = flds.split(','); for(i = 0; i < flds.length; i++) { pair = flds[i].split('.'); switch(pair.length) { case 1: tbl = tbl0; fld = flds[i]; break; case 2: tbl = pair[0]; fld = pair[1]; break; default: _errMsg = 'Illegal field name: ' + flds[i]; return null; } if((f = document.forms[tbl]) == null || (elem = f.elements[fld]) == null) { _errMsg = 'Illegal field name: ' + flds[i]; return null; } vals[i] = getInputElem(elem); if(self._schema && (s = getFldAttrX(elem, 'n')) != null && vals[i].length == 0) { _errMsg = s; return null; } } return vals; } function formValList(tbl, fldList, delim) { var vals = someFormVals(tbl, fldList); if(vals == null) return null; if(delim == null) delim = '\t'; return vals.join(delim); } //http://somanyschemes.com/2008/11/23/replacement-for-script-onload-in-ie function importJS(src, look_for, callback) { if(eval("typeof " + look_for) == 'undefined') { var s = document.createElement('script'); s.setAttribute('type', 'text/javascript'); s.setAttribute('src', src); if(callback) var interval = setInterval(function() { if(eval("typeof " + look_for) != 'undefined') { clearInterval(interval); callback(); } }, 50); var head = document.getElementsByTagName('head')[0]; if (head) head.appendChild(s); else document.body.appendChild(s); } else if(callback) callback(); } /* function setDivPos(id, top, left, w) { var obj = document.getElementById(id).style; obj.width = w != null ? w : 600; obj.top = top + parseInt(document.body.scrollTop); obj.left = left + parseInt(document.body.scrollLeft); obj.display = "block"; } */ /* Public names: qryFormVals mapForm setFormVals setFormByQry insNumComma * strToInt * Cross-file names: getMngSchem setMngForm0 getFldAttrX _chkNull setMngButton * resetForm */ // Get information according to the table tree, including _schema, _vals, etc. // oVals: original input data send back by the server due to error detection function getMngSchema(/*oVals*/) { var s, i, op, resVer, f = _multi ? _tblTree.form : _f; var args = _multi ? { '_tblTree':_tblTree.mapTblTree(), '_multi':'m' } : { '_tblTree':_mapTblTree(_tblTree), '_multi':'' }; // Ready to send the table tree back resVer = f._resVer.value; if(resVer != ''/* && !oVals*/) { args['_op'] = 'f'; args['_resVer'] = resVer; args['_recNo'] = f._recNo.value; op = 'f'; } s = getXML(f._section.value, args, _doreUrl + 'schema.php'); //document.write(s); if(s) eval(s); return s != null; /* // Get the schema via the invisible iframe '_loader'. // With IE, the maximum length for the GET method is 2080. // The elegant way: GET method. The management page is not loaded again. // Supposed the access control mechanism like $_roleList is set before // 'manage.php' or 'managem.php' is included in the management page, will // the protection ruin out here? No! See the comment in 'schema0.php'. if(s.length <= 1024) document.getElementById('_loader').src = _doreUrl + 'schema.php?' + s; // The remaining statements of the process must be invoked by the // iframe, since the the iframe won't load anything before the current // process has complete running and enters the ready state... else { f.method = 'post'; sendCmd(op); } */ } // If false is returned, the schema DOM is not consistent with the form. function setMngForm0(f) { var i, elems = f.elements, elem, type, name, radios = new Array(), len; var ok = !f._type || f._type.value != 'm'; // not multi-value form var flds = _schema[_multi ? f.name : 'T'], attrs; if(flds == null) return false; for(i = 0; i < elems.length; i++) { elem = elems[i]; type = elem.type; if(ok && (type == 'text' || type == 'textarea' || type == 'password')) { elem.onchange = elem.onchange ? _extHandler(elem.onchange, "if(!chkData(this)) return false;\n") : chkData; // Set the attribute "maxLength". name = elem.name; if(type != 'textarea' && name.length > 0 && name.charAt(0) != '_') { attrs = flds[name]; if(!attrs) return false; len = attrs['l']; if(len != null) if(elem.maxLength <= 0 || elem.maxLength > len) elem.maxLength = len; else if(_check && elem.maxLength == len) _chkCnt++; } } } return true; } function getFldAttrX(elem, type) { var tbl = _multi ? elem.form.name : (elem.form == _f ? 'T' : ''); var s = elem.name; if(tbl.length > 0 && tbl.charAt(0) != '_' && s.length > 0 && s.charAt(0) != '_') s = _schema[tbl][s][type]; else switch(type) { case 't': s = elem.dataType; break; case 'r': s = elem.range; break; case 'n': if((s = elem.nullMsg) == null && elem.notNull == '1') { s = elem.desc ? elem.desc : elem.name; s = _lackErrStr + (s.length > 0 ? s : "..."); } break; default: s = null; } if(typeof(s) == "undefined") s = null; return s; } // Insert commas into a number. function insNumComma(s) { var minus = '', i, t = ''; s = s.toString(); if((i = s.indexOf('.')) > 0) t = s.substr(i), s = s.substr(0, i); if(s.charAt(0) == '-') s = s.substr(1), minus = '-'; i = s.length; while(i > 3) { i -= 3; t = ',' + s.substr(i, 3) + t; } return minus + s.substr(0, i) + t; } /* // For IE only function insNumComma(num) { var obj1 = new Number(num); var obj2 = obj1.toLocaleString(); return obj2.substring(0, obj2.lastIndexOf(".")); } */ function _convFldList(flds) { var negative = false, i, parts; if(flds) { if(negative = flds.charAt(0) == '!') flds = flds.substr(1); if(flds.length > 0) { parts = flds.split(','); flds = new Array(parts.length); for(i = 0; i < parts.length; i++) flds[parts[i]] = true; } else flds = null; } return [flds, negative]; } // "flds" is an array of input element names in form "f". If it's not given, // the values in array "vals" are assigned to the input elements with names, but// not beginning with underlines. If "flds" is given, names in it may begin with // underlines and "vals" is in parallel with it. function setFormVals(f, vals, flds, readFlds) { var i = 0, j, k = 0, radios = new Array, elems = f.elements, elem, type; var opts, s, len = f.length, p; if(flds == null) { if(_multi) k = 1; } else { flds = flds.split(','); if(flds.length == 1) { // beginning field i = getElemIdxByName(flds[0], f); if(i < 0) return; flds = null; } else len = flds.length; } var pair = _convFldList(readFlds); readFlds = pair[0]; var negative = pair[1]; for(; i < len && k < vals.length; i++) { if(flds) { elem = elems[flds[i]]; if(elem == null) return; } else elem = elems[i]; type = elem.type; var name = elem.name; if(name.length == 0 || type == "radio" && radios[name] || flds == null && name.charAt(0) == '_') continue; var val = vals[k++]; var yes = readFlds ? (readFlds[name] ? negative : !negative) : negative; // Even a "hidden" type has a "disabled" property. _setDisabled(elem, yes); switch(type) { case "text": if(_insComma && ((s = getFldAttrX(elem, 't')) == 'int' || s == 'float')) val = insNumComma(val); case "textarea": case "hidden": case "password": elem.value = val; break; case "checkbox": elem.checked = val == elem.value; break; case "select-one": // Not dynamic/static selection tree. if(srchSelTree(elem) < 1) { elem.selectedIndex = 0; setSel(elem, val); } break; case "radio": elem = elems[name]; // radio array // one button only if(elem.type == "radio") elem = new Array(elem); for(j = 0; j < elem.length; j++) { if(elem[j].value == val) elem[j].checked = true; _setDisabled(elem[j], yes); } radios[name] = 1; break; case "select-multiple": opts = elem.options; val = val.split(','); for(j = 0; j < elem.length; j++) opts[j].selected = false; // if(curAttrs[i].type == "bits") showBits(elem, val); // else for(j = 0; j < val.length; j++) setSel(elem, val[j]); break; case "file": /* var style, d = document.getElementById(name); if(d == null || (style = d.style) == null) continue; d.innerHTML = "Download; style.display = 'visible'; */ // elem.value = val; // Doesn't work. break; default: alert("Input type '" + type + "' not allowed!"); } } if(self.RTsrch && !flds) { // ... var node = RTsrch(f); for(i = 0; i < _selTrees.length; i++) if((p = _selTrees[i]).form == f) if(p.tree) selPath0(p, node, vals); else selPathD0(p, node, vals); } if(self.mapForm) mapForm(f, false); } function setFormByQry(sec, primaryVal, tbl, fld) { var f = document.forms[tbl]; var s = getXML(sec, {'_values':primaryVal}); if(s == null) { alert(_errMsg); return; } // Strip the newline at end of 's'. setFormVals(f, s.substr(0, s.length - 1).split('\t'), fld); } function strToInt(s) { var i, c, n = 0, minus, base = '0'.charCodeAt(0); i = (minus = s.charAt(0) == '-') ? 1 : 0; if(i == s.length) return NaN; for(; i < s.length; i++) { if((c = s.charCodeAt(i) - base) < 0 || c > 9) return NaN; n = n * 10 + c; } if(minus) n = -n; return n; // return n.search(/^-?[0-9]+$/) == 0 ? parseInt(n) : NaN; } function _chkNull(f) { var elems = f.elements, radios = new Array, elem, i, msg = '', val, s; for(i = 0; i < f.length; i++) { elem = elems[i]; if((elem.type != 'radio' || !hasVal(radios, elem.name)) && (s = getFldAttrX(elem, 'n')) != null) { val = getInputElem(elem); if(val.length == 0) msg += ',' + s; } } if(msg !== '') msg = _valWantedStr + msg.substr(1); return msg; } var _dayOfMonths = new Array(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); function compTime(s, op, type) { function chkTime(t, end) { var h, m, s; var parts = t.split(':'), cnt = parts.length; if(cnt >= 1) { if(isNaN(h = strToInt(parts[0])) || h < 0 || h > 23) return -1; } else h = end ? 23 : 0; if(cnt >= 2) { if(isNaN(m = strToInt(parts[1])) || m < 0 || m > 59) return -1; } else m = end ? 59 : 0; if(cnt >= 3) { if(cnt > 3 || isNaN(s = strToInt(parts[2])) || s < 0 || s > 59) return -1; } else s = end ? 59 : 0; return h*60*60 + m*60 + s; } var t = '', n, i, cnt; var parts, end, y, m, d, max; end = op == 2 || op == 3; if(type == 'time') { if((n = chkTime(s, end)) < 0) _errMsg = _timeErrStr; return n; } if(type == 'datetime') { parts = s.split(' '); if(parts.length == 2) { s = parts[0]; // date part t = parts[1]; // time part } } // del = (i = s.indexOf('/.-')) < s.length ? s.charAt(i) : '/'; parts = s.split('/'); cnt = parts.length; // An imcomplete date with the time part. if((t.length > 0 || op == 0) && cnt != 3) { _errMsg = _fmtErrStr; return -1; } if(isNaN(y = strToInt(parts[0]))) { _errMsg = _yearErrStr; return -1; } if(cnt >= 2) { if(isNaN(m = strToInt(parts[1])) || m < 1 || m > 12 || parts[1].length >2) { _errMsg = _monthErrStr; return -1; } } else m = end ? 12 : 1; if(m != 2) max = _dayOfMonths[m]; else max = y % 4 || y % 100 == 0 && y % 400 ? 28 : 29; if(cnt >= 3) { if(cnt > 3 || isNaN(d = strToInt(parts[2])) || d < 1 || d > max || parts[2].length > 2) { _errMsg = _dateErrStr + ": 1~" + max; return -1; } } else d = end ? max : 1; n = y*12*31 + (m-1)*31 + d-1; if(type == 'datetime') { n = parseFloat(n) * 86400; if(t) { if((i = chkTime(t, end)) < 0) { _errMsg = _timeErrStr; return -1; } n += i; } else if(end) n += 86400 - 1; } return n; } function chkData(elem) { function chkData0(elem) { var i, ranges, range, val = elem.value, node, rows, errMsg; var isTime = false, s, n, op, insComma = false; var cmpOp, msg1 = "illegal range", between, min, max; var re = new RegExp('[\/.-]', 'g'); // Compatable with Mozilla. var re1 = new RegExp('^ +| +$', 'g'); if(val.length == 0) return true; errMsg = getFldAttrX(elem, 'e'); if((s = getFldAttrX(elem, 'p')) != null && val.search(s) < 0) { _errMsg = errMsg ? errMsg : _fmtErrStr; return false; } var type = getFldAttrX(elem, 't'), range0 = getFldAttrX(elem, 'r'); switch(type) { case 'int': if(insComma = _insComma && elem.type == 'text') val = val.replace(/,/g, ''); val = val.replace(re1, ''); if(isNaN(val = strToInt(val))) { _errMsg = _intErrStr; return false; } if(self.RTsrch) { node = RTsrch(elem.form); if(node.subSerial == elem && (val < 1 || val > ((rows = RTcurRows(node)) ? rows.length : 1))) { _errMsg = _serialErrStr; return false; } } elem.value = insComma ? insNumComma(val) : val; break; case 'float': if(insComma = _insComma && elem.type == 'text') val = val.replace(/,/g, ''); val = val.replace(re1, ''); if(isNaN(val)) { _errMsg = _numErrStr; return false; } elem.value = insComma ? insNumComma(val) : val; val = parseFloat(val); break; case 'date': case 'datetime': case 'time': isTime = true; // nomorlize val val = val.replace(re1, ''); if((val = compTime(val.replace(re, '/'), 0, type)) < 0) return false; // nomorlize range if(range0) range0 = range0.replace(re, '/'); break; case 'string': case 'blob': case null: if(self.chkInputLen && !chkInputLen(elem)) return false; break; default: _errMsg = 'data type "' + type + '" unknown'; return false; } if(range0 == null) return true; if(type == 'datetime') range0 = range0.replace(/ *, */g, ',').replace(/ *~ */g,'~').replace(/^ +/,'').replace(/ +$/,''); else range0 = range0.replace(/ /g, ''); ranges = range0.split(','); for(i = 0; i < ranges.length; i++) { range = ranges[i]; s = range.charAt(0); n = 0; // length of comparative operator // op: 1) >=; 2) >; 3) <=; 4) <; 5) <> if(s == '<') if((s = range.charAt(1)) == '>') { n = 2; op = 5; } else if(s == '=') { n = 2; op = 3; } else { n = 1; op = 4; } else if(s == '>') if(range.charAt(1) == '=') { n = 2; op = 1; } else { n = 1; op = 2; } if(n > 0) { // comparative operator found cmpOp = range.substr(0, n); range = range.substr(n); if(isTime) if(op < 5) { if((n = compTime(range, op, type)) < 0) { _errMsg = msg1; return false; } switch(op) { case 1: n = val >= n; break; case 2: n = val > n; break; case 3: n = val <= n; break; case 4: n = val < n; break; } } else { // <> if((t0 = compTime(range, 1, type)) < 0) { _errMsg = msg1; return false; } if((t1 = compTime(range, 3, type)) < 0) { _errMsg = msg1; return false; } n = val < t0 || val > t1; } else { if(type == 'int') { if(isNaN(n = strToInt(range))) { _errMsg = msg1; return false; } } else if(type == 'float') { if(isNaN(range)) { _errMsg = msg1; return false; } n = parseFloat(range); } else n = range; // string or blob switch(op) { case 1: n = val >= n; break; case 2: n = val > n; break; case 3: n = val <= n; break; case 4: n = val < n; break; case 5: n = val != n; break; } } } else { parts = range.split('~'); if(between = parts.length == 2) { // '~' found if(parts[0].length == 0 || parts[1].length == 0) { _errMsg = msg1; return false; } } if(isTime) { min = compTime(between ? parts[0] : range, 1, type); max = compTime(between ? parts[1] : range, 3, type); if(min < 0 || max < 0) { _errMsg = msg1; return false; } between = true; } else if(type == 'int') { if(isNaN(min = strToInt(parts[0]))) { _errMsg = msg1; return false; } if(between) { if(isNaN(max = strToInt(parts[1]))) { _errMsg = msg1; return false; } } } else if(type == 'float') { if(isNaN(parts[0])) { _errMsg = msg1; return false; } min = parseFloat(parts[0]); if(between) { if(isNaN(parts[1])) { _errMsg = msg1; return false; } max = parseFloat(parts[1]); } } else { // string or blob min = parts[0]; if(between) max = parts[1]; } if(between) { if(min >= max) { _errMsg = msg1; return false; } n = val >= min && val <= max; } else n = val == min; } if(n) return true; } _errMsg = errMsg ? errMsg : _rngErrStr + range0; return false; } if(elem == null || elem.eventPhase != null) elem = this; var ret = true; if(_mngMode > 0 && !(ret = chkData0(elem))) alert(_errMsg); return ret; } // If 'dim' is set to 1 or 2, a one or two dimension array is returned // respectively. function qryFormVals(f, fldList, sec, dim) { var s = formValList(f.name, fldList), rows, i, n; if(s == null || (s = getXML(sec, {'_values':s})) == null) { alert(_errMsg); return null; } if(dim == null) return s; if(s.length == 0) { _errMsg = 'Not found'; return null; } // Strip the last newline. rows = s.substr(0, s.length - 1).split('\n'); if(dim == 1) return rows[0].split('\t'); for(i = 0; i < rows.length; i++) rows[i] = rows[i].split('\t'); return rows; } function setMngButton(elem, val, handler) { if(val != null && val.length > 0) { elem.value = val, elem.style.display = ''; if(handler != null) elem.onclick = handler; } else { elem.value = '', elem.style.display = 'none'; if(val == null) elem.onclick = null; } } function resetForm(node) { var f = node.form, elems = f.elements, elem, i, name; var pair = _convFldList(node.insFlds); var insFlds = pair[0], negative = pair[1]; f.reset(); for(i = 0; i < elems.length; i++) { elem = elems[i]; if(elem.type == "hidden") { if(typeof elem._val != "undefined") elem.value = elem._val; } else if((name = elem.name) != "" && name.charAt(0) != '_') _setDisabled(elem, insFlds ? (insFlds[name] ? negative : !negative) : negative); } clrSelTreeForm(f); if(self.mapForm) mapForm(f, true); } // Public names: selPath Node(deprecated!) // Cross-file names: SelTree selPath0 // MLPD: multi-level pulldown // 0) depth; 1) keyword; 2) code function Node(dep, name, val) { this.dep = dep; this.name = name; this.val = val ? val : name; } function SelTree(sel, tree, dep, beg, end) { function goPath(tree, path, dep, flag) { var i = 0, j = -1, n, val; path = path.split('/'); while(i < path.length) { val = path[i++]; while((n = tree[++j].dep - i) >= 0) if(n == 0 && tree[j][2] == val) { path[i - 1] = flag == 2 ? j+1 : j; break; } if(n < 0) return null; } if(flag == 1) j = path[i - 1]; // lower bound else while(tree[++j].dep > i); // upper bound while(i < dep) path[i++] = j; return path; } var p, i, n, cnt, f, elems, parts, s; _errMsg = null; var msg = 'SELECT tree "' + tree + '": '; if(eval("typeof " + tree) != "object") { _errMsg = msg + "not found or illegal"; return; } tree = eval(tree); if(tree[0].constructor != Array) // Object Node for(i = 0; i < tree.length; i++) { p = tree[i]; tree[i] = new Array(p.dep, p.name, p.val); } n = parseInt(dep); if(isNaN(n)) { parts = dep.split(','); dep = parts.length + 1; } else if((dep = n) < 1) { _errMsg = msg + "illegal depth"; return; } if(tree[tree.length - 1][0] > 0) tree[tree.length] = new Array(0, null); p = null; if(beg && (p = goPath(tree, beg, dep, 1)) == null) { _errMsg = msg + _begPathErrStr; return; } this.begPath = p; p = null; if(end && (p = goPath(tree, end, dep, 2)) == null) { _errMsg = msg + _endPathErrStr; return; } this.endPath = p; this.tree = tree; this.path = new Array(dep + 1); this.path[0] = -1; this.elems = new Array(dep); this.form = f = sel.form; elems = f.elements; n = getElemIdx(sel); cnt = 0; for(i = 0; i < dep; i++) { if(i > 0) // "self._mngMode != null" means management pages. s = parts ? parts[i-1] : null; sel = s ? (self._mngMode != null ? _getElemByName(s, f) : elems[s]) : elems[n+i]; if(!sel) { _errMsg = msg + (s ? 'SELECT element "' + s + '" not found' : 'too deep'); return; } if(sel.type != "select-one" && (i < dep - 1 || sel.type != "select-multiple")) { _errMsg = msg + "select-one expected"; return; } if(i > 0 && sel.form != f) { if(cnt == 0) break; if(sel.form._parent.value != f.name) { _errMsg = msg + "as crossing forms, neighboring forms require parent-child relation"; return; } f = sel.form; cnt = 0; } if(sel.name != "" && sel.name.charAt(0) != "_") cnt++; sel.onfocus = _selMenu; if(sel.onchange == null) sel.onchange = selPath; this.elems[i] = sel; } if(cnt == 0 && i < dep) _errMsg = msg + "as crossing forms, at least one SELECT has a field name in each form, except the last"; } // Prepare the pulldown menu for the SELECT element "sel" at the dep'th level. function _selMenu(sel, dep) { var i, j, path, idx, p; if(dep) { clrSel(sel); idx = _selTree.path[dep]; } else { dep = srchSelTree(sel = this); if(sel.length > 1) return; idx = -1; } var tree = _selTree.tree; var begX = (path = _selTree.begPath) ? path[dep-1] : 0; var endX = (path = _selTree.endPath) ? path[dep-1] : tree.length - 1; path = _selTree.path; // Search for the ancestor selection already selected. dep0 = dep; while(path[--dep0] == null); // A MLPD may spread into some forms. If the selected path // is not sufficient for the part of the MLPD in the form, we give up. if(_selTree.elems[dep0].form != sel.form) return; i = path[dep0]; if(begX > i) i = begX - 1; j = 0; while(tree[++i][0] > dep0 && i < endX) if(tree[i][0] == dep) { p = tree[i]; sel.options[++j] = new Option(p[1], p[2] ? p[2] : p[1], false, i == idx); } } function selPath(sel0, dep, idx) { if(dep == null) { if(sel0 == null || sel0.eventPhase != null) sel0 = this; dep = srchSelTree(sel0); } var elems = _selTree.elems, tree = _selTree.tree, path = _selTree.path; var i, j, dep0; // Search for the ancestor selection already selected. dep0 = dep; while(path[--dep0] == null); // 'idx' is the index to 'tree' related to the selected option. if(idx == null && (i = sel0.selectedIndex) > 0) { idx = path[dep0]; if(_selTree.begPath && (j = _selTree.begPath[dep-1]) > idx) idx = j - 1; while(i > 0) if(tree[++idx][0] == dep) i--; path[dep] = idx; } if(idx != null) { // The current SELECT element has been selected. // There are ancestor selection(s) not selected yet, since the path can be // decided, so rearrange the pulldowns for them. if(dep0 < dep - 1) { // Backtrace for ancestor nodes. for(i = dep; --i > dep0; path[i] = idx) while(tree[--idx][0] > i); for(i = dep0; i < dep; i++) _selMenu(elems[i], i+1); } else _selMenu(sel0, dep); if(dep < elems.length) { // show the next level _selMenu(elems[dep], dep+1); path[++dep] = null; } } else path[dep] = null; // Clear descendent selections. for(i = dep; i < elems.length; ) { clrSel(elems[i]); path[++i] = null; } if(self.selChgEx) selChgEx(sel0); } function selPath0(p, node, vals) { _selTree = p; var elems = p.elems, path = p.path, i, f = node.form, idx; for(i = 0; i < elems.length && elems[i].form != f; i++); if(i == elems.length || (idx = path[i]) == null) return; var tree = p.tree, sel, n = 1, dep0 = 0, s, q; // Part or all of the MLPD is in the form. If there is only part, the // ancestor SELECT element(s) should be selected. while(i < elems.length && elems[i].form == f) { sel = elems[i++]; if((s = sel.name) != "" && s.charAt(0) != "-" && (s = vals[RTcolIdx(node, s)]) != "" && n > 0) { while((n = tree[++idx][0] - dep0) > 0) { q = tree[idx]; if(q[0] == i && (q[2] ? q[2] : q[1]) == s) { path[dep0 = i] = idx; break; } } if(n > 0) { if(path[i-1]) _selMenu(sel, i); else selPath(sel, i, idx); continue; } } clrSel(sel); path[i] = null; } if(i < elems.length && path[i] != null) { // The last element is for a multi-value form. if(elems[i].type == "select-multiple") _selMenu(elems[i], i+1); _fixSelTree(p, i, RTchild(node, elems[i].form.name)); } } // Public names: selPathD selPathD0 // Cross-file names: SelTreeD clrSels function SelTreeD(sel, sec, dep, colList, floating) { var i, n, elems = sel.form.elements; this.section = sec; this.depth = dep = dep ? parseInt(dep) : 1; this.form = sel.form; this.idx = n = getElemIdx(sel); this.dynamic = new Array(dep); if(colList) if(colList.constructor == String) this.colList = colList; else this.cond = colList; this.floating = floating; for(i = 0; i < dep; i++) { sel = elems[n + i]; if(sel.type != "select-one") { alert("Select-one expected for selection trees"); return; } if(sel.onchange == null) sel.onchange = selPathD; if(sel.onfocus == null) sel.onfocus = _selMenuD; this.dynamic[i] = sel.length == 1; if(this.floating && i+1 < dep && sel.name.length > 0 && sel.name.charAt(0) != '_') { alert("For a floating multi-level pulldown, the field name should bind with the last level"); return; } } // The function cleans the pulldowns from the (i+1)'th depth. If i is not given, // the whole pulldowns are cleaned. this.clrSels = function (i){ var elem, elems = this.form.elements, idx; if(i == null) i = 0; idx = this.idx + i; for(; i < this.depth; i++, idx++) { elem = elems[idx]; if(this.dynamic[i]) clrSel(elem); else elem.selectedIndex = 0; } } } // Format of 'doc': [\t]\n... function _selMenuD0(sel, doc, val) { var i, items, len, pair, opts = sel.options, opt, yes = false; if(sel.func != null) { _errMsg = 'Attribute "func" for dynamic selection is invalid'; return false;} items = doc.split('\n'); len = items.length; if(items[len - 1].length == 0) len--; for(i = 1; i <= len; i++) { pair = items[i-1].split('\t'); if(pair.length == 1) opts[i] = new Option(pair[0]); else opts[i] = new Option(pair[1], pair[0]); if(pair[0] == val) opts[i].selected = yes = true; } if(val != null && !yes && opts[0].value != val) { _errMsg = "Pulldown menu '" + sel.name + "': value '" + val + "' not matched"; return false; } return true; } // Prepare a pulldown menu for the dep'th level function _selMenuD(sel, dep) { if(dep == null) dep = srchSelTree(sel = this); var f = sel.form, elems = f.elements, i, p, cond = '', vals, condEx = ''; // _selTree has been set by invoking "srchSelTree". if(sel.length > 1 && !_selTree.colList && !_selTree.cond) return; // Collect selected values of ancestors into "cond". for(i = 1; i < dep; i++) { p = elems[_selTree.idx + i - 1]; // An ancestor is not selected yet, so nothing is done. if(p.selectedIndex <= 0) return; opt = p.options[p.selectedIndex]; cond += opt.value.length > 0 ? opt.value : opt.text; cond += '\t'; } // Tell server the depth of the pulldowns for PHP function "selTreeAR". for(; i < _selTree.depth; i++) cond += '\t'; if(_selTree.colList && dep == 1) { if((condEx = formValList(f.name, _selTree.colList)) == null) { alert(_errMsg); return; } } else if(_selTree.cond) condEx = _selTree.cond(dep); doc = getXML(_selTree.section, {'_fld':f.name + '.' + elems[_selTree.idx].name, '_depth':dep, '_cond':cond, '_condEx':condEx}); if(doc == null) { alert(_errMsg); return; } if(doc.length == 0) return; // no query result clrSel(sel); if(!_selMenuD0(sel, doc)) alert(_errMsg); } function selPathD(sel) { var dep, opt, elems; if(sel == null || sel.eventPhase != null) sel = this; if(self.selChgEx) selChgEx(sel); dep = srchSelTree(sel); if(dep < _selTree.depth) { // Not the last level _selTree.clrSels(dep); if(sel.selectedIndex > 0) _selMenuD(sel.form.elements[_selTree.idx + dep], dep+1); // The input element bound to the table field is floating, so we assign // the value to the 0'th option of the last level. if(_selTree.floating) { elems = sel.form.elements; opt = elems[_selTree.idx + _selTree.depth-1].options[0]; if(sel.selectedIndex == 0 && dep > 1) sel = elems[_selTree.idx + dep - 2]; opt.value = sel.options[sel.selectedIndex].value; opt.selected = true; } } } function selPathD0(p, node, vals) { var f = p.form, elems = f.elements, sel, name, s = '', t, condEx = ''; var x, x0, i, dom, flag = '', dep, fld; for(i = 0; i < p.depth; i++) { name = elems[p.idx + i].name; if(name.length > 0 && name.charAt(0) != '_') { x = RTcolIdx(node, name); if(!x0) x0 = x; s += vals[x]; if(!fld) fld = f.name + '.' + name; } else flag = '*'; s += '\t'; if(p.colList) { if(i == 0) { if((t = formValList(f.name, p.colList)) == null) { alert(_errMsg); return false; } } else t = ''; condEx += t + '\003'; } else if(p.cond) condEx += p.cond(i+1) + '\003'; } p.clrSels(p); // The multi-level pulldown have no concern with a database. if(x0 == null || vals[x0] == '') return true; // XML: // // value path // items of level 1 // items of level 2 // ... // dom = getXML(p.section, {'_fld':fld, '_cond':s+flag, '_condEx':condEx}); if(dom == null) { alert(_errMsg); return false; } dep = dom.childNodes.length - 1; node = dom.firstChild; // value path of all levels vals = node.firstChild.nodeValue.split('\t'); for(i = 0; i < dep && vals[i].length > 0; i++) { node = node.nextSibling; sel = elems[p.idx + i]; // node.firstChild: ... found // "sel.length == 1" is always true, except for the first level. The first // level is set once only, unless an external condition exists. if(node.firstChild && (sel.length == 1 || condEx != '') ? !_selMenuD0(sel, node.firstChild.nodeValue, vals[i]) : !setSel(sel, vals[i])) { alert(_errMsg); break; } } if(p.floating && i > 0 && i < p.depth) { var opt = elems[p.idx + p.depth - 1].options[0]; opt.value = vals[i-1]; opt.selected = true; } return true; } // Public names: FolderNode, val, desc, obj, render, addChild //**************************************************************** // You are free to copy the "Folder-Tree" script as long as you // keep this copyright notice: // Script found in: http://www.geocities.com/Paris/LeftBank/2178/ // Author: Marcelino Alves Martins (martins@hks.com) December '97. //**************************************************************** // Author: Lin, Shih // Last update time: Jan 12 2006 // Children of a folder are rendered as a table, one node one row basically. // The first cell of a row is an folder or leave icon and the second the // description. This makes the icon and the description align well. However, // while the child node has children also, there is an additional row under the // row for the child node. The first cell of the extra row is void for indent // and the second holds another table for children. // (93/12/3) IE allows 26 layers of nested tables at most. // The program has good scalability. It expands 800 children nodes smoothly. // No id or name for any HTML objects here, so no collision to HTML ids or // names given by users. It can handle multiple trees at the same time. var _ftIndice = new Array, _s, _s0; // "p" is a leave node. When "p.addChild" is called, "p" becomes a folder // node. // isLeave: true) leave node; false or null) folder node // obj: extra information // close: for root node only. true) close the folder; false or null) open function FolderNode(val, desc, isLeave, obj, close) { if(isLeave && isLeave.constructor == Function) { // Root this.sec = val; this.func = isLeave; this.folderFunc = obj; this.isOpen = false; this.id = _ftIndice.length; this.render = function(id, open) { var obj; if(typeof id != "string") obj = id; else if(!(obj = document.getElementById(id))) { alert('id "' + id + '" unknown'); return; } // Add a pseudo root. var node = new FolderNode(null, null, true); node.children = new Array(this); if(obj == null) obj = document.body; node.render1(obj); // Render the root. if(open != false) this.folderAct(); // open the root folder } } else { this.val = val; this.parent = null; // isOpen: null) leave node; true) folder node and open; // false) folder node and close this.isOpen = isLeave ? null : false; if(obj) this.obj = obj; } this.desc = desc; this.children = null; this.isLastNode = true; this.navObj = null; // isLeave: true) leave node; false or null) folder node this.addChild = function(val, desc, isLeave, obj) { if(this.children == null) { this.children = new Array; if(this.isOpen == null) this.isOpen = false; // become folder } var node = new FolderNode(val, desc, isLeave, obj); node.parent = this; return this.children[this.children.length] = node; } this.folderAct = function() { // The folder has become a leave. if(this.isOpen == null) { this.leaveAct(); return; } var i, n, obj = this.navObj, cond = "", doc, p, children, x; // tr->td->img var img = obj.cells[0].firstChild; // Decide the sibling order of the folder. if(p = this.parent) { children = p.children; for(i = x = 0; this != children[i]; i++, x++) if(children[i].children) x++; } else x = 0; x++; if(!this.isOpen && this.children == null) { // Collect values of ancestors into "cond". for(p = this, i = 0; p.id == null; p = p.parent, i++) if(cond.length == 0) cond = p.val; else cond = p.val + '\t' + cond; if(p.sec == null) { alert("No child"); return; } // Static tree // Get children. i++; // "'_x':1" says the request is from a folder tree. // '_condEx':condEx doc = getXML(p.sec, {'_x':1, '_depth':i, '_cond':cond}); //doc = "1\tPage A\t0\n2\tPage B\t0\n3\tVoid folder\t1\n"; if(doc == null) { alert(_errMsg); return; } if(doc.length == 0) { // No child! The folder becomes a leave. this.isOpen = null; img.src = _doreUrl + "images/ftv2doc.gif"; this.leaveAct(); return; } var items = doc.split('\n'); var len = items.length; if(items[len - 1].length == 0) len--; for(i = 0; i < len; i++) { var parts = items[i].split('\t'); // Every line has two or more fields seperated by tab. The first field is // the node value. The last is equal to 0 or 1, 0 for directory and 1 for // leave node. If there are remainings, the second is the description and // the rest fields are the extra information. n = parts.length - 1; this.addChild(parts[0], parts.length > 2 ? parts[1] : null, parts[n] == 1, parts.length > 3 ? parts.slice(2, n) : null); } // Get the left side. p = this; var leftSide = ""; /* while((p = p.parent) && p.parent) if(p.isLastNode) leftSide = "" + leftSide; else leftSide = "" + leftSide; */ var tr = obj.parentNode.insertRow(x); // Insert a row to the table domInsertCell(tr); this.render1(domInsertCell(tr)); } // Switch the folder icons. i = (this.isOpen = !this.isOpen) ? 0 : 1; /* if(img = obj.cells[0].firstChild.firstChild) img.src = _doreUrl + "images/" + (this.isLastNode ? "ftv2lastplus" : "ftv2plus") + i + ".gif"; */ img.src = _doreUrl + "images/ftv2folder" + i + ".gif"; this.visible(obj.parentNode.rows[x]); this.leaveAct(); } // isOpen) true) folder node and open; false) folder node and close; // null) leave node this.leaveAct = function() { var path = new Array(), p, i, func; for(p = this, i = 0; p.id == null; p = p.parent, i++) path[i] = p; if(this.isOpen == null) func = p.func; else if(this.isOpen) func = p.folderFunc; if(func) func(path.reverse()); } this.setObj = function(tbl) { var node, i, j, children = this.children, rows; rows = tbl.rows; for(i = j = 0; i < children.length; i++, j++) { node = children[i]; node.navObj = rows[j]; // tr->td->table if(node.children) node.setObj(rows[++j].lastChild.firstChild); } } this.renderNode = function(s, isLastNode) { var s1, s2; this.isLastNode = isLastNode; _ftIndice[id = _ftIndice.length] = this; // s = '
' + s; if(this.isOpen != null) { // folder /* if(this.parent) s += ""; */ s1 = "folder"; s2 = "folder1"; } else { /* s += ""); */ s1 = "leave"; s2 = "doc"; } s += "
"; //s += "
"; return s + '' + this.desc + '\n'; } this.render0 = function(leftSide, isLastNode, addRow) { var node, i, children = this.children; /* if(this.parent) if(isLastNode) leftSide += ""; else leftSide += ""; */ // Note: No newline before " 10000) _s0 += _s, _s = ""; if(node.children) node.render0(leftSide, i == children.length-1, true); } _s += '
'; // No newline } this.render1 = function(obj) { _s0 = _s = ""; this.render0(''/*leftSide*/, this.isLastNode); obj.innerHTML = _s0 + _s; _s = _s0 = null; this.setObj(obj.firstChild); } this.visible = function(obj) { obj.style.display = this.isOpen ? "" : "none"; // inline //obj.visibility = this.isOpen ? "show" : "hidden"; } } /* function _drInsertRow(tbl, x) { return tbl.insertRow(x); //return tbl.appendChild(document.createElement("TR")); } */