Advertisement
konyakov

autosuggest.js | AJAX+PHP+MySQL Autosuggest (Cyr & Utf-8)

Dec 26th, 2011
231
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * An autosuggest textbox control.
  3.  * @class
  4.  * @scope public
  5.  */
  6. function AutoSuggestControl(oTextbox /*:HTMLInputElement*/,
  7.                             oProvider /*:SuggestionProvider*/) {
  8.    
  9.     /**
  10.      * The currently selected suggestions.
  11.      * @scope private
  12.      */  
  13.     this.cur /*:int*/ = -1;
  14.  
  15.     /**
  16.      * The dropdown list layer.
  17.      * @scope private
  18.      */
  19.     this.layer = null;
  20.    
  21.     /**
  22.      * Suggestion provider for the autosuggest feature.
  23.      * @scope private.
  24.      */
  25.     this.provider /*:SuggestionProvider*/ = oProvider;
  26.    
  27.     /**
  28.      * The textbox to capture.
  29.      * @scope private
  30.      */
  31.     this.textbox /*:HTMLInputElement*/ = oTextbox;
  32.    
  33.     /**
  34.      * Timeout ID for fast typers.
  35.      * @scope private
  36.      */
  37.     this.timeoutId /*:int*/ = null;
  38.  
  39.     /**
  40.      * The text that the user typed.
  41.      * @scope private
  42.      */
  43.     this.userText /*:String*/ = oTextbox.value;
  44.    
  45.     //initialize the control
  46.     this.init();
  47.    
  48. }
  49.  
  50. /**
  51.  * Autosuggests one or more suggestions for what the user has typed.
  52.  * If no suggestions are passed in, then no autosuggest occurs.
  53.  * @scope private
  54.  * @param aSuggestions An array of suggestion strings.
  55.  * @param bTypeAhead If the control should provide a type ahead suggestion.
  56.  */
  57. AutoSuggestControl.prototype.autosuggest = function (aSuggestions /*:Array*/,
  58.                                                      bTypeAhead /*:boolean*/) {
  59.    
  60.     //re-initialize pointer to current suggestion
  61.     this.cur = -1;
  62.    
  63.     //make sure there's at least one suggestion
  64.     if (aSuggestions.length > 0) {
  65.         if (bTypeAhead) {
  66.            this.typeAhead(aSuggestions[0]);
  67.         }
  68.        
  69.         this.showSuggestions(aSuggestions);
  70.     } else {
  71.         this.hideSuggestions();
  72.     }
  73. };
  74.  
  75. /**
  76.  * Creates the dropdown layer to display multiple suggestions.
  77.  * @scope private
  78.  */
  79. AutoSuggestControl.prototype.createDropDown = function () {
  80.  
  81.  
  82.     //create the layer and assign styles
  83.     this.layer = document.createElement("div");
  84.     this.layer.className = "suggestions";
  85.     this.layer.style.visibility = "hidden";
  86.     this.layer.style.width = this.textbox.offsetWidth;
  87.     document.body.appendChild(this.layer);    
  88.    
  89.     //when the user clicks on the a suggestion, get the text (innerHTML)
  90.     //and place it into a textbox
  91.     var oThis = this;
  92.     this.layer.onmousedown =
  93.     this.layer.onmouseup =
  94.     this.layer.onmouseover = function (oEvent) {
  95.         oEvent = oEvent || window.event;
  96.         oTarget = oEvent.target || oEvent.srcElement;
  97.  
  98.         if (oEvent.type == "mousedown") {
  99.             oThis.textbox.value = oTarget.firstChild.nodeValue;
  100.             oThis.hideSuggestions();
  101.         } else if (oEvent.type == "mouseover") {
  102.             oThis.highlightSuggestion(oTarget);
  103.         } else {
  104.             oThis.textbox.focus();
  105.         }
  106.     };
  107.    
  108. };
  109.  
  110. /**
  111.  * Gets the left coordinate of the textbox.
  112.  * @scope private
  113.  * @return The left coordinate of the textbox in pixels.
  114.  */
  115. AutoSuggestControl.prototype.getLeft = function () /*:int*/ {
  116.  
  117.     var oNode = this.textbox;
  118.     var iLeft = 0;
  119.    
  120.     while(oNode.tagName != "BODY") {
  121.         iLeft += oNode.offsetLeft;
  122.         oNode = oNode.offsetParent;        
  123.     }
  124.    
  125.     return iLeft;
  126. };
  127.  
  128. /**
  129.  * Gets the top coordinate of the textbox.
  130.  * @scope private
  131.  * @return The top coordinate of the textbox in pixels.
  132.  */
  133. AutoSuggestControl.prototype.getTop = function () /*:int*/ {
  134.  
  135.     var oNode = this.textbox;
  136.     var iTop = 0;
  137.    
  138.     while(oNode.tagName != "BODY") {
  139.         iTop += oNode.offsetTop;
  140.         oNode = oNode.offsetParent;
  141.     }
  142.    
  143.     return iTop;
  144. };
  145.  
  146. /**
  147.  * Highlights the next or previous suggestion in the dropdown and
  148.  * places the suggestion into the textbox.
  149.  * @param iDiff Either a positive or negative number indicating whether
  150.  *              to select the next or previous sugggestion, respectively.
  151.  * @scope private
  152.  */
  153. AutoSuggestControl.prototype.goToSuggestion = function (iDiff /*:int*/) {
  154.     var cSuggestionNodes = this.layer.childNodes;
  155.    
  156.     if (cSuggestionNodes.length > 0) {
  157.         var oNode = null;
  158.    
  159.         if (iDiff > 0) {
  160.             if (this.cur < cSuggestionNodes.length-1) {
  161.                 oNode = cSuggestionNodes[++this.cur];
  162.             }        
  163.         } else {
  164.             if (this.cur > 0) {
  165.                 oNode = cSuggestionNodes[--this.cur];
  166.             }    
  167.         }
  168.        
  169.         if (oNode) {
  170.             this.highlightSuggestion(oNode);
  171.             this.textbox.value = oNode.firstChild.nodeValue;
  172.         }
  173.     }
  174. };
  175.  
  176. /**
  177.  * Handles three keydown events.
  178.  * @scope private
  179.  * @param oEvent The event object for the keydown event.
  180.  */
  181. AutoSuggestControl.prototype.handleKeyDown = function (oEvent /*:Event*/) {
  182.  
  183.     switch(oEvent.keyCode) {
  184.         case 38: //up arrow
  185.             this.goToSuggestion(-1);
  186.             break;
  187.         case 40: //down arrow
  188.             this.goToSuggestion(1);
  189.             break;
  190.         case 27: //esc
  191.             this.textbox.value = this.userText;
  192.             this.selectRange(this.userText.length, 0);
  193.             /* falls through */
  194.         case 13: //enter
  195.             this.hideSuggestions();
  196.             oEvent.returnValue = false;
  197.             if (oEvent.preventDefault) {
  198.                 oEvent.preventDefault();
  199.             }
  200.             break;
  201.     }
  202.  
  203. };
  204.  
  205. /**
  206.  * Handles keyup events.
  207.  * @scope private
  208.  * @param oEvent The event object for the keyup event.
  209.  */
  210. AutoSuggestControl.prototype.handleKeyUp = function (oEvent /*:Event*/) {
  211.  
  212.     var iKeyCode = oEvent.keyCode;
  213.     var oThis = this;
  214.    
  215.     //get the currently entered text
  216.     this.userText = this.textbox.value;
  217.    
  218.     clearTimeout(this.timeoutId);
  219.  
  220.     //for backspace (8) and delete (46), shows suggestions without typeahead
  221.     if (iKeyCode == 8 || iKeyCode == 46) {
  222.        
  223.         this.timeoutId = setTimeout( function () {
  224.             oThis.provider.requestSuggestions(oThis, false);
  225.         }, 250);
  226.        
  227.     //make sure not to interfere with non-character keys
  228.     } else if (iKeyCode < 32 || (iKeyCode >= 33 && iKeyCode < 46) || (iKeyCode >= 112 && iKeyCode <= 123)) {
  229.         //ignore
  230.     } else {
  231.         //request suggestions from the suggestion provider with typeahead
  232.         this.timeoutId = setTimeout( function () {
  233.             oThis.provider.requestSuggestions(oThis, true);
  234.         }, 250);
  235.     }
  236. };
  237.  
  238. /**
  239.  * Hides the suggestion dropdown.
  240.  * @scope private
  241.  */
  242. AutoSuggestControl.prototype.hideSuggestions = function () {
  243.     this.layer.style.visibility = "hidden";
  244. };
  245.  
  246. /**
  247.  * Highlights the given node in the suggestions dropdown.
  248.  * @scope private
  249.  * @param oSuggestionNode The node representing a suggestion in the dropdown.
  250.  */
  251. AutoSuggestControl.prototype.highlightSuggestion = function (oSuggestionNode) {
  252.    
  253.     for (var i=0; i < this.layer.childNodes.length; i++) {
  254.         var oNode = this.layer.childNodes[i];
  255.         if (oNode == oSuggestionNode) {
  256.             oNode.className = "current"
  257.         } else if (oNode.className == "current") {
  258.             oNode.className = "";
  259.         }
  260.     }
  261. };
  262.  
  263. /**
  264.  * Initializes the textbox with event handlers for
  265.  * auto suggest functionality.
  266.  * @scope private
  267.  */
  268. AutoSuggestControl.prototype.init = function () {
  269.  
  270.     //save a reference to this object
  271.     var oThis = this;
  272.    
  273.     //assign the onkeyup event handler
  274.     this.textbox.onkeyup = function (oEvent) {
  275.    
  276.         //check for the proper location of the event object
  277.         if (!oEvent) {
  278.             oEvent = window.event;
  279.         }    
  280.        
  281.         //call the handleKeyUp() method with the event object
  282.         oThis.handleKeyUp(oEvent);
  283.     };
  284.    
  285.     //assign onkeydown event handler
  286.     this.textbox.onkeydown = function (oEvent) {
  287.  
  288.         //check for the proper location of the event object
  289.         if (!oEvent) {
  290.             oEvent = window.event;
  291.         }    
  292.        
  293.         //call the handleKeyDown() method with the event object
  294.         oThis.handleKeyDown(oEvent);
  295.     };
  296.    
  297.     //assign onblur event handler (hides suggestions)    
  298.     this.textbox.onblur = function () {
  299.         oThis.hideSuggestions();
  300.     };
  301.    
  302.     //create the suggestions dropdown
  303.     this.createDropDown();
  304. };
  305.  
  306. /**
  307.  * Selects a range of text in the textbox.
  308.  * @scope public
  309.  * @param iStart The start index (base 0) of the selection.
  310.  * @param iEnd The end index of the selection.
  311.  */
  312. AutoSuggestControl.prototype.selectRange = function (iStart /*:int*/, iEnd /*:int*/) {
  313.  
  314.     //use text ranges for Internet Explorer
  315.     if (this.textbox.createTextRange) {
  316.         var oRange = this.textbox.createTextRange();
  317.         oRange.moveStart("character", iStart);
  318.         oRange.moveEnd("character", iEnd - this.textbox.value.length);      
  319.         oRange.select();
  320.        
  321.     //use setSelectionRange() for Mozilla
  322.     } else if (this.textbox.setSelectionRange) {
  323.         this.textbox.setSelectionRange(iStart, iEnd);
  324.     }    
  325.  
  326.     //set focus back to the textbox
  327.     this.textbox.focus();      
  328. };
  329.  
  330. /**
  331.  * Builds the suggestion layer contents, moves it into position,
  332.  * and displays the layer.
  333.  * @scope private
  334.  * @param aSuggestions An array of suggestions for the control.
  335.  */
  336. AutoSuggestControl.prototype.showSuggestions = function (aSuggestions /*:Array*/) {
  337.    
  338.     var oDiv = null;
  339.     this.layer.innerHTML = "";  //clear contents of the layer
  340.    
  341.     for (var i=0; i < aSuggestions.length; i++) {
  342.         oDiv = document.createElement("div");
  343.         oDiv.appendChild(document.createTextNode(aSuggestions[i]));
  344.         this.layer.appendChild(oDiv);
  345.     }
  346.    
  347.     this.layer.style.left = this.getLeft() + "px";
  348.     this.layer.style.top = (this.getTop()+this.textbox.offsetHeight) + "px";
  349.     this.layer.style.visibility = "visible";
  350.  
  351. };
  352.  
  353. /**
  354.  * Inserts a suggestion into the textbox, highlighting the
  355.  * suggested part of the text.
  356.  * @scope private
  357.  * @param sSuggestion The suggestion for the textbox.
  358.  */
  359. AutoSuggestControl.prototype.typeAhead = function (sSuggestion /*:String*/) {
  360.  
  361.     //check for support of typeahead functionality
  362.     if (this.textbox.createTextRange || this.textbox.setSelectionRange){
  363.         var iLen = this.textbox.value.length;
  364.         this.textbox.value = sSuggestion;
  365.         this.selectRange(iLen, sSuggestion.length);
  366.     }
  367. };
  368.  
  369. /**
  370.  * Provides suggestions for state/province names.
  371.  * @class
  372.  * @scope public
  373.  */
  374. function SuggestionProvider() {
  375.     this.http = zXmlHttp.createRequest();
  376. }
  377.  
  378. /**
  379.  * Request suggestions for the given autosuggest control.
  380.  * @scope protected
  381.  * @param oAutoSuggestControl The autosuggest control to provide suggestions for.
  382.  */
  383. SuggestionProvider.prototype.requestSuggestions = function (oAutoSuggestControl /*:AutoSuggestControl*/,
  384.                                                             bTypeAhead /*:boolean*/) {
  385.  
  386.     var oHttp = this.http;
  387.                                    
  388.     //cancel any active requests                          
  389.     if (oHttp.readyState != 0) {
  390.         oHttp.abort();
  391.     }                
  392.    
  393.     //define the data
  394.     var oData = {
  395.         requesting: "CitiesInRussia",
  396.         text: oAutoSuggestControl.userText,
  397.         limit: 5
  398.     };
  399.    
  400.     //open connection to server
  401.     oHttp.open("post", "suggestions.php", true);
  402.     oHttp.onreadystatechange = function () {
  403.         if (oHttp.readyState == 4) {
  404.             //evaluate the returned text JavaScript (an array)
  405.             var aSuggestions = JSON.parse(oHttp.responseText);
  406.  
  407.             //provide suggestions to the control
  408.             oAutoSuggestControl.autosuggest(aSuggestions, bTypeAhead);        
  409.         }    
  410.     };
  411.  
  412.     //send the request
  413.     oHttp.send(JSON.stringify(oData));
  414.  
  415. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement