Major improvements in the security of the CAPTCHA system (no SQL injection or anything like that); fixed denied form submission due to _af_acting on form object wrongly switched to true
/*
* Auto-completing page/username fields
* NOTE: A more efficient version of the username field is used for Mozilla browsers. The updated code is in autofill.js.
*/
// The ultimate Javascript app: AJAX auto-completion, which responds to up/down arrow keys, the enter key, and the escape key
// The idea was pilfered mercilessly from vBulletin, but uses about 8
// bytes of vB code. All the rest was coded by me, Mr. Javascript Newbie...
// ...in about 8 hours.
// You folks better like this stuff.
function nameCompleteEventHandler(e)
{
if(!e) e = window.event;
switch(e.keyCode)
{
case 38: // up
unSelectMove('up');
break;
case 40: // down
unSelectMove('down');
break;
case 27: // escape
case 9: // tab
destroyUsernameDropdowns();
break;
case 13: // enter
unSelect();
break;
default: return false; break;
}
return true;
}
function unSelectMove(dir)
{
if(submitAuthorized) return false;
var thediv = document.getElementById(unObjDivCurrentId);
thetable = thediv.firstChild;
cel = thetable.firstChild.firstChild;
d = true;
index = false;
changed = false;
// Object of the game: extract the username, determine its index in the userlist array, and then color the menu items and set unObjCurrentSelection
while(d) // Set to false if an exception occurs or if we arrive at our destination
{
//*
if(!cel) d=false;
celbak = cel;
cel = cel.nextSibling;
if(!cel) d=false;
try {
if(cel.firstChild.nextSibling) html = cel.firstChild.nextSibling.innerHTML;
else html = cel.firstChild.innerHTML;
cel.firstChild.className = 'row1';
if(cel.firstChild.nextSibling) cel.firstChild.nextSibling.className = 'row1';
thename = html.substr(7, html.length-15);
// FINALLY! we have extracted the username
// Now get its position in the userlist array
if(thename == unObjCurrentSelection)
{
index = parseInt(in_array(thename, userlist));
}
if(typeof(index) == 'number')
{
if(dir=='down')
n = index+1;
else if(dir == 'up')
n = index - 1;
// Try to trap moving the selection up or down beyond the top of bottom
if(n > userlist.length-1 || n < 0)
{
cel.firstChild.className = 'row2';
if(cel.firstChild.nextSibling) cel.firstChild.nextSibling.className = 'row2';
return;
}
if(dir=='down') no=cel.nextSibling;
else if(dir=='up') no=cel.previousSibling;
no.firstChild.className = 'row2';
if(no.firstChild.nextSibling) no.firstChild.nextSibling.className = 'row2';
if(no.firstChild.id)
{
scroll = getScrollOffset() + getHeight();
elemht = getElementHeight(no.firstChild.id);
elemoff = fetch_offset(no.firstChild);
whereto = elemoff['top'] + elemht;
if(whereto > scroll)
{
window.location.hash = '#'+no.firstChild.id;
unObj.focus();
}
}
cel=cel.nextSibling;
unObjCurrentSelection = userlist[n];
index = false;
changed = true;
return;
}
} catch(e) { }
//*/ d = false;
}
}
function unSelect()
{
if(!unObj || submitAuthorized) return false;
if ( unObjCurrentSelection )
unObj.value = unObjCurrentSelection;
destroyUsernameDropdowns();
}
function in_array(needle, haystack)
{
for(var i in haystack)
{
if(haystack[i] == needle) return i;
}
return false;
}
function ajaxUserNameComplete(o)
{
if(!o) {destroyUsernameDropdowns(); return;}
if(!o.value) {destroyUsernameDropdowns(); return;}
if(o.value.length < 3) {destroyUsernameDropdowns(); return;}
//if(IE) return; // This control doesn't work in IE. Yes, it's true! document.createElement doesn't work.
if(!o.id)
{
o.id = 'usernametextboxobj_' + Math.floor(Math.random() * 10000000);
}
unObj = o;
o.setAttribute("autocomplete","off");
o.onkeyup = function(e, o) { o=unObj; if(!nameCompleteEventHandler(e)) ajaxUserNameComplete(o); }
val = escape(o.value).replace('+', '%2B');
ajaxGet(stdAjaxPrefix+'&_mode=fillusername&name='+val, function()
{
if(ajax.readyState==4)
{
// Determine the appropriate left/top positions, then create a div to use for the drop-down list
// The trick here is to be able to make the div dynamically destroy itself depending on how far the user's mouse is from it
destroyUsernameDropdowns();
off = fetch_offset(unObj);
dim = fetch_dimensions(unObj);
left = off['left'];
i1 = off['top'];
i2 = dim['h'];
var top = 0;
top = i1 + i2;
var thediv = document.createElement('div');
thediv.className = 'tblholder';
thediv.style.marginTop = '0px';
thediv.style.position = 'absolute';
thediv.style.top = top + 'px';
thediv.style.left = left + 'px';
thediv.style.zIndex = getHighestZ() + 2;
id = 'usernamehoverobj_' + Math.floor(Math.random() * 10000000);
unObjDivCurrentId = id;
thediv.id = id;
unObj.onblur = function() { destroyUsernameDropdowns(); }
var response = String(ajax.responseText) + ' ';
if ( response.substr(0,1) != '{' )
{
new messagebox(MB_OK|MB_ICONSTOP, 'Invalid response', 'Invalid or unexpected JSON response from server:<pre>' + ajax.responseText + '</pre>');
return false;
}
response = parseJSON(response);
var errorstring = false;
if ( response.mode == 'error' )
{
errorstring = response.error;
}
else
{
var userlist = response.users_real;
}
if(errorstring)
{
html = '<span style="color: #555; padding: 4px;">'+errorstring+'</span>';
}
else
{
html = '<table border="0" cellspacing="1" cellpadding="3" style="width: auto;"><tr><th><small>Username matches</small></th></tr>';
cls = 'row2';
unObjCurrentSelection = userlist[0];
for(i=0;i<userlist.length;i++)
{
tmpnam = 'listobjnode_'+Math.floor(Math.random() * 10000000);
html = html + '<tr><td id="'+tmpnam+'" class="'+cls+'" style="cursor: pointer;" onclick="document.getElementById(\''+unObj.id+'\').value=\''+userlist[i]+'\';destroyUsernameDropdowns();"><small>'+userlist[i]+'</small></td></tr>';
if(cls=='row2') cls='row1';
}
html = html + '</table>';
}
thediv.innerHTML = html;
var body = document.getElementsByTagName('body');
body = body[0];
unSelectMenuOn = true;
submitAuthorized = false;
body.appendChild(thediv);
}
});
}
function ajaxPageNameComplete(o)
{
if(!o) {destroyUsernameDropdowns(); return;}
if(!o.value) {destroyUsernameDropdowns(); return;}
if(o.value.length < 3) {destroyUsernameDropdowns(); return;}
if(IE) return; // This control doesn't work in IE. Yes, it's true! document.createElement doesn't work.
if(!o.id)
{
o.id = 'usernametextboxobj_' + Math.floor(Math.random() * 10000000);
}
o.setAttribute("autocomplete","off");
unObj = o;
o.onkeyup = function(e, o) { o=unObj; if(!nameCompleteEventHandler(e)) ajaxPageNameComplete(o); }
val = escape(o.value).replace('+', '%2B');
ajaxGet(stdAjaxPrefix+'&_mode=fillpagename&name='+val, function()
{
if(!ajax) return;
if(ajax.readyState==4)
{
// Determine the appropriate left/top positions, then create a div to use for the drop-down list
// The trick here is to be able to make the div dynamically destroy itself depending on how far the user's mouse is from it
destroyUsernameDropdowns();
off = fetch_offset(unObj);
dim = fetch_dimensions(unObj);
left = off['left'];
top = off['top'] + dim['h'];
var thediv = document.createElement('div');
thediv.className = 'tblholder';
thediv.style.marginTop = '0px';
thediv.style.position = 'absolute';
thediv.style.top = top + 'px';
thediv.style.left = left + 'px';
thediv.style.zIndex = getHighestZ() + 2;
id = 'usernamehoverobj_' + Math.floor(Math.random() * 10000000);
unObjDivCurrentId = id;
thediv.id = id;
eval(ajax.responseText);
if(errorstring)
{
html = '<span style="color: #555; padding: 4px;">'+errorstring+'</span>';
}
else
{
html = '<table border="0" cellspacing="1" cellpadding="3" style="width: auto;"><tr><th colspan="2">Page name matches</th></tr><tr><th><small>Page title</small></th><th><small>Page ID</small></th></tr>';
cls = 'row2';
unObjCurrentSelection = userlist[0];
for(i=0;i<userlist.length;i++)
{
tmpnam = 'listobjnode_'+Math.floor(Math.random() * 10000000);
html = html + '<tr><td id="'+tmpnam+'" class="'+cls+'" style="cursor: pointer;" onclick="document.getElementById(\''+unObj.id+'\').value=\''+userlist[i]+'\';destroyUsernameDropdowns();"><small>'+namelist[i]+'</small></td><td class="'+cls+'" style="cursor: pointer;" onclick="document.getElementById(\''+unObj.id+'\').value=\''+userlist[i]+'\';destroyUsernameDropdowns();"><small>'+userlist[i]+'</small></td></tr>';
if(cls=='row2') cls='row1';
}
html = html + '</table>';
}
thediv.innerHTML = html;
var body = document.getElementsByTagName('body');
body = body[0];
unSelectMenuOn = true;
submitAuthorized = false;
body.appendChild(thediv);
}
});
}
function destroyUsernameDropdowns()
{
var divs = document.getElementsByTagName('div');
var prefix = 'usernamehoverobj_';
for(i=0;i<divs.length;i++)
{
if ( divs[i].id )
{
if(divs[i].id.substr(0, prefix.length)==prefix)
{
divs[i].innerHTML = '';
divs[i].style.display = 'none';
}
}
}
unSelectMenuOn = false;
unObjDivCurrentId = false;
unObjCurrentSelection = false;
submitAuthorized = true;
}
function get_parent_form(o)
{
if ( !o.parentNode )
return false;
if ( o.tagName == 'FORM' )
return o;
var p = o.parentNode;
while(true)
{
if ( p.tagName == 'FORM' )
return p;
else if ( !p )
return false;
else
p = p.parentNode;
}
}