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