/*global jQuery */
(function($)  {
   $.fn.extend({
      /**
       * Allows you to call an AJAX script to retrieve a list of search
       * results, which will then be displayed in a scrollable div below
       * the selected element.
       * @param ajax_file string The AJAX file to call.  It must retrieve a key
       *                         of "q" out of the GET object, and return a
       *                         newline-separated list of results.
       * @param data object Any additional parameters you want to pass to the
       *                    AJAX script.
       * @param options object A set of key/value pairs that customize the
       *                       behavior of the method:
       *                       <dl>
       *                         <dt>min_length</dt>
       *                         <dd>The minimum number of characters the user
       *                           must type before the search takes place.
       *                           Default=3.
       *                         </dd>
       *                         <dt>error_responses</dt>
       *                         <dd>An array of responses considered to be
       *                           error messages.  If any of the responses
       *                           match, they will not be linked in the results
       *                           panel.
       *                           Default: ['No results.', 'Too many results.'}</dd>
       *                       </dl>
       * @return jQuery The selected elements
       * @addon
       * @member jQuery
       */
      suggest : function (ajax_file, data, options)  {
         /** Ensure options.min_length exists and is a positive number. */
         if (!options.min_length ||
             isNaN(options.min_length) ||
             options.min_length < 0)  {
            options.min_length = 3;
         }
         if (!options.error_responses)  {
            options.error_responses = ['No results.', 'Too many results.'];
         }
         
         $(this).each(function()  {
            /**
             * Create the results div.  Set its class.  Set the width to the
             * width of the text box.  Insert it after the text box.  Set the
             * "textbox" data item to the text box so we can find it later.  Set
             * the "results" data item of the text box to the div so we can find
             * it later.
             */
            var el_results = document.createElement("div");
            $(el_results).addClass("wt_suggest_results").
               width($(this).width()).
               insertAfter($(this)).
               data("textbox", $(this));
            $(this).data("results", $(el_results));
            
            /**
             * Disable the browser's autocomplete.
             */
            $(this).attr("autocomplete", "off");
            
            /**
             * Disable the enter key on the text box.
             */
            $(this).keypress(function(e)  {
               if (e.keyCode === 13) { return false; }
            });
            
            /**
             * Define the behavior that occurs when a user types a character in
             * the text box.
             */
            $(this).keyup(function(e)  {
               /**
                * Check the length of the value.  If it's less than the
                * min_length, hide the results box and get out.
                */
               /*if (e.keyCode !== 32 && (
                     e.keyCode < 65 || e.keyCode > 90))  {
                  return;
               }*/
               var self       = this;
               var srch       = $(this).val();
               var jq_results = $(this).data("results");
               jq_results.empty();
               if (!srch || srch.length < options.min_length)  {
                  jq_results.hide();
                  return;
               }
               
               /**
                * Record the time of the keypress in the "latest_keypress_time"
                * data item.  In the AJAX response, we'll compare the
                * keypress_time with the "latest_keypress_time" data item.  If
                * they do not match we'll not process the callback function.
                * This will prevent callback functions from overlapping each
                * other.
                */
               var keypress_time = new Date();
               $(self).data("latest_keypress_time", keypress_time);
               
               /**
                * Perform an AJAX request to the specified ajax file.  Set the
                * "q" data item to the value of the text box.  Also pass along
                * any parameters the user defined.
                */
               if (typeof data === 'undefined') { data = {}; }
               data.q = $(self).val();
               var nl = new RegExp("\\n");
               $.get(ajax_file, data, function(response)  {
                  /**
                   * Compare the local keypress_time variable to the
                   * "latest_keypress_time" data item.  If it's earlier, then
                   * this response should be ignored, since there is a later
                   * AJAX request in the works.
                   */
                  var latest_keypress_time = $(self).data("latest_keypress_time");
                  if (keypress_time < latest_keypress_time)  { return; }
                  
                  /**
                   * When the AJAX request returns a result, split it by
                   * newlines. If a result is one of the error responses,
                   * enclose it in an <em> element and add it to the results
                   * div.  Otherwise, enclose it in an <a> element.
                   */
                  var results  = response.split(nl);
                  var re_space = new RegExp(" ", "g");
                  for (var i=0; i<results.length; i++)  {
                     var result = results[i].HTMLEncode().replace(re_space, String.fromCharCode(160));
                     var text   = document.createTextNode(result);
                     if ($.inArray(result, options.error_responses) > -1)  {
                        var em = document.createElement("em");
                        $(em).append(text);
                        jq_results.append(em);
                     } else {
                        var a      = document.createElement("a");
                        $(a).attr("href", "javascript%3A%3B").
                           data("results", jq_results).
                           append(text);
                        jq_results.append(a);
                     }
                     var br     = document.createElement("br");
                     jq_results.append(br);
                  }
   
                  /**
                   * Set the onclick event on each <a> tag to populate the text
                   * box with the result and hide the results div. Finally,
                   * show the text box.
                   */
                  jq_results.find("a").click(function()  {
                     var jq_results = $(this).data("results");
                     var textbox    = jq_results.data("textbox");
                     textbox.val($(this).text());
                     jq_results.hide();
                  });
                  jq_results.show();
               });   // $.get()
            });   // $(this).keyup()
         });   // $(this).each()
         return $(this);
      }      // suggest()
   });   // $.fn.extend()
})(jQuery);
   
/**
 * Converts certain characters in the string to their HTML-entity equivalents.
 *
 * The conversions peformed are:
 * <ul>
 *   <li><code>&amp;</code> (ampersand) becomes <code>&amp;amp;</code></li>
 *   <li><code>&quot;</code> (double quote) becomes <code>&amp;quot;</code></li>
 *   <li><code>&#039;</code> (single quote) becomes <code>&amp;#039;</code></li>
 *   <li><code>&lt;</code> (less than) becomes <code>&amp;lt;</code></li>
 *   <li><code>&gt;</code> (greater than) becomes <code>&amp;gt;</code></li>
 * </ul>
 * @return {String} The HTML-encoded string.
 */
String.prototype.HTMLEncode = function()  {
   return this.replace(/&/g, "&amp;") .
               replace(/</g, "&lt;")  .
               replace(/>/g, "&gt;")  .
               replace(/"/g, "&quot;").
               replace(/'/g, "&#039;");
};