1
+ − 1
/*
+ − 2
* Auto-completing page/username fields
212
d57af0b0302e
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
Dan
diff
changeset
+ − 3
* NOTE: A more efficient version of the username field is used for Mozilla browsers. The updated code is in autofill.js.
1
+ − 4
*/
+ − 5
+ − 6
// The ultimate Javascript app: AJAX auto-completion, which responds to up/down arrow keys, the enter key, and the escape key
+ − 7
// The idea was pilfered mercilessly from vBulletin, but uses about 8
+ − 8
// bytes of vB code. All the rest was coded by me, Mr. Javascript Newbie...
+ − 9
+ − 10
// ...in about 8 hours.
+ − 11
// You folks better like this stuff.
+ − 12
+ − 13
function nameCompleteEventHandler(e)
+ − 14
{
+ − 15
if(!e) e = window.event;
+ − 16
switch(e.keyCode)
+ − 17
{
+ − 18
case 38: // up
+ − 19
unSelectMove('up');
+ − 20
break;
+ − 21
case 40: // down
+ − 22
unSelectMove('down');
+ − 23
break;
+ − 24
case 27: // escape
+ − 25
case 9: // tab
+ − 26
destroyUsernameDropdowns();
+ − 27
break;
+ − 28
case 13: // enter
+ − 29
unSelect();
+ − 30
break;
+ − 31
default: return false; break;
+ − 32
}
+ − 33
return true;
+ − 34
}
+ − 35
+ − 36
function unSelectMove(dir)
+ − 37
{
+ − 38
if(submitAuthorized) return false;
+ − 39
var thediv = document.getElementById(unObjDivCurrentId);
+ − 40
thetable = thediv.firstChild;
+ − 41
cel = thetable.firstChild.firstChild;
+ − 42
d = true;
+ − 43
index = false;
+ − 44
changed = false;
+ − 45
// Object of the game: extract the username, determine its index in the userlist array, and then color the menu items and set unObjCurrentSelection
+ − 46
while(d) // Set to false if an exception occurs or if we arrive at our destination
+ − 47
{
+ − 48
//*
+ − 49
if(!cel) d=false;
+ − 50
celbak = cel;
+ − 51
cel = cel.nextSibling;
+ − 52
if(!cel) d=false;
+ − 53
try {
+ − 54
if(cel.firstChild.nextSibling) html = cel.firstChild.nextSibling.innerHTML;
+ − 55
else html = cel.firstChild.innerHTML;
+ − 56
cel.firstChild.className = 'row1';
+ − 57
if(cel.firstChild.nextSibling) cel.firstChild.nextSibling.className = 'row1';
+ − 58
thename = html.substr(7, html.length-15);
+ − 59
// FINALLY! we have extracted the username
+ − 60
// Now get its position in the userlist array
+ − 61
if(thename == unObjCurrentSelection)
+ − 62
{
+ − 63
index = parseInt(in_array(thename, userlist));
+ − 64
}
+ − 65
if(typeof(index) == 'number')
+ − 66
{
+ − 67
if(dir=='down')
+ − 68
n = index+1;
+ − 69
else if(dir == 'up')
+ − 70
n = index - 1;
+ − 71
+ − 72
// Try to trap moving the selection up or down beyond the top of bottom
+ − 73
if(n > userlist.length-1 || n < 0)
+ − 74
{
+ − 75
cel.firstChild.className = 'row2';
+ − 76
if(cel.firstChild.nextSibling) cel.firstChild.nextSibling.className = 'row2';
+ − 77
return;
+ − 78
}
+ − 79
+ − 80
if(dir=='down') no=cel.nextSibling;
+ − 81
else if(dir=='up') no=cel.previousSibling;
+ − 82
no.firstChild.className = 'row2';
+ − 83
if(no.firstChild.nextSibling) no.firstChild.nextSibling.className = 'row2';
+ − 84
if(no.firstChild.id)
+ − 85
{
+ − 86
scroll = getScrollOffset() + getHeight();
+ − 87
elemht = getElementHeight(no.firstChild.id);
+ − 88
elemoff = fetch_offset(no.firstChild);
+ − 89
whereto = elemoff['top'] + elemht;
+ − 90
if(whereto > scroll)
+ − 91
{
+ − 92
window.location.hash = '#'+no.firstChild.id;
+ − 93
unObj.focus();
+ − 94
}
+ − 95
}
+ − 96
cel=cel.nextSibling;
+ − 97
unObjCurrentSelection = userlist[n];
+ − 98
index = false;
+ − 99
changed = true;
+ − 100
return;
+ − 101
}
+ − 102
} catch(e) { }
+ − 103
//*/ d = false;
+ − 104
}
+ − 105
}
+ − 106
+ − 107
function unSelect()
+ − 108
{
+ − 109
if(!unObj || submitAuthorized) return false;
+ − 110
if ( unObjCurrentSelection )
+ − 111
unObj.value = unObjCurrentSelection;
+ − 112
destroyUsernameDropdowns();
+ − 113
}
+ − 114
+ − 115
function in_array(needle, haystack)
+ − 116
{
+ − 117
for(var i in haystack)
+ − 118
{
+ − 119
if(haystack[i] == needle) return i;
+ − 120
}
+ − 121
return false;
+ − 122
}
+ − 123
+ − 124
function ajaxUserNameComplete(o)
+ − 125
{
+ − 126
if(!o) {destroyUsernameDropdowns(); return;}
+ − 127
if(!o.value) {destroyUsernameDropdowns(); return;}
+ − 128
if(o.value.length < 3) {destroyUsernameDropdowns(); return;}
+ − 129
//if(IE) return; // This control doesn't work in IE. Yes, it's true! document.createElement doesn't work.
+ − 130
if(!o.id)
+ − 131
{
+ − 132
o.id = 'usernametextboxobj_' + Math.floor(Math.random() * 10000000);
+ − 133
}
+ − 134
unObj = o;
+ − 135
o.setAttribute("autocomplete","off");
+ − 136
o.onkeyup = function(e, o) { o=unObj; if(!nameCompleteEventHandler(e)) ajaxUserNameComplete(o); }
+ − 137
val = escape(o.value).replace('+', '%2B');
+ − 138
ajaxGet(stdAjaxPrefix+'&_mode=fillusername&name='+val, function()
+ − 139
{
+ − 140
if(ajax.readyState==4)
+ − 141
{
+ − 142
// Determine the appropriate left/top positions, then create a div to use for the drop-down list
+ − 143
// 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
+ − 144
destroyUsernameDropdowns();
+ − 145
off = fetch_offset(unObj);
+ − 146
dim = fetch_dimensions(unObj);
+ − 147
left = off['left'];
+ − 148
i1 = off['top'];
+ − 149
i2 = dim['h'];
+ − 150
var top = 0;
+ − 151
top = i1 + i2;
+ − 152
var thediv = document.createElement('div');
+ − 153
thediv.className = 'tblholder';
+ − 154
thediv.style.marginTop = '0px';
+ − 155
thediv.style.position = 'absolute';
+ − 156
thediv.style.top = top + 'px';
+ − 157
thediv.style.left = left + 'px';
+ − 158
thediv.style.zIndex = getHighestZ() + 2;
+ − 159
id = 'usernamehoverobj_' + Math.floor(Math.random() * 10000000);
+ − 160
unObjDivCurrentId = id;
+ − 161
thediv.id = id;
+ − 162
unObj.onblur = function() { destroyUsernameDropdowns(); }
+ − 163
174
+ − 164
var response = String(ajax.responseText) + ' ';
+ − 165
if ( response.substr(0,1) != '{' )
+ − 166
{
+ − 167
new messagebox(MB_OK|MB_ICONSTOP, 'Invalid response', 'Invalid or unexpected JSON response from server:<pre>' + ajax.responseText + '</pre>');
+ − 168
return false;
+ − 169
}
+ − 170
+ − 171
response = parseJSON(response);
+ − 172
var errorstring = false;
+ − 173
if ( response.mode == 'error' )
+ − 174
{
+ − 175
errorstring = response.error;
+ − 176
}
+ − 177
else
+ − 178
{
+ − 179
var userlist = response.users_real;
+ − 180
}
+ − 181
1
+ − 182
if(errorstring)
+ − 183
{
+ − 184
html = '<span style="color: #555; padding: 4px;">'+errorstring+'</span>';
+ − 185
}
+ − 186
else
+ − 187
{
+ − 188
html = '<table border="0" cellspacing="1" cellpadding="3" style="width: auto;"><tr><th><small>Username matches</small></th></tr>';
+ − 189
cls = 'row2';
+ − 190
unObjCurrentSelection = userlist[0];
+ − 191
for(i=0;i<userlist.length;i++)
+ − 192
{
+ − 193
tmpnam = 'listobjnode_'+Math.floor(Math.random() * 10000000);
+ − 194
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>';
+ − 195
if(cls=='row2') cls='row1';
+ − 196
}
+ − 197
html = html + '</table>';
+ − 198
}
+ − 199
+ − 200
thediv.innerHTML = html;
+ − 201
var body = document.getElementsByTagName('body');
+ − 202
body = body[0];
+ − 203
unSelectMenuOn = true;
+ − 204
submitAuthorized = false;
+ − 205
body.appendChild(thediv);
+ − 206
}
+ − 207
});
+ − 208
}
+ − 209
+ − 210
function ajaxPageNameComplete(o)
+ − 211
{
+ − 212
if(!o) {destroyUsernameDropdowns(); return;}
+ − 213
if(!o.value) {destroyUsernameDropdowns(); return;}
+ − 214
if(o.value.length < 3) {destroyUsernameDropdowns(); return;}
+ − 215
if(IE) return; // This control doesn't work in IE. Yes, it's true! document.createElement doesn't work.
+ − 216
if(!o.id)
+ − 217
{
+ − 218
o.id = 'usernametextboxobj_' + Math.floor(Math.random() * 10000000);
+ − 219
}
+ − 220
o.setAttribute("autocomplete","off");
+ − 221
unObj = o;
+ − 222
o.onkeyup = function(e, o) { o=unObj; if(!nameCompleteEventHandler(e)) ajaxPageNameComplete(o); }
+ − 223
val = escape(o.value).replace('+', '%2B');
+ − 224
ajaxGet(stdAjaxPrefix+'&_mode=fillpagename&name='+val, function()
+ − 225
{
+ − 226
if(!ajax) return;
+ − 227
if(ajax.readyState==4)
+ − 228
{
+ − 229
// Determine the appropriate left/top positions, then create a div to use for the drop-down list
+ − 230
// 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
+ − 231
destroyUsernameDropdowns();
+ − 232
off = fetch_offset(unObj);
+ − 233
dim = fetch_dimensions(unObj);
+ − 234
left = off['left'];
+ − 235
top = off['top'] + dim['h'];
+ − 236
var thediv = document.createElement('div');
+ − 237
thediv.className = 'tblholder';
+ − 238
thediv.style.marginTop = '0px';
+ − 239
thediv.style.position = 'absolute';
+ − 240
thediv.style.top = top + 'px';
+ − 241
thediv.style.left = left + 'px';
+ − 242
thediv.style.zIndex = getHighestZ() + 2;
+ − 243
id = 'usernamehoverobj_' + Math.floor(Math.random() * 10000000);
+ − 244
unObjDivCurrentId = id;
+ − 245
thediv.id = id;
+ − 246
+ − 247
eval(ajax.responseText);
+ − 248
if(errorstring)
+ − 249
{
+ − 250
html = '<span style="color: #555; padding: 4px;">'+errorstring+'</span>';
+ − 251
}
+ − 252
else
+ − 253
{
+ − 254
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>';
+ − 255
cls = 'row2';
+ − 256
unObjCurrentSelection = userlist[0];
+ − 257
for(i=0;i<userlist.length;i++)
+ − 258
{
+ − 259
tmpnam = 'listobjnode_'+Math.floor(Math.random() * 10000000);
+ − 260
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>';
+ − 261
if(cls=='row2') cls='row1';
+ − 262
}
+ − 263
html = html + '</table>';
+ − 264
}
+ − 265
+ − 266
thediv.innerHTML = html;
+ − 267
var body = document.getElementsByTagName('body');
+ − 268
body = body[0];
+ − 269
unSelectMenuOn = true;
+ − 270
submitAuthorized = false;
+ − 271
body.appendChild(thediv);
+ − 272
}
+ − 273
});
+ − 274
}
+ − 275
+ − 276
function destroyUsernameDropdowns()
+ − 277
{
+ − 278
var divs = document.getElementsByTagName('div');
+ − 279
var prefix = 'usernamehoverobj_';
+ − 280
for(i=0;i<divs.length;i++)
+ − 281
{
+ − 282
if ( divs[i].id )
+ − 283
{
+ − 284
if(divs[i].id.substr(0, prefix.length)==prefix)
+ − 285
{
+ − 286
divs[i].innerHTML = '';
+ − 287
divs[i].style.display = 'none';
+ − 288
}
+ − 289
}
+ − 290
}
+ − 291
unSelectMenuOn = false;
+ − 292
unObjDivCurrentId = false;
+ − 293
unObjCurrentSelection = false;
+ − 294
submitAuthorized = true;
+ − 295
}
+ − 296
+ − 297
function get_parent_form(o)
+ − 298
{
+ − 299
if ( !o.parentNode )
+ − 300
return false;
+ − 301
if ( o.tagName == 'FORM' )
+ − 302
return o;
+ − 303
var p = o.parentNode;
+ − 304
while(true)
+ − 305
{
+ − 306
if ( p.tagName == 'FORM' )
+ − 307
return p;
+ − 308
else if ( !p )
+ − 309
return false;
+ − 310
else
+ − 311
p = p.parentNode;
+ − 312
}
+ − 313
}
+ − 314