<!-- Rapid Forms provides various tags that make it quick and easy to produce working new or edit forms. 

### Overview

The main tags are:

 - `<form>`, which acts like the dumb HTML tag if you provide the `action` attribute, and picks up various Rapid smarts 
   otherwise.
   
 - `<input>`, which automatically choses an appropriate form control based on the type of the date.
 
### Ajax Attributes

Several of the tags in this taglib support the following set of ajax attributes:
                                 
 - update: one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call. Default - no
   update.
 
   NOTE: yes that's *DOM ID's* not part-names. A common source of confusion because by default the part name and DOM ID are
   the same.

 - params: a hash of name/value pairs that will be converted to HTTP parameters in the ajax request
 
 - confirm: a message to be displayed in a JavaScript confirm dialog. By default there is no confirm dialog
 
 - message: a message to be displayed in the Ajax progress spinner. Default: "Saving..."
 
 - spinner-next-to: DOM ID of an element to position the ajax progress spinner next to.
 
### Ajax Callbacks

The following attributes are also supported by all the ajax tags. Set them to fragments of javascript to have that script
executed at various points in the ajax request cycle:
 
 - before: script to run before the request
 
 - success: script to run on successful completion of the request
 
 - failure: script to run on a request failure 
 
 - complete: script to run on completion, regardless of success or failure.
 
-->
  
  
<!-- nodoc. -->
<def tag="hidden-fields" attrs="fields, for-query-string, skip"><%= 
  pairs = if for_query_string
            query_params.to_a
          else
            hiddens = case fields
                      when '*', nil
                        # TODO: Need a better (i.e. extensible) way to eleminate certain fields
                        this.class.column_names - ['type', 'created_at', 'updated_at']
                      else
                        comma_split(fields)
                      end
            hiddens.map do |field| 
              val = this.send(field)
              param_name = param_name_for(form_field_path + [field])
              [param_name, val] unless val.nil? || 
                                       field.to_sym.in?(this.class.attr_protected) || 
                                       (this.new_record? && val == this.class.column(field).default)
            end.compact
          end
  skip = comma_split skip
  pairs.reject! { |p| p.first.in?(skip) }
  pairs.map { |n, v| hidden_field_tag(n, v.to_s) if v && n.not_in?(scope.form_field_names) }.compact.join("\n")
%></def>


<!--
`<form>` has been extended in Rapid to make it easier to construct and use forms with Hobo models. In addition to the base
`<form>` tag, a form with contents is generated for each Hobo model. These are found in
`app/views/taglibs/auto/rapid/forms.dryml`.

### Usage

`<form>` can be used as a regular HTML tag: 

        <form action="/blog_posts/1" method="POST">...</form>

If no `action` attribute is provided then the context is used to construct an appropriate action using restful routing:

* If the context is a new record then the form action will be a `POST` to the create action:
  
        <form with="&BlogPost.new">...</form> -> <form action="/blog_posts" method="POST">...</form>

* If the context is a saved record then the form action will be a `PUT` to the update action. This is handled in a special
  way by Rails due to current browsers not supporting `PUT`, the method is set to `POST` with a hidden input called `_method`
  with a value of `PUT`. Hobo adds this automatically:

        <% blog_post = BlogPost.find(1) %>
        <form with="&blog_post">...</form> -> 
        <form action="/blog_posts/1" method="POST">
          <input id="_method" type="hidden" value="PUT" name="_method"/>
          ...
        </form>

AJAX based submission can be enabled by simply adding an `update` attribute. e.g.

        <div part="comments"><collection:comments/></div>
        <form with="&Comment.new" update="comments"/>

`<form>` support all of the standard ajax attributes.

### Additional Notes

- Hobo automatically inserts an `auth_token` hidden field if forgery protection is enabled

- Hobo inserts a `page_path` hidden field in create / update forms which it uses to re-render the correct page if a
  validation error occurs.

- `<form>` supports all of the standrd ajax attributes - (see the main taglib docs for Rapid Forms)
  
### Attributes

 - [all AJAX attributes](/api_taglibs/rapid_forms)

 - action: the controller action.  Default is create or update as appropriate

 - method: PUT or POST

 - web-method

 - lifecycle

 - owner

 - multipart: if set, the encoding is set to multipart/form-data.  The default is x-www-form-urlencoded

 - reset-form: Clear the form after submission (only makes sense for ajax forms)
 
 - refocus-form: Refocus the first form-field after submission (only makes sense for ajax forms)

### Parameters

The standard form tag does not have any parameters, nor does it have any default content.   However, Hobo does autogenerate polymorphic form tags for each of your models into `app/views/taglibs/auto/rapid/forms.dryml`.  These forms have the following parameters:

 - error-messages

 - field-list
 
 - actions

 - submit

 - cancel

