diff options
Diffstat (limited to 'actionpack/lib/action_view/helpers/scriptaculous_helper.rb')
-rw-r--r-- | actionpack/lib/action_view/helpers/scriptaculous_helper.rb | 142 |
1 files changed, 89 insertions, 53 deletions
diff --git a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb index 04af2781d7..37319cca1b 100644 --- a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb +++ b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb @@ -3,11 +3,11 @@ require 'active_support/json' module ActionView module Helpers - # Provides a set of helpers for calling Scriptaculous JavaScript + # Provides a set of helpers for calling Scriptaculous JavaScript # functions, including those which create Ajax controls and visual effects. # - # To be able to use these helpers, you must include the Prototype - # JavaScript framework and the Scriptaculous JavaScript library in your + # To be able to use these helpers, you must include the Prototype + # JavaScript framework and the Scriptaculous JavaScript library in your # pages. See the documentation for ActionView::Helpers::JavaScriptHelper # for more information on including the necessary JavaScript. # @@ -18,22 +18,17 @@ module ActionView unless const_defined? :TOGGLE_EFFECTS TOGGLE_EFFECTS = [:toggle_appear, :toggle_slide, :toggle_blind] end - + # Returns a JavaScript snippet to be used on the Ajax callbacks for # starting visual effects. # - # Example: - # <%= link_to_remote "Reload", :update => "posts", - # :url => { :action => "reload" }, - # :complete => visual_effect(:highlight, "posts", :duration => 0.5) - # # If no +element_id+ is given, it assumes "element" which should be a local - # variable in the generated JavaScript execution context. This can be + # variable in the generated JavaScript execution context. This can be # used for example with +drop_receiving_element+: # # <%= drop_receiving_element (...), :loading => visual_effect(:fade) %> # - # This would fade the element that was dropped on the drop receiving + # This would fade the element that was dropped on the drop receiving # element. # # For toggling visual effects, you can use <tt>:toggle_appear</tt>, <tt>:toggle_slide</tt>, and @@ -44,13 +39,13 @@ module ActionView # http://script.aculo.us for more documentation. 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 @@ -61,7 +56,7 @@ module ActionView "new Effect.#{name.to_s.camelize}(#{element},#{options_for_javascript(js_options)});" end end - + # Makes the element with the DOM ID specified by +element_id+ sortable # by drag-and-drop and make an Ajax call whenever the sort order has # changed. By default, the action called gets the serialized sortable @@ -71,84 +66,84 @@ module ActionView # # <%= sortable_element("my_list", :url => { :action => "order" }) %> # - # In the example, the action gets a "my_list" array parameter - # containing the values of the ids of elements the sortable consists + # In the example, the action gets a "my_list" array parameter + # containing the values of the ids of elements the sortable consists # of, in the current order. # # Important: For this to work, the sortable elements must have id # attributes in the form "string_identifier". For example, "item_1". Only # the identifier part of the id attribute will be serialized. - # + # # Additional +options+ are: # # * <tt>:format</tt> - A regular expression to determine what to send as the # serialized id to the server (the default is <tt>/^[^_]*_(.*)$/</tt>). - # + # # * <tt>:constraint</tt> - Whether to constrain the dragging to either # <tt>:horizontal</tt> or <tt>:vertical</tt> (or false to make it unconstrained). - # + # # * <tt>:overlap</tt> - Calculate the item overlap in the <tt>:horizontal</tt> # or <tt>:vertical</tt> direction. - # + # # * <tt>:tag</tt> - Which children of the container element to treat as # sortable (default is <tt>li</tt>). - # + # # * <tt>:containment</tt> - Takes an element or array of elements to treat as # potential drop targets (defaults to the original target element). - # + # # * <tt>:only</tt> - A CSS class name or array of class names used to filter # out child elements as candidates. - # + # # * <tt>:scroll</tt> - Determines whether to scroll the list during drag # operations if the list runs past the visual border. - # + # # * <tt>:tree</tt> - Determines whether to treat nested lists as part of the # main sortable list. This means that you can create multi-layer lists, # and not only sort items at the same level, but drag and sort items # between levels. - # + # # * <tt>:hoverclass</tt> - If set, the Droppable will have this additional CSS class - # when an accepted Draggable is hovered over it. - # + # when an accepted Draggable is hovered over it. + # # * <tt>:handle</tt> - Sets whether the element should only be draggable by an # embedded handle. The value may be a string referencing a CSS class value # (as of script.aculo.us V1.5). The first child/grandchild/etc. element # found within the element that has this CSS class value will be used as # the handle. - # + # # * <tt>:ghosting</tt> - Clones the element and drags the clone, leaving # the original in place until the clone is dropped (default is <tt>false</tt>). - # + # # * <tt>:dropOnEmpty</tt> - If true the Sortable container will be made into # a Droppable, that can receive a Draggable (as according to the containment # rules) as a child element when there are no more elements inside (default # is <tt>false</tt>). - # + # # * <tt>:onChange</tt> - Called whenever the sort order changes while dragging. When # dragging from one Sortable to another, the callback is called once on each # Sortable. Gets the affected element as its parameter. - # + # # * <tt>:onUpdate</tt> - Called when the drag ends and the Sortable's order is # changed in any way. When dragging from one Sortable to another, the callback # is called once on each Sortable. Gets the container as its parameter. - # + # # See http://script.aculo.us for more documentation. def sortable_element(element_id, options = {}) javascript_tag(sortable_element_js(element_id, options).chop!) end - + 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 @@ -156,24 +151,24 @@ module ActionView # # Example: # <%= draggable_element("my_image", :revert => true) - # + # # You can change the behaviour with various options, see # http://script.aculo.us for more documentation. def draggable_element(element_id, options = {}) javascript_tag(draggable_element_js(element_id, options).chop!) end - + def draggable_element_js(element_id, options = {}) #:nodoc: %(new Draggable(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});) end # Makes the element with the DOM ID specified by +element_id+ receive # dropped draggable elements (created by +draggable_element+). - # and make an AJAX call. By default, the action called gets the DOM ID + # and make an AJAX call. By default, the action called gets the DOM ID # of the element as parameter. # # Example: - # <%= drop_receiving_element("my_cart", :url => + # <%= drop_receiving_element("my_cart", :url => # { :controller => "cart", :action => "add" }) %> # # You can change the behaviour with various options, see @@ -181,46 +176,87 @@ module ActionView # # Some of these +options+ include: # * <tt>:accept</tt> - Set this to a string or an array of strings describing the - # allowable CSS classes that the +draggable_element+ must have in order + # allowable CSS classes that the +draggable_element+ must have in order # to be accepted by this +drop_receiving_element+. - # + # # * <tt>:confirm</tt> - Adds a confirmation dialog. Example: - # + # # :confirm => "Are you sure you want to do this?" - # + # # * <tt>:hoverclass</tt> - If set, the +drop_receiving_element+ will have # this additional CSS class when an accepted +draggable_element+ is - # hovered over it. - # + # hovered over it. + # # * <tt>:onDrop</tt> - Called when a +draggable_element+ is dropped onto - # this element. Override this callback with a JavaScript expression to + # this element. Override this callback with a JavaScript expression to # change the default drop behaviour. Example: - # + # # :onDrop => "function(draggable_element, droppable_element, event) { alert('I like bananas') }" - # + # # This callback gets three parameters: The Draggable element, the Droppable # element and the Event object. You can extract additional information about # the drop - like if the Ctrl or Shift keys were pressed - from the Event object. - # + # # * <tt>:with</tt> - A JavaScript expression specifying the parameters for # the XMLHttpRequest. Any expressions should return a valid URL query string. 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: 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[: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 + + protected + def array_or_string_for_javascript(option) + if option.kind_of?(Array) + "['#{option.join('\',\'')}']" + elsif !option.nil? + "'#{option}'" + end + end + end + + module PrototypeHelper + class JavaScriptGenerator + module GeneratorMethods + # Starts a script.aculo.us visual effect. See + # ActionView::Helpers::ScriptaculousHelper for more information. + def visual_effect(name, id = nil, options = {}) + record @context.send(:visual_effect, name, id, options) + end + + # Creates a script.aculo.us sortable element. Useful + # to recreate sortable elements after items get added + # or deleted. + # See ActionView::Helpers::ScriptaculousHelper for more information. + def sortable(id, options = {}) + record @context.send(:sortable_element_js, id, options) + end + + # Creates a script.aculo.us draggable element. + # See ActionView::Helpers::ScriptaculousHelper for more information. + def draggable(id, options = {}) + record @context.send(:draggable_element_js, id, options) + end + + # Creates a script.aculo.us drop receiving element. + # See ActionView::Helpers::ScriptaculousHelper for more information. + def drop_receiving(id, options = {}) + record @context.send(:drop_receiving_element_js, id, options) + end + end + end end end end |