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