-->
<def tag="form" polymorphic attrs="update, hidden-fields, action, method, web-method, lifecycle, owner, multipart"><%= 
  ajax_attrs, html_attrs = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)
  html_attrs[:enctype] ||= "multipart/form-data" if multipart
   
  new_record = this.try.new_record?

  method = if method.nil?
             (action || web_method || new_record) ? "post" : "put"
           else
             method.downcase
           end

  html_attrs[:action] = action || begin
     target = if owner
                collection_name = this.class.reverse_reflection(owner).name
                this.send(owner).send(collection_name)
              else
                this
              end
     action = web_method || lifecycle
     object_url(target, action, :method => method)
   end
   
  if action.nil? && (html_attrs[:action].nil? ||
                     (lifecycle.nil? && new_record && !this.creatable_by?(current_user)) ||
                     (lifecycle.nil? && !new_record && !can_edit?))
    Dryml.last_if = false
    ""
  else
    if method == "put"
      # browsers don't support put -- use post and add the Rails _method hack
      http_method_hidden = hidden_field_tag("_method", "PUT") 
      html_attrs[:method] = "post" 
    else
      html_attrs[:method] = method
    end
     
    if update || !ajax_attrs.empty?
      # add an onsubmit to convert to an ajax form if `update` is given
      function = ajax_updater(:post_form, update, ajax_attrs)
      html_attrs[:onsubmit] = [html_attrs[:onsubmit], "#{function}; return false;"].compact.join("; ")
    end
     
    hiddens = nil
    body = with_form_context do
      # It is important to evaluate parameters.default first, in order to populate scope.form_field_names
      b = parameters.default
      hiddens = self.hidden_fields(:fields => hidden_fields) if new_record
      b
    end
     
    auth_token = if method.nil? || method == 'get' || !protect_against_forgery?
                   ''
                 else
                   element(:input, {:type => "hidden", 
                           :name => request_forgery_protection_token.to_s,
                           :value => form_authenticity_token}, nil, true, true)
                 end
     
    unless method == "get"
      page_path = if (request.post? || request.put?) && params[:page_path]
                    params[:page_path]
                  else
                    view_name.sub(Dryml::EMPTY_PAGE, params[:action] || '')
                  end
      page_path_hidden = hidden_field_tag("page_path", page_path) 
    end
    
    hiddens_div = element(:div, {:class => "hidden-fields"}, [http_method_hidden, page_path_hidden, auth_token, hiddens].join)
     
    body = [hiddens_div, body].join
    
    if action.nil? # don't add automatic css classes if the action was specified
      if web_method
        add_classes!(html_attrs, "#{type_id.dasherize}-#{web_method}-form")
      else
        add_classes!(html_attrs, "#{'new ' if new_record}#{type_id.dasherize}")
      end
    end

    Dryml.last_if = true
    element("form", html_attrs, body)
  end
%></def>

<!-- A shortcut for generating a submit button.

### Usage

    <submit label="Go!"/> -> <input type="submit" value="Go!" class="button submit-button"/>
    <submit image="/images/go.png"/> -> <input type="image" src="/images/go.png" class="button submit-button"/>
  
-->
<def tag="submit" attrs="label, image">
  <input if="&image" type="image" src="&image" merge-attrs class="image-button submit-button"/>
  <else>
    <input type="submit" value="#{label}" merge-attrs class="button submit-button"/>
  </else>
</def>

<!--
Provides an editable control tailored to the type of the object in context. `<input>` tags should be used within a
`<form>`. `<input>` is a _polymorphic_ tag which means that there are a variety of definitions, each one written for a
particular type. For example there are inputs for `text`, `boolean`, `password`, `date`, `datetime`, `integer`,
`float`, `string` and more.

### Usage

The tag behaves as a regular HTML input if the type attribute is given:

    <input type="text" name="my_input"/> -> Output is exactly as provided, untouched by Rapid

If no type attribute is given then the _context_ is used. For example if the context is a blog post:

    <input:title/> -> 
    <input id="blog_post[name]" class="string blog-post-name" type="text" value="My Blog Post" name="blog_post[name]"/>

    <input:created_at/> -> 
    <select id="blog_post_created_at_year" name="blog_post[created_at][year]">...</select>
    <select id="blog_post_created_at_month" name="blog_post[created_at][month]">...</select>
    <select id="blog_post_created_at_day" name="blog_post[created_at][day]">...</select>

    <input:description/> -> 
    <textarea class="text blog-post-description" id="blog_post[description]" name="blog_post[description]">...</textarea>

If the context is a `belongs_to` association, the `<select-one>` tag is used.

If the context is a `has_many :through` association, the polymorphic `<collection-input>` tag is used.

### Attributes

 - no-edit: control what happens if `can_edit?` is false. Can be one of:
 
   - view: render the current value using the `<view>` tag
   - disable: render the input as normal, but add HTML's `disabled` attribute
   - skip: render nothing at all
   - ignore: render the input normally. That is, don't even perform the edit check.
-->
<def tag="input" attrs="no-edit"><%=
  if attributes[:type]
    element :input, attributes, nil, true, true
  else
    no_edit ||= :view
    no_edit = no_edit.to_sym
    no_edit_permission = !can_edit? unless no_edit == :ignore
    if no_edit_permission && no_edit == :view
      view
    elsif no_edit_permission && no_edit == :skip
      ""
    else
      attrs = add_classes(attributes, type_id.dasherize, type_and_field.dasherize)
      attrs[:name] ||= param_name_for_this
      attrs[:disabled] = true if no_edit_permission && no_edit == :disable
      the_input = if (refl = this_field_reflection)
                    if refl.macro == :belongs_to
                      call_polymorphic_tag('input', attrs) or select_one(attrs)
                    elsif refl.macro == :has_many
                      if refl.options[:through]
                        collection_input(attrs)
                      else
                        input_many(attrs)
                      end
                    end
                  else
                    call_polymorphic_tag('input', attrs) or
                      (call_polymorphic_tag('input', HoboFields.to_class(this_type::COLUMN_TYPE), attrs) if defined?(this_type::COLUMN_TYPE)) or
                      raise HoboError, ("No input tag for #{this_field}:#{this_type} (this=#{this.inspect})")
                  end
      if this_parent.errors[this_field]
        "<span class='field-with-errors'>#{the_input}</span>"
      else
        the_input
      end
    end
  end
