includes/clientside/static/autocomplete.js
changeset 1 fe660c52c48f
child 73 0a74676a2f2f
equal deleted inserted replaced
0:902822492a68 1:fe660c52c48f
       
     1 /*
       
     2  * Auto-completing page/username fields
       
     3  */
       
     4 
       
     5 // The ultimate Javascript app: AJAX auto-completion, which responds to up/down arrow keys, the enter key, and the escape key
       
     6 // The idea was pilfered mercilessly from vBulletin, but uses about 8
       
     7 // bytes of vB code. All the rest was coded by me, Mr. Javascript Newbie...
       
     8 
       
     9 // ...in about 8 hours.
       
    10 // You folks better like this stuff.
       
    11 
       
    12 function nameCompleteEventHandler(e)
       
    13 {
       
    14   if(!e) e = window.event;
       
    15   switch(e.keyCode)
       
    16   {
       
    17     case 38: // up
       
    18       unSelectMove('up');
       
    19       break;
       
    20     case 40: // down
       
    21       unSelectMove('down');
       
    22       break;
       
    23     case 27: // escape
       
    24     case 9:  // tab
       
    25       destroyUsernameDropdowns();
       
    26       break;
       
    27     case 13: // enter
       
    28       unSelect();
       
    29       break;
       
    30     default: return false; break;
       
    31   }
       
    32   return true;
       
    33 }
       
    34 
       
    35 function unSelectMove(dir)
       
    36 {
       
    37   if(submitAuthorized) return false;
       
    38   var thediv = document.getElementById(unObjDivCurrentId);
       
    39   thetable = thediv.firstChild;
       
    40   cel = thetable.firstChild.firstChild;
       
    41   d = true;
       
    42   index = false;
       
    43   changed = false;
       
    44   // Object of the game: extract the username, determine its index in the userlist array, and then color the menu items and set unObjCurrentSelection
       
    45   while(d) // Set to false if an exception occurs or if we arrive at our destination
       
    46   {
       
    47     //*
       
    48     if(!cel) d=false;
       
    49     celbak = cel;
       
    50     cel = cel.nextSibling;
       
    51     if(!cel) d=false;
       
    52     try {
       
    53       if(cel.firstChild.nextSibling) html = cel.firstChild.nextSibling.innerHTML;
       
    54       else html = cel.firstChild.innerHTML;
       
    55       cel.firstChild.className = 'row1';
       
    56       if(cel.firstChild.nextSibling) cel.firstChild.nextSibling.className = 'row1';
       
    57       thename = html.substr(7, html.length-15);
       
    58       // FINALLY! we have extracted the username
       
    59       // Now get its position in the userlist array
       
    60       if(thename == unObjCurrentSelection)
       
    61       {
       
    62         index = parseInt(in_array(thename, userlist));
       
    63       }
       
    64       if(typeof(index) == 'number')
       
    65       {
       
    66         if(dir=='down')
       
    67           n = index+1;
       
    68         else if(dir == 'up')
       
    69           n = index - 1;
       
    70         
       
    71         // Try to trap moving the selection up or down beyond the top of bottom
       
    72         if(n > userlist.length-1 || n < 0)
       
    73         {
       
    74           cel.firstChild.className = 'row2';
       
    75           if(cel.firstChild.nextSibling) cel.firstChild.nextSibling.className = 'row2';
       
    76           return;
       
    77         }
       
    78         
       
    79         if(dir=='down') no=cel.nextSibling;
       
    80         else if(dir=='up') no=cel.previousSibling;
       
    81         no.firstChild.className = 'row2';
       
    82         if(no.firstChild.nextSibling) no.firstChild.nextSibling.className = 'row2';
       
    83         if(no.firstChild.id)
       
    84         {
       
    85           scroll = getScrollOffset() + getHeight();
       
    86           elemht = getElementHeight(no.firstChild.id);
       
    87           elemoff = fetch_offset(no.firstChild);
       
    88           whereto = elemoff['top'] + elemht;
       
    89           if(whereto > scroll)
       
    90           {
       
    91             window.location.hash = '#'+no.firstChild.id;
       
    92             unObj.focus();
       
    93           }
       
    94         }
       
    95         cel=cel.nextSibling;
       
    96         unObjCurrentSelection = userlist[n];
       
    97         index = false;
       
    98         changed = true;
       
    99         return;
       
   100       }
       
   101     } catch(e) { }
       
   102     //*/ d = false;
       
   103   }
       
   104 }
       
   105 
       
   106 function unSelect()
       
   107 {
       
   108   if(!unObj || submitAuthorized) return false;
       
   109   if ( unObjCurrentSelection )
       
   110     unObj.value = unObjCurrentSelection;
       
   111   destroyUsernameDropdowns(); 
       
   112 }
       
   113 
       
   114 function in_array(needle, haystack)
       
   115 {
       
   116   for(var i in haystack)
       
   117   {
       
   118     if(haystack[i] == needle) return i;
       
   119   }
       
   120   return false;
       
   121 }
       
   122 
       
   123 function ajaxUserNameComplete(o)
       
   124 {
       
   125   if(!o) {destroyUsernameDropdowns(); return;}
       
   126   if(!o.value) {destroyUsernameDropdowns(); return;}
       
   127   if(o.value.length < 3) {destroyUsernameDropdowns(); return;}
       
   128   //if(IE) return; // This control doesn't work in IE. Yes, it's true! document.createElement doesn't work.
       
   129   if(!o.id)
       
   130   {
       
   131     o.id = 'usernametextboxobj_' + Math.floor(Math.random() * 10000000);
       
   132   }
       
   133   unObj = o;
       
   134   o.setAttribute("autocomplete","off");
       
   135   o.onkeyup = function(e, o) { o=unObj; if(!nameCompleteEventHandler(e)) ajaxUserNameComplete(o); }
       
   136   val = escape(o.value).replace('+', '%2B');
       
   137   ajaxGet(stdAjaxPrefix+'&_mode=fillusername&name='+val, function()
       
   138     {
       
   139       if(ajax.readyState==4)
       
   140       {
       
   141         // Determine the appropriate left/top positions, then create a div to use for the drop-down list
       
   142         // 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
       
   143         destroyUsernameDropdowns();
       
   144         off = fetch_offset(unObj);
       
   145         dim = fetch_dimensions(unObj);
       
   146         left = off['left'];
       
   147         i1 = off['top'];
       
   148         i2 = dim['h'];
       
   149         var top = 0;
       
   150         top = i1 + i2;
       
   151         var thediv = document.createElement('div');
       
   152         thediv.className = 'tblholder';
       
   153         thediv.style.marginTop = '0px';
       
   154         thediv.style.position = 'absolute';
       
   155         thediv.style.top  = top  + 'px';
       
   156         thediv.style.left = left + 'px';
       
   157         thediv.style.zIndex = getHighestZ() + 2;
       
   158         id = 'usernamehoverobj_' + Math.floor(Math.random() * 10000000);
       
   159         unObjDivCurrentId = id;
       
   160         thediv.id = id;
       
   161         unObj.onblur = function() { destroyUsernameDropdowns(); }
       
   162         
       
   163         eval(ajax.responseText);
       
   164         if(errorstring)
       
   165         {
       
   166           html = '<span style="color: #555; padding: 4px;">'+errorstring+'</span>';
       
   167         }
       
   168         else
       
   169         {
       
   170           html = '<table border="0" cellspacing="1" cellpadding="3" style="width: auto;"><tr><th><small>Username matches</small></th></tr>';
       
   171           cls = 'row2';
       
   172           unObjCurrentSelection = userlist[0];
       
   173           for(i=0;i<userlist.length;i++)
       
   174           {
       
   175             tmpnam = 'listobjnode_'+Math.floor(Math.random() * 10000000);
       
   176             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>';
       
   177             if(cls=='row2') cls='row1';
       
   178           }
       
   179           html = html + '</table>';
       
   180         }
       
   181         
       
   182         thediv.innerHTML = html;
       
   183         var body = document.getElementsByTagName('body');
       
   184         body = body[0];
       
   185         unSelectMenuOn = true;
       
   186         submitAuthorized = false;
       
   187         body.appendChild(thediv);
       
   188       }
       
   189     });
       
   190 }
       
   191 
       
   192 function ajaxPageNameComplete(o)
       
   193 {
       
   194   if(!o) {destroyUsernameDropdowns(); return;}
       
   195   if(!o.value) {destroyUsernameDropdowns(); return;}
       
   196   if(o.value.length < 3) {destroyUsernameDropdowns(); return;}
       
   197   if(IE) return; // This control doesn't work in IE. Yes, it's true! document.createElement doesn't work.
       
   198   if(!o.id)
       
   199   {
       
   200     o.id = 'usernametextboxobj_' + Math.floor(Math.random() * 10000000);
       
   201   }
       
   202   o.setAttribute("autocomplete","off");
       
   203   unObj = o;
       
   204   o.onkeyup = function(e, o) { o=unObj; if(!nameCompleteEventHandler(e)) ajaxPageNameComplete(o); }
       
   205   val = escape(o.value).replace('+', '%2B');
       
   206   ajaxGet(stdAjaxPrefix+'&_mode=fillpagename&name='+val, function()
       
   207     {
       
   208       if(!ajax) return;
       
   209       if(ajax.readyState==4)
       
   210       {
       
   211         // Determine the appropriate left/top positions, then create a div to use for the drop-down list
       
   212         // 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
       
   213         destroyUsernameDropdowns();
       
   214         off = fetch_offset(unObj);
       
   215         dim = fetch_dimensions(unObj);
       
   216         left = off['left'];
       
   217         top = off['top'] + dim['h'];
       
   218         var thediv = document.createElement('div');
       
   219         thediv.className = 'tblholder';
       
   220         thediv.style.marginTop = '0px';
       
   221         thediv.style.position = 'absolute';
       
   222         thediv.style.top  = top  + 'px';
       
   223         thediv.style.left = left + 'px';
       
   224         thediv.style.zIndex = getHighestZ() + 2;
       
   225         id = 'usernamehoverobj_' + Math.floor(Math.random() * 10000000);
       
   226         unObjDivCurrentId = id;
       
   227         thediv.id = id;
       
   228         
       
   229         eval(ajax.responseText);
       
   230         if(errorstring)
       
   231         {
       
   232           html = '<span style="color: #555; padding: 4px;">'+errorstring+'</span>';
       
   233         }
       
   234         else
       
   235         {
       
   236           html = '<table border="0" cellspacing="1" cellpadding="3" style="width: auto;"><tr><th colspan="2">Page name matches</th></tr><tr><th><small>Page title</small></th><th><small>Page ID</small></th></tr>';
       
   237           cls = 'row2';
       
   238           unObjCurrentSelection = userlist[0];
       
   239           for(i=0;i<userlist.length;i++)
       
   240           {
       
   241             tmpnam = 'listobjnode_'+Math.floor(Math.random() * 10000000);
       
   242             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>';
       
   243             if(cls=='row2') cls='row1';
       
   244           }
       
   245           html = html + '</table>';
       
   246         }
       
   247         
       
   248         thediv.innerHTML = html;
       
   249         var body = document.getElementsByTagName('body');
       
   250         body = body[0];
       
   251         unSelectMenuOn = true;
       
   252         submitAuthorized = false;
       
   253         body.appendChild(thediv);
       
   254         
       
   255         unObj.onblur = function() { CheckDestroyUsernameDropdowns(thediv.id); };
       
   256       }
       
   257     });
       
   258 }
       
   259 
       
   260 function CheckDestroyUsernameDropdowns(id)
       
   261 {
       
   262   elem = document.getElementById(id);
       
   263   if(!elem) return;
       
   264   if(queryOnObj(elem, 100))
       
   265   {
       
   266     destroyUsernameDropdowns();
       
   267   }
       
   268 }
       
   269 
       
   270 function destroyUsernameDropdowns()
       
   271 {
       
   272   var divs = document.getElementsByTagName('div');
       
   273   var prefix = 'usernamehoverobj_';
       
   274   for(i=0;i<divs.length;i++)                                                                                                                                                                                                                         
       
   275   {
       
   276     if ( divs[i].id )
       
   277     {
       
   278       if(divs[i].id.substr(0, prefix.length)==prefix)
       
   279       {
       
   280         divs[i].innerHTML = '';
       
   281         divs[i].style.display = 'none';
       
   282       }
       
   283     }
       
   284   }
       
   285   unSelectMenuOn = false;
       
   286   unObjDivCurrentId = false;
       
   287   unObjCurrentSelection = false;
       
   288   submitAuthorized = true;
       
   289 }
       
   290 
       
   291 function get_parent_form(o)
       
   292 {
       
   293   if ( !o.parentNode )
       
   294     return false;
       
   295   if ( o.tagName == 'FORM' )
       
   296     return o;
       
   297   var p = o.parentNode;
       
   298   while(true)
       
   299   {
       
   300     if ( p.tagName == 'FORM' )
       
   301       return p;
       
   302     else if ( !p )
       
   303       return false;
       
   304     else
       
   305       p = p.parentNode;
       
   306   }
       
   307 }
       
   308