module ActionView::Helpers::ScriptaculousHelper

def array_or_string_for_javascript(option)

def array_or_string_for_javascript(option)
  if option.kind_of?(Array)
    "['#{option.join('\',\'')}']"
  elsif !option.nil?
    "'#{option}'"
  end
end

def draggable_element(element_id, options = {})

http://script.aculo.us for more documentation.
You can change the behaviour with various options, see

<%= draggable_element("my_image", :revert => true)
Example:

Makes the element with the DOM ID specified by +element_id+ draggable.
def draggable_element(element_id, options = {})
  javascript_tag(draggable_element_js(element_id, options).chop!)
end

def draggable_element_js(element_id, options = {}) #:nodoc:

:nodoc:
def draggable_element_js(element_id, options = {}) #:nodoc:
  %(new Draggable(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});)
end

def drop_receiving_element(element_id, options = {})

the XMLHttpRequest. Any expressions should return a valid URL query string.
* :with - A JavaScript expression specifying the parameters for

the drop - like if the Ctrl or Shift keys were pressed - from the Event object.
element and the Event object. You can extract additional information about
This callback gets three parameters: The Draggable element, the Droppable

:onDrop => "function(draggable_element, droppable_element, event) { alert('I like bananas') }"

change the default drop behaviour. Example:
this element. Override this callback with a JavaScript expression to
* :onDrop - Called when a +draggable_element+ is dropped onto

hovered over it.
this additional CSS class when an accepted +draggable_element+ is
* :hoverclass - If set, the +drop_receiving_element+ will have

:confirm => "Are you sure you want to do this?"

* :confirm - Adds a confirmation dialog. Example:

to be accepted by this +drop_receiving_element+.
allowable CSS classes that the +draggable_element+ must have in order
* :accept - Set this to a string or an array of strings describing the
Some of these +options+ include:

http://script.aculo.us for more documentation.
You can change the behaviour with various options, see

{ :controller => "cart", :action => "add" }) %>
<%= drop_receiving_element("my_cart", :url =>
Example:

of the element as parameter.
and make an AJAX call. By default, the action called gets the DOM ID
dropped draggable elements (created by +draggable_element+).
Makes the element with the DOM ID specified by +element_id+ receive
def drop_receiving_element(element_id, options = {})
  javascript_tag(drop_receiving_element_js(element_id, options).chop!)
end

def drop_receiving_element_js(element_id, options = {}) #:nodoc:

:nodoc:
def drop_receiving_element_js(element_id, options = {}) #:nodoc:
  options[:with]     ||= "'id=' + encodeURIComponent(element.id)"
  options[:onDrop]   ||= "function(element){" + remote_function(options) + "}"
  options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) }
  options[:accept] = array_or_string_for_javascript(options[:accept]) if options[:accept]
  options[:hoverclass] = "'#{options[:hoverclass]}'" if options[:hoverclass]
  # Confirmation happens during the onDrop callback, so it can be removed from the options
  options.delete(:confirm) if options[:confirm]
  %(Droppables.add(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});)
end

def sortable_element(element_id, options = {})

See http://script.aculo.us for more documentation.

is called once on each Sortable. Gets the container as its parameter.
changed in any way. When dragging from one Sortable to another, the callback
* :onUpdate - Called when the drag ends and the Sortable's order is

Sortable. Gets the affected element as its parameter.
dragging from one Sortable to another, the callback is called once on each
* :onChange - Called whenever the sort order changes while dragging. When

