Fixed search indexer causing duplicate keys when two "words" of 64+ characters encountered and first 64 characters are the same (thanks Vadi); attempt to fix onunload confirmation during page editing
/** * 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($(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 = $(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('Username 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('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 = $(autofill.field_id).Top(); var tb_height = $(autofill.field_id).Height(); var af_top = tb_top + tb_height - 9; var tb_left = $(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 ) { 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 ( $(afobj.field_id).object.value.length < 3 ) return false; var resp_json = parseJSON(response); var resp_code = $(afobj.field_id).object.value.toLowerCase().substr(0, 3); afobj.responses[resp_code] = resp_json; afobj.process_dataset(resp_json); } } var usernamefragment = ajaxEscape($(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 = $(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($(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($(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 = $(this.box_id).Height(); var top = $(this.box_id).object.scrollTop; var scroll_bottom = height + top; var td_top = $(state_td.parentNode.nextSibling.firstChild).Top() - $(this.box_id).Top(); var td_height = $(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); $(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 $(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 = $(this.box_id).object.scrollTop; var td_top = $(state_td.parentNode.previousSibling.firstChild).Top() - $(this.box_id).Top(); if ( td_top < top ) { $(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 $(this.field_id).object.focus(); } } else { $(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($(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 = $(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;}