--- a/includes/clientside/static/autofill.js Wed Jun 18 22:43:16 2008 -0400
+++ b/includes/clientside/static/autofill.js Sun Jun 22 18:13:59 2008 -0400
@@ -2,505 +2,145 @@
* 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');
+
+// fill schemas
+var autofill_schemas = {};
+
+// default, generic schema
+autofill_schemas.generic = {
+ template: '<div id="--ID--_region" spry:region="autofill_region_--ID--" class="tblholder">' + "\n" +
+ ' <table border="0" cellspacing="1" cellpadding="3" style="font-size: smaller;">' + "\n" +
+ ' <tr spry:repeat="autofill_region_--ID--">' + "\n" +
+ ' <td class="row1" spry:suggest="{name}">{name}</td>' + "\n" +
+ ' </tr>' + "\n" +
+ ' </table>' + "\n" +
+ '</div>',
- 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)
+ init: function(element, fillclass)
{
- // window.console.info('Processing the following dataset.');
- // window.console.debug(resp_json);
- var autofill = this;
+ // setup the dataset
+ window["autofill_region_" + element.id] = new Spry.Data.JSONDataSet(makeUrlNS('Special', 'Autofill', 'type=' + fillclass));
- 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;
- }
- }
- }
+ // inject our HTML wrapper
+ var template = this.template.replace(new RegExp('--ID--', 'g'), element.id);
+ var wrapper = element.parentNode; // document.createElement('div');
+ wrapper.id = 'autofill_wrap_' + element.id;
- 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;
+ // a bunch of hacks to add a spry wrapper
+ // var el2 = element.cloneNode(false);
+ // wrapper.appendChild(el2);
+ wrapper.innerHTML += template;
+ // insertAfter(element.parentNode, wrapper, element);
+ // element.parentNode.removeChild(element);
+ // element = el2;
- // 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 autosuggest = new Spry.Widget.AutoSuggest("autofill_wrap_" + element.id, element.id + '_region', window["autofill_region_" + element.id], 'name', {loadFromServer: true, urlParam: 'userinput', hoverSuggestClass: 'row2', minCharsType: 3});
+ }
+};
+
+function autofill_init_element(element, params)
+{
+ if ( element.parentNode.id.match(/^autofill_wrap_/) )
+ return false;
+
+ params = params || {};
+ // assign an ID if it doesn't have one yet
+ if ( !element.id )
+ {
+ element.id = 'autofill_' + Math.floor(Math.random() * 100000);
+ }
+ var id = element.id;
+
+ // get the fill type
+ var fillclass = element.className;
+ fillclass = fillclass.split(' ');
+ fillclass = fillclass[1];
+
+ var schema = ( autofill_schemas[fillclass] ) ? autofill_schemas[fillclass] : autofill_schemas['generic'];
+ if ( typeof(schema.init) != 'function' )
+ {
+ schema.init = autofill_schemas.generic.init;
+ }
+ schema.init(element, fillclass, params);
+
+ element.af_initted = true;
+}
+
+var autofill_onload = function()
+{
+ autofill_schemas.username = {
+ template: '<div id="--ID--_region" spry:region="autofill_region_--ID--" class="tblholder">' + "\n" +
+ ' <table border="0" cellspacing="1" cellpadding="3" style="font-size: smaller;">' + "\n" +
+ ' <tr>' + "\n" +
+ ' <th>' + $lang.get('user_autofill_heading_suggestions') + '</th>' + "\n" +
+ ' </tr>' + "\n" +
+ ' <tr spry:repeat="autofill_region_--ID--">' + "\n" +
+ ' <td class="row1" spry:suggest="{name}">{name_highlight}<br /><small style="{rank_style}">{rank_title}</small></td>' + "\n" +
+ ' </tr>' + "\n" +
+ ' </table>' + "\n" +
+ '</div>',
- 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 )
+ init: function(element, fillclass, params)
{
- 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);
- }
+ // calculate positions before spry f***s everything up
+ var top = $dynano(element).Top() + $dynano(element).Height() - 10; // tblholder has 10px top margin
+ var left = $dynano(element).Left();
- // 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;
- }
+ var allow_anon = ( params.allow_anon ) ? '1' : '0';
+ // setup the dataset
+ window["autofill_region_" + element.id] = new Spry.Data.JSONDataSet(makeUrlNS('Special', 'Autofill', 'type=' + fillclass + '&allow_anon' + allow_anon));
+ Spry.Data.initRegions(document.body);
+ (window["autofill_region_" + element.id]).loadData();
+
+ // inject our HTML wrapper
+ var template = this.template.replace(new RegExp('--ID--', 'g'), element.id);
+ var wrapper = element.parentNode; // document.createElement('div');
+ wrapper.id = 'autofill_wrap_' + element.id;
+
+ // a bunch of hacks to add a spry wrapper
+ wrapper.innerHTML = template + wrapper.innerHTML;
+
+ var autosuggest = new Spry.Widget.AutoSuggest("autofill_wrap_" + element.id, element.id + '_region', window["autofill_region_" + element.id], 'name', {loadFromServer: true, urlParam: 'userinput', hoverSuggestClass: 'row2', minCharsType: 3});
+ var regiondiv = document.getElementById(element.id + '_region');
+ regiondiv.style.position = 'absolute';
+ regiondiv.style.top = top + 'px';
+ regiondiv.style.left = left + 'px';
+ }
+ };
- // 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);
+ autofill_schemas.page = {
+ template: '<div id="--ID--_region" spry:region="autofill_region_--ID--" class="tblholder">' + "\n" +
+ ' <table border="0" cellspacing="1" cellpadding="3" style="font-size: smaller;">' + "\n" +
+ ' <tr>' + "\n" +
+ ' <th colspan="2">' + $lang.get('page_autosuggest_heading') + '</th>' + "\n" +
+ ' </tr>' + "\n" +
+ ' <tr spry:repeat="autofill_region_--ID--">' + "\n" +
+ ' <td class="row1" spry:suggest="{page_id}">{pid_highlight}<br /><small>{name_highlight}</small></td>' + "\n" +
+ ' </tr>' + "\n" +
+ ' </table>' + "\n" +
+ '</div>'
}
- 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;
- }
- }
- }
- }
+ var inputs = document.getElementsByClassName('input', 'autofill');
- 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()
+ for ( var i = 0; i < inputs.length; i++ )
{
- 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;
- }
+ autofill_init_element(inputs[i]);
}
+}
+
+addOnloadHook(autofill_onload);
+
+function AutofillUsername(element, event, allowanon)
+{
+ element.onkeyup = element.onkeydown = element.onkeypress = function(e) {};
- 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;
- }
+ element.className = 'autofill username';
- 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;
- }
+ allowanon = allowanon ? true : false;
+ autofill_init_element(element, {
+ allow_anon: allowanon
+ });
}
function findParentForm(o)