Finished theme manager to the point where it's in a working state in Firefox and hopefully IE.
+ − /*
+ − * Auto-completing page/username fields
+ − * NOTE: A more efficient version of the username field is used for Mozilla browsers. The updated code is in autofill.js.
+ − */
+ −
+ − // The ultimate Javascript app: AJAX auto-completion, which responds to up/down arrow keys, the enter key, and the escape key
+ − // The idea was pilfered mercilessly from vBulletin, but uses about 8
+ − // bytes of vB code. All the rest was coded by me, Mr. Javascript Newbie...
+ −
+ − // ...in about 8 hours.
+ − // You folks better like this stuff.
+ −
+ − function nameCompleteEventHandler(e)
+ − {
+ − if(!e) e = window.event;
+ − switch(e.keyCode)
+ − {
+ − case 38: // up
+ − unSelectMove('up');
+ − break;
+ − case 40: // down
+ − unSelectMove('down');
+ − break;
+ − case 27: // escape
+ − case 9: // tab
+ − destroyUsernameDropdowns();
+ − break;
+ − case 13: // enter
+ − unSelect();
+ − break;
+ − default: return false; break;
+ − }
+ − return true;
+ − }
+ −
+ − function unSelectMove(dir)
+ − {
+ − if(submitAuthorized) return false;
+ − var thediv = document.getElementById(unObjDivCurrentId);
+ − thetable = thediv.firstChild;
+ − cel = thetable.firstChild.firstChild;
+ − d = true;
+ − index = false;
+ − changed = false;
+ − // Object of the game: extract the username, determine its index in the userlist array, and then color the menu items and set unObjCurrentSelection
+ − while(d) // Set to false if an exception occurs or if we arrive at our destination
+ − {
+ − //*
+ − if(!cel) d=false;
+ − celbak = cel;
+ − cel = cel.nextSibling;
+ − if(!cel) d=false;
+ − try {
+ − if(cel.firstChild.nextSibling) html = cel.firstChild.nextSibling.innerHTML;
+ − else html = cel.firstChild.innerHTML;
+ − cel.firstChild.className = 'row1';
+ − if(cel.firstChild.nextSibling) cel.firstChild.nextSibling.className = 'row1';
+ − thename = html.substr(7, html.length-15);
+ − // FINALLY! we have extracted the username
+ − // Now get its position in the userlist array
+ − if(thename == unObjCurrentSelection)
+ − {
+ − index = parseInt(in_array(thename, userlist));
+ − }
+ − if(typeof(index) == 'number')
+ − {
+ − if(dir=='down')
+ − n = index+1;
+ − else if(dir == 'up')
+ − n = index - 1;
+ −
+ − // Try to trap moving the selection up or down beyond the top of bottom
+ − if(n > userlist.length-1 || n < 0)
+ − {
+ − cel.firstChild.className = 'row2';
+ − if(cel.firstChild.nextSibling) cel.firstChild.nextSibling.className = 'row2';
+ − return;
+ − }
+ −
+ − if(dir=='down') no=cel.nextSibling;
+ − else if(dir=='up') no=cel.previousSibling;
+ − no.firstChild.className = 'row2';
+ − if(no.firstChild.nextSibling) no.firstChild.nextSibling.className = 'row2';
+ − if(no.firstChild.id)
+ − {
+ − scroll = getScrollOffset() + getHeight();
+ − elemht = getElementHeight(no.firstChild.id);
+ − elemoff = fetch_offset(no.firstChild);
+ − whereto = elemoff['top'] + elemht;
+ − if(whereto > scroll)
+ − {
+ − window.location.hash = '#'+no.firstChild.id;
+ − unObj.focus();
+ − }
+ − }
+ − cel=cel.nextSibling;
+ − unObjCurrentSelection = userlist[n];
+ − index = false;
+ − changed = true;
+ − return;
+ − }
+ − } catch(e) { }
+ − //*/ d = false;
+ − }
+ − }
+ −
+ − function unSelect()
+ − {
+ − if(!unObj || submitAuthorized) return false;
+ − if ( unObjCurrentSelection )
+ − unObj.value = unObjCurrentSelection;
+ − destroyUsernameDropdowns();
+ − }
+ −
+ − function in_array(needle, haystack)
+ − {
+ − for(var i in haystack)
+ − {
+ − if(haystack[i] == needle) return i;
+ − }
+ − return false;
+ − }
+ −
+ − function ajaxUserNameComplete(o)
+ − {
+ − if(!o) {destroyUsernameDropdowns(); return;}
+ − if(!o.value) {destroyUsernameDropdowns(); return;}
+ − if(o.value.length < 3) {destroyUsernameDropdowns(); return;}
+ − //if(IE) return; // This control doesn't work in IE. Yes, it's true! document.createElement doesn't work.
+ − if(!o.id)
+ − {
+ − o.id = 'usernametextboxobj_' + Math.floor(Math.random() * 10000000);
+ − }
+ − unObj = o;
+ − o.setAttribute("autocomplete","off");
+ − o.onkeyup = function(e, o) { o=unObj; if(!nameCompleteEventHandler(e)) ajaxUserNameComplete(o); }
+ − val = escape(o.value).replace('+', '%2B');
+ − ajaxGet(stdAjaxPrefix+'&_mode=fillusername&name='+val, function()
+ − {
+ − if ( ajax.readyState == 4 && ajax.status == 200 )
+ − {
+ − // Determine the appropriate left/top positions, then create a div to use for the drop-down list
+ − // 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
+ − destroyUsernameDropdowns();
+ − off = fetch_offset(unObj);
+ − dim = fetch_dimensions(unObj);
+ − left = off['left'];
+ − i1 = off['top'];
+ − i2 = dim['h'];
+ − var top = 0;
+ − top = i1 + i2;
+ − var thediv = document.createElement('div');
+ − thediv.className = 'tblholder';
+ − thediv.style.marginTop = '0px';
+ − thediv.style.position = 'absolute';
+ − thediv.style.top = top + 'px';
+ − thediv.style.left = left + 'px';
+ − thediv.style.zIndex = getHighestZ() + 2;
+ − id = 'usernamehoverobj_' + Math.floor(Math.random() * 10000000);
+ − unObjDivCurrentId = id;
+ − thediv.id = id;
+ − unObj.onblur = function() { destroyUsernameDropdowns(); }
+ −
+ − 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;
+ − }
+ −
+ − response = parseJSON(response);
+ − var errorstring = false;
+ − if ( response.mode == 'error' )
+ − {
+ − errorstring = response.error;
+ − }
+ − else
+ − {
+ − var userlist = response.users_real;
+ − }
+ −
+ − if(errorstring)
+ − {
+ − html = '<span style="color: #555; padding: 4px;">'+errorstring+'</span>';
+ − }
+ − else
+ − {
+ − html = '<table border="0" cellspacing="1" cellpadding="3" style="width: auto;"><tr><th><small>' + $lang.get('user_autofill_heading_suggestions') + '</small></th></tr>';
+ − cls = 'row2';
+ − unObjCurrentSelection = userlist[0];
+ − for(i=0;i<userlist.length;i++)
+ − {
+ − tmpnam = 'listobjnode_'+Math.floor(Math.random() * 10000000);
+ − 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>';
+ − if(cls=='row2') cls='row1';
+ − }
+ − html = html + '</table>';
+ − }
+ −
+ − thediv.innerHTML = html;
+ − var body = document.getElementsByTagName('body');
+ − body = body[0];
+ − unSelectMenuOn = true;
+ − submitAuthorized = false;
+ − body.appendChild(thediv);
+ − }
+ − });
+ − }
+ −
+ − function ajaxPageNameComplete(o)
+ − {
+ − if(!o) {destroyUsernameDropdowns(); return;}
+ − if(!o.value) {destroyUsernameDropdowns(); return;}
+ − if(o.value.length < 3) {destroyUsernameDropdowns(); return;}
+ − if(IE) return; // This control doesn't work in IE. Yes, it's true! document.createElement doesn't work.
+ − if(!o.id)
+ − {
+ − o.id = 'usernametextboxobj_' + Math.floor(Math.random() * 10000000);
+ − }
+ − o.setAttribute("autocomplete","off");
+ − unObj = o;
+ − o.onkeyup = function(e, o) { o=unObj; if(!nameCompleteEventHandler(e)) ajaxPageNameComplete(o); }
+ − val = escape(o.value).replace('+', '%2B');
+ − ajaxGet(stdAjaxPrefix+'&_mode=fillpagename&name='+val, function()
+ − {
+ − if(!ajax) return;
+ − if ( ajax.readyState == 4 && ajax.status == 200 )
+ − {
+ − // Determine the appropriate left/top positions, then create a div to use for the drop-down list
+ − // 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
+ − destroyUsernameDropdowns();
+ − off = fetch_offset(unObj);
+ − dim = fetch_dimensions(unObj);
+ − left = off['left'];
+ − top = off['top'] + dim['h'];
+ − var thediv = document.createElement('div');
+ − thediv.className = 'tblholder';
+ − thediv.style.marginTop = '0px';
+ − thediv.style.position = 'absolute';
+ − thediv.style.top = top + 'px';
+ − thediv.style.left = left + 'px';
+ − thediv.style.zIndex = getHighestZ() + 2;
+ − id = 'usernamehoverobj_' + Math.floor(Math.random() * 10000000);
+ − unObjDivCurrentId = id;
+ − thediv.id = id;
+ −
+ − eval(ajax.responseText);
+ − if(errorstring)
+ − {
+ − html = '<span style="color: #555; padding: 4px;">'+errorstring+'</span>';
+ − }
+ − else
+ − {
+ − html = '<table border="0" cellspacing="1" cellpadding="3" style="width: auto;"><tr><th colspan="2">' + $lang.get('page_autosuggest_heading') + '</th></tr><tr><th><small>' + $lang.get('page_autosuggest_col_name') + '</small></th><th><small>' + $lang.get('page_autosuggest_col_page_id') + '</small></th></tr>';
+ − cls = 'row2';
+ − unObjCurrentSelection = userlist[0];
+ − for(i=0;i<userlist.length;i++)
+ − {
+ − tmpnam = 'listobjnode_'+Math.floor(Math.random() * 10000000);
+ − 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>';
+ − if(cls=='row2') cls='row1';
+ − }
+ − html = html + '</table>';
+ − }
+ −
+ − thediv.innerHTML = html;
+ − var body = document.getElementsByTagName('body');
+ − body = body[0];
+ − unSelectMenuOn = true;
+ − submitAuthorized = false;
+ − body.appendChild(thediv);
+ − }
+ − });
+ − }
+ −
+ − function destroyUsernameDropdowns()
+ − {
+ − var divs = document.getElementsByTagName('div');
+ − var prefix = 'usernamehoverobj_';
+ − for(i=0;i<divs.length;i++)
+ − {
+ − if ( divs[i].id )
+ − {
+ − if(divs[i].id.substr(0, prefix.length)==prefix)
+ − {
+ − divs[i].innerHTML = '';
+ − divs[i].style.display = 'none';
+ − }
+ − }
+ − }
+ − unSelectMenuOn = false;
+ − unObjDivCurrentId = false;
+ − unObjCurrentSelection = false;
+ − submitAuthorized = true;
+ − }
+ −
+ − function get_parent_form(o)
+ − {
+ − if ( !o.parentNode )
+ − return false;
+ − if ( o.tagName == 'FORM' )
+ − return o;
+ − var p = o.parentNode;
+ − while(true)
+ − {
+ − if ( p.tagName == 'FORM' )
+ − return p;
+ − else if ( !p )
+ − return false;
+ − else
+ − p = p.parentNode;
+ − }
+ − }
+ −