%></def>


<!-- This tag is called by `<input>` when the context is a `has_many :through` collection. By default a `<select-many>`
is used, but this can be customised on a per-type basis. For example, say you would like the `<check-many>` tag used to
edit collections a `Category` model in your application:

    <def tag="collection-input" for="Category"><check-many merge/></def>
-->
<def tag="collection-input" polymorphic></def>

<!-- The default `<collection-input>` - calls `<select-many>` -->
<def tag="collection-input" for="ActiveRecord::Base"><select-many merge/></def>


<!-- A `<textarea>` input -->
<def tag="input" for="text" attrs="name">
  <%= text_area_tag(name, this, attributes) %>
</def>

<!-- A checkbox plus a hidden-field. The hidden field trick comes from Rails - it means that when the checkbox is not checked, the parameter name is still submitted, with a '0' value (the value is '1' when the checkbox is checked) -->
<def tag="input" for="boolean" attrs="name">
  <%= unless attributes[:disabled]
        cb_tag = check_box_tag(name, '1', this, attributes)
        cb_hidden_tag = hidden_field_tag(name, '0', :id => nil)
        HoboSupport::RAILS_AT_LEAST_23 ? cb_hidden_tag + cb_tag : cb_tag + cb_hidden_tag
      end %>
</def>
  
<!-- A password input - `<input type='password'>` -->
<def tag="input" for="password" attrs="name">
  <%= password_field_tag(name, this, attributes) %>
</def>

<!-- A date picker, using the `select_date` helper from Rails
  
### Attributes

 - order: The order of the year, month and day menus. A comma separated string or an array. Default: "year, month, day"
 
Any other attributes are passed through to the `select_date` helper.

The menus default to the current date if the current value is nil.
 
  -->
<def tag="input" for="date" attrs="order">
  <% order = order.nil? ? [:year, :month, :day] : comma_split(order).*.to_sym -%>
  <%= select_date(this || current_time, attributes.merge(:prefix => param_name_for_this, :order => order)) %>
</def>


<!-- A date/time picker, using the `select_date` helper from Rails
  
### Attributes

 - order: The order of the year, month and date menus. A comma separated string or an array. Default: "year, month,
   day, hour, minute, second"
 
Any other attributes are passed through to the `select_date` helper.
 
The menus default to the current time if the current value is nil.
 
  -->
<def tag="input" for="time" attrs="order">
  <% order = order.nil? ? [:year, :month, :day, :hour, :minute, :second] : comma_split(order).*.to_sym -%>
  <%= select_date(this || current_time, attributes.merge(:prefix => param_name_for_this, :order => order)) %>
</def>


<!-- A date/time picker, using the `select_datetime` helper from Rails
  
### Attributes

 - order: The order of the year, month and date menus. A comma separated string or an array. Default: "year, month,
   day, hour, minute"
 
Any other attributes are passed through to the `select_datetime` helper.
 
The menus default to the current time if the current value is nil.

  -->
<def tag="input" for="datetime" attrs="order">
  <% if ! order.nil?
       order = comma_split(order).*.to_sym 
       attributes.merge!(:order => order)
     end -%>
  <%= select_datetime(this || current_time, attributes.merge(:prefix => param_name_for_this)) %>
</def>

<!-- An `<input type='text'>` input. -->
<def tag="input" for="integer" attrs="name">
  <%= text_field_tag(name, this, attributes) %>
</def>

<!-- An `<input type='text'>` input. -->
<def tag="input" for="BigDecimal" attrs="name">
  <%= text_field_tag(name, this, attributes) %>
</def>

<!-- An `<input type='text'>` input. -->
<def tag="input" for="float" attrs="name">
  <%= text_field_tag(name, this, attributes) %>
</def>

<!-- An `<input type='text'>` input. -->
<def tag="input" for="string" attrs="name">
  <%= text_field_tag(name, this, attributes) %>
</def>

<!-- A `<select>` menu containing the values of an 'enum string'.
  
### Attributes

 - `labels` - A hash that gives custom labels for the values of the enum.
   Any values that do not have corresponding keys in this hash will get `value.titleize` as the label.
 - `titleize` - Set to false to have the value itself (rather than `value.titleize`) be the default label. Default: true
 - `first-option` - a string to be used for an extra option in the first position. E.g. "Please choose..."
 - `first-value` - the value to be used with the `first-option`. Typically not used, meaning the option has a blank value.
  
   -->
<def tag="input" for="HoboFields::EnumString" attrs="labels, titleize, first-option, first-value"><%
  labels ||= {}
  labels = HashWithIndifferentAccess.new(labels)
  titleize = true if titleize.nil?
  options = this_type.values.map {|v| [labels.fetch(v, titleize ? this_type.translated_values[v].titleize : this_type.translated_values[v]), v] }
  %>
  <select name="#{param_name_for_this}" merge-attrs>
    <option value="#{first_value}" unless="&first_option.nil?"><first-option/></option>
    <%= options_for_select(options, this) %>
  </select>
</def>


<!-- Provides either an ajax or non-ajax button to invoke a "remote method" or "web method" declared in the controller.
Web Methods provide support for the RPC model of client-server interaction, in contrast to the REST model. The
preference in Rails is to use REST as much as possible, but we are pragmatists, and sometimes you just to need a remote
procedure call.