is false).
rules) as a child element when there are no more elements inside (default
a Droppable, that can receive a Draggable (as according to the containment
* :dropOnEmpty - If true the Sortable container will be made into

the original in place until the clone is dropped (default is false).
* :ghosting - Clones the element and drags the clone, leaving

the handle.
found within the element that has this CSS class value will be used as
(as of script.aculo.us V1.5). The first child/grandchild/etc. element
embedded handle. The value may be a string referencing a CSS class value
* :handle - Sets whether the element should only be draggable by an

when an accepted Draggable is hovered over it.
* :hoverclass - If set, the Droppable will have this additional CSS class

between levels.
and not only sort items at the same level, but drag and sort items
main sortable list. This means that you can create multi-layer lists,
* :tree - Determines whether to treat nested lists as part of the

operations if the list runs past the visual border.
* :scroll - Determines whether to scroll the list during drag

out child elements as candidates.
* :only - A CSS class name or array of class names used to filter

potential drop targets (defaults to the original target element).
* :containment - Takes an element or array of elements to treat as

sortable (default is li).
* :tag - Which children of the container element to treat as

or :vertical direction.
* :overlap - Calculate the item overlap in the :horizontal

:horizontal or :vertical (or false to make it unconstrained).
* :constraint - Whether to constrain the dragging to either

serialized id to the server (the default is /^[^_]*_(.*)$/).
* :format - A regular expression to determine what to send as the

Additional +options+ are:

the identifier part of the id attribute will be serialized.
attributes in the form "string_identifier". For example, "item_1". Only
Important: For this to work, the sortable elements must have id

of, in the current order.
containing the values of the ids of elements the sortable consists
In the example, the action gets a "my_list" array parameter

<%= sortable_element("my_list", :url => { :action => "order" }) %>

Example:

element as parameters.
changed. By default, the action called gets the serialized sortable
by drag-and-drop and make an Ajax call whenever the sort order has
Makes the element with the DOM ID specified by +element_id+ sortable
def sortable_element(element_id, options = {})
  javascript_tag(sortable_element_js(element_id, options).chop!)
end

def sortable_element_js(element_id, options = {}) #:nodoc:

:nodoc:
def sortable_element_js(element_id, options = {}) #:nodoc:
  options[:with]     ||= "Sortable.serialize(#{ActiveSupport::JSON.encode(element_id)})"
  options[:onUpdate] ||= "function(){" + remote_function(options) + "}"
  options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) }
  [:tag, :overlap, :constraint, :handle].each do |option|
    options[option] = "'#{options[option]}'" if options[option]
  end
  options[:containment] = array_or_string_for_javascript(options[:containment]) if options[:containment]
  options[:only] = array_or_string_for_javascript(options[:only]) if options[:only]
  %(Sortable.create(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});)
end

def visual_effect(name, element_id = false, js_options = {})

http://script.aculo.us for more documentation.
You can change the behaviour with various options, see

blinddown/blindup respectively.
:toggle_blind which will alternate between appear/fade, slidedown/slideup, and
For toggling visual effects, you can use :toggle_appear, :toggle_slide, and

element.
This would fade the element that was dropped on the drop receiving

<%= drop_receiving_element (...), :loading => visual_effect(:fade) %>

used for example with +drop_receiving_element+:
variable in the generated JavaScript execution context. This can be
If no +element_id+ is given, it assumes "element" which should be a local

starting visual effects.
Returns a JavaScript snippet to be used on the Ajax callbacks for
def visual_effect(name, element_id = false, js_options = {})
  element = element_id ? ActiveSupport::JSON.encode(element_id) : "element"
  js_options[:queue] = if js_options[:queue].is_a?(Hash)
    '{' + js_options[:queue].map {|k, v| k == :limit ? "#{k}:#{v}" : "#{k}:'#{v}'" }.join(',') + '}'
  elsif js_options[:queue]
    "'#{js_options[:queue]}'"
  end if js_options[:queue]
  [:endcolor, :direction, :startcolor, :scaleMode, :restorecolor].each do |option|
    js_options[option] = "'#{js_options[option]}'" if js_options[option]
  end
  if TOGGLE_EFFECTS.include? name.to_sym
    "Effect.toggle(#{element},'#{name.to_s.gsub(/^toggle_/,'')}',#{options_for_javascript(js_options)});"
  else
    "new Effect.#{name.to_s.camelize}(#{element},#{options_for_javascript(js_options)});"
  end
end