mirror of https://github.com/rails/rails
Merge remote branch 'mainstream/master'
Conflicts: activemodel/lib/active_model/state_machine.rb
This commit is contained in:
commit
ed60021f39
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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])
|
||||
|
|
|
@ -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])
|
||||
|
||||
# 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 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)
|
||||
|
||||
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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
HTML
|
|
@ -0,0 +1 @@
|
|||
PLAIN
|
|
@ -0,0 +1 @@
|
|||
body_text
|
|
@ -0,0 +1 @@
|
|||
HTML -- <%= yield %>
|
|
@ -0,0 +1 @@
|
|||
PLAIN -- <%= yield %>
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
# $("details").visualEffect("toggle_blind");
|
||||
# $("more_link").update("Show me less");
|
||||
# }
|
||||
# catch (e) {
|
||||
# alert('RJS error:\n\n' + e.toString());
|
||||
# alert('$(\"details\").visualEffect(\"toggle_blind\");
|
||||
# \n$(\"more_link\").update(\"Show me less\");');
|
||||
# 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
|
||||
|
|
|
@ -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'] = "'"
|
||||
end
|
||||
js_options['parameters'] << "#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}')"
|
||||
end
|
||||
|
||||
options_for_javascript(js_options)
|
||||
end
|
||||
|
||||
def method_option_to_s(method)
|
||||
(method.is_a?(String) and !method.index("'").nil?) ? method : "'#{method}'"
|
||||
end
|
||||
|
||||
def build_observer(klass, name, options = {})
|
||||
if options[:with] && (options[:with] !~ /[\{=(.]/)
|
||||
options[:with] = "'#{options[:with]}=' + encodeURIComponent(value)"
|
||||
else
|
||||
options[:with] ||= 'value' unless options[:function]
|
||||
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 build_callbacks(options)
|
||||
callbacks = {}
|
||||
options.each do |callback, code|
|
||||
if CALLBACKS.include?(callback)
|
||||
name = 'on' + callback.to_s.capitalize
|
||||
callbacks[name] = "function(request){#{code}}"
|
||||
"{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}"
|
||||
end
|
||||
end
|
||||
callbacks
|
||||
end
|
||||
|
||||
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'] << " + '&"
|
||||
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
|
||||
|
||||
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}}"
|
||||
end
|
||||
end
|
||||
callbacks
|
||||
end
|
||||
end
|
||||
|
||||
# Converts chained method calls on DOM proxy elements into JavaScript chains
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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&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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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'
|
||||
|
|
@ -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&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&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
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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("header", "\\u003Ch1\\u003EGreetings\\u003C/h1\\u003E");; 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("header", "\\u003Ch1\\u003EGreetings\\u003C/h1\\u003E");; 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!')")
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 = ''
|
||||
|
||||
|
|
|
@ -43,7 +43,6 @@ module ActiveModel
|
|||
autoload :Observer, 'active_model/observing'
|
||||
autoload :Observing
|
||||
autoload :Serialization
|
||||
autoload :StateMachine
|
||||
autoload :TestCase
|
||||
autoload :Translation
|
||||
autoload :VERSION
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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?
|
||||
|
|
|
@ -73,7 +73,6 @@ module ActiveRecord
|
|||
autoload :SchemaDumper
|
||||
autoload :Serialization
|
||||
autoload :SessionStore
|
||||
autoload :StateMachine
|
||||
autoload :Timestamp
|
||||
autoload :Transactions
|
||||
autoload :Validations
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 => '')
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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?
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
# 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
|
||||
|
||||
private
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
|
||||
def superclass_delegating_writer(*names, &block)
|
||||
options = names.extract_options!
|
||||
|
||||
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
|
||||
|
||||
self.send(:"#{name}=", yield) if block_given?
|
||||
end
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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);
|
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,5 +1,4 @@
|
|||
require 'rails/generators'
|
||||
Rails::Generators.configure!
|
||||
|
||||
if ARGV.size == 0
|
||||
Rails::Generators.help
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
require 'rails/generators'
|
||||
Rails::Generators.configure!
|
||||
|
||||
if ARGV.size == 0
|
||||
Rails::Generators.help
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue