Added a copy of Firebug Lite for debugging purposes. License is uncertain but being treated as MPL. (If is is not MPL then it is under something more permissive that permits relicensing anyway)
/**
* Javascript auto-completion for form fields. This supercedes the code in autocomplete.js for MOZILLA ONLY. It doesn't seem to work real
* well with other browsers yet.
*/
var af_current = false;
function AutofillUsername(parent, event, allowanon)
{
// if this is IE, use the old code
if ( IE )
{
ajaxUserNameComplete(parent);
return false;
}
if ( parent.afobj )
{
parent.afobj.go();
return true;
}
parent.autocomplete = 'off';
parent.setAttribute('autocomplete', 'off');
this.repeat = false;
this.event = event;
this.box_id = false;
this.boxes = new Array();
this.state = false;
this.allowanon = ( allowanon ) ? true : false;
if ( !parent.id )
parent.id = 'afuser_' + Math.floor(Math.random() * 1000000);
this.field_id = parent.id;
// constants
this.KEY_UP = 38;
this.KEY_DOWN = 40;
this.KEY_ESC = 27;
this.KEY_TAB = 9;
this.KEY_ENTER = 13;
// response cache
this.responses = new Object();
// ajax placeholder
this.process_dataset = function(resp_json)
{
// window.console.info('Processing the following dataset.');
// window.console.debug(resp_json);
var autofill = this;
if ( typeof(autofill.event) == 'object' )
{
if ( autofill.event.keyCode )
{
if ( autofill.event.keyCode == autofill.KEY_ENTER && autofill.boxes.length < 1 && !autofill.box_id )
{
// user hit enter after accepting a suggestion - submit the form
var frm = findParentForm($dynano(autofill.field_id).object);
frm._af_acting = false;
frm.submit();
// window.console.info('Submitting form');
return false;
}
if ( autofill.event.keyCode == autofill.KEY_UP || autofill.event.keyCode == autofill.KEY_DOWN || autofill.event.keyCode == autofill.KEY_ESC || autofill.event.keyCode == autofill.KEY_TAB || autofill.event.keyCode == autofill.KEY_ENTER )
{
autofill.keyhandler();
// window.console.info('Control key detected, called keyhandler and exiting');
return true;
}
}
}
if ( this.box_id )
{
this.destroy();
// window.console.info('already have a box open - destroying and exiting');
//return false;
}
var users = new Array();
for ( var i = 0; i < resp_json.users_real.length; i++ )
{
try
{
var user = resp_json.users_real[i].toLowerCase();
var inp = $dynano(autofill.field_id).object.value;
inp = inp.toLowerCase();
if ( user.indexOf(inp) > -1 )
{
users.push(resp_json.users_real[i]);
}
}
catch(e)
{
users.push(resp_json.users_real[i]);
}
}
// This was used ONLY for debugging the DOM and list logic
// resp_json.users = resp_json.users_real;
// construct table
var div = document.createElement('div');
div.className = 'tblholder';
div.style.clip = 'rect(0px,auto,auto,0px)';
div.style.maxHeight = '200px';
div.style.overflow = 'auto';
div.style.zIndex = '9999';
var table = document.createElement('table');
table.border = '0';
table.cellSpacing = '1';
table.cellPadding = '3';
var tr = document.createElement('tr');
var th = document.createElement('th');
th.appendChild(document.createTextNode($lang.get('user_autofill_heading_suggestions')));
tr.appendChild(th);
table.appendChild(tr);
if ( users.length < 1 )
{
var tr = document.createElement('tr');
var td = document.createElement('td');
td.className = 'row1';
td.appendChild(document.createTextNode($lang.get('user_autofill_msg_no_suggestions')));
td.afobj = autofill;
tr.appendChild(td);
table.appendChild(tr);
}
else
for ( var i = 0; i < users.length; i++ )
{
var user = users[i];
var tr = document.createElement('tr');
var td = document.createElement('td');
td.className = ( i == 0 ) ? 'row2' : 'row1';
td.appendChild(document.createTextNode(user));
td.afobj = autofill;
td.style.cursor = 'pointer';
td.onclick = function()
{
this.afobj.set(this.firstChild.nodeValue);
}
tr.appendChild(td);
table.appendChild(tr);
}
// Finalize div
var tb_top = $dynano(autofill.field_id).Top();
var tb_height = $dynano(autofill.field_id).Height();
var af_top = tb_top + tb_height - 9;
var tb_left = $dynano(autofill.field_id).Left();
var af_left = tb_left;
div.style.position = 'absolute';
div.style.left = af_left + 'px';
div.style.top = af_top + 'px';
div.style.width = '200px';
div.style.fontSize = '7pt';
div.style.fontFamily = 'Trebuchet MS, arial, helvetica, sans-serif';
div.id = 'afuserdrop_' + Math.floor(Math.random() * 1000000);
div.appendChild(table);
autofill.boxes.push(div.id);
autofill.box_id = div.id;
if ( users.length > 0 )
autofill.state = users[0];
var body = document.getElementsByTagName('body')[0];
body.appendChild(div);
autofill.repeat = true;
}
// perform ajax call
this.fetch_and_process = function()
{
af_current = this;
var processResponse = function()
{
if ( ajax.readyState == 4 && ajax.status == 200 )
{
var afobj = af_current;
af_current = false;
// parse the JSON response
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;
}
if ( $dynano(afobj.field_id).object.value.length < 3 )
return false;
var resp_json = parseJSON(response);
var resp_code = $dynano(afobj.field_id).object.value.toLowerCase().substr(0, 3);
afobj.responses[resp_code] = resp_json;
afobj.process_dataset(resp_json);
}
}
var usernamefragment = ajaxEscape($dynano(this.field_id).object.value);
ajaxGet(stdAjaxPrefix + '&_mode=fillusername&name=' + usernamefragment + '&allowanon=' + ( this.allowanon ? '1' : '0' ), processResponse);
}
this.go = function()
{
if ( document.getElementById(this.field_id).value.length < 3 )
{
this.destroy();
return false;
}
if ( af_current )
return false;
var resp_code = $dynano(this.field_id).object.value.toLowerCase().substr(0, 3);
if ( this.responses.length < 1 || ! this.responses[ resp_code ] )
{
// window.console.info('Cannot find dataset ' + resp_code + ' in cache, sending AJAX request');
this.fetch_and_process();
}
else
{
// window.console.info('Using cached dataset: ' + resp_code);
var resp_json = this.responses[ resp_code ];
this.process_dataset(resp_json);
}
document.getElementById(this.field_id).onkeyup = function(event)
{
this.afobj.event = event;
this.afobj.go();
}
document.getElementById(this.field_id).onkeydown = function(event)
{
var form = findParentForm(this);
if ( typeof(event) != 'object' )
var event = window.event;
if ( typeof(event) == 'object' )
{
if ( event.keyCode == this.afobj.KEY_ENTER && this.afobj.boxes.length < 1 && !this.afobj.box_id )
{
// user hit enter after accepting a suggestion - submit the form
form._af_acting = false;
return true;
}
else
{
form._af_acting = true;
return true;
}
}
}
}
this.keyhandler = function()
{
var key = this.event.keyCode;
if ( key == this.KEY_ENTER && !this.repeat )
{
submitAuthorized = true;
var form = findParentForm($dynano(this.field_id).object);
form._af_acting = false;
return true;
}
switch(key)
{
case this.KEY_UP:
this.focus_up();
break;
case this.KEY_DOWN:
this.focus_down();
break;
case this.KEY_ESC:
this.destroy();
break;
case this.KEY_TAB:
this.destroy();
break;
case this.KEY_ENTER:
this.set();
break;
}
var form = findParentForm($dynano(this.field_id).object);
form._af_acting = false;
}
this.get_state_td = function()
{
var div = document.getElementById(this.box_id);
if ( !div )
return false;
if ( !this.state )
return false;
var table = div.firstChild;
for ( var i = 1; i < table.childNodes.length; i++ )
{
// the table is DOM-constructed so no cruddy HTML hacks :-)
var child = table.childNodes[i];
var tn = child.firstChild.firstChild;
if ( tn.nodeValue == this.state )
return child.firstChild;
}
return false;
}
this.focus_down = function()
{
var state_td = this.get_state_td();
if ( !state_td )
return false;
if ( state_td.parentNode.nextSibling )
{
// Ooh boy, DOM stuff can be so complicated...
// <tr> → <tr>
// ↑ <td> <td> ↓
// user user
var newstate = state_td.parentNode.nextSibling.firstChild.firstChild.nodeValue;
if ( !newstate )
return false;
this.state = newstate;
state_td.className = 'row1';
state_td.parentNode.nextSibling.firstChild.className = 'row2';
// Exception - automatically scroll around if the item is off-screen
var height = $dynano(this.box_id).Height();
var top = $dynano(this.box_id).object.scrollTop;
var scroll_bottom = height + top;
var td_top = $dynano(state_td.parentNode.nextSibling.firstChild).Top() - $dynano(this.box_id).Top();
var td_height = $dynano(state_td.parentNode.nextSibling.firstChild).Height();
var td_bottom = td_top + td_height;
if ( td_bottom > scroll_bottom )
{
var scrollY = td_top - height + 2*td_height - 7;
// window.console.debug(scrollY);
$dynano(this.box_id).object.scrollTop = scrollY;
/*
var newtd = state_td.parentNode.nextSibling.firstChild;
var a = document.createElement('a');
var id = 'autofill' + Math.floor(Math.random() * 100000);
a.name = id;
a.id = id;
newtd.appendChild(a);
window.location.hash = '#' + id;
*/
// In firefox, scrolling like that makes the field get unfocused
$dynano(this.field_id).object.focus();
}
}
else
{
return false;
}
}
this.focus_up = function()
{
var state_td = this.get_state_td();
if ( !state_td )
return false;
if ( state_td.parentNode.previousSibling && state_td.parentNode.previousSibling.firstChild.tagName != 'TH' )
{
// Ooh boy, DOM stuff can be so complicated...
// <tr> ← <tr>
// ↓ <td> <td> ↑
// user user
var newstate = state_td.parentNode.previousSibling.firstChild.firstChild.nodeValue;
if ( !newstate )
{
return false;
}
this.state = newstate;
state_td.className = 'row1';
state_td.parentNode.previousSibling.firstChild.className = 'row2';
// Exception - automatically scroll around if the item is off-screen
var top = $dynano(this.box_id).object.scrollTop;
var td_top = $dynano(state_td.parentNode.previousSibling.firstChild).Top() - $dynano(this.box_id).Top();
if ( td_top < top )
{
$dynano(this.box_id).object.scrollTop = td_top - 10;
/*
var newtd = state_td.parentNode.previousSibling.firstChild;
var a = document.createElement('a');
var id = 'autofill' + Math.floor(Math.random() * 100000);
a.name = id;
a.id = id;
newtd.appendChild(a);
window.location.hash = '#' + id;
*/
// In firefox, scrolling like that makes the field get unfocused
$dynano(this.field_id).object.focus();
}
}
else
{
$dynano(this.box_id).object.scrollTop = 0;
return false;
}
}
this.destroy = function()
{
this.repeat = false;
var body = document.getElementsByTagName('body')[0];
var div = document.getElementById(this.box_id);
if ( !div )
return false;
setTimeout('var body = document.getElementsByTagName("body")[0]; body.removeChild(document.getElementById("'+div.id+'"));', 20);
// hackish workaround for divs that stick around past their welcoming period
for ( var i = 0; i < this.boxes.length; i++ )
{
var div = document.getElementById(this.boxes[i]);
if ( div )
setTimeout('var body = document.getElementsByTagName("body")[0]; var div = document.getElementById("'+div.id+'"); if ( div ) body.removeChild(div);', 20);
delete(this.boxes[i]);
}
this.boxes = new Array();
this.box_id = false;
this.state = false;
}
this.set = function(val)
{
var ta = document.getElementById(this.field_id);
if ( val )
ta.value = val;
else if ( this.state )
ta.value = this.state;
this.destroy();
findParentForm($dynano(this.field_id.object))._af_acting = false;
}
this.sleep = function()
{
if ( this.box_id )
{
var div = document.getElementById(this.box_id);
div.style.display = 'none';
}
var el = $dynano(this.field_id).object;
var fr = findParentForm(el);
el._af_acting = false;
}
this.wake = function()
{
if ( this.box_id )
{
var div = document.getElementById(this.box_id);
div.style.display = 'block';
}
}
parent.onblur = function()
{
af_current = this.afobj;
window.setTimeout('if ( af_current ) af_current.sleep(); af_current = false;', 50);
}
parent.onfocus = function()
{
af_current = this.afobj;
window.setTimeout('if ( af_current ) af_current.wake(); af_current = false;', 50);
}
parent.afobj = this;
var frm = findParentForm(parent);
if ( frm.onsubmit )
{
frm.orig_onsubmit = frm.onsubmit;
frm.onsubmit = function(e)
{
if ( this._af_acting )
return false;
this.orig_onsubmit(e);
}
}
else
{
frm.onsubmit = function()
{
if ( this._af_acting )
return false;
}
}
if ( parent.value.length < 3 )
{
this.destroy();
return false;
}
}
function findParentForm(o)
{
if ( o.tagName == 'FORM' )
return o;
while(true)
{
o = o.parentNode;
if ( !o )
return false;
if ( o.tagName == 'FORM' )
return o;
}
return false;
}