require File.dirname(__FILE__) + '/tag_helper'
module ActionView
module Helpers
# You must call <%= define_javascript_functions %> in your application,
# or copy the included Javascript libraries into your application's
# public/javascripts/ directory, before using these helpers.
module JavascriptHelper
unless const_defined? :CALLBACKS
CALLBACKS = [:uninitialized, :loading, :loaded, :interactive, :complete]
JAVASCRIPT_PATH = File.join(File.dirname(__FILE__), 'javascripts')
end
# Returns a link that'll trigger a javascript +function+ using the
# onclick handler and return false after the fact.
#
# Examples:
# link_to_function "Greeting", "alert('Hello world!')"
# link_to_function(image_tag("delete"), "if confirm('Really?'){ do_delete(); }")
def link_to_function(name, function, html_options = {})
content_tag(
"a", name,
html_options.symbolize_keys.merge(:href => "#", :onclick => "#{function}; return false;")
)
end
# Returns a link to a remote action defined by options[:url]
# (using the url_for format) that's called in the background using
# XMLHttpRequest. The result of that request can then be inserted into a
# DOM object whose id can be specified with options[:update].
# Usually, the result would be a partial prepared by the controller with
# either render_partial or render_partial_collection.
#
# Examples:
# link_to_remote "Delete this post", :update => "posts", :url => { :action => "destroy", :id => post.id }
# link_to_remote(image_tag("refresh"), :update => "emails", :url => { :action => "list_emails" })
#
# By default, these remote requests are processed asynchronous during
# which various callbacks can be triggered (for progress indicators and
# the likes).
#
# Example:
# link_to_remote word,
# :url => { :action => "undo", :n => word_counter },
# :complete => "undoRequestCompleted(request)"
#
# The complete list of callbacks that may be specified are:
#
# :uninitialized:: Called before the remote document is
# initialized with data.
# :loading:: Called when the remote document is being
# loaded with data by the browser.
# :loaded:: Called when the browser has finished loading
# the remote document.
# :interactive:: Called when the user can interact with the
# remote document, even though it has not
# finished loading.
# :complete:: Called when the XMLHttpRequest is complete.
#
# If you for some reason or another need synchronous processing (that'll
# block the browser while the request is happening), you can specify
# options[:type] = :synchronous.
def link_to_remote(name, options = {}, html_options = {})
link_to_function(name, remote_function(options), html_options)
end
def form_remote_tag(options = {})
options[:form] = true
options[:html] ||= { }
options[:html][:onsubmit] = "#{remote_function(options)}; return false;"
tag("form", options[:html], true)
end
def remote_function(options) #:nodoc: for now
javascript_options = options_for_ajax(options)
function = options[:update] ?
"new Ajax.Updater('#{options[:update]}', " :
"new Ajax.Request("
function << "'#{url_for(options[:url])}'"
function << ", #{javascript_options})"
function = "#{options[:before]}; #{function}" if options[:before]
function = "#{function}; #{options[:after]}" if options[:after]
function = "if (#{options[:condition]}) { #{function}; }" if options[:condition]
return function
end
# Includes the Action Pack Javascript library inside a single '
end
# Observes the field with the DOM ID specified by +field_id+ and makes
# an Ajax when its contents have changed.
#
# Required +options+ are:
# :frequency:: The frequency (in seconds) at which changes to
# this field will be detected.
# :url:: +url_for+-style options for the action to call
# when the field has changed.
#
# Additional options are:
# :update:: Specifies the DOM ID of the element whose
# innerHTML should be updated with the
# XMLHttpRequest response text.
# :with:: A Javascript expression specifying the
# parameters for the XMLHttpRequest. This defaults
# to 'value', which in the evaluated context
# refers to the new field value.
#
# Additionally, you may specify any of the options documented in
# +link_to_remote.
def observe_field(field_id, options = {})
build_observer('Form.Element.Observer', field_id, options)
end
# Like +observe_field+, but operates on an entire form identified by the
# DOM ID +form_id+. +options+ are the same as +observe_field+, except
# the default value of the :with option evaluates to the
# serialized (request string) value of the form.
def observe_form(form_id, options = {})
build_observer('Form.Observer', form_id, options)
end
private
def escape_javascript(javascript)
(javascript || '').gsub('"', '\"')
end
def options_for_ajax(options)
js_options = build_callbacks(options)
js_options['asynchronous'] = options[:type] != :synchronous
js_options['method'] = options[:method] if options[:method]
if options[:form]
js_options['parameters'] = 'Form.serialize(this)'
elsif options[:with]
js_options['parameters'] = options[:with]
end
'{' + js_options.map {|k, v| "#{k}:#{v}"}.join(', ') + '}'
end
def build_observer(klass, name, options = {})
options[:with] ||= 'value' if options[:update]
callback = remote_function(options)
javascript = '"
end
def build_callbacks(options)
CALLBACKS.inject({}) do |callbacks, callback|
if options[callback]
name = 'on' + callback.to_s.capitalize
code = escape_javascript(options[callback])
callbacks[name] = "function(request){#{code}}"
end
callbacks
end
end
end
end
end