module AutoCompleteMacrosHelper      
  # Adds AJAX autocomplete functionality to the text input field with the 
  # DOM ID specified by +field_id+.
  #
  # This function expects that the called action returns an HTML <ul> list,
  # or nothing if no entries should be displayed for autocompletion.
  #
  # You'll probably want to turn the browser's built-in autocompletion off,
  # so be sure to include an <tt>autocomplete="off"</tt> attribute with your text
  # input field.
  #
  # The autocompleter object is assigned to a Javascript variable named <tt>field_id</tt>_auto_completer.
  # This object is useful if you for example want to trigger the auto-complete suggestions through
  # other means than user input (for that specific case, call the <tt>activate</tt> method on that object). 
  # 
  # Required +options+ are:
  # <tt>:url</tt>::                  URL to call for autocompletion results
  #                                  in url_for format.
  # 
  # Addtional +options+ are:
  # <tt>:update</tt>::               Specifies the DOM ID of the element whose 
  #                                  innerHTML should be updated with the autocomplete
  #                                  entries returned by the AJAX request. 
  #                                  Defaults to <tt>field_id</tt> + '_auto_complete'
  # <tt>:with</tt>::                 A JavaScript expression specifying the
  #                                  parameters for the XMLHttpRequest. This defaults
  #                                  to 'fieldname=value'.
  # <tt>:frequency</tt>::            Determines the time to wait after the last keystroke
  #                                  for the AJAX request to be initiated.
  # <tt>:indicator</tt>::            Specifies the DOM ID of an element which will be
  #                                  displayed while autocomplete is running.
  # <tt>:tokens</tt>::               A string or an array of strings containing
  #                                  separator tokens for tokenized incremental 
  #                                  autocompletion. Example: <tt>:tokens => ','</tt> would
  #                                  allow multiple autocompletion entries, separated
  #                                  by commas.
  # <tt>:min_chars</tt>::            The minimum number of characters that should be
  #                                  in the input field before an Ajax call is made
  #                                  to the server.
  # <tt>:on_hide</tt>::              A Javascript expression that is called when the
  #                                  autocompletion div is hidden. The expression
  #                                  should take two variables: element and update.
  #                                  Element is a DOM element for the field, update
  #                                  is a DOM element for the div from which the
  #                                  innerHTML is replaced.
  # <tt>:on_show</tt>::              Like on_hide, only now the expression is called
  #                                  then the div is shown.
  # <tt>:after_update_element</tt>:: A Javascript expression that is called when the
  #                                  user has selected one of the proposed values. 
  #                                  The expression should take two variables: element and value.
  #                                  Element is a DOM element for the field, value
  #                                  is the value selected by the user.
  # <tt>:select</tt>::               Pick the class of the element from which the value for 
  #                                  insertion should be extracted. If this is not specified,
  #                                  the entire element is used.
  # <tt>:method</tt>::               Specifies the HTTP verb to use when the autocompletion
  #                                  request is made. Defaults to POST.
  def auto_complete_field(field_id, options = {})
    function =  "var #{field_id}_auto_completer = new Ajax.Autocompleter("
    function << "'#{field_id}', "
    function << "'" + (options[:update] || "#{field_id}_auto_complete") + "', "
    function << "'#{url_for(options[:url])}'"
    
    js_options = {}
    js_options[:tokens] = array_or_string_for_javascript(options[:tokens]) if options[:tokens]
    js_options[:callback]   = "function(element, value) { return #{options[:with]} }" if options[:with]
    js_options[:indicator]  = "'#{options[:indicator]}'" if options[:indicator]
    js_options[:select]     = "'#{options[:select]}'" if options[:select]
    js_options[:paramName]  = "'#{options[:param_name]}'" if options[:param_name]
    js_options[:frequency]  = "#{options[:frequency]}" if options[:frequency]
    js_options[:method]     = "'#{options[:method].to_s}'" if options[:method]

    { :after_update_element => :afterUpdateElement, 
      :on_show => :onShow, :on_hide => :onHide, :min_chars => :minChars }.each do |k,v|
      js_options[v] = options[k] if options[k]
    end

    function << (', ' + options_for_javascript(js_options) + ')')

    javascript_tag(function)
  end
  
  # Use this method in your view to generate a return for the AJAX autocomplete requests.
  #
  # Example action:
  #
  #   def auto_complete_for_item_title
  #     @items = Item.find(:all, 
  #       :conditions => [ 'LOWER(description) LIKE ?', 
  #       '%' + request.raw_post.downcase + '%' ])
  #     render :inline => "<%= auto_complete_result(@items, 'description') %>"
  #   end
  #
  # The auto_complete_result can of course also be called from a view belonging to the 
  # auto_complete action if you need to decorate it further.
  def auto_complete_result(entries, field, phrase = nil)
    return unless entries
    items = entries.map { |entry| content_tag("li", phrase ? highlight(entry[field], phrase) : h(entry[field])) }
    content_tag("ul", items.uniq)
  end
  
  # Wrapper for text_field with added AJAX autocompletion functionality.
  #
  # In your controller, you'll need to define an action called
  # auto_complete_for to respond the AJAX calls,
  # 
  def text_field_with_auto_complete(object, method, tag_options = {}, completion_options = {})
    (completion_options[:skip_style] ? "" : auto_complete_stylesheet) +
    text_field(object, method, tag_options) +
    content_tag("div", "", :id => "#{object}_#{method}_auto_complete", :class => "auto_complete") +
    auto_complete_field("#{object}_#{method}", { :url => { :action => "auto_complete_for_#{object}_#{method}" } }.update(completion_options))
  end

  private
    def auto_complete_stylesheet
      content_tag('style', <<-EOT, :type => Mime::CSS)
        div.auto_complete {
          width: 350px;
          background: #fff;
        }
        div.auto_complete ul {
          border:1px solid #888;
          margin:0;
          padding:0;
          width:100%;
          list-style-type:none;
        }
        div.auto_complete ul li {
          margin:0;
          padding:3px;
        }
        div.auto_complete ul li.selected {
          background-color: #ffb;
        }
        div.auto_complete ul strong.highlight {
          color: #800; 
          margin:0;
          padding:0;
        }
      EOT
    end

end   