The URL that the call is POSTed to is the `object_url` of `this`, plus the method name

`<remote-method-button>` supports all of the standard ajax attributes (see the main taglib documention for Rapid
Forms). If any ajax attributes are given, the button becomes an ajax button, if not, Rails' `button_to` is used, which behaves similarly to a standard link.

### Attributes 

 - method: the name of the web-method to call
 
 - label: the label on the button
 
 -->
<def tag="remote-method-button" attrs="method, update, label, confirm, url"><%= 
  ajax_attributes, html_attributes = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)

  url ||= object_url(this, method.to_s.gsub('-', '_'), :method => :post)
  raise ArgumentError, "no such web method '#{method}' on #{this.typed_id}" unless url
  
  add_classes!(html_attributes, "button remote-method-button #{method}-button")
  label ||= method.titleize
  if update || !ajax_attributes.empty?
    ajax_attributes[:message] ||= label
    func = ajax_updater(url, update, ajax_attributes.merge(:confirm => confirm))
    html_attributes.update(:onclick => "var e = this; " + func, :type =>'button', :value => label)
    element(:input, html_attributes, nil, true, true)
  else
    button_to(label, url, html_attributes.merge(:confirm => confirm))
  end
%></def>

  
<!-- Provides an ajax button to send a RESTful update or "PUT" to the server. i.e. to udate one or more fields of a
record.
  
Note that unlike simliar tags, `<update-button>` does not support both ajax and non-ajax modes at this time. It only
does ajax.

`<update-button>` supports all of the standard ajax attributes (see the main taglib documention for Rapid Forms).

### Attributes

 - label: The label on the button. 
 
 - fields: A hash with new field values pairs to update the resource with. The items in the hash will be converted to
   HTTP parameters.
 
 - params: Another hash with additional HTTP parameters to include in the ajax request
 
  -->
<def tag="update-button" attrs="label, update, fields, params"><%=
   raise HoboError.new("no update specified") unless update

   ajax_attributes, html_attributes = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)
   params = (params || {}).merge(this.class.name.underscore => fields)
   ajax_attributes.reverse_merge!(:message => label, :params => params, :method => :put)
   func = ajax_updater(object_url(this), update, ajax_attributes)
   html_attributes.reverse_merge!(:type =>'button', :onclick => func, :value => label)
   
   element :input, add_classes(html_attributes, "button update-button update-#{this.class.name.underscore}-button"), nil, true, true %>
</def>


<!-- Provides either an ajax or non-ajax delete button to send a RESTful "DELETE". The context should be a record for
which you to want provide a delete button.

The Rapid Library has a convention of marking (in the output HTML, using a special CSS class) elements as "object
elements", with the class and ID of the ActiveRecord object that they represent. `<delete-button>` assumes it is placed
inside such an element, and will automatically find the right element to remove (fade out) from the DOM. The
`<collection>` tag adds this metadata (CSS class) automatically, so `<delete-button>` works well when used inside a
`<collection>`. This is a Clever Trick which needs to be revisted and perhaps simplified.

If used within a `<collection>`, `<delete-button>` also knows how to add an "empty message" such as "no comments to
display" when you delete the last item. Clever Tricks abound.

Current limitation: There is no support for the ajax callbacks at this time.

All the standard ajax attributes *except the callbacks* are supported (see the main taglib documention for Rapid Forms).



### Attributes

 - label: The label for the button. Default: "Remove"
 
 - in-place: delete in place (ajax)? Default: true, or false if the record to be deleted is the same as the top level
   context of the page

 - image: URL of an image for the button. Changes the rendered tag from `<input type='button'>` to `<input type='image'
   src='...'>`
 
 - fade: Perform the fade effect (true/false)? Default: true
 
  -->
<def tag="delete-button" attrs="label, update, in-place, image, confirm, fade, subsite"><%=
  in_place = false if in_place.nil? && this == @this && request.method == :get
  url = object_url(this, :method => :delete, :subsite => subsite)
  if (Dryml.last_if = url && can_delete?)
    attributes = attributes.merge(if image
                                    { :type => "image", :src => "#{base_url}/images/#{image}" }
                                  else
                                    { :type => "button" }
                                  end)
    label ||= ht("hobo.actions.remove", :default=>"Remove") 
    confirm = ht("hobo.messages.confirm", :default=>"Are you sure?") if confirm.nil?
    
    add_classes!(attributes,
                 image ? "image-button" : "button",
                 "delete-button delete-#{this.class.name.underscore.dasherize}-button")
    if url
      if in_place == false
        attributes[:confirm] = confirm if confirm
        attributes[:method] = :delete
        button_to(label, url, attributes)
      else
        fade = true if fade.nil?
        attributes[:value] = label
        attributes[:onclick] = "Hobo.removeButton(this, '#{url}', #{js_updates(update)}, {fade:#{fade}, confirm: #{confirm.inspect}})"
        element(:input, attributes, nil, true, true)
      end
    end
  else
    ""
  end
%></def>


<!-- Provides an ajax create button that will send a RESTful "POST" to the server to create a new resource.
  
All of the standard ajax attributes are supported (see the main taglib documention for Rapid Forms).

### Attributes

 - model: The class to instantiate, pass either the class name or the class object.

 -->
