diff options
Diffstat (limited to 'actionpack/lib/action_view/helpers/javascript_helper.rb')
-rw-r--r-- | actionpack/lib/action_view/helpers/javascript_helper.rb | 123 |
1 files changed, 117 insertions, 6 deletions
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index d5e2a8b2dc..31c15d222c 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -16,7 +16,8 @@ module ActionView # the use of form_remote_tag. module JavascriptHelper unless const_defined? :CALLBACKS - CALLBACKS = [:uninitialized, :loading, :loaded, :interactive, :complete] + CALLBACKS = [ :uninitialized, :loading, :loaded, :interactive, :complete ] + AJAX_OPTIONS = [ :url, :asynchronous, :method, :insertion, :form, :with ].concat(CALLBACKS) JAVASCRIPT_PATH = File.join(File.dirname(__FILE__), 'javascripts') end @@ -146,8 +147,10 @@ module ActionView return function end - # Includes the Action Pack Javascript library inside a single <script> - # tag. + # Includes the Action Pack Javascript libraries inside a single <script> + # tag. The function first includes prototype.js and then its core extensions, + # (determined by filenames starting with "prototype"). + # Afterwards, any additional scripts will be included in random order. # # Note: The recommended approach is to copy the contents of # lib/action_view/helpers/javascripts/ into your application's @@ -155,12 +158,22 @@ module ActionView # create remote <script> links. def define_javascript_functions javascript = '<script type="text/javascript">' - Dir.glob(File.join(JAVASCRIPT_PATH, '*')).each { |filename| javascript << "\n" << IO.read(filename) } + + # load prototype.js and its extensions first + prototype_libs = Dir.glob(File.join(JAVASCRIPT_PATH, 'prototype*')).sort.reverse + prototype_libs.each do |filename| + javascript << "\n" << IO.read(filename) + end + + # load other librairies + (Dir.glob(File.join(JAVASCRIPT_PATH, '*')) - prototype_libs).each do |filename| + javascript << "\n" << IO.read(filename) + end javascript << '</script>' end # Observes the field with the DOM ID specified by +field_id+ and makes - # an Ajax when its contents have changed. + # an Ajax call when its contents have changed. # # Required +options+ are: # <tt>:frequency</tt>:: The frequency (in seconds) at which changes to @@ -190,13 +203,111 @@ module ActionView def observe_form(form_id, options = {}) build_observer('Form.Observer', form_id, options) end + + + # 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 a HTML <ul> list, + # or nothing if no entries should be displayed for autocompletion. + # + # Required +options+ are: + # <tt>:url</tt>:: Specifies the DOM ID of the element whose + # innerHTML should be updated with the autocomplete + # entries returned by XMLHttpRequest. + # + # 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 field_id + '_autocomplete' + # <tt>:with</tt>:: A Javascript expression specifying the + # parameters for the XMLHttpRequest. This defaults + # to 'value', which in the evaluated context + # refers to the new field value. + # <tt>:indicator</tt>:: Specifies the DOM ID of an elment which will be + # displayed while autocomplete is running. + # + def remote_autocomplete(field_id, options = {}) + function = "new Ajax.Autocompleter(" + function << "'#{field_id}', " + function << "'" + (options[:update] || "#{field_id}_autocomplete") + "', " + function << "'#{url_for(options[:url])}'" + + js_options = {} + js_options[:callback] = "function(element, value) {return #{options[:with]}}" if options[:with] + js_options[:indicator] = "'#{options[:indicator]}'" if options[:indicator] + function << (', ' + options_for_javascript(js_options) + ')') + + javascript_tag(function) + end + + # Use this method in your view to generate a return for the Ajax automplete requests. + # + # Example Action: + # @items = Item.find(:all, :conditions => [ 'LOWER(description) LIKE ?', + # '%' + params[:for].downcase + '%' ], 'description ASC') + # render :layout => false + # + # Example View: + # <%= autocomplete_responder @items, 'description' %> + def autocomplete_responder(entries, field, phrase = nil) + "<ul>#{entries.map { |entry| '<li>' + (phrase ? highlight(entry[field],phrase) : h(entry[field])) + '</li>' }.join}</ul>" if entries + 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 ) + # + # You can change the behaviour with various options, see + # http://script.aculo.us for more documentation. + def visual_effect(name, element_id, js_options = {}) + "new Effect.#{name.to_s.capitalize}('#{element_id}',#{options_for_javascript(js_options)});" + 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 + # element as parameters. + # + # Example: + # <%= remote_sortable("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 + # of, in the current order. + # + # You can change the behaviour with various options, see + # http://script.aculo.us for more documentation. + # + def remote_sortable(element_id, options = {}) + options[:with] ||= "Sortable.serialize('#{element_id}')" + options[:onUpdate] ||= "function(){" + remote_function(options) + "}" + options.delete_if { |key, value| AJAX_OPTIONS.include?(key) } + + javascript_tag("Sortable.create('#{element_id}', #{options_for_javascript(options)})") + end # Escape carrier returns and single and double quotes for Javascript segments. def escape_javascript(javascript) (javascript || '').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" } end + # Returns a Javascript tag with the +content+ inside. Example: + # javascript_tag "alert('All is good')" # => <script type="text/javascript">alert('All is good')</script> + def javascript_tag(content) + content_tag("script", content, :type => "text/javascript") + end + private + def options_for_javascript(options) + '{' + options.map {|k, v| "#{k}:#{v}"}.join(', ') + '}' + end + def options_for_ajax(options) js_options = build_callbacks(options) @@ -210,7 +321,7 @@ module ActionView js_options['parameters'] = options[:with] end - '{' + js_options.map {|k, v| "#{k}:#{v}"}.join(', ') + '}' + options_for_javascript(js_options) end def method_option_to_s(method) |