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
+ − 163
eval(ajax.responseText);
+ − 164
if(errorstring)
+ − 165
{
+ − 166
html = '<span style="color: #555; padding: 4px;">'+errorstring+'</span>';
+ − 167
}
+ − 168
else
+ − 169
{
+ − 170
html = '<table border="0" cellspacing="1" cellpadding="3" style="width: auto;"><tr><th><small>Username matches</small></th></tr>';
+ − 171
cls = 'row2';
+ − 172
unObjCurrentSelection = userlist[0];
+ − 173
for(i=0;i<userlist.length;i++)
+ − 174
{
+ − 175
tmpnam = 'listobjnode_'+Math.floor(Math.random() * 10000000);
+ − 176
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>';
+ − 177
if(cls=='row2') cls='row1';
+ − 178
}
+ − 179
html = html + '</table>';
+ − 180
}
+ − 181
+ − 182
thediv.innerHTML = html;
+ − 183
var body = document.getElementsByTagName('body');
+ − 184
body = body[0];
+ − 185
unSelectMenuOn = true;
+ − 186
submitAuthorized = false;
+ − 187
body.appendChild(thediv);
+ − 188
}
+ − 189
});
+ − 190
}
+ − 191
+ − 192
function ajaxPageNameComplete(o)
+ − 193
{
+ − 194
if(!o) {destroyUsernameDropdowns(); return;}
+ − 195
if(!o.value) {destroyUsernameDropdowns(); return;}
+ − 196
if(o.value.length < 3) {destroyUsernameDropdowns(); return;}
+ − 197
if(IE) return; // This control doesn't work in IE. Yes, it's true! document.createElement doesn't work.
+ − 198
if(!o.id)
+ − 199
{
+ − 200
o.id = 'usernametextboxobj_' + Math.floor(Math.random() * 10000000);
+ − 201
}
+ − 202
o.setAttribute("autocomplete","off");
+ − 203
unObj = o;
+ − 204
o.onkeyup = function(e, o) { o=unObj; if(!nameCompleteEventHandler(e)) ajaxPageNameComplete(o); }
+ − 205
val = escape(o.value).replace('+', '%2B');
+ − 206
ajaxGet(stdAjaxPrefix+'&_mode=fillpagename&name='+val, function()
+ − 207
{
+ − 208
if(!ajax) return;
+ − 209
if(ajax.readyState==4)
+ − 210
{
+ − 211
// Determine the appropriate left/top positions, then create a div to use for the drop-down list
+ − 212
// 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
+ − 213
destroyUsernameDropdowns();
+ − 214
off = fetch_offset(unObj);
+ − 215
dim = fetch_dimensions(unObj);
+ − 216
left = off['left'];
+ − 217
top = off['top'] + dim['h'];
+ − 218
var thediv = document.createElement('div');
+ − 219
thediv.className = 'tblholder';
+ − 220
thediv.style.marginTop = '0px';
+ − 221
thediv.style.position = 'absolute';
+ − 222
thediv.style.top = top + 'px';
+ − 223
thediv.style.left = left + 'px';
+ − 224
thediv.style.zIndex = getHighestZ() + 2;
+ − 225
id = 'usernamehoverobj_' + Math.floor(Math.random() * 10000000);
+ − 226
unObjDivCurrentId = id;
+ − 227
thediv.id = id;
+ − 228
+ − 229
eval(ajax.responseText);
+ − 230
if(errorstring)
+ − 231
{
+ − 232
html = '<span style="color: #555; padding: 4px;">'+errorstring+'</span>';
+ − 233
}
+ − 234
else
+ − 235
{
+ − 236
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>';
+ − 237
cls = 'row2';
+ − 238
unObjCurrentSelection = userlist[0];
+ − 239
for(i=0;i<userlist.length;i++)
+ − 240
{
+ − 241
tmpnam = 'listobjnode_'+Math.floor(Math.random() * 10000000);
+ − 242
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>';
+ − 243
if(cls=='row2') cls='row1';
+ − 244
}
+ − 245
html = html + '</table>';
+ − 246
}
+ − 247
+ − 248
thediv.innerHTML = html;
+ − 249
var body = document.getElementsByTagName('body');
+ − 250
body = body[0];
+ − 251
unSelectMenuOn = true;
+ − 252
submitAuthorized = false;
+ − 253
body.appendChild(thediv);
+ − 254
+ − 255
unObj.onblur = function() { CheckDestroyUsernameDropdowns(thediv.id); };
+ − 256
}
+ − 257
});
+ − 258
}
+ − 259
+ − 260
function CheckDestroyUsernameDropdowns(id)
+ − 261
{
+ − 262
elem = document.getElementById(id);
+ − 263
if(!elem) return;
+ − 264
if(queryOnObj(elem, 100))
+ − 265
{
+ − 266
destroyUsernameDropdowns();
+ − 267
}
+ − 268
}
+ − 269
+ − 270
function destroyUsernameDropdowns()
+ − 271
{
+ − 272
var divs = document.getElementsByTagName('div');
+ − 273
var prefix = 'usernamehoverobj_';
+ − 274
for(i=0;i<divs.length;i++)
+ − 275
{
+ − 276
if ( divs[i].id )
+ − 277
{
+ − 278
if(divs[i].id.substr(0, prefix.length)==prefix)
+ − 279
{
+ − 280
divs[i].innerHTML = '';
+ − 281
divs[i].style.display = 'none';
+ − 282
}
+ − 283
}
+ − 284
}
+ − 285
unSelectMenuOn = false;
+ − 286
unObjDivCurrentId = false;
+ − 287
unObjCurrentSelection = false;
+ − 288
submitAuthorized = true;
+ − 289
}
+ − 290
+ − 291
function get_parent_form(o)
+ − 292
{
+ − 293
if ( !o.parentNode )
+ − 294
return false;
+ − 295
if ( o.tagName == 'FORM' )
+ − 296
return o;
+ − 297
var p = o.parentNode;
+ − 298
while(true)
+ − 299
{
+ − 300
if ( p.tagName == 'FORM' )
+ − 301
return p;
+ − 302
else if ( !p )
+ − 303
return false;
+ − 304
else
+ − 305
p = p.parentNode;
+ − 306
}
+ − 307
}
+ − 308