<def tag="create-button" attrs="model, update, label, fields, message"><%=
  raise HoboError.new("no update specified") unless update

  fields ||= {}
  class_or_assoc = if model
                     model.is_a?(String) ? model.constantize : model
                   elsif Hobo.simple_has_many_association?(this)
                     fields[this_field_reflection.primary_key_name] = this.proxy_owner.id
                     this
                   else
                     raise HoboError.new("invalid context for <create-button>")
                   end
  new = class_or_assoc.new(fields)
  new.set_creator(current_user)
  if can_create?(new)
    label ||= ht("#{new.class.name.pluralize.underscore}.actions.new", :default=>"New #{new.class.name.titleize}")  
    ajax_attributes = { :message => message }
    class_name = new.class.name.underscore
    ajax_attributes[:params] = { class_name => fields } unless fields.empty?
    func = ajax_updater(object_url(new.class, :method => :post), update, ajax_attributes)
    element :input, add_classes(attributes.merge(:type =>'button', :onclick => func, :value => label),
                                "button create-button create-#{class_name}-button"), nil, true, true
  end
%></def>


<!-- A `<select>` menu from which the user can choose the target record for a `belongs_to` association.

This is the default input that Rapid uses for `belongs_to` associations. The menu is constructed using the `to_s` representation of the records.

### Attributes

 - `include-none` - whether to include a 'none' option (i.e. set the foreign key to null).   If this value is not supplied, the default is "true" if the current value is nil; otherwise the default is "false".  One implication of this is that the default may change when the form is re-rendered due to a validation failure.  Setting this value explicitly is recommended.
 - `blank-message` - the message for the 'none' option. Defaults to "(No `<model-name>`)", e.g. "(No Product)"
 - `options` - an array of records to include in the menu. Defaults to the all the records in the target table that match any `:conditions` declared on the `belongs_to` (subject to `limit`)
 - `limit` - if `options` is not specified, this limits the number of records.  Default: 100
 - `text_method` - The method to call on each record to get the text for the option.  Multiple methods are supported ie "institution.name"
 
### See Also

For situations where there are too many target records to practically include in a menu, `<name-one>` provides an autocompleter which would be more suitable.

  -->
<def tag="select-one" attrs="include-none, blank-message, options, sort, limit, text-method"><%
  raise HoboError.new("Not allowed to edit #{this_field}") if !attributes[:disabled] && !can_edit? 

  blank_message ||= ht("#{this_type.name.underscore}.message.no", :default=>"(No #{this_type.view_hints.model_name})")
  limit ||= 100
   
  options ||= begin
    conditions = ActiveRecord::Associations::BelongsToAssociation.new(this_parent, this_field_reflection).conditions
    this_field_reflection.klass.all(:conditions => conditions, :limit => limit).select {|x| can_view?(x)}
  end

  if text_method.nil?
    select_options = options.map { |x| [x.to_s, x.id] }
  else
    select_options = options.map do |x| 
      [ text_method.split(".").inject(x) { |v, method| v.send(method) },
           x.id ]
    end
  end
  select_options = select_options.sort if sort
  select_options.insert(0, [blank_message, ""]) if include_none || (this.nil? && include_none != false)
  attributes = add_classes(attributes, "input", "belongs_to", type_and_field)
  -%>
  <select name="#{param_name_for_this(true)}" merge-attrs="&attributes.except :name">
    <%= options_for_select(select_options, this ? this.id : "") %>
  </select>
</def>


<!-- An `<input type="text">` with auto-completion. Allows the user to chose the target of a `belongs_to` association by name.

This tag relies on an autocompleter being defined in a controller.  A simple example:

    <form with="&ProjectMembership.new">
      <name-one:user>
    </form>

    class ProjectMembership < ActiveRecord::Base
      hobo_model
      belongs_to :user
    end

    class User < ActiveRecord::Base
      hobo_user_model
      has_many :project_memberships, :accessible => true, :dependent => :destroy
    end

    class UsersController < ApplicationController
      autocomplete
    end

The route used by the autocompleter looks something like `/users/complete_name`.  The first part of this route is specified by the `complete-target` attribute, and the second part is specified by the `completer` attribute.

`complete-target` specifies the controller for the route.  It can be specified by either supplying a model class or a model.  If a model is supplied, the id of the model is passed as a parameter to the controller.  (`?id=7`, for example)  The default for this attribute is the class of the context.   In other words, the class that contains the `has_many / has_one`, not the class with the `belongs_to`.

`completer` specifies the action for the route.   `name-one` prepends `complete_` to the value given here.  This should be exactly the same as the first parameter to `autocomplete` in your controller.   As an example:  `autocomplete :email_address` would correspond to `completer="email_address"`.  The default for this attribute is the name field for the model being searched, which is usually `name`, but not always.

The query string is passed to the controller in the `query` parameter.  (`?query=hello` for example).

