aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2010-01-31 14:32:26 +0000
committerPratik Naik <pratiknaik@gmail.com>2010-01-31 14:32:26 +0000
commited60021f39f7913537dcad181e4061e423dc6c3d (patch)
tree86532c43aabe9125422f84b48a899f2672900bf5
parentc6af337d2d6c86792cdc8132224ebe9294d35774 (diff)
parentb3a028259f373fd58fea2171a1e9e8b2fe3e253a (diff)
downloadrails-ed60021f39f7913537dcad181e4061e423dc6c3d.tar.gz
rails-ed60021f39f7913537dcad181e4061e423dc6c3d.tar.bz2
rails-ed60021f39f7913537dcad181e4061e423dc6c3d.zip
Merge remote branch 'mainstream/master'
Conflicts: activemodel/lib/active_model/state_machine.rb
-rw-r--r--actionmailer/lib/action_mailer.rb10
-rw-r--r--actionmailer/lib/action_mailer/base.rb7
-rw-r--r--actionmailer/lib/action_mailer/railtie.rb4
-rw-r--r--actionmailer/lib/action_mailer/tmail_compat.rb8
-rw-r--r--actionmailer/test/base_test.rb56
-rw-r--r--actionmailer/test/fixtures/base_mailer/different_layout.html.erb1
-rw-r--r--actionmailer/test/fixtures/base_mailer/different_layout.text.erb1
-rw-r--r--actionmailer/test/fixtures/base_mailer/email_custom_layout.text.html.erb1
-rw-r--r--actionmailer/test/fixtures/layouts/different_layout.html.erb1
-rw-r--r--actionmailer/test/fixtures/layouts/different_layout.text.erb1
-rw-r--r--actionmailer/test/old_base/tmail_compat_test.rb10
-rw-r--r--actionmailer/test/old_base/url_test.rb5
-rw-r--r--actionpack/README77
-rw-r--r--actionpack/lib/abstract_controller.rb3
-rw-r--r--actionpack/lib/abstract_controller/compatibility.rb18
-rw-r--r--actionpack/lib/abstract_controller/helpers.rb12
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb14
-rw-r--r--actionpack/lib/abstract_controller/url_for.rb156
-rw-r--r--actionpack/lib/action_controller.rb10
-rw-r--r--actionpack/lib/action_controller/base.rb8
-rw-r--r--actionpack/lib/action_controller/deprecated.rb2
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb15
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb17
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb5
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb149
-rw-r--r--actionpack/lib/action_dispatch.rb1
-rw-r--r--actionpack/lib/action_dispatch/testing/test_response.rb5
-rw-r--r--actionpack/lib/action_view/base.rb17
-rw-r--r--actionpack/lib/action_view/helpers.rb7
-rw-r--r--actionpack/lib/action_view/helpers/ajax_helper.rb713
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb36
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb11
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb152
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb170
-rw-r--r--actionpack/lib/action_view/helpers/scriptaculous_helper.rb142
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb80
-rw-r--r--actionpack/lib/action_view/paths.rb2
-rw-r--r--actionpack/test/abstract/render_test.rb43
-rw-r--r--actionpack/test/controller/assert_select_test.rb4
-rw-r--r--actionpack/test/controller/helper_test.rb21
-rw-r--r--actionpack/test/controller/mime_responds_test.rb2
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb45
-rw-r--r--actionpack/test/controller/url_for_test.rb (renamed from actionpack/test/abstract/url_for_test.rb)14
-rw-r--r--actionpack/test/template/ajax_helper_test.rb452
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb15
-rw-r--r--actionpack/test/template/form_helper_test.rb29
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb10
-rw-r--r--actionpack/test/template/javascript_helper_test.rb29
-rw-r--r--actionpack/test/template/prototype_helper_test.rb2
-rw-r--r--actionpack/test/template/url_helper_test.rb43
-rw-r--r--activemodel/lib/active_model.rb1
-rw-r--r--activemodel/lib/active_model/errors.rb21
-rw-r--r--activemodel/lib/active_model/state_machine.rb219
-rw-r--r--activemodel/lib/active_model/state_machine/event.rb62
-rw-r--r--activemodel/lib/active_model/state_machine/machine.rb75
-rw-r--r--activemodel/lib/active_model/state_machine/state.rb47
-rw-r--r--activemodel/lib/active_model/state_machine/state_transition.rb40
-rw-r--r--activemodel/test/cases/state_machine/event_test.rb49
-rw-r--r--activemodel/test/cases/state_machine/machine_test.rb43
-rw-r--r--activemodel/test/cases/state_machine/state_test.rb72
-rw-r--r--activemodel/test/cases/state_machine/state_transition_test.rb84
-rw-r--r--activemodel/test/cases/state_machine_test.rb312
-rw-r--r--activemodel/test/cases/validations/i18n_validation_test.rb28
-rw-r--r--activerecord/lib/active_record.rb1
-rw-r--r--activerecord/lib/active_record/locale/en.yml24
-rw-r--r--activerecord/lib/active_record/railtie.rb14
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb2
-rw-r--r--activerecord/lib/active_record/state_machine.rb24
-rw-r--r--activerecord/lib/active_record/validations.rb4
-rw-r--r--activerecord/test/cases/autosave_association_test.rb4
-rw-r--r--activerecord/test/cases/state_machine_test.rb42
-rw-r--r--activerecord/test/cases/validations/i18n_validation_test.rb14
-rw-r--r--activerecord/test/models/traffic_light.rb27
-rw-r--r--activesupport/lib/active_support/core_ext/class/delegating_attributes.rb80
-rw-r--r--activesupport/test/core_ext/class/delegating_attributes_test.rb34
-rw-r--r--railties/Rakefile4
-rw-r--r--railties/lib/generators/rails/app/templates/public/javascripts/jquery-1.4.1.min.js152
-rw-r--r--railties/lib/generators/rails/app/templates/public/javascripts/jquery.rails.js239
-rw-r--r--railties/lib/generators/rails/app/templates/public/javascripts/prototype.rails.js324
-rw-r--r--railties/lib/generators/rails/app/templates/public/javascripts/rails.js77
-rw-r--r--railties/lib/rails/commands/destroy.rb1
-rwxr-xr-xrailties/lib/rails/commands/generate.rb1
-rw-r--r--railties/lib/rails/configuration.rb20
-rw-r--r--railties/lib/rails/generators.rb3
-rw-r--r--railties/lib/rails/initializable.rb29
-rw-r--r--railties/lib/rails/plugin.rb2
-rw-r--r--railties/lib/rails/test_unit/railtie.rb4
-rw-r--r--railties/lib/rails/test_unit/testing.rake (renamed from railties/lib/rails/tasks/testing.rake)0
-rw-r--r--railties/test/application/configuration_test.rb7
-rw-r--r--railties/test/generators/generators_test_helper.rb2
-rw-r--r--railties/test/initializable_test.rb43
-rw-r--r--railties/test/railties/plugin_test.rb2
93 files changed, 951 insertions, 3920 deletions
diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb
index 17f63aca25..0265e6e222 100644
--- a/actionmailer/lib/action_mailer.rb
+++ b/actionmailer/lib/action_mailer.rb
@@ -24,9 +24,17 @@
actionpack_path = File.expand_path('../../../actionpack/lib', __FILE__)
$:.unshift(actionpack_path) if File.directory?(actionpack_path) && !$:.include?(actionpack_path)
-require 'action_controller'
+require 'abstract_controller'
require 'action_view'
+# Common ActiveSupport usage in ActionMailer
+require 'active_support/core_ext/class'
+require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/array/uniq_by'
+require 'active_support/core_ext/module/attr_internal'
+require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/string/inflections'
+
module ActionMailer
extend ::ActiveSupport::Autoload
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 087f345c3f..aa9822c6ab 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -1,8 +1,3 @@
-require 'active_support/core_ext/class'
-require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/array/uniq_by'
-require 'active_support/core_ext/module/delegation'
-require 'active_support/core_ext/string/inflections'
require 'mail'
require 'action_mailer/tmail_compat'
require 'action_mailer/collector'
@@ -263,8 +258,8 @@ module ActionMailer #:nodoc:
include AbstractController::LocalizedCache
include AbstractController::Layouts
include AbstractController::Helpers
- include AbstractController::UrlFor
include AbstractController::Translation
+ include AbstractController::Compatibility
helper ActionMailer::MailHelper
diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb
index 7ed1519e36..4ed70503fd 100644
--- a/actionmailer/lib/action_mailer/railtie.rb
+++ b/actionmailer/lib/action_mailer/railtie.rb
@@ -17,5 +17,9 @@ module ActionMailer
ActionMailer::Base.send "#{k}=", v
end
end
+
+ initializer "action_mailer.url_for" do |app|
+ ActionMailer::Base.send(:include, ActionController::UrlFor) if defined?(ActionController)
+ end
end
end \ No newline at end of file
diff --git a/actionmailer/lib/action_mailer/tmail_compat.rb b/actionmailer/lib/action_mailer/tmail_compat.rb
index c6efdc53b6..26962f972f 100644
--- a/actionmailer/lib/action_mailer/tmail_compat.rb
+++ b/actionmailer/lib/action_mailer/tmail_compat.rb
@@ -17,7 +17,13 @@ module Mail
old_transfer_encoding
end
end
-
+
+ def transfer_encoding=(value)
+ ActiveSupport::Deprecation.warn('Message#transfer_encoding= is deprecated, please call ' <<
+ 'Message#content_transfer_encoding= with the same arguments', caller[0,2])
+ self.content_transfer_encoding = value
+ end
+
def original_filename
ActiveSupport::Deprecation.warn('Message#original_filename is deprecated, ' <<
'please call Message#filename', caller[0,2])
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 03e3f81acd..57bfe2375e 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -78,6 +78,23 @@ class BaseTest < ActiveSupport::TestCase
format.html{ render "welcome" } if include_html
end
end
+
+ def different_template(template_name='')
+ mail do |format|
+ format.text { render :template => template_name }
+ format.html { render :template => template_name }
+ end
+ end
+
+ def different_layout(layout_name='')
+ mail do |format|
+ format.text {
+ render :layout => layout_name
+ }
+ format.html { render :layout => layout_name }
+ end
+ end
+
end
test "method call to mail does not raise error" do
@@ -398,6 +415,21 @@ class BaseTest < ActiveSupport::TestCase
assert_equal("7bit", email.parts[1].content_transfer_encoding)
end
+ test "explicit multipart should be multipart" do
+ mail = BaseMailer.explicit_multipart
+ assert_not_nil(mail.content_type_parameters[:boundary])
+ end
+
+ test "should set a content type if only has an html part" do
+ mail = BaseMailer.html_only
+ assert_equal('text/html', mail.mime_type)
+ end
+
+ test "should set a content type if only has an plain text part" do
+ mail = BaseMailer.plain_text_only
+ assert_equal('text/plain', mail.mime_type)
+ end
+
test "explicit multipart with one part is rendered as body" do
email = BaseMailer.custom_block
assert_equal(0, email.parts.size)
@@ -439,20 +471,18 @@ class BaseTest < ActiveSupport::TestCase
BaseMailer.expects(:welcome).returns(mail)
BaseMailer.welcome.deliver
end
-
- test "explicit multipart should be multipart" do
- mail = BaseMailer.explicit_multipart
- assert_not_nil(mail.content_type_parameters[:boundary])
- end
-
- test "should set a content type if only has an html part" do
- mail = BaseMailer.html_only
- assert_equal('text/html', mail.mime_type)
+
+ # Rendering
+ test "that you can specify a different template" do
+ mail = BaseMailer.different_template('explicit_multipart_templates')
+ assert_equal("HTML Explicit Multipart Templates", mail.html_part.body.decoded)
+ assert_equal("TEXT Explicit Multipart Templates", mail.text_part.body.decoded)
end
-
- test "should set a content type if only has an plain text part" do
- mail = BaseMailer.plain_text_only
- assert_equal('text/plain', mail.mime_type)
+
+ test "that you can specify a different layout" do
+ mail = BaseMailer.different_layout('different_layout')
+ assert_equal("HTML -- HTML", mail.html_part.body.decoded)
+ assert_equal("PLAIN -- PLAIN", mail.text_part.body.decoded)
end
protected
diff --git a/actionmailer/test/fixtures/base_mailer/different_layout.html.erb b/actionmailer/test/fixtures/base_mailer/different_layout.html.erb
new file mode 100644
index 0000000000..3225efc49a
--- /dev/null
+++ b/actionmailer/test/fixtures/base_mailer/different_layout.html.erb
@@ -0,0 +1 @@
+HTML \ No newline at end of file
diff --git a/actionmailer/test/fixtures/base_mailer/different_layout.text.erb b/actionmailer/test/fixtures/base_mailer/different_layout.text.erb
new file mode 100644
index 0000000000..b547f4a332
--- /dev/null
+++ b/actionmailer/test/fixtures/base_mailer/different_layout.text.erb
@@ -0,0 +1 @@
+PLAIN \ No newline at end of file
diff --git a/actionmailer/test/fixtures/base_mailer/email_custom_layout.text.html.erb b/actionmailer/test/fixtures/base_mailer/email_custom_layout.text.html.erb
new file mode 100644
index 0000000000..a2187308b6
--- /dev/null
+++ b/actionmailer/test/fixtures/base_mailer/email_custom_layout.text.html.erb
@@ -0,0 +1 @@
+body_text \ No newline at end of file
diff --git a/actionmailer/test/fixtures/layouts/different_layout.html.erb b/actionmailer/test/fixtures/layouts/different_layout.html.erb
new file mode 100644
index 0000000000..99eb026ae1
--- /dev/null
+++ b/actionmailer/test/fixtures/layouts/different_layout.html.erb
@@ -0,0 +1 @@
+HTML -- <%= yield %> \ No newline at end of file
diff --git a/actionmailer/test/fixtures/layouts/different_layout.text.erb b/actionmailer/test/fixtures/layouts/different_layout.text.erb
new file mode 100644
index 0000000000..b93467b7ae
--- /dev/null
+++ b/actionmailer/test/fixtures/layouts/different_layout.text.erb
@@ -0,0 +1 @@
+PLAIN -- <%= yield %> \ No newline at end of file
diff --git a/actionmailer/test/old_base/tmail_compat_test.rb b/actionmailer/test/old_base/tmail_compat_test.rb
index 7c1d9a07c1..255205de84 100644
--- a/actionmailer/test/old_base/tmail_compat_test.rb
+++ b/actionmailer/test/old_base/tmail_compat_test.rb
@@ -21,5 +21,15 @@ class TmailCompatTest < ActiveSupport::TestCase
end
assert_equal mail.content_transfer_encoding, "base64"
end
+
+ def test_transfer_encoding_setter_raises_deprecation_warning
+ mail = Mail.new
+ assert_deprecated do
+ assert_nothing_raised do
+ mail.transfer_encoding = "base64"
+ end
+ end
+ assert_equal mail.content_transfer_encoding, "base64"
+ end
end
diff --git a/actionmailer/test/old_base/url_test.rb b/actionmailer/test/old_base/url_test.rb
index 5affb47997..d851431c7a 100644
--- a/actionmailer/test/old_base/url_test.rb
+++ b/actionmailer/test/old_base/url_test.rb
@@ -1,8 +1,13 @@
require 'abstract_unit'
+require 'action_controller'
class WelcomeController < ActionController::Base
end
+class ActionMailer::Base
+ include ActionController::UrlFor
+end
+
class TestMailer < ActionMailer::Base
default_url_options[:host] = 'www.basecamphq.com'
diff --git a/actionpack/README b/actionpack/README
index e4ce4aa044..8bdcb9120a 100644
--- a/actionpack/README
+++ b/actionpack/README
@@ -37,15 +37,15 @@ A short rundown of the major features:
def show
@customer = find_customer
end
-
+
def update
@customer = find_customer
@customer.attributes = params[:customer]
- @customer.save ?
+ @customer.save ?
redirect_to(:action => "show") :
render(:action => "edit")
end
-
+
private
def find_customer() Customer.find(params[:id]) end
end
@@ -64,7 +64,7 @@ A short rundown of the major features:
<% unless @person.is_client? %>
Not for clients to see...
<% end %>
-
+
{Learn more}[link:classes/ActionView.html]
@@ -99,24 +99,24 @@ A short rundown of the major features:
before_filter :authenticate, :cache, :audit
after_filter { |c| c.response.body = Gzip::compress(c.response.body) }
after_filter LocalizeFilter
-
+
def index
# Before this action is run, the user will be authenticated, the cache
# will be examined to see if a valid copy of the results already
# exists, and the action will be logged for auditing.
-
- # After this action has run, the output will first be localized then
+
+ # After this action has run, the output will first be localized then
# compressed to minimize bandwidth usage
end
-
+
private
def authenticate
# Implement the filter with full access to both request and response
end
end
-
+
{Learn more}[link:classes/ActionController/Filters/ClassMethods.html]
-
+
* Helpers for forms, dates, action links, and text
@@ -124,26 +124,26 @@ A short rundown of the major features:
<%= html_date_select(Date.today) %>
<%= link_to "New post", :controller => "post", :action => "new" %>
<%= truncate(post.title, :length => 25) %>
-
+
{Learn more}[link:classes/ActionView/Helpers.html]
-* Layout sharing for template reuse (think simple version of Struts
+* Layout sharing for template reuse (think simple version of Struts
Tiles[http://jakarta.apache.org/struts/userGuide/dev_tiles.html])
class WeblogController < ActionController::Base
layout "weblog_layout"
-
+
def hello_world
end
end
Layout file (called weblog_layout):
<html><body><%= yield %></body></html>
-
+
Template for hello_world action:
<h1>Hello world</h1>
-
+
Result of running hello_world action:
<html><body><h1>Hello world</h1></body></html>
@@ -156,9 +156,9 @@ A short rundown of the major features:
Accessing /clients/37signals/basecamp/project/dash calls ProjectController#dash with
{ "client_name" => "37signals", "project_name" => "basecamp" } in params[:params]
-
+
From that URL, you can rewrite the redirect in a number of ways:
-
+
redirect_to(:action => "edit") =>
/clients/37signals/basecamp/project/dash
@@ -168,15 +168,6 @@ A short rundown of the major features:
{Learn more}[link:classes/ActionController/Base.html]
-* Javascript and Ajax integration
-
- link_to_function "Greeting", "alert('Hello world!')"
- link_to_remote "Delete this post", :update => "posts",
- :url => { :action => "destroy", :id => post.id }
-
- {Learn more}[link:classes/ActionView/Helpers/JavaScriptHelper.html]
-
-
* Easy testing of both controller and rendered template through ActionController::TestCase
class LoginControllerTest < ActionController::TestCase
@@ -218,18 +209,18 @@ A short rundown of the major features:
class WeblogController < ActionController::Base
caches_page :show
caches_action :account
-
+
def show
- # the output of the method will be cached as
+ # the output of the method will be cached as
# ActionController::Base.page_cache_directory + "/weblog/show/n.html"
# and the web server will pick it up without even hitting Rails
end
-
+
def account
# the output of the method will be cached in the fragment store
# but Rails is hit to retrieve it, so filters are run
end
-
+
def update
List.update(params[:list][:id], params[:list])
expire_page :action => "show", :id => params[:list][:id]
@@ -256,26 +247,26 @@ A short rundown of the major features:
class AccountController < ActionController::Base
scaffold :account
end
-
+
The AccountController now has the full CRUD range of actions and default
templates: list, show, destroy, new, create, edit, update
-
+
{Learn more}[link:classes/ActionController/Scaffolding/ClassMethods.html]
* Form building for Active Record model objects
- The post object has a title (varchar), content (text), and
+ The post object has a title (varchar), content (text), and
written_on (date)
<%= form "post" %>
-
+
...will generate something like (the selects will have more options, of
course):
-
+
<form action="create" method="POST">
<p>
- <b>Title:</b><br/>
+ <b>Title:</b><br/>
<input type="text" name="post[title]" value="<%= @post.title %>" />
</p>
<p>
@@ -293,7 +284,7 @@ A short rundown of the major features:
</form>
This form generates a params[:post] array that can be used directly in a save action:
-
+
class WeblogController < ActionController::Base
def create
post = Post.create(params[:post])
@@ -318,19 +309,19 @@ methods:
class WeblogController < ActionController::Base
layout "weblog/layout"
-
+
def index
@posts = Post.find(:all)
end
-
+
def show
@post = Post.find(params[:id])
end
-
+
def new
@post = Post.new
end
-
+
def create
@post = Post.create(params[:post])
redirect_to :action => "show", :id => @post.id
@@ -364,7 +355,7 @@ And the templates look like this:
weblog/new.html.erb:
<%= form "post" %>
-
+
This simple setup will list all the posts in the system on the index page,
which is called by accessing /weblog/. It uses the form builder for the Active
Record model to make the new screen, which in turn hands everything over to
@@ -379,7 +370,7 @@ The latest version of Action Pack can be found at
* http://rubyforge.org/project/showfiles.php?group_id=249
-Documentation can be found at
+Documentation can be found at
* http://api.rubyonrails.com
diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb
index 2c2ef16622..1e15ab090c 100644
--- a/actionpack/lib/abstract_controller.rb
+++ b/actionpack/lib/abstract_controller.rb
@@ -2,6 +2,7 @@ activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
require 'active_support/ruby/shim'
+require 'active_support/dependencies/autoload'
require 'active_support/core_ext/module/attr_internal'
require 'active_support/core_ext/module/delegation'
@@ -11,11 +12,11 @@ module AbstractController
autoload :Base
autoload :Callbacks
autoload :Collector
+ autoload :Compatibility
autoload :Helpers
autoload :Layouts
autoload :LocalizedCache
autoload :Logger
autoload :Rendering
autoload :Translation
- autoload :UrlFor
end
diff --git a/actionpack/lib/abstract_controller/compatibility.rb b/actionpack/lib/abstract_controller/compatibility.rb
new file mode 100644
index 0000000000..7fb93a0eb5
--- /dev/null
+++ b/actionpack/lib/abstract_controller/compatibility.rb
@@ -0,0 +1,18 @@
+module AbstractController
+ module Compatibility
+ extend ActiveSupport::Concern
+
+ def _find_layout(name, details)
+ details[:prefix] = nil if name =~ /\blayouts/
+ super
+ end
+
+ # Move this into a "don't run in production" module
+ def _default_layout(details, require_layout = false)
+ super
+ rescue ActionView::MissingTemplate
+ _find_layout(_layout({}), {})
+ nil
+ end
+ end
+end
diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb
index 1d898d1a4c..eb621c0865 100644
--- a/actionpack/lib/abstract_controller/helpers.rb
+++ b/actionpack/lib/abstract_controller/helpers.rb
@@ -25,7 +25,7 @@ module AbstractController
def inherited(klass)
helpers = _helpers
klass._helpers = Module.new { include helpers }
-
+ klass.class_eval { default_helper_module! unless name.blank? }
super
end
@@ -146,6 +146,16 @@ module AbstractController
end
end
end
+
+ def default_helper_module!
+ module_name = name.sub(/Controller$/, '')
+ module_path = module_name.underscore
+ helper module_path
+ rescue MissingSourceFile => e
+ raise e unless e.is_missing? "helpers/#{module_path}_helper"
+ rescue NameError => e
+ raise e unless e.missing_name? "#{module_name}Helper"
+ end
end
end
end
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 1dec3f2c3e..ac407bda5e 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -157,13 +157,23 @@ module AbstractController
options[:_template_name] = options[:file]
end
- name = (options[:_template_name] || action_name).to_s
+ name = (options[:_template_name] || options[:action] || action_name).to_s
+ options[:_prefix] ||= _prefix if (options.keys & [:partial, :file, :template]).empty?
+
+ details = _normalize_details(options)
options[:_template] ||= with_template_cache(name) do
- find_template(name, { :formats => formats }, options)
+ find_template(name, details, options)
end
end
+ def _normalize_details(options)
+ details = { :formats => formats }
+ details[:formats] = Array(options[:format]) if options[:format]
+ details[:locale] = Array(options[:locale]) if options[:locale]
+ details
+ end
+
def find_template(name, details, options)
view_paths.find(name, details, options[:_prefix], options[:_partial])
end
diff --git a/actionpack/lib/abstract_controller/url_for.rb b/actionpack/lib/abstract_controller/url_for.rb
deleted file mode 100644
index 6b7d2b1f34..0000000000
--- a/actionpack/lib/abstract_controller/url_for.rb
+++ /dev/null
@@ -1,156 +0,0 @@
-module AbstractController
- # In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
- # is also possible: an URL can be generated from one of your routing definitions.
- # URL generation functionality is centralized in this module.
- #
- # See AbstractController::Routing and AbstractController::Resources for general
- # information about routing and routes.rb.
- #
- # <b>Tip:</b> If you need to generate URLs from your models or some other place,
- # then AbstractController::UrlFor is what you're looking for. Read on for
- # an introduction.
- #
- # == URL generation from parameters
- #
- # As you may know, some functions - such as AbstractController::Base#url_for
- # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
- # of parameters. For example, you've probably had the chance to write code
- # like this in one of your views:
- #
- # <%= link_to('Click here', :controller => 'users',
- # :action => 'new', :message => 'Welcome!') %>
- #
- # #=> Generates a link to: /users/new?message=Welcome%21
- #
- # link_to, and all other functions that require URL generation functionality,
- # actually use AbstractController::UrlFor under the hood. And in particular,
- # they use the AbstractController::UrlFor#url_for method. One can generate
- # the same path as the above example by using the following code:
- #
- # include UrlFor
- # url_for(:controller => 'users',
- # :action => 'new',
- # :message => 'Welcome!',
- # :only_path => true)
- # # => "/users/new?message=Welcome%21"
- #
- # Notice the <tt>:only_path => true</tt> part. This is because UrlFor has no
- # information about the website hostname that your Rails app is serving. So if you
- # want to include the hostname as well, then you must also pass the <tt>:host</tt>
- # argument:
- #
- # include UrlFor
- # url_for(:controller => 'users',
- # :action => 'new',
- # :message => 'Welcome!',
- # :host => 'www.example.com') # Changed this.
- # # => "http://www.example.com/users/new?message=Welcome%21"
- #
- # By default, all controllers and views have access to a special version of url_for,
- # that already knows what the current hostname is. So if you use url_for in your
- # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
- # argument.
- #
- # For convenience reasons, mailers provide a shortcut for AbstractController::UrlFor#url_for.
- # So within mailers, you only have to type 'url_for' instead of 'AbstractController::UrlFor#url_for'
- # in full. However, mailers don't have hostname information, and what's why you'll still
- # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
- #
- #
- # == URL generation for named routes
- #
- # UrlFor also allows one to access methods that have been auto-generated from
- # named routes. For example, suppose that you have a 'users' resource in your
- # <b>routes.rb</b>:
- #
- # map.resources :users
- #
- # This generates, among other things, the method <tt>users_path</tt>. By default,
- # this method is accessible from your controllers, views and mailers. If you need
- # to access this auto-generated method from other places (such as a model), then
- # you can do that by including AbstractController::UrlFor in your class:
- #
- # class User < ActiveRecord::Base
- # include AbstractController::UrlFor
- #
- # def base_uri
- # user_path(self)
- # end
- # end
- #
- # User.find(1).base_uri # => "/users/1"
- #
- module UrlFor
- extend ActiveSupport::Concern
-
- included do
- ActionController::Routing::Routes.install_helpers(self)
- extlib_inheritable_accessor :default_url_options,
- :instance_writer => false, :instance_reader => false
- self.default_url_options ||= {}
- end
-
- # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in
- # the form of a hash, just like the one you would use for url_for directly. Example:
- #
- # def default_url_options(options)
- # { :project => @project.active? ? @project.url_name : "unknown" }
- # end
- #
- # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the
- # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set
- # by this method.
- def default_url_options(options = nil)
- self.class.default_url_options
- end
-
- def rewrite_options(options) #:nodoc:
- if options.delete(:use_defaults) != false && (defaults = default_url_options(options))
- defaults.merge(options)
- else
- options
- end
- end
-
- # Generate a url based on the options provided, default_url_options and the
- # routes defined in routes.rb. The following options are supported:
- #
- # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
- # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
- # * <tt>:host</tt> - Specifies the host the link should be targeted at.
- # If <tt>:only_path</tt> is false, this option must be
- # provided either explicitly, or via +default_url_options+.
- # * <tt>:port</tt> - Optionally specify the port to connect to.
- # * <tt>:anchor</tt> - An anchor name to be appended to the path.
- # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
- # +relative_url_root+ set in AbstractController::Base.relative_url_root.
- # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
- #
- # Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
- # +url_for+ is forwarded to the Routes module.
- #
- # Examples:
- #
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing'
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
- # url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true # => 'http://somehost.org/tasks/testing/'
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
- def url_for(options = {})
- options ||= {}
- case options
- when String
- options
- when Hash
- _url_rewriter.rewrite(rewrite_options(options))
- else
- polymorphic_url(options)
- end
- end
-
- protected
-
- def _url_rewriter
- ActionController::UrlRewriter
- end
- end
-end
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 33e107d216..759e52b135 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -1,6 +1,5 @@
-activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
-$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
-require 'active_support/ruby/shim'
+require 'abstract_controller'
+require 'action_dispatch'
module ActionController
extend ActiveSupport::Autoload
@@ -41,6 +40,7 @@ module ActionController
autoload :Integration, 'action_controller/deprecated/integration_test'
autoload :IntegrationTest, 'action_controller/deprecated/integration_test'
autoload :PerformanceTest, 'action_controller/deprecated/performance_test'
+ autoload :UrlWriter, 'action_controller/deprecated'
autoload :Routing, 'action_controller/deprecated'
autoload :TestCase, 'action_controller/test_case'
@@ -66,13 +66,11 @@ module ActionController
end
# All of these simply register additional autoloads
-require 'abstract_controller'
-require 'action_dispatch'
require 'action_view'
require 'action_controller/vendor/html-scanner'
# Common ActiveSupport usage in ActionController
-require "active_support/concern"
+require 'active_support/concern'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/load_error'
require 'active_support/core_ext/module/attr_internal'
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 215b70734c..10244f8216 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -81,5 +81,13 @@ module ActionController
filter << block if block
filter
end
+
+ protected
+
+ # Overwrite url rewriter to use request.
+ def _url_rewriter
+ return ActionController::UrlRewriter unless request
+ @_url_rewriter ||= ActionController::UrlRewriter.new(request, params)
+ end
end
end
diff --git a/actionpack/lib/action_controller/deprecated.rb b/actionpack/lib/action_controller/deprecated.rb
index edc0e5b3fe..a4eef07841 100644
--- a/actionpack/lib/action_controller/deprecated.rb
+++ b/actionpack/lib/action_controller/deprecated.rb
@@ -2,4 +2,4 @@ ActionController::AbstractRequest = ActionController::Request = ActionDispatch::
ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response
ActionController::Routing = ActionDispatch::Routing
ActionController::Routing::Routes = ActionDispatch::Routing::RouteSet.new
-ActionController::UrlWriter = AbstractController::UrlFor
+ActionController::UrlWriter = ActionController::UrlFor
diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb
index 0e869e4e87..2be0fa097e 100644
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ b/actionpack/lib/action_controller/metal/compatibility.rb
@@ -2,6 +2,8 @@ module ActionController
module Compatibility
extend ActiveSupport::Concern
+ include AbstractController::Compatibility
+
class ::ActionController::ActionControllerError < StandardError #:nodoc:
end
@@ -103,19 +105,6 @@ module ActionController
super || (respond_to?(:method_missing) && "_handle_method_missing")
end
- def _find_layout(name, details)
- details[:prefix] = nil if name =~ /\blayouts/
- super
- end
-
- # Move this into a "don't run in production" module
- def _default_layout(details, require_layout = false)
- super
- rescue ActionView::MissingTemplate
- _find_layout(_layout({}), {})
- nil
- end
-
def performed?
response_body
end
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index 03ba4b3f83..05843a061b 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -56,18 +56,15 @@ module ActionController
module ClassMethods
def helpers_dir
+ ActiveSupport::Deprecation.warn "helpers_dir is deprecated, use helpers_path instead"
self.helpers_path
end
def helpers_dir=(value)
+ ActiveSupport::Deprecation.warn "helpers_dir= is deprecated, use helpers_path= instead"
self.helpers_path = Array(value)
end
- def inherited(klass)
- klass.class_eval { default_helper_module! unless name.blank? }
- super
- end
-
# Declares helper accessors for controller attributes. For example, the
# following adds new +name+ and <tt>name=</tt> instance methods to a
# controller and makes them available to the view:
@@ -101,16 +98,6 @@ module ActionController
super(args)
end
- def default_helper_module!
- module_name = name.sub(/Controller$/, '')
- module_path = module_name.underscore
- helper module_path
- rescue MissingSourceFile => e
- raise e unless e.is_missing? "helpers/#{module_path}_helper"
- rescue NameError => e
- raise e unless e.missing_name? "#{module_name}Helper"
- end
-
# Extract helper names from files in app/helpers/**/*_helper.rb
def all_application_helpers
helpers = []
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 8f03035b2b..0aae9f8579 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -58,11 +58,6 @@ module ActionController
options.merge! :partial => action
end
- if (options.keys & [:partial, :file, :template, :text, :inline]).empty?
- options[:_template_name] ||= options[:action]
- options[:_prefix] = _prefix
- end
-
if options[:status]
options[:status] = Rack::Utils.status_code(options[:status])
end
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index 73feacb872..387e6a554b 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -1,15 +1,156 @@
module ActionController
+ # In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
+ # is also possible: an URL can be generated from one of your routing definitions.
+ # URL generation functionality is centralized in this module.
+ #
+ # See ActionController::Routing and ActionController::Resources for general
+ # information about routing and routes.rb.
+ #
+ # <b>Tip:</b> If you need to generate URLs from your models or some other place,
+ # then ActionController::UrlFor is what you're looking for. Read on for
+ # an introduction.
+ #
+ # == URL generation from parameters
+ #
+ # As you may know, some functions - such as ActionController::Base#url_for
+ # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
+ # of parameters. For example, you've probably had the chance to write code
+ # like this in one of your views:
+ #
+ # <%= link_to('Click here', :controller => 'users',
+ # :action => 'new', :message => 'Welcome!') %>
+ #
+ # #=> Generates a link to: /users/new?message=Welcome%21
+ #
+ # link_to, and all other functions that require URL generation functionality,
+ # actually use ActionController::UrlFor under the hood. And in particular,
+ # they use the ActionController::UrlFor#url_for method. One can generate
+ # the same path as the above example by using the following code:
+ #
+ # include UrlFor
+ # url_for(:controller => 'users',
+ # :action => 'new',
+ # :message => 'Welcome!',
+ # :only_path => true)
+ # # => "/users/new?message=Welcome%21"
+ #
+ # Notice the <tt>:only_path => true</tt> part. This is because UrlFor has no
+ # information about the website hostname that your Rails app is serving. So if you
+ # want to include the hostname as well, then you must also pass the <tt>:host</tt>
+ # argument:
+ #
+ # include UrlFor
+ # url_for(:controller => 'users',
+ # :action => 'new',
+ # :message => 'Welcome!',
+ # :host => 'www.example.com') # Changed this.
+ # # => "http://www.example.com/users/new?message=Welcome%21"
+ #
+ # By default, all controllers and views have access to a special version of url_for,
+ # that already knows what the current hostname is. So if you use url_for in your
+ # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
+ # argument.
+ #
+ # For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for.
+ # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlFor#url_for'
+ # in full. However, mailers don't have hostname information, and what's why you'll still
+ # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
+ #
+ #
+ # == URL generation for named routes
+ #
+ # UrlFor also allows one to access methods that have been auto-generated from
+ # named routes. For example, suppose that you have a 'users' resource in your
+ # <b>routes.rb</b>:
+ #
+ # map.resources :users
+ #
+ # This generates, among other things, the method <tt>users_path</tt>. By default,
+ # this method is accessible from your controllers, views and mailers. If you need
+ # to access this auto-generated method from other places (such as a model), then
+ # you can do that by including ActionController::UrlFor in your class:
+ #
+ # class User < ActiveRecord::Base
+ # include ActionController::UrlFor
+ #
+ # def base_uri
+ # user_path(self)
+ # end
+ # end
+ #
+ # User.find(1).base_uri # => "/users/1"
+ #
module UrlFor
extend ActiveSupport::Concern
- include AbstractController::UrlFor
- include ActionController::RackDelegation
+ included do
+ ActionController::Routing::Routes.install_helpers(self)
+ extlib_inheritable_accessor :default_url_options,
+ :instance_writer => false, :instance_reader => false
+ self.default_url_options ||= {}
+ end
+
+ # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in
+ # the form of a hash, just like the one you would use for url_for directly. Example:
+ #
+ # def default_url_options(options)
+ # { :project => @project.active? ? @project.url_name : "unknown" }
+ # end
+ #
+ # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the
+ # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set
+ # by this method.
+ def default_url_options(options = nil)
+ self.class.default_url_options
+ end
+
+ def rewrite_options(options) #:nodoc:
+ if options.delete(:use_defaults) != false && (defaults = default_url_options(options))
+ defaults.merge(options)
+ else
+ options
+ end
+ end
+
+ # Generate a url based on the options provided, default_url_options and the
+ # routes defined in routes.rb. The following options are supported:
+ #
+ # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
+ # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
+ # * <tt>:host</tt> - Specifies the host the link should be targeted at.
+ # If <tt>:only_path</tt> is false, this option must be
+ # provided either explicitly, or via +default_url_options+.
+ # * <tt>:port</tt> - Optionally specify the port to connect to.
+ # * <tt>:anchor</tt> - An anchor name to be appended to the path.
+ # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
+ # +relative_url_root+ set in ActionController::Base.relative_url_root.
+ # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
+ #
+ # Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
+ # +url_for+ is forwarded to the Routes module.
+ #
+ # Examples:
+ #
+ # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing'
+ # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
+ # url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true # => 'http://somehost.org/tasks/testing/'
+ # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
+ def url_for(options = {})
+ options ||= {}
+ case options
+ when String
+ options
+ when Hash
+ _url_rewriter.rewrite(rewrite_options(options))
+ else
+ polymorphic_url(options)
+ end
+ end
protected
def _url_rewriter
- return ActionController::UrlRewriter unless request
- @_url_rewriter ||= ActionController::UrlRewriter.new(request, params)
+ ActionController::UrlRewriter
end
end
end
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index f0490a5619..c6eb097ee5 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -23,6 +23,7 @@
activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
+
require 'active_support'
require 'active_support/dependencies/autoload'
diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb
index 6d019023ce..9a51a32899 100644
--- a/actionpack/lib/action_dispatch/testing/test_response.rb
+++ b/actionpack/lib/action_dispatch/testing/test_response.rb
@@ -36,6 +36,11 @@ module ActionDispatch
@template.layout
end
+ def redirected_to
+ ::ActiveSupport::Deprecation.warn("response.redirected_to is deprecated. Use response.redirect_url instead", caller)
+ redirect_url
+ end
+
def redirect_url_match?(pattern)
::ActiveSupport::Deprecation.warn("response.redirect_url_match? is deprecated. Use assert_match(/foo/, response.redirect_url) instead", caller)
return false if redirect_url.nil?
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index af13f2cd3e..07ef3f2140 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -6,15 +6,20 @@ module ActionView #:nodoc:
end
class MissingTemplate < ActionViewError #:nodoc:
- attr_reader :path, :action_name
+ attr_reader :path
- def initialize(paths, path, template_format = nil)
+ def initialize(paths, path, details, partial)
@path = path
- @action_name = path.split("/").last.split(".")[0...-1].join(".")
- full_template_path = path.include?('.') ? path : "#{path}.erb"
display_paths = paths.compact.join(":")
- template_type = (path =~ /layouts/i) ? 'layout' : 'template'
- super("Missing #{template_type} #{full_template_path} in view path #{display_paths}")
+ template_type = if partial
+ "partial"
+ elsif path =~ /layouts/i
+ 'layout'
+ else
+ 'template'
+ end
+
+ super("Missing #{template_type} #{path} with #{details.inspect} in view path #{display_paths}")
end
end
diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb
index 080eb87445..ceb0e18d80 100644
--- a/actionpack/lib/action_view/helpers.rb
+++ b/actionpack/lib/action_view/helpers.rb
@@ -3,7 +3,6 @@ require 'active_support/benchmarkable'
module ActionView #:nodoc:
module Helpers #:nodoc:
autoload :ActiveModelHelper, 'action_view/helpers/active_model_helper'
- autoload :AjaxHelperCompat, 'action_view/helpers/ajax_helper'
autoload :AssetTagHelper, 'action_view/helpers/asset_tag_helper'
autoload :AtomFeedHelper, 'action_view/helpers/atom_feed_helper'
autoload :CacheHelper, 'action_view/helpers/cache_helper'
@@ -15,11 +14,12 @@ module ActionView #:nodoc:
autoload :FormTagHelper, 'action_view/helpers/form_tag_helper'
autoload :JavaScriptHelper, 'action_view/helpers/javascript_helper'
autoload :NumberHelper, 'action_view/helpers/number_helper'
- autoload :AjaxHelper, 'action_view/helpers/ajax_helper'
+ autoload :PrototypeHelper, 'action_view/helpers/prototype_helper'
autoload :RawOutputHelper, 'action_view/helpers/raw_output_helper'
autoload :RecordIdentificationHelper, 'action_view/helpers/record_identification_helper'
autoload :RecordTagHelper, 'action_view/helpers/record_tag_helper'
autoload :SanitizeHelper, 'action_view/helpers/sanitize_helper'
+ autoload :ScriptaculousHelper, 'action_view/helpers/scriptaculous_helper'
autoload :TagHelper, 'action_view/helpers/tag_helper'
autoload :TextHelper, 'action_view/helpers/text_helper'
autoload :TranslationHelper, 'action_view/helpers/translation_helper'
@@ -47,11 +47,12 @@ module ActionView #:nodoc:
include FormTagHelper
include JavaScriptHelper
include NumberHelper
- include AjaxHelperCompat
+ include PrototypeHelper
include RawOutputHelper
include RecordIdentificationHelper
include RecordTagHelper
include SanitizeHelper
+ include ScriptaculousHelper
include TagHelper
include TextHelper
include TranslationHelper
diff --git a/actionpack/lib/action_view/helpers/ajax_helper.rb b/actionpack/lib/action_view/helpers/ajax_helper.rb
deleted file mode 100644
index 169a803848..0000000000
--- a/actionpack/lib/action_view/helpers/ajax_helper.rb
+++ /dev/null
@@ -1,713 +0,0 @@
-module ActionView
- module Helpers
- module AjaxHelper
- # Included for backwards compatibility / RJS functionality
- # Rails classes should not be aware of individual JS frameworks
- include PrototypeHelper
-
- # Returns a form that will allow the unobtrusive JavaScript drivers to submit the
- # form dynamically. The default driver behaviour is an XMLHttpRequest in the background
- # instead of the regular POST arrangement. Even though it's using JavaScript to serialize
- # the form elements, the form submission will work just like a regular submission as
- # viewed by the receiving side (all elements available in <tt>params</tt>). The options
- # for specifying the target with <tt>:url</tt> anddefining callbacks is the same as +link_to_remote+.
- #
- # === Resource
- #
- # Example:
- #
- # # Generates:
- # # <form action='/authors'
- # # data-remote='true'
- # # class='new_author'
- # # id='create-author'
- # # method='post'> ... </form>
- # #
- # <% remote_form_for(@record, {:html => { :id => 'create-author' }}) do |f| %>
- # ...
- # <% end %>
- #
- # This will expand to be the same as:
- #
- # <% remote_form_for :post, @post, :url => post_path(@post),
- # :html => { :method => :put,
- # :class => "edit_post",
- # :id => "edit_post_45" } do |f| %>
- # ...
- # <% end %>
- #
- # === Nested Resource
- #
- # Example:
- # # Generates:
- # # <form action='/authors/1/articles'
- # # data-remote="true"
- # # class='new_article'
- # # method='post'
- # # id='new_article'></form>
- # #
- # <% remote_form_for([@author, @article]) do |f| %>
- # ...
- # <% end %>
- #
- # This will expand to be the same as:
- #
- # <% remote_form_for :article, @article, :url => author_article_path(@author, @article),
- # :html => { :method => :put,
- # :class => "new_article",
- # :id => "new_comment" } do |f| %>
- # ...
- # <% end %>
- #
- # If you don't need to attach a form to a resource, then check out form_remote_tag.
- #
- # See FormHelper#form_for for additional semantics.
- def remote_form_for(record_or_name_or_array, *args, &proc)
- options = args.extract_options!
-
- if confirm = options.delete(:confirm)
- add_confirm_to_attributes!(options, confirm)
- end
-
- object_name = extract_object_name_for_form!(args, options, record_or_name_or_array)
-
- concat(form_remote_tag(options))
- fields_for(object_name, *(args << options), &proc)
- concat('</form>'.html_safe!)
- end
- alias_method :form_remote_for, :remote_form_for
-
- # Returns a form tag that will allow the unobtrusive JavaScript drivers to submit the
- # form dynamically. The default JavaScript driver behaviour is an XMLHttpRequest
- # in the background instead of the regular POST arrangement. Even though it's using
- # JavaScript to serialize the form elements, the form submission will work just like
- # a regular submission as viewed by the receiving side (all elements available in
- # <tt>params</tt>). The options for specifying the target with <tt>:url</tt> and
- # defining callbacks is the same as +link_to_remote+.
- #
- # A "fall-through" target for browsers that doesn't do JavaScript can be
- # specified with the <tt>:action</tt>/<tt>:method</tt> options on <tt>:html</tt>.
- #
- # Example:
- #
- # # Generates:
- # # <form action="http://www.example.com/fast"
- # # method="post"
- # # data-remote="true"
- # # data-update-success="glass_of_beer"></form>
- # #
- # form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) {}
- #
- # The Hash passed to the <tt>:html</tt> key is equivalent to the options (2nd)
- # argument in the FormTagHelper.form_tag method.
- #
- # By default the fall-through action is the same as the one specified in
- # the <tt>:url</tt> (and the default method is <tt>:post</tt>).
- #
- # form_remote_tag also takes a block, like form_tag:
- # # Generates:
- # # <form action='/posts'
- # # method='post'
- # # data-remote='true'>
- # # <input name="commit" type="submit" value="Save" />
- # # </form>
- # #
- # <% form_remote_tag :url => '/posts' do -%>
- # <%= submit_tag 'Save' %>
- # <% end -%>
- #
- # # Generates:
- # # <form action="http://www.example.com/fast"
- # # method="post"
- # # data-remote="true"
- # # data-update-success="glass_of_beer">Hello world!</form>
- # #
- # <% form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) do -%>
- # "Hello world!"
- # <% end -%>
- #
- def form_remote_tag(options = {}, &block)
- html_options = options.delete(:callbacks)
-
- attributes = {}
- attributes.merge!(extract_remote_attributes!(options))
- attributes.merge!(html_options) if html_options
- attributes.merge!(options)
- attributes.delete(:builder)
-
- form_tag(attributes.delete(:action) || attributes.delete("data-url"), attributes, &block)
- end
-
- # Returns a link that will allow unobtrusive JavaScript to dynamical adjust its
- # behaviour. The default behaviour is an XMLHttpRequest in the background instead
- # of the regular GET arrangement. The result of that request can then be inserted
- # into a DOM object whose id can be specified with <tt>options[:update]</tt>. Usually,
- # the result would be a partial prepared by the controller with render :partial.
- #
- # Examples:
- #
- # # Generates:
- # # <a href="#"
- # # data-remote="true"
- # # data-url="http://www.example.com/whatnot"
- # # data-method="delete"
- # # rel="nofollow">Remove Author</a>
- # #
- # link_to_remote("Remove Author", { :url => { :action => "whatnot" },
- # :method => "delete"})
- #
- #
- # You can override the generated HTML options by specifying a hash in
- # <tt>options[:html]</tt>.
- #
- # # Generates:
- # # <a class="fine"
- # # href="#"
- # # data-remote="true"
- # # data-url="http://www.example.com/whatnot"
- # # data-method="delete"
- # # rel="nofollow">Remove Author</a>
- # #
- # link_to_remote("Remove Author", { :url => { :action => "whatnot" },
- # :method => "delete",
- # :html => { :class => "fine" }})
- #
- #
- # You can also specify a hash for <tt>options[:update]</tt> to allow for
- # easy redirection of output to an other DOM element if a server-side
- # error occurs:
- #
- # Example:
- # # Generates:
- # #
- # # <a href="#"
- # # data-url="http://www.example.com/destroy"
- # # data-update-success="posts"
- # # data-update-failure="error"
- # # data-remote="true">Delete this Post</a>'
- # #
- # link_to_remote "Delete this post",
- # :url => { :action => "destroy"},
- # :update => { :success => "posts", :failure => "error" }
- #
- # Optionally, you can use the <tt>options[:position]</tt> parameter to
- # influence how the target DOM element is updated. It must be one of
- # <tt>:before</tt>, <tt>:top</tt>, <tt>:bottom</tt>, or <tt>:after</tt>.
- #
- # Example:
- # # Generates:
- # # <a href="#"
- # # data-remote="true"
- # # data-url="http://www.example.com/whatnot"
- # # data-update-position="bottom">Remove Author</a>
- # #
- # link_to_remote("Remove Author", :url => { :action => "whatnot" }, :position => :bottom)
- #
- #
- # The method used is by default POST. You can also specify GET or you
- # can simulate PUT or DELETE over POST. All specified with <tt>options[:method]</tt>
- #
- # Example:
- # # Generates:
- # # <a href='#'
- # # data-url='/person/4'
- # # rel='nofollow'
- # # data-remote='true'
- # # data-method='delete'>Destroy</a>
- # #
- # link_to_remote "Destroy", :url => person_url(:id => person), :method => :delete
- #
- # By default, these remote requests are processed asynchronous during
- # which various JavaScript callbacks can be triggered (for progress
- # indicators and the likes). All callbacks get access to the
- # <tt>request</tt> object, which holds the underlying XMLHttpRequest.
- #
- # To access the server response, use <tt>request.responseText</tt>, to
- # find out the HTTP status, use <tt>request.status</tt>.
- #
- # Example:
- # # Generates:
- # #
- # # <a href='#'
- # # data-url='http://www.example.com/undo?n=5'
- # # data-oncomplete='undoRequestCompleted(request)'
- # # data-remote='true'>undo</a>
- # #
- # link_to_remote "undo",
- # :url => { :controller => "words", :action => "undo", :n => word_counter },
- # :complete => "undoRequestCompleted(request)"
- #
- # The callbacks that may be specified are (in order):
- #
- # <tt>:loading</tt>:: Called when the remote document is being
- # loaded with data by the browser.
- # <tt>:loaded</tt>:: Called when the browser has finished loading
- # the remote document.
- # <tt>:interactive</tt>:: Called when the user can interact with the
- # remote document, even though it has not
- # finished loading.
- # <tt>:success</tt>:: Called when the XMLHttpRequest is completed,
- # and the HTTP status code is in the 2XX range.
- # <tt>:failure</tt>:: Called when the XMLHttpRequest is completed,
- # and the HTTP status code is not in the 2XX
- # range.
- # <tt>:complete</tt>:: Called when the XMLHttpRequest is complete
- # (fires after success/failure if they are
- # present).
- #
- # You can further refine <tt>:success</tt> and <tt>:failure</tt> by
- # adding additional callbacks for specific status codes.
- #
- # Example:
- #
- # # Generates:
- # # <a href='/testing/action'
- # # date-remote='true'
- # # data-failure="function(request){alert('HTTP Error '+ request.status +'+!');return false}"
- # # data-404="function(request){alert('Not found...? Wrong URL...?')}"> Hello</a>
- # #
- # link_to_remote word,
- # :url => { :action => "action" },
- # 404 => "alert('Not found...? Wrong URL...?')",
- # :failure => "alert('HTTP Error ' + request.status + '!')"
- #
- # A status code callback overrides the success/failure handlers if
- # present.
- #
- # If you for some reason or another need synchronous processing (that'll
- # block the browser while the request is happening), you can specify
- # <tt>options[:type] = :synchronous</tt>.
- #
- # You can customize further browser side call logic by passing in
- # JavaScript code snippets via some optional parameters. In their order
- # of use these are:
- #
- # <tt>:confirm</tt>:: Adds confirmation dialog.
- # <tt>:condition</tt>:: Perform remote request conditionally
- # by this expression. Use this to
- # describe browser-side conditions when
- # request should not be initiated.
- # <tt>:before</tt>:: Called before request is initiated.
- # <tt>:after</tt>:: Called immediately after request was
- # initiated and before <tt>:loading</tt>.
- # <tt>:submit</tt>:: Specifies the DOM element ID that's used
- # as the parent of the form elements. By
- # default this is the current form, but
- # it could just as well be the ID of a
- # table row or any other DOM element.
- # <tt>:with</tt>:: A JavaScript expression specifying
- # the parameters for the XMLHttpRequest.
- # Any expressions should return a valid
- # URL query string.
- #
- # Example:
- #
- # :with => "'name=' + $('name').value"
- #
- # You can generate a link that uses the UJS drivers in the general case, while
- # degrading gracefully to plain link behavior in the absence of
- # JavaScript by setting <tt>html_options[:href]</tt> to an alternate URL.
- # Note the extra curly braces around the <tt>options</tt> hash separate
- # it as the second parameter from <tt>html_options</tt>, the third.
- #
- # Example:
- #
- # # Generates:
- # # <a href='/posts/1'
- # # rel='nofollow'
- # # data-remote='true'
- # # data-method='delete'> Delete this post</a>
- # #
- # link_to_remote "Delete this post",
- # { :update => "posts", :url => { :action => "destroy", :id => post.id } }
- #
- def link_to_remote(name, options, html_options = {})
- attributes = {}
-
- attributes.merge!(:rel => "nofollow") if options[:method] && options[:method].to_s.downcase == "delete"
- attributes.merge!(extract_remote_attributes!(options))
-
- if confirm = options.delete(:confirm)
- add_confirm_to_attributes!(attributes, confirm)
- end
-
- attributes.merge!(html_options)
- href = html_options[:href].nil? ? "#" : html_options[:href]
- attributes.merge!(:href => href)
-
- content_tag(:a, name, attributes)
- end
-
- # Returns an input of type button, which allows the unobtrusive JavaScript driver
- # to dynamically adjust its behaviour. The default driver behaviour is to call a
- # remote action via XMLHttpRequest in the background.
- # The options for specifying the target with :url and defining callbacks is the same
- # as link_to_remote.
- #
- # Example:
- #
- # # Generates:
- # # <input class="fine"
- # # type="button"
- # # value="Remote outpost"
- # # data-remote="true"
- # # data-url="http://www.example.com/whatnot" />
- # #
- # button_to_remote("Remote outpost", { :url => { :action => "whatnot" }}, { :class => "fine" })
- #
- def button_to_remote(name, options = {}, html_options = {})
- attributes = html_options.merge!(:type => "button", :value => name)
-
- if confirm = options.delete(:confirm)
- add_confirm_to_attributes!(attributes, confirm)
- end
-
- if disable_with = options.delete(:disable_with)
- add_disable_with_to_attributes!(attributes, disable_with)
- end
-
- attributes.merge!(extract_remote_attributes!(options))
-
- tag(:input, attributes)
- end
-
- # Returns an input tag of type button, with the element name of +name+ and a value (i.e., display text)
- # of +value+ which will allow the unobtrusive JavaScript driver to dynamically adjust its behaviour
- # The default behaviour is to call a remote action via XMLHttpRequest in the background.
- #
- # request that reloads the page.
- #
- # # Create a button that submits to the create action
- # #
- # # Generates:
- # # <input name='create_btn'
- # # type='button'
- # # value='Create'
- # # data-remote='true'
- # # data-url='/create' />
- # #
- # <%= submit_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %>
- #
- # # Submit to the remote action update and update the DIV succeed or fail based
- # # on the success or failure of the request
- # #
- # # Generates:
- # # <input name='update_btn'
- # # type='button'
- # # value='Update'
- # # date-remote-submit='true'
- # # data-url='/testing/update'
- # # data-success='succeed'
- # # data-failure='fail' />
- # #
- # <%= submit_to_remote 'update_btn', 'Update', :url => { :action => 'update' },
- # :update => { :success => "succeed", :failure => "fail" }
- #
- # <tt>options</tt> argument is the same as in form_remote_tag.
- def submit_to_remote(name, value, options = {})
- html_options = options.delete(:html) || {}
- html_options.merge!(:name => name, :value => value, :type => "button")
-
- attributes = extract_remote_attributes!(options)
- attributes.merge!(html_options)
- attributes["data-remote-submit"] = true
- attributes.delete("data-remote")
-
- tag(:input, attributes)
- end
-
- # Periodically provides the UJS driver with the information to call the specified
- # url (<tt>options[:url]</tt>) every <tt>options[:frequency]</tt> seconds (default is 10). Usually used to
- # update a specified div (<tt>options[:update]</tt>) with the results
- # of the remote call. The options for specifying the target with <tt>:url</tt>
- # and defining callbacks is the same as link_to_remote.
- # Examples:
- # # Call get_averages and put its results in 'avg' every 10 seconds
- # # Generates:
- # # <script data-periodical='true'
- # # data-url='/get_averages'
- # # type='application/json'
- # # data-update-success='avg'
- # # data-frequency='10'></script>
- # #
- # periodically_call_remote(:url => { :action => 'get_averages' }, :update => 'avg')
- #
- # # Call invoice every 10 seconds with the id of the customer
- # # If it succeeds, update the invoice DIV; if it fails, update the error DIV
- # # Generates:
- # # <script data-periodical='true'
- # # data-url='/invoice/1'
- # # type='application/json'
- # # data-update-success='invoice'
- # # data-update-failure='error'
- # # data-frequency='10'></script>"
- # #
- # periodically_call_remote(:url => { :action => 'invoice', :id => 1 },
- # :update => { :success => "invoice", :failure => "error" }
- #
- # # Call update every 20 seconds and update the new_block DIV
- # # Generates:
- # # <script data-periodical='true'
- # # data-url='update'
- # # type='application/json'
- # # data-update-success='news_block'
- # # data-frequency='20'></script>
- # #
- # periodically_call_remote(:url => 'update', :frequency => '20', :update => 'news_block')
- #
- def periodically_call_remote(options = {})
- attributes = extract_observer_attributes!(options)
- attributes["data-periodical"] = true
- attributes["data-frequency"] ||= 10
-
- # periodically_call_remote does not need data-observe=true
- attributes.delete('data-observe')
-
- script_decorator(attributes).html_safe!
- end
-
- # Observes the field with the DOM ID specified by +field_id+ and calls a
- # callback when its contents have changed. The default callback is an
- # Ajax call. By default the value of the observed field is sent as a
- # parameter with the Ajax call.
- #
- # Example:
- # # Generates:
- # # "<script type='text/javascript'
- # # data-observe='true'
- # # data-observed='suggest'
- # # data-frequency='0.25'
- # # type='application/json'
- # # data-url='/find_suggestion'
- # # data-update-success='suggest'
- # # data-with='q'></script>"
- # #
- # <%= observe_field :suggest, :url => { :action => :find_suggestion },
- # :frequency => 0.25,
- # :update => :suggest,
- # :with => 'q'
- # %>
- #
- # Required +options+ are either of:
- # <tt>:url</tt>:: +url_for+-style options for the action to call
- # when the field has changed.
- # <tt>:function</tt>:: Instead of making a remote call to a URL, you
- # can specify javascript code to be called instead.
- # Note that the value of this option is used as the
- # *body* of the javascript function, a function definition
- # with parameters named element and value will be generated for you
- # for example:
- # observe_field("glass", :frequency => 1, :function => "alert('Element changed')")
- # will generate:
- # new Form.Element.Observer('glass', 1, function(element, value) {alert('Element changed')})
- # The element parameter is the DOM element being observed, and the value is its value at the
- # time the observer is triggered.
- #
- # Additional options are:
- # <tt>:frequency</tt>:: The frequency (in seconds) at which changes to
- # this field will be detected. Not setting this
- # option at all or to a value equal to or less than
- # zero will use event based observation instead of
- # time based observation.
- # <tt>:update</tt>:: Specifies the DOM ID of the element whose
- # innerHTML should be updated with the
- # XMLHttpRequest response text.
- # <tt>:with</tt>:: A JavaScript expression specifying the parameters
- # for the XMLHttpRequest. The default is to send the
- # key and value of the observed field. Any custom
- # expressions should return a valid URL query string.
- # The value of the field is stored in the JavaScript
- # variable +value+.
- #
- # Examples
- #
- # :with => "'my_custom_key=' + value"
- # :with => "'person[name]=' + prompt('New name')"
- # :with => "Form.Element.serialize('other-field')"
- #
- # Finally
- # :with => 'name'
- # is shorthand for
- # :with => "'name=' + value"
- # This essentially just changes the key of the parameter.
- #
- # Additionally, you may specify any of the options documented in the
- # <em>Common options</em> section at the top of this document.
- #
- # Example:
- #
- # # Sends params: {:title => 'Title of the book'} when the book_title input
- # # field is changed.
- # observe_field 'book_title',
- # :url => 'http://example.com/books/edit/1',
- # :with => 'title'
- #
- #
- def observe_field(name, options = {})
- html_options = options.delete(:callbacks)
-
- options[:observed] = name
- attributes = extract_observer_attributes!(options)
- attributes.merge!(html_options) if html_options
-
- script_decorator(attributes).html_safe!
- end
-
- # Observes the form with the DOM ID specified by +form_id+ and calls a
- # callback when its contents have changed. The default callback is an
- # Ajax call. By default all fields of the observed field are sent as
- # parameters with the Ajax call.
- #
- # The +options+ for +observe_form+ are the same as the options for
- # +observe_field+. The JavaScript variable +value+ available to the
- # <tt>:with</tt> option is set to the serialized form by default.
- def observe_form(name, options = {})
- html_options = options.delete(:callbacks)
-
- options[:observed] = name
- attributes = extract_observer_attributes!(options)
- attributes.merge!(html_options) if html_options
-
- script_decorator(attributes).html_safe!
- end
-
- def script_decorator(options)
- attributes = %w(type="application/json")
- attributes += options.map{|k, v| k + '="' + v.to_s + '"'}
- "<script " + attributes.join(" ") + "></script>"
- end
-
- private
-
- def extract_remote_attributes!(options)
- attributes = options.delete(:html) || {}
-
- attributes.merge!(extract_update_attributes!(options))
- attributes.merge!(extract_request_attributes!(options))
- attributes["data-remote"] = true
-
- if submit = options.delete(:submit)
- attributes["data-submit"] = submit
- end
-
- attributes
- end
-
- def extract_request_attributes!(options)
- attributes = {}
- if method = options.delete(:method)
- attributes["data-method"] = method.to_s
- end
-
- if type = options.delete(:type)
- attributes["data-remote-type"] = type.to_s
- end
-
- url_options = options.delete(:url)
- url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash)
- attributes["data-url"] = escape_javascript(url_for(url_options)) if url_options
-
- purge_unused_attributes!(attributes)
- end
-
- def extract_update_attributes!(options)
- attributes = {}
- update = options.delete(:update)
- if update.is_a?(Hash)
- attributes["data-update-success"] = update[:success]
- attributes["data-update-failure"] = update[:failure]
- else
- attributes["data-update-success"] = update
- end
-
- if position = options.delete(:position)
- attributes["data-update-position"] = position.to_s
- end
-
- purge_unused_attributes!(attributes)
- end
-
- def extract_observer_attributes!(options)
- callback = options.delete(:function)
- frequency = options.delete(:frequency) || 10
-
-
- attributes = extract_remote_attributes!(options)
- attributes["data-observe"] = true
- attributes["data-observed"] = options.delete(:observed)
- attributes["data-onobserve"] = callback if callback
- attributes["data-frequency"] = frequency if frequency && frequency.to_f != 0
- attributes.delete("data-remote")
-
- purge_unused_attributes!(attributes)
- end
-
- def purge_unused_attributes!(attributes)
- attributes.delete_if {|key, value| value.nil? }
- attributes
- end
- end
-
- # TODO: All evaled goes here per wycat
- module AjaxHelperCompat
- include AjaxHelper
-
- def link_to_remote(name, options, html_options = {})
- set_callbacks(options, html_options)
- set_with_and_condition_attributes(options, html_options)
- super
- end
-
- def button_to_remote(name, options = {}, html_options = {})
- set_callbacks(options, html_options)
- set_with_and_condition_attributes(options, html_options)
- super
- end
-
- def form_remote_tag(options, &block)
- html = {}
- set_callbacks(options, html)
- set_with_and_condition_attributes(options, html)
- options.merge!(:callbacks => html)
- super
- end
-
- def observe_field(name, options = {})
- html = {}
- set_with_and_condition_attributes(options, html)
- options.merge!(:callbacks => html)
- super
- end
-
- def observe_form(name, options = {})
- html = {}
- set_with_and_condition_attributes(options, html)
- options.merge!(:callbacks => html)
- super
- end
-
- private
- def set_callbacks(options, html)
- [:before, :after, :uninitialized, :complete, :failure, :success, :interactive, :loaded, :loading].each do |type|
- html["data-on#{type}"] = options.delete(type.to_sym)
- end
-
- options.each do |option, value|
- if option.is_a?(Integer)
- html["data-on#{option}"] = options.delete(option)
- end
- end
- end
-
- def set_with_and_condition_attributes(options, html)
- if with = options.delete(:with)
- html["data-with"] = with
- end
-
- if condition = options.delete(:condition)
- html["data-condition"] = condition
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 83357dd76f..4df99f8293 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -140,7 +140,7 @@ module ActionView
:stylesheets_dir => "#{assets_dir}/stylesheets",
}
- JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
+ JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls', 'rails'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
# Returns a link tag that browsers and news readers can use to auto-detect
# an RSS or ATOM feed. The +type+ can either be <tt>:rss</tt> (default) or
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index b81925c7e7..157deebe40 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -262,8 +262,23 @@ module ActionView
# FormTagHelper#form_tag.
def form_for(record_or_name_or_array, *args, &proc)
raise ArgumentError, "Missing block" unless block_given?
+
options = args.extract_options!
- object_name = extract_object_name_for_form!(args, options, record_or_name_or_array)
+
+ case record_or_name_or_array
+ when String, Symbol
+ object_name = record_or_name_or_array
+ when Array
+ object = record_or_name_or_array.last
+ object_name = ActionController::RecordIdentifier.singular_class_name(object)
+ apply_form_for_options!(record_or_name_or_array, options)
+ args.unshift object
+ else
+ object = record_or_name_or_array
+ object_name = ActionController::RecordIdentifier.singular_class_name(object)
+ apply_form_for_options!([object], options)
+ args.unshift object
+ end
concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}))
fields_for(object_name, *(args << options), &proc)
@@ -727,25 +742,6 @@ module ActionView
def radio_button(object_name, method, tag_value, options = {})
InstanceTag.new(object_name, method, self, options.delete(:object)).to_radio_button_tag(tag_value, options)
end
-
- private
- def extract_object_name_for_form!(args, options, record_or_name_or_array)
- case record_or_name_or_array
- when String, Symbol
- object_name = record_or_name_or_array
- when Array
- object = record_or_name_or_array.last
- object_name = ActionController::RecordIdentifier.singular_class_name(object)
- apply_form_for_options!(record_or_name_or_array, options)
- args.unshift object
- else
- object = record_or_name_or_array
- object_name = ActionController::RecordIdentifier.singular_class_name(object)
- apply_form_for_options!([object], options)
- args.unshift object
- end
- object_name
- end
end
module InstanceTagMethods #:nodoc:
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index ebce5c1513..fb32f78e5b 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -57,7 +57,7 @@ module ActionView
# ==== Examples
# select_tag "people", options_from_collection_for_select(@people, "name", "id")
# # <select id="people" name="people"><option value="1">David</option></select>
- #
+ #
# select_tag "people", "<option>David</option>"
# # => <select id="people" name="people"><option>David</option></select>
#
@@ -128,7 +128,7 @@ module ActionView
# Creates a label field
#
- # ==== Options
+ # ==== Options
# * Creates standard HTML attributes for the tag.
#
# ==== Examples
@@ -352,20 +352,20 @@ module ActionView
# # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
#
# submit_tag "Complete sale", :disable_with => "Please wait..."
- # # => <input name="commit" data-disable-with="Please wait..."
+ # # => <input name="commit" onclick="this.disabled=true;this.value='Please wait...';this.form.submit();"
# # type="submit" value="Complete sale" />
#
# submit_tag nil, :class => "form_submit"
# # => <input class="form_submit" name="commit" type="submit" />
#
# submit_tag "Edit", :disable_with => "Editing...", :class => "edit-button"
- # # => <input class="edit-button" data-disable-with="Editing..."
+ # # => <input class="edit-button" onclick="this.disabled=true;this.value='Editing...';this.form.submit();"
# # name="commit" type="submit" value="Edit" />
def submit_tag(value = "Save changes", options = {})
options.stringify_keys!
if disable_with = options.delete("disable_with")
- add_disable_with_to_attributes!(options, disable_with)
+ options["data-disable-with"] = disable_with if disable_with
end
if confirm = options.delete("confirm")
@@ -482,7 +482,6 @@ module ActionView
def sanitize_to_id(name)
name.to_s.gsub(']','').gsub(/[^-a-zA-Z0-9:.]/, "_")
end
-
end
end
end
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index ee6481b86d..7dca9849c0 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -35,98 +35,7 @@ module ActionView
# For documentation on +javascript_include_tag+ see
# ActionView::Helpers::AssetTagHelper.
module JavaScriptHelper
- unless const_defined? :JAVASCRIPT_PATH
- JAVASCRIPT_PATH = File.join(File.dirname(__FILE__), 'javascripts')
- end
-
- include AjaxHelperCompat
-
- # Returns a link of the given +name+ that will trigger a JavaScript +function+ using the
- # onclick handler and return false after the fact.
- #
- # The first argument +name+ is used as the link text.
- #
- # The next arguments are optional and may include the javascript function definition and a hash of html_options.
- #
- # The +function+ argument can be omitted in favor of an +update_page+
- # block, which evaluates to a string when the template is rendered
- # (instead of making an Ajax request first).
- #
- # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
- #
- # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
- #
- #
- # Examples:
- # link_to_function "Greeting", "alert('Hello world!')"
- # Produces:
- # <a onclick="alert('Hello world!'); return false;" href="#">Greeting</a>
- #
- # link_to_function(image_tag("delete"), "if (confirm('Really?')) do_delete()")
- # Produces:
- # <a onclick="if (confirm('Really?')) do_delete(); return false;" href="#">
- # <img src="/images/delete.png?" alt="Delete"/>
- # </a>
- #
- # link_to_function("Show me more", nil, :id => "more_link") do |page|
- # page[:details].visual_effect :toggle_blind
- # page[:more_link].replace_html "Show me less"
- # end
- # Produces:
- # <a href="#" id="more_link" onclick="try {
- # $(&quot;details&quot;).visualEffect(&quot;toggle_blind&quot;);
- # $(&quot;more_link&quot;).update(&quot;Show me less&quot;);
- # }
- # catch (e) {
- # alert('RJS error:\n\n' + e.toString());
- # alert('$(\&quot;details\&quot;).visualEffect(\&quot;toggle_blind\&quot;);
- # \n$(\&quot;more_link\&quot;).update(\&quot;Show me less\&quot;);');
- # throw e
- # };
- # return false;">Show me more</a>
- #
- def link_to_function(name, *args, &block)
- html_options = args.extract_options!.symbolize_keys
-
- function = block_given? ? update_page(&block) : args[0] || ''
- onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;"
- href = html_options[:href] || '#'
-
- content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick))
- end
-
- # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the
- # onclick handler.
- #
- # The first argument +name+ is used as the button's value or display text.
- #
- # The next arguments are optional and may include the javascript function definition and a hash of html_options.
- #
- # The +function+ argument can be omitted in favor of an +update_page+
- # block, which evaluates to a string when the template is rendered
- # (instead of making an Ajax request first).
- #
- # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
- #
- # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
- #
- # Examples:
- # button_to_function "Greeting", "alert('Hello world!')"
- # button_to_function "Delete", "if (confirm('Really?')) do_delete()"
- # button_to_function "Details" do |page|
- # page[:details].visual_effect :toggle_slide
- # end
- # button_to_function "Details", :class => "details_button" do |page|
- # page[:details].visual_effect :toggle_slide
- # end
- def button_to_function(name, *args, &block)
- html_options = args.extract_options!.symbolize_keys
-
- function = block_given? ? update_page(&block) : args[0] || ''
- onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
-
- tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
- end
+ include PrototypeHelper
JS_ESCAPE_MAP = {
'\\' => '\\\\',
@@ -186,65 +95,6 @@ module ActionView
def javascript_cdata_section(content) #:nodoc:
"\n//#{cdata_section("\n#{content}\n//")}\n"
end
-
- protected
- def convert_options_to_javascript!(html_options, url = '')
- confirm, popup = html_options.delete("confirm"), html_options.delete("popup")
-
- method, href = html_options.delete("method"), html_options['href']
-
- if popup && method
- raise ActionView::ActionViewError, "You can't use :popup and :method in the same link"
- elsif confirm && popup
- add_confirm_to_attributes!(html_options, confirm)
- html_options["data-popup"] = popup_attributes(popup)
- elsif confirm && method
- add_confirm_to_attributes!(html_options, confirm)
- add_method_to_attributes!(html_options, method, url)
- elsif confirm
- add_confirm_to_attributes!(html_options, confirm)
- elsif method
- add_method_to_attributes!(html_options, method, url)
- elsif popup
- html_options["data-popup"] = popup_attributes(popup)
- end
- end
-
- def add_confirm_to_attributes!(html_options, confirm)
- html_options["data-confirm"] = confirm if confirm
- end
-
- def add_method_to_attributes!(html_options, method, url = nil)
- html_options["rel"] = "nofollow" if method.to_s.downcase == "delete"
- html_options["data-method"] = method
- if url.size > 0
- html_options["data-url"] = url
- end
- end
-
- def add_disable_with_to_attributes!(html_options, disable_with)
- html_options["data-disable-with"] = disable_with if disable_with
- end
-
- def popup_attributes(popup)
- popup.is_a?(Array) ? "{title: '#{popup.first}', options: '#{popup.last}'}" : "true"
- end
-
- def options_for_javascript(options)
- if options.empty?
- '{}'
- else
- "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}"
- end
- end
-
- def array_or_string_for_javascript(option)
- if option.kind_of?(Array)
- "['#{option.join('\',\'')}']"
- elsif !option.nil?
- "'#{option}'"
- end
- end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index d861810f19..d335d89274 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -1,7 +1,6 @@
require 'set'
require 'active_support/json'
require 'active_support/core_ext/object/returning'
-require 'action_view/helpers/scriptaculous_helper'
module ActionView
module Helpers
@@ -28,6 +27,26 @@ module ActionView
# ActionView::Helpers::JavaScriptHelper for more information on including
# this and other JavaScript files in your Rails templates.)
#
+ # Now you're ready to call a remote action either through a link...
+ #
+ # link_to_remote "Add to cart",
+ # :url => { :action => "add", :id => product.id },
+ # :update => { :success => "cart", :failure => "error" }
+ #
+ # ...through a form...
+ #
+ # <% form_remote_tag :url => '/shipping' do -%>
+ # <div><%= submit_tag 'Recalculate Shipping' %></div>
+ # <% end -%>
+ #
+ # As you can see, there are numerous ways to use Prototype's Ajax functions (and actually more than
+ # are listed here); check out the documentation for each method to find out more about its usage and options.
+ #
+ # === Common Options
+ # See link_to_remote for documentation of options common to all Ajax
+ # helpers; any of the options specified by link_to_remote can be used
+ # by the other helpers.
+ #
# == Designing your Rails actions for Ajax
# When building your action handlers (that is, the Rails actions that receive your background requests), it's
# important to remember a few things. First, whatever your action would normally return to the browser, it will
@@ -74,8 +93,6 @@ module ActionView
# See JavaScriptGenerator for information on updating multiple elements
# on the page in an Ajax response.
module PrototypeHelper
- include ScriptaculousHelper
-
unless const_defined? :CALLBACKS
CALLBACKS = Set.new([ :create, :uninitialized, :loading, :loaded,
:interactive, :complete, :failure, :success ] +
@@ -85,6 +102,39 @@ module ActionView
:form, :with, :update, :script, :type ]).merge(CALLBACKS)
end
+ # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the
+ # onclick handler.
+ #
+ # The first argument +name+ is used as the button's value or display text.
+ #
+ # The next arguments are optional and may include the javascript function definition and a hash of html_options.
+ #
+ # The +function+ argument can be omitted in favor of an +update_page+
+ # block, which evaluates to a string when the template is rendered
+ # (instead of making an Ajax request first).
+ #
+ # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
+ #
+ # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
+ #
+ # Examples:
+ # button_to_function "Greeting", "alert('Hello world!')"
+ # button_to_function "Delete", "if (confirm('Really?')) do_delete()"
+ # button_to_function "Details" do |page|
+ # page[:details].visual_effect :toggle_slide
+ # end
+ # button_to_function "Details", :class => "details_button" do |page|
+ # page[:details].visual_effect :toggle_slide
+ # end
+ def button_to_function(name, *args, &block)
+ html_options = args.extract_options!.symbolize_keys
+
+ function = block_given? ? update_page(&block) : args[0] || ''
+ onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
+
+ tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
+ end
+
# Returns the JavaScript needed for a remote function.
# Takes the same arguments as link_to_remote.
#
@@ -502,32 +552,6 @@ module ActionView
record "}, #{(seconds * 1000).to_i})"
end
- # 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
-
private
def loop_on_multiple_args(method, ids)
record(ids.size>1 ?
@@ -599,65 +623,57 @@ module ActionView
javascript_tag update_page(&block), html_options
end
- protected
- def options_for_ajax(options)
- js_options = build_callbacks(options)
-
- js_options['asynchronous'] = options[:type] != :synchronous
- js_options['method'] = method_option_to_s(options[:method]) if options[:method]
- js_options['insertion'] = "'#{options[:position].to_s.downcase}'" if options[:position]
- js_options['evalScripts'] = options[:script].nil? || options[:script]
-
- if options[:form]
- js_options['parameters'] = 'Form.serialize(this)'
- elsif options[:submit]
- js_options['parameters'] = "Form.serialize('#{options[:submit]}')"
- elsif options[:with]
- js_options['parameters'] = options[:with]
- end
-
- if protect_against_forgery? && !options[:form]
- if js_options['parameters']
- js_options['parameters'] << " + '&"
+ protected
+ def options_for_javascript(options)
+ if options.empty?
+ '{}'
else
- js_options['parameters'] = "'"
+ "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}"
end
- js_options['parameters'] << "#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}')"
end
- options_for_javascript(js_options)
- end
+ def options_for_ajax(options)
+ js_options = build_callbacks(options)
- def method_option_to_s(method)
- (method.is_a?(String) and !method.index("'").nil?) ? method : "'#{method}'"
- end
+ js_options['asynchronous'] = options[:type] != :synchronous
+ js_options['method'] = method_option_to_s(options[:method]) if options[:method]
+ js_options['insertion'] = "'#{options[:position].to_s.downcase}'" if options[:position]
+ js_options['evalScripts'] = options[:script].nil? || options[:script]
- def build_observer(klass, name, options = {})
- if options[:with] && (options[:with] !~ /[\{=(.]/)
- options[:with] = "'#{options[:with]}=' + encodeURIComponent(value)"
- else
- options[:with] ||= 'value' unless options[:function]
+ if options[:form]
+ js_options['parameters'] = 'Form.serialize(this)'
+ elsif options[:submit]
+ js_options['parameters'] = "Form.serialize('#{options[:submit]}')"
+ elsif options[:with]
+ js_options['parameters'] = options[:with]
+ end
+
+ if protect_against_forgery? && !options[:form]
+ if js_options['parameters']
+ js_options['parameters'] << " + '&"
+ else
+ js_options['parameters'] = "'"
+ end
+ js_options['parameters'] << "#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}')"
+ end
+
+ options_for_javascript(js_options)
end
- callback = options[:function] || remote_function(options)
- javascript = "new #{klass}('#{name}', "
- javascript << "#{options[:frequency]}, " if options[:frequency]
- javascript << "function(element, value) {"
- javascript << "#{callback}}"
- javascript << ")"
- javascript_tag(javascript)
- end
+ def method_option_to_s(method)
+ (method.is_a?(String) and !method.index("'").nil?) ? method : "'#{method}'"
+ end
- def build_callbacks(options)
- callbacks = {}
- options.each do |callback, code|
- if CALLBACKS.include?(callback)
- name = 'on' + callback.to_s.capitalize
- callbacks[name] = "function(request){#{code}}"
+ def build_callbacks(options)
+ callbacks = {}
+ options.each do |callback, code|
+ if CALLBACKS.include?(callback)
+ name = 'on' + callback.to_s.capitalize
+ callbacks[name] = "function(request){#{code}}"
+ end
end
+ callbacks
end
- callbacks
- end
end
# Converts chained method calls on DOM proxy elements into JavaScript chains
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
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 4424dbba42..bd179ef0b3 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -120,10 +120,6 @@ module ActionView
# * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
# prompt with the question specified. If the user accepts, the link is
# processed normally, otherwise no action is taken.
- # * <tt>:popup => true || array of window options</tt> - This will force the
- # link to open in a popup window. By passing true, a default browser window
- # will be opened with the URL. You can also specify an array of options
- # that are passed to the <tt>window.open</tt> JavaScript call.
# * <tt>:method => symbol of HTTP verb</tt> - This modifier will dynamically
# create an HTML form and immediately submit the form for processing using
# the HTTP verb specified. Useful for having links perform a POST operation
@@ -136,10 +132,6 @@ module ActionView
# the request object's methods for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>.
# * The +html_options+ will accept a hash of html attributes for the link tag.
#
- # You can mix and match the +html_options+ with the exception of
- # <tt>:popup</tt> and <tt>:method</tt> which will raise an
- # <tt>ActionView::ActionViewError</tt> exception.
- #
# ==== Examples
# Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
# and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base
@@ -203,17 +195,11 @@ module ActionView
# link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux")
# # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
#
- # The three options specific to +link_to+ (<tt>:confirm</tt>, <tt>:popup</tt>, and <tt>:method</tt>) are used as follows:
+ # The three options specific to +link_to+ (<tt>:confirm</tt> and <tt>:method</tt>) are used as follows:
#
# link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
# # => <a href="http://www.rubyonrails.org/" onclick="return confirm('Are you sure?');">Visit Other Site</a>
#
- # link_to "Help", { :action => "help" }, :popup => true
- # # => <a href="/testing/help/" onclick="window.open(this.href);return false;">Help</a>
- #
- # link_to "View Image", @image, :popup => ['new_window_name', 'height=300,width=600']
- # # => <a href="/images/9" onclick="window.open(this.href,'new_window_name','height=300,width=600');return false;">View Image</a>
- #
# link_to "Delete Image", @image, :confirm => "Are you sure?", :method => :delete
# # => <a href="/images/9" onclick="if (confirm('Are you sure?')) { var f = document.createElement('form');
# f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;
@@ -232,11 +218,11 @@ module ActionView
html_options = args[2]
url = url_for(options)
+ html_options = convert_options_to_data_attributes(options, html_options)
if html_options
html_options = html_options.stringify_keys
href = html_options['href']
- convert_options_to_javascript!(html_options, url)
tag_options = tag_options(html_options)
else
tag_options = nil
@@ -305,11 +291,10 @@ module ActionView
request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
end
-
url = options.is_a?(String) ? options : self.url_for(options)
name ||= url
-
- convert_options_to_javascript!(html_options, url)
+
+ html_options = convert_options_to_data_attributes(options, html_options)
html_options.merge!("type" => "submit", "value" => name)
@@ -562,6 +547,63 @@ module ActionView
end
private
+ def convert_options_to_data_attributes(options, html_options)
+ html_options = {} if html_options.nil?
+ html_options = html_options.stringify_keys
+
+ if (options.is_a?(Hash) && options.key?('remote')) || (html_options.is_a?(Hash) && html_options.key?('remote'))
+ html_options['data-remote'] = 'true'
+ options.delete('remote') if options.is_a?(Hash)
+ html_options.delete('remote') if html_options.is_a?(Hash)
+ end
+
+ confirm = html_options.delete("confirm")
+
+ if html_options.key?("popup")
+ ActiveSupport::Deprecation.warn(":popup has been deprecated", caller)
+ end
+
+ method, href = html_options.delete("method"), html_options['href']
+
+ if confirm && method
+ add_confirm_to_attributes!(html_options, confirm)
+ add_method_to_attributes!(html_options, method)
+ elsif confirm
+ add_confirm_to_attributes!(html_options, confirm)
+ elsif method
+ add_method_to_attributes!(html_options, method)
+ end
+
+ html_options["data-url"] = options[:url] if options.is_a?(Hash) && options[:url]
+
+ html_options
+ end
+
+ def add_confirm_to_attributes!(html_options, confirm)
+ html_options["data-confirm"] = confirm if confirm
+ end
+
+ def add_method_to_attributes!(html_options, method)
+ html_options["rel"] = "nofollow" if method && method.to_s.downcase != "get"
+ html_options["data-method"] = method if method
+ end
+
+ def options_for_javascript(options)
+ if options.empty?
+ '{}'
+ else
+ "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}"
+ end
+ end
+
+ def array_or_string_for_javascript(option)
+ if option.kind_of?(Array)
+ "['#{option.join('\',\'')}']"
+ elsif !option.nil?
+ "'#{option}'"
+ end
+ end
+
# Processes the +html_options+ hash, converting the boolean
# attributes from true/false form into the form required by
# HTML/XHTML. (An attribute is considered to be boolean if
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index 0059b79e5f..6e55d69d00 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -45,7 +45,7 @@ module ActionView #:nodoc:
end
end
- raise ActionView::MissingTemplate.new(self, "#{prefix}/#{path} - #{details.inspect} - partial: #{!!partial}")
+ raise ActionView::MissingTemplate.new(self, "#{prefix}/#{path}", details, partial)
end
def exists?(path, extension = nil, prefix = nil, partial = false)
diff --git a/actionpack/test/abstract/render_test.rb b/actionpack/test/abstract/render_test.rb
index ffd430fa86..db924633ca 100644
--- a/actionpack/test/abstract/render_test.rb
+++ b/actionpack/test/abstract/render_test.rb
@@ -11,13 +11,17 @@ module AbstractController
end
self.view_paths = [ActionView::FixtureResolver.new(
- "default.erb" => "With Default",
"template.erb" => "With Template",
+ "renderer/default.erb" => "With Default",
"renderer/string.erb" => "With String",
"renderer/symbol.erb" => "With Symbol",
+ "renderer/template_name.erb" => "With Template Name",
"string/with_path.erb" => "With String With Path",
"some/file.erb" => "With File",
- "template_name.erb" => "With Template Name"
+ "with_format.html.erb" => "With html format",
+ "with_format.xml.erb" => "With xml format",
+ "with_locale.en.erb" => "With en locale",
+ "with_locale.pl.erb" => "With pl locale"
)]
def template
@@ -59,6 +63,22 @@ module AbstractController
def object
render :_template => ActionView::Template::Text.new("With Object")
end
+
+ def with_html_format
+ render :template => "with_format", :format => :html
+ end
+
+ def with_xml_format
+ render :template => "with_format", :format => :xml
+ end
+
+ def with_en_locale
+ render :template => "with_locale"
+ end
+
+ def with_pl_locale
+ render :template => "with_locale", :locale => :pl
+ end
end
class TestRenderer < ActiveSupport::TestCase
@@ -117,6 +137,25 @@ module AbstractController
assert_equal "With Object", @controller.response_body
end
+ def test_render_with_html_format
+ @controller.process(:with_html_format)
+ assert_equal "With html format", @controller.response_body
+ end
+
+ def test_render_with_xml_format
+ @controller.process(:with_xml_format)
+ assert_equal "With xml format", @controller.response_body
+ end
+
+ def test_render_with_en_locale
+ @controller.process(:with_en_locale)
+ assert_equal "With en locale", @controller.response_body
+ end
+
+ def test_render_with_pl_locale
+ @controller.process(:with_pl_locale)
+ assert_equal "With pl locale", @controller.response_body
+ end
end
end
end
diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb
index 2e77d2f8ad..612827dd41 100644
--- a/actionpack/test/controller/assert_select_test.rb
+++ b/actionpack/test/controller/assert_select_test.rb
@@ -18,7 +18,7 @@ unless defined?(ActionMailer)
end
end
-ActionMailer::Base.template_root = FIXTURE_LOAD_PATH
+ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH
class AssertSelectTest < ActionController::TestCase
Assertion = ActiveSupport::TestCase::Assertion
@@ -716,7 +716,7 @@ EOF
def test_assert_select_email
assert_raise(Assertion) { assert_select_email {} }
- AssertSelectMailer.deliver_test "<div><p>foo</p><p>bar</p></div>"
+ AssertSelectMailer.test("<div><p>foo</p><p>bar</p></div>").deliver
assert_select_email do
assert_select "div:root" do
assert_select "p:first-child", "foo"
diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb
index e53e62d1ff..75a96d6497 100644
--- a/actionpack/test/controller/helper_test.rb
+++ b/actionpack/test/controller/helper_test.rb
@@ -135,17 +135,16 @@ class HelperTest < ActiveSupport::TestCase
assert methods.include?('foobar')
end
- # TODO Add this deprecation back before Rails 3.0 final release
- # def test_deprecation
- # assert_deprecated do
- # ActionController::Base.helpers_dir = "some/foo/bar"
- # end
- # assert_deprecated do
- # assert_equal ["some/foo/bar"], ActionController::Base.helpers_dir
- # end
- # ensure
- # ActionController::Base.helpers_path = [File.dirname(__FILE__) + '/../fixtures/helpers']
- # end
+ def test_deprecation
+ assert_deprecated do
+ ActionController::Base.helpers_dir = "some/foo/bar"
+ end
+ assert_deprecated do
+ assert_equal ["some/foo/bar"], ActionController::Base.helpers_dir
+ end
+ ensure
+ ActionController::Base.helpers_path = [File.dirname(__FILE__) + '/../fixtures/helpers']
+ end
private
def expected_helper_methods
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index ba2347e4e2..3bd3369242 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -499,7 +499,7 @@ class RespondWithController < ActionController::Base
def using_resource_with_action
respond_with(resource, :action => :foo) do |format|
- format.html { raise ActionView::MissingTemplate.new([], "method") }
+ format.html { raise ActionView::MissingTemplate.new([], "foo/bar", {}, false) }
end
end
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index 09003adf73..b2a0e2e2a3 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -6,14 +6,10 @@ module RequestForgeryProtectionActions
def index
render :inline => "<%= form_tag('/') {} %>"
end
-
+
def show_button
render :inline => "<%= button_to('New', '/') {} %>"
end
-
- def remote_form
- render :inline => "<% form_remote_tag(:url => '/') {} %>"
- end
def unsafe
render :text => 'pwn'
@@ -30,11 +26,11 @@ end
class FreeCookieController < RequestForgeryProtectionController
self.allow_forgery_protection = false
-
+
def index
render :inline => "<%= form_tag('/') {} %>"
end
-
+
def show_button
render :inline => "<%= button_to('New', '/') {} %>"
end
@@ -65,11 +61,6 @@ module RequestForgeryProtectionTests
assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
end
- def test_should_render_remote_form_with_only_one_token_parameter
- get :remote_form
- assert_equal 1, @response.body.scan(@token).size
- end
-
def test_should_allow_get
get :index
assert_response :success
@@ -84,12 +75,12 @@ module RequestForgeryProtectionTests
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
assert_raise(ActionController::InvalidAuthenticityToken) { post :index, :format => :html }
end
-
+
def test_should_not_allow_html_put_without_token
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
assert_raise(ActionController::InvalidAuthenticityToken) { put :index, :format => :html }
end
-
+
def test_should_not_allow_html_delete_without_token
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
assert_raise(ActionController::InvalidAuthenticityToken) { delete :index, :format => :html }
@@ -154,51 +145,51 @@ module RequestForgeryProtectionTests
delete :index, :format => 'xml'
end
end
-
+
def test_should_allow_xhr_post_without_token
assert_nothing_raised { xhr :post, :index }
end
-
+
def test_should_allow_xhr_put_without_token
assert_nothing_raised { xhr :put, :index }
end
-
+
def test_should_allow_xhr_delete_without_token
assert_nothing_raised { xhr :delete, :index }
end
-
+
def test_should_allow_xhr_post_with_encoded_form_content_type_without_token
@request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
assert_nothing_raised { xhr :post, :index }
end
-
+
def test_should_allow_post_with_token
post :index, :authenticity_token => @token
assert_response :success
end
-
+
def test_should_allow_put_with_token
put :index, :authenticity_token => @token
assert_response :success
end
-
+
def test_should_allow_delete_with_token
delete :index, :authenticity_token => @token
assert_response :success
end
-
+
def test_should_allow_post_with_xml
@request.env['CONTENT_TYPE'] = Mime::XML.to_s
post :index, :format => 'xml'
assert_response :success
end
-
+
def test_should_allow_put_with_xml
@request.env['CONTENT_TYPE'] = Mime::XML.to_s
put :index, :format => 'xml'
assert_response :success
end
-
+
def test_should_allow_delete_with_xml
@request.env['CONTENT_TYPE'] = Mime::XML.to_s
delete :index, :format => 'xml'
@@ -231,17 +222,17 @@ class FreeCookieControllerTest < ActionController::TestCase
ActiveSupport::SecureRandom.stubs(:base64).returns(@token)
end
-
+
def test_should_not_render_form_with_token_tag
get :index
assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false
end
-
+
def test_should_not_render_button_to_with_token_tag
get :show_button
assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false
end
-
+
def test_should_allow_all_methods_without_token
[:post, :put, :delete].each do |method|
assert_nothing_raised { send(method, :index)}
diff --git a/actionpack/test/abstract/url_for_test.rb b/actionpack/test/controller/url_for_test.rb
index e5570349b8..749fa5861f 100644
--- a/actionpack/test/abstract/url_for_test.rb
+++ b/actionpack/test/controller/url_for_test.rb
@@ -5,7 +5,7 @@ module AbstractController
class UrlForTests < ActionController::TestCase
class W
- include AbstractController::UrlFor
+ include ActionController::UrlFor
end
def teardown
@@ -132,7 +132,7 @@ module AbstractController
end
# We need to create a new class in order to install the new named route.
- kls = Class.new { include AbstractController::UrlFor }
+ kls = Class.new { include ActionController::UrlFor }
controller = kls.new
assert controller.respond_to?(:home_url)
assert_equal 'http://www.basecamphq.com/home/sweet/home/again',
@@ -153,7 +153,7 @@ module AbstractController
match '/home/sweet/home/:user', :to => 'home#index', :as => :home
end
- kls = Class.new { include AbstractController::UrlFor }
+ kls = Class.new { include ActionController::UrlFor }
controller = kls.new
assert_equal 'http://www.basecamphq.com/subdir/home/sweet/home/again',
@@ -171,7 +171,7 @@ module AbstractController
end
# We need to create a new class in order to install the new named route.
- kls = Class.new { include AbstractController::UrlFor }
+ kls = Class.new { include ActionController::UrlFor }
controller = kls.new
assert controller.respond_to?(:home_url)
assert_equal '/brave/new/world',
@@ -239,7 +239,7 @@ module AbstractController
end
# We need to create a new class in order to install the new named route.
- kls = Class.new { include AbstractController::UrlFor }
+ kls = Class.new { include ActionController::UrlFor }
kls.default_url_options[:host] = 'www.basecamphq.com'
controller = kls.new
@@ -251,8 +251,8 @@ module AbstractController
end
def test_multiple_includes_maintain_distinct_options
- first_class = Class.new { include AbstractController::UrlFor }
- second_class = Class.new { include AbstractController::UrlFor }
+ first_class = Class.new { include ActionController::UrlFor }
+ second_class = Class.new { include ActionController::UrlFor }
first_host, second_host = 'firsthost.com', 'secondhost.com'
diff --git a/actionpack/test/template/ajax_helper_test.rb b/actionpack/test/template/ajax_helper_test.rb
deleted file mode 100644
index c925dbb8f6..0000000000
--- a/actionpack/test/template/ajax_helper_test.rb
+++ /dev/null
@@ -1,452 +0,0 @@
-require 'abstract_unit'
-require 'active_model'
-
-class Author
- extend ActiveModel::Naming
- include ActiveModel::Conversion
-
- attr_reader :id
- def save
- @id = 1
- end
-
- def new_record?
- @id.nil?
- end
-
- def name
- @id.nil? ? 'new author' : "author ##{@id}"
- end
-end
-
-class Article
- extend ActiveModel::Naming
- include ActiveModel::Conversion
- attr_reader :id
- attr_reader :author_id
-
- def save
- @id = 1
- @author_id = 1
- end
-
- def new_record?
- @id.nil?
- end
-
- def name
- @id.nil? ? 'new article' : "article ##{@id}"
- end
-end
-
-class AjaxHelperBaseTest < ActionView::TestCase
- attr_accessor :formats, :output_buffer
-
- def reset_formats(format)
- @format = format
- end
-
- def setup
- super
- @template = self
- @controller = Class.new do
-
- def url_for(options)
- return optons unless options.is_a?(Hash)
-
- url = options.delete(:only_path) ? '/' : 'http://www.example.com'
-
- if controller = options.delete(:controller)
- url << '/' << controller.to_s
- end
- if action = options.delete(:action)
- url << '/' << action.to_s
- end
-
- if id = options.delete(:id)
- url << '/' << id.to_s
- end
-
- url << hash_to_param(options) if options.any?
-
- url.gsub!(/\/\/+/,'/')
-
- url
- end
-
- private
- def hash_to_param(hash)
- hash.map { |k,v| "#{k}=#{v}" }.join('&').insert(0,'?')
- end
- end.new
- end
-
- protected
- def request_forgery_protection_token
- nil
- end
-
- def protect_against_forgery?
- false
- end
-end
-
-class AjaxHelperTest < AjaxHelperBaseTest
- def _evaluate_assigns_and_ivars() end
-
- def setup
- @record = @author = Author.new
- @article = Article.new
- super
- end
-
- test "link_to_remote" do
- assert_dom_equal %(<a class=\"fine\" href=\"#\" data-remote=\"true\" data-url=\"/whatnot\">Remove Author</a>),
- link_to_remote("Remove Author", { :url => { :action => "whatnot" }}, { :class => "fine" })
- assert_dom_equal %(<a href=\"#\" data-remote=\"true\" data-url=\"/whatnot\" data-oncomplete=\"alert(request.responseText)\">Remove Author</a>),
- link_to_remote("Remove Author", :complete => "alert(request.responseText)", :url => { :action => "whatnot" })
- assert_dom_equal %(<a href=\"#\" data-remote=\"true\" data-url=\"/whatnot\" data-onsuccess=\"alert(request.responseText)\">Remove Author</a>),
- link_to_remote("Remove Author", :success => "alert(request.responseText)", :url => { :action => "whatnot" })
- assert_dom_equal %(<a href=\"#\" data-remote=\"true\" data-url=\"/whatnot\" data-onfailure=\"alert(request.responseText)\">Remove Author</a>),
- link_to_remote("Remove Author", :failure => "alert(request.responseText)", :url => { :action => "whatnot" })
- assert_dom_equal %(<a href=\"#\" data-remote=\"true\" data-url=\"/whatnot?a=10&amp;b=20\" data-onfailure=\"alert(request.responseText)\">Remove Author</a>),
- link_to_remote("Remove Author", :failure => "alert(request.responseText)", :url => { :action => "whatnot", :a => '10', :b => '20' })
- assert_dom_equal %(<a href=\"#\" data-remote=\"true\" data-url=\"/whatnot\" data-remote-type=\"synchronous\">Remove Author</a>),
- link_to_remote("Remove Author", :url => { :action => "whatnot" }, :type => :synchronous)
- assert_dom_equal %(<a href=\"#\" data-remote=\"true\" data-url=\"/whatnot\" data-update-position=\"bottom\">Remove Author</a>),
- link_to_remote("Remove Author", :url => { :action => "whatnot" }, :position => :bottom)
- end
-
- test "link_to_remote with url and oncomplete" do
- actual = link_to_remote "undo", :url => { :controller => "words", :action => "undo", :n => 5 }, :complete => "undoRequestCompleted(request)"
- expected = '<a href="#" data-url="/words/undo?n=5" data-remote="true" data-oncomplete="undoRequestCompleted(request)">undo</a>'
- assert_dom_equal expected, actual
- end
-
- test "link_to_remote with delete" do
- actual = link_to_remote("Remove Author", { :url => { :action => "whatnot" }, :method => 'delete'}, { :class => "fine" })
- expected = '<a class="fine" rel="nofollow" href="#" data-remote="true" data-method="delete" data-url="/whatnot">Remove Author</a>'
- assert_dom_equal expected, actual
- end
-
- test "link_to_remote using both url and href" do
- expected = '<a href="/destroy" data-url="/destroy" data-update-success="posts" data-remote="true">Delete this Post</a>'
- assert_dom_equal expected, link_to_remote( "Delete this Post",
- { :update => "posts",
- :url => { :action => "destroy" } },
- :href => url_for(:action => "destroy"))
- end
-
- test "link_to_remote with update-success and url" do
- expected = '<a href="#" data-url="/destroy" data-update-success="posts" data-update-failure="error" data-remote="true">Delete this Post</a>'
- assert_dom_equal expected, link_to_remote( "Delete this Post", :url => { :action => "destroy"},
- :update => { :success => "posts", :failure => "error" })
- end
-
- test "link_to_remote with before/after callbacks" do
- assert_dom_equal %(<a href=\"#\" data-remote=\"true\" data-url=\"/whatnot\" data-onbefore=\"before();\" data-onafter=\"after();\">Remote outauthor</a>),
- link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :before => "before();", :after => "after();")
- end
-
- test "link_to_remote using :with expression" do
- expected = %(<a href=\"#\" data-remote=\"true\" data-url=\"/whatnot\" data-with=\"id=123\">Remote outauthor</a>)
- assert_dom_equal expected, link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :with => "id=123")
- end
-
- test "link_to_remote using :condition expression" do
- expected = %(<a href=\"#\" data-remote=\"true\" data-url=\"/whatnot\" data-condition=\"$('foo').val() == true\">Remote outauthor</a>)
- assert_dom_equal expected, link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :condition => '$(\'foo\').val() == true')
- end
-
- test "link_to_remote using explicit :href" do
- expected = %(<a href=\"http://www.example.com/testhref\" data-remote=\"true\" data-url=\"/whatnot\" data-condition=\"$('foo').val() == true\">Remote outauthor</a>)
- assert_dom_equal expected, link_to_remote("Remote outauthor", {:url => { :action => "whatnot" }, :condition => '$(\'foo\').val() == true'}, :href => 'http://www.example.com/testhref')
- end
-
- test "link_to_remote using :submit" do
- expected = %(<a href=\"#\" data-remote=\"true\" data-url=\"/whatnot\" data-submit=\"myForm\">Remote outauthor</a>)
- assert_dom_equal expected, link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :submit => 'myForm')
- end
-
- test "link_to_remote with method delete" do
- assert_dom_equal %(<a class=\"fine\" href=\"#\" data-remote=\"true\" data-url=\"/whatnot\" data-method=\"delete\" rel=\"nofollow\">Remote outauthor</a>),
- link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }, :method => "delete"}, { :class => "fine" })
- end
-
- test "link_to_remote with method delete as symbol" do
- assert_dom_equal %(<a class=\"fine\" href=\"#\" data-remote=\"true\" data-url=\"/whatnot\" data-method=\"delete\" rel=\"nofollow\">Remote outauthor</a>),
- link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }, :method => :delete}, { :class => "fine" })
- end
-
- test "link_to_remote html options" do
- assert_dom_equal %(<a class=\"fine\" href=\"#\" data-remote=\"true\" data-url=\"/whatnot\">Remote outauthor</a>),
- link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }, :html => { :class => "fine" } })
- end
-
- test "link_to_remote url quote escaping" do
- assert_dom_equal %(<a href="#" data-remote=\"true\" data-url=\"/whatnot\\\'s\">Remote</a>),
- link_to_remote("Remote", { :url => { :action => "whatnot's" } })
- end
-
- test "link_to_remote with confirm" do
- assert_dom_equal %(<a class=\"fine\" href=\"#\" data-remote=\"true\" data-url=\"/whatnot\" data-method=\"delete\" rel=\"nofollow\" data-confirm="Are you sure?">Remote confirm</a>),
- link_to_remote("Remote confirm", { :url => { :action => "whatnot" }, :method => "delete", :confirm => "Are you sure?"}, { :class => "fine" })
- end
-
- test "button_to_remote" do
- assert_dom_equal %(<input class=\"fine\" type=\"button\" value=\"Remote outpost\" data-remote=\"true\" data-url=\"/whatnot\" />),
- button_to_remote("Remote outpost", { :url => { :action => "whatnot" }}, { :class => "fine" })
- assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" data-remote=\"true\" data-url=\"/whatnot\" data-oncomplete=\"alert(request.reponseText)\" />),
- button_to_remote("Remote outpost", :complete => "alert(request.reponseText)", :url => { :action => "whatnot" })
- assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" data-remote=\"true\" data-url=\"/whatnot\" data-onsuccess=\"alert(request.reponseText)\" />),
- button_to_remote("Remote outpost", :success => "alert(request.reponseText)", :url => { :action => "whatnot" })
- assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" data-remote=\"true\" data-url=\"/whatnot\" data-onfailure=\"alert(request.reponseText)\" />),
- button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot" })
- assert_dom_equal %(<input type=\"button\" value=\"Remote outpost\" data-remote=\"true\" data-url=\"/whatnot?a=10&amp;b=20\" data-onfailure=\"alert(request.reponseText)\" />),
- button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot", :a => '10', :b => '20' })
- end
-
- test "button_to_remote with confirm" do
- assert_dom_equal %(<input class=\"fine\" type=\"button\" value=\"Remote outpost\" data-remote=\"true\" data-url=\"/whatnot\" data-confirm="Are you sure?" />),
- button_to_remote("Remote outpost", { :url => { :action => "whatnot" }, :confirm => "Are you sure?"}, { :class => "fine" })
- end
-
- test "button_to_remote with :submit" do
- assert_dom_equal %(<input class=\"fine\" type=\"button\" value=\"Remote outpost\" data-remote=\"true\" data-url=\"/whatnot\" data-submit="myForm" />),
- button_to_remote("Remote outpost", { :url => { :action => "whatnot" }, :submit => "myForm"}, { :class => "fine" })
- end
-
- test "periodically_call_remote" do
- expected = "<script data-url='/mehr_bier' data-update-success='schremser_bier' type='application/json' data-frequency='10' data-periodical='true'></script>"
- actual = periodically_call_remote(:update => "schremser_bier", :url => { :action => "mehr_bier" })
- assert_dom_equal expected, actual
- end
-
- test "periodically_call_remote_with_frequency" do
- expected = "<script data-periodical='true' type='application/json' data-frequency='2'></script>"
- actual = periodically_call_remote(:frequency => 2)
- assert_dom_equal expected, actual
- end
-
- test "periodically_call_remote_with_function" do
- expected = "<script data-periodical=\"true\" type=\"application/json\" data-onobserve=\"alert('test')\" data-frequency='2'></script>"
- actual = periodically_call_remote(:frequency => 2, :function => "alert('test')")
- assert_dom_equal expected, actual
- end
-
- test "periodically_call_remote_with_update" do
- actual = periodically_call_remote(:url => { :action => 'get_averages' }, :update => 'avg')
- expected = "<script data-periodical='true' data-url='/get_averages' type='application/json' data-update-success='avg' data-frequency='10'></script>"
- assert_dom_equal expected, actual
- end
-
- test "periodically_call_remote with update success and failure" do
- actual = periodically_call_remote(:url => { :action => 'invoice', :id => 1 },:update => { :success => "invoice", :failure => "error" })
- expected = "<script data-periodical='true' data-url='/invoice/1' type='application/json' data-update-success='invoice' data-frequency='10' data-update-failure='error'></script>"
- assert_dom_equal expected, actual
- end
-
- test "periodically_call_remote with frequency and update" do
- actual = periodically_call_remote(:url => 'update', :frequency => '20', :update => 'news_block')
- expected = "<script data-periodical='true' data-url='update' type='application/json' data-update-success='news_block' data-frequency='20'></script>"
- assert_dom_equal expected, actual
- end
-
- test "form_remote_tag" do
- assert_dom_equal %(<form action=\"/fast\" method=\"post\" data-remote=\"true\" data-update-success=\"glass_of_beer\">),
- form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast } )
- assert_dom_equal %(<form action=\"/fast\" method=\"post\" data-remote=\"true\" data-update-success=\"glass_of_beer\">),
- form_remote_tag(:update => { :success => "glass_of_beer" }, :url => { :action => :fast })
- assert_dom_equal %(<form action=\"/fast\" method=\"post\" data-remote=\"true\" data-update-failure=\"glass_of_water\">),
- form_remote_tag(:update => { :failure => "glass_of_water" }, :url => { :action => :fast })
- assert_dom_equal %(<form action=\"/fast\" method=\"post\" data-remote=\"true\" data-update-success=\"glass_of_beer\" data-update-failure=\"glass_of_water\">),
- form_remote_tag(:update => { :success => 'glass_of_beer', :failure => "glass_of_water" }, :url => { :action => :fast })
- end
-
- test "form_remote_tag with method" do
- assert_dom_equal %(<form action=\"/fast\" method=\"post\" data-remote=\"true\" data-update-success=\"glass_of_beer\"><div style='margin:0;padding:0;display:inline'><input name='_method' type='hidden' value='put' /></div>),
- form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :html => { :method => :put })
- end
-
- test "form_remote_tag with url" do
- form_remote_tag(:url => '/posts' ){}
- expected = "<form action='/posts' method='post' data-remote='true'></form>"
- assert_dom_equal expected, output_buffer
- end
-
- test "form_remote_tag with block in erb" do
- __in_erb_template = ''
- form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) { concat "Hello world!" }
- assert_dom_equal %(<form action=\"/fast\" method=\"post\" data-remote=\"true\" data-update-success=\"glass_of_beer\">Hello world!</form>), output_buffer
- end
-
- test "remote_form_for with record identification with new record" do
- remote_form_for(@record, {:html => { :id => 'create-author' }}) {}
- expected = %(<form action='#{authors_path}' data-remote=\"true\" class='new_author' id='create-author' method='post'></form>)
- assert_dom_equal expected, output_buffer
- end
-
- test "remote_form_for with url" do
- remote_form_for(@record, {:html => { :id => 'create-author' }}) {}
- expected = "<form action='/authors' data-remote='true' class='new_author' id='create-author' method='post'></form>"
- assert_dom_equal expected, output_buffer
- end
-
- test "remote_form_for with record identification without html options" do
- remote_form_for(@record) {}
- expected = %(<form action='#{authors_path}' data-remote=\"true\" class='new_author' method='post' id='new_author'></form>)
- assert_dom_equal expected, output_buffer
- end
-
- test "remote_form_for with record identification with existing record" do
- @record.save
- remote_form_for(@record) {}
-
- expected = %(<form action='#{author_path(@record)}' id='edit_author_1' method='post' data-remote=\"true\" class='edit_author'><div style='margin:0;padding:0;display:inline'><input name='_method' type='hidden' value='put' /></div></form>)
- assert_dom_equal expected, output_buffer
- end
-
- test "remote_form_for with new nested object and an excisting parent" do
- @author.save
- remote_form_for([@author, @article]) {}
-
- expected = %(<form action='#{author_articles_path(@author)}' data-remote=\"true\" class='new_article' method='post' id='new_article'></form>)
- assert_dom_equal expected, output_buffer
- end
-
- test "remote_form_for with existing object in list" do
- @author.save
- @article.save
-
- remote_form_for([@author, @article]) {}
-
- expected = %(<form action='#{author_article_path(@author, @article)}' id='edit_article_#{@article.id}' method='post' data-remote=\"true\" class='edit_article'><div style='margin:0;padding:0;display:inline'><input name='_method' type='hidden' value='put' /></div></form>)
- assert_dom_equal expected, output_buffer
- end
-
- test "on callbacks" do
- callbacks = [:uninitialized, :loading, :loaded, :interactive, :complete, :success, :failure]
- callbacks.each do |callback|
- assert_dom_equal %(<form action=\"/fast\" method=\"post\" data-remote=\"true\" data-update-success=\"glass_of_beer\" data-on#{callback}=\"monkeys();\">),
- form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();")
- assert_dom_equal %(<form action=\"/fast\" method=\"post\" data-remote=\"true\" data-update-success=\"glass_of_beer\" data-on#{callback}=\"monkeys();\">),
- form_remote_tag(:update => { :success => "glass_of_beer" }, :url => { :action => :fast }, callback=>"monkeys();")
- assert_dom_equal %(<form action=\"/fast\" method=\"post\" data-remote=\"true\" data-update-failure=\"glass_of_beer\" data-on#{callback}=\"monkeys();\">),
- form_remote_tag(:update => { :failure => "glass_of_beer" }, :url => { :action => :fast }, callback=>"monkeys();")
- assert_dom_equal %(<form action=\"/fast\" method=\"post\" data-remote=\"true\" data-update-failure=\"glass_of_water\" data-update-success=\"glass_of_beer\" data-on#{callback}=\"monkeys();\">),
- form_remote_tag(:update => { :success => "glass_of_beer", :failure => "glass_of_water" }, :url => { :action => :fast }, callback=>"monkeys();")
- end
-
- #HTTP status codes 200 up to 599 have callbacks
- #these should work
- 100.upto(599) do |callback|
- assert_dom_equal %(<form action=\"/fast\" method=\"post\" data-remote=\"true\" data-update-success=\"glass_of_beer\" data-on#{callback}=\"monkeys();\">),
- form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();")
- end
-
- #test 200 and 404
- assert_dom_equal %(<form action=\"/fast\" method=\"post\" data-remote=\"true\" data-update-success=\"glass_of_beer\" data-on200=\"monkeys();\" data-on404=\"bananas();\">),
- form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, 200=>"monkeys();", 404=>"bananas();")
-
- #these shouldn't
- 1.upto(99) do |callback|
- assert_dom_equal %(<form action=\"/fast\" method=\"post\" data-remote=\"true\" data-update-success=\"glass_of_beer\" data-on#{callback}=\"monkeys();\">),
- form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();")
- end
- 600.upto(999) do |callback|
- assert_dom_equal %(<form action=\"/fast\" method=\"post\" data-remote=\"true\" data-update-success=\"glass_of_beer\" data-on#{callback}=\"monkeys();\">),
- form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();")
- end
-
- #test ultimate combo
- assert_dom_equal %(<form data-on404=\"bananas();\" method=\"post\" data-onsuccess=\"s()\" action=\"/fast\" data-oncomplete=\"c();\" data-update-success=\"glass_of_beer\" data-on200=\"monkeys();\" data-onloading=\"c1()\" data-remote=\"true\" data-onfailure=\"f();\">),
- form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :loading => "c1()", :success => "s()", :failure => "f();", :complete => "c();", 200=>"monkeys();", 404=>"bananas();")
-
- end
-
- test "submit_to_remote" do
- assert_dom_equal %(<input name=\"More beer!\" type=\"button\" value=\"1000000\" data-url=\"/empty_bottle\" data-remote-submit=\"true\" data-update-success=\"empty_bottle\" />),
- submit_to_remote("More beer!", 1_000_000, :url => { :action => 'empty_bottle' }, :update => "empty_bottle")
- end
-
- test "submit_to_remote simple" do
- expected = "<input name='create_btn' type='button' value='Create' data-remote-submit='true' data-url='/create' />"
- actual = submit_to_remote 'create_btn', 'Create', :url => { :action => 'create' }
- assert_dom_equal expected, actual
- end
-
- test "submit_to_remote with success and failure" do
- expected = "<input name='update_btn' data-url='/update' data-remote-submit='true' data-update-failure='fail' data-update-success='succeed' value='Update' type='button' />"
- actual = submit_to_remote 'update_btn', 'Update', :url => { :action => 'update' }, :update => { :success => "succeed", :failure => "fail" }
- assert_dom_equal expected, actual
- end
-
- test "observe_field" do
- assert_dom_equal %(<script type=\"text/javascript\" data-observe=\"true\" data-observed=\"glass\" data-frequency=\"300\" type=\"application/json\" data-url=\"/reorder_if_empty\"></script>),
- observe_field("glass", :frequency => 5.minutes, :url => { :action => "reorder_if_empty" })
- end
-
- test "observe_field with url, frequency, update and with" do
- actual = observe_field :suggest, :url => { :action => :find_suggestion }, :frequency => 0.25, :update => :suggest, :with => 'q'
- expected = "<script type='text/javascript' data-observe='true' data-observed='suggest' data-frequency='0.25' type='application/json' data-url='/find_suggestion' data-update-success='suggest' data-with='q'></script>"
- assert_dom_equal actual, expected
- end
-
- test "observe_field default frequency" do
- actual = observe_field :suggest
- expected = "<script type='text/javascript' data-observe='true' data-observed='suggest' data-frequency='10' type='application/json'></script>"
- assert_dom_equal actual, expected
- end
-
- test "observe_field using with option" do
- expected = %(<script type=\"text/javascript\" data-observe=\"true\" data-observed=\"glass\" data-frequency=\"300\" type=\"application/json\" data-url=\"/check_value\" data-with=\"id=123\"></script>)
- assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => 'id=123')
- end
-
- test "observe_field using condition option" do
- expected = %(<script type=\"text/javascript\" data-observe=\"true\" data-observed=\"glass\" data-frequency=\"300\" type=\"application/json\" data-url=\"/check_value\" data-condition=\"$('foo').val() == true\"></script>)
- assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :condition => '$(\'foo\').val() == true')
- end
-
- test "observe_field using json in with option" do
- expected = %(<script data-with=\"{'id':value}\" data-observed=\"glass\" data-url=\"/check_value\" data-observe=\"true\" type=\"application/json\" data-frequency=\"300\"></script>)
- assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "{'id':value}")
- end
-
- test "observe_field using function for callback" do
- assert_dom_equal %(<script data-observed=\"glass\" data-observe=\"true\" type=\"application/json\" data-onobserve=\"alert('Element changed')\" data-frequency=\"300\"></script>),
- observe_field("glass", :frequency => 5.minutes, :function => "alert('Element changed')")
- end
-
- test "observe_form" do
- assert_dom_equal %(<script data-observed=\"cart\" data-url=\"/cart_changed\" data-observe=\"true\" type=\"application/json\" data-frequency=\"2\"></script>),
- observe_form("cart", :frequency => 2, :url => { :action => "cart_changed" })
- end
-
- test "observe_form using function for callback" do
- assert_dom_equal %(<script data-observed=\"cart\" data-observe=\"true\" type=\"application/json\" data-onobserve=\"alert('Form changed')\" data-frequency=\"2\"></script>),
- observe_form("cart", :frequency => 2, :function => "alert('Form changed')")
- end
-
- test "observe_field without frequency" do
- assert_dom_equal %(<script data-observed=\"glass\" data-observe=\"true\" type=\"application/json\" data-frequency='10'></script>),
- observe_field("glass")
- end
-
- protected
- def author_path(record)
- "/authors/#{record.id}"
- end
-
- def authors_path
- "/authors"
- end
-
- def author_articles_path(author)
- "/authors/#{author.id}/articles"
- end
-
- def author_article_path(author, article)
- "/authors/#{author.id}/articles/#{article.id}"
- end
-end
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index 57802ebf42..586de66714 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -86,11 +86,11 @@ class AssetTagHelperTest < ActionView::TestCase
%(javascript_include_tag("bank.js")) => %(<script src="/javascripts/bank.js" type="text/javascript"></script>),
%(javascript_include_tag("bank", :lang => "vbscript")) => %(<script lang="vbscript" src="/javascripts/bank.js" type="text/javascript"></script>),
%(javascript_include_tag("common.javascript", "/elsewhere/cools")) => %(<script src="/javascripts/common.javascript" type="text/javascript"></script>\n<script src="/elsewhere/cools.js" type="text/javascript"></script>),
- %(javascript_include_tag(:defaults)) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>),
+ %(javascript_include_tag(:defaults)) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/rails.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>),
%(javascript_include_tag(:all)) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>),
%(javascript_include_tag(:all, :recursive => true)) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/subdir/subdir.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>),
- %(javascript_include_tag(:defaults, "bank")) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>),
- %(javascript_include_tag("bank", :defaults)) => %(<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>),
+ %(javascript_include_tag(:defaults, "bank")) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/rails.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>),
+ %(javascript_include_tag("bank", :defaults)) => %(<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/rails.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>),
%(javascript_include_tag("http://example.com/all")) => %(<script src="http://example.com/all" type="text/javascript"></script>),
%(javascript_include_tag("http://example.com/all.js")) => %(<script src="http://example.com/all.js" type="text/javascript"></script>),
@@ -235,7 +235,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_javascript_include_tag_with_given_asset_id
ENV["RAILS_ASSET_ID"] = "1"
- assert_dom_equal(%(<script src="/javascripts/prototype.js?1" type="text/javascript"></script>\n<script src="/javascripts/effects.js?1" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js?1" type="text/javascript"></script>\n<script src="/javascripts/controls.js?1" type="text/javascript"></script>\n<script src="/javascripts/application.js?1" type="text/javascript"></script>), javascript_include_tag(:defaults))
+ assert_dom_equal(%(<script src="/javascripts/prototype.js?1" type="text/javascript"></script>\n<script src="/javascripts/effects.js?1" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js?1" type="text/javascript"></script>\n<script src="/javascripts/controls.js?1" type="text/javascript"></script>\n<script src="/javascripts/rails.js?1" type="text/javascript"></script>\n<script src="/javascripts/application.js?1" type="text/javascript"></script>), javascript_include_tag(:defaults))
end
def test_javascript_include_tag_is_html_safe
@@ -246,14 +246,14 @@ class AssetTagHelperTest < ActionView::TestCase
def test_register_javascript_include_default
ENV["RAILS_ASSET_ID"] = ""
ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'bank'
- assert_dom_equal %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>), javascript_include_tag(:defaults)
+ assert_dom_equal %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/rails.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>), javascript_include_tag(:defaults)
end
def test_register_javascript_include_default_mixed_defaults
ENV["RAILS_ASSET_ID"] = ""
ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'bank'
ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'robber', '/elsewhere/cools.js'
- assert_dom_equal %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/elsewhere/cools.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>), javascript_include_tag(:defaults)
+ assert_dom_equal %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/rails.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/elsewhere/cools.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>), javascript_include_tag(:defaults)
end
def test_custom_javascript_expansions
@@ -265,7 +265,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_custom_javascript_expansions_and_defaults_puts_application_js_at_the_end
ENV["RAILS_ASSET_ID"] = ""
ActionView::Helpers::AssetTagHelper::register_javascript_expansion :robbery => ["bank", "robber"]
- assert_dom_equal %(<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>), javascript_include_tag('controls',:defaults, :robbery, 'effects')
+ assert_dom_equal %(<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/rails.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>), javascript_include_tag('controls',:defaults, :robbery, 'effects')
end
def test_custom_javascript_expansions_with_undefined_symbol
@@ -965,6 +965,5 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
def test_assert_css_and_js_of_the_same_name_return_correct_extension
assert_dom_equal(%(/collaboration/hieraki/javascripts/foo.js), javascript_path("foo"))
assert_dom_equal(%(/collaboration/hieraki/stylesheets/foo.css), stylesheet_path("foo"))
-
end
end
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 147d3dc05d..c97343fbe5 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -1230,26 +1230,6 @@ class FormHelperTest < ActionView::TestCase
end
- # Perhaps this test should be moved to prototype helper tests.
- def test_remote_form_for_with_labelled_builder
- self.extend ActionView::Helpers::AjaxHelper
-
- remote_form_for(:post, @post, :builder => LabelledFormBuilder) do |f|
- concat f.text_field(:title)
- concat f.text_area(:body)
- concat f.check_box(:secret)
- end
-
- expected =
- %(<form action="http://www.example.com" data-remote="true" method="post">) +
- "<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" +
- "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea><br/>" +
- "<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' /><br/>" +
- "</form>"
-
- assert_dom_equal expected, output_buffer
- end
-
def test_fields_for_with_labelled_builder
fields_for(:post, @post, :builder => LabelledFormBuilder) do |f|
concat f.text_field(:title)
@@ -1396,15 +1376,6 @@ class FormHelperTest < ActionView::TestCase
assert_equal expected, output_buffer
end
- def test_remote_form_for_with_html_options_adds_options_to_form_tag
- self.extend ActionView::Helpers::AjaxHelper
-
- remote_form_for(:post, @post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end
- expected = "<form action=\"http://www.example.com\" class=\"some_class\" id=\"some_form\" method=\"post\" data-remote=\"true\"></form>"
-
- assert_dom_equal expected, output_buffer
- end
-
protected
def comments_path(post)
"/posts/#{post.id}/comments"
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index c8d929cee8..01bde8ea04 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -285,8 +285,8 @@ class FormTagHelperTest < ActionView::TestCase
def test_submit_tag
assert_dom_equal(
- %(<input name='commit' data-disable-with="Saving..." onclick="alert('hello!');" type="submit" value="Save" />),
- submit_tag("Save", :disable_with => "Saving...", :onclick => "alert('hello!');")
+ %(<input name='commit' data-disable-with="Saving..." onclick="alert('hello!')" type="submit" value="Save" />),
+ submit_tag("Save", :disable_with => "Saving...", :onclick => "alert('hello!')")
)
end
@@ -299,21 +299,21 @@ class FormTagHelperTest < ActionView::TestCase
def test_submit_tag_with_confirmation
assert_dom_equal(
- %(<input name='commit' type='submit' value='Save' data-confirm="Are you sure?"/>),
+ %(<input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />),
submit_tag("Save", :confirm => "Are you sure?")
)
end
def test_submit_tag_with_confirmation_and_with_disable_with
assert_dom_equal(
- %(<input name="commit" data-confirm="Are you sure?" data-disable-with="Saving..." type="submit" value="Save" />),
+ %(<input name="commit" data-disable-with="Saving..." data-confirm="Are you sure?" type="submit" value="Save" />),
submit_tag("Save", :disable_with => "Saving...", :confirm => "Are you sure?")
)
end
def test_image_submit_tag_with_confirmation
assert_dom_equal(
- %(<input type="image" src="/images/save.gif" data-confirm="Are you sure?"/>),
+ %(<input type="image" src="/images/save.gif" data-confirm="Are you sure?" />),
image_submit_tag("save.gif", :confirm => "Are you sure?")
)
end
diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb
index 03caad3d46..b3e7abc387 100644
--- a/actionpack/test/template/javascript_helper_test.rb
+++ b/actionpack/test/template/javascript_helper_test.rb
@@ -30,35 +30,6 @@ class JavaScriptHelperTest < ActionView::TestCase
assert_equal %(dont <\\/close> tags), escape_javascript(%(dont </close> tags))
end
- def test_link_to_function
- assert_dom_equal %(<a href="#" onclick="alert('Hello world!'); return false;">Greeting</a>),
- link_to_function("Greeting", "alert('Hello world!')")
- end
-
- def test_link_to_function_with_existing_onclick
- assert_dom_equal %(<a href="#" onclick="confirm('Sanity!'); alert('Hello world!'); return false;">Greeting</a>),
- link_to_function("Greeting", "alert('Hello world!')", :onclick => "confirm('Sanity!')")
- end
-
- def test_link_to_function_with_rjs_block
- html = link_to_function( "Greet me!" ) do |page|
- page.replace_html 'header', "<h1>Greetings</h1>"
- end
- assert_dom_equal %(<a href="#" onclick="Element.update(&quot;header&quot;, &quot;\\u003Ch1\\u003EGreetings\\u003C/h1\\u003E&quot;);; return false;">Greet me!</a>), html
- end
-
- def test_link_to_function_with_rjs_block_and_options
- html = link_to_function( "Greet me!", :class => "updater" ) do |page|
- page.replace_html 'header', "<h1>Greetings</h1>"
- end
- assert_dom_equal %(<a href="#" class="updater" onclick="Element.update(&quot;header&quot;, &quot;\\u003Ch1\\u003EGreetings\\u003C/h1\\u003E&quot;);; return false;">Greet me!</a>), html
- end
-
- def test_link_to_function_with_href
- assert_dom_equal %(<a href="http://example.com/" onclick="alert('Hello world!'); return false;">Greeting</a>),
- link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/')
- end
-
def test_button_to_function
assert_dom_equal %(<input type="button" onclick="alert('Hello world!');" value="Greeting" />),
button_to_function("Greeting", "alert('Hello world!')")
diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb
index 86f9c231c0..d95bdc2b90 100644
--- a/actionpack/test/template/prototype_helper_test.rb
+++ b/actionpack/test/template/prototype_helper_test.rb
@@ -101,7 +101,6 @@ class PrototypeHelperTest < PrototypeHelperBaseTest
assert_equal javascript_tag(create_generator(&block).to_s, {:defer => 'true'}), update_page_tag({:defer => 'true'}, &block)
end
-
protected
def author_path(record)
"/authors/#{record.id}"
@@ -466,4 +465,3 @@ return value.reverse();
assert_equal "MyObject.myMethod(function() { $(\"one\").show();\n$(\"two\").hide(); });", @generator.to_s
end
end
-
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index c0e6826ec5..b498ec8429 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -174,81 +174,58 @@ class UrlHelperTest < ActionView::TestCase
link_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
)
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"You can\'t possibly be sure, can you?\">Hello</a>",
+ "<a href=\"http://www.example.com\" data-confirm=\"You can't possibly be sure, can you?\">Hello</a>",
link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure, can you?")
)
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"You can\'t possibly be sure,\n can you?\">Hello</a>",
+ "<a href=\"http://www.example.com\" data-confirm=\"You can't possibly be sure,\n can you?\">Hello</a>",
link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure,\n can you?")
)
end
- def test_link_tag_with_popup
+ def test_link_to_with_remote
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-popup=\"true\">Hello</a>",
- link_to("Hello", "http://www.example.com", :popup => true)
- )
- assert_dom_equal(
- "<a href=\"http://www.example.com\" data-popup=\"true\">Hello</a>",
- link_to("Hello", "http://www.example.com", :popup => 'true')
- )
- assert_dom_equal(
- "<a href=\"http://www.example.com\" data-popup=\"{title: 'window_name', options: 'width=300,height=300'}\">Hello</a>",
- link_to("Hello", "http://www.example.com", :popup => ['window_name', 'width=300,height=300'])
- )
- end
-
- def test_link_tag_with_popup_and_javascript_confirm
- assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"Fo\' sho\'?\" data-popup=\"true\">Hello</a>",
- link_to("Hello", "http://www.example.com", { :popup => true, :confirm => "Fo' sho'?" })
- )
- assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"Are you serious?\" data-popup=\"{title: 'window_name', options: 'width=300,height=300'}\">Hello</a>",
- link_to("Hello", "http://www.example.com", { :popup => ['window_name', 'width=300,height=300'], :confirm => "Are you serious?" })
+ "<a href=\"http://www.example.com\" data-remote=\"true\">Hello</a>",
+ link_to("Hello", "http://www.example.com", :remote => true)
)
end
def test_link_tag_using_post_javascript
assert_dom_equal(
- "<a href='http://www.example.com' data-url='http://www.example.com' data-method=\"post\">Hello</a>",
+ "<a href='http://www.example.com' data-method=\"post\" rel=\"nofollow\">Hello</a>",
link_to("Hello", "http://www.example.com", :method => :post)
)
end
def test_link_tag_using_delete_javascript
assert_dom_equal(
- "<a href='http://www.example.com' data-url='http://www.example.com' rel=\"nofollow\" data-method=\"delete\">Destroy</a>",
+ "<a href='http://www.example.com' rel=\"nofollow\" data-method=\"delete\">Destroy</a>",
link_to("Destroy", "http://www.example.com", :method => :delete)
)
end
def test_link_tag_using_delete_javascript_and_href
assert_dom_equal(
- "<a href='\#' data-url='http://www.example.com' rel=\"nofollow\" data-method=\"delete\">Destroy</a>",
+ "<a href='\#' rel=\"nofollow\" data-method=\"delete\">Destroy</a>",
link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#')
)
end
def test_link_tag_using_post_javascript_and_confirm
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-url='http://www.example.com' data-method=\"post\" data-confirm=\"Are you serious?\">Hello</a>",
+ "<a href=\"http://www.example.com\" data-method=\"post\" rel=\"nofollow\" data-confirm=\"Are you serious?\">Hello</a>",
link_to("Hello", "http://www.example.com", :method => :post, :confirm => "Are you serious?")
)
end
def test_link_tag_using_delete_javascript_and_href_and_confirm
assert_dom_equal(
- "<a href=\"#\" data-url='http://www.example.com' rel=\"nofollow\" data-method=\"delete\" data-confirm=\"Are you serious?\">Destroy</a>",
+ "<a href='\#' rel=\"nofollow\" data-confirm=\"Are you serious?\" data-method=\"delete\">Destroy</a>",
link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#', :confirm => "Are you serious?"),
"When specifying url, form should be generated with it, but not this.href"
)
end
- def test_link_tag_using_post_javascript_and_popup
- assert_raise(ActionView::ActionViewError) { link_to("Hello", "http://www.example.com", :popup => true, :method => :post, :confirm => "Are you serious?") }
- end
-
def test_link_tag_using_block_in_erb
__in_erb_template = ''
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index 6eab00c177..1609075f7e 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -43,7 +43,6 @@ module ActiveModel
autoload :Observer, 'active_model/observing'
autoload :Observing
autoload :Serialization
- autoload :StateMachine
autoload :TestCase
autoload :Translation
autoload :VERSION
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 76e6ad93a7..f1d74db5f3 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -242,28 +242,35 @@ module ActiveModel
# <li><tt>activemodel.errors.models.admin.blank</tt></li>
# <li><tt>activemodel.errors.models.user.attributes.title.blank</tt></li>
# <li><tt>activemodel.errors.models.user.blank</tt></li>
- # <li><tt>activemodel.errors.messages.blank</tt></li>
# <li>any default you provided through the +options+ hash (in the activemodel.errors scope)</li>
+ # <li><tt>activemodel.errors.messages.blank</tt></li>
+ # <li><tt>errors.attributes.title.blank</tt></li>
+ # <li><tt>errors.messages.blank</tt></li>
# </ol>
def generate_message(attribute, message = :invalid, options = {})
message, options[:default] = options[:default], message if options[:default].is_a?(Symbol)
defaults = @base.class.lookup_ancestors.map do |klass|
- [ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}",
- :"models.#{klass.name.underscore}.#{message}" ]
+ [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.attributes.#{attribute}.#{message}",
+ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.#{message}" ]
end
defaults << options.delete(:default)
- defaults = defaults.compact.flatten << :"messages.#{message}"
+ defaults << :"#{@base.class.i18n_scope}.errors.messages.#{message}"
+ defaults << :"errors.attributes.#{attribute}.#{message}"
+ defaults << :"errors.messages.#{message}"
+
+ defaults.compact!
+ defaults.flatten!
key = defaults.shift
value = @base.send(:read_attribute_for_validation, attribute)
- options = { :default => defaults,
+ options = {
+ :default => defaults,
:model => @base.class.model_name.human,
:attribute => @base.class.human_attribute_name(attribute),
- :value => value,
- :scope => [:errors]
+ :value => value
}.merge(options)
I18n.translate(key, options)
diff --git a/activemodel/lib/active_model/state_machine.rb b/activemodel/lib/active_model/state_machine.rb
deleted file mode 100644
index 64b91c1659..0000000000
--- a/activemodel/lib/active_model/state_machine.rb
+++ /dev/null
@@ -1,219 +0,0 @@
-module ActiveModel
-
- # ActiveModel::StateMachine provides methods that turn your object into a
- # finite state machine, able to move from one state to another.
- #
- # A minimal implementation could be:
- #
- # class EmailMessage
- # include ActiveModel::StateMachine
- #
- # state_machine do
- # state :unread
- # state :read
- # end
- #
- # event :open_email do
- # transitions :to => :read, :from => :unread
- # end
- # end
- #
- # === Examples
- #
- # class TrafficLight
- # include ActiveModel::StateMachine
- #
- # attr_reader :runners_caught
- #
- # def initialize
- # @runners_caught = 0
- # end
- #
- # state_machine do
- # state :red
- # state :green
- # state :yellow
- # state :blink
- #
- # event :change_color do
- # transitions :to => :red, :from => [:yellow],
- # :on_transition => :catch_runners
- # transitions :to => :green, :from => [:red]
- # transitions :to => :yellow, :from => [:green]
- # end
- #
- # event :defect do
- # transitions :to => :blink, :from => [:yellow, :red, :green]
- # end
- #
- # event :repair do
- # transitions :to => :red, :from => [:blink]
- # end
- # end
- #
- # def catch_runners
- # @runners_caught += 1
- # end
- # end
- #
- # light = TrafficLight.new
- # light.current_state # => :red
- # light.change_color! # => true
- # light.current_state # => :green
- # light.green? # => true
- # light.change_color! # => true
- # light.current_state # => :yellow
- # light.red? # => false
- # light.change_color! # => true
- # light.runners_caught # => 1
- #
- # * The initial state for TrafficLight is red which is the first state defined.
- #
- # TrafficLight.state_machine.initial_state # => :red
- #
- # * Call an event to transition a state machine, e.g. <tt>change_color!</tt>.
- # You can call the event with or without the exclamation mark, however, the common Ruby
- # idiom is to name methods that directly change the state of the receivier with
- # an exclamation mark, so <tt>change_color!</tt> is preferred over <tt>change_color</tt>.
- #
- # light.current_state #=> :green
- # light.change_color! #=> true
- # light.current_state #=> :yellow
- #
- # * On a succesful transition to red (from yellow), the local +catch_runners+
- # method is executed
- #
- # light.current_state #=> :red
- # light.change_color! #=> true
- # light.runners_caught #=> 1
- #
- # * The object acts differently depending on its current state, for instance,
- # the change_color! method has a different action depending on the current
- # color of the light
- #
- # light.change_color! #=> true
- # light.current_state #=> :red
- # light.change_color! #=> true
- # light.current_state #=> :green
- #
- # * Get the possible events for a state
- #
- # TrafficLight.state_machine.events_for(:red) # => [:change_color, :defect]
- # TrafficLight.state_machine.events_for(:blink) # => [:repair]
- #
- # The StateMachine also supports the following features :
- #
- # * Success callbacks on event transition
- #
- # event :sample, :success => :we_win do
- # ...
- # end
- #
- # * Enter and exit callbacks par state
- #
- # state :open, :enter => [:alert_twitter, :send_emails], :exit => :alert_twitter
- #
- # * Guards on transition
- #
- # event :close do
- # # You may only close the store if the safe is locked!!
- # transitions :to => :closed, :from => :open, :guard => :safe_locked?
- # end
- #
- # * Setting the initial state
- #
- # state_machine :initial => :yellow do
- # ...
- # end
- #
- # * Named the state machine, to have more than one
- #
- # class Stated
- # include ActiveModel::StateMachine
- #
- # strate_machine :name => :ontest do
- # end
- #
- # state_machine do
- # end
- # end
- #
- # # Get the state of the <tt>:ontest</tt> state machine
- # stat.current_state(:ontest)
- # # Get the initial state
- # Stated.state_machine(:ontest).initial_state
- #
- # * Changing the state
- #
- # stat.current_state(:default, :astate) # => :astate
- # # But you must give the name of the state machine, here <tt>:default</tt>
- #
- module StateMachine
- autoload :Event, 'active_model/state_machine/event'
- autoload :Machine, 'active_model/state_machine/machine'
- autoload :State, 'active_model/state_machine/state'
- autoload :StateTransition, 'active_model/state_machine/state_transition'
-
- extend ActiveSupport::Concern
-
- class InvalidTransition < Exception
- end
-
- module ClassMethods
- def inherited(klass)
- super
- klass.state_machines = state_machines
- end
-
- def state_machines
- @state_machines ||= {}
- end
-
- def state_machines=(value)
- @state_machines = value ? value.dup : nil
- end
-
- def state_machine(name = nil, options = {}, &block)
- if name.is_a?(Hash)
- options = name
- name = nil
- end
- name ||= :default
- state_machines[name] ||= Machine.new(self, name)
- block ? state_machines[name].update(options, &block) : state_machines[name]
- end
-
- def define_state_query_method(state_name)
- name = "#{state_name}?"
- undef_method(name) if method_defined?(name)
- class_eval "def #{name}; current_state.to_s == %(#{state_name}) end"
- end
- end
-
- def current_state(name = nil, new_state = nil, persist = false)
- sm = self.class.state_machine(name)
- ivar = sm.current_state_variable
- if name && new_state
- if persist && respond_to?(:write_state)
- write_state(sm, new_state)
- end
-
- if respond_to?(:write_state_without_persistence)
- write_state_without_persistence(sm, new_state)
- end
-
- instance_variable_set(ivar, new_state)
- else
- instance_variable_set(ivar, nil) unless instance_variable_defined?(ivar)
- value = instance_variable_get(ivar)
- return value if value
-
- if respond_to?(:read_state)
- value = instance_variable_set(ivar, read_state(sm))
- end
-
- value || sm.initial_state
- end
- end
- end
-end
diff --git a/activemodel/lib/active_model/state_machine/event.rb b/activemodel/lib/active_model/state_machine/event.rb
deleted file mode 100644
index 30e9601dc2..0000000000
--- a/activemodel/lib/active_model/state_machine/event.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-module ActiveModel
- module StateMachine
- class Event
- attr_reader :name, :success
-
- def initialize(machine, name, options = {}, &block)
- @machine, @name, @transitions = machine, name, []
- if machine
- machine.klass.send(:define_method, "#{name}!") do |*args|
- machine.fire_event(name, self, true, *args)
- end
-
- machine.klass.send(:define_method, name.to_s) do |*args|
- machine.fire_event(name, self, false, *args)
- end
- end
- update(options, &block)
- end
-
- def fire(obj, to_state = nil, *args)
- transitions = @transitions.select { |t| t.from == obj.current_state(@machine ? @machine.name : nil) }
- raise InvalidTransition if transitions.size == 0
-
- next_state = nil
- transitions.each do |transition|
- next if to_state && !Array(transition.to).include?(to_state)
- if transition.perform(obj)
- next_state = to_state || Array(transition.to).first
- transition.execute(obj, *args)
- break
- end
- end
- next_state
- end
-
- def transitions_from_state?(state)
- @transitions.any? { |t| t.from? state }
- end
-
- def ==(event)
- if event.is_a? Symbol
- name == event
- else
- name == event.name
- end
- end
-
- def update(options = {}, &block)
- if options.key?(:success) then @success = options[:success] end
- if block then instance_eval(&block) end
- self
- end
-
- private
- def transitions(trans_opts)
- Array(trans_opts[:from]).each do |s|
- @transitions << StateTransition.new(trans_opts.merge({:from => s.to_sym}))
- end
- end
- end
- end
-end
diff --git a/activemodel/lib/active_model/state_machine/machine.rb b/activemodel/lib/active_model/state_machine/machine.rb
deleted file mode 100644
index 777531213e..0000000000
--- a/activemodel/lib/active_model/state_machine/machine.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-module ActiveModel
- module StateMachine
- class Machine
- attr_writer :initial_state
- attr_accessor :states, :events, :state_index
- attr_reader :klass, :name
-
- def initialize(klass, name, options = {}, &block)
- @klass, @name, @states, @state_index, @events = klass, name, [], {}, {}
- update(options, &block)
- end
-
- def initial_state
- @initial_state ||= (states.first ? states.first.name : nil)
- end
-
- def update(options = {}, &block)
- if options.key?(:initial) then @initial_state = options[:initial] end
- if block then instance_eval(&block) end
- self
- end
-
- def fire_event(event, record, persist, *args)
- state_index[record.current_state(@name)].call_action(:exit, record)
- if new_state = @events[event].fire(record, *args)
- state_index[new_state].call_action(:enter, record)
-
- if record.respond_to?(event_fired_callback)
- record.send(event_fired_callback, record.current_state, new_state)
- end
-
- record.current_state(@name, new_state, persist)
- record.send(@events[event].success) if @events[event].success
- true
- else
- if record.respond_to?(event_failed_callback)
- record.send(event_failed_callback, event)
- end
-
- false
- end
- end
-
- def states_for_select
- states.map { |st| [st.display_name, st.name.to_s] }
- end
-
- def events_for(state)
- events = @events.values.select { |event| event.transitions_from_state?(state) }
- events.map! { |event| event.name }
- end
-
- def current_state_variable
- "@#{@name}_current_state"
- end
-
- private
- def state(name, options = {})
- @states << (state_index[name] ||= State.new(name, :machine => self)).update(options)
- end
-
- def event(name, options = {}, &block)
- (@events[name] ||= Event.new(self, name)).update(options, &block)
- end
-
- def event_fired_callback
- @event_fired_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_fired'
- end
-
- def event_failed_callback
- @event_failed_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_failed'
- end
- end
- end
-end
diff --git a/activemodel/lib/active_model/state_machine/state.rb b/activemodel/lib/active_model/state_machine/state.rb
deleted file mode 100644
index 76916b1d86..0000000000
--- a/activemodel/lib/active_model/state_machine/state.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-module ActiveModel
- module StateMachine
- class State
- attr_reader :name, :options
-
- def initialize(name, options = {})
- @name = name
- if machine = options.delete(:machine)
- machine.klass.define_state_query_method(name)
- end
- update(options)
- end
-
- def ==(state)
- if state.is_a? Symbol
- name == state
- else
- name == state.name
- end
- end
-
- def call_action(action, record)
- action = @options[action]
- case action
- when Symbol, String
- record.send(action)
- when Proc
- action.call(record)
- end
- end
-
- def display_name
- @display_name ||= name.to_s.gsub(/_/, ' ').capitalize
- end
-
- def for_select
- [display_name, name.to_s]
- end
-
- def update(options = {})
- if options.key?(:display) then @display_name = options.delete(:display) end
- @options = options
- self
- end
- end
- end
-end
diff --git a/activemodel/lib/active_model/state_machine/state_transition.rb b/activemodel/lib/active_model/state_machine/state_transition.rb
deleted file mode 100644
index b0c5504de7..0000000000
--- a/activemodel/lib/active_model/state_machine/state_transition.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-module ActiveModel
- module StateMachine
- class StateTransition
- attr_reader :from, :to, :options
-
- def initialize(opts)
- @from, @to, @guard, @on_transition = opts[:from], opts[:to], opts[:guard], opts[:on_transition]
- @options = opts
- end
-
- def perform(obj)
- case @guard
- when Symbol, String
- obj.send(@guard)
- when Proc
- @guard.call(obj)
- else
- true
- end
- end
-
- def execute(obj, *args)
- case @on_transition
- when Symbol, String
- obj.send(@on_transition, *args)
- when Proc
- @on_transition.call(obj, *args)
- end
- end
-
- def ==(obj)
- @from == obj.from && @to == obj.to
- end
-
- def from?(value)
- @from == value
- end
- end
- end
-end
diff --git a/activemodel/test/cases/state_machine/event_test.rb b/activemodel/test/cases/state_machine/event_test.rb
deleted file mode 100644
index 2a0ef53a3f..0000000000
--- a/activemodel/test/cases/state_machine/event_test.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-require 'cases/helper'
-
-class EventTest < ActiveModel::TestCase
- def setup
- @state_name = :close_order
- @success = :success_callback
- end
-
- def new_event
- @event = ActiveModel::StateMachine::Event.new(nil, @state_name, {:success => @success}) do
- transitions :to => :closed, :from => [:open, :received]
- end
- end
-
- test 'should set the name' do
- assert_equal @state_name, new_event.name
- end
-
- test 'should set the success option' do
- assert_equal @success, new_event.success
- end
-
- test 'should create StateTransitions' do
- ActiveModel::StateMachine::StateTransition.expects(:new).with(:to => :closed, :from => :open)
- ActiveModel::StateMachine::StateTransition.expects(:new).with(:to => :closed, :from => :received)
- new_event
- end
-end
-
-class EventBeingFiredTest < ActiveModel::TestCase
- test 'should raise an AASM::InvalidTransition error if the transitions are empty' do
- event = ActiveModel::StateMachine::Event.new(nil, :event)
-
- assert_raise ActiveModel::StateMachine::InvalidTransition do
- event.fire(nil)
- end
- end
-
- test 'should return the state of the first matching transition it finds' do
- event = ActiveModel::StateMachine::Event.new(nil, :event) do
- transitions :to => :closed, :from => [:open, :received]
- end
-
- obj = stub
- obj.stubs(:current_state).returns(:open)
-
- assert_equal :closed, event.fire(obj)
- end
-end
diff --git a/activemodel/test/cases/state_machine/machine_test.rb b/activemodel/test/cases/state_machine/machine_test.rb
deleted file mode 100644
index 2aa954d80c..0000000000
--- a/activemodel/test/cases/state_machine/machine_test.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-require 'cases/helper'
-
-class MachineTestSubject
- include ActiveModel::StateMachine
-
- state_machine do
- state :open
- state :closed
- end
-
- state_machine :initial => :foo do
- event :shutdown do
- transitions :from => :open, :to => :closed
- end
-
- event :timeout do
- transitions :from => :open, :to => :closed
- end
- end
-
- state_machine :extra, :initial => :bar do
- end
-end
-
-class StateMachineMachineTest < ActiveModel::TestCase
- test "allows reuse of existing machines" do
- assert_equal 2, MachineTestSubject.state_machines.size
- end
-
- test "sets #initial_state from :initial option" do
- assert_equal :bar, MachineTestSubject.state_machine(:extra).initial_state
- end
-
- test "accesses non-default state machine" do
- assert_kind_of ActiveModel::StateMachine::Machine, MachineTestSubject.state_machine(:extra)
- end
-
- test "finds events for given state" do
- events = MachineTestSubject.state_machine.events_for(:open)
- assert events.include?(:shutdown)
- assert events.include?(:timeout)
- end
-end
diff --git a/activemodel/test/cases/state_machine/state_test.rb b/activemodel/test/cases/state_machine/state_test.rb
deleted file mode 100644
index 527bfd4c04..0000000000
--- a/activemodel/test/cases/state_machine/state_test.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-require 'cases/helper'
-
-class StateTestSubject
- include ActiveModel::StateMachine
-
- state_machine do
- end
-end
-
-class StateTest < ActiveModel::TestCase
- def setup
- @state_name = :astate
- @machine = StateTestSubject.state_machine
- @options = { :crazy_custom_key => 'key', :machine => @machine }
- end
-
- def new_state(options={})
- ActiveModel::StateMachine::State.new(@state_name, @options.merge(options))
- end
-
- test 'sets the name' do
- assert_equal :astate, new_state.name
- end
-
- test 'sets the display_name from name' do
- assert_equal "Astate", new_state.display_name
- end
-
- test 'sets the display_name from options' do
- assert_equal "A State", new_state(:display => "A State").display_name
- end
-
- test 'sets the options and expose them as options' do
- @options.delete(:machine)
- assert_equal @options, new_state.options
- end
-
- test 'equals a symbol of the same name' do
- assert_equal new_state, :astate
- end
-
- test 'equals a State of the same name' do
- assert_equal new_state, new_state
- end
-
- test 'should send a message to the record for an action if the action is present as a symbol' do
- state = new_state(:entering => :foo)
-
- record = stub
- record.expects(:foo)
-
- state.call_action(:entering, record)
- end
-
- test 'should send a message to the record for an action if the action is present as a string' do
- state = new_state(:entering => 'foo')
-
- record = stub
- record.expects(:foo)
-
- state.call_action(:entering, record)
- end
-
- test 'should call a proc, passing in the record for an action if the action is present' do
- state = new_state(:entering => Proc.new {|r| r.foobar})
-
- record = stub
- record.expects(:foobar)
-
- state.call_action(:entering, record)
- end
-end
diff --git a/activemodel/test/cases/state_machine/state_transition_test.rb b/activemodel/test/cases/state_machine/state_transition_test.rb
deleted file mode 100644
index 17f9d88be7..0000000000
--- a/activemodel/test/cases/state_machine/state_transition_test.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-require 'cases/helper'
-
-class StateTransitionTest < ActiveModel::TestCase
- test 'should set from, to, and opts attr readers' do
- opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
- st = ActiveModel::StateMachine::StateTransition.new(opts)
-
- assert_equal opts[:from], st.from
- assert_equal opts[:to], st.to
- assert_equal opts, st.options
- end
-
- test 'should pass equality check if from and to are the same' do
- opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
- st = ActiveModel::StateMachine::StateTransition.new(opts)
-
- obj = stub
- obj.stubs(:from).returns(opts[:from])
- obj.stubs(:to).returns(opts[:to])
-
- assert_equal st, obj
- end
-
- test 'should fail equality check if from are not the same' do
- opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
- st = ActiveModel::StateMachine::StateTransition.new(opts)
-
- obj = stub
- obj.stubs(:from).returns('blah')
- obj.stubs(:to).returns(opts[:to])
-
- assert_not_equal st, obj
- end
-
- test 'should fail equality check if to are not the same' do
- opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
- st = ActiveModel::StateMachine::StateTransition.new(opts)
-
- obj = stub
- obj.stubs(:from).returns(opts[:from])
- obj.stubs(:to).returns('blah')
-
- assert_not_equal st, obj
- end
-end
-
-class StateTransitionGuardCheckTest < ActiveModel::TestCase
- test 'should return true of there is no guard' do
- opts = {:from => 'foo', :to => 'bar'}
- st = ActiveModel::StateMachine::StateTransition.new(opts)
-
- assert st.perform(nil)
- end
-
- test 'should call the method on the object if guard is a symbol' do
- opts = {:from => 'foo', :to => 'bar', :guard => :test_guard}
- st = ActiveModel::StateMachine::StateTransition.new(opts)
-
- obj = stub
- obj.expects(:test_guard)
-
- st.perform(obj)
- end
-
- test 'should call the method on the object if guard is a string' do
- opts = {:from => 'foo', :to => 'bar', :guard => 'test_guard'}
- st = ActiveModel::StateMachine::StateTransition.new(opts)
-
- obj = stub
- obj.expects(:test_guard)
-
- st.perform(obj)
- end
-
- test 'should call the proc passing the object if the guard is a proc' do
- opts = {:from => 'foo', :to => 'bar', :guard => Proc.new {|o| o.test_guard}}
- st = ActiveModel::StateMachine::StateTransition.new(opts)
-
- obj = stub
- obj.expects(:test_guard)
-
- st.perform(obj)
- end
-end
diff --git a/activemodel/test/cases/state_machine_test.rb b/activemodel/test/cases/state_machine_test.rb
deleted file mode 100644
index f66299741e..0000000000
--- a/activemodel/test/cases/state_machine_test.rb
+++ /dev/null
@@ -1,312 +0,0 @@
-require 'cases/helper'
-
-class StateMachineSubject
- include ActiveModel::StateMachine
-
- state_machine do
- state :open, :exit => :exit
- state :closed, :enter => :enter
-
- event :close, :success => :success_callback do
- transitions :to => :closed, :from => [:open]
- end
-
- event :null do
- transitions :to => :closed, :from => [:open], :guard => :always_false
- end
- end
-
- state_machine :bar do
- state :read
- state :ended
-
- event :foo do
- transitions :to => :ended, :from => [:read]
- end
- end
-
- def always_false
- false
- end
-
- def success_callback
- end
-
- def enter
- end
- def exit
- end
-end
-
-class StateMachineSubjectSubclass < StateMachineSubject
-end
-
-class StateMachineClassLevelTest < ActiveModel::TestCase
- test 'defines a class level #state_machine method on its including class' do
- assert StateMachineSubject.respond_to?(:state_machine)
- end
-
- test 'defines a class level #state_machines method on its including class' do
- assert StateMachineSubject.respond_to?(:state_machines)
- end
-
- test 'class level #state_machine returns machine instance' do
- assert_kind_of ActiveModel::StateMachine::Machine, StateMachineSubject.state_machine
- end
-
- test 'class level #state_machine returns machine instance with given name' do
- assert_kind_of ActiveModel::StateMachine::Machine, StateMachineSubject.state_machine(:default)
- end
-
- test 'class level #state_machines returns hash of machine instances' do
- assert_kind_of ActiveModel::StateMachine::Machine, StateMachineSubject.state_machines[:default]
- end
-
- test "should return a select friendly array of states in the form of [['Friendly name', 'state_name']]" do
- assert_equal [['Open', 'open'], ['Closed', 'closed']], StateMachineSubject.state_machine.states_for_select
- end
-end
-
-class StateMachineInstanceLevelTest < ActiveModel::TestCase
- def setup
- @foo = StateMachineSubject.new
- end
-
- test 'defines an accessor for the current state' do
- assert @foo.respond_to?(:current_state)
- end
-
- test 'defines a state querying instance method on including class' do
- assert @foo.respond_to?(:open?)
- end
-
- test 'defines an event! instance method' do
- assert @foo.respond_to?(:close!)
- end
-
- test 'defines an event instance method' do
- assert @foo.respond_to?(:close)
- end
-end
-
-class StateMachineInitialStatesTest < ActiveModel::TestCase
- def setup
- @foo = StateMachineSubject.new
- end
-
- test 'sets the initial state' do
- assert_equal :open, @foo.current_state
- end
-
- test '#open? should be initially true' do
- assert @foo.open?
- end
-
- test '#closed? should be initially false' do
- assert !@foo.closed?
- end
-
- test 'uses the first state defined if no initial state is given' do
- assert_equal :read, @foo.current_state(:bar)
- end
-end
-
-class StateMachineEventFiringWithPersistenceTest < ActiveModel::TestCase
- def setup
- @subj = StateMachineSubject.new
- end
-
- test 'updates the current state' do
- @subj.close!
-
- assert_equal :closed, @subj.current_state
- end
-
- test 'fires the Event' do
- @subj.class.state_machine.events[:close].expects(:fire).with(@subj)
- @subj.close!
- end
-
- test 'calls the success callback if one was provided' do
- @subj.expects(:success_callback)
- @subj.close!
- end
-
- test 'attempts to persist if write_state is defined' do
- def @subj.write_state
- end
-
- @subj.expects(:write_state)
- @subj.close!
- end
-end
-
-class StateMachineEventFiringWithoutPersistence < ActiveModel::TestCase
- test 'updates the current state' do
- subj = StateMachineSubject.new
- assert_equal :open, subj.current_state
- subj.close
- assert_equal :closed, subj.current_state
- end
-
- test 'fires the Event' do
- subj = StateMachineSubject.new
-
- StateMachineSubject.state_machine.events[:close].expects(:fire).with(subj)
- subj.close
- end
-
- test 'attempts to persist if write_state is defined' do
- subj = StateMachineSubject.new
-
- def subj.write_state
- end
-
- subj.expects(:write_state_without_persistence)
-
- subj.close
- end
-end
-
-class StateMachinePersistenceTest < ActiveModel::TestCase
- test 'reads the state if it has not been set and read_state is defined' do
- subj = StateMachineSubject.new
- def subj.read_state
- end
-
- subj.expects(:read_state).with(StateMachineSubject.state_machine)
-
- subj.current_state
- end
-end
-
-class StateMachineEventCallbacksTest < ActiveModel::TestCase
- test 'should call aasm_event_fired if defined and successful for bang fire' do
- subj = StateMachineSubject.new
- def subj.aasm_event_fired(from, to)
- end
-
- subj.expects(:event_fired)
-
- subj.close!
- end
-
- test 'should call aasm_event_fired if defined and successful for non-bang fire' do
- subj = StateMachineSubject.new
- def subj.aasm_event_fired(from, to)
- end
-
- subj.expects(:event_fired)
-
- subj.close
- end
-
- test 'should call aasm_event_failed if defined and transition failed for bang fire' do
- subj = StateMachineSubject.new
- def subj.event_failed(event)
- end
-
- subj.expects(:event_failed)
-
- subj.null!
- end
-
- test 'should call aasm_event_failed if defined and transition failed for non-bang fire' do
- subj = StateMachineSubject.new
- def subj.aasm_event_failed(event)
- end
-
- subj.expects(:event_failed)
-
- subj.null
- end
-end
-
-class StateMachineStateActionsTest < ActiveModel::TestCase
- test "calls enter when entering state" do
- subj = StateMachineSubject.new
- subj.expects(:enter)
- subj.close
- end
-
- test "calls exit when exiting state" do
- subj = StateMachineSubject.new
- subj.expects(:exit)
- subj.close
- end
-end
-
-class StateMachineInheritanceTest < ActiveModel::TestCase
- test "has the same states as its parent" do
- assert_equal StateMachineSubject.state_machine.states, StateMachineSubjectSubclass.state_machine.states
- end
-
- test "has the same events as its parent" do
- assert_equal StateMachineSubject.state_machine.events, StateMachineSubjectSubclass.state_machine.events
- end
-end
-
-class StateMachineSubject
- state_machine :chetan_patil, :initial => :sleeping do
- state :sleeping
- state :showering
- state :working
- state :dating
-
- event :wakeup do
- transitions :from => :sleeping, :to => [:showering, :working]
- end
-
- event :dress do
- transitions :from => :sleeping, :to => :working, :on_transition => :wear_clothes
- transitions :from => :showering, :to => [:working, :dating], :on_transition => Proc.new { |obj, *args| obj.wear_clothes(*args) }
- end
- end
-
- def wear_clothes(shirt_color, trouser_type)
- end
-end
-
-class StateMachineWithComplexTransitionsTest < ActiveModel::TestCase
- def setup
- @subj = StateMachineSubject.new
- end
-
- test 'transitions to specified next state (sleeping to showering)' do
- @subj.wakeup! :showering
-
- assert_equal :showering, @subj.current_state(:chetan_patil)
- end
-
- test 'transitions to specified next state (sleeping to working)' do
- @subj.wakeup! :working
-
- assert_equal :working, @subj.current_state(:chetan_patil)
- end
-
- test 'transitions to default (first or showering) state' do
- @subj.wakeup!
-
- assert_equal :showering, @subj.current_state(:chetan_patil)
- end
-
- test 'transitions to default state when on_transition invoked' do
- @subj.dress!(nil, 'purple', 'dressy')
-
- assert_equal :working, @subj.current_state(:chetan_patil)
- end
-
- test 'calls on_transition method with args' do
- @subj.wakeup! :showering
-
- @subj.expects(:wear_clothes).with('blue', 'jeans')
- @subj.dress! :working, 'blue', 'jeans'
- end
-
- test 'calls on_transition proc' do
- @subj.wakeup! :showering
-
- @subj.expects(:wear_clothes).with('purple', 'slacks')
- @subj.dress!(:dating, 'purple', 'slacks')
- end
-end
diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb
index 7d33fcea98..38844bb1fa 100644
--- a/activemodel/test/cases/validations/i18n_validation_test.rb
+++ b/activemodel/test/cases/validations/i18n_validation_test.rb
@@ -254,7 +254,7 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_confirmation_of w/o mocha
def test_validates_confirmation_of_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :errors => {:models => {:person => {:attributes => {:title => {:confirmation => 'custom message'}}}}}
+ I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:confirmation => 'custom message'}}}}}}
I18n.backend.store_translations 'en', :errors => {:messages => {:confirmation => 'global message'}}
Person.validates_confirmation_of :title
@@ -275,7 +275,7 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_acceptance_of w/o mocha
def test_validates_acceptance_of_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :errors => {:models => {:person => {:attributes => {:title => {:accepted => 'custom message'}}}}}
+ I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:accepted => 'custom message'}}}}}}
I18n.backend.store_translations 'en', :errors => {:messages => {:accepted => 'global message'}}
Person.validates_acceptance_of :title, :allow_nil => false
@@ -294,7 +294,7 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_presence_of w/o mocha
def test_validates_presence_of_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :errors => {:models => {:person => {:attributes => {:title => {:blank => 'custom message'}}}}}
+ I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:blank => 'custom message'}}}}}}
I18n.backend.store_translations 'en', :errors => {:messages => {:blank => 'global message'}}
Person.validates_presence_of :title
@@ -313,7 +313,7 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_length_of :within w/o mocha
def test_validates_length_of_within_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :errors => {:models => {:person => {:attributes => {:title => {:too_short => 'custom message'}}}}}
+ I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:too_short => 'custom message'}}}}}}
I18n.backend.store_translations 'en', :errors => {:messages => {:too_short => 'global message'}}
Person.validates_length_of :title, :within => 3..5
@@ -332,7 +332,7 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_length_of :is w/o mocha
def test_validates_length_of_is_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :errors => {:models => {:person => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}
+ I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}}
I18n.backend.store_translations 'en', :errors => {:messages => {:wrong_length => 'global message'}}
Person.validates_length_of :title, :is => 5
@@ -351,7 +351,7 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_format_of w/o mocha
def test_validates_format_of_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :errors => {:models => {:person => {:attributes => {:title => {:invalid => 'custom message'}}}}}
+ I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:invalid => 'custom message'}}}}}}
I18n.backend.store_translations 'en', :errors => {:messages => {:invalid => 'global message'}}
Person.validates_format_of :title, :with => /^[1-9][0-9]*$/
@@ -370,7 +370,7 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_inclusion_of w/o mocha
def test_validates_inclusion_of_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :errors => {:models => {:person => {:attributes => {:title => {:inclusion => 'custom message'}}}}}
+ I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:inclusion => 'custom message'}}}}}}
I18n.backend.store_translations 'en', :errors => {:messages => {:inclusion => 'global message'}}
Person.validates_inclusion_of :title, :in => %w(a b c)
@@ -389,7 +389,7 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_exclusion_of w/o mocha
def test_validates_exclusion_of_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :errors => {:models => {:person => {:attributes => {:title => {:exclusion => 'custom message'}}}}}
+ I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:exclusion => 'custom message'}}}}}}
I18n.backend.store_translations 'en', :errors => {:messages => {:exclusion => 'global message'}}
Person.validates_exclusion_of :title, :in => %w(a b c)
@@ -410,7 +410,7 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_numericality_of without :only_integer w/o mocha
def test_validates_numericality_of_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :errors => {:models => {:person => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}
+ I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}}
I18n.backend.store_translations 'en', :errors => {:messages => {:not_a_number => 'global message'}}
Person.validates_numericality_of :title
@@ -431,7 +431,7 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_numericality_of with :only_integer w/o mocha
def test_validates_numericality_of_only_integer_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :errors => {:models => {:person => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}
+ I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}}
I18n.backend.store_translations 'en', :errors => {:messages => {:not_a_number => 'global message'}}
Person.validates_numericality_of :title, :only_integer => true
@@ -452,7 +452,7 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_numericality_of :odd w/o mocha
def test_validates_numericality_of_odd_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :errors => {:models => {:person => {:attributes => {:title => {:odd => 'custom message'}}}}}
+ I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:odd => 'custom message'}}}}}}
I18n.backend.store_translations 'en', :errors => {:messages => {:odd => 'global message'}}
Person.validates_numericality_of :title, :only_integer => true, :odd => true
@@ -473,7 +473,7 @@ class I18nValidationTest < ActiveModel::TestCase
# validates_numericality_of :less_than w/o mocha
def test_validates_numericality_of_less_than_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :errors => {:models => {:person => {:attributes => {:title => {:less_than => 'custom message'}}}}}
+ I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:less_than => 'custom message'}}}}}}
I18n.backend.store_translations 'en', :errors => {:messages => {:less_than => 'global message'}}
Person.validates_numericality_of :title, :only_integer => true, :less_than => 0
@@ -502,7 +502,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_validates_with_message_symbol_must_translate_per_attribute
- I18n.backend.store_translations 'en', :errors => {:models => {:person => {:attributes => {:title => {:custom_error => "I am a custom error"}}}}}
+ I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:custom_error => "I am a custom error"}}}}}}
Person.validates_presence_of :title, :message => :custom_error
@person.title = nil
@person.valid?
@@ -510,7 +510,7 @@ class I18nValidationTest < ActiveModel::TestCase
end
def test_validates_with_message_symbol_must_translate_per_model
- I18n.backend.store_translations 'en', :errors => {:models => {:person => {:custom_error => "I am a custom error"}}}
+ I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:custom_error => "I am a custom error"}}}}
Person.validates_presence_of :title, :message => :custom_error
@person.title = nil
@person.valid?
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index cc0accf90e..0c4cc6a554 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -73,7 +73,6 @@ module ActiveRecord
autoload :SchemaDumper
autoload :Serialization
autoload :SessionStore
- autoload :StateMachine
autoload :Timestamp
autoload :Transactions
autoload :Validations
diff --git a/activerecord/lib/active_record/locale/en.yml b/activerecord/lib/active_record/locale/en.yml
index 4115cc8e17..810359fef3 100644
--- a/activerecord/lib/active_record/locale/en.yml
+++ b/activerecord/lib/active_record/locale/en.yml
@@ -1,9 +1,16 @@
en:
- errors:
- messages:
- taken: "has already been taken"
- record_invalid: "Validation failed: {{errors}}"
- # Append your own errors here or at the model/attributes scope.
+ # Attributes names common to most models
+ #attributes:
+ #created_at: "Created at"
+ #updated_at: "Updated at"
+
+ # ActiveRecord models configuration
+ activerecord:
+ errors:
+ messages:
+ taken: "has already been taken"
+ record_invalid: "Validation failed: {{errors}}"
+ # Append your own errors here or at the model/attributes scope.
# You can define own errors for models or model attributes.
# The values :model, :attribute and :value are always available for interpolation.
@@ -19,13 +26,6 @@ en:
# custom blank validation message for login attribute of User model.
#models:
- # Attributes names common to most models
- #attributes:
- #created_at: "Created at"
- #updated_at: "Updated at"
-
- # ActiveRecord models configuration
- #activerecord:
# Translate model names. Used in Model.human_name().
#models:
# For example,
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index dc80ac4b30..2b204043b4 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -50,7 +50,7 @@ module ActiveRecord
end
# Setup database middleware after initializers have run
- initializer "active_record.initialize_database_middleware" do |app|
+ initializer "active_record.initialize_database_middleware", :after => "action_controller.set_configs" do |app|
middleware = app.config.middleware
if middleware.include?("ActiveRecord::SessionStore")
middleware.insert_before "ActiveRecord::SessionStore", ActiveRecord::ConnectionAdapters::ConnectionManagement
@@ -77,17 +77,5 @@ module ActiveRecord
end
end
end
-
- initializer "active_record.i18n_deprecation" do
- require 'active_support/i18n'
-
- begin
- I18n.t(:"activerecord.errors", :raise => true)
- warn "[DEPRECATION] \"activerecord.errors\" namespace is deprecated in I18n " <<
- "yml files, please use just \"errors\" instead."
- rescue Exception => e
- # No message then.
- end
- end
end
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 9e855209f9..65e5c0495c 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -25,7 +25,7 @@ module ActiveRecord
case value
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope
values = value.to_a
- values.any? ? attribute.in(values) : attribute.eq(nil)
+ attribute.in(values)
when Range
# TODO : Arel should handle ranges with excluded end.
if value.exclude_end?
diff --git a/activerecord/lib/active_record/state_machine.rb b/activerecord/lib/active_record/state_machine.rb
deleted file mode 100644
index aebd03344a..0000000000
--- a/activerecord/lib/active_record/state_machine.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-module ActiveRecord
- module StateMachine #:nodoc:
- extend ActiveSupport::Concern
- include ActiveModel::StateMachine
-
- included do
- before_validation :set_initial_state
- validates_presence_of :state
- end
-
- protected
- def write_state(state_machine, state)
- update_attributes! :state => state.to_s
- end
-
- def read_state(state_machine)
- self.state.to_sym
- end
-
- def set_initial_state
- self.state ||= self.class.state_machine.initial_state.to_s
- end
- end
-end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index a9743aa1ea..8b266be638 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -10,8 +10,8 @@ module ActiveRecord
attr_reader :record
def initialize(record)
@record = record
- errors = @record.errors.full_messages.join(I18n.t('support.array.words_connector', :default => ', '))
- super(I18n.t('errors.messages.record_invalid', :errors => errors))
+ errors = @record.errors.full_messages.join(", ")
+ super(I18n.t("activerecord.errors.messages.record_invalid", :errors => errors))
end
end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index cc5460ddb7..2995cc6f72 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -1002,9 +1002,9 @@ module AutosaveAssociationOnACollectionAssociationTests
end
def test_should_default_invalid_error_from_i18n
- I18n.backend.store_translations(:en, :errors => { :models =>
+ I18n.backend.store_translations(:en, :activerecord => {:errors => { :models =>
{ @association_name.to_s.singularize.to_sym => { :blank => "cannot be blank" } }
- })
+ }})
@pirate.send(@association_name).build(:name => '')
diff --git a/activerecord/test/cases/state_machine_test.rb b/activerecord/test/cases/state_machine_test.rb
deleted file mode 100644
index 5d13668bab..0000000000
--- a/activerecord/test/cases/state_machine_test.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require 'cases/helper'
-require 'models/traffic_light'
-
-class StateMachineTest < ActiveRecord::TestCase
- def setup
- @light = TrafficLight.create!
- end
-
- test "states initial state" do
- assert @light.off?
- assert_equal :off, @light.current_state
- end
-
- test "transition to a valid state" do
- @light.reset
- assert @light.red?
- assert_equal :red, @light.current_state
-
- @light.green_on
- assert @light.green?
- assert_equal :green, @light.current_state
- end
-
- test "transition does not persist state" do
- @light.reset
- assert_equal :red, @light.current_state
- @light.reload
- assert_equal "off", @light.state
- end
-
- test "transition does persists state" do
- @light.reset!
- assert_equal :red, @light.current_state
- @light.reload
- assert_equal "red", @light.state
- end
-
- test "transition to an invalid state" do
- assert_raise(ActiveModel::StateMachine::InvalidTransition) { @light.yellow_on }
- assert_equal :off, @light.current_state
- end
-end
diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb
index 5dfbb1516f..a0ff35f948 100644
--- a/activerecord/test/cases/validations/i18n_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_validation_test.rb
@@ -50,8 +50,8 @@ class I18nValidationTest < ActiveRecord::TestCase
# validates_uniqueness_of w/o mocha
def test_validates_associated_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :errors => {:models => {:topic => {:attributes => {:title => {:taken => 'custom message'}}}}}
- I18n.backend.store_translations 'en', :errors => {:messages => {:taken => 'global message'}}
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:taken => 'custom message'}}}}}}
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:taken => 'global message'}}}
Topic.validates_uniqueness_of :title
unique_topic.valid?
@@ -59,7 +59,7 @@ class I18nValidationTest < ActiveRecord::TestCase
end
def test_validates_associated_finds_global_default_translation
- I18n.backend.store_translations 'en', :errors => {:messages => {:taken => 'global message'}}
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:taken => 'global message'}}}
Topic.validates_uniqueness_of :title
unique_topic.valid?
@@ -83,16 +83,16 @@ class I18nValidationTest < ActiveRecord::TestCase
# validates_associated w/o mocha
def test_validates_associated_finds_custom_model_key_translation
- I18n.backend.store_translations 'en', :errors => {:models => {:topic => {:attributes => {:replies => {:invalid => 'custom message'}}}}}
- I18n.backend.store_translations 'en', :errors => {:messages => {:invalid => 'global message'}}
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:replies => {:invalid => 'custom message'}}}}}}
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
Topic.validates_associated :replies
replied_topic.valid?
- assert_equal ['custom message'], replied_topic.errors[:replies]
+ assert_equal ['custom message'], replied_topic.errors[:replies].uniq
end
def test_validates_associated_finds_global_default_translation
- I18n.backend.store_translations 'en', :errors => {:messages => {:invalid => 'global message'}}
+ I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}}
Topic.validates_associated :replies
replied_topic.valid?
diff --git a/activerecord/test/models/traffic_light.rb b/activerecord/test/models/traffic_light.rb
deleted file mode 100644
index f8cfddbef9..0000000000
--- a/activerecord/test/models/traffic_light.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-class TrafficLight < ActiveRecord::Base
- include ActiveRecord::StateMachine
-
- state_machine do
- state :off
-
- state :red
- state :green
- state :yellow
-
- event :red_on do
- transitions :to => :red, :from => [:yellow]
- end
-
- event :green_on do
- transitions :to => :green, :from => [:red]
- end
-
- event :yellow_on do
- transitions :to => :yellow, :from => [:green]
- end
-
- event :reset do
- transitions :to => :red, :from => [:off]
- end
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
index 72e0eefb0a..19382abb76 100644
--- a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
+++ b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
@@ -1,67 +1,41 @@
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/object/metaclass'
class Class
- def superclass_delegating_reader(*names)
- class_to_stop_searching_on = superclass.name.blank? ? "Object" : superclass.name
- options = names.extract_options!
+ def superclass_delegating_accessor(name, options = {})
+ # Create private _name and _name= methods that can still be used if the public
+ # methods are overridden. This allows
+ _superclass_delegating_accessor("_#{name}")
- names.each do |name|
- # def self.only_reader
- # if defined?(@only_reader)
- # @only_reader
- # elsif superclass < Object && superclass.respond_to?(:only_reader)
- # superclass.only_reader
- # end
- # end
- class_eval <<-EOS, __FILE__, __LINE__ + 1
- def self.#{name}
- if defined?(@#{name})
- @#{name}
- elsif superclass < #{class_to_stop_searching_on} && superclass.respond_to?(:#{name})
- superclass.#{name}
- end
- end
- EOS
+ # Generate the public methods name, name=, and name?
+ # These methods dispatch to the private _name, and _name= methods, making them
+ # overridable
+ metaclass.send(:define_method, name) { send("_#{name}") }
+ metaclass.send(:define_method, "#{name}?") { !!send("_#{name}") }
+ metaclass.send(:define_method, "#{name}=") { |value| send("_#{name}=", value) }
- unless options[:instance_reader] == false
- class_eval <<-EOS, __FILE__, __LINE__ + 1
- def #{name} # def only_reader
- self.class.#{name} # self.class.only_reader
- end # end
- def self.#{name}? # def self.only_reader?
- !!#{name} # !!only_reader
- end # end
- def #{name}? # def only_reader?
- !!#{name} # !!only_reader
- end # end
- EOS
- end
- end
+ # If an instance_reader is needed, generate methods for name and name= on the
+ # class itself, so instances will be able to see them
+ define_method(name) { send("_#{name}") } if options[:instance_reader] != false
+ define_method("#{name}?") { !!send("#{name}") } if options[:instance_reader] != false
end
- def superclass_delegating_writer(*names, &block)
- options = names.extract_options!
+private
- names.each do |name|
- class_eval <<-EOS, __FILE__, __LINE__ + 1
- def self.#{name}=(value) # def self.property=(value)
- @#{name} = value # @property = value
- end # end
- EOS
+ # Take the object being set and store it in a method. This gives us automatic
+ # inheritance behavior, without having to store the object in an instance
+ # variable and look up the superclass chain manually.
+ def _stash_object_in_method(object, method, instance_reader = true)
+ metaclass.send(:define_method, method) { object }
+ define_method(method) { object } if instance_reader
+ end
- self.send(:"#{name}=", yield) if block_given?
+ def _superclass_delegating_accessor(name, options = {})
+ metaclass.send(:define_method, "#{name}=") do |value|
+ _stash_object_in_method(value, name, options[:instance_reader] != false)
end
+ self.send("#{name}=", nil)
end
- # These class attributes behave something like the class
- # inheritable accessors. But instead of copying the hash over at
- # the time the subclass is first defined, the accessors simply
- # delegate to their superclass unless they have been given a
- # specific value. This stops the strange situation where values
- # set after class definition don't get applied to subclasses.
- def superclass_delegating_accessor(*names, &block)
- superclass_delegating_reader(*names)
- superclass_delegating_writer(*names, &block)
- end
end
diff --git a/activesupport/test/core_ext/class/delegating_attributes_test.rb b/activesupport/test/core_ext/class/delegating_attributes_test.rb
index beb55ba17e..011068ab74 100644
--- a/activesupport/test/core_ext/class/delegating_attributes_test.rb
+++ b/activesupport/test/core_ext/class/delegating_attributes_test.rb
@@ -21,27 +21,6 @@ class DelegatingAttributesTest < Test::Unit::TestCase
@single_class = Class.new(Object)
end
- def test_simple_reader_declaration
- single_class.superclass_delegating_reader :only_reader
- # The class and instance should have an accessor, but there
- # should be no mutator
- assert single_class.respond_to?(:only_reader)
- assert single_class.respond_to?(:only_reader?)
- assert single_class.public_instance_methods.map(&:to_s).include?("only_reader")
- assert single_class.public_instance_methods.map(&:to_s).include?("only_reader?")
- assert !single_class.respond_to?(:only_reader=)
- end
-
- def test_simple_writer_declaration
- single_class.superclass_delegating_writer :only_writer
- # The class should have a mutator, the instances shouldn't
- # neither should have an accessor
- assert single_class.respond_to?(:only_writer=)
- assert !single_class.public_instance_methods.include?("only_writer=")
- assert !single_class.public_instance_methods.include?("only_writer")
- assert !single_class.respond_to?(:only_writer)
- end
-
def test_simple_accessor_declaration
single_class.superclass_delegating_accessor :both
# Class should have accessor and mutator
@@ -74,19 +53,6 @@ class DelegatingAttributesTest < Test::Unit::TestCase
assert_equal false, single_class.both?
end
- def test_working_with_accessors
- single_class.superclass_delegating_reader :only_reader
- single_class.instance_variable_set("@only_reader", "reading only")
- assert_equal "reading only", single_class.only_reader
- assert_equal "reading only", single_class.new.only_reader
- end
-
- def test_working_with_simple_mutators
- single_class.superclass_delegating_writer :only_writer
- single_class.only_writer="written"
- assert_equal "written", single_class.instance_variable_get("@only_writer")
- end
-
def test_child_class_delegates_to_parent_but_can_be_overridden
parent = Class.new
parent.superclass_delegating_accessor :both
diff --git a/railties/Rakefile b/railties/Rakefile
index eff59f1f40..b8f5e81b85 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -64,6 +64,10 @@ task :generate_guides do
ruby "guides/rails_guides.rb"
end
+task :update_prototype_ujs do
+ system "curl http://github.com/rails/prototype-ujs/raw/master/src/rails.js > lib/generators/rails/app/templates/public/javascripts/rails.js"
+end
+
# Generate documentation ------------------------------------------------------------------
Rake::RDocTask.new { |rdoc|
diff --git a/railties/lib/generators/rails/app/templates/public/javascripts/jquery-1.4.1.min.js b/railties/lib/generators/rails/app/templates/public/javascripts/jquery-1.4.1.min.js
deleted file mode 100644
index 0c7294c90a..0000000000
--- a/railties/lib/generators/rails/app/templates/public/javascripts/jquery-1.4.1.min.js
+++ /dev/null
@@ -1,152 +0,0 @@
-/*!
- * jQuery JavaScript Library v1.4.1
- * http://jquery.com/
- *
- * Copyright 2010, John Resig
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * Includes Sizzle.js
- * http://sizzlejs.com/
- * Copyright 2010, The Dojo Foundation
- * Released under the MIT, BSD, and GPL Licenses.
- *
- * Date: Mon Jan 25 19:43:33 2010 -0500
- */
-(function(z,v){function la(){if(!c.isReady){try{r.documentElement.doScroll("left")}catch(a){setTimeout(la,1);return}c.ready()}}function Ma(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,i){var j=a.length;if(typeof b==="object"){for(var n in b)X(a,n,b[n],f,e,d);return a}if(d!==v){f=!i&&f&&c.isFunction(d);for(n=0;n<j;n++)e(a[n],b,f?d.call(a[n],n,e(a[n],b)):d,i);return a}return j?
-e(a[0],b):null}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function ma(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function na(a){var b,d=[],f=[],e=arguments,i,j,n,o,m,s,x=c.extend({},c.data(this,"events").live);if(!(a.button&&a.type==="click")){for(o in x){j=x[o];if(j.live===a.type||j.altLive&&c.inArray(a.type,j.altLive)>-1){i=j.data;i.beforeFilter&&i.beforeFilter[a.type]&&!i.beforeFilter[a.type](a)||f.push(j.selector)}else delete x[o]}i=c(a.target).closest(f,
-a.currentTarget);m=0;for(s=i.length;m<s;m++)for(o in x){j=x[o];n=i[m].elem;f=null;if(i[m].selector===j.selector){if(j.live==="mouseenter"||j.live==="mouseleave")f=c(a.relatedTarget).closest(j.selector)[0];if(!f||f!==n)d.push({elem:n,fn:j})}}m=0;for(s=d.length;m<s;m++){i=d[m];a.currentTarget=i.elem;a.data=i.fn.data;if(i.fn.apply(i.elem,e)===false){b=false;break}}return b}}function oa(a,b){return"live."+(a?a+".":"")+b.replace(/\./g,"`").replace(/ /g,"&")}function pa(a){return!a||!a.parentNode||a.parentNode.nodeType===
-11}function qa(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var i in f)for(var j in f[i])c.event.add(this,i,f[i][j],f[i][j].data)}}})}function ra(a,b,d){var f,e,i;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&a[0].indexOf("<option")<0&&(c.support.checkClone||!sa.test(a[0]))){e=true;if(i=c.fragments[a[0]])if(i!==1)f=i}if(!f){b=b&&b[0]?b[0].ownerDocument||b[0]:r;f=b.createDocumentFragment();
-c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=i?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(ta.concat.apply([],ta.slice(0,b)),function(){d[this]=a});return d}function ua(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Na=z.jQuery,Oa=z.$,r=z.document,S,Pa=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Qa=/^.[^:#\[\.,]*$/,Ra=/\S/,Sa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Ta=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,O=navigator.userAgent,
-va=false,P=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,Q=Array.prototype.slice,wa=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(typeof a==="string")if((d=Pa.exec(a))&&(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:r;if(a=Ta.exec(a))if(c.isPlainObject(b)){a=[r.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=ra([d[1]],
-[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}}else{if(b=r.getElementById(d[2])){if(b.id!==d[2])return S.find(a);this.length=1;this[0]=b}this.context=r;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=r;a=r.getElementsByTagName(a)}else return!b||b.jquery?(b||S).find(a):c(b).find(a);else if(c.isFunction(a))return S.ready(a);if(a.selector!==v){this.selector=a.selector;this.context=a.context}return c.isArray(a)?this.setArray(a):c.makeArray(a,
-this)},selector:"",jquery:"1.4.1",length:0,size:function(){return this.length},toArray:function(){return Q.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){a=c(a||null);a.prevObject=this;a.context=this.context;if(b==="find")a.selector=this.selector+(this.selector?" ":"")+d;else if(b)a.selector=this.selector+"."+b+"("+d+")";return a},setArray:function(a){this.length=0;ba.apply(this,a);return this},each:function(a,b){return c.each(this,
-a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(r,c);else P&&P.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(Q.apply(this,arguments),"slice",Q.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};
-c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,i,j,n;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(i in e){j=a[i];n=e[i];if(a!==n)if(f&&n&&(c.isPlainObject(n)||c.isArray(n))){j=j&&(c.isPlainObject(j)||c.isArray(j))?j:c.isArray(n)?[]:{};a[i]=c.extend(f,j,n)}else if(n!==v)a[i]=n}return a};c.extend({noConflict:function(a){z.$=
-Oa;if(a)z.jQuery=Na;return c},isReady:false,ready:function(){if(!c.isReady){if(!r.body)return setTimeout(c.ready,13);c.isReady=true;if(P){for(var a,b=0;a=P[b++];)a.call(r,c);P=null}c.fn.triggerHandler&&c(r).triggerHandler("ready")}},bindReady:function(){if(!va){va=true;if(r.readyState==="complete")return c.ready();if(r.addEventListener){r.addEventListener("DOMContentLoaded",L,false);z.addEventListener("load",c.ready,false)}else if(r.attachEvent){r.attachEvent("onreadystatechange",L);z.attachEvent("onload",
-c.ready);var a=false;try{a=z.frameElement==null}catch(b){}r.documentElement.doScroll&&a&&la()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,"isPrototypeOf"))return false;var b;for(b in a);return b===v||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;
-return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return z.JSON&&z.JSON.parse?z.JSON.parse(a):(new Function("return "+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Ra.test(a)){var b=r.getElementsByTagName("head")[0]||
-r.documentElement,d=r.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(r.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,i=a.length,j=i===v||c.isFunction(a);if(d)if(j)for(f in a){if(b.apply(a[f],d)===false)break}else for(;e<i;){if(b.apply(a[e++],d)===false)break}else if(j)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=
-a[0];e<i&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Sa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==
-v;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,i=a.length;e<i;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,i=0,j=a.length;i<j;i++){e=b(a[i],i,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=v}else if(b&&!c.isFunction(b)){d=b;b=v}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},
-uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});O=c.uaMatch(O);if(O.browser){c.browser[O.browser]=true;c.browser.version=O.version}if(c.browser.webkit)c.browser.safari=true;if(wa)c.inArray=function(a,b){return wa.call(b,a)};S=c(r);if(r.addEventListener)L=function(){r.removeEventListener("DOMContentLoaded",
-L,false);c.ready()};else if(r.attachEvent)L=function(){if(r.readyState==="complete"){r.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=r.documentElement,b=r.createElement("script"),d=r.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var e=d.getElementsByTagName("*"),i=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!i)){c.support=
-{leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(i.getAttribute("style")),hrefNormalized:i.getAttribute("href")==="/a",opacity:/^0.55$/.test(i.style.opacity),cssFloat:!!i.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:r.createElement("select").appendChild(r.createElement("option")).selected,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};
-b.type="text/javascript";try{b.appendChild(r.createTextNode("window."+f+"=1;"))}catch(j){}a.insertBefore(b,a.firstChild);if(z[f]){c.support.scriptEval=true;delete z[f]}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function n(){c.support.noCloneEvent=false;d.detachEvent("onclick",n)});d.cloneNode(true).fireEvent("onclick")}d=r.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=r.createDocumentFragment();a.appendChild(d.firstChild);
-c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var n=r.createElement("div");n.style.width=n.style.paddingLeft="1px";r.body.appendChild(n);c.boxModel=c.support.boxModel=n.offsetWidth===2;r.body.removeChild(n).style.display="none"});a=function(n){var o=r.createElement("div");n="on"+n;var m=n in o;if(!m){o.setAttribute(n,"return;");m=typeof o[n]==="function"}return m};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=i=null}})();c.props=
-{"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ua=0,xa={},Va={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var f=a[G],e=c.cache;if(!b&&!f)return null;f||(f=++Ua);if(typeof b==="object"){a[G]=f;e=e[f]=c.extend(true,
-{},b)}else e=e[f]?e[f]:typeof d==="undefined"?Va:(e[f]={});if(d!==v){a[G]=f;e[b]=d}return typeof b==="string"?e[b]:e}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{try{delete a[G]}catch(i){a.removeAttribute&&a.removeAttribute(G)}delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,
-a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===v){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===v&&this.length)f=c.data(this[0],a);return f===v&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);
-return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===v)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||
-a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var ya=/[\n\t]/g,ca=/\s+/,Wa=/\r/g,Xa=/href|src|style/,Ya=/(button|input)/i,Za=/(button|input|object|select|textarea)/i,$a=/^(a|area)$/i,za=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(o){var m=
-c(this);m.addClass(a.call(this,o,m.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className)for(var i=" "+e.className+" ",j=0,n=b.length;j<n;j++){if(i.indexOf(" "+b[j]+" ")<0)e.className+=" "+b[j]}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(o){var m=c(this);m.removeClass(a.call(this,o,m.attr("class")))});if(a&&typeof a==="string"||a===v)for(var b=(a||"").split(ca),
-d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var i=(" "+e.className+" ").replace(ya," "),j=0,n=b.length;j<n;j++)i=i.replace(" "+b[j]+" "," ");e.className=i.substring(1,i.length-1)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var i=c(this);i.toggleClass(a.call(this,e,i.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,i=0,j=c(this),n=b,o=
-a.split(ca);e=o[i++];){n=f?n:!j.hasClass(e);j[n?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(ya," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===v){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||
-{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var i=b?d:0;for(d=b?d+1:e.length;i<d;i++){var j=e[i];if(j.selected){a=c(j).val();if(b)return a;f.push(a)}}return f}if(za.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Wa,"")}return v}var n=c.isFunction(a);return this.each(function(o){var m=c(this),s=a;if(this.nodeType===1){if(n)s=a.call(this,o,m.val());
-if(typeof s==="number")s+="";if(c.isArray(s)&&za.test(this.type))this.checked=c.inArray(m.val(),s)>=0;else if(c.nodeName(this,"select")){var x=c.makeArray(s);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),x)>=0});if(!x.length)this.selectedIndex=-1}else this.value=s}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return v;if(f&&b in c.attrFn)return c(a)[b](d);
-f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==v;b=f&&c.props[b]||b;if(a.nodeType===1){var i=Xa.test(b);if(b in a&&f&&!i){if(e){b==="type"&&Ya.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:Za.test(a.nodeName)||$a.test(a.nodeName)&&a.href?0:v;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=
-""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&i?a.getAttribute(b,2):a.getAttribute(b);return a===null?v:a}return c.style(a,b,d)}});var ab=function(a){return a.replace(/[^\w\s\.\|`]/g,function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==z&&!a.frameElement)a=z;if(!d.guid)d.guid=c.guid++;if(f!==v){d=c.proxy(d);d.data=f}var e=c.data(a,"events")||c.data(a,"events",{}),i=c.data(a,"handle"),j;if(!i){j=
-function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(j.elem,arguments):v};i=c.data(a,"handle",j)}if(i){i.elem=a;b=b.split(/\s+/);for(var n,o=0;n=b[o++];){var m=n.split(".");n=m.shift();if(o>1){d=c.proxy(d);if(f!==v)d.data=f}d.type=m.slice(0).sort().join(".");var s=e[n],x=this.special[n]||{};if(!s){s=e[n]={};if(!x.setup||x.setup.call(a,f,m,d)===false)if(a.addEventListener)a.addEventListener(n,i,false);else a.attachEvent&&a.attachEvent("on"+n,i)}if(x.add)if((m=x.add.call(a,
-d,f,m,s))&&c.isFunction(m)){m.guid=m.guid||d.guid;m.data=m.data||d.data;m.type=m.type||d.type;d=m}s[d.guid]=d;this.global[n]=true}a=null}}},global:{},remove:function(a,b,d){if(!(a.nodeType===3||a.nodeType===8)){var f=c.data(a,"events"),e,i,j;if(f){if(b===v||typeof b==="string"&&b.charAt(0)===".")for(i in f)this.remove(a,i+(b||""));else{if(b.type){d=b.handler;b=b.type}b=b.split(/\s+/);for(var n=0;i=b[n++];){var o=i.split(".");i=o.shift();var m=!o.length,s=c.map(o.slice(0).sort(),ab);s=new RegExp("(^|\\.)"+
-s.join("\\.(?:.*\\.)?")+"(\\.|$)");var x=this.special[i]||{};if(f[i]){if(d){j=f[i][d.guid];delete f[i][d.guid]}else for(var A in f[i])if(m||s.test(f[i][A].type))delete f[i][A];x.remove&&x.remove.call(a,o,j);for(e in f[i])break;if(!e){if(!x.teardown||x.teardown.call(a,o)===false)if(a.removeEventListener)a.removeEventListener(i,c.data(a,"handle"),false);else a.detachEvent&&a.detachEvent("on"+i,c.data(a,"handle"));e=null;delete f[i]}}}}for(e in f)break;if(!e){if(A=c.data(a,"handle"))A.elem=null;c.removeData(a,
-"events");c.removeData(a,"handle")}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();this.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return v;a.result=v;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,
-b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(i){}if(!a.isPropagationStopped()&&f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){d=a.target;var j;if(!(c.nodeName(d,"a")&&e==="click")&&!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()])){try{if(d[e]){if(j=d["on"+e])d["on"+e]=null;this.triggered=true;d[e]()}}catch(n){}if(j)d["on"+e]=j;this.triggered=false}}},handle:function(a){var b,
-d;a=arguments[0]=c.event.fix(a||z.event);a.currentTarget=this;d=a.type.split(".");a.type=d.shift();b=!d.length&&!a.exclusive;var f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)");d=(c.data(this,"events")||{})[a.type];for(var e in d){var i=d[e];if(b||f.test(i.type)){a.handler=i;a.data=i.data;i=i.apply(this,arguments);if(i!==v){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
-fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||r;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=r.documentElement;d=r.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
-d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==v)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a,b){c.extend(a,b||{});a.guid+=b.selector+b.live;b.liveProxy=a;c.event.add(this,b.live,na,b)},remove:function(a){if(a.length){var b=
-0,d=new RegExp("(^|\\.)"+a[0]+"(\\.|$)");c.each(c.data(this,"events").live||{},function(){d.test(this.type)&&b++});b<1&&c.event.remove(this,a[0],na)}},special:{}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};
-c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y};var Aa=function(a){for(var b=
-a.relatedTarget;b&&b!==this;)try{b=b.parentNode}catch(d){break}if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}},Ba=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ba:Aa,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ba:Aa)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(a,b,d){if(this.nodeName.toLowerCase()!==
-"form"){c.event.add(this,"click.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="submit"||i==="image")&&c(e).closest("form").length)return ma("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="text"||i==="password")&&c(e).closest("form").length&&f.keyCode===13)return ma("submit",this,arguments)})}else return false},remove:function(a,b){c.event.remove(this,"click.specialSubmit"+(b?"."+b.guid:""));c.event.remove(this,
-"keypress.specialSubmit"+(b?"."+b.guid:""))}};if(!c.support.changeBubbles){var da=/textarea|input|select/i;function Ca(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d}function ea(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Ca(d);if(a.type!=="focusout"||
-d.type!=="radio")c.data(d,"_change_data",e);if(!(f===v||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}}c.event.special.change={filters:{focusout:ea,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return ea.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return ea.call(this,a)},beforeactivate:function(a){a=
-a.target;a.nodeName.toLowerCase()==="input"&&a.type==="radio"&&c.data(a,"_change_data",Ca(a))}},setup:function(a,b,d){for(var f in T)c.event.add(this,f+".specialChange."+d.guid,T[f]);return da.test(this.nodeName)},remove:function(a,b){for(var d in T)c.event.remove(this,d+".specialChange"+(b?"."+b.guid:""),T[d]);return da.test(this.nodeName)}};var T=c.event.special.change.filters}r.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,
-f)}c.event.special[b]={setup:function(){this.addEventListener(a,d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var i in d)this[b](i,f,d[i],e);return this}if(c.isFunction(f)){e=f;f=v}var j=b==="one"?c.proxy(e,function(n){c(this).unbind(n,j);return e.apply(this,arguments)}):e;return d==="unload"&&b!=="one"?this.one(d,f,e):this.each(function(){c.event.add(this,d,j,f)})}});c.fn.extend({unbind:function(a,
-b){if(typeof a==="object"&&!a.preventDefault){for(var d in a)this.unbind(d,a[d]);return this}return this.each(function(){c.event.remove(this,a,b)})},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+
-a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e){var i,j=0;if(c.isFunction(f)){e=f;f=v}for(d=(d||"").split(/\s+/);(i=d[j++])!=null;){i=i==="focus"?"focusin":i==="blur"?"focusout":i==="hover"?d.push("mouseleave")&&"mouseenter":i;b==="live"?c(this.context).bind(oa(i,this.selector),{data:f,selector:this.selector,
-live:i},e):c(this.context).unbind(oa(i,this.selector),e?{guid:e.guid+this.selector+i}:null)}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});z.attachEvent&&!z.addEventListener&&z.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});
-(function(){function a(g){for(var h="",k,l=0;g[l];l++){k=g[l];if(k.nodeType===3||k.nodeType===4)h+=k.nodeValue;else if(k.nodeType!==8)h+=a(k.childNodes)}return h}function b(g,h,k,l,q,p){q=0;for(var u=l.length;q<u;q++){var t=l[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===k){y=l[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=k;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}l[q]=y}}}function d(g,h,k,l,q,p){q=0;for(var u=l.length;q<u;q++){var t=l[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===
-k){y=l[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=k;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(o.filter(h,[t]).length>0){y=t;break}}t=t[g]}l[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,i=Object.prototype.toString,j=false,n=true;[0,0].sort(function(){n=false;return 0});var o=function(g,h,k,l){k=k||[];var q=h=h||r;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||
-typeof g!=="string")return k;for(var p=[],u,t,y,R,H=true,M=w(h),I=g;(f.exec(""),u=f.exec(I))!==null;){I=u[3];p.push(u[1]);if(u[2]){R=u[3];break}}if(p.length>1&&s.exec(g))if(p.length===2&&m.relative[p[0]])t=fa(p[0]+p[1],h);else for(t=m.relative[p[0]]?[h]:o(p.shift(),h);p.length;){g=p.shift();if(m.relative[g])g+=p.shift();t=fa(g,t)}else{if(!l&&p.length>1&&h.nodeType===9&&!M&&m.match.ID.test(p[0])&&!m.match.ID.test(p[p.length-1])){u=o.find(p.shift(),h,M);h=u.expr?o.filter(u.expr,u.set)[0]:u.set[0]}if(h){u=
-l?{expr:p.pop(),set:A(l)}:o.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=u.expr?o.filter(u.expr,u.set):u.set;if(p.length>0)y=A(t);else H=false;for(;p.length;){var D=p.pop();u=D;if(m.relative[D])u=p.pop();else D="";if(u==null)u=h;m.relative[D](y,u,M)}}else y=[]}y||(y=t);y||o.error(D||g);if(i.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))k.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&
-y[g].nodeType===1&&k.push(t[g]);else k.push.apply(k,y);else A(y,k);if(R){o(R,q,k,l);o.uniqueSort(k)}return k};o.uniqueSort=function(g){if(C){j=n;g.sort(C);if(j)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};o.matches=function(g,h){return o(g,null,null,h)};o.find=function(g,h,k){var l,q;if(!g)return[];for(var p=0,u=m.order.length;p<u;p++){var t=m.order[p];if(q=m.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");l=m.find[t](q,
-h,k);if(l!=null){g=g.replace(m.match[t],"");break}}}}l||(l=h.getElementsByTagName("*"));return{set:l,expr:g}};o.filter=function(g,h,k,l){for(var q=g,p=[],u=h,t,y,R=h&&h[0]&&w(h[0]);g&&h.length;){for(var H in m.filter)if((t=m.leftMatch[H].exec(g))!=null&&t[2]){var M=m.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-1)!=="\\"){if(u===p)p=[];if(m.preFilter[H])if(t=m.preFilter[H](t,u,k,p,l,R)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=u[U])!=null;U++)if(D){I=M(D,t,U,u);var Da=
-l^!!I;if(k&&I!=null)if(Da)y=true;else u[U]=false;else if(Da){p.push(D);y=true}}if(I!==v){k||(u=p);g=g.replace(m.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)o.error(g);else break;q=g}return u};o.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var m=o.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
-TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},relative:{"+":function(g,h){var k=typeof h==="string",l=k&&!/\W/.test(h);k=k&&!l;if(l)h=h.toLowerCase();l=0;for(var q=g.length,
-p;l<q;l++)if(p=g[l]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[l]=k||p&&p.nodeName.toLowerCase()===h?p||false:p===h}k&&o.filter(h,g,true)},">":function(g,h){var k=typeof h==="string";if(k&&!/\W/.test(h)){h=h.toLowerCase();for(var l=0,q=g.length;l<q;l++){var p=g[l];if(p){k=p.parentNode;g[l]=k.nodeName.toLowerCase()===h?k:false}}}else{l=0;for(q=g.length;l<q;l++)if(p=g[l])g[l]=k?p.parentNode:p.parentNode===h;k&&o.filter(h,g,true)}},"":function(g,h,k){var l=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=
-h=h.toLowerCase();q=b}q("parentNode",h,l,g,p,k)},"~":function(g,h,k){var l=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,l,g,p,k)}},find:{ID:function(g,h,k){if(typeof h.getElementById!=="undefined"&&!k)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var k=[];h=h.getElementsByName(g[1]);for(var l=0,q=h.length;l<q;l++)h[l].getAttribute("name")===g[1]&&k.push(h[l]);return k.length===0?null:k}},
-TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,k,l,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var u;(u=h[p])!=null;p++)if(u)if(q^(u.className&&(" "+u.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))k||l.push(u);else if(k)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&
-"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,k,l,q,p){h=g[1].replace(/\\/g,"");if(!p&&m.attrMap[h])g[1]=m.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,k,l,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=o(g[3],null,null,h);else{g=o.filter(g[3],h,k,true^q);k||l.push.apply(l,g);return false}else if(m.match.POS.test(g[0])||m.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);
-return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,k){return!!o(k[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===
-g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,h){return h===0},last:function(g,h,k,l){return h===l.length-1},even:function(g,h){return h%2===
-0},odd:function(g,h){return h%2===1},lt:function(g,h,k){return h<k[3]-0},gt:function(g,h,k){return h>k[3]-0},nth:function(g,h,k){return k[3]-0===h},eq:function(g,h,k){return k[3]-0===h}},filter:{PSEUDO:function(g,h,k,l){var q=h[1],p=m.filters[q];if(p)return p(g,k,h,l);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=h[3];k=0;for(l=h.length;k<l;k++)if(h[k]===g)return false;return true}else o.error("Syntax error, unrecognized expression: "+
-q)},CHILD:function(g,h){var k=h[1],l=g;switch(k){case "only":case "first":for(;l=l.previousSibling;)if(l.nodeType===1)return false;if(k==="first")return true;l=g;case "last":for(;l=l.nextSibling;)if(l.nodeType===1)return false;return true;case "nth":k=h[2];var q=h[3];if(k===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var u=0;for(l=p.firstChild;l;l=l.nextSibling)if(l.nodeType===1)l.nodeIndex=++u;p.sizcache=h}g=g.nodeIndex-q;return k===0?g===0:g%k===0&&g/k>=
-0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var k=h[1];g=m.attrHandle[k]?m.attrHandle[k](g):g[k]!=null?g[k]:g.getAttribute(k);k=g+"";var l=h[2];h=h[4];return g==null?l==="!=":l==="="?k===h:l==="*="?k.indexOf(h)>=0:l==="~="?(" "+k+" ").indexOf(h)>=0:!h?k&&g!==false:l==="!="?k!==h:l==="^="?
-k.indexOf(h)===0:l==="$="?k.substr(k.length-h.length)===h:l==="|="?k===h||k.substr(0,h.length+1)===h+"-":false},POS:function(g,h,k,l){var q=m.setFilters[h[2]];if(q)return q(g,k,h,l)}}},s=m.match.POS;for(var x in m.match){m.match[x]=new RegExp(m.match[x].source+/(?![^\[]*\])(?![^\(]*\))/.source);m.leftMatch[x]=new RegExp(/(^(?:.|\r|\n)*?)/.source+m.match[x].source.replace(/\\(\d+)/g,function(g,h){return"\\"+(h-0+1)}))}var A=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};
-try{Array.prototype.slice.call(r.documentElement.childNodes,0)}catch(B){A=function(g,h){h=h||[];if(i.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var k=0,l=g.length;k<l;k++)h.push(g[k]);else for(k=0;g[k];k++)h.push(g[k]);return h}}var C;if(r.documentElement.compareDocumentPosition)C=function(g,h){if(!g.compareDocumentPosition||!h.compareDocumentPosition){if(g==h)j=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===
-h?0:1;if(g===0)j=true;return g};else if("sourceIndex"in r.documentElement)C=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)j=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)j=true;return g};else if(r.createRange)C=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)j=true;return g.ownerDocument?-1:1}var k=g.ownerDocument.createRange(),l=h.ownerDocument.createRange();k.setStart(g,0);k.setEnd(g,0);l.setStart(h,0);l.setEnd(h,0);g=k.compareBoundaryPoints(Range.START_TO_END,
-l);if(g===0)j=true;return g};(function(){var g=r.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var k=r.documentElement;k.insertBefore(g,k.firstChild);if(r.getElementById(h)){m.find.ID=function(l,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(l[1]))?q.id===l[1]||typeof q.getAttributeNode!=="undefined"&&q.getAttributeNode("id").nodeValue===l[1]?[q]:v:[]};m.filter.ID=function(l,q){var p=typeof l.getAttributeNode!=="undefined"&&l.getAttributeNode("id");
-return l.nodeType===1&&p&&p.nodeValue===q}}k.removeChild(g);k=g=null})();(function(){var g=r.createElement("div");g.appendChild(r.createComment(""));if(g.getElementsByTagName("*").length>0)m.find.TAG=function(h,k){k=k.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var l=0;k[l];l++)k[l].nodeType===1&&h.push(k[l]);k=h}return k};g.innerHTML="<a href='#'></a>";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")m.attrHandle.href=function(h){return h.getAttribute("href",
-2)};g=null})();r.querySelectorAll&&function(){var g=o,h=r.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){o=function(l,q,p,u){q=q||r;if(!u&&q.nodeType===9&&!w(q))try{return A(q.querySelectorAll(l),p)}catch(t){}return g(l,q,p,u)};for(var k in g)o[k]=g[k];h=null}}();(function(){var g=r.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===
-0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){m.order.splice(1,0,"CLASS");m.find.CLASS=function(h,k,l){if(typeof k.getElementsByClassName!=="undefined"&&!l)return k.getElementsByClassName(h[1])};g=null}}})();var E=r.compareDocumentPosition?function(g,h){return g.compareDocumentPosition(h)&16}:function(g,h){return g!==h&&(g.contains?g.contains(h):true)},w=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},fa=function(g,h){var k=[],
-l="",q;for(h=h.nodeType?[h]:h;q=m.match.PSEUDO.exec(g);){l+=q[0];g=g.replace(m.match.PSEUDO,"")}g=m.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)o(g,h[q],k);return o.filter(l,k)};c.find=o;c.expr=o.selectors;c.expr[":"]=c.expr.filters;c.unique=o.uniqueSort;c.getText=a;c.isXMLDoc=w;c.contains=E})();var bb=/Until$/,cb=/^(?:parents|prevUntil|prevAll)/,db=/,/;Q=Array.prototype.slice;var Ea=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,i){return!!b.call(e,i,e)===d});else if(b.nodeType)return c.grep(a,
-function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Qa.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;c.find(a,this[f],b);if(f>0)for(var i=d;i<b.length;i++)for(var j=0;j<d;j++)if(b[j]===b[i]){b.splice(i--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=
-0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ea(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ea(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,i={},j;if(f&&a.length){e=0;for(var n=a.length;e<n;e++){j=a[e];i[j]||(i[j]=c.expr.match.POS.test(j)?c(j,b||this.context):j)}for(;f&&f.ownerDocument&&f!==b;){for(j in i){e=i[j];if(e.jquery?e.index(f)>
--1:c(f).is(e)){d.push({selector:j,elem:f});delete i[j]}}f=f.parentNode}}return d}var o=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(m,s){for(;s&&s.ownerDocument&&s!==b;){if(o?o.index(s)>-1:c(s).is(a))return s;s=s.parentNode}return null})},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),
-a);return this.pushStack(pa(a[0])||pa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},
-nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);bb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):
-e;if((this.length>1||db.test(f))&&cb.test(a))e=e.reverse();return this.pushStack(e,a,Q.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===v||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==
-b&&d.push(a);return d}});var Fa=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ga=/(<([\w:]+)[^>]*?)\/>/g,eb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,Ha=/<([\w:]+)/,fb=/<tbody/i,gb=/<|&\w+;/,sa=/checked\s*(?:[^=]|=\s*.checked.)/i,Ia=function(a,b,d){return eb.test(d)?a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],
-col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==v)return this.empty().append((this[0]&&this[0].ownerDocument||r).createTextNode(a));return c.getText(this)},
-wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?
-d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,
-false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&
-!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Fa,"").replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){qa(this,b);qa(this.find("*"),b.find("*"))}return b},html:function(a){if(a===v)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Fa,""):null;else if(typeof a==="string"&&!/<script/i.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(Ha.exec(a)||
-["",""])[1].toLowerCase()]){a=a.replace(Ga,Ia);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var i=c(this),j=i.html();i.empty().append(function(){return a.call(this,e,j)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,
-b,f))});else a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(s){return c.nodeName(s,"table")?s.getElementsByTagName("tbody")[0]||s.appendChild(s.ownerDocument.createElement("tbody")):s}var e,i,j=a[0],n=[];if(!c.support.checkClone&&arguments.length===3&&typeof j===
-"string"&&sa.test(j))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(j))return this.each(function(s){var x=c(this);a[0]=j.call(this,s,b?x.html():v);x.domManip(a,b,d)});if(this[0]){e=a[0]&&a[0].parentNode&&a[0].parentNode.nodeType===11?{fragment:a[0].parentNode}:ra(a,this,n);if(i=e.fragment.firstChild){b=b&&c.nodeName(i,"tr");for(var o=0,m=this.length;o<m;o++)d.call(b?f(this[o],i):this[o],e.cacheable||this.length>1||o>0?e.fragment.cloneNode(true):e.fragment)}n&&c.each(n,
-Ma)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);for(var e=0,i=d.length;e<i;e++){var j=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),j);f=f.concat(j)}return this.pushStack(f,a,d.selector)}});c.each({remove:function(a,b){if(!a||c.filter(a,[this]).length){if(!b&&this.nodeType===1){c.cleanData(this.getElementsByTagName("*"));c.cleanData([this])}this.parentNode&&
-this.parentNode.removeChild(this)}},empty:function(){for(this.nodeType===1&&c.cleanData(this.getElementsByTagName("*"));this.firstChild;)this.removeChild(this.firstChild)}},function(a,b){c.fn[a]=function(){return this.each(b,arguments)}});c.extend({clean:function(a,b,d,f){b=b||r;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||r;var e=[];c.each(a,function(i,j){if(typeof j==="number")j+="";if(j){if(typeof j==="string"&&!gb.test(j))j=b.createTextNode(j);else if(typeof j===
-"string"){j=j.replace(Ga,Ia);var n=(Ha.exec(j)||["",""])[1].toLowerCase(),o=F[n]||F._default,m=o[0];i=b.createElement("div");for(i.innerHTML=o[1]+j+o[2];m--;)i=i.lastChild;if(!c.support.tbody){m=fb.test(j);n=n==="table"&&!m?i.firstChild&&i.firstChild.childNodes:o[1]==="<table>"&&!m?i.childNodes:[];for(o=n.length-1;o>=0;--o)c.nodeName(n[o],"tbody")&&!n[o].childNodes.length&&n[o].parentNode.removeChild(n[o])}!c.support.leadingWhitespace&&V.test(j)&&i.insertBefore(b.createTextNode(V.exec(j)[0]),i.firstChild);
-j=c.makeArray(i.childNodes)}if(j.nodeType)e.push(j);else e=c.merge(e,j)}});if(d)for(a=0;e[a];a++)if(f&&c.nodeName(e[a],"script")&&(!e[a].type||e[a].type.toLowerCase()==="text/javascript"))f.push(e[a].parentNode?e[a].parentNode.removeChild(e[a]):e[a]);else{e[a].nodeType===1&&e.splice.apply(e,[a+1,0].concat(c.makeArray(e[a].getElementsByTagName("script"))));d.appendChild(e[a])}return e},cleanData:function(a){for(var b=0,d;(d=a[b])!=null;b++){c.event.remove(d);c.removeData(d)}}});var hb=/z-?index|font-?weight|opacity|zoom|line-?height/i,
-Ja=/alpha\([^)]*\)/,Ka=/opacity=([^)]*)/,ga=/float/i,ha=/-([a-z])/ig,ib=/([A-Z])/g,jb=/^-?\d+(?:px)?$/i,kb=/^-?\d/,lb={position:"absolute",visibility:"hidden",display:"block"},mb=["Left","Right"],nb=["Top","Bottom"],ob=r.defaultView&&r.defaultView.getComputedStyle,La=c.support.cssFloat?"cssFloat":"styleFloat",ia=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===v)return c.curCSS(d,f);if(typeof e==="number"&&!hb.test(f))e+="px";c.style(d,f,e)})};
-c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return v;if((b==="width"||b==="height")&&parseFloat(d)<0)d=v;var f=a.style||a,e=d!==v;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=Ja.test(a)?a.replace(Ja,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Ka.exec(f.filter)[1])/100+"":""}if(ga.test(b))b=La;b=b.replace(ha,ia);if(e)f[b]=d;return f[b]},css:function(a,
-b,d,f){if(b==="width"||b==="height"){var e,i=b==="width"?mb:nb;function j(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(i,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,"border"+this+"Width",true))||0})}a.offsetWidth!==0?j():c.swap(a,lb,j);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&
-a.currentStyle){f=Ka.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ga.test(b))b=La;if(!d&&e&&e[b])f=e[b];else if(ob){if(ga.test(b))b="float";b=b.replace(ib,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ha,ia);f=a.currentStyle[b]||a.currentStyle[d];if(!jb.test(f)&&kb.test(f)){b=e.left;var i=a.runtimeStyle.left;a.runtimeStyle.left=
-a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=i}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var pb=
-J(),qb=/<script(.|\s)*?\/script>/gi,rb=/select|textarea/i,sb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ja=/\?/,tb=/(\?|&)_=.*?(&|$)/,ub=/^(\w+:)?\/\/([^\/?#]+)/,vb=/%20/g;c.fn.extend({_load:c.fn.load,load:function(a,b,d){if(typeof a!=="string")return this._load(a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=
-c.param(b,c.ajaxSettings.traditional);f="POST"}var i=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(j,n){if(n==="success"||n==="notmodified")i.html(e?c("<div />").append(j.responseText.replace(qb,"")).find(e):j.responseText);d&&i.each(d,[j.responseText,n,j])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&
-(this.checked||rb.test(this.nodeName)||sb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,
-b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:z.XMLHttpRequest&&(z.location.protocol!=="file:"||!z.ActiveXObject)?function(){return new z.XMLHttpRequest}:
-function(){try{return new z.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&e.success.call(o,n,j,w);e.global&&f("ajaxSuccess",[w,e])}function d(){e.complete&&e.complete.call(o,w,j);e.global&&f("ajaxComplete",[w,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}
-function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),i,j,n,o=a&&a.context||e,m=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(m==="GET")N.test(e.url)||(e.url+=(ja.test(e.url)?"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||
-N.test(e.url))){i=e.jsonpCallback||"jsonp"+pb++;if(e.data)e.data=(e.data+"").replace(N,"="+i+"$1");e.url=e.url.replace(N,"="+i+"$1");e.dataType="script";z[i]=z[i]||function(q){n=q;b();d();z[i]=v;try{delete z[i]}catch(p){}A&&A.removeChild(B)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===false&&m==="GET"){var s=J(),x=e.url.replace(tb,"$1_="+s+"$2");e.url=x+(x===e.url?(ja.test(e.url)?"&":"?")+"_="+s:"")}if(e.data&&m==="GET")e.url+=(ja.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&
-c.event.trigger("ajaxStart");s=(s=ub.exec(e.url))&&(s[1]&&s[1]!==location.protocol||s[2]!==location.host);if(e.dataType==="script"&&m==="GET"&&s){var A=r.getElementsByTagName("head")[0]||r.documentElement,B=r.createElement("script");B.src=e.url;if(e.scriptCharset)B.charset=e.scriptCharset;if(!i){var C=false;B.onload=B.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;b();d();B.onload=B.onreadystatechange=null;A&&B.parentNode&&
-A.removeChild(B)}}}A.insertBefore(B,A.firstChild);return v}var E=false,w=e.xhr();if(w){e.username?w.open(m,e.url,e.async,e.username,e.password):w.open(m,e.url,e.async);try{if(e.data||a&&a.contentType)w.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[e.url]);c.etag[e.url]&&w.setRequestHeader("If-None-Match",c.etag[e.url])}s||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",
-e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(fa){}if(e.beforeSend&&e.beforeSend.call(o,w,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");w.abort();return false}e.global&&f("ajaxSend",[w,e]);var g=w.onreadystatechange=function(q){if(!w||w.readyState===0||q==="abort"){E||d();E=true;if(w)w.onreadystatechange=c.noop}else if(!E&&w&&(w.readyState===4||q==="timeout")){E=true;w.onreadystatechange=c.noop;j=q==="timeout"?"timeout":!c.httpSuccess(w)?
-"error":e.ifModified&&c.httpNotModified(w,e.url)?"notmodified":"success";var p;if(j==="success")try{n=c.httpData(w,e.dataType,e)}catch(u){j="parsererror";p=u}if(j==="success"||j==="notmodified")i||b();else c.handleError(e,w,j,p);d();q==="timeout"&&w.abort();if(e.async)w=null}};try{var h=w.abort;w.abort=function(){w&&h.call(w);g("abort")}}catch(k){}e.async&&e.timeout>0&&setTimeout(function(){w&&!E&&g("timeout")},e.timeout);try{w.send(m==="POST"||m==="PUT"||m==="DELETE"?e.data:null)}catch(l){c.handleError(e,
-w,null,l);d()}e.async||g();return w}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=
-f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(j,n){if(c.isArray(n))c.each(n,
-function(o,m){b?f(j,m):d(j+"["+(typeof m==="object"||c.isArray(m)?o:"")+"]",m)});else!b&&n!=null&&typeof n==="object"?c.each(n,function(o,m){d(j+"["+o+"]",m)}):f(j,n)}function f(j,n){n=c.isFunction(n)?n():n;e[e.length]=encodeURIComponent(j)+"="+encodeURIComponent(n)}var e=[];if(b===v)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var i in a)d(i,a[i]);return e.join("&").replace(vb,"+")}});var ka={},wb=/toggle|show|hide/,xb=/^([+-]=)?([\d+-.]+)(.*)$/,
-W,ta=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(ka[d])f=ka[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();
-ka[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&
-c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var i=c.extend({},e),j,n=this.nodeType===1&&c(this).is(":hidden"),
-o=this;for(j in a){var m=j.replace(ha,ia);if(j!==m){a[m]=a[j];delete a[j];j=m}if(a[j]==="hide"&&n||a[j]==="show"&&!n)return i.complete.call(this);if((j==="height"||j==="width")&&this.style){i.display=c.css(this,"display");i.overflow=this.style.overflow}if(c.isArray(a[j])){(i.specialEasing=i.specialEasing||{})[j]=a[j][1];a[j]=a[j][0]}}if(i.overflow!=null)this.style.overflow="hidden";i.curAnim=c.extend({},a);c.each(a,function(s,x){var A=new c.fx(o,i,s);if(wb.test(x))A[x==="toggle"?n?"show":"hide":x](a);
-else{var B=xb.exec(x),C=A.cur(true)||0;if(B){x=parseFloat(B[2]);var E=B[3]||"px";if(E!=="px"){o.style[s]=(x||1)+E;C=(x||1)/A.cur(true)*C;o.style[s]=C+E}if(B[1])x=(B[1]==="-="?-1:1)*x+C;A.custom(C,x,E)}else A.custom(C,x,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",
-1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration==="number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,
-b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==
-null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(i){return e.step(i)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop===
-"width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=
-this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=
-c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=
-null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in r.documentElement?function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),
-f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(a)return this.each(function(s){c.offset.setOffset(this,a,s)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=
-b,e=b.ownerDocument,i,j=e.documentElement,n=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var o=b.offsetTop,m=b.offsetLeft;(b=b.parentNode)&&b!==n&&b!==j;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;i=e?e.getComputedStyle(b,null):b.currentStyle;o-=b.scrollTop;m-=b.scrollLeft;if(b===d){o+=b.offsetTop;m+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){o+=parseFloat(i.borderTopWidth)||
-0;m+=parseFloat(i.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&i.overflow!=="visible"){o+=parseFloat(i.borderTopWidth)||0;m+=parseFloat(i.borderLeftWidth)||0}f=i}if(f.position==="relative"||f.position==="static"){o+=n.offsetTop;m+=n.offsetLeft}if(c.offset.supportsFixedPosition&&f.position==="fixed"){o+=Math.max(j.scrollTop,n.scrollTop);m+=Math.max(j.scrollLeft,n.scrollLeft)}return{top:o,left:m}};c.offset={initialize:function(){var a=r.body,b=r.createElement("div"),
-d,f,e,i=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.insertBefore(b,a.firstChild);
-d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i;a.removeChild(b);c.offset.initialize=c.noop},
-bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),i=parseInt(c.curCSS(a,"top",true),10)||0,j=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,d,e);d={top:b.top-e.top+i,left:b.left-
-e.left+j};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=
-this.offsetParent||r.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],i;if(!e)return null;if(f!==v)return this.each(function(){if(i=ua(this))i.scrollTo(!a?f:c(i).scrollLeft(),a?f:c(i).scrollTop());else this[d]=f});else return(i=ua(e))?"pageXOffset"in i?i[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&i.document.documentElement[d]||i.document.body[d]:e[d]}});
-c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(i){var j=c(this);j[d](f.call(this,i,j[d]()))});return"scrollTo"in e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||
-e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===v?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});z.jQuery=z.$=c})(window);
diff --git a/railties/lib/generators/rails/app/templates/public/javascripts/jquery.rails.js b/railties/lib/generators/rails/app/templates/public/javascripts/jquery.rails.js
deleted file mode 100644
index 887da514e2..0000000000
--- a/railties/lib/generators/rails/app/templates/public/javascripts/jquery.rails.js
+++ /dev/null
@@ -1,239 +0,0 @@
-jQuery(function ($) {
- var rails = {
- update: function (selector, content, position) {
- var element = $('#' + selector);
- if (position) {
- switch (position) {
- case "before":
- element.before(content);
- break;
- case "after":
- element.after(content);
- break;
- case "top":
- element.prepend(content);
- break;
- case "bottom":
- element.append(content);
- break;
- default:
- element.append(content);
- break;
- }
- } else {
- element.html(content);
- }
- },
- remote: function (e) {
- var el = $(this),
- data = [],
- condition = el.attr('data-condition') ? eval(el.attr('data-condition')) : true,
- method = el.attr('method') || el.attr('data-method') || 'GET',
- url = el.attr('action') || el.attr('data-url') || '#',
- async = el.attr('data-remote-type') === 'synchronous' ? false : true;
-
- if (el.attr('data-submit')) {
- data = $('#' + el.attr('data-submit')).serializeArray();
- } else if (el.attr('data-with')) {
-
- if (e && e.target.tagName.toUpperCase() == 'SCRIPT' && el.attr('data-observed') !== null) {
- var observed = $('#' + el.attr('data-observed'));
- if(observed[0].tagName.toUpperCase() === 'FORM'){
- data = el.attr('data-with') + '=' + observed.serialize();
- } else if(observed[0].tagName.toUpperCase() === 'INPUT' && observed.attr('type').toUpperCase() !== "BUTTON" && observed.attr('type').toUpperCase() !== "SUBMIT") {
- data = el.attr('data-with') + '=' + observed.val();
- }
- } else {
- // TODO: remove eval when deprecated
- data = eval(el.attr('data-with'));
- }
- } else if (e && e.target.tagName.toUpperCase() == 'FORM') {
- data = el.serializeArray();
- } else if (e && e.target.tagName.toUpperCase() == 'INPUT') {
- data = el.closest('form').serializeArray();
- }
-
- if (condition) {
- el.trigger('rails:before');
-
- $.ajax({
- async: async,
- url: url,
- data: data,
- type: method.toUpperCase(),
- beforeSend: function (xhr) {
- xhr.setRequestHeader("Accept", "text/javascript")
- el.trigger('rails:after', xhr);
- el.trigger('rails:loading', xhr);
- },
- success: function (data, status, xhr) {
- el.trigger('rails:success', [data, status, xhr]);
- if (el.attr('data-update-success')) {
- rails.update(el.attr('data-update-success'), data, el.attr('data-update-position'));
- }
- },
- complete: function (xhr) {
- // enable disabled_with buttons
- if (el[0].tagName.toUpperCase() == 'FORM') {
- el.children('input[type="button"][data-enable-with],input[type="submit"][data-enable-with]').each(function(i, button){
- button = $(button);
- button.attr('value', button.attr('data-enable-with'));
- button.removeAttr('data-enable-with');
- button.removeAttr('disabled');
-
- });
- } else {
- el.attr('value', el.attr('data-enable-with'));
- el.removeAttr('data-enable-with');
- el.removeAttr('disabled');
- }
-
- el.trigger('rails:complete', xhr);
- el.trigger('rails:loaded', xhr);
- },
- error: function (xhr, status, error) {
- el.trigger('rails:failure', [xhr, status, error]);
- if (el.attr('data-update-failure')) {
- rails.update(el.attr('data-update-failure'), xhr.responseText, el.attr('data-update-position'));
- }
- }
- });
- }
- e.preventDefault();
- }
- }
-
- /**
- * observe_form, and observe_field
- */
- $('script[data-observe="true"]').each(function (index, e) {
- var el = $(e),
- observed = $('#' + $(e).attr('data-observed'));
- frequency = el.attr('data-frequency') ? el.attr('data-frequency') : 10,
- value = observed[0].tagName.toUpperCase() === 'FORM' ? observed.serialize() : observed.val();
-
- var observe = function (observed, frequency, value, e) {
- return function () {
- var event = new jQuery.Event('periodical'),
- newValue = observed[0].tagName.toUpperCase() === 'FORM' ? observed.serialize() : observed.val();
- event.target = e;
-
- if(value !== newValue) {
- value = newValue;
- $(e).trigger('rails:observe');
- rails.remote.call(el, event);
- }
- }
- }(observed, frequency, value, e);
-
- setInterval(observe, frequency * 1000);
- });
-
- /**
- * confirm
- * make sure this event is first!
- */
- $('a[data-confirm],input[type="submit"][data-confirm],input[type="button"][data-confirm]').live('click', function(e){
- var el = $(this);
-
- if(!confirm(el.attr('data-confirm'))){
- return false;
- }
- });
-
- /**
- * periodically_call_remote
- */
- $('script[data-periodical="true"]').each(function (index, e) {
- var el = $(e),
- frequency = el.attr('data-frequency') ? el.attr('data-frequency') : 10;
-
- setInterval(function () {
- return function () {
- var event = new jQuery.Event('periodical');
- event.target = e;
-
- rails.remote.call(el, event);
- }
- }(e, el), frequency * 1000);
- });
-
- /**
- * disable_with
- */
- $('input[type="button"][data-disable-with],input[type="submit"][data-disable-with]').live('click', function(e){
- var el = $(this);
-
- el.attr('data-enable-with', el.attr('value'));
- el.attr('disabled', 'disabled');
- el.attr('value', el.attr('data-disable-with'));
- });
-
- /**
- * remote_form_tag, and remote_form_for
- */
- $('form[data-remote="true"]').live('submit', rails.remote);
-
- /**
- * link_to_remote, button_to_remote, and submit_to_remote
- */
- $('a[data-remote="true"],input[data-remote="true"],input[data-remote-submit="true"]').live('click', rails.remote);
-
- /*
- * popup
- */
- $('a[data-popup],input[type="button"][data-popup]').live('click', function(e){
- var el = $(this),
- url = el.attr('data-url') || el.attr('href');
-
- e.preventDefault();
-
- if(el.attr('data-popup') === "true"){
- window.open(url);
- } else {
- window.open(url, el.attr('data-popup'));
- }
- });
-
- /**
- *
- * Rails 2.x Helper / Event Handlers
- * By default we listen to all callbacks, and status code callbacks and
- * check the element for data-<callback> attribute and eval it.
- *
- */
- rails.compat = {
- evalAttribute: function (element, attribute) {
- var el = $(element),
- attr = el.attr('data-' + attribute);
- return (attr) ? eval(attr) : true;
- }
- };
-
- $('form[data-remote="true"],a[data-remote="true"],input[data-remote="true"],script[data-observe="true"]')
- .live('rails:before', function (e) {
- rails.compat.evalAttribute(this, 'onbefore');
- })
- .live('rails:after', function (e, xhr) {
- rails.compat.evalAttribute(this, 'onafter');
- })
- .live('rails:loading', function (e, xhr) {
- rails.compat.evalAttribute(this, 'onloading');
- })
- .live('rails:loaded', function (e, xhr) {
- rails.compat.evalAttribute(this, 'onloaded');
- })
- .live('rails:complete', function (e, xhr) {
- rails.compat.evalAttribute(this, 'oncomplete');
- rails.compat.evalAttribute(this, 'on' + xhr.status);
- })
- .live('rails:success', function (e, data, status, xhr) {
- rails.compat.evalAttribute(this, 'onsuccess');
- })
- .live('rails:failure', function (e, xhr, status, error) {
- rails.compat.evalAttribute(this, 'onfailure');
- })
- .live('rails:observe', function (e) {
- rails.compat.evalAttribute(this, 'onobserve');
- });
-});
diff --git a/railties/lib/generators/rails/app/templates/public/javascripts/prototype.rails.js b/railties/lib/generators/rails/app/templates/public/javascripts/prototype.rails.js
deleted file mode 100644
index aaed677fda..0000000000
--- a/railties/lib/generators/rails/app/templates/public/javascripts/prototype.rails.js
+++ /dev/null
@@ -1,324 +0,0 @@
-Event.observe(document, 'dom:loaded', function() {
- function handle_remote(el, e){
- var data = null,
- method = el.readAttribute('method') || el.readAttribute('data-method') || 'GET',
- url = el.readAttribute('action') || el.readAttribute('data-url') || '#',
- async = el.readAttribute('data-remote-type') === 'synchronous' ? false : true,
- update = el.readAttribute('data-update-success'),
- position = el.readAttribute('data-update-position');
-
- if (el.readAttribute('data-submit')) {
- var submit_el = $(el.readAttribute('data-submit'));
- if(submit_el !== undefined && submit_el.tagName.toUpperCase() == 'FORM'){
- data = submit_el.serialize();
- }
- } else if (el.readAttribute('data-with')) {
- // It seems there is a big inconsistency between what :with means depending on the element type
- // so this is going to look a bit crazy
- if(el.tagName.toUpperCase() === 'SCRIPT' && el.readAttribute('data-observed') !== null){
- // Handle observe_field and observe_form
- var observed_element = $(el.readAttribute('data-observed'));
-
- if(observed_element.tagName.toUpperCase() === 'FORM'){
- data = el.readAttribute('data-with') + '=' + observed_element.serialize();
- } else if(observed_element.tagName.toUpperCase() === 'INPUT' && observed_element.readAttribute('type').toUpperCase() !== "BUTTON" && observed_element.readAttribute('type').toUpperCase() !== "SUBMIT") {
- data = el.readAttribute('data-with') + '=' + observed_element.getValue();
- }
- } else {
- // Handle link_to and button_to
- data = evalAttribute(el, 'data-with');
- }
- } else if(el.tagName.toUpperCase() === 'FORM') {
- data = el.serialize();
- }
-
- document.fire('rails:before');
-
- var request = new Ajax.Request(url, {
- method: method,
- asynchronous: async,
- parameters: data,
- evalJS: true,
- evalJSON: true,
- onComplete: function(xhr){
- document.fire('rails:complete', {xhr: xhr, element: el, submitted_button: getEventProperty(e, 'submitted_button')});
- },
- onLoading: function(xhr){
- document.fire('rails:after', {xhr: xhr, element: el});
- document.fire('rails:loading', {xhr: xhr, element: el});
- },
- onLoaded: function(xhr){
- document.fire('rails:loaded', {xhr: xhr, element: el});
- },
- onSuccess: function(xhr){
- document.fire('rails:success', {xhr: xhr, element: el});
- },
- onFailure: function(xhr){
- document.fire('rails:failure', {xhr: xhr, element: el});
- }
- });
-
- }
-
- function setEventProperty(e, property, value){
- if(e.memo === undefined){
- e.memo = {};
- }
-
- e.memo[property] = value;
- }
-
- function getEventProperty(e, property){
- if(e !== null && e.memo !== undefined && e.memo[property] !== undefined){
- return e.memo[property];
- }
- }
-
- function confirmed(e, el){
- if(getEventProperty(e,'confirm_checked') !== true){
- setEventProperty(e, 'confirm_checked', true);
-
- el = Event.findElement(e, 'form') || el;
- var confirm_msg = el.readAttribute('data-confirm');
-
- if(confirm_msg !== null){
- var result = el.fire('rails:confirm', {confirm_msg: confirm_msg});
- if(result.memo.stop_event === true){
- Event.stop(e);
- return false;
- }
- }
- }
- return true;
- }
-
- function disable_button(el){
- var disable_with = el.readAttribute('data-disable-with');
- if(disable_with !== null){
- el.writeAttribute('data-enable-with', el.readAttribute('value'));
- el.writeAttribute('value', disable_with);
- el.writeAttribute('disabled', true);
- }
- }
-
- function enable_button(el){
- var enable_with = el.readAttribute('data-enable-with');
- if(enable_with !== null){
- el.writeAttribute('value', enable_with);
- }
- el.writeAttribute('disabled', false);
- }
-
- function updateHTML(el, content, result){
- var element_id = null;
-
- if(result === 'success'){
- element_id = el.readAttribute('data-update-success');
- } else if(result === 'failure'){
- element_id = el.readAttribute('data-update-failure');
- }
-
- var element_to_update = $(element_id);
- if(element_to_update !== null){
- var position = el.readAttribute('data-update-position');
- if(position !== null){
- var options = {};
- options[position] = content;
- element_to_update.insert(options);
- } else {
- element_to_update.update(content);
- }
- }
- }
-
- $$("script[data-periodical=true]").each(function(el){
- var executor = new PeriodicalExecuter(function() { handle_remote(el);}, el.readAttribute('data-frequency'));
- });
-
- $$("script[data-observe=true]").each(function(el){
- var observed_element = $(el.readAttribute('data-observed'));
- var original_value = observed_element.tagName.toUpperCase() === 'FORM' ? observed_element.serialize() : observed_element.getValue();
- var callback = el.readAttribute('data-onobserve');
- var executor = new PeriodicalExecuter(function() {
- var value = observed_element.tagName.toUpperCase() === 'FORM' ? observed_element.serialize() : observed_element.getValue();
-
- if(original_value !== value){
- original_value = value;
-
- if(callback !== null){
- evalAttribute(el, 'onobserve');
- } else if(el.readAttribute('data-url') !== null){
- handle_remote(el);
- }
- }
- }, el.readAttribute('data-frequency'));
-
- });
-
- /**
- *
- * Event Listeners
- *
- * the original element is contained inside the event,
- * for some reason prototype wont let me listen for custom events on document
- * if the event wasn't fired on document
- *
- */
-
- Event.observe(document, 'submit', function (e) {
- var form = Event.findElement(e, 'form');
- // Make sure conditions and confirm have not already run
- if(form !== undefined && conditions_met(e, form) && confirmed(e, form)){
-
- var button = form.down('input[data-submitted=true]');
- button.writeAttribute('data-submitted', null);
- setEventProperty(e, 'submitted_button', button);
- disable_button(button);
-
- if(form.readAttribute('data-remote') === 'true'){
- Event.stop(e);
- handle_remote(form, e);
- }
- }
- });
-
- Event.observe(document, 'click', function (e) {
- var el = Event.findElement(e, 'a') || Event.findElement(e, 'input');
-
- if(el !== undefined && el.tagName.toUpperCase() === 'INPUT' && el.readAttribute('type').toUpperCase() === 'SUBMIT'){
- el.writeAttribute('data-submitted', 'true');
-
- // Submit is handled by submit event, don't continue on this path
- el = undefined;
- } else if(el !== undefined && el.tagName.toUpperCase() === 'INPUT' && el.readAttribute('type').toUpperCase() !== 'BUTTON'){
- // Make sure other inputs do not send this event
- el = undefined;
- }
-
- if(el !== undefined && conditions_met(e, el) && confirmed(e, el)){
- if(el.tagName.toUpperCase() === 'INPUT' && el.readAttribute('type').toUpperCase() === 'BUTTON'){
- disable_button(el);
- }
-
- if(el.readAttribute('data-remote') === 'true'){
- Event.stop(e);
- handle_remote(el, e);
- } else if(el.readAttribute('data-popup') !== null){
- Event.stop(e);
- document.fire('rails:popup', {element: el});
- }
- }
- });
-
- /**
- *
- * Default Event Handlers
- *
- */
- Event.observe(document, 'rails:confirm', function(e){
- setEventProperty(e, 'stop_event', !confirm(getEventProperty(e,'confirm_msg')));
- });
-
- Event.observe(document, 'rails:popup', function(e){
- var el = getEventProperty(e, 'element');
- var url = el.readAttribute('href') || el.readAttribute('data-url');
-
- if(el.readAttribute('data-popup') === true){
- window.open(url);
- } else {
- window.open(url, el.readAttribute('data-popup'));
- }
- });
-
- Event.observe(document, 'rails:complete', function(e){
- var el = getEventProperty(e, 'element');
-
- if(el.tagName.toUpperCase() === 'FORM'){
- var button = getEventProperty(e, 'submitted_button') ;
- enable_button(button);
- }
- });
-
- Event.observe(document, 'rails:success', function(e){
- var el = getEventProperty(e, 'element'),
- xhr = getEventProperty(e, 'xhr');
-
- if(xhr.responseText !== null){
- updateHTML(el, xhr.responseText, 'success');
- }
- });
-
- Event.observe(document, 'rails:failure', function(e){
- var el = getEventProperty(e, 'element'),
- xhr = getEventProperty(e, 'xhr');
-
- if(xhr.responseText !== null){
- updateHTML(el, xhr.responseText, 'failure');
- }
- });
-
- /**
- *
- * Rails 2.x Helpers / Event Handlers
- *
- */
- function evalAttribute(el, attribute){
- var js = el.readAttribute('data-' + attribute);
-
- if(js){
- eval(js);
- }
- }
-
- function conditions_met(e, el){
- if(getEventProperty(e,'condition_checked') !== true){
- setEventProperty(e, 'condition_checked', true);
-
- el = Event.findElement(e, 'form') || el;
- var conditions = el.readAttribute('data-condition');
-
- if(conditions !== null){
- if(eval(conditions) === false){
- Event.stop(e);
- return false;
- }
- }
- }
- return true;
- }
-
- Event.observe(document, 'rails:success', function(e){
- evalAttribute(el, 'onsuccess');
- });
-
- Event.observe(document, 'rails:failure', function(e){
- evalAttribute(el, 'onfailure');
- });
-
- Event.observe(document, 'rails:complete', function(e){
- var el = getEventProperty(e, 'element');
-
- evalAttribute(el, 'oncomplete');
- evalAttribute(el, 'on' + getEventProperty('xhr', xhr.status));
-
- if(el.readAttribute('data-periodical') === 'true'){
- evalAttribute(el, 'onobserve');
- }
- });
-
- Event.observe(document, 'rails:loading', function(e){
- evalAttribute(el, 'onloading');
- });
-
- Event.observe(document, 'rails:loaded', function(e){
- evalAttribute(el, 'onloaded');
- });
-
- Event.observe(document, 'rails:before', function(e){
- evalAttribute(el, 'onbefore');
- });
-
- Event.observe(document, 'rails:after', function(e){
- evalAttribute(el, 'onafter');
- });
-});
diff --git a/railties/lib/generators/rails/app/templates/public/javascripts/rails.js b/railties/lib/generators/rails/app/templates/public/javascripts/rails.js
new file mode 100644
index 0000000000..2a0a05d25f
--- /dev/null
+++ b/railties/lib/generators/rails/app/templates/public/javascripts/rails.js
@@ -0,0 +1,77 @@
+document.observe("dom:loaded", function() {
+ function handleRemote(element) {
+ var method, url, params;
+
+ if (element.tagName.toLowerCase() == 'form') {
+ method = element.readAttribute('method') || 'post';
+ url = element.readAttribute('action');
+ params = element.serialize(true);
+ } else {
+ method = element.readAttribute('data-method') || 'get';
+ // TODO: data-url support is going away, just use href
+ url = element.readAttribute('data-url') || element.readAttribute('href');
+ params = {};
+ }
+
+ var event = element.fire("ajax:before");
+ if (event.stopped) return false;
+
+ new Ajax.Request(url, {
+ method: method,
+ parameters: params,
+ asynchronous: true,
+ evalScripts: true,
+
+ onLoading: function(request) { element.fire("ajax:loading", {request: request}); },
+ onLoaded: function(request) { element.fire("ajax:loaded", {request: request}); },
+ onInteractive: function(request) { element.fire("ajax:interactive", {request: request}); },
+ onComplete: function(request) { element.fire("ajax:complete", {request: request}); },
+ onSuccess: function(request) { element.fire("ajax:success", {request: request}); },
+ onFailure: function(request) { element.fire("ajax:failure", {request: request}); }
+ });
+
+ element.fire("ajax:after");
+ }
+
+ $(document.body).observe("click", function(event) {
+ var element = event.findElement("a[data-remote=true]");
+ if (element) {
+ handleRemote(element);
+ event.stop();
+ }
+ });
+
+ $(document.body).observe("ajax:before", function(event) {
+ var message = event.element().readAttribute('data-confirm');
+ if (message && !confirm(message)) event.stop();
+ });
+
+ // TODO: I don't think submit bubbles in IE
+ $(document.body).observe("submit", function(event) {
+ var inputs = event.element().select("input[type=submit][data-disable-with]");
+ inputs.each(function(input) {
+ input.disabled = true;
+ input.writeAttribute('data-original-value', input.value);
+ input.value = input.readAttribute('data-disable-with');
+ });
+
+ var element = event.findElement("form[data-remote=true]");
+ if (element) {
+ handleRemote(element);
+ event.stop();
+ }
+ });
+
+ $(document.body).observe("ajax:complete", function(event) {
+ var element = event.element();
+
+ if (element.tagName.toLowerCase() == 'form') {
+ var inputs = element.select("input[type=submit][disabled=true][data-disable-with]");
+ inputs.each(function(input) {
+ input.value = input.readAttribute('data-original-value');
+ input.writeAttribute('data-original-value', null);
+ input.disabled = false;
+ });
+ }
+ });
+});
diff --git a/railties/lib/rails/commands/destroy.rb b/railties/lib/rails/commands/destroy.rb
index a2eff377ce..92a06ebdd8 100644
--- a/railties/lib/rails/commands/destroy.rb
+++ b/railties/lib/rails/commands/destroy.rb
@@ -1,5 +1,4 @@
require 'rails/generators'
-Rails::Generators.configure!
if ARGV.size == 0
Rails::Generators.help
diff --git a/railties/lib/rails/commands/generate.rb b/railties/lib/rails/commands/generate.rb
index c1120aad74..5e45d8ab46 100755
--- a/railties/lib/rails/commands/generate.rb
+++ b/railties/lib/rails/commands/generate.rb
@@ -1,5 +1,4 @@
require 'rails/generators'
-Rails::Generators.configure!
if ARGV.size == 0
Rails::Generators.help
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index a95075562f..6a4ebe883b 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -128,61 +128,61 @@ module Rails
def view_path=(value)
ActiveSupport::Deprecation.warn "config.view_path= is deprecated, " <<
- "please do config.paths.app.views= instead", caller
+ "please do paths.app.views= instead", caller
paths.app.views = value
end
def view_path
ActiveSupport::Deprecation.warn "config.view_path is deprecated, " <<
- "please do config.paths.app.views instead", caller
+ "please do paths.app.views instead", caller
paths.app.views.to_a.first
end
def routes_configuration_file=(value)
ActiveSupport::Deprecation.warn "config.routes_configuration_file= is deprecated, " <<
- "please do config.paths.config.routes= instead", caller
+ "please do paths.config.routes= instead", caller
paths.config.routes = value
end
def routes_configuration_file
ActiveSupport::Deprecation.warn "config.routes_configuration_file is deprecated, " <<
- "please do config.paths.config.routes instead", caller
+ "please do paths.config.routes instead", caller
paths.config.routes.to_a.first
end
def database_configuration_file=(value)
ActiveSupport::Deprecation.warn "config.database_configuration_file= is deprecated, " <<
- "please do config.paths.config.database= instead", caller
+ "please do paths.config.database= instead", caller
paths.config.database = value
end
def database_configuration_file
ActiveSupport::Deprecation.warn "config.database_configuration_file is deprecated, " <<
- "please do config.paths.config.database instead", caller
+ "please do paths.config.database instead", caller
paths.config.database.to_a.first
end
def log_path=(value)
ActiveSupport::Deprecation.warn "config.log_path= is deprecated, " <<
- "please do config.paths.log= instead", caller
+ "please do paths.log= instead", caller
paths.config.log = value
end
def log_path
ActiveSupport::Deprecation.warn "config.log_path is deprecated, " <<
- "please do config.paths.log instead", caller
+ "please do paths.log instead", caller
paths.config.log.to_a.first
end
def controller_paths=(value)
ActiveSupport::Deprecation.warn "config.controller_paths= is deprecated, " <<
- "please do config.paths.app.controllers= instead", caller
+ "please do paths.app.controllers= instead", caller
paths.app.controllers = value
end
def controller_paths
ActiveSupport::Deprecation.warn "config.controller_paths is deprecated, " <<
- "please do config.paths.app.controllers instead", caller
+ "please do paths.app.controllers instead", caller
paths.app.controllers.to_a.uniq
end
end
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 1271de7af9..90b513fcb4 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -286,3 +286,6 @@ module Rails
end
end
+# If the application was already defined, configure generators,
+# otherwise you have to configure it by hand.
+Rails::Generators.configure! if Rails.respond_to?(:application) && Rails.application \ No newline at end of file
diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb
index d91f67823f..9a82e051e7 100644
--- a/railties/lib/rails/initializable.rb
+++ b/railties/lib/rails/initializable.rb
@@ -1,3 +1,5 @@
+require 'tsort'
+
module Rails
module Initializable
def self.included(base)
@@ -30,29 +32,21 @@ module Rails
end
class Collection < Array
+ include TSort
+
+ alias :tsort_each_node :each
+ def tsort_each_child(initializer, &block)
+ select { |i| i.before == initializer.name || i.name == initializer.after }.each(&block)
+ end
+
def initialize(initializers = [])
- super()
- initializers.each do |initializer|
- if initializer.before
- index = index_for(initializer.before)
- elsif initializer.after
- index = index_for(initializer.after)
- index += 1 if index
- else
- index = length
- end
- insert(index || -1, initializer)
- end
+ super(initializers)
+ replace(tsort)
end
def +(other)
Collection.new(to_a + other.to_a)
end
-
- def index_for(name)
- initializer = find { |i| i.name == name }
- initializer && index(initializer)
- end
end
def run_initializers(*args)
@@ -87,6 +81,7 @@ module Rails
def initializer(name, opts = {}, &blk)
raise ArgumentError, "A block must be passed when defining an initializer" unless blk
+ opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] }
initializers << Initializer.new(name, nil, opts, &blk)
end
diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb
index 881c97f02d..64d03e7599 100644
--- a/railties/lib/rails/plugin.rb
+++ b/railties/lib/rails/plugin.rb
@@ -43,7 +43,7 @@ module Rails
@config ||= Engine::Configuration.new
end
- initializer :load_init_rb do |app|
+ initializer :load_init_rb, :before => :load_application_initializers do |app|
file = Dir["#{root}/{rails/init,init}.rb"].first
config = app.config
eval(File.read(file), binding, file) if file && File.file?(file)
diff --git a/railties/lib/rails/test_unit/railtie.rb b/railties/lib/rails/test_unit/railtie.rb
index f93dace9bb..21b9dbc074 100644
--- a/railties/lib/rails/test_unit/railtie.rb
+++ b/railties/lib/rails/test_unit/railtie.rb
@@ -11,7 +11,7 @@ module Rails
end
rake_tasks do
- load "rails/tasks/testing.rake"
- end
+ load "rails/test_unit/testing.rake"
+ end
end
end \ No newline at end of file
diff --git a/railties/lib/rails/tasks/testing.rake b/railties/lib/rails/test_unit/testing.rake
index 57857fb911..57857fb911 100644
--- a/railties/lib/rails/tasks/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 57bd797ef0..60d644bd59 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -37,6 +37,13 @@ module ApplicationTests
require "#{app_path}/config/environment"
assert_respond_to Rails::Application, :routes_reloader
assert_equal Rails::Application.routes_reloader, Rails.application.routes_reloader
+ assert_equal Rails::Application.routes_reloader, AppTemplate::Application.routes_reloader
+ end
+
+ test "Rails::Application responds to paths" do
+ require "#{app_path}/config/environment"
+ assert_respond_to AppTemplate::Application, :paths
+ assert_equal AppTemplate::Application.paths.app.views.to_a, ["#{app_path}/app/views"]
end
test "the application root is set correctly" do
diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb
index d17be5b964..3cd16a69f9 100644
--- a/railties/test/generators/generators_test_helper.rb
+++ b/railties/test/generators/generators_test_helper.rb
@@ -9,7 +9,7 @@ Rails.application.config.root = Rails.root
require 'rails/generators'
require 'rails/generators/test_case'
-Rails::Generators.configure!
+
require 'active_record'
require 'action_dispatch'
diff --git a/railties/test/initializable_test.rb b/railties/test/initializable_test.rb
index 0c7378cb64..6a7e4dcc25 100644
--- a/railties/test/initializable_test.rb
+++ b/railties/test/initializable_test.rb
@@ -50,7 +50,7 @@ module InitializableTests
$arr << 3
end
- initializer :four, :after => :one do
+ initializer :four, :after => :one, :before => :two do
$arr << 4
end
end
@@ -97,7 +97,7 @@ module InitializableTests
$arr << 3
end
- initializer :terminate, :after => :first do
+ initializer :terminate, :after => :first, :before => :startup do
$arr << two
end
@@ -121,6 +121,39 @@ module InitializableTests
end
end
+ module Interdependent
+ class PluginA
+ include Rails::Initializable
+
+ initializer "plugin_a.startup" do
+ $arr << 1
+ end
+
+ initializer "plugin_a.terminate" do
+ $arr << 4
+ end
+ end
+
+ class PluginB
+ include Rails::Initializable
+
+ initializer "plugin_b.startup", :after => "plugin_a.startup" do
+ $arr << 2
+ end
+
+ initializer "plugin_b.terminate", :before => "plugin_a.terminate" do
+ $arr << 3
+ end
+ end
+
+ class Application
+ include Rails::Initializable
+ def self.initializers
+ PluginB.initializers + PluginA.initializers
+ end
+ end
+ end
+
class Basic < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
@@ -174,6 +207,12 @@ module InitializableTests
Child.run_initializers
assert_equal [5, 3, 1, 4, 2], $arr
end
+
+ test "handles dependencies introduced before all initializers are loaded" do
+ $arr = []
+ Interdependent::Application.run_initializers
+ assert_equal [1, 2, 3, 4], $arr
+ end
end
class InstanceTest < ActiveSupport::TestCase
diff --git a/railties/test/railties/plugin_test.rb b/railties/test/railties/plugin_test.rb
index 997b692e49..09b859dcdd 100644
--- a/railties/test/railties/plugin_test.rb
+++ b/railties/test/railties/plugin_test.rb
@@ -47,7 +47,7 @@ module RailtiesTest
assert_equal :debug, LEVEL
end
- test "plugin_init_is_ran_before_application_ones" do
+ test "plugin_init_is_run_before_application_ones" do
plugin "foo", "$foo = true" do |plugin|
plugin.write "lib/foo.rb", "module Foo; end"
end