diff -r 41c45314ac27 -r 5e8fd89c02ea includes/clientside/static/autofill.js --- 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: '
' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + '
{name}
' + "\n" + + '
', - 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: '
' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + '
' + $lang.get('user_autofill_heading_suggestions') + '
{name_highlight}
{rank_title}
' + "\n" + + '
', - 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:
' + ajax.responseText + '
'); - 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: '
' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + ' ' + "\n" + + '
' + $lang.get('page_autosuggest_heading') + '
{pid_highlight}
{name_highlight}
' + "\n" + + '
' } - 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... - // → - // ↑ ↓ - // 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... - // ← - // ↓ ↑ - // 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)