For more information on how to customize the controller, see the [controller manual](http://cookbook.hobocentral.net/manual/controllers#autocompleters)

Here's a more complex example.  This used to be a part of [agility](http://cookbook.hobocentral.net/tutorials/agility) until it was simplified.

    class ProjectsController < ApplicationController
      autocomplete :new_member_name do
        project = find_instance
        hobo_completions :name, User.without_project(project).is_not(project.owner)
      end
    end

Note that this was added to the projects controller, rather than the users controller as in the first example.  You can read this as: create an auto-complete action called `new_member_name` that finds users that are not already members of the project, and not the owner of the project, and completes the :name field.

    <name-one:user complete-target="&@project" completer="new_member_name"/>

We're using an object as the complete-target rather than a class.   This allows the `find_instance` in our controller action to function.

There's another example of `<name-one>` use in the [recipes](http://cookbook.hobocentral.net/recipes/36-using-a-name-one-one-a).

### Attributes:

- `complete-target`, `completer`: see above
- `min-chars`: The minimum number of characters that must be entered in the input field before an Ajax request is made.
- `nil-value`: If there is no current value, this text will appear greyed out inside the control, and will disappear on focus.

### Note:

If you wish to set `min-chars` to 0, you will require this [patch to controls.js](http://github.com/bryanlarsen/scriptaculous/commit/3915b7b).   'controls.js' was added to your project via the rails generator, not via Hobo.

  -->
<def tag="name-one" attrs="complete-target, completer, min-chars, nil-value"><%
  complete_target ||= this_field_reflection.klass
  completer ||= (complete_target.is_a?(Class) ? complete_target : complete_target.class).name_attribute
  min_chars ||= 1
  value = name(:no_wrapper => true, :if_present => true)
  -%>
  <input type="text" name="#{param_name_for_this}" 
         class="autocompleter #{type_and_field._?.dasherize} #{css_data :complete_on, typed_id(complete_target), completer} #{css_data :min_chars, min_chars} #{'nil-value' if value==''}"
         value="#{value=='' ? nil_value : value}"
         merge-attrs/>
  <div class="completions-popup" style="display:none"></div>
</def>


<!-- nodoc. -->
<def tag="sti-type-input">
  <select name="#{param_name_for(form_field_path + ['type'])}">
    <%= options_for_select(this.class.send(:subclasses).map{|x| [x.name.titleize, x.name]}, this.class.name) %>
  </select>
</def>


<!-- A `<select>` menu input. This tag differes from `<select-menu>` only in that it adds the correct `name` attribute for the current field, and `selected` default to `this`.
  
### Attributes
 
 - `options` - an array of options suitable to be passed to the Rails `options_for_select` helper.
 - `selected` - the value (from the `options` array) that should be initially selected. Defaults to `this`
 - `first-option` - a string to be used for an extra option in the first position. E.g. "Please choose..."
 - `first-value` - the value to be used with the `first-option`. Typically not used, meaning the option has a blank value.

  -->
<def tag="select-input">
  <select-menu name="#{param_name_for_this}" selected="&this" merge/>
</def>


<!-- Renders a readable list of error messages following a form submission. Expects the errors to be in `this.errors`. Renders nothing if there are no errors.
  -->
<def tag="error-messages">
  <section class="error-messages" merge-attrs if="&this.errors.length > 0">
    <h2 param="heading">To proceed please correct the following:</h2>
    <ul param>
      <% this.errors.each do |attr, message|; next if message == "..." -%>
        <li param><%= this.class.human_attribute_name(attr) unless attr.to_s == 'base' %> <%= message %></li>
      <% end -%>
    </ul>
  </section>
</def>


<!--
An input for `has_many :through` associations that lets the user chose the items from a `<select>` menu.
  
To use this tag, the model of the items the user is chosing *must* have unique names, and the 
-->
<def tag="select-many" attrs="options, targets, remove-label, prompt, disabled, name"><%
  prompt ||= "Add #{this_field.titleize.singularize}"
  options ||= this_field_reflection.klass.all(:conditions =>this.conditions).select {|x| can_view?(x)}
  name ||= param_name_for_this
                
  values = this
  remove_label ||= ht("hobo.actions.remove", :default=>'Remove')
  -%>
  <div class="input select-many" merge-attrs>
    <div style="display:none" class="item-proto">
      <div class="item" param="proto-item">
        <span></span>
        <input type="hidden" name="#{name}[]" param="proto-hidden"/>
        <input type="button" class="remove-item" value="#{remove_label}" param="proto-remove-button"/>
      </div>
    </div>
    <div class="items">
      <div class="item" param="item" repeat>
        <span><%= h this.to_s %></span>
        <input type="hidden" name="#{name}[]" value="@#{h this.id}" disabled="&disabled"
               param="hidden"/>
        <input type="button" class="remove-item" value="#{remove_label}" disabled="&disabled"
               param="remove-button"/>
      </div>
    </div>
    <select merge-attrs="&{:disabled => disabled}">
      <option value=""><prompt/></option>
      <repeat with="&options">
        <if test="&this.in?(values)">
          <optgroup class="disabled-option" label="#{h this.to_s}" alt="@#{this.id}">&nbsp;</optgroup>
        </if>
        <else>
          <option value="@#{this.id}"><%= h this.to_s %></option>
        </else>
      </repeat>
    </select>
  </div>
</def>


<!--
Used inside a form to specify where to redirect after successful submission. This works by inserting a hidden field called `after_submit` which is used by Hobo if present to perform a redirect after the form submission.

### Usage

Use the `stay-here` attribute to remain on the current page:

    <form>
      <after-submit stay-here/>
      ...
    </form>

Use the `go-back` option to return to the previous page:

    <form>
      <after-submit go-back/>
      ...
    </form>

Use the `uri` option to specify a redirect location:

    <form>
      <after-submit uri="/admin"/>
      ...
    </form>
-->
<def tag="after-submit" attrs="uri, stay-here, go-back"><% 
  uri = "stay-here" if stay_here
  uri = session[:previous_uri] if go_back
  -%>
  <input type="hidden" value="&params[:after_submit] || uri" name="after_submit" if="&uri"/>
</def>


<!-- A simple wrapper around the `<select>` tag and `options_for_select` helper
  
  ### Attributes

 - `options` - an array of options suitable to be passed to the Rails `options_for_select` helper.
 - `selected` - the value (from the `options` array) that should be initially selected. Defaults to `this`
 - `first-option` - a string to be used for an extra option in the first position. E.g. "Please choose..."
 - `first-value` - the value to be used with the `first-option`. Typically not used, meaning the option has a blank value.

 -->
<def tag="select-menu" attrs="options, selected, first-option, first-value">
  <select merge-attrs param="default">
    <% selected=this if selected.nil? %>
    <option value="#{first_value}" unless="&first_option.nil?"><first-option/></option>
    <do param="options"><% options_for_select(options, selected) %></do>
  </select>
</def>


<!-- Renders a `<ul>` list of checkboxes, one for each of the potential targt in a `has_many` association. The user can check the items they wish to have associated. A typical use might be selecting categories for a blog post.

### Attributes

 - `options` - an array of models that may be added to the collection
 - `disabled` - if true, sets the disabled flag on all check boxes.

  -->
<def tag="check-many" attrs="options, disabled"><% 
  collection = this
  param_name = param_name_for_this
  options ||= begin
    conditions = ActiveRecord::Associations::BelongsToAssociation.new(this_parent, this_field_reflection).conditions
    this_field_reflection.klass.all(:conditions => conditions, :limit => 100).select {|x| can_view?(x)}
  end
  -%>
  <ul class="check-many" param="default" merge-attrs>
    <input type="hidden" name="#{param_name}[]" value=""/><% # ensure all items are removed when nothing checked 
    %>
    <li repeat="&options" param>
      <input type="checkbox" name="#{param_name}[]" value="@#{this.id}" checked="&this.in?(collection)" disabled="&disabled"/>
      <name param/>
    </li>
  </ul>
</def>


<!-- Renders an `<input type='hidden'>` for the `id` field of the current context -->
<def tag="hidden-id-field">
  <if:id><input type="hidden" name="#{param_name_for_this}" value="#{this}" /></if>
</def>


<!-- Creates a sub-section of the form which the user can repeat using (+) and (-) buttons, in order to allow an entire `has_many` collection to be created/edited in a single form.
  
This tag is very different from tags like `<select-many>` and `<check-many>` in that:

 - Those tags are used to *choose existing records* to include in the association, while `<input-many>` is used to actually create or edit the records in the association.
 
### Example

Say you are creating a new `Category` in your online shop, and you want to create some initial products *in the same form*, you can add the following to your form:

    <input-many:products><field-list fields="name, price"/></input-many>
    
The body of the tag will be repeated for each of the current records in the collection, or will just appear once (with blank fields) if the colleciton is empty.

### Attributes

 - fields:  If you do not specify any content for the input-many, a `<field-list>` is rendered.   This attribute is passed through to the `<field-list>`

 - skip:  Passed through to the `<field-list>`.  If not specified, it defaults to the parent association.
  
### Example

Say you are creating a new `Category` in your online shop, and you want to create some initial products *in the same form*, you can add the following to your form:

    <hjq-input-many:products fields="name, price" />
    
You'll often want to provide the `item` parameter:

    <hjq-input-many:products><item:><field-list fields="name, price" /></item:></hjq-input-many>

A fully worked up example of nested hjq-input-many's may be found in [agility/jquery-test](http://github.com/tablatom/agility/blob/jquery-test/app/views/projects/nested_has_many_test.dryml)

### Attributes

 - `minimum`: the minimum number of items in the collection.  Currently only '0' and '1' are supported values.  The default is '0'.

 - `fields`, `skip`: passed down to the `field-list` tag in the default `item`.

 - `template`: the default values for new items.  Normally this functionality is better provided by Model.new, but it's here if you need it.

### Events

 - `rapid:add`: fired after the element is inserted.  `memo.element`
 is set to the new element inserted.

 - `rapid:remove`: fired before the element is
 inserted. `memo.element` is set to the element to be deleted.  The
 removal may be cancelled by stopping the event.

 - `rapid:change`: fired after an element has been removed or
 inserted.  `memo.element` set as above.

Example javascript:

      var last_added;
      var last_removed;
      Event.addBehavior({
        '.stories:rapid:add' : function(ev) {
          last_added = ev.memo.element;
        },
        '.stories:rapid:remove' : function(ev) {
          if(!confirm("really?")) ev.stop();
        }
      });

Note: if your javascript does not work, please ensure that you have
the Hobo version of lowpro.js.

  -->
<def tag="input-many" attrs="minimum, fields, skip, more-skip, template" polymorphic >
<%
# helper function to create id's on buttons to facilitate testing
def underize(s)
  s.gsub(/\[/,"_").gsub(/\]/,"")
end
%>
  <set empty="&this.empty?"/>
  <% template ||= this.try.new_candidate || this.member_class.new %>
  <% minimum ||= 0 ; minimum = minimum.to_i %>
  <% skip ||= this.proxy_reflection.klass.reflect_on_all_associations.detect {|p| p.primary_key_name==this.proxy_reflection.primary_key_name}.try.name.to_s if this.respond_to? :proxy_reflection %>
  <% skip += ",#{more_skip}" if more_skip -%>
  <ul class="input-many #{this_field.dasherize} #{css_data :input_many_prefix, param_name_for_this} #{css_data(:minimum, minimum)}" merge-attrs>
    <fake-field-context fake-field="-1" context="&template">
      <li class="input-many-li input-many-template" id="#{underize param_name_for_this}">
        <div class="input-many-item" param="default">
          <field-list param merge-attrs="fields" skip="&skip" />
        </div>
        <div class="buttons">
          <button param="remove-item" id="#{underize param_name_for_this}_remove">-</button>
          <button param="add-item" id="#{underize param_name_for_this}_add">+</button>
        </div>
      </li>
    </fake-field-context>
    <li class="input-many-li empty #{'hidden' unless this.empty? and minimum==0}" id="#{underize param_name_for_this}_-1_empty">
      <!-- HACK way to signal an empty collection to the controller -->
      <input type="hidden" class="empty-input" id="#{underize param_name_for_this}" name="#{param_name_for_this}" value="" disabled="&(!this.empty? || minimum>0)" />
      <fake-field-context fake-field="-1" context="&template">
        <div param="empty-message">
          <ht key="#{this.class.class_name.tableize}.collection.empty_message">          
            No <%= this.class.class_name.titleize.downcase.pluralize %>.
          </ht>
        </div>
        <div class="buttons">
          <button param="remove-item" class="hidden" id="#{underize param_name_for_this}_remove">-</button>
          <button param="add-item" id="#{underize param_name_for_this}_add">+</button>
        </div>
      </fake-field-context>      
    </li>
    <fake-field-context fake-field="0" context="&template">
      <li class="input-many-li" if="&(this_parent.empty? && minimum>0)" id="#{underize param_name_for_this}">
        <div class="input-many-item" param="default">
          <field-list param merge-attrs="fields" skip="&skip" />
        </div>
        <div class="buttons">
          <button param="remove-item" class="hidden" id="#{underize param_name_for_this}_remove">-</button>
          <button param="add-item" id="#{underize param_name_for_this}_add">+</button>
        </div>
      </li>
    </fake-field-context>       
    <li repeat class="input-many-li #{'record-with-errors' unless this.errors.empty?}" id="#{underize param_name_for_this}">
      <error-messages without-heading class="sub-record"/>
      <hidden-id-field/>
      <div class="input-many-item" param="default">
        <field-list param merge-attrs="fields" skip="&skip" />
      </div>
      <div class="buttons">
        <button param="remove-item" class="#{'hidden' if this_parent.length<=minimum}" id="#{underize param_name_for_this}_remove">-</button>
        <button param="add-item" class="#{'hidden' if not last_item?}" id="#{underize param_name_for_this}_add">+</button>
      </div>
    </li>
  </ul>
</def>

<!-- Renders a sub-section of a form with fields for every record in a `has_many` association. This is similar to `<input-many>` except there is no ability to add and remove items (i.e. no (+) and (-) buttons).
  -->
<def tag="input-all">
  <% association_fkey = this_field_reflection.primary_key_name -%>
  <ul class="input-all #{this_field.dasherize}">
    <li repeat class="#{'record-with-errors' unless this.errors.empty?}">
      <set-scoped form-field-names="&[]">
        <hidden-id-field/>
        <do param="default"/>
        <hidden-fields skip="&association_fkey"/>
      </set-scoped>
    </li>
  </ul>
</def>

<!-- An enhanced version of [`<input-many>`](/api_tag_defs/input-many)  that supports drag-and-drop re-ordering.

Each item in the collection has a `<div class="ordering-handle" param="handle">` added, which can be used to drag the item up and down.

If the items in the collection contain an [`acts_as_list`](http://ar.rubyonrails.org/classes/ActiveRecord/Acts/List/ClassMethods.html) declaration, it is updated.

The specified sort order may be maintained even without `acts_as_list`.  The items will be passed to the controller in the correct order, so the order may be persisted there.

### Attributes

 - `id`:  Due to a limitation in script.aculo.us, an id is required.  If you do not supply one, one will be generated.
 - `position-column`:  The position column may be specified via `acts_as_list`, via a `position_column` method on your model or via this attribute.
 - others:  all other attributes are passed through to `<input-many>`

-->

<def tag="sortable-input-many" attrs="id, position-column, template">
  <% this_id = this_parent.id || rand(100000) -%>
  <% id ||= "sortable-input-many-#{this_parent.class.name.underscore.dasherize}-#{this_id}-#{this_field_reflection.name}" -%>
  <% template ||= this.try.new_candidate || this.member_class.new %>
  <% position_column ||= template.try.position_column -%>
  <input-many merge id="&id" class="sortable-input-many" template="&template" more-skip="&position_column">
    <default: replace>
        <div class="ordering-handle" param="handle" if="&can_edit?">&uarr;<br/>&darr;</div>
        <if test="&position_column">
          <input class="sortable-position" type="hidden" value="&this.send(position_column)" name="#{param_name_for_this}[#{position_column}]" />
        </if>
        <default restore/>
    </default:>
  </input-many>
</def>

<!-- Renders the common "or (Cancel)" for a form. Attributes are merged into the link (`<a>Cancel</a>`), making it easy to customise the destination of the cancel link. By default it will link to `this` or `this.class`.
  -->
<def tag="or-cancel">
  <if test="&linkable?"><ht key="hobo.support.or">or</ht> <a merge-attrs><ht key="hobo.actions.cancel">Cancel</ht></a></if>
  <else>
    <if test="&linkable?(this.class)"><ht key="hobo.support.or">or</ht> <a to="&this.class" merge-attrs><ht key="hobo.actions.cancel">Cancel</ht></a></if>
  </else>
</def>