diff options
Diffstat (limited to 'actionpack/test')
188 files changed, 5960 insertions, 3591 deletions
diff --git a/actionpack/test/abstract/collector_test.rb b/actionpack/test/abstract/collector_test.rb index b1a5044399..fc59bf19c4 100644 --- a/actionpack/test/abstract/collector_test.rb +++ b/actionpack/test/abstract/collector_test.rb @@ -24,15 +24,21 @@ module AbstractController test "does not respond to unknown mime types" do collector = MyCollector.new - assert !collector.respond_to?(:unknown) + assert_not_respond_to collector, :unknown end test "register mime types on method missing" do AbstractController::Collector.send(:remove_method, :js) - collector = MyCollector.new - assert !collector.respond_to?(:js) - collector.js - assert_respond_to collector, :js + begin + collector = MyCollector.new + assert_not_respond_to collector, :js + collector.js + assert_respond_to collector, :js + ensure + unless AbstractController::Collector.method_defined? :js + AbstractController::Collector.generate_method_for_mime :js + end + end end test "does not register unknown mime types" do diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 0e163a93eb..2d69ceed3a 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -4,8 +4,6 @@ $:.unshift(File.dirname(__FILE__) + '/lib') $:.unshift(File.dirname(__FILE__) + '/fixtures/helpers') $:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers') -ENV['TMPDIR'] = File.join(File.dirname(__FILE__), 'tmp') - require 'active_support/core_ext/kernel/reporting' # These are the normal settings that will be set up by Railties @@ -15,8 +13,15 @@ silence_warnings do Encoding.default_external = "UTF-8" end +require 'drb' +require 'drb/unix' +require 'tempfile' + +PROCESS_COUNT = (ENV['N'] || 4).to_i + require 'active_support/testing/autorun' require 'abstract_controller' +require 'abstract_controller/railties/routes_helpers' require 'action_controller' require 'action_view' require 'action_view/testing/resolvers' @@ -43,10 +48,13 @@ Thread.abort_on_exception = true # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true +# Disable available locale checks to avoid warnings running the test suite. +I18n.enforce_available_locales = false + # Register danish language for testing I18n.backend.store_translations 'da', {} I18n.backend.store_translations 'pt-BR', {} -ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort +ORIGINAL_LOCALES = I18n.available_locales.map(&:to_s).sort FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') FIXTURES = Pathname.new(FIXTURE_LOAD_PATH) @@ -102,6 +110,9 @@ end module ActiveSupport class TestCase include ActionDispatch::DrawOnce + if ActiveSupport::Testing::Isolation.forking_env? && PROCESS_COUNT > 0 + parallelize_me! + end end end @@ -184,6 +195,7 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase yield temporary_routes ensure self.class.app = old_app + self.remove! silence_warnings { Object.const_set(:SharedTestRoutes, old_routes) } end @@ -248,9 +260,8 @@ end module ActionController class Base - include ActionController::Testing # This stub emulates the Railtie including the URL helpers from a Rails application - include SharedTestRoutes.url_helpers + extend AbstractController::Railties::RoutesHelpers.with(SharedTestRoutes) include SharedTestRoutes.mounted_helpers self.view_paths = FIXTURE_LOAD_PATH @@ -303,22 +314,95 @@ end module ActionDispatch module RoutingVerbs - def get(uri_or_host, path = nil) + def send_request(uri_or_host, method, path) host = uri_or_host.host unless path path ||= uri_or_host.path params = {'PATH_INFO' => path, - 'REQUEST_METHOD' => 'GET', + 'REQUEST_METHOD' => method, 'HTTP_HOST' => host} - routes.call(params)[2].join + routes.call(params) + end + + def request_path_params(path, options = {}) + method = options[:method] || 'GET' + resp = send_request URI('http://localhost' + path), method.to_s.upcase, nil + status = resp.first + if status == 404 + raise ActionController::RoutingError, "No route matches #{path.inspect}" + end + controller.request.path_parameters + end + + def get(uri_or_host, path = nil) + send_request(uri_or_host, 'GET', path)[2].join + end + + def post(uri_or_host, path = nil) + send_request(uri_or_host, 'POST', path)[2].join + end + + def put(uri_or_host, path = nil) + send_request(uri_or_host, 'PUT', path)[2].join + end + + def delete(uri_or_host, path = nil) + send_request(uri_or_host, 'DELETE', path)[2].join + end + + def patch(uri_or_host, path = nil) + send_request(uri_or_host, 'PATCH', path)[2].join end end end module RoutingTestHelpers - def url_for(set, options, recall = nil) - set.send(:url_for, options.merge(:only_path => true, :_recall => recall)) + def url_for(set, options) + route_name = options.delete :use_route + set.url_for options.merge(:only_path => true), route_name + end + + def make_set(strict = true) + tc = self + TestSet.new ->(c) { tc.controller = c }, strict + end + + class TestSet < ActionDispatch::Routing::RouteSet + attr_reader :strict + + def initialize(block, strict = false) + @block = block + @strict = strict + super() + end + + class Dispatcher < ActionDispatch::Routing::RouteSet::Dispatcher + def initialize(defaults, set, block) + super(defaults) + @block = block + @set = set + end + + def controller(params, default_controller=true) + super(params, @set.strict) + end + + def controller_reference(controller_param) + block = @block + set = @set + super if @set.strict + Class.new(ActionController::Base) { + include set.url_helpers + define_method(:process) { |name| block.call(self) } + def to_a; [200, {}, []]; end + } + end + end + + def dispatcher defaults + TestSet::Dispatcher.new defaults, self, @block + end end end @@ -331,7 +415,6 @@ class ThreadsController < ResourcesController; end class MessagesController < ResourcesController; end class CommentsController < ResourcesController; end class ReviewsController < ResourcesController; end -class AuthorsController < ResourcesController; end class LogosController < ResourcesController; end class AccountsController < ResourcesController; end @@ -342,8 +425,6 @@ class PreferencesController < ResourcesController; end module Backoffice class ProductsController < ResourcesController; end - class TagsController < ResourcesController; end - class ManufacturersController < ResourcesController; end class ImagesController < ResourcesController; end module Admin @@ -351,3 +432,90 @@ module Backoffice class ImagesController < ResourcesController; end end end + +# Skips the current run on Rubinius using Minitest::Assertions#skip +def rubinius_skip(message = '') + skip message if RUBY_ENGINE == 'rbx' +end +# Skips the current run on JRuby using Minitest::Assertions#skip +def jruby_skip(message = '') + skip message if defined?(JRUBY_VERSION) +end + +require 'mocha/setup' # FIXME: stop using mocha + +class ForkingExecutor + class Server + include DRb::DRbUndumped + + def initialize + @queue = Queue.new + end + + def record reporter, result + reporter.record result + end + + def << o + o[2] = DRbObject.new(o[2]) if o + @queue << o + end + def pop; @queue.pop; end + end + + def initialize size + @size = size + @queue = Server.new + file = File.join Dir.tmpdir, Dir::Tmpname.make_tmpname('rails-tests', 'fd') + @url = "drbunix://#{file}" + @pool = nil + DRb.start_service @url, @queue + end + + def << work; @queue << work; end + + def shutdown + pool = @size.times.map { + fork { + DRb.stop_service + queue = DRbObject.new_with_uri @url + while job = queue.pop + klass = job[0] + method = job[1] + reporter = job[2] + result = Minitest.run_one_method klass, method + if result.error? + translate_exceptions result + end + queue.record reporter, result + end + } + } + @size.times { @queue << nil } + pool.each { |pid| Process.waitpid pid } + end + + private + def translate_exceptions(result) + result.failures.map! { |e| + begin + Marshal.dump e + e + rescue TypeError + ex = Exception.new e.message + ex.set_backtrace e.backtrace + Minitest::UnexpectedError.new ex + end + } + end +end + +if ActiveSupport::Testing::Isolation.forking_env? && PROCESS_COUNT > 0 + # Use N processes (N defaults to 4) + Minitest.parallel_executor = ForkingExecutor.new(PROCESS_COUNT) +end + +# FIXME: we have tests that depend on run order, we should fix that and +# remove this method call. +require 'active_support/test_case' +ActiveSupport::TestCase.test_order = :sorted diff --git a/actionpack/test/assertions/response_assertions_test.rb b/actionpack/test/assertions/response_assertions_test.rb index 8eec98e916..5e64cae7e2 100644 --- a/actionpack/test/assertions/response_assertions_test.rb +++ b/actionpack/test/assertions/response_assertions_test.rb @@ -19,7 +19,7 @@ module ActionDispatch @response = FakeResponse.new sym assert_response sym - assert_raises(MiniTest::Assertion) { + assert_raises(Minitest::Assertion) { assert_response :unauthorized } end @@ -29,11 +29,11 @@ module ActionDispatch @response = FakeResponse.new 400 assert_response 400 - assert_raises(MiniTest::Assertion) { + assert_raises(Minitest::Assertion) { assert_response :unauthorized } - assert_raises(MiniTest::Assertion) { + assert_raises(Minitest::Assertion) { assert_response 500 } end @@ -42,11 +42,11 @@ module ActionDispatch @response = FakeResponse.new 401 assert_response :unauthorized - assert_raises(MiniTest::Assertion) { + assert_raises(Minitest::Assertion) { assert_response :ok } - assert_raises(MiniTest::Assertion) { + assert_raises(Minitest::Assertion) { assert_response :success } end diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index ba4cffcd3e..d0dfbfbc74 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -1,5 +1,4 @@ require 'abstract_unit' -require 'action_view/vendor/html-scanner' require 'controller/fake_controllers' class ActionPackAssertionsController < ActionController::Base @@ -147,11 +146,6 @@ end class ActionPackAssertionsControllerTest < ActionController::TestCase - def test_assert_tag_and_url_for - get :render_url - assert_tag :content => "/action_pack_assertions/flash_me" - end - def test_render_file_absolute_path get :render_file_absolute_path assert_match(/\A= Action Pack/, @response.body) @@ -165,24 +159,24 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_get_request assert_raise(RuntimeError) { get :raise_exception_on_get } get :raise_exception_on_post - assert_equal @response.body, 'request method: GET' + assert_equal 'request method: GET', @response.body end def test_post_request assert_raise(RuntimeError) { post :raise_exception_on_post } post :raise_exception_on_get - assert_equal @response.body, 'request method: POST' + assert_equal 'request method: POST', @response.body end def test_get_post_request_switch post :raise_exception_on_get - assert_equal @response.body, 'request method: POST' + assert_equal 'request method: POST', @response.body get :raise_exception_on_post - assert_equal @response.body, 'request method: GET' + assert_equal 'request method: GET', @response.body post :raise_exception_on_get - assert_equal @response.body, 'request method: POST' + assert_equal 'request method: POST', @response.body get :raise_exception_on_post - assert_equal @response.body, 'request method: GET' + assert_equal 'request method: GET', @response.body end def test_string_constraint @@ -302,7 +296,7 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_session_exist process :session_stuffing - assert_equal session['xmas'], 'turkey' + assert_equal 'turkey', session['xmas'] end def session_does_not_exist @@ -444,22 +438,18 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_assert_response_uses_exception_message @controller = AssertResponseWithUnexpectedErrorController.new - get :index + e = assert_raise RuntimeError, 'Expected non-success response' do + get :index + end assert_response :success - flunk 'Expected non-success response' - rescue RuntimeError => e - assert e.message.include?('FAIL') + assert_includes 'FAIL', e.message end def test_assert_response_failure_response_with_no_exception @controller = AssertResponseWithUnexpectedErrorController.new get :show - assert_response :success - flunk 'Expected non-success response' - rescue ActiveSupport::TestCase::Assertion - # success - rescue - flunk "assert_response failed to handle failure response with missing, but optional, exception." + assert_response 500 + assert_equal 'Boom', response.body end end @@ -492,6 +482,11 @@ class AssertTemplateTest < ActionController::TestCase assert_raise(ActiveSupport::TestCase::Assertion) do assert_template :file => 'test/hello_world' end + + get :render_file_absolute_path + assert_raise(ActiveSupport::TestCase::Assertion) do + assert_template file: nil + end end def test_with_nil_passes_when_no_template_rendered @@ -580,6 +575,13 @@ class AssertTemplateTest < ActionController::TestCase end end + def test_fails_expecting_not_known_layout + get :render_with_layout + assert_raise(ArgumentError) do + assert_template :layout => 1 + end + end + def test_passes_with_correct_layout get :render_with_layout assert_template :layout => "layouts/standard" @@ -616,6 +618,24 @@ class AssertTemplateTest < ActionController::TestCase get :nothing assert_template nil + + get :partial + assert_template partial: 'test/_partial' + + get :nothing + assert_template partial: nil + + get :render_with_layout + assert_template layout: 'layouts/standard' + + get :nothing + assert_template layout: nil + + get :render_file_relative_path + assert_template file: 'README.rdoc' + + get :nothing + assert_template file: nil end end diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb deleted file mode 100644 index 114bbf3c22..0000000000 --- a/actionpack/test/controller/assert_select_test.rb +++ /dev/null @@ -1,351 +0,0 @@ -# encoding: utf-8 -#-- -# Copyright (c) 2006 Assaf Arkin (http://labnotes.org) -# Under MIT and/or CC By license. -#++ - -require 'abstract_unit' -require 'controller/fake_controllers' - -require 'action_mailer' -require 'action_view' - -ActionMailer::Base.send(:include, ActionView::Layouts) -ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH - -class AssertSelectTest < ActionController::TestCase - Assertion = ActiveSupport::TestCase::Assertion - - class AssertSelectMailer < ActionMailer::Base - def test(html) - mail :body => html, :content_type => "text/html", - :subject => "Test e-mail", :from => "test@test.host", :to => "test <test@test.host>" - end - end - - class AssertMultipartSelectMailer < ActionMailer::Base - def test(options) - mail :subject => "Test e-mail", :from => "test@test.host", :to => "test <test@test.host>" do |format| - format.text { render :text => options[:text] } - format.html { render :text => options[:html] } - end - end - end - - class AssertSelectController < ActionController::Base - def response_with=(content) - @content = content - end - - def response_with(&block) - @update = block - end - - def html() - render :text=>@content, :layout=>false, :content_type=>Mime::HTML - @content = nil - end - - def xml() - render :text=>@content, :layout=>false, :content_type=>Mime::XML - @content = nil - end - end - - tests AssertSelectController - - def setup - super - ActionMailer::Base.delivery_method = :test - ActionMailer::Base.perform_deliveries = true - ActionMailer::Base.deliveries = [] - end - - def teardown - super - ActionMailer::Base.deliveries.clear - end - - def assert_failure(message, &block) - e = assert_raise(Assertion, &block) - assert_match(message, e.message) if Regexp === message - assert_equal(message, e.message) if String === message - end - - # - # Test assert select. - # - - def test_assert_select - render_html %Q{<div id="1"></div><div id="2"></div>} - assert_select "div", 2 - assert_failure(/Expected at least 1 element matching \"p\", found 0/) { assert_select "p" } - end - - def test_equality_integer - render_html %Q{<div id="1"></div><div id="2"></div>} - assert_failure(/Expected exactly 3 elements matching \"div\", found 2/) { assert_select "div", 3 } - assert_failure(/Expected exactly 0 elements matching \"div\", found 2/) { assert_select "div", 0 } - end - - def test_equality_true_false - render_html %Q{<div id="1"></div><div id="2"></div>} - assert_nothing_raised { assert_select "div" } - assert_raise(Assertion) { assert_select "p" } - assert_nothing_raised { assert_select "div", true } - assert_raise(Assertion) { assert_select "p", true } - assert_raise(Assertion) { assert_select "div", false } - assert_nothing_raised { assert_select "p", false } - end - - def test_equality_false_message - render_html %Q{<div id="1"></div><div id="2"></div>} - assert_failure(/Expected exactly 0 elements matching \"div\", found 2/) { assert_select "div", false } - end - - def test_equality_string_and_regexp - render_html %Q{<div id="1">foo</div><div id="2">foo</div>} - assert_nothing_raised { assert_select "div", "foo" } - assert_raise(Assertion) { assert_select "div", "bar" } - assert_nothing_raised { assert_select "div", :text=>"foo" } - assert_raise(Assertion) { assert_select "div", :text=>"bar" } - assert_nothing_raised { assert_select "div", /(foo|bar)/ } - assert_raise(Assertion) { assert_select "div", /foobar/ } - assert_nothing_raised { assert_select "div", :text=>/(foo|bar)/ } - assert_raise(Assertion) { assert_select "div", :text=>/foobar/ } - assert_raise(Assertion) { assert_select "p", :text=>/foobar/ } - end - - def test_equality_of_html - render_html %Q{<p>\n<em>"This is <strong>not</strong> a big problem,"</em> he said.\n</p>} - text = "\"This is not a big problem,\" he said." - html = "<em>\"This is <strong>not</strong> a big problem,\"</em> he said." - assert_nothing_raised { assert_select "p", text } - assert_raise(Assertion) { assert_select "p", html } - assert_nothing_raised { assert_select "p", :html=>html } - assert_raise(Assertion) { assert_select "p", :html=>text } - # No stripping for pre. - render_html %Q{<pre>\n<em>"This is <strong>not</strong> a big problem,"</em> he said.\n</pre>} - text = "\n\"This is not a big problem,\" he said.\n" - html = "\n<em>\"This is <strong>not</strong> a big problem,\"</em> he said.\n" - assert_nothing_raised { assert_select "pre", text } - assert_raise(Assertion) { assert_select "pre", html } - assert_nothing_raised { assert_select "pre", :html=>html } - assert_raise(Assertion) { assert_select "pre", :html=>text } - end - - def test_strip_textarea - render_html %Q{<textarea>\n\nfoo\n</textarea>} - assert_select "textarea", "\nfoo\n" - render_html %Q{<textarea>\nfoo</textarea>} - assert_select "textarea", "foo" - end - - def test_counts - render_html %Q{<div id="1">foo</div><div id="2">foo</div>} - assert_nothing_raised { assert_select "div", 2 } - assert_failure(/Expected exactly 3 elements matching \"div\", found 2/) do - assert_select "div", 3 - end - assert_nothing_raised { assert_select "div", 1..2 } - assert_failure(/Expected between 3 and 4 elements matching \"div\", found 2/) do - assert_select "div", 3..4 - end - assert_nothing_raised { assert_select "div", :count=>2 } - assert_failure(/Expected exactly 3 elements matching \"div\", found 2/) do - assert_select "div", :count=>3 - end - assert_nothing_raised { assert_select "div", :minimum=>1 } - assert_nothing_raised { assert_select "div", :minimum=>2 } - assert_failure(/Expected at least 3 elements matching \"div\", found 2/) do - assert_select "div", :minimum=>3 - end - assert_nothing_raised { assert_select "div", :maximum=>2 } - assert_nothing_raised { assert_select "div", :maximum=>3 } - assert_failure(/Expected at most 1 element matching \"div\", found 2/) do - assert_select "div", :maximum=>1 - end - assert_nothing_raised { assert_select "div", :minimum=>1, :maximum=>2 } - assert_failure(/Expected between 3 and 4 elements matching \"div\", found 2/) do - assert_select "div", :minimum=>3, :maximum=>4 - end - end - - def test_substitution_values - render_html %Q{<div id="1">foo</div><div id="2">foo</div>} - assert_select "div#?", /\d+/ do |elements| - assert_equal 2, elements.size - end - assert_select "div" do - assert_select "div#?", /\d+/ do |elements| - assert_equal 2, elements.size - assert_select "#1" - assert_select "#2" - end - end - end - - def test_nested_assert_select - render_html %Q{<div id="1">foo</div><div id="2">foo</div>} - assert_select "div" do |elements| - assert_equal 2, elements.size - assert_select elements[0], "#1" - assert_select elements[1], "#2" - end - assert_select "div" do - assert_select "div" do |elements| - assert_equal 2, elements.size - # Testing in a group is one thing - assert_select "#1,#2" - # Testing individually is another. - assert_select "#1" - assert_select "#2" - assert_select "#3", false - end - end - - assert_failure(/Expected at least 1 element matching \"#4\", found 0\./) do - assert_select "div" do - assert_select "#4" - end - end - end - - def test_assert_select_text_match - render_html %Q{<div id="1"><span>foo</span></div><div id="2"><span>bar</span></div>} - assert_select "div" do - assert_nothing_raised { assert_select "div", "foo" } - assert_nothing_raised { assert_select "div", "bar" } - assert_nothing_raised { assert_select "div", /\w*/ } - assert_nothing_raised { assert_select "div", :text => /\w*/, :count=>2 } - assert_raise(Assertion) { assert_select "div", :text=>"foo", :count=>2 } - assert_nothing_raised { assert_select "div", :html=>"<span>bar</span>" } - assert_nothing_raised { assert_select "div", :html=>"<span>bar</span>" } - assert_nothing_raised { assert_select "div", :html=>/\w*/ } - assert_nothing_raised { assert_select "div", :html=>/\w*/, :count=>2 } - assert_raise(Assertion) { assert_select "div", :html=>"<span>foo</span>", :count=>2 } - end - end - - def test_elect_with_xml_namespace_attributes - render_html %Q{<link xlink:href="http://nowhere.com"></link>} - assert_nothing_raised { assert_select "link[xlink:href=http://nowhere.com]" } - end - - # - # Test css_select. - # - - def test_css_select - render_html %Q{<div id="1"></div><div id="2"></div>} - assert_equal 2, css_select("div").size - assert_equal 0, css_select("p").size - end - - def test_nested_css_select - render_html %Q{<div id="1">foo</div><div id="2">foo</div>} - assert_select "div#?", /\d+/ do |elements| - assert_equal 1, css_select(elements[0], "div").size - assert_equal 1, css_select(elements[1], "div").size - end - assert_select "div" do - assert_equal 2, css_select("div").size - css_select("div").each do |element| - # Testing as a group is one thing - assert !css_select("#1,#2").empty? - # Testing individually is another - assert !css_select("#1").empty? - assert !css_select("#2").empty? - end - end - end - - def test_feed_item_encoded - render_xml <<-EOF -<rss version="2.0"> - <channel> - <item> - <description> - <![CDATA[ - <p>Test 1</p> - ]]> - </description> - </item> - <item> - <description> - <![CDATA[ - <p>Test 2</p> - ]]> - </description> - </item> - </channel> -</rss> -EOF - assert_select "channel item description" do - # Test element regardless of wrapper. - assert_select_encoded do - assert_select "p", :count=>2, :text=>/Test/ - end - # Test through encoded wrapper. - assert_select_encoded do - assert_select "encoded p", :count=>2, :text=>/Test/ - end - # Use :root instead (recommended) - assert_select_encoded do - assert_select ":root p", :count=>2, :text=>/Test/ - end - # Test individually. - assert_select "description" do |elements| - assert_select_encoded elements[0] do - assert_select "p", "Test 1" - end - assert_select_encoded elements[1] do - assert_select "p", "Test 2" - end - end - end - - # Test that we only un-encode element itself. - assert_select "channel item" do - assert_select_encoded do - assert_select "p", 0 - end - end - end - - # - # Test assert_select_email - # - - def test_assert_select_email - assert_raise(Assertion) { assert_select_email {} } - 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" - assert_select "p:last-child", "bar" - end - end - end - - def test_assert_select_email_multipart - AssertMultipartSelectMailer.test(:html => "<div><p>foo</p><p>bar</p></div>", :text => 'foo bar').deliver - assert_select_email do - assert_select "div:root" do - assert_select "p:first-child", "foo" - assert_select "p:last-child", "bar" - end - end - end - - protected - def render_html(html) - @controller.response_with = html - get :html - end - - def render_xml(xml) - @controller.response_with = xml - get :xml - end -end diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb index b2bfdae174..950788743e 100644 --- a/actionpack/test/controller/base_test.rb +++ b/actionpack/test/controller/base_test.rb @@ -158,7 +158,7 @@ class PerformActionTest < ActionController::TestCase exception = assert_raise AbstractController::ActionNotFound do get :non_existent end - assert_equal exception.message, "The action 'non_existent' could not be found for EmptyController" + assert_equal "The action 'non_existent' could not be found for EmptyController", exception.message end def test_get_on_hidden_should_fail diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 57b45b8f7b..c0e6a2ebd1 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -164,6 +164,13 @@ class FunctionalCachingController < CachingController end end + def formatted_fragment_cached_with_variant + respond_to do |format| + format.html.phone + format.html + end + end + def fragment_cached_without_digest end end @@ -190,7 +197,7 @@ CACHED assert_equal expected_body, @response.body assert_equal "This bit's fragment cached", - @store.read("views/test.host/functional_caching/fragment_cached/#{template_digest("functional_caching/fragment_cached", "html")}") + @store.read("views/test.host/functional_caching/fragment_cached/#{template_digest("functional_caching/fragment_cached")}") end def test_fragment_caching_in_partials @@ -199,7 +206,7 @@ CACHED assert_match(/Old fragment caching in a partial/, @response.body) assert_match("Old fragment caching in a partial", - @store.read("views/test.host/functional_caching/html_fragment_cached_with_partial/#{template_digest("functional_caching/_partial", "html")}")) + @store.read("views/test.host/functional_caching/html_fragment_cached_with_partial/#{template_digest("functional_caching/_partial")}")) end def test_skipping_fragment_cache_digesting @@ -217,7 +224,23 @@ CACHED assert_match(/Some inline content/, @response.body) assert_match(/Some cached content/, @response.body) assert_match("Some cached content", - @store.read("views/test.host/functional_caching/inline_fragment_cached/#{template_digest("functional_caching/inline_fragment_cached", "html")}")) + @store.read("views/test.host/functional_caching/inline_fragment_cached/#{template_digest("functional_caching/inline_fragment_cached")}")) + end + + def test_fragment_cache_instrumentation + payload = nil + + subscriber = proc do |*args| + event = ActiveSupport::Notifications::Event.new(*args) + payload = event.payload + end + + ActiveSupport::Notifications.subscribed(subscriber, "read_fragment.action_controller") do + get :inline_fragment_cached + end + + assert_equal "functional_caching", payload[:controller] + assert_equal "inline_fragment_cached", payload[:action] end def test_html_formatted_fragment_caching @@ -228,7 +251,7 @@ CACHED assert_equal expected_body, @response.body assert_equal "<p>ERB</p>", - @store.read("views/test.host/functional_caching/formatted_fragment_cached/#{template_digest("functional_caching/formatted_fragment_cached", "html")}") + @store.read("views/test.host/functional_caching/formatted_fragment_cached/#{template_digest("functional_caching/formatted_fragment_cached")}") end def test_xml_formatted_fragment_caching @@ -239,12 +262,26 @@ CACHED assert_equal expected_body, @response.body assert_equal " <p>Builder</p>\n", - @store.read("views/test.host/functional_caching/formatted_fragment_cached/#{template_digest("functional_caching/formatted_fragment_cached", "xml")}") + @store.read("views/test.host/functional_caching/formatted_fragment_cached/#{template_digest("functional_caching/formatted_fragment_cached")}") + end + + + def test_fragment_caching_with_variant + @request.variant = :phone + + get :formatted_fragment_cached_with_variant, :format => "html" + assert_response :success + expected_body = "<body>\n<p>PHONE</p>\n</body>\n" + + assert_equal expected_body, @response.body + + assert_equal "<p>PHONE</p>", + @store.read("views/test.host/functional_caching/formatted_fragment_cached_with_variant/#{template_digest("functional_caching/formatted_fragment_cached_with_variant")}") end private - def template_digest(name, format) - ActionView::Digestor.digest(name, format, @controller.lookup_context) + def template_digest(name) + ActionView::Digestor.digest(name: name, finder: @controller.lookup_context) end end diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb index 03d5d27cf4..89667df3a4 100644 --- a/actionpack/test/controller/content_type_test.rb +++ b/actionpack/test/controller/content_type_test.rb @@ -68,12 +68,11 @@ class ContentTypeTest < ActionController::TestCase end def test_render_changed_charset_default - ActionDispatch::Response.default_charset = "utf-16" - get :render_defaults - assert_equal "utf-16", @response.charset - assert_equal Mime::HTML, @response.content_type - ensure - ActionDispatch::Response.default_charset = "utf-8" + with_default_charset "utf-16" do + get :render_defaults + assert_equal "utf-16", @response.charset + assert_equal Mime::HTML, @response.content_type + end end # :ported: @@ -105,12 +104,11 @@ class ContentTypeTest < ActionController::TestCase end def test_nil_default_for_erb - ActionDispatch::Response.default_charset = nil - get :render_default_for_erb - assert_equal Mime::HTML, @response.content_type - assert_nil @response.charset, @response.headers.inspect - ensure - ActionDispatch::Response.default_charset = "utf-8" + with_default_charset nil do + get :render_default_for_erb + assert_equal Mime::HTML, @response.content_type + assert_nil @response.charset, @response.headers.inspect + end end def test_default_for_erb @@ -130,6 +128,16 @@ class ContentTypeTest < ActionController::TestCase assert_equal Mime::HTML, @response.content_type assert_equal "utf-8", @response.charset end + + private + + def with_default_charset(charset) + old_default_charset = ActionDispatch::Response.default_charset + ActionDispatch::Response.default_charset = charset + yield + ensure + ActionDispatch::Response.default_charset = old_default_charset + end end class AcceptBasedContentTypeTest < ActionController::TestCase diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb index 3b5d7ef446..2e08a6af9f 100644 --- a/actionpack/test/controller/filters_test.rb +++ b/actionpack/test/controller/filters_test.rb @@ -2,15 +2,15 @@ require 'abstract_unit' class ActionController::Base class << self - %w(append_around_filter prepend_after_filter prepend_around_filter prepend_before_filter skip_after_filter skip_before_filter skip_filter).each do |pending| + %w(append_around_action prepend_after_action prepend_around_action prepend_before_action skip_after_action skip_before_action skip_action_callback).each do |pending| define_method(pending) do |*args| $stderr.puts "#{pending} unimplemented: #{args.inspect}" end unless method_defined?(pending) end - def before_filters + def before_actions filters = _process_action_callbacks.select { |c| c.kind == :before } - filters.map! { |c| c.raw_filter } + filters.map!(&:raw_filter) end end @@ -28,8 +28,8 @@ end class FilterTest < ActionController::TestCase class TestController < ActionController::Base - before_filter :ensure_login - after_filter :clean_up + before_action :ensure_login + after_action :clean_up def show render :inline => "ran action" @@ -42,13 +42,13 @@ class FilterTest < ActionController::TestCase end def clean_up - @ran_after_filter ||= [] - @ran_after_filter << "clean_up" + @ran_after_action ||= [] + @ran_after_action << "clean_up" end end class ChangingTheRequirementsController < TestController - before_filter :ensure_login, :except => [:go_wild] + before_action :ensure_login, :except => [:go_wild] def go_wild render :text => "gobble" @@ -56,9 +56,9 @@ class FilterTest < ActionController::TestCase end class TestMultipleFiltersController < ActionController::Base - before_filter :try_1 - before_filter :try_2 - before_filter :try_3 + before_action :try_1 + before_action :try_2 + before_action :try_3 (1..3).each do |i| define_method "fail_#{i}" do @@ -78,8 +78,8 @@ class FilterTest < ActionController::TestCase end class RenderingController < ActionController::Base - before_filter :before_filter_rendering - after_filter :unreached_after_filter + before_action :before_action_rendering + after_action :unreached_after_action def show @ran_action = true @@ -87,29 +87,29 @@ class FilterTest < ActionController::TestCase end private - def before_filter_rendering + def before_action_rendering @ran_filter ||= [] - @ran_filter << "before_filter_rendering" + @ran_filter << "before_action_rendering" render :inline => "something else" end - def unreached_after_filter - @ran_filter << "unreached_after_filter_after_render" + def unreached_after_action + @ran_filter << "unreached_after_action_after_render" end end - class RenderingForPrependAfterFilterController < RenderingController - prepend_after_filter :unreached_prepend_after_filter + class RenderingForPrependAfterActionController < RenderingController + prepend_after_action :unreached_prepend_after_action private - def unreached_prepend_after_filter - @ran_filter << "unreached_preprend_after_filter_after_render" + def unreached_prepend_after_action + @ran_filter << "unreached_preprend_after_action_after_render" end end - class BeforeFilterRedirectionController < ActionController::Base - before_filter :before_filter_redirects - after_filter :unreached_after_filter + class BeforeActionRedirectionController < ActionController::Base + before_action :before_action_redirects + after_action :unreached_after_action def show @ran_action = true @@ -122,23 +122,23 @@ class FilterTest < ActionController::TestCase end private - def before_filter_redirects + def before_action_redirects @ran_filter ||= [] - @ran_filter << "before_filter_redirects" + @ran_filter << "before_action_redirects" redirect_to(:action => 'target_of_redirection') end - def unreached_after_filter - @ran_filter << "unreached_after_filter_after_redirection" + def unreached_after_action + @ran_filter << "unreached_after_action_after_redirection" end end - class BeforeFilterRedirectionForPrependAfterFilterController < BeforeFilterRedirectionController - prepend_after_filter :unreached_prepend_after_filter_after_redirection + class BeforeActionRedirectionForPrependAfterActionController < BeforeActionRedirectionController + prepend_after_action :unreached_prepend_after_action_after_redirection private - def unreached_prepend_after_filter_after_redirection - @ran_filter << "unreached_prepend_after_filter_after_redirection" + def unreached_prepend_after_action_after_redirection + @ran_filter << "unreached_prepend_after_action_after_redirection" end end @@ -151,8 +151,8 @@ class FilterTest < ActionController::TestCase render :inline => "ran action" end - def show_without_filter - render :inline => "ran action without filter" + def show_without_action + render :inline => "ran action without action" end private @@ -168,66 +168,70 @@ class FilterTest < ActionController::TestCase end class ConditionalCollectionFilterController < ConditionalFilterController - before_filter :ensure_login, :except => [ :show_without_filter, :another_action ] + before_action :ensure_login, :except => [ :show_without_action, :another_action ] end class OnlyConditionSymController < ConditionalFilterController - before_filter :ensure_login, :only => :show + before_action :ensure_login, :only => :show end class ExceptConditionSymController < ConditionalFilterController - before_filter :ensure_login, :except => :show_without_filter + before_action :ensure_login, :except => :show_without_action end class BeforeAndAfterConditionController < ConditionalFilterController - before_filter :ensure_login, :only => :show - after_filter :clean_up_tmp, :only => :show + before_action :ensure_login, :only => :show + after_action :clean_up_tmp, :only => :show end class OnlyConditionProcController < ConditionalFilterController - before_filter(:only => :show) {|c| c.instance_variable_set(:"@ran_proc_filter", true) } + before_action(:only => :show) {|c| c.instance_variable_set(:"@ran_proc_action", true) } end class ExceptConditionProcController < ConditionalFilterController - before_filter(:except => :show_without_filter) {|c| c.instance_variable_set(:"@ran_proc_filter", true) } + before_action(:except => :show_without_action) {|c| c.instance_variable_set(:"@ran_proc_action", true) } end class ConditionalClassFilter - def self.before(controller) controller.instance_variable_set(:"@ran_class_filter", true) end + def self.before(controller) controller.instance_variable_set(:"@ran_class_action", true) end end class OnlyConditionClassController < ConditionalFilterController - before_filter ConditionalClassFilter, :only => :show + before_action ConditionalClassFilter, :only => :show end class ExceptConditionClassController < ConditionalFilterController - before_filter ConditionalClassFilter, :except => :show_without_filter + before_action ConditionalClassFilter, :except => :show_without_action end class AnomolousYetValidConditionController < ConditionalFilterController - before_filter(ConditionalClassFilter, :ensure_login, Proc.new {|c| c.instance_variable_set(:"@ran_proc_filter1", true)}, :except => :show_without_filter) { |c| c.instance_variable_set(:"@ran_proc_filter2", true)} + before_action(ConditionalClassFilter, :ensure_login, Proc.new {|c| c.instance_variable_set(:"@ran_proc_action1", true)}, :except => :show_without_action) { |c| c.instance_variable_set(:"@ran_proc_action2", true)} end class OnlyConditionalOptionsFilter < ConditionalFilterController - before_filter :ensure_login, :only => :index, :if => Proc.new {|c| c.instance_variable_set(:"@ran_conditional_index_proc", true) } + before_action :ensure_login, :only => :index, :if => Proc.new {|c| c.instance_variable_set(:"@ran_conditional_index_proc", true) } end class ConditionalOptionsFilter < ConditionalFilterController - before_filter :ensure_login, :if => Proc.new { |c| true } - before_filter :clean_up_tmp, :if => Proc.new { |c| false } + before_action :ensure_login, :if => Proc.new { |c| true } + before_action :clean_up_tmp, :if => Proc.new { |c| false } end class ConditionalOptionsSkipFilter < ConditionalFilterController - before_filter :ensure_login - before_filter :clean_up_tmp + before_action :ensure_login + before_action :clean_up_tmp - skip_before_filter :ensure_login, if: -> { false } - skip_before_filter :clean_up_tmp, if: -> { true } + skip_before_action :ensure_login, if: -> { false } + skip_before_action :clean_up_tmp, if: -> { true } + end + + class ClassController < ConditionalFilterController + before_action ConditionalClassFilter end class PrependingController < TestController - prepend_before_filter :wonderful_life - # skip_before_filter :fire_flash + prepend_before_action :wonderful_life + # skip_before_action :fire_flash private def wonderful_life @@ -237,8 +241,8 @@ class FilterTest < ActionController::TestCase end class SkippingAndLimitedController < TestController - skip_before_filter :ensure_login - before_filter :ensure_login, :only => :index + skip_before_action :ensure_login + before_action :ensure_login, :only => :index def index render :text => 'ok' @@ -250,9 +254,9 @@ class FilterTest < ActionController::TestCase end class SkippingAndReorderingController < TestController - skip_before_filter :ensure_login - before_filter :find_record - before_filter :ensure_login + skip_before_action :ensure_login + before_action :find_record + before_action :ensure_login def index render :text => 'ok' @@ -266,10 +270,10 @@ class FilterTest < ActionController::TestCase end class ConditionalSkippingController < TestController - skip_before_filter :ensure_login, :only => [ :login ] - skip_after_filter :clean_up, :only => [ :login ] + skip_before_action :ensure_login, :only => [ :login ] + skip_after_action :clean_up, :only => [ :login ] - before_filter :find_user, :only => [ :change_password ] + before_action :find_user, :only => [ :change_password ] def login render :inline => "ran action" @@ -287,8 +291,8 @@ class FilterTest < ActionController::TestCase end class ConditionalParentOfConditionalSkippingController < ConditionalFilterController - before_filter :conditional_in_parent_before, :only => [:show, :another_action] - after_filter :conditional_in_parent_after, :only => [:show, :another_action] + before_action :conditional_in_parent_before, :only => [:show, :another_action] + after_action :conditional_in_parent_after, :only => [:show, :another_action] private @@ -304,20 +308,20 @@ class FilterTest < ActionController::TestCase end class ChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController - skip_before_filter :conditional_in_parent_before, :only => :another_action - skip_after_filter :conditional_in_parent_after, :only => :another_action + skip_before_action :conditional_in_parent_before, :only => :another_action + skip_after_action :conditional_in_parent_after, :only => :another_action end class AnotherChildOfConditionalParentController < ConditionalParentOfConditionalSkippingController - skip_before_filter :conditional_in_parent_before, :only => :show + skip_before_action :conditional_in_parent_before, :only => :show end class ProcController < PrependingController - before_filter(proc { |c| c.instance_variable_set(:"@ran_proc_filter", true) }) + before_action(proc { |c| c.instance_variable_set(:"@ran_proc_action", true) }) end class ImplicitProcController < PrependingController - before_filter { |c| c.instance_variable_set(:"@ran_proc_filter", true) } + before_action { |c| c.instance_variable_set(:"@ran_proc_action", true) } end class AuditFilter @@ -363,7 +367,7 @@ class FilterTest < ActionController::TestCase end class AuditController < ActionController::Base - before_filter(AuditFilter) + before_action(AuditFilter) def show render :text => "hello" @@ -371,14 +375,14 @@ class FilterTest < ActionController::TestCase end class AroundFilterController < PrependingController - around_filter AroundFilter.new + around_action AroundFilter.new end class BeforeAfterClassFilterController < PrependingController begin filter = AroundFilter.new - before_filter filter - after_filter filter + before_action filter + after_action filter end end @@ -390,18 +394,18 @@ class FilterTest < ActionController::TestCase super() end - before_filter { |c| c.class.execution_log << " before procfilter " } - prepend_around_filter AroundFilter.new + before_action { |c| c.class.execution_log << " before procfilter " } + prepend_around_action AroundFilter.new - after_filter { |c| c.class.execution_log << " after procfilter " } - append_around_filter AppendedAroundFilter.new + after_action { |c| c.class.execution_log << " after procfilter " } + append_around_action AppendedAroundFilter.new end class MixedSpecializationController < ActionController::Base class OutOfOrder < StandardError; end - before_filter :first - before_filter :second, :only => :foo + before_action :first + before_action :second, :only => :foo def foo render :text => 'foo' @@ -422,7 +426,7 @@ class FilterTest < ActionController::TestCase end class DynamicDispatchController < ActionController::Base - before_filter :choose + before_action :choose %w(foo bar baz).each do |action| define_method(action) { render :text => action } @@ -435,9 +439,9 @@ class FilterTest < ActionController::TestCase end class PrependingBeforeAndAfterController < ActionController::Base - prepend_before_filter :before_all - prepend_after_filter :after_all - before_filter :between_before_all_and_after_all + prepend_before_action :before_all + prepend_after_action :after_all + before_action :between_before_all_and_after_all def before_all @ran_filter ||= [] @@ -469,7 +473,7 @@ class FilterTest < ActionController::TestCase end class RescuedController < ActionController::Base - around_filter RescuingAroundFilterWithBlock.new + around_action RescuingAroundFilterWithBlock.new def show raise ErrorToRescue.new("Something made the bad noise.") @@ -478,10 +482,10 @@ class FilterTest < ActionController::TestCase class NonYieldingAroundFilterController < ActionController::Base - before_filter :filter_one - around_filter :non_yielding_filter - before_filter :filter_two - after_filter :filter_three + before_action :filter_one + around_action :non_yielding_action + before_action :action_two + after_action :action_three def index render :inline => "index" @@ -494,24 +498,23 @@ class FilterTest < ActionController::TestCase @filters << "filter_one" end - def filter_two - @filters << "filter_two" + def action_two + @filters << "action_two" end - def non_yielding_filter + def non_yielding_action @filters << "it didn't yield" - @filter_return_value end - def filter_three - @filters << "filter_three" + def action_three + @filters << "action_three" end end class ImplicitActionsController < ActionController::Base - before_filter :find_only, :only => :edit - before_filter :find_except, :except => :edit + before_action :find_only, :only => :edit + before_action :find_except, :except => :edit private @@ -524,80 +527,63 @@ class FilterTest < ActionController::TestCase end end - def test_non_yielding_around_filters_not_returning_false_do_not_raise - controller = NonYieldingAroundFilterController.new - controller.instance_variable_set "@filter_return_value", true - assert_nothing_raised do - test_process(controller, "index") - end - end - - def test_non_yielding_around_filters_returning_false_do_not_raise + def test_non_yielding_around_actions_do_not_raise controller = NonYieldingAroundFilterController.new - controller.instance_variable_set "@filter_return_value", false assert_nothing_raised do test_process(controller, "index") end end - def test_after_filters_are_not_run_if_around_filter_returns_false + def test_after_actions_are_not_run_if_around_action_does_not_yield controller = NonYieldingAroundFilterController.new - controller.instance_variable_set "@filter_return_value", false test_process(controller, "index") assert_equal ["filter_one", "it didn't yield"], controller.assigns['filters'] end - def test_after_filters_are_not_run_if_around_filter_does_not_yield - controller = NonYieldingAroundFilterController.new - controller.instance_variable_set "@filter_return_value", true - test_process(controller, "index") - assert_equal ["filter_one", "it didn't yield"], controller.assigns['filters'] - end - - def test_added_filter_to_inheritance_graph - assert_equal [ :ensure_login ], TestController.before_filters + def test_added_action_to_inheritance_graph + assert_equal [ :ensure_login ], TestController.before_actions end def test_base_class_in_isolation - assert_equal [ ], ActionController::Base.before_filters + assert_equal [ ], ActionController::Base.before_actions end - def test_prepending_filter - assert_equal [ :wonderful_life, :ensure_login ], PrependingController.before_filters + def test_prepending_action + assert_equal [ :wonderful_life, :ensure_login ], PrependingController.before_actions end - def test_running_filters + def test_running_actions test_process(PrependingController) assert_equal %w( wonderful_life ensure_login ), assigns["ran_filter"] end - def test_running_filters_with_proc + def test_running_actions_with_proc test_process(ProcController) - assert assigns["ran_proc_filter"] + assert assigns["ran_proc_action"] end - def test_running_filters_with_implicit_proc + def test_running_actions_with_implicit_proc test_process(ImplicitProcController) - assert assigns["ran_proc_filter"] + assert assigns["ran_proc_action"] end - def test_running_filters_with_class + def test_running_actions_with_class test_process(AuditController) assert assigns["was_audited"] end - def test_running_anomolous_yet_valid_condition_filters + def test_running_anomolous_yet_valid_condition_actions test_process(AnomolousYetValidConditionController) assert_equal %w( ensure_login ), assigns["ran_filter"] - assert assigns["ran_class_filter"] - assert assigns["ran_proc_filter1"] - assert assigns["ran_proc_filter2"] + assert assigns["ran_class_action"] + assert assigns["ran_proc_action1"] + assert assigns["ran_proc_action2"] - test_process(AnomolousYetValidConditionController, "show_without_filter") + test_process(AnomolousYetValidConditionController, "show_without_action") assert_nil assigns["ran_filter"] - assert !assigns["ran_class_filter"] - assert !assigns["ran_proc_filter1"] - assert !assigns["ran_proc_filter2"] + assert !assigns["ran_class_action"] + assert !assigns["ran_proc_action1"] + assert !assigns["ran_proc_action2"] end def test_running_conditional_options @@ -610,47 +596,59 @@ class FilterTest < ActionController::TestCase assert_equal %w( ensure_login ), assigns["ran_filter"] end - def test_running_collection_condition_filters + def test_skipping_class_actions + test_process(ClassController) + assert_equal true, assigns["ran_class_action"] + + skipping_class_controller = Class.new(ClassController) do + skip_before_action ConditionalClassFilter + end + + test_process(skipping_class_controller) + assert_nil assigns['ran_class_action'] + end + + def test_running_collection_condition_actions test_process(ConditionalCollectionFilterController) assert_equal %w( ensure_login ), assigns["ran_filter"] - test_process(ConditionalCollectionFilterController, "show_without_filter") + test_process(ConditionalCollectionFilterController, "show_without_action") assert_nil assigns["ran_filter"] test_process(ConditionalCollectionFilterController, "another_action") assert_nil assigns["ran_filter"] end - def test_running_only_condition_filters + def test_running_only_condition_actions test_process(OnlyConditionSymController) assert_equal %w( ensure_login ), assigns["ran_filter"] - test_process(OnlyConditionSymController, "show_without_filter") + test_process(OnlyConditionSymController, "show_without_action") assert_nil assigns["ran_filter"] test_process(OnlyConditionProcController) - assert assigns["ran_proc_filter"] - test_process(OnlyConditionProcController, "show_without_filter") - assert !assigns["ran_proc_filter"] + assert assigns["ran_proc_action"] + test_process(OnlyConditionProcController, "show_without_action") + assert !assigns["ran_proc_action"] test_process(OnlyConditionClassController) - assert assigns["ran_class_filter"] - test_process(OnlyConditionClassController, "show_without_filter") - assert !assigns["ran_class_filter"] + assert assigns["ran_class_action"] + test_process(OnlyConditionClassController, "show_without_action") + assert !assigns["ran_class_action"] end - def test_running_except_condition_filters + def test_running_except_condition_actions test_process(ExceptConditionSymController) assert_equal %w( ensure_login ), assigns["ran_filter"] - test_process(ExceptConditionSymController, "show_without_filter") + test_process(ExceptConditionSymController, "show_without_action") assert_nil assigns["ran_filter"] test_process(ExceptConditionProcController) - assert assigns["ran_proc_filter"] - test_process(ExceptConditionProcController, "show_without_filter") - assert !assigns["ran_proc_filter"] + assert assigns["ran_proc_action"] + test_process(ExceptConditionProcController, "show_without_action") + assert !assigns["ran_proc_action"] test_process(ExceptConditionClassController) - assert assigns["ran_class_filter"] - test_process(ExceptConditionClassController, "show_without_filter") - assert !assigns["ran_class_filter"] + assert assigns["ran_class_action"] + test_process(ExceptConditionClassController, "show_without_action") + assert !assigns["ran_class_action"] end def test_running_only_condition_and_conditional_options @@ -658,70 +656,70 @@ class FilterTest < ActionController::TestCase assert_not assigns["ran_conditional_index_proc"] end - def test_running_before_and_after_condition_filters + def test_running_before_and_after_condition_actions test_process(BeforeAndAfterConditionController) assert_equal %w( ensure_login clean_up_tmp), assigns["ran_filter"] - test_process(BeforeAndAfterConditionController, "show_without_filter") + test_process(BeforeAndAfterConditionController, "show_without_action") assert_nil assigns["ran_filter"] end - def test_around_filter + def test_around_action test_process(AroundFilterController) assert assigns["before_ran"] assert assigns["after_ran"] end - def test_before_after_class_filter + def test_before_after_class_action test_process(BeforeAfterClassFilterController) assert assigns["before_ran"] assert assigns["after_ran"] end - def test_having_properties_in_around_filter + def test_having_properties_in_around_action test_process(AroundFilterController) assert_equal "before and after", assigns["execution_log"] end - def test_prepending_and_appending_around_filter + def test_prepending_and_appending_around_action test_process(MixedFilterController) assert_equal " before aroundfilter before procfilter before appended aroundfilter " + " after appended aroundfilter after procfilter after aroundfilter ", MixedFilterController.execution_log end - def test_rendering_breaks_filtering_chain + def test_rendering_breaks_actioning_chain response = test_process(RenderingController) assert_equal "something else", response.body assert !assigns["ran_action"] end - def test_before_filter_rendering_breaks_filtering_chain_for_after_filter + def test_before_action_rendering_breaks_actioning_chain_for_after_action test_process(RenderingController) - assert_equal %w( before_filter_rendering ), assigns["ran_filter"] + assert_equal %w( before_action_rendering ), assigns["ran_filter"] assert !assigns["ran_action"] end - def test_before_filter_redirects_breaks_filtering_chain_for_after_filter - test_process(BeforeFilterRedirectionController) + def test_before_action_redirects_breaks_actioning_chain_for_after_action + test_process(BeforeActionRedirectionController) assert_response :redirect - assert_equal "http://test.host/filter_test/before_filter_redirection/target_of_redirection", redirect_to_url - assert_equal %w( before_filter_redirects ), assigns["ran_filter"] + assert_equal "http://test.host/filter_test/before_action_redirection/target_of_redirection", redirect_to_url + assert_equal %w( before_action_redirects ), assigns["ran_filter"] end - def test_before_filter_rendering_breaks_filtering_chain_for_preprend_after_filter - test_process(RenderingForPrependAfterFilterController) - assert_equal %w( before_filter_rendering ), assigns["ran_filter"] + def test_before_action_rendering_breaks_actioning_chain_for_preprend_after_action + test_process(RenderingForPrependAfterActionController) + assert_equal %w( before_action_rendering ), assigns["ran_filter"] assert !assigns["ran_action"] end - def test_before_filter_redirects_breaks_filtering_chain_for_preprend_after_filter - test_process(BeforeFilterRedirectionForPrependAfterFilterController) + def test_before_action_redirects_breaks_actioning_chain_for_preprend_after_action + test_process(BeforeActionRedirectionForPrependAfterActionController) assert_response :redirect - assert_equal "http://test.host/filter_test/before_filter_redirection_for_prepend_after_filter/target_of_redirection", redirect_to_url - assert_equal %w( before_filter_redirects ), assigns["ran_filter"] + assert_equal "http://test.host/filter_test/before_action_redirection_for_prepend_after_action/target_of_redirection", redirect_to_url + assert_equal %w( before_action_redirects ), assigns["ran_filter"] end - def test_filters_with_mixed_specialization_run_in_order + def test_actions_with_mixed_specialization_run_in_order assert_nothing_raised do response = test_process(MixedSpecializationController, 'bar') assert_equal 'bar', response.body @@ -742,7 +740,7 @@ class FilterTest < ActionController::TestCase end end - def test_running_prepended_before_and_after_filter + def test_running_prepended_before_and_after_action test_process(PrependingBeforeAndAfterController) assert_equal %w( before_all between_before_all_and_after_all after_all ), assigns["ran_filter"] end @@ -759,26 +757,26 @@ class FilterTest < ActionController::TestCase assert_equal %w( find_record ensure_login ), assigns["ran_filter"] end - def test_conditional_skipping_of_filters + def test_conditional_skipping_of_actions test_process(ConditionalSkippingController, "login") assert_nil assigns["ran_filter"] test_process(ConditionalSkippingController, "change_password") assert_equal %w( ensure_login find_user ), assigns["ran_filter"] test_process(ConditionalSkippingController, "login") - assert !@controller.instance_variable_defined?("@ran_after_filter") + assert !@controller.instance_variable_defined?("@ran_after_action") test_process(ConditionalSkippingController, "change_password") - assert_equal %w( clean_up ), @controller.instance_variable_get("@ran_after_filter") + assert_equal %w( clean_up ), @controller.instance_variable_get("@ran_after_action") end - def test_conditional_skipping_of_filters_when_parent_filter_is_also_conditional + def test_conditional_skipping_of_actions_when_parent_action_is_also_conditional test_process(ChildOfConditionalParentController) assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), assigns['ran_filter'] test_process(ChildOfConditionalParentController, 'another_action') assert_nil assigns['ran_filter'] end - def test_condition_skipping_of_filters_when_siblings_also_have_conditions + def test_condition_skipping_of_actions_when_siblings_also_have_conditions test_process(ChildOfConditionalParentController) assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), assigns['ran_filter'] test_process(AnotherChildOfConditionalParentController) @@ -792,7 +790,7 @@ class FilterTest < ActionController::TestCase assert_nil assigns['ran_filter'] end - def test_a_rescuing_around_filter + def test_a_rescuing_around_action response = nil assert_nothing_raised do response = test_process(RescuedController) @@ -802,7 +800,7 @@ class FilterTest < ActionController::TestCase assert_equal("I rescued this: #<FilterTest::ErrorToRescue: Something made the bad noise.>", response.body) end - def test_filters_obey_only_and_except_for_implicit_actions + def test_actions_obey_only_and_except_for_implicit_actions test_process(ImplicitActionsController, 'show') assert_equal 'Except', assigns(:except) assert_nil assigns(:only) @@ -836,7 +834,7 @@ class PostsController < ActionController::Base include AroundExceptions end - module_eval %w(raises_before raises_after raises_both no_raise no_filter).map { |action| "def #{action}; default_action end" }.join("\n") + module_eval %w(raises_before raises_after raises_both no_raise no_action).map { |action| "def #{action}; default_action end" }.join("\n") private def default_action @@ -845,9 +843,9 @@ class PostsController < ActionController::Base end class ControllerWithSymbolAsFilter < PostsController - around_filter :raise_before, :only => :raises_before - around_filter :raise_after, :only => :raises_after - around_filter :without_exception, :only => :no_raise + around_action :raise_before, :only => :raises_before + around_action :raise_after, :only => :raises_after + around_action :without_exception, :only => :no_raise private def raise_before @@ -879,7 +877,7 @@ class ControllerWithFilterClass < PostsController end end - around_filter YieldingFilter, :only => :raises_after + around_action YieldingFilter, :only => :raises_after end class ControllerWithFilterInstance < PostsController @@ -890,22 +888,11 @@ class ControllerWithFilterInstance < PostsController end end - around_filter YieldingFilter.new, :only => :raises_after -end - -class ControllerWithFilterMethod < PostsController - class YieldingFilter < DefaultFilter - def around(controller) - yield - raise After - end - end - - around_filter YieldingFilter.new.method(:around), :only => :raises_after + around_action YieldingFilter.new, :only => :raises_after end class ControllerWithProcFilter < PostsController - around_filter(:only => :no_raise) do |c,b| + around_action(:only => :no_raise) do |c,b| c.instance_variable_set(:"@before", true) b.call c.instance_variable_set(:"@after", true) @@ -913,14 +900,14 @@ class ControllerWithProcFilter < PostsController end class ControllerWithNestedFilters < ControllerWithSymbolAsFilter - around_filter :raise_before, :raise_after, :without_exception, :only => :raises_both + around_action :raise_before, :raise_after, :without_exception, :only => :raises_both end class ControllerWithAllTypesOfFilters < PostsController - before_filter :before - around_filter :around - after_filter :after - around_filter :around_again + before_action :before + around_action :around + after_action :after + around_action :around_again private def before @@ -946,8 +933,8 @@ class ControllerWithAllTypesOfFilters < PostsController end class ControllerWithTwoLessFilters < ControllerWithAllTypesOfFilters - skip_filter :around_again - skip_filter :after + skip_action_callback :around_again + skip_action_callback :after end class YieldingAroundFiltersTest < ActionController::TestCase @@ -958,7 +945,7 @@ class YieldingAroundFiltersTest < ActionController::TestCase assert_nothing_raised { test_process(controller,'no_raise') } assert_nothing_raised { test_process(controller,'raises_before') } assert_nothing_raised { test_process(controller,'raises_after') } - assert_nothing_raised { test_process(controller,'no_filter') } + assert_nothing_raised { test_process(controller,'no_action') } end def test_with_symbol @@ -987,7 +974,7 @@ class YieldingAroundFiltersTest < ActionController::TestCase assert assigns['after'] end - def test_nested_filters + def test_nested_actions controller = ControllerWithNestedFilters assert_nothing_raised do begin @@ -1003,34 +990,34 @@ class YieldingAroundFiltersTest < ActionController::TestCase end end - def test_filter_order_with_all_filter_types + def test_action_order_with_all_action_types test_process(ControllerWithAllTypesOfFilters,'no_raise') assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) after around (after yield)', assigns['ran_filter'].join(' ') end - def test_filter_order_with_skip_filter_method + def test_action_order_with_skip_action_method test_process(ControllerWithTwoLessFilters,'no_raise') assert_equal 'before around (before yield) around (after yield)', assigns['ran_filter'].join(' ') end - def test_first_filter_in_multiple_before_filter_chain_halts + def test_first_action_in_multiple_before_action_chain_halts controller = ::FilterTest::TestMultipleFiltersController.new response = test_process(controller, 'fail_1') - assert_equal ' ', response.body + assert_equal '', response.body assert_equal 1, controller.instance_variable_get(:@try) end - def test_second_filter_in_multiple_before_filter_chain_halts + def test_second_action_in_multiple_before_action_chain_halts controller = ::FilterTest::TestMultipleFiltersController.new response = test_process(controller, 'fail_2') - assert_equal ' ', response.body + assert_equal '', response.body assert_equal 2, controller.instance_variable_get(:@try) end - def test_last_filter_in_multiple_before_filter_chain_halts + def test_last_action_in_multiple_before_action_chain_halts controller = ::FilterTest::TestMultipleFiltersController.new response = test_process(controller, 'fail_3') - assert_equal ' ', response.body + assert_equal '', response.body assert_equal 3, controller.instance_variable_get(:@try) end diff --git a/actionpack/test/controller/flash_hash_test.rb b/actionpack/test/controller/flash_hash_test.rb index 5490d9394b..d979b561f2 100644 --- a/actionpack/test/controller/flash_hash_test.rb +++ b/actionpack/test/controller/flash_hash_test.rb @@ -29,6 +29,15 @@ module ActionDispatch assert_equal 'world', @hash['hello'] end + def test_key + @hash['foo'] = 'bar' + + assert @hash.key?('foo') + assert @hash.key?(:foo) + assert_not @hash.key?('bar') + assert_not @hash.key?(:bar) + end + def test_delete @hash['foo'] = 'bar' @hash.delete 'foo' @@ -67,6 +76,16 @@ module ActionDispatch assert_equal({'flashes' => {'message' => 'Hello'}, 'discard' => %w[message]}, hash.to_session_value) end + def test_from_session_value_on_json_serializer + decrypted_data = "{ \"session_id\":\"d98bdf6d129618fc2548c354c161cfb5\", \"flash\":{\"discard\":[], \"flashes\":{\"message\":\"hey you\"}} }" + session = ActionDispatch::Cookies::JsonSerializer.load(decrypted_data) + hash = Flash::FlashHash.from_session_value(session['flash']) + + assert_equal({'discard' => %w[message], 'flashes' => { 'message' => 'hey you'}}, hash.to_session_value) + assert_equal "hey you", hash[:message] + assert_equal "hey you", hash["message"] + end + def test_empty? assert @hash.empty? @hash['zomg'] = 'bears' diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index 9ceab91e42..3720a920d0 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -175,13 +175,13 @@ class FlashTest < ActionController::TestCase assert_equal(:foo_indeed, flash.discard(:foo)) # valid key passed assert_nil flash.discard(:unknown) # non existent key passed - assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard().to_hash) # nothing passed - assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard(nil).to_hash) # nothing passed + assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.discard().to_hash) # nothing passed + assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.discard(nil).to_hash) # nothing passed assert_equal(:foo_indeed, flash.keep(:foo)) # valid key passed assert_nil flash.keep(:unknown) # non existent key passed - assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep().to_hash) # nothing passed - assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep(nil).to_hash) # nothing passed + assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.keep().to_hash) # nothing passed + assert_equal({"foo" => :foo_indeed, "bar" => :bar_indeed}, flash.keep(nil).to_hash) # nothing passed end def test_redirect_to_with_alert @@ -210,20 +210,29 @@ class FlashTest < ActionController::TestCase end def test_redirect_to_with_adding_flash_types - @controller.class.add_flash_types :foo + original_controller = @controller + test_controller_with_flash_type_foo = Class.new(TestController) do + add_flash_types :foo + end + @controller = test_controller_with_flash_type_foo.new get :redirect_with_foo_flash assert_equal "for great justice", @controller.send(:flash)[:foo] + ensure + @controller = original_controller end - class SubclassesTestController < TestController; end - def test_add_flash_type_to_subclasses - TestController.add_flash_types :foo - assert SubclassesTestController._flash_types.include?(:foo) + test_controller_with_flash_type_foo = Class.new(TestController) do + add_flash_types :foo + end + subclass_controller_with_no_flash_type = Class.new(test_controller_with_flash_type_foo) + assert subclass_controller_with_no_flash_type._flash_types.include?(:foo) end - def test_do_not_add_flash_type_to_parent_class - SubclassesTestController.add_flash_types :bar + def test_does_not_add_flash_type_to_parent_class + Class.new(TestController) do + add_flash_types :bar + end assert_not TestController._flash_types.include?(:bar) end end diff --git a/actionpack/test/controller/force_ssl_test.rb b/actionpack/test/controller/force_ssl_test.rb index 3655b90e32..00d4612ac9 100644 --- a/actionpack/test/controller/force_ssl_test.rb +++ b/actionpack/test/controller/force_ssl_test.rb @@ -93,8 +93,6 @@ class RedirectToSSL < ForceSSLController end class ForceSSLControllerLevelTest < ActionController::TestCase - tests ForceSSLControllerLevel - def test_banana_redirects_to_https get :banana assert_response 301 @@ -115,8 +113,6 @@ class ForceSSLControllerLevelTest < ActionController::TestCase end class ForceSSLCustomOptionsTest < ActionController::TestCase - tests ForceSSLCustomOptions - def setup @request.env['HTTP_HOST'] = 'www.example.com:80' end @@ -189,8 +185,6 @@ class ForceSSLCustomOptionsTest < ActionController::TestCase end class ForceSSLOnlyActionTest < ActionController::TestCase - tests ForceSSLOnlyAction - def test_banana_not_redirects_to_https get :banana assert_response 200 @@ -204,8 +198,6 @@ class ForceSSLOnlyActionTest < ActionController::TestCase end class ForceSSLExceptActionTest < ActionController::TestCase - tests ForceSSLExceptAction - def test_banana_not_redirects_to_https get :banana assert_response 200 @@ -219,8 +211,6 @@ class ForceSSLExceptActionTest < ActionController::TestCase end class ForceSSLIfConditionTest < ActionController::TestCase - tests ForceSSLIfCondition - def test_banana_not_redirects_to_https get :banana assert_response 200 @@ -234,8 +224,6 @@ class ForceSSLIfConditionTest < ActionController::TestCase end class ForceSSLFlashTest < ActionController::TestCase - tests ForceSSLFlash - def test_cheeseburger_redirects_to_https get :set_flash assert_response 302 @@ -315,7 +303,6 @@ class ForceSSLOptionalSegmentsTest < ActionController::TestCase end class RedirectToSSLTest < ActionController::TestCase - tests RedirectToSSL def test_banana_redirects_to_https_if_not_https get :banana assert_response 301 @@ -334,4 +321,4 @@ class RedirectToSSLTest < ActionController::TestCase assert_response 200 assert_equal 'ihaz', response.body end -end
\ No newline at end of file +end diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index 20f99f19ee..936b8c2450 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -60,6 +60,12 @@ class HelpersPathsController < ActionController::Base end end +class HelpersTypoController < ActionController::Base + path = File.expand_path('../../fixtures/helpers_typo', __FILE__) + $:.unshift(path) + self.helpers_path = path +end + module LocalAbcHelper def a() end def b() end @@ -82,6 +88,22 @@ class HelperPathsTest < ActiveSupport::TestCase end end +class HelpersTypoControllerTest < ActiveSupport::TestCase + def setup + @autoload_paths = ActiveSupport::Dependencies.autoload_paths + ActiveSupport::Dependencies.autoload_paths = Array(HelpersTypoController.helpers_path) + end + + def test_helper_typo_error_message + e = assert_raise(NameError) { HelpersTypoController.helper 'admin/users' } + assert_equal "Couldn't find Admin::UsersHelper, expected it to be defined in helpers/admin/users_helper.rb", e.message + end + + def teardown + ActiveSupport::Dependencies.autoload_paths = @autoload_paths + end +end + class HelperTest < ActiveSupport::TestCase class TestController < ActionController::Base attr_accessor :delegate_attr diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index 90548d4294..9052fc6962 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -129,6 +129,13 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert_response :unauthorized end + test "authentication request with wrong scheme" do + header = 'Bearer ' + encode_credentials('David', 'Goliath').split(' ', 2)[1] + @request.env['HTTP_AUTHORIZATION'] = header + get :search + assert_response :unauthorized + end + private def encode_credentials(username, password) diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index 9f1c168209..52a0bc9aa3 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -21,7 +21,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase def authenticate authenticate_or_request_with_http_digest("SuperSecret") do |username| - # Return the password + # Returns the password USERS[username] end end diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb index ebf6d224aa..a758df2ec6 100644 --- a/actionpack/test/controller/http_token_authentication_test.rb +++ b/actionpack/test/controller/http_token_authentication_test.rb @@ -21,7 +21,7 @@ class HttpTokenAuthenticationTest < ActionController::TestCase private def authenticate - authenticate_or_request_with_http_token do |token, options| + authenticate_or_request_with_http_token do |token, _| token == 'lifo' end end @@ -132,13 +132,69 @@ class HttpTokenAuthenticationTest < ActionController::TestCase assert_equal(expected, actual) end - private + test "token_and_options returns empty string with empty token" do + token = '' + actual = ActionController::HttpAuthentication::Token.token_and_options(sample_request(token)).first + expected = token + assert_equal(expected, actual) + end + + test "token_and_options returns correct token with nounce option" do + token = "rcHu+HzSFw89Ypyhn/896A=" + nonce_hash = {nonce: "123abc"} + actual = ActionController::HttpAuthentication::Token.token_and_options(sample_request(token, nonce_hash)) + expected_token = token + expected_nonce = {"nonce" => nonce_hash[:nonce]} + assert_equal(expected_token, actual.first) + assert_equal(expected_nonce, actual.last) + end - def sample_request(token) - @sample_request ||= OpenStruct.new authorization: %{Token token="#{token}"} + test "token_and_options returns nil with no value after the equal sign" do + actual = ActionController::HttpAuthentication::Token.token_and_options(malformed_request).first + expected = nil + assert_equal(expected, actual) end - def encode_credentials(token, options = {}) - ActionController::HttpAuthentication::Token.encode_credentials(token, options) + test "raw_params returns a tuple of two key value pair strings" do + auth = sample_request("rcHu+HzSFw89Ypyhn/896A=").authorization.to_s + actual = ActionController::HttpAuthentication::Token.raw_params(auth) + expected = ["token=\"rcHu+HzSFw89Ypyhn/896A=\"", "nonce=\"def\""] + assert_equal(expected, actual) + end + + test "token_and_options returns right token when token key is not specified in header" do + token = "rcHu+HzSFw89Ypyhn/896A=" + + actual = ActionController::HttpAuthentication::Token.token_and_options( + sample_request_without_token_key(token) + ).first + + expected = token + assert_equal(expected, actual) end + + private + + def sample_request(token, options = {nonce: "def"}) + authorization = options.inject([%{Token token="#{token}"}]) do |arr, (k, v)| + arr << "#{k}=\"#{v}\"" + end.join(", ") + mock_authorization_request(authorization) + end + + def malformed_request + mock_authorization_request(%{Token token=}) + end + + def sample_request_without_token_key(token) + mock_authorization_request(%{Token #{token}}) + end + + def mock_authorization_request(authorization) + OpenStruct.new(authorization: authorization) + end + + def encode_credentials(token, options = {}) + ActionController::HttpAuthentication::Token.encode_credentials(token, options) + end end diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index e851cc6a63..5535c7ae78 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' require 'controller/fake_controllers' -require 'action_view/vendor/html-scanner' +require 'rails/engine' class SessionTest < ActiveSupport::TestCase StubApp = lambda { |env| @@ -292,7 +292,7 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest assert_equal({}, cookies.to_hash) assert_equal "OK", body assert_equal "OK", response.body - assert_kind_of HTML::Document, html_document + assert_kind_of Nokogiri::HTML::Document, html_document assert_equal 1, request_count end end @@ -308,7 +308,7 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest assert_equal({}, cookies.to_hash) assert_equal "Created", body assert_equal "Created", response.body - assert_kind_of HTML::Document, html_document + assert_kind_of Nokogiri::HTML::Document, html_document assert_equal 1, request_count end end @@ -368,12 +368,16 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest assert_response :redirect assert_response :found assert_equal "<html><body>You are being <a href=\"http://www.example.com/get\">redirected</a>.</body></html>", response.body - assert_kind_of HTML::Document, html_document + assert_kind_of Nokogiri::HTML::Document, html_document assert_equal 1, request_count follow_redirect! assert_response :success assert_equal "/get", path + + get '/moved' + assert_response :redirect + assert_redirected_to '/method' end end @@ -511,6 +515,8 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest end set.draw do + get 'moved' => redirect('/method') + match ':action', :to => controller, :via => [:get, :post], :as => :action get 'get/:action', :to => controller, :as => :get_action end @@ -589,7 +595,7 @@ class ApplicationIntegrationTest < ActionDispatch::IntegrationTest @routes ||= ActionDispatch::Routing::RouteSet.new end - class MountedApp + class MountedApp < Rails::Engine def self.routes @routes ||= ActionDispatch::Routing::RouteSet.new end @@ -609,6 +615,8 @@ class ApplicationIntegrationTest < ActionDispatch::IntegrationTest get 'bar', :to => 'application_integration_test/test#index', :as => :bar mount MountedApp => '/mounted', :as => "mounted" + get 'fooz' => proc { |env| [ 200, {'X-Cascade' => 'pass'}, [ "omg" ] ] }, :anchor => false + get 'fooz', :to => 'application_integration_test/test#index' end def app @@ -625,6 +633,12 @@ class ApplicationIntegrationTest < ActionDispatch::IntegrationTest assert_equal '/mounted/baz', mounted.baz_path end + test "path after cascade pass" do + get '/fooz' + assert_equal 'index', response.body + assert_equal '/fooz', path + end + test "route helpers after controller access" do get '/' assert_equal '/', empty_string_path @@ -769,3 +783,94 @@ class UrlOptionsIntegrationTest < ActionDispatch::IntegrationTest assert_equal "/foo/1/edit", url_for(:action => 'edit', :only_path => true) end end + +class HeadWithStatusActionIntegrationTest < ActionDispatch::IntegrationTest + class FooController < ActionController::Base + def status + head :ok + end + end + + def self.routes + @routes ||= ActionDispatch::Routing::RouteSet.new + end + + def self.call(env) + routes.call(env) + end + + def app + self.class + end + + routes.draw do + get "/foo/status" => 'head_with_status_action_integration_test/foo#status' + end + + test "get /foo/status with head result does not cause stack overflow error" do + assert_nothing_raised do + get '/foo/status' + end + assert_response :ok + end +end + +class IntegrationWithRoutingTest < ActionDispatch::IntegrationTest + class FooController < ActionController::Base + def index + render plain: 'ok' + end + end + + def test_with_routing_resets_session + klass_namespace = self.class.name.underscore + + with_routing do |routes| + routes.draw do + namespace klass_namespace do + resources :foo, path: '/with' + end + end + + get '/integration_with_routing_test/with' + assert_response 200 + assert_equal 'ok', response.body + end + + with_routing do |routes| + routes.draw do + namespace klass_namespace do + resources :foo, path: '/routing' + end + end + + get '/integration_with_routing_test/routing' + assert_response 200 + assert_equal 'ok', response.body + end + end +end + +# to work in contexts like rspec before(:all) +class IntegrationRequestsWithoutSetup < ActionDispatch::IntegrationTest + self._setup_callbacks = [] + self._teardown_callbacks = [] + + class FooController < ActionController::Base + def ok + cookies[:key] = 'ok' + render plain: 'ok' + end + end + + def test_request + with_routing do |routes| + routes.draw { get ':action' => FooController } + get '/ok' + + assert_response 200 + assert_equal 'ok', response.body + assert_equal 'ok', cookies['key'] + end + end +end diff --git a/actionpack/test/controller/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb index 0a431270b5..7fd1276e98 100644 --- a/actionpack/test/controller/live_stream_test.rb +++ b/actionpack/test/controller/live_stream_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'active_support/concurrency/latch' +Thread.abort_on_exception = true module ActionController class SSETest < ActionController::TestCase @@ -38,14 +39,19 @@ module ActionController ensure sse.close end + + def sse_with_multiple_line_message + sse = SSE.new(response.stream) + sse.write("first line.\nsecond line.") + ensure + sse.close + end end tests SSETestController def wait_for_response_stream_close - while !response.stream.closed? - sleep 0.01 - end + response.body end def test_basic_sse @@ -88,9 +94,21 @@ module ActionController assert_match(/data: {\"name\":\"Ryan\"}/, second_response) assert_match(/id: 2/, second_response) end + + def test_sse_with_multiple_line_message + get :sse_with_multiple_line_message + + wait_for_response_stream_close + first_response, second_response = response.body.split("\n") + assert_match(/data: first line/, first_response) + assert_match(/data: second line/, second_response) + end end class LiveStreamTest < ActionController::TestCase + class Exception < StandardError + end + class TestController < ActionController::Base include ActionController::Live @@ -100,6 +118,12 @@ module ActionController 'test' end + def set_cookie + cookies[:hello] = "world" + response.stream.write "hello world" + response.close + end + def render_text render :text => 'zomg' end @@ -138,13 +162,18 @@ module ActionController end def with_stale - render :text => 'stale' if stale?(:etag => "123") + render text: 'stale' if stale?(etag: "123", template: false) end def exception_in_view render 'doesntexist' end + def exception_in_view_after_commit + response.stream.write "" + render 'doesntexist' + end + def exception_with_callback response.headers['Content-Type'] = 'text/event-stream' @@ -153,32 +182,66 @@ module ActionController response.stream.close end + response.stream.write "" # make sure the response is committed raise 'An exception occurred...' end + def exception_in_controller + raise Exception, 'Exception in controller' + end + + def bad_request_error + raise ActionController::BadRequest + end + def exception_in_exception_callback response.headers['Content-Type'] = 'text/event-stream' response.stream.on_error do raise 'We need to go deeper.' end + response.stream.write '' response.stream.write params[:widget][:didnt_check_for_nil] end - end - tests TestController + def overfill_buffer_and_die + # Write until the buffer is full. It doesn't expose that + # information directly, so we must hard-code its size: + 10.times do + response.stream.write '.' + end + # .. plus one more, because the #each frees up a slot: + response.stream.write '.' + + latch.release - class TestResponse < Live::Response - def recycle! - initialize + # This write will block, and eventually raise + response.stream.write 'x' + + 20.times do + response.stream.write '.' + end end - end - def build_response - TestResponse.new + def ignore_client_disconnect + response.stream.ignore_disconnect = true + + response.stream.write '' # commit + + # These writes will be ignored + 15.times do + response.stream.write 'x' + end + + logger.info 'Work complete' + latch.release + end end + tests TestController + def assert_stream_closed assert response.stream.closed?, 'stream should be closed' + assert response.sent?, 'stream should be sent' end def capture_log_output @@ -192,6 +255,13 @@ module ActionController end end + def test_set_cookie + @controller = TestController.new + get :set_cookie + assert_equal({'hello' => 'world'}, @response.cookies) + assert_equal "hello world", @response.body + end + def test_set_response! @controller.set_response!(@request) assert_kind_of(Live::Response, @controller.response) @@ -213,6 +283,7 @@ module ActionController @controller.response = @response t = Thread.new(@response) { |resp| + resp.await_commit resp.stream.each do |part| assert_equal parts.shift, part ol = @controller.latch @@ -226,6 +297,62 @@ module ActionController assert t.join(3), 'timeout expired before the thread terminated' end + def test_abort_with_full_buffer + @controller.latch = ActiveSupport::Concurrency::Latch.new + + @request.parameters[:format] = 'plain' + @controller.request = @request + @controller.response = @response + + got_error = ActiveSupport::Concurrency::Latch.new + @response.stream.on_error do + ActionController::Base.logger.warn 'Error while streaming' + got_error.release + end + + t = Thread.new(@response) { |resp| + resp.await_commit + _, _, body = resp.to_a + body.each do |part| + @controller.latch.await + body.close + break + end + } + + capture_log_output do |output| + @controller.process :overfill_buffer_and_die + t.join + got_error.await + assert_match 'Error while streaming', output.rewind && output.read + end + end + + def test_ignore_client_disconnect + @controller.latch = ActiveSupport::Concurrency::Latch.new + + @controller.request = @request + @controller.response = @response + + t = Thread.new(@response) { |resp| + resp.await_commit + _, _, body = resp.to_a + body.each do |part| + body.close + break + end + } + + capture_log_output do |output| + @controller.process :ignore_client_disconnect + t.join + Timeout.timeout(3) do + @controller.latch.await + end + assert_match 'Work complete', output.rewind && output.read + end + end + def test_thread_locals_get_copied @controller.tc = self Thread.current[:originating_thread] = Thread.current.object_id @@ -249,24 +376,34 @@ module ActionController end def test_exception_handling_html - capture_log_output do |output| + assert_raises(ActionView::MissingTemplate) do get :exception_in_view + end + + capture_log_output do |output| + get :exception_in_view_after_commit assert_match %r((window\.location = "/500\.html"</script></html>)$), response.body assert_match 'Missing template test/doesntexist', output.rewind && output.read assert_stream_closed end + assert response.body + assert_stream_closed end def test_exception_handling_plain_text - capture_log_output do |output| + assert_raises(ActionView::MissingTemplate) do get :exception_in_view, format: :json + end + + capture_log_output do |output| + get :exception_in_view_after_commit, format: :json assert_equal '', response.body assert_match 'Missing template test/doesntexist', output.rewind && output.read assert_stream_closed end end - def test_exception_callback + def test_exception_callback_when_committed capture_log_output do |output| get :exception_with_callback, format: 'text/event-stream' assert_equal %(data: "500 Internal Server Error"\n\n), response.body @@ -275,7 +412,19 @@ module ActionController end end - def test_exceptions_raised_handling_exceptions + def test_exception_in_controller_before_streaming + assert_raises(ActionController::LiveStreamTest::Exception) do + get :exception_in_controller, format: 'text/event-stream' + end + end + + def test_bad_request_in_controller_before_streaming + assert_raises(ActionController::BadRequest) do + get :bad_request_error, format: 'text/event-stream' + end + end + + def test_exceptions_raised_handling_exceptions_and_committed capture_log_output do |output| get :exception_in_exception_callback, format: 'text/event-stream' assert_equal '', response.body @@ -295,4 +444,11 @@ module ActionController assert_equal 304, @response.status.to_i end end + + class BufferTest < ActionController::TestCase + def test_nil_callback + buf = ActionController::Live::Buffer.new nil + assert buf.call_on_error + end + end end diff --git a/actionpack/test/controller/localized_templates_test.rb b/actionpack/test/controller/localized_templates_test.rb index c95ef8a0c7..2be947c648 100644 --- a/actionpack/test/controller/localized_templates_test.rb +++ b/actionpack/test/controller/localized_templates_test.rb @@ -8,22 +8,24 @@ end class LocalizedTemplatesTest < ActionController::TestCase tests LocalizedController + setup do + @old_locale = I18n.locale + end + + teardown do + I18n.locale = @old_locale + end + def test_localized_template_is_used - old_locale = I18n.locale I18n.locale = :de get :hello_world - assert_equal "Gutten Tag", @response.body - ensure - I18n.locale = old_locale + assert_equal "Guten Tag", @response.body end def test_default_locale_template_is_used_when_locale_is_missing - old_locale = I18n.locale I18n.locale = :dk get :hello_world assert_equal "Hello World", @response.body - ensure - I18n.locale = old_locale end def test_use_fallback_locales @@ -32,17 +34,13 @@ class LocalizedTemplatesTest < ActionController::TestCase I18n.fallbacks[:"de-AT"] = [:de] get :hello_world - assert_equal "Gutten Tag", @response.body + assert_equal "Guten Tag", @response.body end def test_localized_template_has_correct_header_with_no_format_in_template_name - old_locale = I18n.locale I18n.locale = :it - get :hello_world assert_equal "Ciao Mondo", @response.body assert_equal "text/html", @response.content_type - ensure - I18n.locale = old_locale end end diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index 075347be52..864c6ee130 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -73,6 +73,16 @@ module Another def with_action_not_found raise AbstractController::ActionNotFound end + + def append_info_to_payload(payload) + super + payload[:test_key] = "test_value" + @last_payload = payload + end + + def last_payload + @last_payload + end end end @@ -85,7 +95,7 @@ class ACLogSubscriberTest < ActionController::TestCase @old_logger = ActionController::Base.logger - @cache_path = File.expand_path('../temp/test_cache', File.dirname(__FILE__)) + @cache_path = File.join Dir.tmpdir, Dir::Tmpname.make_tmpname('tmp', 'cache') @controller.cache_store = :file_store, @cache_path ActionController::LogSubscriber.attach_to :action_controller end @@ -137,6 +147,17 @@ class ACLogSubscriberTest < ActionController::TestCase assert_equal 'Parameters: {"id"=>"10"}', logs[1] end + def test_multiple_process_with_parameters + get :show, :id => '10' + get :show, :id => '20' + + wait + + assert_equal 6, logs.size + assert_equal 'Parameters: {"id"=>"10"}', logs[1] + assert_equal 'Parameters: {"id"=>"20"}', logs[4] + end + def test_process_action_with_wrapped_parameters @request.env['CONTENT_TYPE'] = 'application/json' post :show, :id => '10', :name => 'jose' @@ -152,6 +173,16 @@ class ACLogSubscriberTest < ActionController::TestCase assert_match(/\(Views: [\d.]+ms\)/, logs[1]) end + def test_append_info_to_payload_is_called_even_with_exception + begin + get :with_exception + wait + rescue Exception + end + + assert_equal "test_value", @controller.last_payload[:test_key] + end + def test_process_action_with_filter_parameters @request.env["action_dispatch.parameter_filter"] = [:lifo, :amount] diff --git a/actionpack/test/controller/mime/accept_format_test.rb b/actionpack/test/controller/mime/accept_format_test.rb index c03c7edeb8..811c507af2 100644 --- a/actionpack/test/controller/mime/accept_format_test.rb +++ b/actionpack/test/controller/mime/accept_format_test.rb @@ -9,8 +9,6 @@ class StarStarMimeController < ActionController::Base end class StarStarMimeControllerTest < ActionController::TestCase - tests StarStarMimeController - def test_javascript_with_format @request.accept = "text/javascript" get :index, :format => 'js' diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb index 2b6c8739af..66d2fd7716 100644 --- a/actionpack/test/controller/mime/respond_to_test.rb +++ b/actionpack/test/controller/mime/respond_to_test.rb @@ -128,6 +128,12 @@ class RespondToController < ActionController::Base end end + def json_with_callback + respond_to do |type| + type.json { render :json => 'JS', :callback => 'alert' } + end + end + def iphone_with_html_response_type request.format = :iphone if request.env["HTTP_ACCEPT"] == "text/iphone" @@ -166,6 +172,86 @@ class RespondToController < ActionController::Base end end + def variant_plus_none_for_format + respond_to do |format| + format.html do |variant| + variant.phone { render text: "phone" } + variant.none + end + end + end + + def variant_inline_syntax + respond_to do |format| + format.js { render text: "js" } + format.html.none { render text: "none" } + format.html.phone { render text: "phone" } + end + end + + def variant_inline_syntax_without_block + respond_to do |format| + format.js + format.html.none + format.html.phone + end + end + + def variant_any + respond_to do |format| + format.html do |variant| + variant.any(:tablet, :phablet){ render text: "any" } + variant.phone { render text: "phone" } + end + end + end + + def variant_any_any + respond_to do |format| + format.html do |variant| + variant.any { render text: "any" } + variant.phone { render text: "phone" } + end + end + end + + def variant_inline_any + respond_to do |format| + format.html.any(:tablet, :phablet){ render text: "any" } + format.html.phone { render text: "phone" } + end + end + + def variant_inline_any_any + respond_to do |format| + format.html.phone { render text: "phone" } + format.html.any { render text: "any" } + end + end + + def variant_any_implicit_render + respond_to do |format| + format.html.phone + format.html.any(:tablet, :phablet) + end + end + + def variant_any_with_none + respond_to do |format| + format.html.any(:none, :phone){ render text: "none or phone" } + end + end + + def format_any_variant_any + respond_to do |format| + format.html { render text: "HTML" } + format.any(:js, :xml) do |variant| + variant.phone{ render text: "phone" } + variant.any(:tablet, :phablet){ render text: "tablet" } + end + end + end + protected def set_layout case action_name @@ -178,8 +264,6 @@ class RespondToController < ActionController::Base end class RespondToControllerTest < ActionController::TestCase - tests RespondToController - def setup super @request.host = "www.example.com" @@ -412,6 +496,11 @@ class RespondToControllerTest < ActionController::TestCase assert_equal 'Whatever you ask for, I got it', @response.body end + def test_handle_any_any_unkown_format + get :handle_any_any, { format: 'php' } + assert_equal 'Whatever you ask for, I got it', @response.body + end + def test_browser_check_with_any_any @request.accept = "application/json, application/xml" get :json_xml_or_html @@ -428,6 +517,13 @@ class RespondToControllerTest < ActionController::TestCase assert_equal '<html><div id="html">HTML for all_types_with_layout</div></html>', @response.body end + def test_json_with_callback_sets_javascript_content_type + @request.accept = 'application/json' + get :json_with_callback + assert_equal '/**/alert(JS)', @response.body + assert_equal 'text/javascript', @response.content_type + end + def test_xhr xhr :get, :js_or_html assert_equal 'JS', @response.body @@ -483,10 +579,10 @@ class RespondToControllerTest < ActionController::TestCase end get :using_defaults - assert_equal "using_defaults - #{[:html].to_s}", @response.body + assert_equal "using_defaults - #{[:html]}", @response.body get :using_defaults, :format => "xml" - assert_equal "using_defaults - #{[:xml].to_s}", @response.body + assert_equal "using_defaults - #{[:xml]}", @response.body end def test_format_with_custom_response_type @@ -544,4 +640,145 @@ class RespondToControllerTest < ActionController::TestCase assert_equal "text/html", @response.content_type assert_equal "tablet", @response.body end + + def test_no_variant_in_variant_setup + get :variant_plus_none_for_format + assert_equal "text/html", @response.content_type + assert_equal "none", @response.body + end + + def test_variant_inline_syntax + get :variant_inline_syntax, format: :js + assert_equal "text/javascript", @response.content_type + assert_equal "js", @response.body + + get :variant_inline_syntax + assert_equal "text/html", @response.content_type + assert_equal "none", @response.body + + @request.variant = :phone + get :variant_inline_syntax + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + end + + def test_variant_inline_syntax_without_block + @request.variant = :phone + get :variant_inline_syntax_without_block + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + end + + def test_variant_any + @request.variant = :phone + get :variant_any + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + + @request.variant = :tablet + get :variant_any + assert_equal "text/html", @response.content_type + assert_equal "any", @response.body + + @request.variant = :phablet + get :variant_any + assert_equal "text/html", @response.content_type + assert_equal "any", @response.body + end + + def test_variant_any_any + get :variant_any_any + assert_equal "text/html", @response.content_type + assert_equal "any", @response.body + + @request.variant = :phone + get :variant_any_any + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + + @request.variant = :yolo + get :variant_any_any + assert_equal "text/html", @response.content_type + assert_equal "any", @response.body + end + + def test_variant_inline_any + @request.variant = :phone + get :variant_any + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + + @request.variant = :tablet + get :variant_inline_any + assert_equal "text/html", @response.content_type + assert_equal "any", @response.body + + @request.variant = :phablet + get :variant_inline_any + assert_equal "text/html", @response.content_type + assert_equal "any", @response.body + end + + def test_variant_inline_any_any + @request.variant = :phone + get :variant_inline_any_any + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + + @request.variant = :yolo + get :variant_inline_any_any + assert_equal "text/html", @response.content_type + assert_equal "any", @response.body + end + + def test_variant_any_implicit_render + @request.variant = :tablet + get :variant_any_implicit_render + assert_equal "text/html", @response.content_type + assert_equal "tablet", @response.body + + @request.variant = :phablet + get :variant_any_implicit_render + assert_equal "text/html", @response.content_type + assert_equal "phablet", @response.body + end + + def test_variant_any_with_none + get :variant_any_with_none + assert_equal "text/html", @response.content_type + assert_equal "none or phone", @response.body + + @request.variant = :phone + get :variant_any_with_none + assert_equal "text/html", @response.content_type + assert_equal "none or phone", @response.body + end + + def test_format_any_variant_any + @request.variant = :tablet + get :format_any_variant_any, format: :js + assert_equal "text/javascript", @response.content_type + assert_equal "tablet", @response.body + end + + def test_variant_negotiation_inline_syntax + @request.variant = [:tablet, :phone] + get :variant_inline_syntax_without_block + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + end + + def test_variant_negotiation_block_syntax + @request.variant = [:tablet, :phone] + get :variant_plus_none_for_format + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + end + + def test_variant_negotiation_without_block + @request.variant = [:tablet, :phone] + get :variant_inline_syntax_without_block + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + end end diff --git a/actionpack/test/controller/mime/respond_with_test.rb b/actionpack/test/controller/mime/respond_with_test.rb deleted file mode 100644 index a70592fa1b..0000000000 --- a/actionpack/test/controller/mime/respond_with_test.rb +++ /dev/null @@ -1,714 +0,0 @@ -require 'abstract_unit' -require 'controller/fake_models' - -class RespondWithController < ActionController::Base - respond_to :html, :json, :touch - respond_to :xml, :except => :using_resource_with_block - respond_to :js, :only => [ :using_resource_with_block, :using_resource, 'using_hash_resource' ] - - def using_resource - respond_with(resource) - end - - def using_hash_resource - respond_with({:result => resource}) - end - - def using_resource_with_block - respond_with(resource) do |format| - format.csv { render :text => "CSV" } - end - end - - def using_resource_with_overwrite_block - respond_with(resource) do |format| - format.html { render :text => "HTML" } - end - end - - def using_resource_with_collection - respond_with([resource, Customer.new("jamis", 9)]) - end - - def using_resource_with_parent - respond_with(Quiz::Store.new("developer?", 11), Customer.new("david", 13)) - end - - def using_resource_with_status_and_location - respond_with(resource, :location => "http://test.host/", :status => :created) - end - - def using_invalid_resource_with_template - respond_with(resource) - end - - def using_options_with_template - @customer = resource - respond_with(@customer, :status => 123, :location => "http://test.host/") - end - - def using_resource_with_responder - responder = proc { |c, r, o| c.render :text => "Resource name is #{r.first.name}" } - respond_with(resource, :responder => responder) - end - - def using_resource_with_action - respond_with(resource, :action => :foo) do |format| - format.html { raise ActionView::MissingTemplate.new([], "bar", ["foo"], {}, false) } - end - end - - def using_responder_with_respond - responder = Class.new(ActionController::Responder) do - def respond; @controller.render :text => "respond #{format}"; end - end - respond_with(resource, :responder => responder) - end - - def respond_with_additional_params - @params = RespondWithController.params - respond_with({:result => resource}, @params) - end - -protected - def self.params - { - :foo => 'bar' - } - end - - def resource - Customer.new("david", request.delete? ? nil : 13) - end -end - -class InheritedRespondWithController < RespondWithController - clear_respond_to - respond_to :xml, :json - - def index - respond_with(resource) do |format| - format.json { render :text => "JSON" } - end - end -end - -class RenderJsonRespondWithController < RespondWithController - clear_respond_to - respond_to :json - - def index - respond_with(resource) do |format| - format.json { render :json => RenderJsonTestException.new('boom') } - end - end - - def create - resource = ValidatedCustomer.new(params[:name], 1) - respond_with(resource) do |format| - format.json do - if resource.errors.empty? - render :json => { :valid => true } - else - render :json => { :valid => false } - end - end - end - end -end - -class CsvRespondWithController < ActionController::Base - respond_to :csv - - class RespondWithCsv - def to_csv - "c,s,v" - end - end - - def index - respond_with(RespondWithCsv.new) - end -end - -class EmptyRespondWithController < ActionController::Base - def index - respond_with(Customer.new("david", 13)) - end -end - -class RespondWithControllerTest < ActionController::TestCase - tests RespondWithController - - def setup - super - @request.host = "www.example.com" - Mime::Type.register_alias('text/html', :iphone) - Mime::Type.register_alias('text/html', :touch) - Mime::Type.register('text/x-mobile', :mobile) - end - - def teardown - super - Mime::Type.unregister(:iphone) - Mime::Type.unregister(:touch) - Mime::Type.unregister(:mobile) - end - - def test_respond_with_shouldnt_modify_original_hash - get :respond_with_additional_params - assert_equal RespondWithController.params, assigns(:params) - end - - def test_using_resource - @request.accept = "application/xml" - get :using_resource - assert_equal "application/xml", @response.content_type - assert_equal "<name>david</name>", @response.body - - @request.accept = "application/json" - assert_raise ActionView::MissingTemplate do - get :using_resource - end - end - - def test_using_resource_with_js_simply_tries_to_render_the_template - @request.accept = "text/javascript" - get :using_resource - assert_equal "text/javascript", @response.content_type - assert_equal "alert(\"Hi\");", @response.body - end - - def test_using_hash_resource_with_js_raises_an_error_if_template_cant_be_found - @request.accept = "text/javascript" - assert_raise ActionView::MissingTemplate do - get :using_hash_resource - end - end - - def test_using_hash_resource - @request.accept = "application/xml" - get :using_hash_resource - assert_equal "application/xml", @response.content_type - assert_equal "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<hash>\n <name>david</name>\n</hash>\n", @response.body - - @request.accept = "application/json" - get :using_hash_resource - assert_equal "application/json", @response.content_type - assert @response.body.include?("result") - assert @response.body.include?('"name":"david"') - assert @response.body.include?('"id":13') - end - - def test_using_hash_resource_with_post - @request.accept = "application/json" - assert_raise ArgumentError, "Nil location provided. Can't build URI." do - post :using_hash_resource - end - end - - def test_using_resource_with_block - @request.accept = "*/*" - get :using_resource_with_block - assert_equal "text/html", @response.content_type - assert_equal 'Hello world!', @response.body - - @request.accept = "text/csv" - get :using_resource_with_block - assert_equal "text/csv", @response.content_type - assert_equal "CSV", @response.body - - @request.accept = "application/xml" - get :using_resource - assert_equal "application/xml", @response.content_type - assert_equal "<name>david</name>", @response.body - end - - def test_using_resource_with_overwrite_block - get :using_resource_with_overwrite_block - assert_equal "text/html", @response.content_type - assert_equal "HTML", @response.body - end - - def test_not_acceptable - @request.accept = "application/xml" - assert_raises(ActionController::UnknownFormat) do - get :using_resource_with_block - end - - @request.accept = "text/javascript" - assert_raises(ActionController::UnknownFormat) do - get :using_resource_with_overwrite_block - end - end - - def test_using_resource_for_post_with_html_redirects_on_success - with_test_route_set do - post :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers/13", @response.location - assert @response.redirect? - end - end - - def test_using_resource_for_post_with_html_rerender_on_failure - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "New world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_post_with_xml_yields_created_on_success - with_test_route_set do - @request.accept = "application/xml" - post :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 201, @response.status - assert_equal "<name>david</name>", @response.body - assert_equal "http://www.example.com/customers/13", @response.location - end - end - - def test_using_resource_for_post_with_xml_yields_unprocessable_entity_on_failure - with_test_route_set do - @request.accept = "application/xml" - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 422, @response.status - assert_equal errors.to_xml, @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_post_with_json_yields_unprocessable_entity_on_failure - with_test_route_set do - @request.accept = "application/json" - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "application/json", @response.content_type - assert_equal 422, @response.status - errors = {:errors => errors} - assert_equal errors.to_json, @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_patch_with_html_redirects_on_success - with_test_route_set do - patch :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers/13", @response.location - assert @response.redirect? - end - end - - def test_using_resource_for_patch_with_html_rerender_on_failure - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - patch :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "Edit world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_patch_with_html_rerender_on_failure_even_on_method_override - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - @request.env["rack.methodoverride.original_method"] = "POST" - patch :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "Edit world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_put_with_html_redirects_on_success - with_test_route_set do - put :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers/13", @response.location - assert @response.redirect? - end - end - - def test_using_resource_for_put_with_html_rerender_on_failure - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - put :using_resource - - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "Edit world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_put_with_html_rerender_on_failure_even_on_method_override - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - @request.env["rack.methodoverride.original_method"] = "POST" - put :using_resource - assert_equal "text/html", @response.content_type - assert_equal 200, @response.status - assert_equal "Edit world!\n", @response.body - assert_nil @response.location - end - end - - def test_using_resource_for_put_with_xml_yields_no_content_on_success - @request.accept = "application/xml" - put :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 204, @response.status - assert_equal "", @response.body - end - - def test_using_resource_for_put_with_json_yields_no_content_on_success - Customer.any_instance.stubs(:to_json).returns('{"name": "David"}') - @request.accept = "application/json" - put :using_resource - assert_equal "application/json", @response.content_type - assert_equal 204, @response.status - assert_equal "", @response.body - end - - def test_using_resource_for_put_with_xml_yields_unprocessable_entity_on_failure - @request.accept = "application/xml" - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - put :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 422, @response.status - assert_equal errors.to_xml, @response.body - assert_nil @response.location - end - - def test_using_resource_for_put_with_json_yields_unprocessable_entity_on_failure - @request.accept = "application/json" - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - put :using_resource - assert_equal "application/json", @response.content_type - assert_equal 422, @response.status - errors = {:errors => errors} - assert_equal errors.to_json, @response.body - assert_nil @response.location - end - - def test_using_resource_for_delete_with_html_redirects_on_success - with_test_route_set do - Customer.any_instance.stubs(:destroyed?).returns(true) - delete :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers", @response.location - end - end - - def test_using_resource_for_delete_with_xml_yields_no_content_on_success - Customer.any_instance.stubs(:destroyed?).returns(true) - @request.accept = "application/xml" - delete :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 204, @response.status - assert_equal "", @response.body - end - - def test_using_resource_for_delete_with_json_yields_no_content_on_success - Customer.any_instance.stubs(:to_json).returns('{"name": "David"}') - Customer.any_instance.stubs(:destroyed?).returns(true) - @request.accept = "application/json" - delete :using_resource - assert_equal "application/json", @response.content_type - assert_equal 204, @response.status - assert_equal "", @response.body - end - - def test_using_resource_for_delete_with_html_redirects_on_failure - with_test_route_set do - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - Customer.any_instance.stubs(:destroyed?).returns(false) - delete :using_resource - assert_equal "text/html", @response.content_type - assert_equal 302, @response.status - assert_equal "http://www.example.com/customers", @response.location - end - end - - def test_using_resource_with_parent_for_get - @request.accept = "application/xml" - get :using_resource_with_parent - assert_equal "application/xml", @response.content_type - assert_equal 200, @response.status - assert_equal "<name>david</name>", @response.body - end - - def test_using_resource_with_parent_for_post - with_test_route_set do - @request.accept = "application/xml" - - post :using_resource_with_parent - assert_equal "application/xml", @response.content_type - assert_equal 201, @response.status - assert_equal "<name>david</name>", @response.body - assert_equal "http://www.example.com/quiz_stores/11/customers/13", @response.location - - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - post :using_resource - assert_equal "application/xml", @response.content_type - assert_equal 422, @response.status - assert_equal errors.to_xml, @response.body - assert_nil @response.location - end - end - - def test_using_resource_with_collection - @request.accept = "application/xml" - get :using_resource_with_collection - assert_equal "application/xml", @response.content_type - assert_equal 200, @response.status - assert_match(/<name>david<\/name>/, @response.body) - assert_match(/<name>jamis<\/name>/, @response.body) - end - - def test_using_resource_with_action - @controller.instance_eval do - def render(params={}) - self.response_body = "#{params[:action]} - #{formats}" - end - end - - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - - post :using_resource_with_action - assert_equal "foo - #{[:html].to_s}", @controller.response.body - end - - def test_respond_as_responder_entry_point - @request.accept = "text/html" - get :using_responder_with_respond - assert_equal "respond html", @response.body - - @request.accept = "application/xml" - get :using_responder_with_respond - assert_equal "respond xml", @response.body - end - - def test_clear_respond_to - @controller = InheritedRespondWithController.new - @request.accept = "text/html" - assert_raises(ActionController::UnknownFormat) do - get :index - end - end - - def test_first_in_respond_to_has_higher_priority - @controller = InheritedRespondWithController.new - @request.accept = "*/*" - get :index - assert_equal "application/xml", @response.content_type - assert_equal "<name>david</name>", @response.body - end - - def test_block_inside_respond_with_is_rendered - @controller = InheritedRespondWithController.new - @request.accept = "application/json" - get :index - assert_equal "JSON", @response.body - end - - def test_render_json_object_responds_to_str_still_produce_json - @controller = RenderJsonRespondWithController.new - @request.accept = "application/json" - get :index, :format => :json - assert_match(/"message":"boom"/, @response.body) - assert_match(/"error":"RenderJsonTestException"/, @response.body) - end - - def test_api_response_with_valid_resource_respect_override_block - @controller = RenderJsonRespondWithController.new - post :create, :name => "sikachu", :format => :json - assert_equal '{"valid":true}', @response.body - end - - def test_api_response_with_invalid_resource_respect_override_block - @controller = RenderJsonRespondWithController.new - post :create, :name => "david", :format => :json - assert_equal '{"valid":false}', @response.body - end - - def test_no_double_render_is_raised - @request.accept = "text/html" - assert_raise ActionView::MissingTemplate do - get :using_resource - end - end - - def test_using_resource_with_status_and_location - @request.accept = "text/html" - post :using_resource_with_status_and_location - assert @response.redirect? - assert_equal "http://test.host/", @response.location - - @request.accept = "application/xml" - get :using_resource_with_status_and_location - assert_equal 201, @response.status - end - - def test_using_resource_with_status_and_location_with_invalid_resource - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - - @request.accept = "text/xml" - - post :using_resource_with_status_and_location - assert_equal errors.to_xml, @response.body - assert_equal 422, @response.status - assert_equal nil, @response.location - - put :using_resource_with_status_and_location - assert_equal errors.to_xml, @response.body - assert_equal 422, @response.status - assert_equal nil, @response.location - end - - def test_using_invalid_resource_with_template - errors = { :name => :invalid } - Customer.any_instance.stubs(:errors).returns(errors) - - @request.accept = "text/xml" - - post :using_invalid_resource_with_template - assert_equal errors.to_xml, @response.body - assert_equal 422, @response.status - assert_equal nil, @response.location - - put :using_invalid_resource_with_template - assert_equal errors.to_xml, @response.body - assert_equal 422, @response.status - assert_equal nil, @response.location - end - - def test_using_options_with_template - @request.accept = "text/xml" - - post :using_options_with_template - assert_equal "<customer-name>david</customer-name>", @response.body - assert_equal 123, @response.status - assert_equal "http://test.host/", @response.location - - put :using_options_with_template - assert_equal "<customer-name>david</customer-name>", @response.body - assert_equal 123, @response.status - assert_equal "http://test.host/", @response.location - end - - def test_using_resource_with_responder - get :using_resource_with_responder - assert_equal "Resource name is david", @response.body - end - - def test_using_resource_with_set_responder - RespondWithController.responder = proc { |c, r, o| c.render :text => "Resource name is #{r.first.name}" } - get :using_resource - assert_equal "Resource name is david", @response.body - ensure - RespondWithController.responder = ActionController::Responder - end - - def test_uses_renderer_if_an_api_behavior - ActionController::Renderers.add :csv do |obj, options| - send_data obj.to_csv, type: Mime::CSV - end - @controller = CsvRespondWithController.new - get :index, format: 'csv' - assert_equal Mime::CSV, @response.content_type - assert_equal "c,s,v", @response.body - end - - def test_raises_missing_renderer_if_an_api_behavior_with_no_renderer - @controller = CsvRespondWithController.new - assert_raise ActionController::MissingRenderer do - get :index, format: 'csv' - end - end - - def test_error_is_raised_if_no_respond_to_is_declared_and_respond_with_is_called - @controller = EmptyRespondWithController.new - @request.accept = "*/*" - assert_raise RuntimeError do - get :index - end - end - - private - def with_test_route_set - with_routing do |set| - set.draw do - resources :customers - resources :quiz_stores do - resources :customers - end - get ":controller/:action" - end - yield - end - end -end - -class FlashResponder < ActionController::Responder - def initialize(controller, resources, options={}) - super - end - - def to_html - controller.flash[:notice] = 'Success' - super - end -end - -class FlashResponderController < ActionController::Base - self.responder = FlashResponder - respond_to :html - - def index - respond_with Object.new do |format| - format.html { render :text => 'HTML' } - end - end -end - -class FlashResponderControllerTest < ActionController::TestCase - tests FlashResponderController - - def test_respond_with_block_executed - get :index - assert_equal 'HTML', @response.body - end - - def test_flash_responder_executed - get :index - assert_equal 'Success', flash[:notice] - end -end diff --git a/actionpack/test/controller/mime/responders_test.rb b/actionpack/test/controller/mime/responders_test.rb new file mode 100644 index 0000000000..032b4c0ab1 --- /dev/null +++ b/actionpack/test/controller/mime/responders_test.rb @@ -0,0 +1,32 @@ +require 'abstract_unit' +require 'controller/fake_models' + +class ResponderTest < ActionController::TestCase + def test_class_level_respond_to + e = assert_raises(NoMethodError) do + Class.new(ActionController::Base) do + respond_to :json + end + end + + assert_includes e.message, '`responders` gem' + assert_includes e.message, '~> 2.0' + end + + def test_respond_with + klass = Class.new(ActionController::Base) do + def index + respond_with Customer.new("david", 13) + end + end + + @controller = klass.new + + e = assert_raises(NoMethodError) do + get :index + end + + assert_includes e.message, '`responders` gem' + assert_includes e.message, '~> 2.0' + end +end diff --git a/actionpack/test/controller/new_base/bare_metal_test.rb b/actionpack/test/controller/new_base/bare_metal_test.rb index 7396c850ad..246ba099af 100644 --- a/actionpack/test/controller/new_base/bare_metal_test.rb +++ b/actionpack/test/controller/new_base/bare_metal_test.rb @@ -2,6 +2,8 @@ require "abstract_unit" module BareMetalTest class BareController < ActionController::Metal + include ActionController::RackDelegation + def index self.response_body = "Hello world" end @@ -81,8 +83,8 @@ module BareMetalTest assert_nil headers['Content-Length'] end - test "head :continue (101) does not return a content-type header" do - headers = HeadController.action(:continue).call(Rack::MockRequest.env_for("/")).second + test "head :switching_protocols (101) does not return a content-type header" do + headers = HeadController.action(:switching_protocols).call(Rack::MockRequest.env_for("/")).second assert_nil headers['Content-Type'] assert_nil headers['Content-Length'] end diff --git a/actionpack/test/controller/new_base/render_body_test.rb b/actionpack/test/controller/new_base/render_body_test.rb new file mode 100644 index 0000000000..f4a3db8b41 --- /dev/null +++ b/actionpack/test/controller/new_base/render_body_test.rb @@ -0,0 +1,170 @@ +require 'abstract_unit' + +module RenderBody + class MinimalController < ActionController::Metal + include AbstractController::Rendering + include ActionController::Rendering + + def index + render body: "Hello World!" + end + end + + class SimpleController < ActionController::Base + self.view_paths = [ActionView::FixtureResolver.new] + + def index + render body: "hello david" + end + end + + class WithLayoutController < ::ApplicationController + self.view_paths = [ActionView::FixtureResolver.new( + "layouts/application.erb" => "<%= yield %>, I'm here!", + "layouts/greetings.erb" => "<%= yield %>, I wish thee well.", + "layouts/ivar.erb" => "<%= yield %>, <%= @ivar %>" + )] + + def index + render body: "hello david" + end + + def custom_code + render body: "hello world", status: 404 + end + + def with_custom_code_as_string + render body: "hello world", status: "404 Not Found" + end + + def with_nil + render body: nil + end + + def with_nil_and_status + render body: nil, status: 403 + end + + def with_false + render body: false + end + + def with_layout_true + render body: "hello world", layout: true + end + + def with_layout_false + render body: "hello world", layout: false + end + + def with_layout_nil + render body: "hello world", layout: nil + end + + def with_custom_layout + render body: "hello world", layout: "greetings" + end + + def with_custom_content_type + response.headers['Content-Type'] = 'application/json' + render body: '["troll","face"]' + end + + def with_ivar_in_layout + @ivar = "hello world" + render body: "hello world", layout: "ivar" + end + end + + class RenderBodyTest < Rack::TestCase + test "rendering body from a minimal controller" do + get "/render_body/minimal/index" + assert_body "Hello World!" + assert_status 200 + end + + test "rendering body from an action with default options renders the body with the layout" do + with_routing do |set| + set.draw { get ':controller', action: 'index' } + + get "/render_body/simple" + assert_body "hello david" + assert_status 200 + end + end + + test "rendering body from an action with default options renders the body without the layout" do + with_routing do |set| + set.draw { get ':controller', action: 'index' } + + get "/render_body/with_layout" + + assert_body "hello david" + assert_status 200 + end + end + + test "rendering body, while also providing a custom status code" do + get "/render_body/with_layout/custom_code" + + assert_body "hello world" + assert_status 404 + end + + test "rendering body with nil returns an empty body" do + get "/render_body/with_layout/with_nil" + + assert_body "" + assert_status 200 + end + + test "Rendering body with nil and custom status code returns an empty body and the status" do + get "/render_body/with_layout/with_nil_and_status" + + assert_body "" + assert_status 403 + end + + test "rendering body with false returns the string 'false'" do + get "/render_body/with_layout/with_false" + + assert_body "false" + assert_status 200 + end + + test "rendering body with layout: true" do + get "/render_body/with_layout/with_layout_true" + + assert_body "hello world, I'm here!" + assert_status 200 + end + + test "rendering body with layout: 'greetings'" do + get "/render_body/with_layout/with_custom_layout" + + assert_body "hello world, I wish thee well." + assert_status 200 + end + + test "specified content type should not be removed" do + get "/render_body/with_layout/with_custom_content_type" + + assert_equal %w{ troll face }, JSON.parse(response.body) + assert_equal 'application/json', response.headers['Content-Type'] + end + + test "rendering body with layout: false" do + get "/render_body/with_layout/with_layout_false" + + assert_body "hello world" + assert_status 200 + end + + test "rendering body with layout: nil" do + get "/render_body/with_layout/with_layout_nil" + + assert_body "hello world" + assert_status 200 + end + end +end diff --git a/actionpack/test/controller/new_base/render_file_test.rb b/actionpack/test/controller/new_base/render_file_test.rb index a961cbf849..0c21bb0719 100644 --- a/actionpack/test/controller/new_base/render_file_test.rb +++ b/actionpack/test/controller/new_base/render_file_test.rb @@ -13,15 +13,6 @@ module RenderFile render :file => File.join(File.dirname(__FILE__), '../../fixtures/test/render_file_with_ivar') end - def without_file_key - render File.join(File.dirname(__FILE__), *%w[.. .. fixtures test hello_world]) - end - - def without_file_key_with_instance_variable - @secret = 'in the sauce' - render File.join(File.dirname(__FILE__), '../../fixtures/test/render_file_with_ivar') - end - def relative_path @secret = 'in the sauce' render :file => '../../fixtures/test/render_file_with_ivar' @@ -41,11 +32,6 @@ module RenderFile path = File.join(File.dirname(__FILE__), '../../fixtures/test/render_file_with_locals') render :file => path, :locals => {:secret => 'in the sauce'} end - - def without_file_key_with_locals - path = FIXTURES.join('test/render_file_with_locals').to_s - render path, :locals => {:secret => 'in the sauce'} - end end class TestBasic < Rack::TestCase @@ -61,16 +47,6 @@ module RenderFile assert_response "The secret is in the sauce\n" end - test "rendering path without specifying the :file key" do - get :without_file_key - assert_response "Hello world!" - end - - test "rendering path without specifying the :file key with ivar" do - get :without_file_key_with_instance_variable - assert_response "The secret is in the sauce\n" - end - test "rendering a relative path" do get :relative_path assert_response "The secret is in the sauce\n" @@ -90,10 +66,5 @@ module RenderFile get :with_locals assert_response "The secret is in the sauce\n" end - - test "rendering path without specifying the :file key with locals" do - get :without_file_key_with_locals - assert_response "The secret is in the sauce\n" - end end end diff --git a/actionpack/test/controller/new_base/render_html_test.rb b/actionpack/test/controller/new_base/render_html_test.rb new file mode 100644 index 0000000000..fe11501eeb --- /dev/null +++ b/actionpack/test/controller/new_base/render_html_test.rb @@ -0,0 +1,190 @@ +require 'abstract_unit' + +module RenderHtml + class MinimalController < ActionController::Metal + include AbstractController::Rendering + include ActionController::Rendering + + def index + render html: "Hello World!" + end + end + + class SimpleController < ActionController::Base + self.view_paths = [ActionView::FixtureResolver.new] + + def index + render html: "hello david" + end + end + + class WithLayoutController < ::ApplicationController + self.view_paths = [ActionView::FixtureResolver.new( + "layouts/application.html.erb" => "<%= yield %>, I'm here!", + "layouts/greetings.html.erb" => "<%= yield %>, I wish thee well.", + "layouts/ivar.html.erb" => "<%= yield %>, <%= @ivar %>" + )] + + def index + render html: "hello david" + end + + def custom_code + render html: "hello world", status: 404 + end + + def with_custom_code_as_string + render html: "hello world", status: "404 Not Found" + end + + def with_nil + render html: nil + end + + def with_nil_and_status + render html: nil, status: 403 + end + + def with_false + render html: false + end + + def with_layout_true + render html: "hello world", layout: true + end + + def with_layout_false + render html: "hello world", layout: false + end + + def with_layout_nil + render html: "hello world", layout: nil + end + + def with_custom_layout + render html: "hello world", layout: "greetings" + end + + def with_ivar_in_layout + @ivar = "hello world" + render html: "hello world", layout: "ivar" + end + + def with_unsafe_html_tag + render html: "<p>hello world</p>", layout: nil + end + + def with_safe_html_tag + render html: "<p>hello world</p>".html_safe, layout: nil + end + end + + class RenderHtmlTest < Rack::TestCase + test "rendering text from a minimal controller" do + get "/render_html/minimal/index" + assert_body "Hello World!" + assert_status 200 + end + + test "rendering text from an action with default options renders the text with the layout" do + with_routing do |set| + set.draw { get ':controller', action: 'index' } + + get "/render_html/simple" + assert_body "hello david" + assert_status 200 + end + end + + test "rendering text from an action with default options renders the text without the layout" do + with_routing do |set| + set.draw { get ':controller', action: 'index' } + + get "/render_html/with_layout" + + assert_body "hello david" + assert_status 200 + end + end + + test "rendering text, while also providing a custom status code" do + get "/render_html/with_layout/custom_code" + + assert_body "hello world" + assert_status 404 + end + + test "rendering text with nil returns an empty body" do + get "/render_html/with_layout/with_nil" + + assert_body "" + assert_status 200 + end + + test "Rendering text with nil and custom status code returns an empty body and the status" do + get "/render_html/with_layout/with_nil_and_status" + + assert_body "" + assert_status 403 + end + + test "rendering text with false returns the string 'false'" do + get "/render_html/with_layout/with_false" + + assert_body "false" + assert_status 200 + end + + test "rendering text with layout: true" do + get "/render_html/with_layout/with_layout_true" + + assert_body "hello world, I'm here!" + assert_status 200 + end + + test "rendering text with layout: 'greetings'" do + get "/render_html/with_layout/with_custom_layout" + + assert_body "hello world, I wish thee well." + assert_status 200 + end + + test "rendering text with layout: false" do + get "/render_html/with_layout/with_layout_false" + + assert_body "hello world" + assert_status 200 + end + + test "rendering text with layout: nil" do + get "/render_html/with_layout/with_layout_nil" + + assert_body "hello world" + assert_status 200 + end + + test "rendering html should escape the string if it is not html safe" do + get "/render_html/with_layout/with_unsafe_html_tag" + + assert_body "<p>hello world</p>" + assert_status 200 + end + + test "rendering html should not escape the string if it is html safe" do + get "/render_html/with_layout/with_safe_html_tag" + + assert_body "<p>hello world</p>" + assert_status 200 + end + + test "rendering from minimal controller returns response with text/html content type" do + get "/render_html/minimal/index" + assert_content_type "text/html" + end + + test "rendering from normal controller returns response with text/html content type" do + get "/render_html/simple/index" + assert_content_type "text/html; charset=utf-8" + end + end +end diff --git a/actionpack/test/controller/new_base/render_implicit_action_test.rb b/actionpack/test/controller/new_base/render_implicit_action_test.rb index 1e2191d417..5b4885f7e0 100644 --- a/actionpack/test/controller/new_base/render_implicit_action_test.rb +++ b/actionpack/test/controller/new_base/render_implicit_action_test.rb @@ -6,7 +6,7 @@ module RenderImplicitAction "render_implicit_action/simple/hello_world.html.erb" => "Hello world!", "render_implicit_action/simple/hyphen-ated.html.erb" => "Hello hyphen-ated!", "render_implicit_action/simple/not_implemented.html.erb" => "Not Implemented" - )] + ), ActionView::FileSystemResolver.new(File.expand_path('../../../controller', __FILE__))] def hello_world() end end @@ -33,10 +33,25 @@ module RenderImplicitAction assert_status 200 end + test "render does not traverse the file system" do + assert_raises(AbstractController::ActionNotFound) do + action_name = %w(.. .. fixtures shared).join(File::SEPARATOR) + SimpleController.action(action_name).call(Rack::MockRequest.env_for("/")) + end + end + test "available_action? returns true for implicit actions" do assert SimpleController.new.available_action?(:hello_world) assert SimpleController.new.available_action?(:"hyphen-ated") assert SimpleController.new.available_action?(:not_implemented) end + + test "available_action? does not allow File::SEPARATOR on the name" do + action_name = %w(evil .. .. path).join(File::SEPARATOR) + assert_equal false, SimpleController.new.available_action?(action_name.to_sym) + + action_name = %w(evil path).join(File::SEPARATOR) + assert_equal false, SimpleController.new.available_action?(action_name.to_sym) + end end end diff --git a/actionpack/test/controller/new_base/render_plain_test.rb b/actionpack/test/controller/new_base/render_plain_test.rb new file mode 100644 index 0000000000..0e36d36b50 --- /dev/null +++ b/actionpack/test/controller/new_base/render_plain_test.rb @@ -0,0 +1,168 @@ +require 'abstract_unit' + +module RenderPlain + class MinimalController < ActionController::Metal + include AbstractController::Rendering + include ActionController::Rendering + + def index + render plain: "Hello World!" + end + end + + class SimpleController < ActionController::Base + self.view_paths = [ActionView::FixtureResolver.new] + + def index + render plain: "hello david" + end + end + + class WithLayoutController < ::ApplicationController + self.view_paths = [ActionView::FixtureResolver.new( + "layouts/application.text.erb" => "<%= yield %>, I'm here!", + "layouts/greetings.text.erb" => "<%= yield %>, I wish thee well.", + "layouts/ivar.text.erb" => "<%= yield %>, <%= @ivar %>" + )] + + def index + render plain: "hello david" + end + + def custom_code + render plain: "hello world", status: 404 + end + + def with_custom_code_as_string + render plain: "hello world", status: "404 Not Found" + end + + def with_nil + render plain: nil + end + + def with_nil_and_status + render plain: nil, status: 403 + end + + def with_false + render plain: false + end + + def with_layout_true + render plain: "hello world", layout: true + end + + def with_layout_false + render plain: "hello world", layout: false + end + + def with_layout_nil + render plain: "hello world", layout: nil + end + + def with_custom_layout + render plain: "hello world", layout: "greetings" + end + + def with_ivar_in_layout + @ivar = "hello world" + render plain: "hello world", layout: "ivar" + end + end + + class RenderPlainTest < Rack::TestCase + test "rendering text from a minimal controller" do + get "/render_plain/minimal/index" + assert_body "Hello World!" + assert_status 200 + end + + test "rendering text from an action with default options renders the text with the layout" do + with_routing do |set| + set.draw { get ':controller', action: 'index' } + + get "/render_plain/simple" + assert_body "hello david" + assert_status 200 + end + end + + test "rendering text from an action with default options renders the text without the layout" do + with_routing do |set| + set.draw { get ':controller', action: 'index' } + + get "/render_plain/with_layout" + + assert_body "hello david" + assert_status 200 + end + end + + test "rendering text, while also providing a custom status code" do + get "/render_plain/with_layout/custom_code" + + assert_body "hello world" + assert_status 404 + end + + test "rendering text with nil returns an empty body" do + get "/render_plain/with_layout/with_nil" + + assert_body "" + assert_status 200 + end + + test "Rendering text with nil and custom status code returns an empty body and the status" do + get "/render_plain/with_layout/with_nil_and_status" + + assert_body "" + assert_status 403 + end + + test "rendering text with false returns the string 'false'" do + get "/render_plain/with_layout/with_false" + + assert_body "false" + assert_status 200 + end + + test "rendering text with layout: true" do + get "/render_plain/with_layout/with_layout_true" + + assert_body "hello world, I'm here!" + assert_status 200 + end + + test "rendering text with layout: 'greetings'" do + get "/render_plain/with_layout/with_custom_layout" + + assert_body "hello world, I wish thee well." + assert_status 200 + end + + test "rendering text with layout: false" do + get "/render_plain/with_layout/with_layout_false" + + assert_body "hello world" + assert_status 200 + end + + test "rendering text with layout: nil" do + get "/render_plain/with_layout/with_layout_nil" + + assert_body "hello world" + assert_status 200 + end + + test "rendering from minimal controller returns response with text/plain content type" do + get "/render_plain/minimal/index" + assert_content_type "text/plain" + end + + test "rendering from normal controller returns response with text/plain content type" do + get "/render_plain/simple/index" + assert_content_type "text/plain; charset=utf-8" + end + end +end diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb index b7a9cf92f2..42a86b1d0d 100644 --- a/actionpack/test/controller/new_base/render_template_test.rb +++ b/actionpack/test/controller/new_base/render_template_test.rb @@ -9,7 +9,7 @@ module RenderTemplate "locals.html.erb" => "The secret is <%= secret %>", "xml_template.xml.builder" => "xml.html do\n xml.p 'Hello'\nend", "with_raw.html.erb" => "Hello <%=raw '<strong>this is raw</strong>' %>", - "with_implicit_raw.html.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in a html template", + "with_implicit_raw.html.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in an html template", "with_implicit_raw.text.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in a text template", "test/with_json.html.erb" => "<%= render :template => 'test/with_json', :formats => [:json] %>", "test/with_json.json.erb" => "<%= render :template => 'test/final', :formats => [:json] %>", @@ -45,6 +45,10 @@ module RenderTemplate render :template => "locals", :locals => { :secret => 'area51' } end + def with_locals_without_key + render "locals", :locals => { :secret => 'area51' } + end + def builder_template render :template => "xml_template" end @@ -101,6 +105,11 @@ module RenderTemplate assert_response "The secret is area51" end + test "rendering a template with local variables without key" do + get :with_locals + assert_response "The secret is area51" + end + test "rendering a builder template" do get :builder_template, "format" => "xml" assert_response "<html>\n <p>Hello</p>\n</html>\n" @@ -114,7 +123,7 @@ module RenderTemplate get :with_implicit_raw - assert_body "Hello <strong>this is also raw</strong> in a html template" + assert_body "Hello <strong>this is also raw</strong> in an html template" assert_status 200 get :with_implicit_raw, format: 'text' diff --git a/actionpack/test/controller/new_base/render_text_test.rb b/actionpack/test/controller/new_base/render_text_test.rb index 2a253799f3..10bad57cd6 100644 --- a/actionpack/test/controller/new_base/render_text_test.rb +++ b/actionpack/test/controller/new_base/render_text_test.rb @@ -14,7 +14,7 @@ module RenderText self.view_paths = [ActionView::FixtureResolver.new] def index - render :text => "hello david" + render text: "hello david" end end @@ -26,48 +26,48 @@ module RenderText )] def index - render :text => "hello david" + render text: "hello david" end def custom_code - render :text => "hello world", :status => 404 + render text: "hello world", status: 404 end def with_custom_code_as_string - render :text => "hello world", :status => "404 Not Found" + render text: "hello world", status: "404 Not Found" end def with_nil - render :text => nil + render text: nil end def with_nil_and_status - render :text => nil, :status => 403 + render text: nil, status: 403 end def with_false - render :text => false + render text: false end def with_layout_true - render :text => "hello world", :layout => true + render text: "hello world", layout: true end def with_layout_false - render :text => "hello world", :layout => false + render text: "hello world", layout: false end def with_layout_nil - render :text => "hello world", :layout => nil + render text: "hello world", layout: nil end def with_custom_layout - render :text => "hello world", :layout => "greetings" + render text: "hello world", layout: "greetings" end def with_ivar_in_layout @ivar = "hello world" - render :text => "hello world", :layout => "ivar" + render text: "hello world", layout: "ivar" end end @@ -80,7 +80,7 @@ module RenderText test "rendering text from an action with default options renders the text with the layout" do with_routing do |set| - set.draw { get ':controller', :action => 'index' } + set.draw { get ':controller', action: 'index' } get "/render_text/simple" assert_body "hello david" @@ -90,7 +90,7 @@ module RenderText test "rendering text from an action with default options renders the text without the layout" do with_routing do |set| - set.draw { get ':controller', :action => 'index' } + set.draw { get ':controller', action: 'index' } get "/render_text/with_layout" @@ -106,17 +106,17 @@ module RenderText assert_status 404 end - test "rendering text with nil returns an empty body padded for Safari" do + test "rendering text with nil returns an empty body" do get "/render_text/with_layout/with_nil" - assert_body " " + assert_body "" assert_status 200 end - test "Rendering text with nil and custom status code returns an empty body padded for Safari and the status" do + test "Rendering text with nil and custom status code returns an empty body and the status" do get "/render_text/with_layout/with_nil_and_status" - assert_body " " + assert_body "" assert_status 403 end @@ -127,28 +127,28 @@ module RenderText assert_status 200 end - test "rendering text with :layout => true" do + test "rendering text with layout: true" do get "/render_text/with_layout/with_layout_true" assert_body "hello world, I'm here!" assert_status 200 end - test "rendering text with :layout => 'greetings'" do + test "rendering text with layout: 'greetings'" do get "/render_text/with_layout/with_custom_layout" assert_body "hello world, I wish thee well." assert_status 200 end - test "rendering text with :layout => false" do + test "rendering text with layout: false" do get "/render_text/with_layout/with_layout_false" assert_body "hello world" assert_status 200 end - test "rendering text with :layout => nil" do + test "rendering text with layout: nil" do get "/render_text/with_layout/with_layout_nil" assert_body "hello world" diff --git a/actionpack/test/controller/parameters/accessors_test.rb b/actionpack/test/controller/parameters/accessors_test.rb new file mode 100644 index 0000000000..97875c3cbb --- /dev/null +++ b/actionpack/test/controller/parameters/accessors_test.rb @@ -0,0 +1,125 @@ +require 'abstract_unit' +require 'action_controller/metal/strong_parameters' +require 'active_support/core_ext/hash/transform_values' + +class ParametersAccessorsTest < ActiveSupport::TestCase + setup do + @params = ActionController::Parameters.new( + person: { + age: '32', + name: { + first: 'David', + last: 'Heinemeier Hansson' + }, + addresses: [{city: 'Chicago', state: 'Illinois'}] + } + ) + end + + test "[] retains permitted status" do + @params.permit! + assert @params[:person].permitted? + assert @params[:person][:name].permitted? + end + + test "[] retains unpermitted status" do + assert_not @params[:person].permitted? + assert_not @params[:person][:name].permitted? + end + + test "each carries permitted status" do + @params.permit! + @params.each { |key, value| assert(value.permitted?) if key == "person" } + end + + test "each carries unpermitted status" do + @params.each { |key, value| assert_not(value.permitted?) if key == "person" } + end + + test "each_pair carries permitted status" do + @params.permit! + @params.each_pair { |key, value| assert(value.permitted?) if key == "person" } + end + + test "each_pair carries unpermitted status" do + @params.each_pair { |key, value| assert_not(value.permitted?) if key == "person" } + end + + test "except retains permitted status" do + @params.permit! + assert @params.except(:person).permitted? + assert @params[:person].except(:name).permitted? + end + + test "except retains unpermitted status" do + assert_not @params.except(:person).permitted? + assert_not @params[:person].except(:name).permitted? + end + + test "fetch retains permitted status" do + @params.permit! + assert @params.fetch(:person).permitted? + assert @params[:person].fetch(:name).permitted? + end + + test "fetch retains unpermitted status" do + assert_not @params.fetch(:person).permitted? + assert_not @params[:person].fetch(:name).permitted? + end + + test "reject retains permitted status" do + assert_not @params.reject { |k| k == "person" }.permitted? + end + + test "reject retains unpermitted status" do + @params.permit! + assert @params.reject { |k| k == "person" }.permitted? + end + + test "select retains permitted status" do + @params.permit! + assert @params.select { |k| k == "person" }.permitted? + end + + test "select retains unpermitted status" do + assert_not @params.select { |k| k == "person" }.permitted? + end + + test "slice retains permitted status" do + @params.permit! + assert @params.slice(:person).permitted? + end + + test "slice retains unpermitted status" do + assert_not @params.slice(:person).permitted? + end + + test "transform_keys retains permitted status" do + @params.permit! + assert @params.transform_keys { |k| k }.permitted? + end + + test "transform_keys retains unpermitted status" do + assert_not @params.transform_keys { |k| k }.permitted? + end + + test "transform_values retains permitted status" do + @params.permit! + assert @params.transform_values { |v| v }.permitted? + end + + test "transform_values retains unpermitted status" do + assert_not @params.transform_values { |v| v }.permitted? + end + + test "values_at retains permitted status" do + @params.permit! + assert @params.values_at(:person).first.permitted? + assert @params[:person].values_at(:name).first.permitted? + end + + test "values_at retains unpermitted status" do + assert_not @params.values_at(:person).first.permitted? + assert_not @params[:person].values_at(:name).first.permitted? + end +end diff --git a/actionpack/test/controller/parameters/always_permitted_parameters_test.rb b/actionpack/test/controller/parameters/always_permitted_parameters_test.rb new file mode 100644 index 0000000000..059f310d49 --- /dev/null +++ b/actionpack/test/controller/parameters/always_permitted_parameters_test.rb @@ -0,0 +1,29 @@ +require 'abstract_unit' +require 'action_controller/metal/strong_parameters' + +class AlwaysPermittedParametersTest < ActiveSupport::TestCase + def setup + ActionController::Parameters.action_on_unpermitted_parameters = :raise + ActionController::Parameters.always_permitted_parameters = %w( controller action format ) + end + + def teardown + ActionController::Parameters.action_on_unpermitted_parameters = false + ActionController::Parameters.always_permitted_parameters = %w( controller action ) + end + + test "shows deprecations warning on NEVER_UNPERMITTED_PARAMS" do + assert_deprecated do + ActionController::Parameters::NEVER_UNPERMITTED_PARAMS + end + end + + test "permits parameters that are whitelisted" do + params = ActionController::Parameters.new({ + book: { pages: 65 }, + format: "json" + }) + permitted = params.permit book: [:pages] + assert permitted.permitted? + end +end diff --git a/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb index 22e603b881..9ce04b9aeb 100644 --- a/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb +++ b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb @@ -10,23 +10,45 @@ class LogOnUnpermittedParamsTest < ActiveSupport::TestCase ActionController::Parameters.action_on_unpermitted_parameters = false end - test "logs on unexpected params" do + test "logs on unexpected param" do params = ActionController::Parameters.new({ book: { pages: 65 }, fishing: "Turnips" }) - assert_logged("Unpermitted parameters: fishing") do + assert_logged("Unpermitted parameter: fishing") do params.permit(book: [:pages]) end end - test "logs on unexpected nested params" do + test "logs on unexpected params" do + params = ActionController::Parameters.new({ + book: { pages: 65 }, + fishing: "Turnips", + car: "Mersedes" + }) + + assert_logged("Unpermitted parameters: fishing, car") do + params.permit(book: [:pages]) + end + end + + test "logs on unexpected nested param" do params = ActionController::Parameters.new({ book: { pages: 65, title: "Green Cats and where to find then." } }) - assert_logged("Unpermitted parameters: title") do + assert_logged("Unpermitted parameter: title") do + params.permit(book: [:pages]) + end + end + + test "logs on unexpected nested params" do + params = ActionController::Parameters.new({ + book: { pages: 65, title: "Green Cats and where to find then.", author: "G. A. Dog" } + }) + + assert_logged("Unpermitted parameters: title, author") do params.permit(book: [:pages]) end end diff --git a/actionpack/test/controller/parameters/mutators_test.rb b/actionpack/test/controller/parameters/mutators_test.rb new file mode 100644 index 0000000000..744d8664be --- /dev/null +++ b/actionpack/test/controller/parameters/mutators_test.rb @@ -0,0 +1,99 @@ +require 'abstract_unit' +require 'action_controller/metal/strong_parameters' +require 'active_support/core_ext/hash/transform_values' + +class ParametersMutatorsTest < ActiveSupport::TestCase + setup do + @params = ActionController::Parameters.new( + person: { + age: '32', + name: { + first: 'David', + last: 'Heinemeier Hansson' + }, + addresses: [{city: 'Chicago', state: 'Illinois'}] + } + ) + end + + test "delete retains permitted status" do + @params.permit! + assert @params.delete(:person).permitted? + end + + test "delete retains unpermitted status" do + assert_not @params.delete(:person).permitted? + end + + test "delete_if retains permitted status" do + @params.permit! + assert @params.delete_if { |k| k == "person" }.permitted? + end + + test "delete_if retains unpermitted status" do + assert_not @params.delete_if { |k| k == "person" }.permitted? + end + + test "extract! retains permitted status" do + @params.permit! + assert @params.extract!(:person).permitted? + end + + test "extract! retains unpermitted status" do + assert_not @params.extract!(:person).permitted? + end + + test "keep_if retains permitted status" do + @params.permit! + assert @params.keep_if { |k,v| k == "person" }.permitted? + end + + test "keep_if retains unpermitted status" do + assert_not @params.keep_if { |k,v| k == "person" }.permitted? + end + + test "reject! retains permitted status" do + @params.permit! + assert @params.reject! { |k| k == "person" }.permitted? + end + + test "reject! retains unpermitted status" do + assert_not @params.reject! { |k| k == "person" }.permitted? + end + + test "select! retains permitted status" do + @params.permit! + assert @params.select! { |k| k != "person" }.permitted? + end + + test "select! retains unpermitted status" do + assert_not @params.select! { |k| k != "person" }.permitted? + end + + test "slice! retains permitted status" do + @params.permit! + assert @params.slice!(:person).permitted? + end + + test "slice! retains unpermitted status" do + assert_not @params.slice!(:person).permitted? + end + + test "transform_keys! retains permitted status" do + @params.permit! + assert @params.transform_keys! { |k| k }.permitted? + end + + test "transform_keys! retains unpermitted status" do + assert_not @params.transform_keys! { |k| k }.permitted? + end + + test "transform_values! retains permitted status" do + @params.permit! + assert @params.transform_values! { |v| v }.permitted? + end + + test "transform_values! retains unpermitted status" do + assert_not @params.transform_values! { |v| v }.permitted? + end +end diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb index b60c5f058d..2ed486516d 100644 --- a/actionpack/test/controller/parameters/parameters_permit_test.rb +++ b/actionpack/test/controller/parameters/parameters_permit_test.rb @@ -8,9 +8,16 @@ class ParametersPermitTest < ActiveSupport::TestCase end setup do - @params = ActionController::Parameters.new({ person: { - age: "32", name: { first: "David", last: "Heinemeier Hansson" } - }}) + @params = ActionController::Parameters.new( + person: { + age: '32', + name: { + first: 'David', + last: 'Heinemeier Hansson' + }, + addresses: [{city: 'Chicago', state: 'Illinois'}] + } + ) @struct_fields = [] %w(0 1 12).each do |number| @@ -153,45 +160,38 @@ class ParametersPermitTest < ActiveSupport::TestCase assert_equal nil, params[:foo] end - test "fetch doesnt raise ParameterMissing exception if there is a default" do - assert_equal "monkey", @params.fetch(:foo, "monkey") - assert_equal "monkey", @params.fetch(:foo) { "monkey" } + test 'hashes in array values get wrapped' do + params = ActionController::Parameters.new(foo: [{}, {}]) + params[:foo].each do |hash| + assert !hash.permitted? + end end - test "not permitted is sticky on accessors" do - assert !@params.slice(:person).permitted? - assert !@params[:person][:name].permitted? - assert !@params[:person].except(:name).permitted? - - @params.each { |key, value| assert(!value.permitted?) if key == "person" } - - assert !@params.fetch(:person).permitted? - - assert !@params.values_at(:person).first.permitted? + # Strong params has an optimization to avoid looping every time you read + # a key whose value is an array and building a new object. We check that + # optimization here. + test 'arrays are converted at most once' do + params = ActionController::Parameters.new(foo: [{}]) + assert_same params[:foo], params[:foo] end - test "permitted is sticky on accessors" do - @params.permit! - assert @params.slice(:person).permitted? - assert @params[:person][:name].permitted? - assert @params[:person].except(:name).permitted? - - @params.each { |key, value| assert(value.permitted?) if key == "person" } - - assert @params.fetch(:person).permitted? - - assert @params.values_at(:person).first.permitted? + # Strong params has an internal cache to avoid duplicated loops in the most + # common usage pattern. See the docs of the method `converted_arrays`. + # + # This test checks that if we push a hash to an array (in-place modification) + # the cache does not get fooled, the hash is still wrapped as strong params, + # and not permitted. + test 'mutated arrays are detected' do + params = ActionController::Parameters.new(users: [{id: 1}]) + + permitted = params.permit(users: [:id]) + permitted[:users] << {injected: 1} + assert_not permitted[:users].last.permitted? end - test "not permitted is sticky on mutators" do - assert !@params.delete_if { |k| k == "person" }.permitted? - assert !@params.keep_if { |k,v| k == "person" }.permitted? - end - - test "permitted is sticky on mutators" do - @params.permit! - assert @params.delete_if { |k| k == "person" }.permitted? - assert @params.keep_if { |k,v| k == "person" }.permitted? + test "fetch doesnt raise ParameterMissing exception if there is a default" do + assert_equal "monkey", @params.fetch(:foo, "monkey") + assert_equal "monkey", @params.fetch(:foo) { "monkey" } end test "not permitted is sticky beyond merges" do @@ -221,6 +221,7 @@ class ParametersPermitTest < ActiveSupport::TestCase assert @params.permitted? assert @params[:person].permitted? assert @params[:person][:name].permitted? + assert @params[:person][:addresses][0].permitted? end test "permitted takes a default value when Parameters.permit_all_parameters is set" do @@ -240,4 +241,49 @@ class ParametersPermitTest < ActiveSupport::TestCase test "permitting parameters as an array" do assert_equal "32", @params[:person].permit([ :age ])[:age] end + + test "to_h returns empty hash on unpermitted params" do + assert @params.to_h.is_a? Hash + assert_not @params.to_h.is_a? ActionController::Parameters + assert @params.to_h.empty? + end + + test "to_h returns converted hash on permitted params" do + @params.permit! + + assert @params.to_h.is_a? Hash + assert_not @params.to_h.is_a? ActionController::Parameters + assert_equal @params.to_hash, @params.to_h + end + + test "to_h returns converted hash when .permit_all_parameters is set" do + begin + ActionController::Parameters.permit_all_parameters = true + params = ActionController::Parameters.new(crab: "Senjougahara Hitagi") + + assert params.to_h.is_a? Hash + assert_not @params.to_h.is_a? ActionController::Parameters + assert_equal({ "crab" => "Senjougahara Hitagi" }, params.to_h) + ensure + ActionController::Parameters.permit_all_parameters = false + end + end + + test "to_h returns always permitted parameter on unpermitted params" do + params = ActionController::Parameters.new( + controller: "users", + action: "create", + user: { + name: "Sengoku Nadeko" + } + ) + + assert_equal({ "controller" => "users", "action" => "create" }, params.to_h) + end + + test "to_unsafe_h returns unfiltered params" do + assert @params.to_h.is_a? Hash + assert_not @params.to_h.is_a? ActionController::Parameters + assert_equal @params.to_hash, @params.to_unsafe_h + end end diff --git a/actionpack/test/controller/parameters/parameters_require_test.rb b/actionpack/test/controller/parameters/parameters_require_test.rb deleted file mode 100644 index bdaba8d2d8..0000000000 --- a/actionpack/test/controller/parameters/parameters_require_test.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'abstract_unit' -require 'action_controller/metal/strong_parameters' - -class ParametersRequireTest < ActiveSupport::TestCase - test "required parameters must be present not merely not nil" do - assert_raises(ActionController::ParameterMissing) do - ActionController::Parameters.new(person: {}).require(:person) - end - end -end diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb index d87e2b85b0..645ecae220 100644 --- a/actionpack/test/controller/params_wrapper_test.rb +++ b/actionpack/test/controller/params_wrapper_test.rb @@ -188,6 +188,26 @@ class ParamsWrapperTest < ActionController::TestCase assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'user' => { 'username' => 'sikachu', 'title' => 'Developer' }}) end end + + def test_preserves_query_string_params + with_default_wrapper_options do + @request.env['CONTENT_TYPE'] = 'application/json' + get :parse, { 'user' => { 'username' => 'nixon' } } + assert_parameters( + {'user' => { 'username' => 'nixon' } } + ) + end + end + + def test_empty_parameter_set + with_default_wrapper_options do + @request.env['CONTENT_TYPE'] = 'application/json' + post :parse, {} + assert_parameters( + {'user' => { } } + ) + end + end end class NamespacedParamsWrapperTest < ActionController::TestCase @@ -317,14 +337,26 @@ class IrregularInflectionParamsWrapperTest < ActionController::TestCase tests ParamswrappernewsController def test_uses_model_attribute_names_with_irregular_inflection - ActiveSupport::Inflector.inflections do |inflect| - inflect.irregular 'paramswrappernews_item', 'paramswrappernews' - end + with_dup do + ActiveSupport::Inflector.inflections do |inflect| + inflect.irregular 'paramswrappernews_item', 'paramswrappernews' + end - with_default_wrapper_options do - @request.env['CONTENT_TYPE'] = 'application/json' - post :parse, { 'username' => 'sikachu', 'test_attr' => 'test_value' } - assert_parameters({ 'username' => 'sikachu', 'test_attr' => 'test_value', 'paramswrappernews_item' => { 'test_attr' => 'test_value' }}) + with_default_wrapper_options do + @request.env['CONTENT_TYPE'] = 'application/json' + post :parse, { 'username' => 'sikachu', 'test_attr' => 'test_value' } + assert_parameters({ 'username' => 'sikachu', 'test_attr' => 'test_value', 'paramswrappernews_item' => { 'test_attr' => 'test_value' }}) + end end end + + private + + def with_dup + original = ActiveSupport::Inflector::Inflections.instance_variable_get(:@__instance__)[:en] + ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: original.dup) + yield + ensure + ActiveSupport::Inflector::Inflections.instance_variable_set(:@__instance__, en: original) + end end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 4331333b98..103ca9c776 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -90,6 +90,10 @@ class RedirectController < ActionController::Base redirect_to nil end + def redirect_to_params + redirect_to ActionController::Parameters.new(status: 200, protocol: 'javascript', f: '%0Aeval(name)') + end + def redirect_to_with_block redirect_to proc { "http://www.rubyonrails.org/" } end @@ -281,6 +285,12 @@ class RedirectTest < ActionController::TestCase end end + def test_redirect_to_params + assert_raise(ActionController::ActionControllerError) do + get :redirect_to_params + end + end + def test_redirect_to_with_block get :redirect_to_with_block assert_response :redirect diff --git a/actionpack/test/controller/render_js_test.rb b/actionpack/test/controller/render_js_test.rb index f070109b27..d550422a2f 100644 --- a/actionpack/test/controller/render_js_test.rb +++ b/actionpack/test/controller/render_js_test.rb @@ -22,7 +22,7 @@ class RenderJSTest < ActionController::TestCase tests TestController def test_render_vanilla_js - get :render_vanilla_js_hello + xhr :get, :render_vanilla_js_hello assert_equal "alert('hello')", @response.body assert_equal "text/javascript", @response.content_type end diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb index 7c0a6bd67e..ada978aa11 100644 --- a/actionpack/test/controller/render_json_test.rb +++ b/actionpack/test/controller/render_json_test.rb @@ -100,13 +100,13 @@ class RenderJsonTest < ActionController::TestCase end def test_render_json_with_callback - get :render_json_hello_world_with_callback - assert_equal 'alert({"hello":"world"})', @response.body + xhr :get, :render_json_hello_world_with_callback + assert_equal '/**/alert({"hello":"world"})', @response.body assert_equal 'text/javascript', @response.content_type end def test_render_json_with_custom_content_type - get :render_json_with_custom_content_type + xhr :get, :render_json_with_custom_content_type assert_equal '{"hello":"world"}', @response.body assert_equal 'text/javascript', @response.content_type end diff --git a/actionpack/test/controller/render_other_test.rb b/actionpack/test/controller/render_other_test.rb index b5e74e373d..af50e11261 100644 --- a/actionpack/test/controller/render_other_test.rb +++ b/actionpack/test/controller/render_other_test.rb @@ -1,9 +1,5 @@ require 'abstract_unit' -ActionController.add_renderer :simon do |says, options| - self.content_type = Mime::TEXT - self.response_body = "Simon says: #{says}" -end class RenderOtherTest < ActionController::TestCase class TestController < ActionController::Base @@ -15,7 +11,14 @@ class RenderOtherTest < ActionController::TestCase tests TestController def test_using_custom_render_option + ActionController.add_renderer :simon do |says, options| + self.content_type = Mime::TEXT + self.response_body = "Simon says: #{says}" + end + get :render_simon_says assert_equal "Simon says: foo", @response.body + ensure + ActionController.remove_renderer :simon end end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 26806fb03f..929b161eb6 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -10,11 +10,17 @@ class TestControllerWithExtraEtags < ActionController::Base etag { nil } def fresh - render text: "stale" if stale?(etag: '123') + render text: "stale" if stale?(etag: '123', template: false) end def array - render text: "stale" if stale?(etag: %w(1 2 3)) + render text: "stale" if stale?(etag: %w(1 2 3), template: false) + end + + def with_template + if stale? template: 'test/hello_world' + render text: 'stale' + end end end @@ -211,6 +217,15 @@ class TestController < ActionController::Base head :forbidden, :x_custom_header => "something" end + def head_with_no_content + # Fill in the headers with dummy data to make + # sure they get removed during the testing + response.headers["Content-Type"] = "dummy" + response.headers["Content-Length"] = 42 + + head 204 + end + private def set_variable_for_layout @@ -242,6 +257,8 @@ class MetalTestController < ActionController::Metal include AbstractController::Rendering include ActionView::Rendering include ActionController::Rendering + include ActionController::RackDelegation + def accessing_logger_in_template render :inline => "<%= logger.class %>" @@ -387,10 +404,6 @@ end class EtagRenderTest < ActionController::TestCase tests TestControllerWithExtraEtags - def setup - super - end - def test_multiple_etags @request.if_none_match = etag(["123", 'ab', :cde, [:f]]) get :fresh @@ -411,6 +424,32 @@ class EtagRenderTest < ActionController::TestCase assert_response :success end + def test_etag_reflects_template_digest + get :with_template + assert_response :ok + assert_not_nil etag = @response.etag + + request.if_none_match = etag + get :with_template + assert_response :not_modified + + # Modify the template digest + path = File.expand_path('../../fixtures/test/hello_world.erb', __FILE__) + old = File.read(path) + + begin + File.write path, 'foo' + ActionView::Digestor.cache.clear + + request.if_none_match = etag + get :with_template + assert_response :ok + assert_not_equal etag, @response.etag + ensure + File.write path, old + end + end + def etag(record) Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(record)).inspect end @@ -515,6 +554,14 @@ class HeadRenderTest < ActionController::TestCase end end + def test_head_with_no_content + get :head_with_no_content + + assert_equal 204, @response.status + assert_nil @response.headers["Content-Type"] + assert_nil @response.headers["Content-Length"] + end + def test_head_with_string_status get :head_with_string_status, :status => "404 Eat Dirt" assert_equal 404, @response.response_code diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 727db79241..3e0bfe8d14 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -52,18 +52,36 @@ module RequestForgeryProtectionActions render :inline => "<%= form_for(:some_resource, :remote => true, :authenticity_token => 'external_token') {} %>" end + def same_origin_js + render js: 'foo();' + end + + def negotiate_same_origin + respond_to do |format| + format.js { same_origin_js } + end + end + + def cross_origin_js + same_origin_js + end + + def negotiate_cross_origin + negotiate_same_origin + end + def rescue_action(e) raise e end end # sample controllers class RequestForgeryProtectionControllerUsingResetSession < ActionController::Base include RequestForgeryProtectionActions - protect_from_forgery :only => %w(index meta), :with => :reset_session + protect_from_forgery :only => %w(index meta same_origin_js negotiate_same_origin), :with => :reset_session end class RequestForgeryProtectionControllerUsingException < ActionController::Base include RequestForgeryProtectionActions - protect_from_forgery :only => %w(index meta), :with => :exception + protect_from_forgery :only => %w(index meta same_origin_js negotiate_same_origin), :with => :exception end class RequestForgeryProtectionControllerUsingNullSession < ActionController::Base @@ -107,27 +125,29 @@ end module RequestForgeryProtectionTests def setup @token = "cf50faa3fe97702ca1ae" - - SecureRandom.stubs(:base64).returns(@token) + @controller.stubs(:form_authenticity_token).returns(@token) + @controller.stubs(:valid_authenticity_token?).with{ |_, t| t == @token }.returns(true) + @controller.stubs(:valid_authenticity_token?).with{ |_, t| t != @token }.returns(false) + @old_request_forgery_protection_token = ActionController::Base.request_forgery_protection_token ActionController::Base.request_forgery_protection_token = :custom_authenticity_token end def teardown - ActionController::Base.request_forgery_protection_token = nil + ActionController::Base.request_forgery_protection_token = @old_request_forgery_protection_token end def test_should_render_form_with_token_tag assert_not_blocked do get :index end - assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', @token + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_render_button_to_with_token_tag assert_not_blocked do get :show_button end - assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', @token + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_render_form_without_token_tag_if_remote @@ -157,7 +177,7 @@ module RequestForgeryProtectionTests assert_not_blocked do get :form_for_remote_with_external_token end - assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', 'external_token' + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', 'external_token' ensure ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms = original end @@ -167,21 +187,21 @@ module RequestForgeryProtectionTests assert_not_blocked do get :form_for_remote_with_external_token end - assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', 'external_token' + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', 'external_token' end def test_should_render_form_with_token_tag_if_remote_and_authenticity_token_requested assert_not_blocked do get :form_for_remote_with_token end - assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', @token + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_render_form_with_token_tag_with_authenticity_token_requested assert_not_blocked do get :form_for_with_token end - assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', @token + assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token end def test_should_allow_get @@ -201,7 +221,7 @@ module RequestForgeryProtectionTests end def test_should_not_allow_post_without_token_irrespective_of_format - assert_blocked { post :index, :format=>'xml' } + assert_blocked { post :index, format: 'xml' } end def test_should_not_allow_patch_without_token @@ -271,6 +291,64 @@ module RequestForgeryProtectionTests end end + def test_should_not_warn_if_csrf_logging_disabled + old_logger = ActionController::Base.logger + logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new + ActionController::Base.logger = logger + ActionController::Base.log_warning_on_csrf_failure = false + + begin + assert_blocked { post :index } + + assert_equal 0, logger.logged(:warn).size + ensure + ActionController::Base.logger = old_logger + ActionController::Base.log_warning_on_csrf_failure = true + end + end + + def test_should_only_allow_same_origin_js_get_with_xhr_header + assert_cross_origin_blocked { get :same_origin_js } + assert_cross_origin_blocked { get :same_origin_js, format: 'js' } + assert_cross_origin_blocked do + @request.accept = 'text/javascript' + get :negotiate_same_origin + end + + assert_cross_origin_not_blocked { xhr :get, :same_origin_js } + assert_cross_origin_not_blocked { xhr :get, :same_origin_js, format: 'js' } + assert_cross_origin_not_blocked do + @request.accept = 'text/javascript' + xhr :get, :negotiate_same_origin + end + end + + # Allow non-GET requests since GET is all a remote <script> tag can muster. + def test_should_allow_non_get_js_without_xhr_header + assert_cross_origin_not_blocked { post :same_origin_js, custom_authenticity_token: @token } + assert_cross_origin_not_blocked { post :same_origin_js, format: 'js', custom_authenticity_token: @token } + assert_cross_origin_not_blocked do + @request.accept = 'text/javascript' + post :negotiate_same_origin, custom_authenticity_token: @token + end + end + + def test_should_only_allow_cross_origin_js_get_without_xhr_header_if_protection_disabled + assert_cross_origin_not_blocked { get :cross_origin_js } + assert_cross_origin_not_blocked { get :cross_origin_js, format: 'js' } + assert_cross_origin_not_blocked do + @request.accept = 'text/javascript' + get :negotiate_cross_origin + end + + assert_cross_origin_not_blocked { xhr :get, :cross_origin_js } + assert_cross_origin_not_blocked { xhr :get, :cross_origin_js, format: 'js' } + assert_cross_origin_not_blocked do + @request.accept = 'text/javascript' + xhr :get, :negotiate_cross_origin + end + end + def assert_blocked session[:something_like_user_id] = 1 yield @@ -282,6 +360,16 @@ module RequestForgeryProtectionTests assert_nothing_raised { yield } assert_response :success end + + def assert_cross_origin_blocked + assert_raises(ActionController::InvalidCrossOriginRequest) do + yield + end + end + + def assert_cross_origin_not_blocked + assert_not_blocked { yield } + end end # OK let's get our test on @@ -290,28 +378,30 @@ class RequestForgeryProtectionControllerUsingResetSessionTest < ActionController include RequestForgeryProtectionTests setup do + @old_request_forgery_protection_token = ActionController::Base.request_forgery_protection_token ActionController::Base.request_forgery_protection_token = :custom_authenticity_token end teardown do - ActionController::Base.request_forgery_protection_token = nil + ActionController::Base.request_forgery_protection_token = @old_request_forgery_protection_token end test 'should emit a csrf-param meta tag and a csrf-token meta tag' do - SecureRandom.stubs(:base64).returns(@token + '<=?') + @controller.stubs(:form_authenticity_token).returns(@token + '<=?') get :meta assert_select 'meta[name=?][content=?]', 'csrf-param', 'custom_authenticity_token' - assert_select 'meta[name=?][content=?]', 'csrf-token', 'cf50faa3fe97702ca1ae<=?' + assert_select 'meta[name=?]', 'csrf-token' + assert_match(/cf50faa3fe97702ca1ae<=\?/, @response.body) end end -class NullSessionDummyKeyGenerator - def generate_key(secret) - '03312270731a2ed0d11ed091c2338a06' +class RequestForgeryProtectionControllerUsingNullSessionTest < ActionController::TestCase + class NullSessionDummyKeyGenerator + def generate_key(secret) + '03312270731a2ed0d11ed091c2338a06' + end end -end -class RequestForgeryProtectionControllerUsingNullSessionTest < ActionController::TestCase def setup @request.env[ActionDispatch::Cookies::GENERATOR_KEY] = NullSessionDummyKeyGenerator.new end @@ -375,17 +465,39 @@ end class CustomAuthenticityParamControllerTest < ActionController::TestCase def setup - ActionController::Base.request_forgery_protection_token = :custom_token_name super + @old_logger = ActionController::Base.logger + @logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new + @token = Base64.strict_encode64(SecureRandom.random_bytes(32)) + @old_request_forgery_protection_token = ActionController::Base.request_forgery_protection_token + ActionController::Base.request_forgery_protection_token = @token end def teardown - ActionController::Base.request_forgery_protection_token = :authenticity_token + ActionController::Base.request_forgery_protection_token = @old_request_forgery_protection_token super end - def test_should_allow_custom_token - post :index, :custom_token_name => 'foobar' - assert_response :ok + def test_should_not_warn_if_form_authenticity_param_matches_form_authenticity_token + ActionController::Base.logger = @logger + @controller.stubs(:valid_authenticity_token?).returns(:true) + + begin + post :index, :custom_token_name => 'foobar' + assert_equal 0, @logger.logged(:warn).size + ensure + ActionController::Base.logger = @old_logger + end + end + + def test_should_warn_if_form_authenticity_param_does_not_match_form_authenticity_token + ActionController::Base.logger = @logger + + begin + post :index, :custom_token_name => 'bazqux' + assert_equal 1, @logger.logged(:warn).size + ensure + ActionController::Base.logger = @old_logger + end end end diff --git a/actionpack/test/controller/required_params_test.rb b/actionpack/test/controller/required_params_test.rb index 343d57c300..6803dbbb62 100644 --- a/actionpack/test/controller/required_params_test.rb +++ b/actionpack/test/controller/required_params_test.rb @@ -24,4 +24,28 @@ class ActionControllerRequiredParamsTest < ActionController::TestCase post :create, { book: { name: "Mjallo!" } } assert_response :ok end + + test "required parameters with false value will not raise" do + post :create, { book: { name: false } } + assert_response :ok + end +end + +class ParametersRequireTest < ActiveSupport::TestCase + + test "required parameters should accept and return false value" do + assert_equal(false, ActionController::Parameters.new(person: false).require(:person)) + end + + test "required parameters must not be nil" do + assert_raises(ActionController::ParameterMissing) do + ActionController::Parameters.new(person: nil).require(:person) + end + end + + test "required parameters must not be empty" do + assert_raises(ActionController::ParameterMissing) do + ActionController::Parameters.new(person: {}).require(:person) + end + end end diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index a5f43c4b6b..0e15883f43 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -43,11 +43,11 @@ class ResourcesTest < ActionController::TestCase :member => member_methods, :path_names => path_names do |options| - collection_methods.keys.each do |action| + collection_methods.each_key do |action| assert_named_route "/messages/#{path_names[action] || action}", "#{action}_messages_path", :action => action end - member_methods.keys.each do |action| + member_methods.each_key do |action| assert_named_route "/messages/1/#{path_names[action] || action}", "#{action}_message_path", :action => action, :id => "1" end @@ -150,7 +150,7 @@ class ResourcesTest < ActionController::TestCase end assert_restful_named_routes_for :messages do |options| - actions.keys.each do |action| + actions.each_key do |action| assert_named_route "/messages/#{action}", "#{action}_messages_path", :action => action end end @@ -180,7 +180,7 @@ class ResourcesTest < ActionController::TestCase end assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| - actions.keys.each do |action| + actions.each_key do |action| assert_named_route "/threads/1/messages/#{action}", "#{action}_thread_messages_path", :action => action end end @@ -207,7 +207,7 @@ class ResourcesTest < ActionController::TestCase end assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| - actions.keys.each do |action| + actions.each_key do |action| assert_named_route "/threads/1/messages/#{action}", "#{action}_thread_messages_path", :action => action end end @@ -237,7 +237,7 @@ class ResourcesTest < ActionController::TestCase end assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options| - actions.keys.each do |action| + actions.each_key do |action| assert_named_route "/threads/1/messages/#{action}.xml", "#{action}_thread_messages_path", :action => action, :format => 'xml' end end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 2c84e95c6e..9caa5cbe57 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -77,17 +77,18 @@ class LegacyRouteSetTests < ActiveSupport::TestCase include ActionDispatch::RoutingVerbs attr_reader :rs + attr_accessor :controller alias :routes :rs def setup - @rs = ::ActionDispatch::Routing::RouteSet.new + @rs = make_set @response = nil end def test_symbols_with_dashes rs.draw do get '/:artist/:song-omg', :to => lambda { |env| - resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters [200, {}, [resp]] } end @@ -99,7 +100,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_id_with_dash rs.draw do get '/journey/:id', :to => lambda { |env| - resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters [200, {}, [resp]] } end @@ -111,7 +112,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_dash_with_custom_regexp rs.draw do get '/:artist/:song-omg', :constraints => { :song => /\d+/ }, :to => lambda { |env| - resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters [200, {}, [resp]] } end @@ -124,7 +125,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_pre_dash rs.draw do get '/:artist/omg-:song', :to => lambda { |env| - resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters [200, {}, [resp]] } end @@ -136,7 +137,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_pre_dash_with_custom_regexp rs.draw do get '/:artist/omg-:song', :constraints => { :song => /\d+/ }, :to => lambda { |env| - resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters [200, {}, [resp]] } end @@ -243,6 +244,32 @@ class LegacyRouteSetTests < ActiveSupport::TestCase assert_equal 'clients', get(URI('http://clients.example.org/')) end + def test_scoped_lambda + scope_called = false + rs.draw do + scope '/foo', :constraints => lambda { |req| scope_called = true } do + get '/', :to => lambda { |env| [200, {}, %w{default}] } + end + end + + assert_equal 'default', get(URI('http://www.example.org/foo/')) + assert scope_called, "scope constraint should be called" + end + + def test_scoped_lambda_with_get_lambda + inner_called = false + + rs.draw do + scope '/foo', :constraints => lambda { |req| flunk "should not be called" } do + get '/', :constraints => lambda { |req| inner_called = true }, + :to => lambda { |env| [200, {}, %w{default}] } + end + end + + assert_equal 'default', get(URI('http://www.example.org/foo/')) + assert inner_called, "inner constraint should be called" + end + def test_empty_string_match rs.draw do get '/:username', :constraints => { :username => /[^\/]+/ }, @@ -291,11 +318,16 @@ class LegacyRouteSetTests < ActiveSupport::TestCase assert_equal '/admin/user/show/10', url_for(rs, { :controller => 'admin/user', :action => 'show', :id => 10 }) - assert_equal '/admin/user/show', url_for(rs, { :action => 'show' }, { :controller => 'admin/user', :action => 'list', :id => '10' }) - assert_equal '/admin/user/list/10', url_for(rs, {}, { :controller => 'admin/user', :action => 'list', :id => '10' }) + get URI('http://test.host/admin/user/list/10') - assert_equal '/admin/stuff', url_for(rs, { :controller => 'stuff' }, { :controller => 'admin/user', :action => 'list', :id => '10' }) - assert_equal '/stuff', url_for(rs, { :controller => '/stuff' }, { :controller => 'admin/user', :action => 'list', :id => '10' }) + assert_equal({ :controller => 'admin/user', :action => 'list', :id => '10' }, + controller.request.path_parameters) + + assert_equal '/admin/user/show', controller.url_for({ :action => 'show', :only_path => true }) + assert_equal '/admin/user/list/10', controller.url_for({:only_path => true}) + + assert_equal '/admin/stuff', controller.url_for({ :controller => 'stuff', :only_path => true }) + assert_equal '/stuff', controller.url_for({ :controller => '/stuff', :only_path => true }) end def test_ignores_leading_slash @@ -419,14 +451,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase get 'page' => 'content#show_page', :as => 'pages', :host => 'foo.com' end routes = setup_for_named_route - routes.expects(:url_for).with({ - :host => 'foo.com', - :only_path => false, - :controller => 'content', - :action => 'show_page', - :use_route => 'pages' - }).once - routes.send(:pages_url) + assert_equal "http://foo.com/page", routes.pages_url end def setup_for_named_route(options = {}) @@ -499,9 +524,10 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_changing_controller rs.draw { get ':controller/:action/:id' } + get URI('http://test.host/admin/user/index/10') + assert_equal '/admin/stuff/show/10', - url_for(rs, {:controller => 'stuff', :action => 'show', :id => 10}, - {:controller => 'admin/user', :action => 'index'}) + controller.url_for({:controller => 'stuff', :action => 'show', :id => 10, :only_path => true}) end def test_paths_escaped @@ -560,8 +586,12 @@ class LegacyRouteSetTests < ActiveSupport::TestCase get '*path' => 'content#show_file' end + get URI('http://test.host/pages/boo') + assert_equal({:controller=>"content", :action=>"show_file", :path=>"pages/boo"}, + controller.request.path_parameters) + assert_equal '/pages/boo', - url_for(rs, {}, { :controller => 'content', :action => 'show_file', :path => %w(pages boo) }) + controller.url_for(:only_path => true) end def test_backwards @@ -570,7 +600,8 @@ class LegacyRouteSetTests < ActiveSupport::TestCase get ':controller(/:action(/:id))' end - assert_equal '/page/20', url_for(rs, { :id => 20 }, { :controller => 'pages', :action => 'show' }) + get URI('http://test.host/pages/show') + assert_equal '/page/20', controller.url_for({ :id => 20, :only_path => true }) assert_equal '/page/20', url_for(rs, { :controller => 'pages', :id => 20, :action => 'show' }) assert_equal '/pages/boo', url_for(rs, { :controller => 'pages', :action => 'boo' }) end @@ -611,7 +642,8 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_action_expiry rs.draw { get ':controller(/:action(/:id))' } - assert_equal '/content', url_for(rs, { :controller => 'content' }, { :controller => 'content', :action => 'show' }) + get URI('http://test.host/content/show') + assert_equal '/content', controller.url_for(:controller => 'content', :only_path => true) end def test_requirement_should_prevent_optional_id @@ -654,14 +686,18 @@ class LegacyRouteSetTests < ActiveSupport::TestCase assert_equal '/pages/2005/6/12', url_for(rs, { :controller => 'content', :action => 'list_pages', :year => 2005, :month => 6, :day => 12 }) + get URI('http://test.host/pages/2005/6/12') + assert_equal({ :controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12' }, + controller.request.path_parameters) + assert_equal '/pages/2005/6/4', - url_for(rs, { :day => 4 }, { :controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12' }) + controller.url_for({ :day => 4, :only_path => true }) assert_equal '/pages/2005/6', - url_for(rs, { :day => nil }, { :controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12' }) + controller.url_for({ :day => nil, :only_path => true }) assert_equal '/pages/2005', - url_for(rs, { :day => nil, :month => nil }, { :controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12' }) + controller.url_for({ :day => nil, :month => nil, :only_path => true }) end def test_root_url_generation_with_controller_and_action @@ -819,9 +855,15 @@ end class RouteSetTest < ActiveSupport::TestCase include RoutingTestHelpers + include ActionDispatch::RoutingVerbs + + attr_reader :set + alias :routes :set + attr_accessor :controller - def set - @set ||= ROUTING::RouteSet.new + def setup + super + @set = make_set end def request @@ -842,13 +884,13 @@ class RouteSetTest < ActiveSupport::TestCase set.draw { get ':controller/(:action(/:id))' } path, extras = set.generate_extras(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world") assert_equal "/foo/bar/15", path - assert_equal %w(that this), extras.map { |e| e.to_s }.sort + assert_equal %w(that this), extras.map(&:to_s).sort end def test_extra_keys set.draw { get ':controller/:action/:id' } extras = set.extra_keys(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world") - assert_equal %w(that this), extras.map { |e| e.to_s }.sort + assert_equal %w(that this), extras.map(&:to_s).sort end def test_generate_extras_not_first @@ -858,7 +900,7 @@ class RouteSetTest < ActiveSupport::TestCase end path, extras = set.generate_extras(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world") assert_equal "/foo/bar/15", path - assert_equal %w(that this), extras.map { |e| e.to_s }.sort + assert_equal %w(that this), extras.map(&:to_s).sort end def test_generate_not_first @@ -876,7 +918,7 @@ class RouteSetTest < ActiveSupport::TestCase get ':controller/:action/:id' end extras = set.extra_keys(:controller => "foo", :action => "bar", :id => 15, :this => "hello", :that => "world") - assert_equal %w(that this), extras.map { |e| e.to_s }.sort + assert_equal %w(that this), extras.map(&:to_s).sort end def test_draw @@ -922,7 +964,8 @@ class RouteSetTest < ActiveSupport::TestCase get '/admin/users' => 'admin/users#index', :as => "users" end - MockController.build(set.url_helpers).new + get URI('http://test.host/people') + controller end def test_named_route_url_method @@ -958,6 +1001,9 @@ class RouteSetTest < ActiveSupport::TestCase assert_equal "http://test.host/people?baz=bar#location", controller.send(:index_url, :baz => "bar", :anchor => 'location') + + assert_equal "http://test.host/people", controller.send(:index_url, anchor: nil) + assert_equal "http://test.host/people", controller.send(:index_url, anchor: false) end def test_named_route_url_method_with_port @@ -1019,12 +1065,12 @@ class RouteSetTest < ActiveSupport::TestCase get '/:controller(/:action(/:id))' end - assert_equal({:controller => 'pages', :action => 'index'}, set.recognize_path('/pages')) - assert_equal({:controller => 'pages', :action => 'index'}, set.recognize_path('/pages/index')) - assert_equal({:controller => 'pages', :action => 'list'}, set.recognize_path('/pages/list')) + assert_equal({:controller => 'pages', :action => 'index'}, request_path_params('/pages')) + assert_equal({:controller => 'pages', :action => 'index'}, request_path_params('/pages/index')) + assert_equal({:controller => 'pages', :action => 'list'}, request_path_params('/pages/list')) - assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, set.recognize_path('/pages/show/10')) - assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, set.recognize_path('/page/10')) + assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, request_path_params('/pages/show/10')) + assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, request_path_params('/page/10')) end def test_route_constraints_on_request_object_with_anchors_are_valid @@ -1076,9 +1122,7 @@ class RouteSetTest < ActiveSupport::TestCase get "/people" => "missing#index" end - assert_raise(ActionController::RoutingError) { - set.recognize_path("/people", :method => :get) - } + assert_raises(ActionController::RoutingError) { request_path_params '/people' } end def test_recognize_with_encoded_id_and_regex @@ -1086,8 +1130,8 @@ class RouteSetTest < ActiveSupport::TestCase get 'page/:id' => 'pages#show', :id => /[a-zA-Z0-9\+]+/ end - assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, set.recognize_path('/page/10')) - assert_equal({:controller => 'pages', :action => 'show', :id => 'hello+world'}, set.recognize_path('/page/hello+world')) + assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, request_path_params('/page/10')) + assert_equal({:controller => 'pages', :action => 'show', :id => 'hello+world'}, request_path_params('/page/hello+world')) end def test_recognize_with_http_methods @@ -1100,40 +1144,40 @@ class RouteSetTest < ActiveSupport::TestCase delete "/people/:id" => "people#destroy" end - params = set.recognize_path("/people", :method => :get) + params = request_path_params("/people", :method => :get) assert_equal("index", params[:action]) - params = set.recognize_path("/people", :method => :post) + params = request_path_params("/people", :method => :post) assert_equal("create", params[:action]) - params = set.recognize_path("/people/5", :method => :put) + params = request_path_params("/people/5", :method => :put) assert_equal("update", params[:action]) - params = set.recognize_path("/people/5", :method => :patch) + params = request_path_params("/people/5", :method => :patch) assert_equal("update", params[:action]) assert_raise(ActionController::UnknownHttpMethod) { - set.recognize_path("/people", :method => :bacon) + request_path_params("/people", :method => :bacon) } - params = set.recognize_path("/people/5", :method => :get) + params = request_path_params("/people/5", :method => :get) assert_equal("show", params[:action]) assert_equal("5", params[:id]) - params = set.recognize_path("/people/5", :method => :put) + params = request_path_params("/people/5", :method => :put) assert_equal("update", params[:action]) assert_equal("5", params[:id]) - params = set.recognize_path("/people/5", :method => :patch) + params = request_path_params("/people/5", :method => :patch) assert_equal("update", params[:action]) assert_equal("5", params[:id]) - params = set.recognize_path("/people/5", :method => :delete) + params = request_path_params("/people/5", :method => :delete) assert_equal("destroy", params[:action]) assert_equal("5", params[:id]) assert_raise(ActionController::RoutingError) { - set.recognize_path("/people/5", :method => :post) + request_path_params("/people/5", :method => :post) } end @@ -1143,11 +1187,11 @@ class RouteSetTest < ActiveSupport::TestCase root :to => "people#index" end - params = set.recognize_path("/people", :method => :get) + params = request_path_params("/people", :method => :get) assert_equal("people", params[:controller]) assert_equal("index", params[:action]) - params = set.recognize_path("/", :method => :get) + params = request_path_params("/", :method => :get) assert_equal("people", params[:controller]) assert_equal("index", params[:action]) end @@ -1158,7 +1202,7 @@ class RouteSetTest < ActiveSupport::TestCase :year => /\d{4}/, :day => /\d{1,2}/, :month => /\d{1,2}/ end - params = set.recognize_path("/articles/2005/11/05/a-very-interesting-article", :method => :get) + params = request_path_params("/articles/2005/11/05/a-very-interesting-article", :method => :get) assert_equal("permalink", params[:action]) assert_equal("2005", params[:year]) assert_equal("11", params[:month]) @@ -1172,7 +1216,7 @@ class RouteSetTest < ActiveSupport::TestCase get '/profile' => 'profile#index' end - set.recognize_path("/profile") rescue nil + request_path_params("/profile") rescue nil assert !Object.const_defined?("Profiler__"), "Profiler should not be loaded" end @@ -1185,17 +1229,17 @@ class RouteSetTest < ActiveSupport::TestCase get "people/:id(.:format)" => "people#show" end - params = set.recognize_path("/people/5", :method => :get) + params = request_path_params("/people/5", :method => :get) assert_equal("show", params[:action]) assert_equal("5", params[:id]) - params = set.recognize_path("/people/5", :method => :put) + params = request_path_params("/people/5", :method => :put) assert_equal("update", params[:action]) - params = set.recognize_path("/people/5", :method => :patch) + params = request_path_params("/people/5", :method => :patch) assert_equal("update", params[:action]) - params = set.recognize_path("/people/5.png", :method => :get) + params = request_path_params("/people/5.png", :method => :get) assert_equal("show", params[:action]) assert_equal("5", params[:id]) assert_equal("png", params[:format]) @@ -1214,7 +1258,7 @@ class RouteSetTest < ActiveSupport::TestCase def test_root_map set.draw { root :to => 'people#index' } - params = set.recognize_path("", :method => :get) + params = request_path_params("", :method => :get) assert_equal("people", params[:controller]) assert_equal("index", params[:action]) end @@ -1228,7 +1272,7 @@ class RouteSetTest < ActiveSupport::TestCase end - params = set.recognize_path("/api/inventory", :method => :get) + params = request_path_params("/api/inventory", :method => :get) assert_equal("api/products", params[:controller]) assert_equal("inventory", params[:action]) end @@ -1240,7 +1284,7 @@ class RouteSetTest < ActiveSupport::TestCase end end - params = set.recognize_path("/api", :method => :get) + params = request_path_params("/api", :method => :get) assert_equal("api/products", params[:controller]) assert_equal("index", params[:action]) end @@ -1252,7 +1296,7 @@ class RouteSetTest < ActiveSupport::TestCase end end - params = set.recognize_path("/prefix/inventory", :method => :get) + params = request_path_params("/prefix/inventory", :method => :get) assert_equal("api/products", params[:controller]) assert_equal("inventory", params[:action]) end @@ -1264,38 +1308,36 @@ class RouteSetTest < ActiveSupport::TestCase end end - params = set.recognize_path("/inventory", :method => :get) + params = request_path_params("/inventory", :method => :get) assert_equal("api/products", params[:controller]) assert_equal("inventory", params[:action]) end - def test_generate_changes_controller_module - set.draw { get ':controller/:action/:id' } - current = { :controller => "bling/bloop", :action => "bap", :id => 9 } - - assert_equal "/foo/bar/baz/7", - url_for(set, { :controller => "foo/bar", :action => "baz", :id => 7 }, current) - end - def test_id_is_sticky_when_it_ought_to_be + @set = make_set false + set.draw do get ':controller/:id/:action' end - url = url_for(set, { :action => "destroy" }, { :controller => "people", :action => "show", :id => "7" }) - assert_equal "/people/7/destroy", url + get URI('http://test.host/people/7/show') + + assert_equal "/people/7/destroy", controller.url_for(:action => 'destroy', :only_path => true) end def test_use_static_path_when_possible + @set = make_set false + set.draw do get 'about' => "welcome#about" get ':controller/:action/:id' end - url = url_for(set, { :controller => "welcome", :action => "about" }, - { :controller => "welcome", :action => "get", :id => "7" }) + get URI('http://test.host/welcom/get/7') - assert_equal "/about", url + assert_equal "/about", controller.url_for(:controller => 'welcome', + :action => 'about', + :only_path => true) end def test_generate @@ -1330,38 +1372,51 @@ class RouteSetTest < ActiveSupport::TestCase end def test_named_routes_are_never_relative_to_modules + @set = make_set false + set.draw do get "/connection/manage(/:action)" => 'connection/manage#index' get "/connection/connection" => "connection/connection#index" get '/connection' => 'connection#index', :as => 'family_connection' end - url = url_for(set, { :controller => "connection" }, { :controller => 'connection/manage' }) + assert_equal({ :controller => 'connection/manage', + :action => 'index', }, request_path_params('/connection/manage')) + + url = controller.url_for({ :controller => "connection", :only_path => true }) assert_equal "/connection/connection", url - url = url_for(set, { :use_route => :family_connection, :controller => "connection" }, { :controller => 'connection/manage' }) + url = controller.url_for({ :use_route => "family_connection", + :controller => "connection", :only_path => true }) assert_equal "/connection", url end def test_action_left_off_when_id_is_recalled + @set = make_set false + set.draw do get ':controller(/:action(/:id))' end - assert_equal '/books', url_for(set, - {:controller => 'books', :action => 'index'}, - {:controller => 'books', :action => 'show', :id => '10'} - ) + + get URI('http://test.host/books/show/10') + + assert_equal '/books', controller.url_for(:controller => 'books', + :only_path => true, + :action => 'index') end def test_query_params_will_be_shown_when_recalled + @set = make_set false + set.draw do get 'show_weblog/:parameter' => 'weblog#show' get ':controller(/:action(/:id))' end - assert_equal '/weblog/edit?parameter=1', url_for(set, - {:action => 'edit', :parameter => 1}, - {:controller => 'weblog', :action => 'show', :parameter => 1} - ) + + get URI('http://test.host/weblog/show/1') + + assert_equal '/weblog/edit?parameter=1', controller.url_for( + {:action => 'edit', :parameter => 1, :only_path => true}) end def test_format_is_not_inherit @@ -1369,22 +1424,30 @@ class RouteSetTest < ActiveSupport::TestCase get '/posts(.:format)' => 'posts#index' end - assert_equal '/posts', url_for(set, - {:controller => 'posts'}, - {:controller => 'posts', :action => 'index', :format => 'xml'} - ) + get URI('http://test.host/posts.xml') + assert_equal({:controller => 'posts', :action => 'index', :format => 'xml'}, + controller.request.path_parameters) + + assert_equal '/posts', controller.url_for( + {:controller => 'posts', :only_path => true}) - assert_equal '/posts.xml', url_for(set, - {:controller => 'posts', :format => 'xml'}, - {:controller => 'posts', :action => 'index', :format => 'xml'} - ) + assert_equal '/posts.xml', controller.url_for( + {:controller => 'posts', :format => 'xml', :only_path => true}) end def test_expiry_determination_should_consider_values_with_to_param + @set = make_set false + set.draw { get 'projects/:project_id/:controller/:action' } - assert_equal '/projects/1/weblog/show', url_for(set, - { :action => 'show', :project_id => 1 }, - { :controller => 'weblog', :action => 'show', :project_id => '1' }) + + get URI('http://test.host/projects/1/weblog/show') + + assert_equal( + { :controller => 'weblog', :action => 'show', :project_id => '1' }, + controller.request.path_parameters) + + assert_equal '/projects/1/weblog/show', + controller.url_for({ :action => 'show', :project_id => 1, :only_path => true }) end def test_named_route_in_nested_resource @@ -1587,7 +1650,6 @@ class RouteSetTest < ActiveSupport::TestCase end def test_slashes_are_implied - @set = nil set.draw { get("/:controller(/:action(/:id))") } assert_equal '/content', url_for(set, { :controller => 'content', :action => 'index' }) @@ -1685,7 +1747,43 @@ class RouteSetTest < ActiveSupport::TestCase assert_equal '/ibocorp', url_for(set, { :controller => 'ibocorp', :action => "show", :page => 1 }) end + include ActionDispatch::RoutingVerbs + + class TestSet < ROUTING::RouteSet + def initialize(block) + @block = block + super() + end + + class Dispatcher < ROUTING::RouteSet::Dispatcher + def initialize(defaults, set, block) + super(defaults) + @block = block + @set = set + end + + def controller_reference(controller_param) + block = @block + set = @set + Class.new(ActionController::Base) { + include set.url_helpers + define_method(:process) { |name| block.call(self) } + def to_a; [200, {}, []]; end + } + end + end + + def dispatcher defaults + TestSet::Dispatcher.new defaults, self, @block + end + end + + alias :routes :set + def test_generate_with_optional_params_recalls_last_request + controller = nil + @set = TestSet.new ->(c) { controller = c } + set.draw do get "blog/", :controller => "blog", :action => "index" @@ -1700,23 +1798,29 @@ class RouteSetTest < ActiveSupport::TestCase get "*anything", :controller => "blog", :action => "unknown_request" end - assert_equal({:controller => "blog", :action => "index"}, set.recognize_path("/blog")) - assert_equal({:controller => "blog", :action => "show", :id => "123"}, set.recognize_path("/blog/show/123")) - assert_equal({:controller => "blog", :action => "show_date", :year => "2004", :day => nil, :month => nil }, set.recognize_path("/blog/2004")) - assert_equal({:controller => "blog", :action => "show_date", :year => "2004", :month => "12", :day => nil }, set.recognize_path("/blog/2004/12")) - assert_equal({:controller => "blog", :action => "show_date", :year => "2004", :month => "12", :day => "25"}, set.recognize_path("/blog/2004/12/25")) - assert_equal({:controller => "articles", :action => "edit", :id => "123"}, set.recognize_path("/blog/articles/edit/123")) - assert_equal({:controller => "articles", :action => "show_stats"}, set.recognize_path("/blog/articles/show_stats")) - assert_equal({:controller => "blog", :action => "unknown_request", :anything => "blog/wibble"}, set.recognize_path("/blog/wibble")) - assert_equal({:controller => "blog", :action => "unknown_request", :anything => "junk"}, set.recognize_path("/junk")) + recognize_path = ->(path) { + get(URI("http://example.org" + path)) + controller.request.path_parameters + } + + assert_equal({:controller => "blog", :action => "index"}, recognize_path.("/blog")) + assert_equal({:controller => "blog", :action => "show", :id => "123"}, recognize_path.("/blog/show/123")) + assert_equal({:controller => "blog", :action => "show_date", :year => "2004", :day => nil, :month => nil }, recognize_path.("/blog/2004")) + assert_equal({:controller => "blog", :action => "show_date", :year => "2004", :month => "12", :day => nil }, recognize_path.("/blog/2004/12")) + assert_equal({:controller => "blog", :action => "show_date", :year => "2004", :month => "12", :day => "25"}, recognize_path.("/blog/2004/12/25")) + assert_equal({:controller => "articles", :action => "edit", :id => "123"}, recognize_path.("/blog/articles/edit/123")) + assert_equal({:controller => "articles", :action => "show_stats"}, recognize_path.("/blog/articles/show_stats")) + assert_equal({:controller => "blog", :action => "unknown_request", :anything => "blog/wibble"}, recognize_path.("/blog/wibble")) + assert_equal({:controller => "blog", :action => "unknown_request", :anything => "junk"}, recognize_path.("/junk")) - last_request = set.recognize_path("/blog/2006/07/28").freeze - assert_equal({:controller => "blog", :action => "show_date", :year => "2006", :month => "07", :day => "28"}, last_request) - assert_equal("/blog/2006/07/25", url_for(set, { :day => 25 }, last_request)) - assert_equal("/blog/2005", url_for(set, { :year => 2005 }, last_request)) - assert_equal("/blog/show/123", url_for(set, { :action => "show" , :id => 123 }, last_request)) - assert_equal("/blog/2006", url_for(set, { :year => 2006 }, last_request)) - assert_equal("/blog/2006", url_for(set, { :year => 2006, :month => nil }, last_request)) + get URI('http://example.org/blog/2006/07/28') + + assert_equal({:controller => "blog", :action => "show_date", :year => "2006", :month => "07", :day => "28"}, controller.request.path_parameters) + assert_equal("/blog/2006/07/25", controller.url_for({ :day => 25, :only_path => true })) + assert_equal("/blog/2005", controller.url_for({ :year => 2005, :only_path => true })) + assert_equal("/blog/show/123", controller.url_for({ :action => "show" , :id => 123, :only_path => true })) + assert_equal("/blog/2006", controller.url_for({ :year => 2006, :only_path => true })) + assert_equal("/blog/2006", controller.url_for({ :year => 2006, :month => nil, :only_path => true })) end private @@ -1789,6 +1893,9 @@ class RackMountIntegrationTests < ActiveSupport::TestCase root :to => "news#index" } + attr_reader :routes + attr_reader :controller + def setup @routes = ActionDispatch::Routing::RouteSet.new @routes.draw(&Mapping) @@ -1833,11 +1940,11 @@ class RackMountIntegrationTests < ActiveSupport::TestCase assert_equal({:controller => 'foo', :action => 'id_default', :id => 1 }, @routes.recognize_path('/id_default')) assert_equal({:controller => 'foo', :action => 'get_or_post'}, @routes.recognize_path('/get_or_post', :method => :get)) assert_equal({:controller => 'foo', :action => 'get_or_post'}, @routes.recognize_path('/get_or_post', :method => :post)) - assert_raise(ActionController::ActionControllerError) { @routes.recognize_path('/get_or_post', :method => :put) } - assert_raise(ActionController::ActionControllerError) { @routes.recognize_path('/get_or_post', :method => :delete) } + assert_raise(ActionController::RoutingError) { @routes.recognize_path('/get_or_post', :method => :put) } + assert_raise(ActionController::RoutingError) { @routes.recognize_path('/get_or_post', :method => :delete) } assert_equal({:controller => 'posts', :action => 'index', :optional => 'bar'}, @routes.recognize_path('/optional/bar')) - assert_raise(ActionController::ActionControllerError) { @routes.recognize_path('/optional') } + assert_raise(ActionController::RoutingError) { @routes.recognize_path('/optional') } assert_equal({:controller => 'posts', :action => 'show', :id => '1', :ws => true}, @routes.recognize_path('/ws/posts/show/1', :method => :get)) assert_equal({:controller => 'posts', :action => 'list', :ws => true}, @routes.recognize_path('/ws/posts/list', :method => :get)) @@ -1916,11 +2023,4 @@ class RackMountIntegrationTests < ActiveSupport::TestCase end extras end - - def assert_raise(e) - result = yield - flunk "Did not raise #{e}, but returned #{result.inspect}" - rescue e - assert true - end end diff --git a/actionpack/test/controller/selector_test.rb b/actionpack/test/controller/selector_test.rb deleted file mode 100644 index 1e80c8601c..0000000000 --- a/actionpack/test/controller/selector_test.rb +++ /dev/null @@ -1,629 +0,0 @@ -#-- -# Copyright (c) 2006 Assaf Arkin (http://labnotes.org) -# Under MIT and/or CC By license. -#++ - -require 'abstract_unit' -require 'controller/fake_controllers' -require 'action_view/vendor/html-scanner' - -class SelectorTest < ActiveSupport::TestCase - # - # Basic selector: element, id, class, attributes. - # - - def test_element - parse(%Q{<div id="1"></div><p></p><div id="2"></div>}) - # Match element by name. - select("div") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "2", @matches[1].attributes["id"] - # Not case sensitive. - select("DIV") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "2", @matches[1].attributes["id"] - # Universal match (all elements). - select("*") - assert_equal 3, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal nil, @matches[1].attributes["id"] - assert_equal "2", @matches[2].attributes["id"] - end - - - def test_identifier - parse(%Q{<div id="1"></div><p></p><div id="2"></div>}) - # Match element by ID. - select("div#1") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - # Match element by ID, substitute value. - select("div#?", 2) - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Element name does not match ID. - select("p#?", 2) - assert_equal 0, @matches.size - # Use regular expression. - select("#?", /\d/) - assert_equal 2, @matches.size - end - - - def test_class_name - parse(%Q{<div id="1" class=" foo "></div><p id="2" class=" foo bar "></p><div id="3" class="bar"></div>}) - # Match element with specified class. - select("div.foo") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - # Match any element with specified class. - select("*.foo") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "2", @matches[1].attributes["id"] - # Match elements with other class. - select("*.bar") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - # Match only element with both class names. - select("*.bar.foo") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - end - - - def test_attribute - parse(%Q{<div id="1"></div><p id="2" title="" bar="foo"></p><div id="3" title="foo"></div>}) - # Match element with attribute. - select("div[title]") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - # Match any element with attribute. - select("*[title]") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - # Match element with attribute value. - select("*[title=foo]") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - # Match element with attribute and attribute value. - select("[bar=foo][title]") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Not case sensitive. - select("[BAR=foo][TiTle]") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - end - - - def test_attribute_quoted - parse(%Q{<div id="1" title="foo"></div><div id="2" title="bar"></div><div id="3" title=" bar "></div>}) - # Match without quotes. - select("[title = bar]") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Match with single quotes. - select("[title = 'bar' ]") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Match with double quotes. - select("[title = \"bar\" ]") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Match with spaces. - select("[title = \" bar \" ]") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - end - - - def test_attribute_equality - parse(%Q{<div id="1" title="foo bar"></div><div id="2" title="barbaz"></div>}) - # Match (fail) complete value. - select("[title=bar]") - assert_equal 0, @matches.size - # Match space-separate word. - select("[title~=foo]") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - select("[title~=bar]") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - # Match beginning of value. - select("[title^=ba]") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Match end of value. - select("[title$=ar]") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - # Match text in value. - select("[title*=bar]") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "2", @matches[1].attributes["id"] - # Match first space separated word. - select("[title|=foo]") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - select("[title|=bar]") - assert_equal 0, @matches.size - end - - - # - # Selector composition: groups, sibling, children - # - - - def test_selector_group - parse(%Q{<h1 id="1"></h1><h2 id="2"></h2><h3 id="3"></h3>}) - # Simple group selector. - select("h1,h3") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - select("h1 , h3") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - # Complex group selector. - parse(%Q{<h1 id="1"><a href="foo"></a></h1><h2 id="2"><a href="bar"></a></h2><h3 id="2"><a href="baz"></a></h3>}) - select("h1 a, h3 a") - assert_equal 2, @matches.size - assert_equal "foo", @matches[0].attributes["href"] - assert_equal "baz", @matches[1].attributes["href"] - # And now for the three selector challenge. - parse(%Q{<h1 id="1"><a href="foo"></a></h1><h2 id="2"><a href="bar"></a></h2><h3 id="2"><a href="baz"></a></h3>}) - select("h1 a, h2 a, h3 a") - assert_equal 3, @matches.size - assert_equal "foo", @matches[0].attributes["href"] - assert_equal "bar", @matches[1].attributes["href"] - assert_equal "baz", @matches[2].attributes["href"] - end - - - def test_sibling_selector - parse(%Q{<h1 id="1"></h1><h2 id="2"></h2><h3 id="3"></h3>}) - # Test next sibling. - select("h1+*") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - select("h1+h2") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - select("h1+h3") - assert_equal 0, @matches.size - select("*+h3") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - # Test any sibling. - select("h1~*") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - select("h2~*") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - end - - - def test_children_selector - parse(%Q{<div><p id="1"><span id="2"></span></p></div><div><p id="3"><span id="4" class="foo"></span></p></div>}) - # Test child selector. - select("div>p") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - select("div>span") - assert_equal 0, @matches.size - select("div>p#3") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - select("div>p>span") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - # Test descendant selector. - select("div p") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - select("div span") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - select("div *#3") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - select("div *#4") - assert_equal 1, @matches.size - assert_equal "4", @matches[0].attributes["id"] - # This is here because it failed before when whitespaces - # were not properly stripped. - select("div .foo") - assert_equal 1, @matches.size - assert_equal "4", @matches[0].attributes["id"] - end - - - # - # Pseudo selectors: root, nth-child, empty, content, etc - # - - - def test_root_selector - parse(%Q{<div id="1"><div id="2"></div></div>}) - # Can only find element if it's root. - select(":root") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - select("#1:root") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - select("#2:root") - assert_equal 0, @matches.size - # Opposite for nth-child. - select("#1:nth-child(1)") - assert_equal 0, @matches.size - end - - - def test_nth_child_odd_even - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # Test odd nth children. - select("tr:nth-child(odd)") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - # Test even nth children. - select("tr:nth-child(even)") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - end - - - def test_nth_child_a_is_zero - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # Test the third child. - select("tr:nth-child(0n+3)") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - # Same but an can be omitted when zero. - select("tr:nth-child(3)") - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - # Second element (but not every second element). - select("tr:nth-child(0n+2)") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Before first and past last returns nothing.: - assert_raise(ArgumentError) { select("tr:nth-child(-1)") } - select("tr:nth-child(0)") - assert_equal 0, @matches.size - select("tr:nth-child(5)") - assert_equal 0, @matches.size - end - - - def test_nth_child_a_is_one - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # a is group of one, pick every element in group. - select("tr:nth-child(1n+0)") - assert_equal 4, @matches.size - # Same but a can be omitted when one. - select("tr:nth-child(n+0)") - assert_equal 4, @matches.size - # Same but b can be omitted when zero. - select("tr:nth-child(n)") - assert_equal 4, @matches.size - end - - - def test_nth_child_b_is_zero - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # If b is zero, pick the n-th element (here each one). - select("tr:nth-child(n+0)") - assert_equal 4, @matches.size - # If b is zero, pick the n-th element (here every second). - select("tr:nth-child(2n+0)") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - # If a and b are both zero, no element selected. - select("tr:nth-child(0n+0)") - assert_equal 0, @matches.size - select("tr:nth-child(0)") - assert_equal 0, @matches.size - end - - - def test_nth_child_a_is_negative - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # Since a is -1, picks the first three elements. - select("tr:nth-child(-n+3)") - assert_equal 3, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "2", @matches[1].attributes["id"] - assert_equal "3", @matches[2].attributes["id"] - # Since a is -2, picks the first in every second of first four elements. - select("tr:nth-child(-2n+3)") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - # Since a is -2, picks the first in every second of first three elements. - select("tr:nth-child(-2n+2)") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - end - - - def test_nth_child_b_is_negative - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # Select last of four. - select("tr:nth-child(4n-1)") - assert_equal 1, @matches.size - assert_equal "4", @matches[0].attributes["id"] - # Select first of four. - select("tr:nth-child(4n-4)") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - # Select last of every second. - select("tr:nth-child(2n-1)") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - # Select nothing since an+b always < 0 - select("tr:nth-child(-1n-1)") - assert_equal 0, @matches.size - end - - - def test_nth_child_substitution_values - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # Test with ?n?. - select("tr:nth-child(?n?)", 2, 1) - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "3", @matches[1].attributes["id"] - select("tr:nth-child(?n?)", 2, 2) - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - select("tr:nth-child(?n?)", 4, 2) - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - # Test with ? (b only). - select("tr:nth-child(?)", 3) - assert_equal 1, @matches.size - assert_equal "3", @matches[0].attributes["id"] - select("tr:nth-child(?)", 5) - assert_equal 0, @matches.size - end - - - def test_nth_last_child - parse(%Q{<table><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # Last two elements. - select("tr:nth-last-child(-n+2)") - assert_equal 2, @matches.size - assert_equal "3", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - # All old elements counting from last one. - select("tr:nth-last-child(odd)") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - end - - - def test_nth_of_type - parse(%Q{<table><thead></thead><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # First two elements. - select("tr:nth-of-type(-n+2)") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "2", @matches[1].attributes["id"] - # All old elements counting from last one. - select("tr:nth-last-of-type(odd)") - assert_equal 2, @matches.size - assert_equal "2", @matches[0].attributes["id"] - assert_equal "4", @matches[1].attributes["id"] - end - - - def test_first_and_last - parse(%Q{<table><thead></thead><tr id="1"></tr><tr id="2"></tr><tr id="3"></tr><tr id="4"></tr></table>}) - # First child. - select("tr:first-child") - assert_equal 0, @matches.size - select(":first-child") - assert_equal 1, @matches.size - assert_equal "thead", @matches[0].name - # First of type. - select("tr:first-of-type") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - select("thead:first-of-type") - assert_equal 1, @matches.size - assert_equal "thead", @matches[0].name - select("div:first-of-type") - assert_equal 0, @matches.size - # Last child. - select("tr:last-child") - assert_equal 1, @matches.size - assert_equal "4", @matches[0].attributes["id"] - # Last of type. - select("tr:last-of-type") - assert_equal 1, @matches.size - assert_equal "4", @matches[0].attributes["id"] - select("thead:last-of-type") - assert_equal 1, @matches.size - assert_equal "thead", @matches[0].name - select("div:last-of-type") - assert_equal 0, @matches.size - end - - - def test_only_child_and_only_type_first_and_last - # Only child. - parse(%Q{<table><tr></tr></table>}) - select("table:only-child") - assert_equal 0, @matches.size - select("tr:only-child") - assert_equal 1, @matches.size - assert_equal "tr", @matches[0].name - parse(%Q{<table><tr></tr><tr></tr></table>}) - select("tr:only-child") - assert_equal 0, @matches.size - # Only of type. - parse(%Q{<table><thead></thead><tr></tr><tr></tr></table>}) - select("thead:only-of-type") - assert_equal 1, @matches.size - assert_equal "thead", @matches[0].name - select("td:only-of-type") - assert_equal 0, @matches.size - end - - - def test_empty - parse(%Q{<table><tr></tr></table>}) - select("table:empty") - assert_equal 0, @matches.size - select("tr:empty") - assert_equal 1, @matches.size - parse(%Q{<div> </div>}) - select("div:empty") - assert_equal 1, @matches.size - end - - - def test_content - parse(%Q{<div> </div>}) - select("div:content()") - assert_equal 1, @matches.size - parse(%Q{<div>something </div>}) - select("div:content()") - assert_equal 0, @matches.size - select("div:content(something)") - assert_equal 1, @matches.size - select("div:content( 'something' )") - assert_equal 1, @matches.size - select("div:content( \"something\" )") - assert_equal 1, @matches.size - select("div:content(?)", "something") - assert_equal 1, @matches.size - select("div:content(?)", /something/) - assert_equal 1, @matches.size - end - - - # - # Test negation. - # - - - def test_element_negation - parse(%Q{<p></p><div></div>}) - select("*") - assert_equal 2, @matches.size - select("*:not(p)") - assert_equal 1, @matches.size - assert_equal "div", @matches[0].name - select("*:not(div)") - assert_equal 1, @matches.size - assert_equal "p", @matches[0].name - select("*:not(span)") - assert_equal 2, @matches.size - end - - - def test_id_negation - parse(%Q{<p id="1"></p><p id="2"></p>}) - select("p") - assert_equal 2, @matches.size - select(":not(#1)") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - select(":not(#2)") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - end - - - def test_class_name_negation - parse(%Q{<p class="foo"></p><p class="bar"></p>}) - select("p") - assert_equal 2, @matches.size - select(":not(.foo)") - assert_equal 1, @matches.size - assert_equal "bar", @matches[0].attributes["class"] - select(":not(.bar)") - assert_equal 1, @matches.size - assert_equal "foo", @matches[0].attributes["class"] - end - - - def test_attribute_negation - parse(%Q{<p title="foo"></p><p title="bar"></p>}) - select("p") - assert_equal 2, @matches.size - select(":not([title=foo])") - assert_equal 1, @matches.size - assert_equal "bar", @matches[0].attributes["title"] - select(":not([title=bar])") - assert_equal 1, @matches.size - assert_equal "foo", @matches[0].attributes["title"] - end - - - def test_pseudo_class_negation - parse(%Q{<div><p id="1"></p><p id="2"></p></div>}) - select("p") - assert_equal 2, @matches.size - select("p:not(:first-child)") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - select("p:not(:nth-child(2))") - assert_equal 1, @matches.size - assert_equal "1", @matches[0].attributes["id"] - end - - - def test_negation_details - parse(%Q{<p id="1"></p><p id="2"></p><p id="3"></p>}) - assert_raise(ArgumentError) { select(":not(") } - assert_raise(ArgumentError) { select(":not(:not())") } - select("p:not(#1):not(#3)") - assert_equal 1, @matches.size - assert_equal "2", @matches[0].attributes["id"] - end - - - def test_select_from_element - parse(%Q{<div><p id="1"></p><p id="2"></p></div>}) - select("div") - @matches = @matches[0].select("p") - assert_equal 2, @matches.size - assert_equal "1", @matches[0].attributes["id"] - assert_equal "2", @matches[1].attributes["id"] - end - - -protected - - def parse(html) - @html = HTML::Document.new(html).root - end - - def select(*selector) - @matches = HTML.selector(*selector).select(@html) - end - -end diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index 8ecc1c7d73..c002cf4d8f 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -9,6 +9,7 @@ end class SendFileController < ActionController::Base include TestFileUtils + include ActionController::Testing layout "layouts/standard" # to make sure layouts don't interfere attr_writer :options @@ -25,12 +26,13 @@ class SendFileController < ActionController::Base end end +class SendFileWithActionControllerLive < SendFileController + include ActionController::Live +end + class SendFileTest < ActionController::TestCase - tests SendFileController include TestFileUtils - Mime::Type.register "image/png", :png unless defined? Mime::PNG - def setup @controller = SendFileController.new @request = ActionController::TestRequest.new @@ -144,7 +146,7 @@ class SendFileTest < ActionController::TestCase } @controller.headers = {} - assert !@controller.send(:send_file_headers!, options) + assert_raise(ArgumentError) { @controller.send(:send_file_headers!, options) } end def test_send_file_headers_guess_type_from_extension @@ -196,4 +198,12 @@ class SendFileTest < ActionController::TestCase assert_equal 200, @response.status end end + + def test_send_file_with_action_controller_live + @controller = SendFileWithActionControllerLive.new + @controller.options = { :content_type => "application/x-ruby" } + + response = process('file') + assert_equal 200, response.status + end end diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb index ff23b22040..f7eba1ef43 100644 --- a/actionpack/test/controller/show_exceptions_test.rb +++ b/actionpack/test/controller/show_exceptions_test.rb @@ -32,7 +32,7 @@ module ShowExceptions test 'show diagnostics from a local ip if show_detailed_exceptions? is set to request.local?' do @app = ShowExceptionsController.action(:boom) - ['127.0.0.1', '127.0.0.127', '::1', '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1%0'].each do |ip_address| + ['127.0.0.1', '127.0.0.127', '127.12.1.1', '::1', '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1%0'].each do |ip_address| self.remote_addr = ip_address get '/' assert_match(/boom/, body) diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index de0476dbde..ba2ff7d12c 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' require 'controller/fake_controllers' require 'active_support/json/decoding' +require 'rails/engine' class TestCaseTest < ActionController::TestCase class TestController < ActionController::Base @@ -132,6 +133,14 @@ XML render :nothing => true end + def test_without_body + render html: '<div class="foo"></div>'.html_safe + end + + def test_with_body + render html: '<body class="foo"></body>'.html_safe + end + private def generate_url(opts) @@ -163,6 +172,42 @@ XML end end + class DefaultUrlOptionsCachingController < ActionController::Base + before_action { @dynamic_opt = 'opt' } + + def test_url_options_reset + render text: url_for(params) + end + + def default_url_options + if defined?(@dynamic_opt) + super.merge dynamic_opt: @dynamic_opt + else + super + end + end + end + + def test_assert_select_without_body + get :test_without_body + + assert_select 'body', 0 + assert_select 'div.foo' + end + + def test_assert_select_with_body + get :test_with_body + + assert_select 'body.foo' + end + + def test_url_options_reset + @controller = DefaultUrlOptionsCachingController.new + get :test_url_options_reset + assert_nil @request.params['dynamic_opt'] + assert_match(/dynamic_opt=opt/, @response.body) + end + def test_raw_post_handling params = Hash[:page, {:name => 'page name'}, 'some key', 123] post :render_raw_post, params.dup @@ -198,7 +243,7 @@ XML assert_equal 200, @response.status end - def test_head_params_as_sting + def test_head_params_as_string assert_raise(NoMethodError) { head :test_params, "document body", :id => 10 } end @@ -330,168 +375,6 @@ XML assert_equal "bar", assigns[:bar] end - def test_assert_tag_tag - process :test_html_output - - # there is a 'form' tag - assert_tag :tag => 'form' - # there is not an 'hr' tag - assert_no_tag :tag => 'hr' - end - - def test_assert_tag_attributes - process :test_html_output - - # there is a tag with an 'id' of 'bar' - assert_tag :attributes => { :id => "bar" } - # there is no tag with a 'name' of 'baz' - assert_no_tag :attributes => { :name => "baz" } - end - - def test_assert_tag_parent - process :test_html_output - - # there is a tag with a parent 'form' tag - assert_tag :parent => { :tag => "form" } - # there is no tag with a parent of 'input' - assert_no_tag :parent => { :tag => "input" } - end - - def test_assert_tag_child - process :test_html_output - - # there is a tag with a child 'input' tag - assert_tag :child => { :tag => "input" } - # there is no tag with a child 'strong' tag - assert_no_tag :child => { :tag => "strong" } - end - - def test_assert_tag_ancestor - process :test_html_output - - # there is a 'li' tag with an ancestor having an id of 'foo' - assert_tag :ancestor => { :attributes => { :id => "foo" } }, :tag => "li" - # there is no tag of any kind with an ancestor having an href matching 'foo' - assert_no_tag :ancestor => { :attributes => { :href => /foo/ } } - end - - def test_assert_tag_descendant - process :test_html_output - - # there is a tag with a descendant 'li' tag - assert_tag :descendant => { :tag => "li" } - # there is no tag with a descendant 'html' tag - assert_no_tag :descendant => { :tag => "html" } - end - - def test_assert_tag_sibling - process :test_html_output - - # there is a tag with a sibling of class 'item' - assert_tag :sibling => { :attributes => { :class => "item" } } - # there is no tag with a sibling 'ul' tag - assert_no_tag :sibling => { :tag => "ul" } - end - - def test_assert_tag_after - process :test_html_output - - # there is a tag following a sibling 'div' tag - assert_tag :after => { :tag => "div" } - # there is no tag following a sibling tag with id 'bar' - assert_no_tag :after => { :attributes => { :id => "bar" } } - end - - def test_assert_tag_before - process :test_html_output - - # there is a tag preceding a tag with id 'bar' - assert_tag :before => { :attributes => { :id => "bar" } } - # there is no tag preceding a 'form' tag - assert_no_tag :before => { :tag => "form" } - end - - def test_assert_tag_children_count - process :test_html_output - - # there is a tag with 2 children - assert_tag :children => { :count => 2 } - # in particular, there is a <ul> tag with two children (a nameless pair of <li>s) - assert_tag :tag => 'ul', :children => { :count => 2 } - # there is no tag with 4 children - assert_no_tag :children => { :count => 4 } - end - - def test_assert_tag_children_less_than - process :test_html_output - - # there is a tag with less than 5 children - assert_tag :children => { :less_than => 5 } - # there is no 'ul' tag with less than 2 children - assert_no_tag :children => { :less_than => 2 }, :tag => "ul" - end - - def test_assert_tag_children_greater_than - process :test_html_output - - # there is a 'body' tag with more than 1 children - assert_tag :children => { :greater_than => 1 }, :tag => "body" - # there is no tag with more than 10 children - assert_no_tag :children => { :greater_than => 10 } - end - - def test_assert_tag_children_only - process :test_html_output - - # there is a tag containing only one child with an id of 'foo' - assert_tag :children => { :count => 1, - :only => { :attributes => { :id => "foo" } } } - # there is no tag containing only one 'li' child - assert_no_tag :children => { :count => 1, :only => { :tag => "li" } } - end - - def test_assert_tag_content - process :test_html_output - - # the output contains the string "Name" - assert_tag :content => /Name/ - # the output does not contain the string "test" - assert_no_tag :content => /test/ - end - - def test_assert_tag_multiple - process :test_html_output - - # there is a 'div', id='bar', with an immediate child whose 'action' - # attribute matches the regexp /somewhere/. - assert_tag :tag => "div", :attributes => { :id => "bar" }, - :child => { :attributes => { :action => /somewhere/ } } - - # there is no 'div', id='foo', with a 'ul' child with more than - # 2 "li" children. - assert_no_tag :tag => "div", :attributes => { :id => "foo" }, - :child => { - :tag => "ul", - :children => { :greater_than => 2, - :only => { :tag => "li" } } } - end - - def test_assert_tag_children_without_content - process :test_html_output - - # there is a form tag with an 'input' child which is a self closing tag - assert_tag :tag => "form", - :children => { :count => 1, - :only => { :tag => "input" } } - - # the body tag has an 'a' child which in turn has an 'img' child - assert_tag :tag => "body", - :children => { :count => 1, - :only => { :tag => "a", - :children => { :count => 1, - :only => { :tag => "img" } } } } - end - def test_should_not_impose_childless_html_tags_in_xml process :test_xml_output @@ -506,23 +389,6 @@ XML assert err.empty? end - def test_assert_tag_attribute_matching - @response.body = '<input type="text" name="my_name">' - assert_tag :tag => 'input', - :attributes => { :name => /my/, :type => 'text' } - assert_no_tag :tag => 'input', - :attributes => { :name => 'my', :type => 'text' } - assert_no_tag :tag => 'input', - :attributes => { :name => /^my$/, :type => 'text' } - end - - def test_assert_tag_content_matching - @response.body = "<p>hello world</p>" - assert_tag :tag => "p", :content => "hello world" - assert_tag :tag => "p", :content => /hello/ - assert_no_tag :tag => "p", :content => "hello" - end - def test_assert_generates assert_generates 'controller/action/5', :controller => 'controller', :action => 'action', :id => '5' assert_generates 'controller/action/7', {:id => "7"}, {:controller => "controller", :action => "action"} @@ -639,7 +505,7 @@ XML def test_id_converted_to_string get :test_params, :id => 20, :foo => Object.new - assert_kind_of String, @request.path_parameters['id'] + assert_kind_of String, @request.path_parameters[:id] end def test_array_path_parameter_handled_properly @@ -650,17 +516,29 @@ XML end get :test_params, :path => ['hello', 'world'] - assert_equal ['hello', 'world'], @request.path_parameters['path'] - assert_equal 'hello/world', @request.path_parameters['path'].to_param + assert_equal ['hello', 'world'], @request.path_parameters[:path] + assert_equal 'hello/world', @request.path_parameters[:path].to_param + end + end + + def test_use_route + with_routing do |set| + set.draw do + get 'via_unnamed_route', to: 'test_case_test/test#test_uri' + get 'via_named_route', as: :a_named_route, to: 'test_case_test/test#test_uri' + end + + assert_deprecated { get :test_uri, use_route: :a_named_route } + assert_equal '/via_named_route', @response.body end end def test_assert_realistic_path_parameters get :test_params, :id => 20, :foo => Object.new - # All elements of path_parameters should use string keys - @request.path_parameters.keys.each do |key| - assert_kind_of String, key + # All elements of path_parameters should use Symbol keys + @request.path_parameters.each_key do |key| + assert_kind_of Symbol, key end end @@ -690,6 +568,7 @@ XML def test_header_properly_reset_after_remote_http_request xhr :get, :test_params assert_nil @request.env['HTTP_X_REQUESTED_WITH'] + assert_nil @request.env['HTTP_ACCEPT'] end def test_header_properly_reset_after_get_request @@ -698,20 +577,28 @@ XML assert_nil @request.instance_variable_get("@request_method") end - def test_params_reset_after_post_request + def test_params_reset_between_post_requests post :no_op, :foo => "bar" assert_equal "bar", @request.params[:foo] - @request.recycle! + post :no_op assert @request.params[:foo].blank? end - def test_symbolized_path_params_reset_after_request + def test_filtered_parameters_reset_between_requests + get :no_op, :foo => "bar" + assert_equal "bar", @request.filtered_parameters[:foo] + + get :no_op, :foo => "baz" + assert_equal "baz", @request.filtered_parameters[:foo] + end + + def test_path_params_reset_between_request get :test_params, :id => "foo" - assert_equal "foo", @request.symbolized_path_parameters[:id] - @request.recycle! + assert_equal "foo", @request.path_parameters[:id] + get :test_params - assert_nil @request.symbolized_path_parameters[:id] + assert_nil @request.path_parameters[:id] end def test_request_protocol_is_reset_after_request @@ -867,6 +754,57 @@ XML end end +module EngineControllerTests + class Engine < ::Rails::Engine + isolate_namespace EngineControllerTests + + routes.draw do + get '/' => 'bar#index' + end + end + + class BarController < ActionController::Base + def index + render :text => 'bar' + end + end + + class BarControllerTest < ActionController::TestCase + tests BarController + + def test_engine_controller_route + get :index + assert_equal @response.body, 'bar' + end + end + + class BarControllerTestWithExplicitRouteSet < ActionController::TestCase + tests BarController + + def setup + @routes = Engine.routes + end + + def test_engine_controller_route + get :index + assert_equal @response.body, 'bar' + end + end + + class BarControllerTestWithHostApplicationRouteSet < ActionController::TestCase + tests BarController + + def test_use_route + with_routing do |set| + set.draw { mount Engine => '/foo' } + + assert_deprecated { get :index, use_route: :foo } + assert_equal @response.body, 'bar' + end + end + end +end + class InferringClassNameTest < ActionController::TestCase def test_determine_controller_class assert_equal ContentController, determine_class("ContentControllerTest") diff --git a/actionpack/test/controller/url_for_integration_test.rb b/actionpack/test/controller/url_for_integration_test.rb index 6c2311e7a5..24a09222b1 100644 --- a/actionpack/test/controller/url_for_integration_test.rb +++ b/actionpack/test/controller/url_for_integration_test.rb @@ -6,6 +6,7 @@ require 'active_support/core_ext/object/with_options' module ActionPack class URLForIntegrationTest < ActiveSupport::TestCase include RoutingTestHelpers + include ActionDispatch::RoutingVerbs Model = Struct.new(:to_param) @@ -61,8 +62,11 @@ module ActionPack root :to => "news#index" } + attr_reader :routes + attr_accessor :controller + def setup - @routes = ActionDispatch::Routing::RouteSet.new + @routes = make_set false @routes.draw(&Mapping) end @@ -70,9 +74,9 @@ module ActionPack ['/admin/users',[ { :use_route => 'admin_users' }]], ['/admin/users',[ { :controller => 'admin/users' }]], ['/admin/users',[ { :controller => 'admin/users', :action => 'index' }]], - ['/admin/users',[ { :action => 'index' }, { :controller => 'admin/users' }]], - ['/admin/users',[ { :controller => 'users', :action => 'index' }, { :controller => 'admin/accounts' }]], - ['/people',[ { :controller => '/people', :action => 'index' }, { :controller => 'admin/accounts' }]], + ['/admin/users',[ { :action => 'index' }, { :controller => 'admin/users', :action => 'index' }, '/admin/users']], + ['/admin/users',[ { :controller => 'users', :action => 'index' }, { :controller => 'admin/accounts', :action => 'show', :id => '1' }, '/admin/accounts/show/1']], + ['/people',[ { :controller => '/people', :action => 'index' }, {:controller=>"admin/accounts", :action=>"foo", :id=>"bar"}, '/admin/accounts/foo/bar']], ['/admin/posts',[ { :controller => 'admin/posts' }]], ['/admin/posts/new',[ { :controller => 'admin/posts', :action => 'new' }]], @@ -86,11 +90,11 @@ module ActionPack ['/archive?year=january',[ { :controller => 'archive', :action => 'index', :year => 'january' }]], ['/people',[ { :controller => 'people', :action => 'index' }]], - ['/people',[ { :action => 'index' }, { :controller => 'people' }]], - ['/people',[ { :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }]], - ['/people',[ { :controller => 'people', :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }]], - ['/people',[ {}, { :controller => 'people', :action => 'index' }]], - ['/people/1',[ { :controller => 'people', :action => 'show' }, { :controller => 'people', :action => 'show', :id => '1' }]], + ['/people',[ { :action => 'index' }, { :controller => 'people', :action => 'index' }, '/people']], + ['/people',[ { :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }, '/people/show/1']], + ['/people',[ { :controller => 'people', :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }, '/people/show/1']], + ['/people',[ {}, { :controller => 'people', :action => 'index' }, '/people']], + ['/people/1',[ { :controller => 'people', :action => 'show' }, { :controller => 'people', :action => 'show', :id => '1' }, '/people/show/1']], ['/people/new',[ { :use_route => 'new_person' }]], ['/people/new',[ { :controller => 'people', :action => 'new' }]], ['/people/1',[ { :use_route => 'person', :id => '1' }]], @@ -98,11 +102,11 @@ module ActionPack ['/people/1.xml',[ { :controller => 'people', :action => 'show', :id => '1', :format => 'xml' }]], ['/people/1',[ { :controller => 'people', :action => 'show', :id => 1 }]], ['/people/1',[ { :controller => 'people', :action => 'show', :id => Model.new('1') }]], - ['/people/1',[ { :action => 'show', :id => '1' }, { :controller => 'people', :action => 'index' }]], - ['/people/1',[ { :action => 'show', :id => 1 }, { :controller => 'people', :action => 'show', :id => '1' }]], - ['/people',[ { :controller => 'people', :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }]], - ['/people/1',[ {}, { :controller => 'people', :action => 'show', :id => '1' }]], - ['/people/1',[ { :controller => 'people', :action => 'show' }, { :controller => 'people', :action => 'index', :id => '1' }]], + ['/people/1',[ { :action => 'show', :id => '1' }, { :controller => 'people', :action => 'index' }, '/people']], + ['/people/1',[ { :action => 'show', :id => 1 }, { :controller => 'people', :action => 'show', :id => '1' }, '/people/show/1']], + ['/people',[ { :controller => 'people', :action => 'index' }, { :controller => 'people', :action => 'show', :id => '1' }, '/people/show/1']], + ['/people/1',[ {}, { :controller => 'people', :action => 'show', :id => '1' }, '/people/show/1']], + ['/people/1',[ { :controller => 'people', :action => 'show' }, { :controller => 'people', :action => 'index', :id => '1' }, '/people/index/1']], ['/people/1/edit',[ { :controller => 'people', :action => 'edit', :id => '1' }]], ['/people/1/edit.xml',[ { :controller => 'people', :action => 'edit', :id => '1', :format => 'xml' }]], ['/people/1/edit',[ { :use_route => 'edit_person', :id => '1' }]], @@ -118,16 +122,15 @@ module ActionPack ['/project',[ { :controller => 'project', :action => 'index' }]], ['/projects/1',[ { :controller => 'project', :action => 'index', :project_id => '1' }]], - ['/projects/1',[ { :controller => 'project', :action => 'index'}, {:project_id => '1' }]], + ['/projects/1',[ { :controller => 'project', :action => 'index'}, {:project_id => '1', :controller => 'project', :action => 'index' }, '/projects/1']], ['/projects/1',[ { :use_route => 'project', :controller => 'project', :action => 'index', :project_id => '1' }]], - ['/projects/1',[ { :use_route => 'project', :controller => 'project', :action => 'index' }, { :project_id => '1' }]], + ['/projects/1',[ { :use_route => 'project', :controller => 'project', :action => 'index' }, { :controller => 'project', :action => 'index', :project_id => '1' }, '/projects/1']], ['/clients',[ { :controller => 'projects', :action => 'index' }]], ['/clients?project_id=1',[ { :controller => 'projects', :action => 'index', :project_id => '1' }]], - ['/clients',[ { :controller => 'projects', :action => 'index' }, { :project_id => '1' }]], - ['/clients',[ { :action => 'index' }, { :controller => 'projects', :action => 'index', :project_id => '1' }]], + ['/clients',[ { :controller => 'projects', :action => 'index' }, { :project_id => '1', :controller => 'project', :action => 'index' }, '/projects/1']], - ['/comment/20',[ { :id => 20 }, { :controller => 'comments', :action => 'show' }]], + ['/comment/20',[ { :id => 20 }, { :controller => 'comments', :action => 'show' }, '/comments/show']], ['/comment/20',[ { :controller => 'comments', :id => 20, :action => 'show' }]], ['/comments/boo',[ { :controller => 'comments', :action => 'boo' }]], @@ -144,24 +147,21 @@ module ActionPack ['/notes',[ { :page_id => nil, :controller => 'notes' }]], ['/notes',[ { :controller => 'notes' }]], ['/notes/print',[ { :controller => 'notes', :action => 'print' }]], - ['/notes/print',[ {}, { :controller => 'notes', :action => 'print' }]], - - ['/notes/index/1',[ { :controller => 'notes' }, { :controller => 'notes', :id => '1' }]], - ['/notes/index/1',[ { :controller => 'notes' }, { :controller => 'notes', :id => '1', :foo => 'bar' }]], - ['/notes/index/1',[ { :controller => 'notes' }, { :controller => 'notes', :id => '1' }]], - ['/notes/index/1',[ { :action => 'index' }, { :controller => 'notes', :id => '1' }]], - ['/notes/index/1',[ {}, { :controller => 'notes', :id => '1' }]], - ['/notes/show/1',[ {}, { :controller => 'notes', :action => 'show', :id => '1' }]], - ['/notes/index/1',[ { :controller => 'notes', :id => '1' }, { :foo => 'bar' }]], - ['/posts',[ { :controller => 'posts' }, { :controller => 'notes', :action => 'show', :id => '1' }]], - ['/notes/list',[ { :action => 'list' }, { :controller => 'notes', :action => 'show', :id => '1' }]], + ['/notes/print',[ {}, { :controller => 'notes', :action => 'print' }, '/notes/print']], + + ['/notes/index/1',[ { :controller => 'notes' }, { :controller => 'notes', :action => 'index', :id => '1' }, '/notes/index/1']], + ['/notes/index/1',[ { :controller => 'notes' }, { :controller => 'notes', :id => '1', :action => 'index' }, '/notes/index/1']], + ['/notes/index/1',[ { :action => 'index' }, { :controller => 'notes', :id => '1', :action => 'index' }, '/notes/index/1']], + ['/notes/index/1',[ {}, { :controller => 'notes', :id => '1', :action => 'index' }, '/notes/index/1']], + ['/notes/show/1',[ {}, { :controller => 'notes', :action => 'show', :id => '1' }, '/notes/show/1']], + ['/posts',[ { :controller => 'posts' }, { :controller => 'notes', :action => 'show', :id => '1' }, '/notes/show/1']], + ['/notes/list',[ { :action => 'list' }, { :controller => 'notes', :action => 'show', :id => '1' }, '/notes/show/1']], ['/posts/ping',[ { :controller => 'posts', :action => 'ping' }]], ['/posts/show/1',[ { :controller => 'posts', :action => 'show', :id => '1' }]], ['/posts',[ { :controller => 'posts' }]], ['/posts',[ { :controller => 'posts', :action => 'index' }]], - ['/posts',[ { :controller => 'posts' }, { :controller => 'posts', :action => 'index' }]], - ['/posts/create',[ { :action => 'create' }, { :controller => 'posts' }]], + ['/posts/create',[ { :action => 'create' }, {:day=>nil, :month=>nil, :controller=>"posts", :action=>"show_date"}, '/blog']], ['/posts?foo=bar',[ { :controller => 'posts', :foo => 'bar' }]], ['/posts?foo%5B%5D=bar&foo%5B%5D=baz', [{ :controller => 'posts', :foo => ['bar', 'baz'] }]], ['/posts?page=2', [{ :controller => 'posts', :page => 2 }]], @@ -169,9 +169,20 @@ module ActionPack ['/news.rss', [{ :controller => 'news', :action => 'index', :format => 'rss' }]], ].each_with_index do |(url, params), i| - define_method("test_#{url.gsub(/\W/, '_')}_#{i}") do - assert_equal url, url_for(@routes, *params), params.inspect - end + if params.length > 1 + hash, path_params, route = *params + hash[:only_path] = true + + define_method("test_#{url.gsub(/\W/, '_')}_#{i}") do + get URI('http://test.host' + route.to_s) + assert_equal path_params, controller.request.path_parameters + assert_equal url, controller.url_for(hash), params.inspect + end + else + define_method("test_#{url.gsub(/\W/, '_')}_#{i}") do + assert_equal url, url_for(@routes, params.first), params.inspect + end + end end end end diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb index d2b4952759..0ffa2d2a03 100644 --- a/actionpack/test/controller/url_for_test.rb +++ b/actionpack/test/controller/url_for_test.rb @@ -11,6 +11,25 @@ module AbstractController W.default_url_options.clear end + def test_nested_optional + klass = Class.new { + include ActionDispatch::Routing::RouteSet.new.tap { |r| + r.draw { + get "/foo/(:bar/(:baz))/:zot", :as => 'fun', + :controller => :articles, + :action => :index + } + }.url_helpers + self.default_url_options[:host] = 'example.com' + } + + path = klass.new.fun_path({:controller => :articles, + :baz => "baz", + :zot => "zot"}) + # :bar key isn't provided + assert_equal '/foo/zot', path + end + def add_host! W.default_url_options[:host] = 'www.basecamphq.com' end @@ -35,6 +54,20 @@ module AbstractController ) end + def test_nil_anchor + assert_equal( + '/c/a', + W.new.url_for(only_path: true, controller: 'c', action: 'a', anchor: nil) + ) + end + + def test_false_anchor + assert_equal( + '/c/a', + W.new.url_for(only_path: true, controller: 'c', action: 'a', anchor: false) + ) + end + def test_anchor_should_call_to_param assert_equal('/c/a#anchor', W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anchor')) @@ -75,7 +108,7 @@ module AbstractController end def test_subdomain_may_be_object - model = mock(:to_param => 'api') + model = Class.new { def self.to_param; 'api'; end } add_host! assert_equal('http://api.basecamphq.com/c/a/i', W.new.url_for(:subdomain => model, :controller => 'c', :action => 'a', :id => 'i') @@ -169,6 +202,18 @@ module AbstractController ) end + def test_without_protocol_and_with_port + add_host! + add_port! + + assert_equal('//www.basecamphq.com:3000/c/a/i', + W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => '//') + ) + assert_equal('//www.basecamphq.com:3000/c/a/i', + W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => false) + ) + end + def test_trailing_slash add_host! options = {:controller => 'foo', :trailing_slash => true, :action => 'bar', :id => '33'} @@ -199,14 +244,11 @@ module AbstractController def test_trailing_slash_with_params url = W.new.url_for(:trailing_slash => true, :only_path => true, :controller => 'cont', :action => 'act', :p1 => 'cafe', :p2 => 'link') params = extract_params(url) - assert_equal params[0], { :p1 => 'cafe' }.to_query - assert_equal params[1], { :p2 => 'link' }.to_query + assert_equal({p1: 'cafe'}.to_query, params[0]) + assert_equal({p2: 'link'}.to_query, params[1]) end def test_relative_url_root_is_respected - # ROUTES TODO: Tests should not have to pass :relative_url_root directly. This - # should probably come from routes. - add_host! assert_equal('https://www.basecamphq.com/subdir/c/a/i', W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https', :script_name => '/subdir') @@ -248,6 +290,13 @@ module AbstractController end end + def test_using_nil_script_name_properly_concats_with_original_script_name + add_host! + assert_equal('https://www.basecamphq.com/subdir/c/a/i', + W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https', :script_name => nil, :original_script_name => '/subdir') + ) + end + def test_only_path with_routing do |set| set.draw do @@ -258,12 +307,12 @@ module AbstractController # We need to create a new class in order to install the new named route. kls = Class.new { include set.url_helpers } controller = kls.new - assert controller.respond_to?(:home_url) + assert_respond_to controller, :home_url assert_equal '/brave/new/world', - controller.send(:url_for, :controller => 'brave', :action => 'new', :id => 'world', :only_path => true) + controller.url_for(:controller => 'brave', :action => 'new', :id => 'world', :only_path => true) - assert_equal("/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'unused', :only_path => true)) - assert_equal("/home/sweet/home/alabama", controller.send(:home_path, 'alabama')) + assert_equal("/home/sweet/home/alabama", controller.home_path(:user => 'alabama', :host => 'unused')) + assert_equal("/home/sweet/home/alabama", controller.home_path('alabama')) end end @@ -276,40 +325,40 @@ module AbstractController def test_two_parameters url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :p1 => 'X1', :p2 => 'Y2') params = extract_params(url) - assert_equal params[0], { :p1 => 'X1' }.to_query - assert_equal params[1], { :p2 => 'Y2' }.to_query + assert_equal({p1: 'X1'}.to_query, params[0]) + assert_equal({p2: 'Y2'}.to_query, params[1]) end def test_hash_parameter url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => {:name => 'Bob', :category => 'prof'}) params = extract_params(url) - assert_equal params[0], { 'query[category]' => 'prof' }.to_query - assert_equal params[1], { 'query[name]' => 'Bob' }.to_query + assert_equal({'query[category]' => 'prof'}.to_query, params[0]) + assert_equal({'query[name]' => 'Bob'}.to_query, params[1]) end def test_array_parameter url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => ['Bob', 'prof']) params = extract_params(url) - assert_equal params[0], { 'query[]' => 'Bob' }.to_query - assert_equal params[1], { 'query[]' => 'prof' }.to_query + assert_equal({'query[]' => 'Bob'}.to_query, params[0]) + assert_equal({'query[]' => 'prof'}.to_query, params[1]) end def test_hash_recursive_parameters url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => {:person => {:name => 'Bob', :position => 'prof'}, :hobby => 'piercing'}) params = extract_params(url) - assert_equal params[0], { 'query[hobby]' => 'piercing' }.to_query - assert_equal params[1], { 'query[person][name]' => 'Bob' }.to_query - assert_equal params[2], { 'query[person][position]' => 'prof' }.to_query + assert_equal({'query[hobby]' => 'piercing'}.to_query, params[0]) + assert_equal({'query[person][name]' => 'Bob' }.to_query, params[1]) + assert_equal({'query[person][position]' => 'prof' }.to_query, params[2]) end def test_hash_recursive_and_array_parameters url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :id => 101, :query => {:person => {:name => 'Bob', :position => ['prof', 'art director']}, :hobby => 'piercing'}) assert_match(%r(^/c/a/101), url) params = extract_params(url) - assert_equal params[0], { 'query[hobby]' => 'piercing' }.to_query - assert_equal params[1], { 'query[person][name]' => 'Bob' }.to_query - assert_equal params[2], { 'query[person][position][]' => 'art director' }.to_query - assert_equal params[3], { 'query[person][position][]' => 'prof' }.to_query + assert_equal({'query[hobby]' => 'piercing' }.to_query, params[0]) + assert_equal({'query[person][name]' => 'Bob' }.to_query, params[1]) + assert_equal({'query[person][position][]' => 'art director'}.to_query, params[2]) + assert_equal({'query[person][position][]' => 'prof' }.to_query, params[3]) end def test_path_generation_for_symbol_parameter_keys diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb index d80b0e2da0..2b109ff19e 100644 --- a/actionpack/test/controller/webservice_test.rb +++ b/actionpack/test/controller/webservice_test.rb @@ -83,6 +83,16 @@ class WebServiceTest < ActionDispatch::IntegrationTest end end + def test_parsing_json_doesnot_rescue_exception + with_test_route_set do + with_params_parsers Mime::JSON => Proc.new { |data| raise Interrupt } do + assert_raises(Interrupt) do + post "/", '{"title":"JSON"}}', 'CONTENT_TYPE' => 'application/json' + end + end + end + end + private def with_params_parsers(parsers = {}) old_session = @integration_session diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 91ac13e7c6..19a98a4054 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -1,16 +1,29 @@ require 'abstract_unit' - -begin - require 'openssl' - OpenSSL::PKCS5 -rescue LoadError, NameError - $stderr.puts "Skipping KeyGenerator test: broken OpenSSL install" -else - +require 'openssl' require 'active_support/key_generator' require 'active_support/message_verifier' class CookiesTest < ActionController::TestCase + class CustomSerializer + def self.load(value) + value.to_s + " and loaded" + end + + def self.dump(value) + value.to_s + " was dumped" + end + end + + class JSONWrapper + def initialize(obj) + @obj = obj + end + + def as_json(options = nil) + "wrapped: #{@obj.as_json(options)}" + end + end + class TestController < ActionController::Base def authenticate cookies["user_name"] = "david" @@ -75,6 +88,11 @@ class CookiesTest < ActionController::TestCase head :ok end + def set_wrapped_signed_cookie + cookies.signed[:user_id] = JSONWrapper.new(45) + head :ok + end + def get_signed_cookie cookies.signed[:user_id] head :ok @@ -85,6 +103,11 @@ class CookiesTest < ActionController::TestCase head :ok end + def set_wrapped_encrypted_cookie + cookies.encrypted[:foo] = JSONWrapper.new('bar') + head :ok + end + def get_encrypted_cookie cookies.encrypted[:foo] head :ok @@ -122,11 +145,21 @@ class CookiesTest < ActionController::TestCase head :ok end + def set_cookie_with_domain_all_as_string + cookies[:user_name] = {:value => "rizwanreza", :domain => 'all'} + head :ok + end + def delete_cookie_with_domain cookies.delete(:user_name, :domain => :all) head :ok end + def delete_cookie_with_domain_all_as_string + cookies.delete(:user_name, :domain => 'all') + head :ok + end + def set_cookie_with_domain_and_tld cookies[:user_name] = {:value => "rizwanreza", :domain => :all, :tld_length => 2} head :ok @@ -176,7 +209,7 @@ class CookiesTest < ActionController::TestCase def setup super - @request.env["action_dispatch.key_generator"] = ActiveSupport::KeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33") + @request.env["action_dispatch.key_generator"] = ActiveSupport::KeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33", iterations: 2) @request.env["action_dispatch.signed_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33" @request.env["action_dispatch.encrypted_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33" @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33" @@ -253,7 +286,7 @@ class CookiesTest < ActionController::TestCase def test_setting_the_same_value_to_permanent_cookie request.cookies[:user_name] = 'Jamie' get :set_permanent_cookie - assert_equal response.cookies, 'user_name' => 'Jamie' + assert_equal({'user_name' => 'Jamie'}, response.cookies) end def test_setting_with_escapable_characters @@ -359,17 +392,128 @@ class CookiesTest < ActionController::TestCase assert_equal 'Jamie', @controller.send(:cookies).permanent[:user_name] end - def test_signed_cookie + def test_signed_cookie_using_default_digest get :set_signed_cookie - assert_equal 45, @controller.send(:cookies).signed[:user_id] + cookies = @controller.send :cookies + assert_not_equal 45, cookies[:user_id] + assert_equal 45, cookies.signed[:user_id] + + key_generator = @request.env["action_dispatch.key_generator"] + signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"] + secret = key_generator.generate_key(signed_cookie_salt) + + verifier = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal, digest: 'SHA1') + assert_equal verifier.generate(45), cookies[:user_id] + end + + def test_signed_cookie_using_custom_digest + @request.env["action_dispatch.cookies_digest"] = 'SHA256' + get :set_signed_cookie + cookies = @controller.send :cookies + assert_not_equal 45, cookies[:user_id] + assert_equal 45, cookies.signed[:user_id] + + key_generator = @request.env["action_dispatch.key_generator"] + signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"] + secret = key_generator.generate_key(signed_cookie_salt) + + verifier = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal, digest: 'SHA256') + assert_equal verifier.generate(45), cookies[:user_id] + end + + def test_signed_cookie_using_default_serializer + get :set_signed_cookie + cookies = @controller.send :cookies + assert_not_equal 45, cookies[:user_id] + assert_equal 45, cookies.signed[:user_id] + end + + def test_signed_cookie_using_marshal_serializer + @request.env["action_dispatch.cookies_serializer"] = :marshal + get :set_signed_cookie + cookies = @controller.send :cookies + assert_not_equal 45, cookies[:user_id] + assert_equal 45, cookies.signed[:user_id] + end + + def test_signed_cookie_using_json_serializer + @request.env["action_dispatch.cookies_serializer"] = :json + get :set_signed_cookie + cookies = @controller.send :cookies + assert_not_equal 45, cookies[:user_id] + assert_equal 45, cookies.signed[:user_id] + end + + def test_wrapped_signed_cookie_using_json_serializer + @request.env["action_dispatch.cookies_serializer"] = :json + get :set_wrapped_signed_cookie + cookies = @controller.send :cookies + assert_not_equal 'wrapped: 45', cookies[:user_id] + assert_equal 'wrapped: 45', cookies.signed[:user_id] + end + + def test_signed_cookie_using_custom_serializer + @request.env["action_dispatch.cookies_serializer"] = CustomSerializer + get :set_signed_cookie + assert_not_equal 45, cookies[:user_id] + assert_equal '45 was dumped and loaded', cookies.signed[:user_id] + end + + def test_signed_cookie_using_hybrid_serializer_can_migrate_marshal_dumped_value_to_json + @request.env["action_dispatch.cookies_serializer"] = :hybrid + + key_generator = @request.env["action_dispatch.key_generator"] + signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"] + secret = key_generator.generate_key(signed_cookie_salt) + + marshal_value = ActiveSupport::MessageVerifier.new(secret, serializer: Marshal).generate(45) + @request.headers["Cookie"] = "user_id=#{marshal_value}" + + get :get_signed_cookie + + cookies = @controller.send :cookies + assert_not_equal 45, cookies[:user_id] + assert_equal 45, cookies.signed[:user_id] + + verifier = ActiveSupport::MessageVerifier.new(secret, serializer: JSON) + assert_equal 45, verifier.verify(@response.cookies['user_id']) end - def test_accessing_nonexistant_signed_cookie_should_not_raise_an_invalid_signature + def test_signed_cookie_using_hybrid_serializer_can_read_from_json_dumped_value + @request.env["action_dispatch.cookies_serializer"] = :hybrid + + key_generator = @request.env["action_dispatch.key_generator"] + signed_cookie_salt = @request.env["action_dispatch.signed_cookie_salt"] + secret = key_generator.generate_key(signed_cookie_salt) + json_value = ActiveSupport::MessageVerifier.new(secret, serializer: JSON).generate(45) + @request.headers["Cookie"] = "user_id=#{json_value}" + + get :get_signed_cookie + + cookies = @controller.send :cookies + assert_not_equal 45, cookies[:user_id] + assert_equal 45, cookies.signed[:user_id] + + assert_nil @response.cookies["user_id"] + end + + def test_accessing_nonexistent_signed_cookie_should_not_raise_an_invalid_signature get :set_signed_cookie assert_nil @controller.send(:cookies).signed[:non_existant_attribute] end - def test_encrypted_cookie + def test_encrypted_cookie_using_default_serializer + get :set_encrypted_cookie + cookies = @controller.send :cookies + assert_not_equal 'bar', cookies[:foo] + assert_raise TypeError do + cookies.signed[:foo] + end + assert_equal 'bar', cookies.encrypted[:foo] + end + + def test_encrypted_cookie_using_marshal_serializer + @request.env["action_dispatch.cookies_serializer"] = :marshal get :set_encrypted_cookie cookies = @controller.send :cookies assert_not_equal 'bar', cookies[:foo] @@ -379,7 +523,99 @@ class CookiesTest < ActionController::TestCase assert_equal 'bar', cookies.encrypted[:foo] end - def test_accessing_nonexistant_encrypted_cookie_should_not_raise_invalid_message + def test_encrypted_cookie_using_json_serializer + @request.env["action_dispatch.cookies_serializer"] = :json + get :set_encrypted_cookie + cookies = @controller.send :cookies + assert_not_equal 'bar', cookies[:foo] + assert_raises ::JSON::ParserError do + cookies.signed[:foo] + end + assert_equal 'bar', cookies.encrypted[:foo] + end + + def test_wrapped_encrypted_cookie_using_json_serializer + @request.env["action_dispatch.cookies_serializer"] = :json + get :set_wrapped_encrypted_cookie + cookies = @controller.send :cookies + assert_not_equal 'wrapped: bar', cookies[:foo] + assert_raises ::JSON::ParserError do + cookies.signed[:foo] + end + assert_equal 'wrapped: bar', cookies.encrypted[:foo] + end + + def test_encrypted_cookie_using_custom_serializer + @request.env["action_dispatch.cookies_serializer"] = CustomSerializer + get :set_encrypted_cookie + assert_not_equal 'bar', cookies.encrypted[:foo] + assert_equal 'bar was dumped and loaded', cookies.encrypted[:foo] + end + + def test_encrypted_cookie_using_custom_digest + @request.env["action_dispatch.cookies_digest"] = 'SHA256' + get :set_encrypted_cookie + cookies = @controller.send :cookies + assert_not_equal 'bar', cookies[:foo] + assert_equal 'bar', cookies.encrypted[:foo] + + sign_secret = @request.env["action_dispatch.key_generator"].generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"]) + + sha1_verifier = ActiveSupport::MessageVerifier.new(sign_secret, serializer: ActiveSupport::MessageEncryptor::NullSerializer, digest: 'SHA1') + sha256_verifier = ActiveSupport::MessageVerifier.new(sign_secret, serializer: ActiveSupport::MessageEncryptor::NullSerializer, digest: 'SHA256') + + assert_raises(ActiveSupport::MessageVerifier::InvalidSignature) do + sha1_verifier.verify(cookies[:foo]) + end + + assert_nothing_raised do + sha256_verifier.verify(cookies[:foo]) + end + end + + def test_encrypted_cookie_using_hybrid_serializer_can_migrate_marshal_dumped_value_to_json + @request.env["action_dispatch.cookies_serializer"] = :hybrid + + key_generator = @request.env["action_dispatch.key_generator"] + encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"] + encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"] + secret = key_generator.generate_key(encrypted_cookie_salt) + sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt) + + marshal_value = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: Marshal).encrypt_and_sign("bar") + @request.headers["Cookie"] = "foo=#{marshal_value}" + + get :get_encrypted_cookie + + cookies = @controller.send :cookies + assert_not_equal "bar", cookies[:foo] + assert_equal "bar", cookies.encrypted[:foo] + + encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON) + assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) + end + + def test_encrypted_cookie_using_hybrid_serializer_can_read_from_json_dumped_value + @request.env["action_dispatch.cookies_serializer"] = :hybrid + + key_generator = @request.env["action_dispatch.key_generator"] + encrypted_cookie_salt = @request.env["action_dispatch.encrypted_cookie_salt"] + encrypted_signed_cookie_salt = @request.env["action_dispatch.encrypted_signed_cookie_salt"] + secret = key_generator.generate_key(encrypted_cookie_salt) + sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt) + json_value = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON).encrypt_and_sign("bar") + @request.headers["Cookie"] = "foo=#{json_value}" + + get :get_encrypted_cookie + + cookies = @controller.send :cookies + assert_not_equal "bar", cookies[:foo] + assert_equal "bar", cookies.encrypted[:foo] + + assert_nil @response.cookies["foo"] + end + + def test_accessing_nonexistent_encrypted_cookie_should_not_raise_invalid_message get :set_encrypted_cookie assert_nil @controller.send(:cookies).encrypted[:non_existant_attribute] end @@ -537,6 +773,123 @@ class CookiesTest < ActionController::TestCase assert_equal 'bar', encryptor.decrypt_and_verify(@response.cookies["foo"]) end + def test_legacy_json_signed_cookie_is_read_and_transparently_upgraded_by_signed_json_cookie_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.cookies_serializer"] = :json + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + + legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate(45) + + @request.headers["Cookie"] = "user_id=#{legacy_value}" + get :get_signed_cookie + + assert_equal 45, @controller.send(:cookies).signed[:user_id] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) + verifier = ActiveSupport::MessageVerifier.new(secret, serializer: JSON) + assert_equal 45, verifier.verify(@response.cookies["user_id"]) + end + + def test_legacy_json_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_json_cookie_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.cookies_serializer"] = :json + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + @request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee" + @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9" + + legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate('bar') + + @request.headers["Cookie"] = "foo=#{legacy_value}" + get :get_encrypted_cookie + + assert_equal 'bar', @controller.send(:cookies).encrypted[:foo] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"]) + sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"]) + encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON) + assert_equal 'bar', encryptor.decrypt_and_verify(@response.cookies["foo"]) + end + + def test_legacy_json_signed_cookie_is_read_and_transparently_upgraded_by_signed_json_hybrid_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.cookies_serializer"] = :hybrid + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + + legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate(45) + + @request.headers["Cookie"] = "user_id=#{legacy_value}" + get :get_signed_cookie + + assert_equal 45, @controller.send(:cookies).signed[:user_id] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) + verifier = ActiveSupport::MessageVerifier.new(secret, serializer: JSON) + assert_equal 45, verifier.verify(@response.cookies["user_id"]) + end + + def test_legacy_json_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_hybrid_cookie_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.cookies_serializer"] = :hybrid + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + @request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee" + @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9" + + legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate('bar') + + @request.headers["Cookie"] = "foo=#{legacy_value}" + get :get_encrypted_cookie + + assert_equal 'bar', @controller.send(:cookies).encrypted[:foo] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"]) + sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"]) + encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON) + assert_equal 'bar', encryptor.decrypt_and_verify(@response.cookies["foo"]) + end + + def test_legacy_marshal_signed_cookie_is_read_and_transparently_upgraded_by_signed_json_hybrid_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.cookies_serializer"] = :hybrid + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + + legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate(45) + + @request.headers["Cookie"] = "user_id=#{legacy_value}" + get :get_signed_cookie + + assert_equal 45, @controller.send(:cookies).signed[:user_id] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) + verifier = ActiveSupport::MessageVerifier.new(secret, serializer: JSON) + assert_equal 45, verifier.verify(@response.cookies["user_id"]) + end + + def test_legacy_marshal_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_hybrid_cookie_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.cookies_serializer"] = :hybrid + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + @request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee" + @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9" + + legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate('bar') + + @request.headers["Cookie"] = "foo=#{legacy_value}" + get :get_encrypted_cookie + + assert_equal 'bar', @controller.send(:cookies).encrypted[:foo] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"]) + sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"]) + encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON) + assert_equal 'bar', encryptor.decrypt_and_verify(@response.cookies["foo"]) + end + def test_legacy_signed_cookie_is_treated_as_nil_by_signed_cookie_jar_if_tampered @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" @@ -694,8 +1047,6 @@ class CookiesTest < ActionController::TestCase assert_equal "dhh", cookies['user_name'] end - - def test_setting_request_cookies_is_indifferent_access cookies.clear cookies[:user_name] = "andrew" @@ -807,5 +1158,3 @@ class CookiesTest < ActionController::TestCase end end end - -end diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index 3045a07ad6..1e5ed60b09 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -10,6 +10,8 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest @closed = false end + # We're obliged to implement this (even though it doesn't actually + # get called here) to properly comply with the Rack SPEC def each end @@ -17,6 +19,10 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest @closed = true end + def method_that_raises + raise StandardError.new 'error in framework' + end + def call(env) env['action_dispatch.show_detailed_exceptions'] = @detailed req = ActionDispatch::Request.new(env) @@ -37,12 +43,28 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest raise ActionController::InvalidAuthenticityToken when "/not_found_original_exception" raise ActionView::Template::Error.new('template', AbstractController::ActionNotFound.new) + when "/missing_template" + raise ActionView::MissingTemplate.new(%w(foo), 'foo/index', %w(foo), false, 'mailer') when "/bad_request" raise ActionController::BadRequest when "/missing_keys" raise ActionController::UrlGenerationError, "No route matches" when "/parameter_missing" raise ActionController::ParameterMissing, :missing_param_key + when "/original_syntax_error" + eval 'broke_syntax =' # `eval` need for raise native SyntaxError at runtime + when "/syntax_error_into_view" + begin + eval 'broke_syntax =' + rescue Exception => e + template = ActionView::Template.new(File.read(__FILE__), + __FILE__, + ActionView::Template::Handlers::Raw.new, + {}) + raise ActionView::Template::Error.new(template, e) + end + when "/framework_raises" + method_that_raises else raise "puke!" end @@ -100,6 +122,15 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_no_match '<|>', routing_table, "there should not be escaped html in the output" end + test 'displays request and response info when a RoutingError occurs' do + @app = DevelopmentApp + + get "/pass", {}, {'action_dispatch.show_exceptions' => true} + + assert_select 'h2', /Request/ + assert_select 'h2', /Response/ + end + test "rescue with diagnostics message" do @app = DevelopmentApp @@ -134,38 +165,39 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest get "/", {}, xhr_request_env assert_response 500 + assert_no_match(/<header>/, body) assert_no_match(/<body>/, body) - assert_equal response.content_type, "text/plain" - assert_match(/puke/, body) + assert_equal "text/plain", response.content_type + assert_match(/RuntimeError\npuke/, body) get "/not_found", {}, xhr_request_env assert_response 404 assert_no_match(/<body>/, body) - assert_equal response.content_type, "text/plain" + assert_equal "text/plain", response.content_type assert_match(/#{AbstractController::ActionNotFound.name}/, body) get "/method_not_allowed", {}, xhr_request_env assert_response 405 assert_no_match(/<body>/, body) - assert_equal response.content_type, "text/plain" + assert_equal "text/plain", response.content_type assert_match(/ActionController::MethodNotAllowed/, body) get "/unknown_http_method", {}, xhr_request_env assert_response 405 assert_no_match(/<body>/, body) - assert_equal response.content_type, "text/plain" + assert_equal "text/plain", response.content_type assert_match(/ActionController::UnknownHttpMethod/, body) get "/bad_request", {}, xhr_request_env assert_response 400 assert_no_match(/<body>/, body) - assert_equal response.content_type, "text/plain" + assert_equal "text/plain", response.content_type assert_match(/ActionController::BadRequest/, body) get "/parameter_missing", {}, xhr_request_env assert_response 400 assert_no_match(/<body>/, body) - assert_equal response.content_type, "text/plain" + assert_equal "text/plain", response.content_type assert_match(/ActionController::ParameterMissing/, body) end @@ -209,6 +241,29 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest assert_match(/RuntimeError\n\s+in FeaturedTileController/, body) end + test "show formatted params" do + @app = DevelopmentApp + + params = { + 'id' => 'unknown', + 'someparam' => { + 'foo' => 'bar', + 'abc' => 'goo' + } + } + + get("/runtime_error", {}, { + 'action_dispatch.show_exceptions' => true, + 'action_dispatch.request.parameters' => { + 'action' => 'show', + 'controller' => 'featured_tile' + }.merge(params) + }) + assert_response 500 + + assert_includes(body, CGI.escapeHTML(PP.pp(params, "", 200))) + end + test "sets the HTTP charset parameter" do @app = DevelopmentApp @@ -242,4 +297,77 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest get "/", {}, env assert_operator((output.rewind && output.read).lines.count, :>, 10) end + + test 'display backtrace when error type is SyntaxError' do + @app = DevelopmentApp + + get '/original_syntax_error', {}, {'action_dispatch.backtrace_cleaner' => ActiveSupport::BacktraceCleaner.new} + + assert_response 500 + assert_select '#Application-Trace' do + assert_select 'pre code', /\(eval\):1: syntax error, unexpected/ + end + end + + test 'display backtrace on template missing errors' do + @app = DevelopmentApp + + get "/missing_template", nil, {} + + assert_select "header h1", /Template is missing/ + + assert_select "#container h2", /^Missing template/ + + assert_select '#Application-Trace' + assert_select '#Framework-Trace' + assert_select '#Full-Trace' + + assert_select 'h2', /Request/ + end + + test 'display backtrace when error type is SyntaxError wrapped by ActionView::Template::Error' do + @app = DevelopmentApp + + get '/syntax_error_into_view', {}, {'action_dispatch.backtrace_cleaner' => ActiveSupport::BacktraceCleaner.new} + + assert_response 500 + assert_select '#Application-Trace' do + assert_select 'pre code', /\(eval\):1: syntax error, unexpected/ + end + end + + test 'debug exceptions app shows user code that caused the error in source view' do + @app = DevelopmentApp + Rails.stubs(:root).returns(Pathname.new('.')) + cleaner = ActiveSupport::BacktraceCleaner.new.tap do |bc| + bc.add_silencer { |line| line =~ /method_that_raises/ } + bc.add_silencer { |line| line !~ %r{test/dispatch/debug_exceptions_test.rb} } + end + + get '/framework_raises', {}, {'action_dispatch.backtrace_cleaner' => cleaner} + + # Assert correct error + assert_response 500 + assert_select 'h2', /error in framework/ + + # assert source view line is the call to method_that_raises + assert_select 'div.source:not(.hidden)' do + assert_select 'pre .line.active', /method_that_raises/ + end + + # assert first source view (hidden) that throws the error + assert_select 'div.source:first' do + assert_select 'pre .line.active', /raise StandardError\.new/ + end + + # assert application trace refers to line that calls method_that_raises is first + assert_select '#Application-Trace' do + assert_select 'pre code a:first', %r{test/dispatch/debug_exceptions_test\.rb:\d+:in `call} + end + + # assert framework trace that that threw the error is first + assert_select '#Framework-Trace' do + assert_select 'pre code a:first', /method_that_raises/ + end + end end diff --git a/actionpack/test/dispatch/exception_wrapper_test.rb b/actionpack/test/dispatch/exception_wrapper_test.rb new file mode 100644 index 0000000000..d7408164ba --- /dev/null +++ b/actionpack/test/dispatch/exception_wrapper_test.rb @@ -0,0 +1,97 @@ +require 'abstract_unit' + +module ActionDispatch + class ExceptionWrapperTest < ActionDispatch::IntegrationTest + class TestError < StandardError + attr_reader :backtrace + + def initialize(*backtrace) + @backtrace = backtrace.flatten + end + end + + class BadlyDefinedError < StandardError + def backtrace + nil + end + end + + setup do + Rails.stubs(:root).returns(Pathname.new('.')) + + cleaner = ActiveSupport::BacktraceCleaner.new + cleaner.add_silencer { |line| line !~ /^lib/ } + + @environment = { 'action_dispatch.backtrace_cleaner' => cleaner } + end + + test '#source_extracts fetches source fragments for every backtrace entry' do + exception = TestError.new("lib/file.rb:42:in `index'") + wrapper = ExceptionWrapper.new({}, exception) + + wrapper.expects(:source_fragment).with('lib/file.rb', 42).returns('foo') + + assert_equal [ code: 'foo', line_number: 42 ], wrapper.source_extracts + end + + + test '#application_trace returns traces only from the application' do + exception = TestError.new(caller.prepend("lib/file.rb:42:in `index'")) + wrapper = ExceptionWrapper.new(@environment, exception) + + assert_equal [ "lib/file.rb:42:in `index'" ], wrapper.application_trace + end + + test '#application_trace cannot be nil' do + nil_backtrace_wrapper = ExceptionWrapper.new(@environment, BadlyDefinedError.new) + nil_cleaner_wrapper = ExceptionWrapper.new({}, BadlyDefinedError.new) + + assert_equal [], nil_backtrace_wrapper.application_trace + assert_equal [], nil_cleaner_wrapper.application_trace + end + + test '#framework_trace returns traces outside the application' do + exception = TestError.new(caller.prepend("lib/file.rb:42:in `index'")) + wrapper = ExceptionWrapper.new(@environment, exception) + + assert_equal caller, wrapper.framework_trace + end + + test '#framework_trace cannot be nil' do + nil_backtrace_wrapper = ExceptionWrapper.new(@environment, BadlyDefinedError.new) + nil_cleaner_wrapper = ExceptionWrapper.new({}, BadlyDefinedError.new) + + assert_equal [], nil_backtrace_wrapper.framework_trace + assert_equal [], nil_cleaner_wrapper.framework_trace + end + + test '#full_trace returns application and framework traces' do + exception = TestError.new(caller.prepend("lib/file.rb:42:in `index'")) + wrapper = ExceptionWrapper.new(@environment, exception) + + assert_equal exception.backtrace, wrapper.full_trace + end + + test '#full_trace cannot be nil' do + nil_backtrace_wrapper = ExceptionWrapper.new(@environment, BadlyDefinedError.new) + nil_cleaner_wrapper = ExceptionWrapper.new({}, BadlyDefinedError.new) + + assert_equal [], nil_backtrace_wrapper.full_trace + assert_equal [], nil_cleaner_wrapper.full_trace + end + + test '#traces returns every trace by category enumerated with an index' do + exception = TestError.new("lib/file.rb:42:in `index'", "/gems/rack.rb:43:in `index'") + wrapper = ExceptionWrapper.new(@environment, exception) + + assert_equal({ + 'Application Trace' => [ id: 0, trace: "lib/file.rb:42:in `index'" ], + 'Framework Trace' => [ id: 1, trace: "/gems/rack.rb:43:in `index'" ], + 'Full Trace' => [ + { id: 0, trace: "lib/file.rb:42:in `index'" }, + { id: 1, trace: "/gems/rack.rb:43:in `index'" } + ] + }, wrapper.traces) + end + end +end diff --git a/actionpack/test/dispatch/header_test.rb b/actionpack/test/dispatch/header_test.rb index 9e37b96951..e2b38c23bc 100644 --- a/actionpack/test/dispatch/header_test.rb +++ b/actionpack/test/dispatch/header_test.rb @@ -55,6 +55,8 @@ class HeaderTest < ActiveSupport::TestCase test "key?" do assert @headers.key?("CONTENT_TYPE") assert @headers.include?("CONTENT_TYPE") + assert @headers.key?("Content-Type") + assert @headers.include?("Content-Type") end test "fetch with block" do diff --git a/actionpack/test/dispatch/live_response_test.rb b/actionpack/test/dispatch/live_response_test.rb index e0cfb73acf..512f3a8a7a 100644 --- a/actionpack/test/dispatch/live_response_test.rb +++ b/actionpack/test/dispatch/live_response_test.rb @@ -6,6 +6,7 @@ module ActionController class ResponseTest < ActiveSupport::TestCase def setup @response = Live::Response.new + @response.request = ActionDispatch::Request.new({}) #yolo end def test_header_merge @@ -34,6 +35,7 @@ module ActionController @response.stream.close } + @response.await_commit @response.each do |part| assert_equal 'foo', part latch.release @@ -58,21 +60,30 @@ module ActionController assert_nil @response.headers['Content-Length'] end - def test_headers_cannot_be_written_after_write + def test_headers_cannot_be_written_after_webserver_reads @response.stream.write 'omg' + latch = ActiveSupport::Concurrency::Latch.new + t = Thread.new { + @response.stream.each do |chunk| + latch.release + end + } + + latch.await assert @response.headers.frozen? e = assert_raises(ActionDispatch::IllegalStateError) do @response.headers['Content-Length'] = "zomg" end assert_equal 'header already sent', e.message + @response.stream.close + t.join end def test_headers_cannot_be_written_after_close @response.stream.close - assert @response.headers.frozen? e = assert_raises(ActionDispatch::IllegalStateError) do @response.headers['Content-Length'] = "zomg" end diff --git a/actionpack/test/dispatch/mapper_test.rb b/actionpack/test/dispatch/mapper_test.rb index 58457b0c28..889f9a4736 100644 --- a/actionpack/test/dispatch/mapper_test.rb +++ b/actionpack/test/dispatch/mapper_test.rb @@ -3,7 +3,7 @@ require 'abstract_unit' module ActionDispatch module Routing class MapperTest < ActiveSupport::TestCase - class FakeSet + class FakeSet < ActionDispatch::Routing::RouteSet attr_reader :routes alias :set :routes @@ -38,7 +38,7 @@ module ActionDispatch def test_mapping_requirements options = { :controller => 'foo', :action => 'bar', :via => :get } - m = Mapper::Mapping.new FakeSet.new, {}, '/store/:name(*rest)', options + m = Mapper::Mapping.build({}, FakeSet.new, '/store/:name(*rest)', nil, options) _, _, requirements, _ = m.to_route assert_equal(/.+?/, requirements[:rest]) end @@ -72,7 +72,7 @@ module ActionDispatch mapper = Mapper.new fakeset mapper.get '/*path/foo/:bar', :to => 'pages#show' assert_equal '/*path/foo/:bar(.:format)', fakeset.conditions.first[:path_info] - assert_nil fakeset.requirements.first[:path] + assert_equal(/.+?/, fakeset.requirements.first[:path]) end def test_map_wildcard_with_multiple_wildcard @@ -80,7 +80,7 @@ module ActionDispatch mapper = Mapper.new fakeset mapper.get '/*foo/*bar', :to => 'pages#show' assert_equal '/*foo/*bar(.:format)', fakeset.conditions.first[:path_info] - assert_nil fakeset.requirements.first[:foo] + assert_equal(/.+?/, fakeset.requirements.first[:foo]) assert_equal(/.+?/, fakeset.requirements.first[:bar]) end diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 981cf2426e..3017a9c2d6 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -1,11 +1,9 @@ require 'abstract_unit' class MimeTypeTest < ActiveSupport::TestCase - Mime::Type.register "image/png", :png unless defined? Mime::PNG - Mime::Type.register "application/pdf", :pdf unless defined? Mime::PDF test "parse single" do - Mime::LOOKUP.keys.each do |mime_type| + Mime::LOOKUP.each_key do |mime_type| unless mime_type == 'image/*' assert_equal [Mime::Type.lookup(mime_type)], Mime::Type.parse(mime_type) end @@ -85,7 +83,7 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse broken acceptlines" do accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/*,,*/*;q=0.5" expect = [Mime::HTML, Mime::XML, "image/*", Mime::TEXT, Mime::ALL] - assert_equal expect, Mime::Type.parse(accept).collect { |c| c.to_s } + assert_equal expect, Mime::Type.parse(accept).collect(&:to_s) end # Accept header send with user HTTP_USER_AGENT: Mozilla/4.0 @@ -93,7 +91,7 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse other broken acceptlines" do accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, , pronto/1.00.00, sslvpn/1.00.00.00, */*" expect = ['image/gif', 'image/x-xbitmap', 'image/jpeg','image/pjpeg', 'application/x-shockwave-flash', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/msword', 'pronto/1.00.00', 'sslvpn/1.00.00.00', Mime::ALL] - assert_equal expect, Mime::Type.parse(accept).collect { |c| c.to_s } + assert_equal expect, Mime::Type.parse(accept).collect(&:to_s) end test "custom type" do @@ -126,7 +124,7 @@ class MimeTypeTest < ActiveSupport::TestCase end Mime::Type.register("text/foo", :foo) - assert_equal registered_mimes, [Mime::FOO] + assert_equal [Mime::FOO], registered_mimes ensure Mime::Type.unregister(:FOO) end diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb index 30e95a0b75..d5a4d8ee11 100644 --- a/actionpack/test/dispatch/mount_test.rb +++ b/actionpack/test/dispatch/mount_test.rb @@ -1,12 +1,18 @@ require 'abstract_unit' +require 'rails/engine' class TestRoutingMount < ActionDispatch::IntegrationTest Router = ActionDispatch::Routing::RouteSet.new - class FakeEngine + class AppWithRoutes < Rails::Engine def self.routes - Object.new + @routes ||= ActionDispatch::Routing::RouteSet.new end + end + + # Test for mounting apps that respond to routes, but aren't Rails-like apps. + class SinatraLikeApp + def self.routes; Object.new; end def self.call(env) [200, {"Content-Type" => "text/html"}, ["OK"]] @@ -21,21 +27,35 @@ class TestRoutingMount < ActionDispatch::IntegrationTest mount SprocketsApp, :at => "/sprockets" mount SprocketsApp => "/shorthand" - mount FakeEngine, :at => "/fakeengine", :as => :fake - mount FakeEngine, :at => "/getfake", :via => :get + mount SinatraLikeApp, :at => "/fakeengine", :as => :fake + mount SinatraLikeApp, :at => "/getfake", :via => :get scope "/its_a" do mount SprocketsApp, :at => "/sprocket" end + + resources :users do + mount AppWithRoutes, :at => "/fakeengine", :as => :fake_mounted_at_resource + end + + mount SprocketsApp, :at => "/", :via => :get end + APP = RoutedRackApp.new Router def app - Router + APP + end + + def test_app_name_is_properly_generated_when_engine_is_mounted_in_resources + assert Router.mounted_helpers.method_defined?(:user_fake_mounted_at_resource), + "A mounted helper should be defined with a parent's prefix" + assert Router.named_routes.routes[:user_fake_mounted_at_resource], + "A named route should be defined with a parent's prefix" end - def test_trailing_slash_is_not_removed_from_path_info - get "/sprockets/omg/" - assert_equal "/sprockets -- /omg/", response.body + def test_mounting_at_root_path + get "/omg" + assert_equal " -- /omg", response.body end def test_mounting_sets_script_name diff --git a/actionpack/test/dispatch/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb index e519fff51e..f90d5499d7 100644 --- a/actionpack/test/dispatch/prefix_generation_test.rb +++ b/actionpack/test/dispatch/prefix_generation_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'rack/test' +require 'rails/engine' module TestGenerationPrefix class Post @@ -15,72 +16,56 @@ module TestGenerationPrefix ActiveModel::Name.new(klass) end + + def to_model; self; end + def persisted?; true; end end class WithMountedEngine < ActionDispatch::IntegrationTest include Rack::Test::Methods - class BlogEngine - def self.routes - @routes ||= begin - routes = ActionDispatch::Routing::RouteSet.new - routes.draw do - get "/posts/:id", :to => "inside_engine_generating#show", :as => :post - get "/posts", :to => "inside_engine_generating#index", :as => :posts - get "/url_to_application", :to => "inside_engine_generating#url_to_application" - get "/polymorphic_path_for_engine", :to => "inside_engine_generating#polymorphic_path_for_engine" - get "/conflicting_url", :to => "inside_engine_generating#conflicting" - get "/foo", :to => "never#invoked", :as => :named_helper_that_should_be_invoked_only_in_respond_to_test - - get "/relative_path_redirect", :to => redirect("foo") - get "/relative_option_redirect", :to => redirect(:path => "foo") - get "/relative_custom_redirect", :to => redirect { |params, request| "foo" } - - get "/absolute_path_redirect", :to => redirect("/foo") - get "/absolute_option_redirect", :to => redirect(:path => "/foo") - get "/absolute_custom_redirect", :to => redirect { |params, request| "/foo" } - end - - routes - end - end - - def self.call(env) - env['action_dispatch.routes'] = routes - routes.call(env) + class BlogEngine < Rails::Engine + routes.draw do + get "/posts/:id", :to => "inside_engine_generating#show", :as => :post + get "/posts", :to => "inside_engine_generating#index", :as => :posts + get "/url_to_application", :to => "inside_engine_generating#url_to_application" + get "/polymorphic_path_for_engine", :to => "inside_engine_generating#polymorphic_path_for_engine" + get "/conflicting_url", :to => "inside_engine_generating#conflicting" + get "/foo", :to => "never#invoked", :as => :named_helper_that_should_be_invoked_only_in_respond_to_test + + get "/relative_path_root", :to => redirect("") + get "/relative_path_redirect", :to => redirect("foo") + get "/relative_option_root", :to => redirect(:path => "") + get "/relative_option_redirect", :to => redirect(:path => "foo") + get "/relative_custom_root", :to => redirect { |params, request| "" } + get "/relative_custom_redirect", :to => redirect { |params, request| "foo" } + + get "/absolute_path_root", :to => redirect("/") + get "/absolute_path_redirect", :to => redirect("/foo") + get "/absolute_option_root", :to => redirect(:path => "/") + get "/absolute_option_redirect", :to => redirect(:path => "/foo") + get "/absolute_custom_root", :to => redirect { |params, request| "/" } + get "/absolute_custom_redirect", :to => redirect { |params, request| "/foo" } end end - class RailsApplication - def self.routes - @routes ||= begin - routes = ActionDispatch::Routing::RouteSet.new - routes.draw do - scope "/:omg", :omg => "awesome" do - mount BlogEngine => "/blog", :as => "blog_engine" - end - get "/posts/:id", :to => "outside_engine_generating#post", :as => :post - get "/generate", :to => "outside_engine_generating#index" - get "/polymorphic_path_for_app", :to => "outside_engine_generating#polymorphic_path_for_app" - get "/polymorphic_path_for_engine", :to => "outside_engine_generating#polymorphic_path_for_engine" - get "/polymorphic_with_url_for", :to => "outside_engine_generating#polymorphic_with_url_for" - get "/conflicting_url", :to => "outside_engine_generating#conflicting" - get "/ivar_usage", :to => "outside_engine_generating#ivar_usage" - root :to => "outside_engine_generating#index" - end - - routes + class RailsApplication < Rails::Engine + routes.draw do + scope "/:omg", :omg => "awesome" do + mount BlogEngine => "/blog", :as => "blog_engine" end - end - - def self.call(env) - env['action_dispatch.routes'] = routes - routes.call(env) + get "/posts/:id", :to => "outside_engine_generating#post", :as => :post + get "/generate", :to => "outside_engine_generating#index" + get "/polymorphic_path_for_app", :to => "outside_engine_generating#polymorphic_path_for_app" + get "/polymorphic_path_for_engine", :to => "outside_engine_generating#polymorphic_path_for_engine" + get "/polymorphic_with_url_for", :to => "outside_engine_generating#polymorphic_with_url_for" + get "/conflicting_url", :to => "outside_engine_generating#conflicting" + get "/ivar_usage", :to => "outside_engine_generating#ivar_usage" + root :to => "outside_engine_generating#index" end end # force draw - RailsApplication.routes RailsApplication.routes.define_mounted_helper(:main_app) class ::InsideEngineGeneratingController < ActionController::Base @@ -152,19 +137,15 @@ module TestGenerationPrefix end def app - RailsApplication + RailsApplication.instance end - def engine_object - @engine_object ||= EngineObject.new - end - - def app_object - @app_object ||= AppObject.new - end + attr_reader :engine_object, :app_object def setup RailsApplication.routes.default_url_options = {} + @engine_object = EngineObject.new + @app_object = AppObject.new end include BlogEngine.routes.mounted_helpers @@ -190,46 +171,64 @@ module TestGenerationPrefix assert_equal "engine", last_response.body end + test "[ENGINE] relative path root uses SCRIPT_NAME from request" do + get "/awesome/blog/relative_path_root" + verify_redirect "http://example.org/awesome/blog" + end + test "[ENGINE] relative path redirect uses SCRIPT_NAME from request" do get "/awesome/blog/relative_path_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/awesome/blog/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/awesome/blog/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/awesome/blog/foo" + end + + test "[ENGINE] relative option root uses SCRIPT_NAME from request" do + get "/awesome/blog/relative_option_root" + verify_redirect "http://example.org/awesome/blog" end test "[ENGINE] relative option redirect uses SCRIPT_NAME from request" do get "/awesome/blog/relative_option_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/awesome/blog/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/awesome/blog/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/awesome/blog/foo" + end + + test "[ENGINE] relative custom root uses SCRIPT_NAME from request" do + get "/awesome/blog/relative_custom_root" + verify_redirect "http://example.org/awesome/blog" end test "[ENGINE] relative custom redirect uses SCRIPT_NAME from request" do get "/awesome/blog/relative_custom_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/awesome/blog/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/awesome/blog/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/awesome/blog/foo" + end + + test "[ENGINE] absolute path root doesn't use SCRIPT_NAME from request" do + get "/awesome/blog/absolute_path_root" + verify_redirect "http://example.org/" end test "[ENGINE] absolute path redirect doesn't use SCRIPT_NAME from request" do get "/awesome/blog/absolute_path_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" + end + + test "[ENGINE] absolute option root doesn't use SCRIPT_NAME from request" do + get "/awesome/blog/absolute_option_root" + verify_redirect "http://example.org/" end test "[ENGINE] absolute option redirect doesn't use SCRIPT_NAME from request" do get "/awesome/blog/absolute_option_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" + end + + test "[ENGINE] absolute custom root doesn't use SCRIPT_NAME from request" do + get "/awesome/blog/absolute_custom_root" + verify_redirect "http://example.org/" end test "[ENGINE] absolute custom redirect doesn't use SCRIPT_NAME from request" do get "/awesome/blog/absolute_custom_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" end # Inside Application @@ -320,6 +319,17 @@ module TestGenerationPrefix path = engine_object.polymorphic_url(Post.new, :host => "www.example.com") assert_equal "http://www.example.com/awesome/blog/posts/1", path end + + private + def verify_redirect(url, status = 301) + assert_equal status, last_response.status + assert_equal url, last_response.headers["Location"] + assert_equal expected_redirect_body(url), last_response.body + end + + def expected_redirect_body(url) + %(<html><body>You are being <a href="#{url}">redirected</a>.</body></html>) + end end class EngineMountedAtRoot < ActionDispatch::IntegrationTest @@ -332,12 +342,18 @@ module TestGenerationPrefix routes.draw do get "/posts/:id", :to => "posts#show", :as => :post - get "/relative_path_redirect", :to => redirect("foo") + get "/relative_path_root", :to => redirect("") + get "/relative_path_redirect", :to => redirect("foo") + get "/relative_option_root", :to => redirect(:path => "") get "/relative_option_redirect", :to => redirect(:path => "foo") + get "/relative_custom_root", :to => redirect { |params, request| "" } get "/relative_custom_redirect", :to => redirect { |params, request| "foo" } - get "/absolute_path_redirect", :to => redirect("/foo") + get "/absolute_path_root", :to => redirect("/") + get "/absolute_path_redirect", :to => redirect("/foo") + get "/absolute_option_root", :to => redirect(:path => "/") get "/absolute_option_redirect", :to => redirect(:path => "/foo") + get "/absolute_custom_root", :to => redirect { |params, request| "/" } get "/absolute_custom_redirect", :to => redirect { |params, request| "/foo" } end @@ -351,27 +367,12 @@ module TestGenerationPrefix end end - class RailsApplication - def self.routes - @routes ||= begin - routes = ActionDispatch::Routing::RouteSet.new - routes.draw do - mount BlogEngine => "/" - end - - routes - end - end - - def self.call(env) - env['action_dispatch.routes'] = routes - routes.call(env) + class RailsApplication < Rails::Engine + routes.draw do + mount BlogEngine => "/" end end - # force draw - RailsApplication.routes - class ::PostsController < ActionController::Base include BlogEngine.routes.url_helpers include RailsApplication.routes.mounted_helpers @@ -382,7 +383,7 @@ module TestGenerationPrefix end def app - RailsApplication + RailsApplication.instance end test "generating path inside engine" do @@ -390,46 +391,75 @@ module TestGenerationPrefix assert_equal "/posts/1", last_response.body end + test "[ENGINE] relative path root uses SCRIPT_NAME from request" do + get "/relative_path_root" + verify_redirect "http://example.org/" + end + test "[ENGINE] relative path redirect uses SCRIPT_NAME from request" do get "/relative_path_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" + end + + test "[ENGINE] relative option root uses SCRIPT_NAME from request" do + get "/relative_option_root" + verify_redirect "http://example.org/" end test "[ENGINE] relative option redirect uses SCRIPT_NAME from request" do get "/relative_option_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" + end + + test "[ENGINE] relative custom root uses SCRIPT_NAME from request" do + get "/relative_custom_root" + verify_redirect "http://example.org/" end test "[ENGINE] relative custom redirect uses SCRIPT_NAME from request" do get "/relative_custom_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" + end + + test "[ENGINE] absolute path root doesn't use SCRIPT_NAME from request" do + get "/absolute_path_root" + verify_redirect "http://example.org/" end test "[ENGINE] absolute path redirect doesn't use SCRIPT_NAME from request" do get "/absolute_path_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" + end + + test "[ENGINE] absolute option root doesn't use SCRIPT_NAME from request" do + get "/absolute_option_root" + verify_redirect "http://example.org/" end test "[ENGINE] absolute option redirect doesn't use SCRIPT_NAME from request" do get "/absolute_option_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body + verify_redirect "http://example.org/foo" + end + + test "[ENGINE] absolute custom root doesn't use SCRIPT_NAME from request" do + get "/absolute_custom_root" + verify_redirect "http://example.org/" end test "[ENGINE] absolute custom redirect doesn't use SCRIPT_NAME from request" do get "/absolute_custom_redirect" - assert_equal 301, last_response.status - assert_equal "http://example.org/foo", last_response.headers["Location"] - assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body - end + verify_redirect "http://example.org/foo" + end + + private + def verify_redirect(url, status = 301) + assert_equal status, last_response.status + assert_equal url, last_response.headers["Location"] + assert_equal expected_redirect_body(url), last_response.body + end + + def expected_redirect_body(url) + %(<html><body>You are being <a href="#{url}">redirected</a>.</body></html>) + end end end diff --git a/actionpack/test/dispatch/rack_test.rb b/actionpack/test/dispatch/rack_test.rb deleted file mode 100644 index 42067854ee..0000000000 --- a/actionpack/test/dispatch/rack_test.rb +++ /dev/null @@ -1,191 +0,0 @@ -require 'abstract_unit' - -# TODO: Merge these tests into RequestTest - -class BaseRackTest < ActiveSupport::TestCase - def setup - @env = { - "HTTP_MAX_FORWARDS" => "10", - "SERVER_NAME" => "glu.ttono.us", - "FCGI_ROLE" => "RESPONDER", - "AUTH_TYPE" => "Basic", - "HTTP_X_FORWARDED_HOST" => "glu.ttono.us", - "HTTP_ACCEPT_CHARSET" => "UTF-8", - "HTTP_ACCEPT_ENCODING" => "gzip, deflate", - "HTTP_CACHE_CONTROL" => "no-cache, max-age=0", - "HTTP_PRAGMA" => "no-cache", - "HTTP_USER_AGENT" => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", - "PATH_INFO" => "/homepage/", - "HTTP_ACCEPT_LANGUAGE" => "en", - "HTTP_NEGOTIATE" => "trans", - "HTTP_HOST" => "glu.ttono.us:8007", - "HTTP_REFERER" => "http://www.google.com/search?q=glu.ttono.us", - "HTTP_FROM" => "googlebot", - "SERVER_PROTOCOL" => "HTTP/1.1", - "REDIRECT_URI" => "/dispatch.fcgi", - "SCRIPT_NAME" => "/dispatch.fcgi", - "SERVER_ADDR" => "207.7.108.53", - "REMOTE_ADDR" => "207.7.108.53", - "REMOTE_HOST" => "google.com", - "REMOTE_IDENT" => "kevin", - "REMOTE_USER" => "kevin", - "SERVER_SOFTWARE" => "lighttpd/1.4.5", - "HTTP_COOKIE" => "_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", - "HTTP_X_FORWARDED_SERVER" => "glu.ttono.us", - "REQUEST_URI" => "/admin", - "DOCUMENT_ROOT" => "/home/kevinc/sites/typo/public", - "PATH_TRANSLATED" => "/home/kevinc/sites/typo/public/homepage/", - "SERVER_PORT" => "8007", - "QUERY_STRING" => "", - "REMOTE_PORT" => "63137", - "GATEWAY_INTERFACE" => "CGI/1.1", - "HTTP_X_FORWARDED_FOR" => "65.88.180.234", - "HTTP_ACCEPT" => "*/*", - "SCRIPT_FILENAME" => "/home/kevinc/sites/typo/public/dispatch.fcgi", - "REDIRECT_STATUS" => "200", - "REQUEST_METHOD" => "GET" - } - @request = ActionDispatch::Request.new(@env) - # some Nokia phone browsers omit the space after the semicolon separator. - # some developers have grown accustomed to using comma in cookie values. - @alt_cookie_fmt_request = ActionDispatch::Request.new(@env.merge({"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"})) - end - - private - def set_content_data(data) - @request.env['REQUEST_METHOD'] = 'POST' - @request.env['CONTENT_LENGTH'] = data.length - @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' - @request.env['rack.input'] = StringIO.new(data) - end -end - -class RackRequestTest < BaseRackTest - test "proxy request" do - assert_equal 'glu.ttono.us', @request.host_with_port - end - - test "http host" do - @env.delete "HTTP_X_FORWARDED_HOST" - @env['HTTP_HOST'] = "rubyonrails.org:8080" - assert_equal "rubyonrails.org", @request.host - assert_equal "rubyonrails.org:8080", @request.host_with_port - - @env['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org" - assert_equal "www.secondhost.org", @request.host - end - - test "http host with default port overrides server port" do - @env.delete "HTTP_X_FORWARDED_HOST" - @env['HTTP_HOST'] = "rubyonrails.org" - assert_equal "rubyonrails.org", @request.host_with_port - end - - test "host with port defaults to server name if no host headers" do - @env.delete "HTTP_X_FORWARDED_HOST" - @env.delete "HTTP_HOST" - assert_equal "glu.ttono.us:8007", @request.host_with_port - end - - test "host with port falls back to server addr if necessary" do - @env.delete "HTTP_X_FORWARDED_HOST" - @env.delete "HTTP_HOST" - @env.delete "SERVER_NAME" - assert_equal "207.7.108.53", @request.host - assert_equal 8007, @request.port - assert_equal "207.7.108.53:8007", @request.host_with_port - end - - test "host with port if http standard port is specified" do - @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:80" - assert_equal "glu.ttono.us", @request.host_with_port - end - - test "host with port if https standard port is specified" do - @env['HTTP_X_FORWARDED_PROTO'] = "https" - @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:443" - assert_equal "glu.ttono.us", @request.host_with_port - end - - test "host if ipv6 reference" do - @env.delete "HTTP_X_FORWARDED_HOST" - @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]" - assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host - end - - test "host if ipv6 reference with port" do - @env.delete "HTTP_X_FORWARDED_HOST" - @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]:8008" - assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host - end - - test "cgi environment variables" do - assert_equal "Basic", @request.auth_type - assert_equal 0, @request.content_length - assert_equal nil, @request.content_mime_type - assert_equal "CGI/1.1", @request.gateway_interface - assert_equal "*/*", @request.accept - assert_equal "UTF-8", @request.accept_charset - assert_equal "gzip, deflate", @request.accept_encoding - assert_equal "en", @request.accept_language - assert_equal "no-cache, max-age=0", @request.cache_control - assert_equal "googlebot", @request.from - assert_equal "glu.ttono.us", @request.host - assert_equal "trans", @request.negotiate - assert_equal "no-cache", @request.pragma - assert_equal "http://www.google.com/search?q=glu.ttono.us", @request.referer - assert_equal "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", @request.user_agent - assert_equal "/homepage/", @request.path_info - assert_equal "/home/kevinc/sites/typo/public/homepage/", @request.path_translated - assert_equal "", @request.query_string - assert_equal "207.7.108.53", @request.remote_addr - assert_equal "google.com", @request.remote_host - assert_equal "kevin", @request.remote_ident - assert_equal "kevin", @request.remote_user - assert_equal "GET", @request.request_method - assert_equal "/dispatch.fcgi", @request.script_name - assert_equal "glu.ttono.us", @request.server_name - assert_equal 8007, @request.server_port - assert_equal "HTTP/1.1", @request.server_protocol - assert_equal "lighttpd", @request.server_software - end - - test "cookie syntax resilience" do - cookies = @request.cookies - assert_equal "c84ace84796670c052c6ceb2451fb0f2", cookies["_session_id"], cookies.inspect - assert_equal "yes", cookies["is_admin"], cookies.inspect - - alt_cookies = @alt_cookie_fmt_request.cookies - #assert_equal "c84ace847,96670c052c6ceb2451fb0f2", alt_cookies["_session_id"], alt_cookies.inspect - assert_equal "yes", alt_cookies["is_admin"], alt_cookies.inspect - end -end - -class RackRequestParamsParsingTest < BaseRackTest - test "doesnt break when content type has charset" do - set_content_data 'flamenco=love' - - assert_equal({"flamenco"=> "love"}, @request.request_parameters) - end - - test "doesnt interpret request uri as query string when missing" do - @request.env['REQUEST_URI'] = 'foo' - assert_equal({}, @request.query_parameters) - end -end - -class RackRequestNeedsRewoundTest < BaseRackTest - test "body should be rewound" do - data = 'foo' - @env['rack.input'] = StringIO.new(data) - @env['CONTENT_LENGTH'] = data.length - @env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' - - # Read the request body by parsing params. - request = ActionDispatch::Request.new(@env) - request.request_parameters - - # Should have rewound the body. - assert_equal 0, request.body.pos - end -end diff --git a/actionpack/test/dispatch/reloader_test.rb b/actionpack/test/dispatch/reloader_test.rb index ce9ccfcee8..62e8197e20 100644 --- a/actionpack/test/dispatch/reloader_test.rb +++ b/actionpack/test/dispatch/reloader_test.rb @@ -3,6 +3,11 @@ require 'abstract_unit' class ReloaderTest < ActiveSupport::TestCase Reloader = ActionDispatch::Reloader + teardown do + Reloader.reset_callbacks :prepare + Reloader.reset_callbacks :cleanup + end + def test_prepare_callbacks a = b = c = nil Reloader.to_prepare { |*args| a = b = c = 1 } diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb index dba9ab688f..b765a13fa1 100644 --- a/actionpack/test/dispatch/request/json_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb @@ -23,6 +23,13 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest ) end + test "parses boolean and number json params for application json" do + assert_parses( + {"item" => {"enabled" => false, "count" => 10}}, + "{\"item\": {\"enabled\": false, \"count\": 10}}", { 'CONTENT_TYPE' => 'application/json' } + ) + end + test "parses json params for application jsonrequest" do assert_parses( {"person" => {"name" => "David"}}, @@ -32,7 +39,7 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest test "nils are stripped from collections" do assert_parses( - {"person" => nil}, + {"person" => []}, "{\"person\":[null]}", { 'CONTENT_TYPE' => 'application/json' } ) assert_parses( @@ -40,7 +47,7 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest "{\"person\":[\"foo\",null]}", { 'CONTENT_TYPE' => 'application/json' } ) assert_parses( - {"person" => nil}, + {"person" => []}, "{\"person\":[null, null]}", { 'CONTENT_TYPE' => 'application/json' } ) end diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb index 2a2f92b5b3..926472163e 100644 --- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb @@ -8,7 +8,11 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest end def parse - self.class.last_request_parameters = request.request_parameters + self.class.last_request_parameters = begin + request.request_parameters + rescue EOFError + {} + end self.class.last_parameters = request.parameters head :ok end @@ -145,7 +149,7 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest test "does not raise EOFError on GET request with multipart content-type" do with_routing do |set| set.draw do - get ':action', to: 'multipart_params_parsing_test/test' + get ':action', controller: 'multipart_params_parsing_test/test' end headers = { "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x" } get "/parse", {}, headers @@ -174,7 +178,7 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest def with_test_routing with_routing do |set| set.draw do - post ':action', :to => 'multipart_params_parsing_test/test' + post ':action', :controller => 'multipart_params_parsing_test/test' end yield end diff --git a/actionpack/test/dispatch/request/query_string_parsing_test.rb b/actionpack/test/dispatch/request/query_string_parsing_test.rb index 0ad0dbc958..50daafbb54 100644 --- a/actionpack/test/dispatch/request/query_string_parsing_test.rb +++ b/actionpack/test/dispatch/request/query_string_parsing_test.rb @@ -95,8 +95,8 @@ class QueryStringParsingTest < ActionDispatch::IntegrationTest assert_parses({"action" => nil}, "action") assert_parses({"action" => {"foo" => nil}}, "action[foo]") assert_parses({"action" => {"foo" => { "bar" => nil }}}, "action[foo][bar]") - assert_parses({"action" => {"foo" => { "bar" => nil }}}, "action[foo][bar][]") - assert_parses({"action" => {"foo" => nil }}, "action[foo][]") + assert_parses({"action" => {"foo" => { "bar" => [] }}}, "action[foo][bar][]") + assert_parses({"action" => {"foo" => [] }}, "action[foo][]") assert_parses({"action"=>{"foo"=>[{"bar"=>nil}]}}, "action[foo][][bar]") end @@ -104,6 +104,22 @@ class QueryStringParsingTest < ActionDispatch::IntegrationTest assert_parses({"action" => ['1']}, "action[]=1&action[]") end + test "perform_deep_munge" do + old_perform_deep_munge = ActionDispatch::Request::Utils.perform_deep_munge + ActionDispatch::Request::Utils.perform_deep_munge = false + begin + assert_parses({"action" => nil}, "action") + assert_parses({"action" => {"foo" => nil}}, "action[foo]") + assert_parses({"action" => {"foo" => {"bar" => nil}}}, "action[foo][bar]") + assert_parses({"action" => {"foo" => {"bar" => [nil]}}}, "action[foo][bar][]") + assert_parses({"action" => {"foo" => [nil]}}, "action[foo][]") + assert_parses({"action" => {"foo" => [{"bar" => nil}]}}, "action[foo][][bar]") + assert_parses({"action" => ['1',nil]}, "action[]=1&action[]") + ensure + ActionDispatch::Request::Utils.perform_deep_munge = old_perform_deep_munge + end + end + test "query string with empty key" do assert_parses( { "action" => "create_customer", "full_name" => "David Heinemeier Hansson" }, diff --git a/actionpack/test/dispatch/request/session_test.rb b/actionpack/test/dispatch/request/session_test.rb index a244d1364c..10fb04e230 100644 --- a/actionpack/test/dispatch/request/session_test.rb +++ b/actionpack/test/dispatch/request/session_test.rb @@ -36,29 +36,55 @@ module ActionDispatch assert_equal s, Session.find(env) end + def test_destroy + s = Session.create(store, {}, {}) + s['rails'] = 'ftw' + + s.destroy + + assert_empty s + end + def test_keys - env = {} - s = Session.create(store, env, {}) + s = Session.create(store, {}, {}) s['rails'] = 'ftw' s['adequate'] = 'awesome' assert_equal %w[rails adequate], s.keys end def test_values - env = {} - s = Session.create(store, env, {}) + s = Session.create(store, {}, {}) s['rails'] = 'ftw' s['adequate'] = 'awesome' assert_equal %w[ftw awesome], s.values end def test_clear - env = {} - s = Session.create(store, env, {}) + s = Session.create(store, {}, {}) s['rails'] = 'ftw' s['adequate'] = 'awesome' + s.clear - assert_equal([], s.values) + assert_empty(s.values) + end + + def test_update + s = Session.create(store, {}, {}) + s['rails'] = 'ftw' + + s.update(:rails => 'awesome') + + assert_equal(['rails'], s.keys) + assert_equal('awesome', s['rails']) + end + + def test_delete + s = Session.create(store, {}, {}) + s['rails'] = 'ftw' + + s.delete('rails') + + assert_empty(s.keys) end def test_fetch @@ -68,13 +94,12 @@ module ActionDispatch assert_equal '1', session.fetch(:one) assert_equal '2', session.fetch(:two, '2') - assert_equal '2', session.fetch(:two) + assert_nil session.fetch(:two, nil) assert_equal 'three', session.fetch(:three) {|el| el.to_s } - assert_equal 'three', session.fetch(:three) assert_raise KeyError do - session.fetch(:four) + session.fetch(:three) end end @@ -83,6 +108,7 @@ module ActionDispatch Class.new { def load_session(env); [1, {}]; end def session_exists?(env); true; end + def destroy_session(env, id, options); 123; end }.new end end diff --git a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb index 9a77454f30..1de05cbf09 100644 --- a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb @@ -130,10 +130,7 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest end test "ambiguous params returns a bad request" do - with_routing do |set| - set.draw do - post ':action', to: ::UrlEncodedParamsParsingTest::TestController - end + with_test_routing do post "/parse", "foo[]=bar&foo[4]=bar" assert_response :bad_request end diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index b308c5749a..ee8e915610 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -1,12 +1,34 @@ require 'abstract_unit' -class RequestTest < ActiveSupport::TestCase +class BaseRequestTest < ActiveSupport::TestCase + def setup + @env = { + :ip_spoofing_check => true, + :tld_length => 1, + "rack.input" => "foo" + } + end def url_for(options = {}) options = { host: 'www.example.com' }.merge!(options) ActionDispatch::Http::URL.url_for(options) end + protected + def stub_request(env = {}) + ip_spoofing_check = env.key?(:ip_spoofing_check) ? env.delete(:ip_spoofing_check) : true + @trusted_proxies ||= nil + ip_app = ActionDispatch::RemoteIp.new(Proc.new { }, ip_spoofing_check, @trusted_proxies) + tld_length = env.key?(:tld_length) ? env.delete(:tld_length) : 1 + ip_app.call(env) + ActionDispatch::Http::URL.tld_length = tld_length + + env = @env.merge(env) + ActionDispatch::Request.new(env) + end +end + +class RequestUrlFor < BaseRequestTest test "url_for class method" do e = assert_raise(ArgumentError) { url_for(:host => nil) } assert_match(/Please provide the :host parameter/, e.message) @@ -31,7 +53,9 @@ class RequestTest < ActiveSupport::TestCase assert_equal 'http://www.example.com?params=', url_for(:params => '') assert_equal 'http://www.example.com?params=1', url_for(:params => 1) end +end +class RequestIP < BaseRequestTest test "remote ip" do request = stub_request 'REMOTE_ADDR' => '1.2.3.4' assert_equal '1.2.3.4', request.remote_ip @@ -128,9 +152,12 @@ class RequestTest < ActiveSupport::TestCase request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,::1' assert_equal nil, request.remote_ip - request = stub_request 'HTTP_X_FORWARDED_FOR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334, fe80:0000:0000:0000:0202:b3ff:fe1e:8329, ::1, fc00::' + request = stub_request 'HTTP_X_FORWARDED_FOR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334, fe80:0000:0000:0000:0202:b3ff:fe1e:8329, ::1, fc00::, fc01::, fdff' assert_equal 'fe80:0000:0000:0000:0202:b3ff:fe1e:8329', request.remote_ip + request = stub_request 'HTTP_X_FORWARDED_FOR' => 'FE00::, FDFF::' + assert_equal 'FE00::', request.remote_ip + request = stub_request 'HTTP_X_FORWARDED_FOR' => 'not_ip_address' assert_equal nil, request.remote_ip end @@ -220,10 +247,12 @@ class RequestTest < ActiveSupport::TestCase end test "remote ip middleware not present still returns an IP" do - request = ActionDispatch::Request.new({'REMOTE_ADDR' => '127.0.0.1'}) + request = stub_request('REMOTE_ADDR' => '127.0.0.1') assert_equal '127.0.0.1', request.remote_ip end +end +class RequestDomain < BaseRequestTest test "domains" do request = stub_request 'HTTP_HOST' => 'www.rubyonrails.org' assert_equal "rubyonrails.org", request.domain @@ -281,7 +310,9 @@ class RequestTest < ActiveSupport::TestCase assert_equal [], request.subdomains assert_equal "", request.subdomain end +end +class RequestPort < BaseRequestTest test "standard_port" do request = stub_request assert_equal 80, request.standard_port @@ -323,7 +354,9 @@ class RequestTest < ActiveSupport::TestCase request = stub_request 'HTTP_HOST' => 'www.example.org:8080' assert_equal ':8080', request.port_string end +end +class RequestPath < BaseRequestTest test "full path" do request = stub_request 'SCRIPT_NAME' => '', 'PATH_INFO' => '/path/of/some/uri', 'QUERY_STRING' => 'mapped=1' assert_equal "/path/of/some/uri?mapped=1", request.fullpath @@ -354,6 +387,32 @@ class RequestTest < ActiveSupport::TestCase assert_equal "/of/some/uri", request.path_info end + test "original_fullpath returns ORIGINAL_FULLPATH" do + request = stub_request('ORIGINAL_FULLPATH' => "/foo?bar") + + path = request.original_fullpath + assert_equal "/foo?bar", path + end + + test "original_url returns url built using ORIGINAL_FULLPATH" do + request = stub_request('ORIGINAL_FULLPATH' => "/foo?bar", + 'HTTP_HOST' => "example.org", + 'rack.url_scheme' => "http") + + url = request.original_url + assert_equal "http://example.org/foo?bar", url + end + + test "original_fullpath returns fullpath if ORIGINAL_FULLPATH is not present" do + request = stub_request('PATH_INFO' => "/foo", + 'QUERY_STRING' => "bar") + + path = request.original_fullpath + assert_equal "/foo?bar", path + end +end + +class RequestHost < BaseRequestTest test "host with default port" do request = stub_request 'HTTP_HOST' => 'rubyonrails.org:80' assert_equal "rubyonrails.org", request.host_with_port @@ -364,15 +423,181 @@ class RequestTest < ActiveSupport::TestCase assert_equal "rubyonrails.org:81", request.host_with_port end - test "server software" do - request = stub_request - assert_equal nil, request.server_software + test "proxy request" do + request = stub_request 'HTTP_HOST' => 'glu.ttono.us:80' + assert_equal "glu.ttono.us", request.host_with_port + end + + test "http host" do + request = stub_request 'HTTP_HOST' => "rubyonrails.org:8080" + assert_equal "rubyonrails.org", request.host + assert_equal "rubyonrails.org:8080", request.host_with_port - request = stub_request 'SERVER_SOFTWARE' => 'Apache3.422' - assert_equal 'apache', request.server_software + request = stub_request 'HTTP_X_FORWARDED_HOST' => "www.firsthost.org, www.secondhost.org" + assert_equal "www.secondhost.org", request.host + end - request = stub_request 'SERVER_SOFTWARE' => 'lighttpd(1.1.4)' - assert_equal 'lighttpd', request.server_software + test "http host with default port overrides server port" do + request = stub_request 'HTTP_HOST' => "rubyonrails.org" + assert_equal "rubyonrails.org", request.host_with_port + end + + test "host with port if http standard port is specified" do + request = stub_request 'HTTP_X_FORWARDED_HOST' => "glu.ttono.us:80" + assert_equal "glu.ttono.us", request.host_with_port + end + + test "host with port if https standard port is specified" do + request = stub_request( + 'HTTP_X_FORWARDED_PROTO' => "https", + 'HTTP_X_FORWARDED_HOST' => "glu.ttono.us:443" + ) + assert_equal "glu.ttono.us", request.host_with_port + end + + test "host if ipv6 reference" do + request = stub_request 'HTTP_HOST' => "[2001:1234:5678:9abc:def0::dead:beef]" + assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", request.host + end + + test "host if ipv6 reference with port" do + request = stub_request 'HTTP_HOST' => "[2001:1234:5678:9abc:def0::dead:beef]:8008" + assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", request.host + end +end + +class RequestCGI < BaseRequestTest + test "CGI environment variables" do + request = stub_request( + "AUTH_TYPE" => "Basic", + "GATEWAY_INTERFACE" => "CGI/1.1", + "HTTP_ACCEPT" => "*/*", + "HTTP_ACCEPT_CHARSET" => "UTF-8", + "HTTP_ACCEPT_ENCODING" => "gzip, deflate", + "HTTP_ACCEPT_LANGUAGE" => "en", + "HTTP_CACHE_CONTROL" => "no-cache, max-age=0", + "HTTP_FROM" => "googlebot", + "HTTP_HOST" => "glu.ttono.us:8007", + "HTTP_NEGOTIATE" => "trans", + "HTTP_PRAGMA" => "no-cache", + "HTTP_REFERER" => "http://www.google.com/search?q=glu.ttono.us", + "HTTP_USER_AGENT" => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", + "PATH_INFO" => "/homepage/", + "PATH_TRANSLATED" => "/home/kevinc/sites/typo/public/homepage/", + "QUERY_STRING" => "", + "REMOTE_ADDR" => "207.7.108.53", + "REMOTE_HOST" => "google.com", + "REMOTE_IDENT" => "kevin", + "REMOTE_USER" => "kevin", + "REQUEST_METHOD" => "GET", + "SCRIPT_NAME" => "/dispatch.fcgi", + "SERVER_NAME" => "glu.ttono.us", + "SERVER_PORT" => "8007", + "SERVER_PROTOCOL" => "HTTP/1.1", + "SERVER_SOFTWARE" => "lighttpd/1.4.5", + ) + + assert_equal "Basic", request.auth_type + assert_equal 0, request.content_length + assert_equal nil, request.content_mime_type + assert_equal "CGI/1.1", request.gateway_interface + assert_equal "*/*", request.accept + assert_equal "UTF-8", request.accept_charset + assert_equal "gzip, deflate", request.accept_encoding + assert_equal "en", request.accept_language + assert_equal "no-cache, max-age=0", request.cache_control + assert_equal "googlebot", request.from + assert_equal "glu.ttono.us", request.host + assert_equal "trans", request.negotiate + assert_equal "no-cache", request.pragma + assert_equal "http://www.google.com/search?q=glu.ttono.us", request.referer + assert_equal "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", request.user_agent + assert_equal "/homepage/", request.path_info + assert_equal "/home/kevinc/sites/typo/public/homepage/", request.path_translated + assert_equal "", request.query_string + assert_equal "207.7.108.53", request.remote_addr + assert_equal "google.com", request.remote_host + assert_equal "kevin", request.remote_ident + assert_equal "kevin", request.remote_user + assert_equal "GET", request.request_method + assert_equal "/dispatch.fcgi", request.script_name + assert_equal "glu.ttono.us", request.server_name + assert_equal 8007, request.server_port + assert_equal "HTTP/1.1", request.server_protocol + assert_equal "lighttpd", request.server_software + end +end + +class LocalhostTest < BaseRequestTest + test "IPs that match localhost" do + request = stub_request("REMOTE_IP" => "127.1.1.1", "REMOTE_ADDR" => "127.1.1.1") + assert request.local? + end +end + +class RequestCookie < BaseRequestTest + test "cookie syntax resilience" do + request = stub_request("HTTP_COOKIE" => "_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes") + assert_equal "c84ace84796670c052c6ceb2451fb0f2", request.cookies["_session_id"], request.cookies.inspect + assert_equal "yes", request.cookies["is_admin"], request.cookies.inspect + + # some Nokia phone browsers omit the space after the semicolon separator. + # some developers have grown accustomed to using comma in cookie values. + request = stub_request("HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes") + assert_equal "c84ace847", request.cookies["_session_id"], request.cookies.inspect + assert_equal "yes", request.cookies["is_admin"], request.cookies.inspect + end +end + +class RequestParamsParsing < BaseRequestTest + test "doesnt break when content type has charset" do + request = stub_request( + 'REQUEST_METHOD' => 'POST', + 'CONTENT_LENGTH' => "flamenco=love".length, + 'CONTENT_TYPE' => 'application/x-www-form-urlencoded; charset=utf-8', + 'rack.input' => StringIO.new("flamenco=love") + ) + + assert_equal({"flamenco"=> "love"}, request.request_parameters) + end + + test "doesnt interpret request uri as query string when missing" do + request = stub_request('REQUEST_URI' => 'foo') + assert_equal({}, request.query_parameters) + end +end + +class RequestRewind < BaseRequestTest + test "body should be rewound" do + data = 'rewind' + env = { + 'rack.input' => StringIO.new(data), + 'CONTENT_LENGTH' => data.length, + 'CONTENT_TYPE' => 'application/x-www-form-urlencoded; charset=utf-8' + } + + # Read the request body by parsing params. + request = stub_request(env) + request.request_parameters + + # Should have rewound the body. + assert_equal 0, request.body.pos + end + + test "raw_post rewinds rack.input if RAW_POST_DATA is nil" do + request = stub_request( + 'rack.input' => StringIO.new("raw"), + 'CONTENT_LENGTH' => 3 + ) + assert_equal "raw", request.raw_post + assert_equal "raw", request.env['rack.input'].read + end +end + +class RequestProtocol < BaseRequestTest + test "server software" do + assert_equal 'lighttpd', stub_request('SERVER_SOFTWARE' => 'lighttpd/1.4.5').server_software + assert_equal 'apache', stub_request('SERVER_SOFTWARE' => 'Apache3.422').server_software end test "xml http request" do @@ -391,19 +616,12 @@ class RequestTest < ActiveSupport::TestCase end test "reports ssl" do - request = stub_request - assert !request.ssl? - - request = stub_request 'HTTPS' => 'on' - assert request.ssl? + assert !stub_request.ssl? + assert stub_request('HTTPS' => 'on').ssl? end test "reports ssl when proxied via lighttpd" do - request = stub_request - assert !request.ssl? - - request = stub_request 'HTTP_X_FORWARDED_PROTO' => 'https' - assert request.ssl? + assert stub_request('HTTP_X_FORWARDED_PROTO' => 'https').ssl? end test "scheme returns https when proxied" do @@ -411,63 +629,92 @@ class RequestTest < ActiveSupport::TestCase assert !request.ssl? assert_equal 'http', request.scheme - request = stub_request 'rack.url_scheme' => 'http', 'HTTP_X_FORWARDED_PROTO' => 'https' + request = stub_request( + 'rack.url_scheme' => 'http', + 'HTTP_X_FORWARDED_PROTO' => 'https' + ) assert request.ssl? assert_equal 'https', request.scheme end +end + +class RequestMethod < BaseRequestTest + test "method returns environment's request method when it has not been + overridden by middleware".squish do - test "String request methods" do - [:get, :post, :patch, :put, :delete].each do |method| - request = stub_request 'REQUEST_METHOD' => method.to_s.upcase - assert_equal method.to_s.upcase, request.method + ActionDispatch::Request::HTTP_METHODS.each do |method| + request = stub_request('REQUEST_METHOD' => method) + + assert_equal method, request.method + assert_equal method.underscore.to_sym, request.method_symbol end end - test "Symbol forms of request methods via method_symbol" do - [:get, :post, :patch, :put, :delete].each do |method| - request = stub_request 'REQUEST_METHOD' => method.to_s.upcase - assert_equal method, request.method_symbol - end + test "allow request method hacking" do + request = stub_request('REQUEST_METHOD' => 'POST') + + assert_equal 'POST', request.request_method + assert_equal 'POST', request.env["REQUEST_METHOD"] + + request.request_method = 'GET' + + assert_equal 'GET', request.request_method + assert_equal 'GET', request.env["REQUEST_METHOD"] end test "invalid http method raises exception" do assert_raise(ActionController::UnknownHttpMethod) do - request = stub_request 'REQUEST_METHOD' => 'RANDOM_METHOD' - request.request_method + stub_request('REQUEST_METHOD' => 'RANDOM_METHOD').request_method end end - test "allow method hacking on post" do - %w(GET OPTIONS PATCH PUT POST DELETE).each do |method| - request = stub_request "REQUEST_METHOD" => method.to_s.upcase - assert_equal(method == "HEAD" ? "GET" : method, request.method) - end + test "method returns original value of environment request method on POST" do + request = stub_request('rack.methodoverride.original_method' => 'POST') + assert_equal 'POST', request.method end - test "invalid method hacking on post raises exception" do + test "method raises exception on invalid HTTP method" do assert_raise(ActionController::UnknownHttpMethod) do - request = stub_request "REQUEST_METHOD" => "_RANDOM_METHOD" - request.request_method + stub_request('rack.methodoverride.original_method' => '_RANDOM_METHOD').method + end + + assert_raise(ActionController::UnknownHttpMethod) do + stub_request('REQUEST_METHOD' => '_RANDOM_METHOD').method end end - test "restrict method hacking" do - [:get, :patch, :put, :delete].each do |method| - request = stub_request 'REQUEST_METHOD' => method.to_s.upcase, - 'action_dispatch.request.request_parameters' => { :_method => 'put' } - assert_equal method.to_s.upcase, request.method + test "exception on invalid HTTP method unaffected by I18n settings" do + old_locales = I18n.available_locales + old_enforce = I18n.config.enforce_available_locales + + begin + I18n.available_locales = [:nl] + I18n.config.enforce_available_locales = true + assert_raise(ActionController::UnknownHttpMethod) do + stub_request('REQUEST_METHOD' => '_RANDOM_METHOD').method + end + ensure + I18n.available_locales = old_locales + I18n.config.enforce_available_locales = old_enforce end end test "post masquerading as patch" do - request = stub_request 'REQUEST_METHOD' => 'PATCH', "rack.methodoverride.original_method" => "POST" + request = stub_request( + 'REQUEST_METHOD' => 'PATCH', + "rack.methodoverride.original_method" => "POST" + ) + assert_equal "POST", request.method assert_equal "PATCH", request.request_method assert request.patch? end test "post masquerading as put" do - request = stub_request 'REQUEST_METHOD' => 'PUT', "rack.methodoverride.original_method" => "POST" + request = stub_request( + 'REQUEST_METHOD' => 'PUT', + "rack.methodoverride.original_method" => "POST" + ) assert_equal "POST", request.method assert_equal "PUT", request.request_method assert request.put? @@ -493,7 +740,9 @@ class RequestTest < ActiveSupport::TestCase end end end +end +class RequestFormat < BaseRequestTest test "xml format" do request = stub_request request.expects(:parameters).at_least_once.returns({ :format => 'xml' }) @@ -513,109 +762,55 @@ class RequestTest < ActiveSupport::TestCase end test "XMLHttpRequest" do - request = stub_request 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest', - 'HTTP_ACCEPT' => - [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(",") + request = stub_request( + 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest', + 'HTTP_ACCEPT' => [Mime::JS, Mime::HTML, Mime::XML, "text/xml", Mime::ALL].join(",") + ) request.expects(:parameters).at_least_once.returns({}) assert request.xhr? assert_equal Mime::JS, request.format end - test "content type" do - request = stub_request 'CONTENT_TYPE' => 'text/html' - assert_equal Mime::HTML, request.content_mime_type - end - - test "can override format with parameter" do + test "can override format with parameter negative" do request = stub_request request.expects(:parameters).at_least_once.returns({ :format => :txt }) assert !request.format.xml? + end + test "can override format with parameter positive" do request = stub_request request.expects(:parameters).at_least_once.returns({ :format => :xml }) assert request.format.xml? end - test "no content type" do - request = stub_request - assert_equal nil, request.content_mime_type - end - - test "content type is XML" do - request = stub_request 'CONTENT_TYPE' => 'application/xml' - assert_equal Mime::XML, request.content_mime_type - end - - test "content type with charset" do - request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8' - assert_equal Mime::XML, request.content_mime_type - end - - test "user agent" do - request = stub_request 'HTTP_USER_AGENT' => 'TestAgent' - assert_equal 'TestAgent', request.user_agent - end - - test "parameters" do - request = stub_request - request.stubs(:request_parameters).returns({ "foo" => 1 }) - request.stubs(:query_parameters).returns({ "bar" => 2 }) - - assert_equal({"foo" => 1, "bar" => 2}, request.parameters) - assert_equal({"foo" => 1}, request.request_parameters) - assert_equal({"bar" => 2}, request.query_parameters) - end - - test "parameters still accessible after rack parse error" do - mock_rack_env = { "QUERY_STRING" => "x[y]=1&x[y][][w]=2", "rack.input" => "foo" } - request = nil - request = stub_request(mock_rack_env) - - assert_raises(ActionController::BadRequest) do - # rack will raise a TypeError when parsing this query string - request.parameters - end - - assert_equal({}, request.parameters) - end - - test "we have access to the original exception" do - mock_rack_env = { "QUERY_STRING" => "x[y]=1&x[y][][w]=2", "rack.input" => "foo" } - request = nil - request = stub_request(mock_rack_env) - - e = assert_raises(ActionController::BadRequest) do - # rack will raise a TypeError when parsing this query string - request.parameters - end - - assert e.original_exception - assert_equal e.original_exception.backtrace, e.backtrace - end - - test "formats with accept header" do + test "formats text/html with accept header" do request = stub_request 'HTTP_ACCEPT' => 'text/html' - request.expects(:parameters).at_least_once.returns({}) assert_equal [Mime::HTML], request.formats + end + test "formats blank with accept header" do request = stub_request 'HTTP_ACCEPT' => '' - request.expects(:parameters).at_least_once.returns({}) assert_equal [Mime::HTML], request.formats + end - request = stub_request 'HTTP_ACCEPT' => '', - 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" - request.expects(:parameters).at_least_once.returns({}) + test "formats XMLHttpRequest with accept header" do + request = stub_request 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" assert_equal [Mime::JS], request.formats - - request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8', - 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" - request.expects(:parameters).at_least_once.returns({}) + end + + test "formats application/xml with accept header" do + request = stub_request('CONTENT_TYPE' => 'application/xml; charset=UTF-8', + 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest") assert_equal [Mime::XML], request.formats + end + test "formats format:text with accept header" do request = stub_request request.expects(:parameters).at_least_once.returns({ :format => :txt }) assert_equal [Mime::TEXT], request.formats + end + test "formats format:unknown with accept header" do request = stub_request request.expects(:parameters).at_least_once.returns({ :format => :unknown }) assert_instance_of Mime::NullType, request.format @@ -624,10 +819,16 @@ class RequestTest < ActiveSupport::TestCase test "format is not nil with unknown format" do request = stub_request request.expects(:parameters).at_least_once.returns({ format: :hello }) - assert_equal request.format.nil?, true - assert_equal request.format.html?, false - assert_equal request.format.xml?, false - assert_equal request.format.json?, false + assert request.format.nil? + assert_not request.format.html? + assert_not request.format.xml? + assert_not request.format.json? + end + + test "format does not throw exceptions when malformed parameters" do + request = stub_request("QUERY_STRING" => "x[y]=1&x[y][][w]=2") + assert request.formats + assert request.format.html? end test "formats with xhr request" do @@ -637,6 +838,7 @@ class RequestTest < ActiveSupport::TestCase end test "ignore_accept_header" do + old_ignore_accept_header = ActionDispatch::Request.ignore_accept_header ActionDispatch::Request.ignore_accept_header = true begin @@ -666,33 +868,115 @@ class RequestTest < ActiveSupport::TestCase request.expects(:parameters).at_least_once.returns({:format => :json}) assert_equal [ Mime::JSON ], request.formats ensure - ActionDispatch::Request.ignore_accept_header = false + ActionDispatch::Request.ignore_accept_header = old_ignore_accept_header end end +end - test "negotiate_mime" do - request = stub_request 'HTTP_ACCEPT' => 'text/html', - 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" +class RequestMimeType < BaseRequestTest + test "content type" do + assert_equal Mime::HTML, stub_request('CONTENT_TYPE' => 'text/html').content_mime_type + end - request.expects(:parameters).at_least_once.returns({}) + test "no content type" do + assert_equal nil, stub_request.content_mime_type + end + + test "content type is XML" do + assert_equal Mime::XML, stub_request('CONTENT_TYPE' => 'application/xml').content_mime_type + end + + test "content type with charset" do + assert_equal Mime::XML, stub_request('CONTENT_TYPE' => 'application/xml; charset=UTF-8').content_mime_type + end + + test "user agent" do + assert_equal 'TestAgent', stub_request('HTTP_USER_AGENT' => 'TestAgent').user_agent + end + + test "negotiate_mime" do + request = stub_request( + 'HTTP_ACCEPT' => 'text/html', + 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" + ) assert_equal nil, request.negotiate_mime([Mime::XML, Mime::JSON]) assert_equal Mime::HTML, request.negotiate_mime([Mime::XML, Mime::HTML]) assert_equal Mime::HTML, request.negotiate_mime([Mime::XML, Mime::ALL]) + end + + test "negotiate_mime with content_type" do + request = stub_request( + 'CONTENT_TYPE' => 'application/xml; charset=UTF-8', + 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" + ) - request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8', - 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" - request.expects(:parameters).at_least_once.returns({}) assert_equal Mime::XML, request.negotiate_mime([Mime::XML, Mime::CSV]) end +end - test "raw_post rewinds rack.input if RAW_POST_DATA is nil" do - request = stub_request('rack.input' => StringIO.new("foo"), - 'CONTENT_LENGTH' => 3) - assert_equal "foo", request.raw_post - assert_equal "foo", request.env['rack.input'].read +class RequestParameters < BaseRequestTest + test "parameters" do + request = stub_request + request.expects(:request_parameters).at_least_once.returns({ "foo" => 1 }) + request.expects(:query_parameters).at_least_once.returns({ "bar" => 2 }) + + assert_equal({"foo" => 1, "bar" => 2}, request.parameters) + assert_equal({"foo" => 1}, request.request_parameters) + assert_equal({"bar" => 2}, request.query_parameters) + end + + test "parameters not accessible after rack parse error" do + request = stub_request("QUERY_STRING" => "x[y]=1&x[y][][w]=2") + + 2.times do + assert_raises(ActionController::BadRequest) do + # rack will raise a Rack::Utils::ParameterTypeError when parsing this query string + request.parameters + end + end end + test "parameters not accessible after rack parse error of invalid UTF8 character" do + request = stub_request("QUERY_STRING" => "foo%81E=1") + + 2.times do + assert_raises(ActionController::BadRequest) do + # rack will raise a Rack::Utils::InvalidParameterError when parsing this query string + request.parameters + end + end + end + + test "parameters not accessible after rack parse error 1" do + request = stub_request( + 'REQUEST_METHOD' => 'POST', + 'CONTENT_LENGTH' => "a%=".length, + 'CONTENT_TYPE' => 'application/x-www-form-urlencoded; charset=utf-8', + 'rack.input' => StringIO.new("a%=") + ) + + assert_raises(ActionController::BadRequest) do + # rack will raise a Rack::Utils::ParameterTypeError when parsing this query string + request.parameters + end + end + + test "we have access to the original exception" do + request = stub_request("QUERY_STRING" => "x[y]=1&x[y][][w]=2") + + e = assert_raises(ActionController::BadRequest) do + # rack will raise a Rack::Utils::ParameterTypeError when parsing this query string + request.parameters + end + + assert e.original_exception + assert_equal e.original_exception.backtrace, e.backtrace + end +end + + +class RequestParameterFilter < BaseRequestTest test "process parameter filter" do test_hashes = [ [{'foo'=>'bar'},{'foo'=>'bar'},%w'food'], @@ -713,17 +997,22 @@ class RequestTest < ActiveSupport::TestCase } parameter_filter = ActionDispatch::Http::ParameterFilter.new(filter_words) - before_filter['barg'] = {'bargain'=>'gain', 'blah'=>'bar', 'bar'=>{'bargain'=>{'blah'=>'foo'}}} - after_filter['barg'] = {'bargain'=>'niag', 'blah'=>'[FILTERED]', 'bar'=>{'bargain'=>{'blah'=>'[FILTERED]'}}} + before_filter['barg'] = {:bargain=>'gain', 'blah'=>'bar', 'bar'=>{'bargain'=>{'blah'=>'foo'}}} + after_filter['barg'] = {:bargain=>'niag', 'blah'=>'[FILTERED]', 'bar'=>{'bargain'=>{'blah'=>'[FILTERED]'}}} assert_equal after_filter, parameter_filter.filter(before_filter) end end test "filtered_parameters returns params filtered" do - request = stub_request('action_dispatch.request.parameters' => - { 'lifo' => 'Pratik', 'amount' => '420', 'step' => '1' }, - 'action_dispatch.parameter_filter' => [:lifo, :amount]) + request = stub_request( + 'action_dispatch.request.parameters' => { + 'lifo' => 'Pratik', + 'amount' => '420', + 'step' => '1' + }, + 'action_dispatch.parameter_filter' => [:lifo, :amount] + ) params = request.filtered_parameters assert_equal "[FILTERED]", params["lifo"] @@ -732,10 +1021,14 @@ class RequestTest < ActiveSupport::TestCase end test "filtered_env filters env as a whole" do - request = stub_request('action_dispatch.request.parameters' => - { 'amount' => '420', 'step' => '1' }, "RAW_POST_DATA" => "yada yada", - 'action_dispatch.parameter_filter' => [:lifo, :amount]) - + request = stub_request( + 'action_dispatch.request.parameters' => { + 'amount' => '420', + 'step' => '1' + }, + "RAW_POST_DATA" => "yada yada", + 'action_dispatch.parameter_filter' => [:lifo, :amount] + ) request = stub_request(request.filtered_env) assert_equal "[FILTERED]", request.raw_post @@ -745,9 +1038,11 @@ class RequestTest < ActiveSupport::TestCase test "filtered_path returns path with filtered query string" do %w(; &).each do |sep| - request = stub_request('QUERY_STRING' => %w(username=sikachu secret=bd4f21f api_key=b1bc3b3cd352f68d79d7).join(sep), + request = stub_request( + 'QUERY_STRING' => %w(username=sikachu secret=bd4f21f api_key=b1bc3b3cd352f68d79d7).join(sep), 'PATH_INFO' => '/authenticate', - 'action_dispatch.parameter_filter' => [:secret, :api_key]) + 'action_dispatch.parameter_filter' => [:secret, :api_key] + ) path = request.filtered_path assert_equal %w(/authenticate?username=sikachu secret=[FILTERED] api_key=[FILTERED]).join(sep), path @@ -755,56 +1050,40 @@ class RequestTest < ActiveSupport::TestCase end test "filtered_path should not unescape a genuine '[FILTERED]' value" do - request = stub_request('QUERY_STRING' => "secret=bd4f21f&genuine=%5BFILTERED%5D", + request = stub_request( + 'QUERY_STRING' => "secret=bd4f21f&genuine=%5BFILTERED%5D", 'PATH_INFO' => '/authenticate', - 'action_dispatch.parameter_filter' => [:secret]) + 'action_dispatch.parameter_filter' => [:secret] + ) path = request.filtered_path - assert_equal "/authenticate?secret=[FILTERED]&genuine=%5BFILTERED%5D", path + assert_equal request.script_name + "/authenticate?secret=[FILTERED]&genuine=%5BFILTERED%5D", path end test "filtered_path should preserve duplication of keys in query string" do - request = stub_request('QUERY_STRING' => "username=sikachu&secret=bd4f21f&username=fxn", + request = stub_request( + 'QUERY_STRING' => "username=sikachu&secret=bd4f21f&username=fxn", 'PATH_INFO' => '/authenticate', - 'action_dispatch.parameter_filter' => [:secret]) + 'action_dispatch.parameter_filter' => [:secret] + ) path = request.filtered_path - assert_equal "/authenticate?username=sikachu&secret=[FILTERED]&username=fxn", path + assert_equal request.script_name + "/authenticate?username=sikachu&secret=[FILTERED]&username=fxn", path end test "filtered_path should ignore searchparts" do - request = stub_request('QUERY_STRING' => "secret", + request = stub_request( + 'QUERY_STRING' => "secret", 'PATH_INFO' => '/authenticate', - 'action_dispatch.parameter_filter' => [:secret]) + 'action_dispatch.parameter_filter' => [:secret] + ) path = request.filtered_path - assert_equal "/authenticate?secret", path - end - - test "original_fullpath returns ORIGINAL_FULLPATH" do - request = stub_request('ORIGINAL_FULLPATH' => "/foo?bar") - - path = request.original_fullpath - assert_equal "/foo?bar", path - end - - test "original_url returns url built using ORIGINAL_FULLPATH" do - request = stub_request('ORIGINAL_FULLPATH' => "/foo?bar", - 'HTTP_HOST' => "example.org", - 'rack.url_scheme' => "http") - - url = request.original_url - assert_equal "http://example.org/foo?bar", url - end - - test "original_fullpath returns fullpath if ORIGINAL_FULLPATH is not present" do - request = stub_request('PATH_INFO' => "/foo", - 'QUERY_STRING' => "bar") - - path = request.original_fullpath - assert_equal "/foo?bar", path + assert_equal request.script_name + "/authenticate?secret", path end +end +class RequestEtag < BaseRequestTest test "if_none_match_etags none" do request = stub_request @@ -843,11 +1122,32 @@ class RequestTest < ActiveSupport::TestCase assert request.etag_matches?(etag), etag end end +end +class RequestVariant < BaseRequestTest test "setting variant" do request = stub_request + request.variant = :mobile - assert_equal :mobile, request.variant + assert_equal [:mobile], request.variant + + request.variant = [:phone, :tablet] + assert_equal [:phone, :tablet], request.variant + + assert_raise ArgumentError do + request.variant = [:phone, "tablet"] + end + + assert_raise ArgumentError do + request.variant = "yolo" + end + end + + test "reset variant" do + request = stub_request + + request.variant = nil + assert_equal nil, request.variant end test "setting variant with non symbol value" do @@ -856,16 +1156,4 @@ class RequestTest < ActiveSupport::TestCase request.variant = "mobile" end end - -protected - - def stub_request(env = {}) - ip_spoofing_check = env.key?(:ip_spoofing_check) ? env.delete(:ip_spoofing_check) : true - @trusted_proxies ||= nil - ip_app = ActionDispatch::RemoteIp.new(Proc.new { }, ip_spoofing_check, @trusted_proxies) - tld_length = env.key?(:tld_length) ? env.delete(:tld_length) : 1 - ip_app.call(env) - ActionDispatch::Http::URL.tld_length = tld_length - ActionDispatch::Request.new(env) - end end diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 4501ea095c..48342e252a 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -1,4 +1,6 @@ require 'abstract_unit' +require 'timeout' +require 'rack/content_length' class ResponseTest < ActiveSupport::TestCase def setup @@ -178,6 +180,7 @@ class ResponseTest < ActiveSupport::TestCase end test "read x_frame_options, x_content_type_options and x_xss_protection" do + original_default_headers = ActionDispatch::Response.default_headers begin ActionDispatch::Response.default_headers = { 'X-Frame-Options' => 'DENY', @@ -193,11 +196,12 @@ class ResponseTest < ActiveSupport::TestCase assert_equal('nosniff', resp.headers['X-Content-Type-Options']) assert_equal('1;', resp.headers['X-XSS-Protection']) ensure - ActionDispatch::Response.default_headers = nil + ActionDispatch::Response.default_headers = original_default_headers end end test "read custom default_header" do + original_default_headers = ActionDispatch::Response.default_headers begin ActionDispatch::Response.default_headers = { 'X-XX-XXXX' => 'Here is my phone number' @@ -209,7 +213,7 @@ class ResponseTest < ActiveSupport::TestCase assert_equal('Here is my phone number', resp.headers['X-XX-XXXX']) ensure - ActionDispatch::Response.default_headers = nil + ActionDispatch::Response.default_headers = original_default_headers end end @@ -217,6 +221,50 @@ class ResponseTest < ActiveSupport::TestCase assert_not @response.respond_to?(:method_missing) assert @response.respond_to?(:method_missing, true) end + + test "can be explicitly destructured into status, headers and an enumerable body" do + response = ActionDispatch::Response.new(404, { 'Content-Type' => 'text/plain' }, ['Not Found']) + status, headers, body = *response + + assert_equal 404, status + assert_equal({ 'Content-Type' => 'text/plain' }, headers) + assert_equal ['Not Found'], body.each.to_a + end + + test "[response].flatten does not recurse infinitely" do + Timeout.timeout(1) do # use a timeout to prevent it stalling indefinitely + status, headers, body = assert_deprecated { [@response].flatten } + assert_equal @response.status, status + assert_equal @response.headers, headers + assert_equal @response.body, body.each.to_a.join + end + end + + test "compatibility with Rack::ContentLength" do + @response.body = 'Hello' + app = lambda { |env| @response.to_a } + env = Rack::MockRequest.env_for("/") + + status, headers, body = app.call(env) + assert_nil headers['Content-Length'] + + status, headers, body = Rack::ContentLength.new(app).call(env) + assert_equal '5', headers['Content-Length'] + end + + test "implicit destructuring and Array conversion is deprecated" do + response = ActionDispatch::Response.new(404, { 'Content-Type' => 'text/plain' }, ['Not Found']) + + assert_deprecated do + status, headers, body = response + + assert_equal 404, status + assert_equal({ 'Content-Type' => 'text/plain' }, headers) + assert_equal ['Not Found'], body.each.to_a + end + + assert_deprecated { response.to_ary } + end end class ResponseIntegrationTest < ActionDispatch::IntegrationTest diff --git a/actionpack/test/dispatch/routing/concerns_test.rb b/actionpack/test/dispatch/routing/concerns_test.rb index 9f37701656..7ef513b0c8 100644 --- a/actionpack/test/dispatch/routing/concerns_test.rb +++ b/actionpack/test/dispatch/routing/concerns_test.rb @@ -36,7 +36,8 @@ class RoutingConcernsTest < ActionDispatch::IntegrationTest end include Routes.url_helpers - def app; Routes end + APP = RoutedRackApp.new Routes + def app; APP end def test_accessing_concern_from_resources get "/posts/1/comments" diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb index c8038bbd7c..3d3d4b74ae 100644 --- a/actionpack/test/dispatch/routing/inspector_test.rb +++ b/actionpack/test/dispatch/routing/inspector_test.rb @@ -2,6 +2,11 @@ require 'abstract_unit' require 'rails/engine' require 'action_dispatch/routing/inspector' +class MountedRackApp + def self.call(env) + end +end + module ActionDispatch module Routing class RoutesInspectorTest < ActiveSupport::TestCase @@ -54,6 +59,27 @@ module ActionDispatch ], output end + def test_displaying_routes_for_engines_without_routes + engine = Class.new(Rails::Engine) do + def self.inspect + "Blog::Engine" + end + end + engine.routes.draw do + end + + output = draw do + mount engine => "/blog", as: "blog" + end + + assert_equal [ + "Prefix Verb URI Pattern Controller#Action", + " blog /blog Blog::Engine", + "", + "Routes for Blog::Engine:" + ], output + end + def test_cart_inspect output = draw do get '/cart', :to => 'cart#show' @@ -160,19 +186,59 @@ module ActionDispatch ], output end - class RackApp - def self.call(env) + def test_rake_routes_shows_routes_with_dashes + output = draw do + get 'about-us' => 'pages#about_us' + get 'our-work/latest' + + resources :photos, only: [:show] do + get 'user-favorites', on: :collection + get 'preview-photo', on: :member + get 'summary-text' + end end + + assert_equal [ + " Prefix Verb URI Pattern Controller#Action", + " about_us GET /about-us(.:format) pages#about_us", + " our_work_latest GET /our-work/latest(.:format) our_work#latest", + "user_favorites_photos GET /photos/user-favorites(.:format) photos#user_favorites", + " preview_photo_photo GET /photos/:id/preview-photo(.:format) photos#preview_photo", + " photo_summary_text GET /photos/:photo_id/summary-text(.:format) photos#summary_text", + " photo GET /photos/:id(.:format) photos#show" + ], output end def test_rake_routes_shows_route_with_rack_app output = draw do - get 'foo/:id' => RackApp, :id => /[A-Z]\d{5}/ + get 'foo/:id' => MountedRackApp, :id => /[A-Z]\d{5}/ end assert_equal [ "Prefix Verb URI Pattern Controller#Action", - " GET /foo/:id(.:format) #{RackApp.name} {:id=>/[A-Z]\\d{5}/}" + " GET /foo/:id(.:format) MountedRackApp {:id=>/[A-Z]\\d{5}/}" + ], output + end + + def test_rake_routes_shows_named_route_with_mounted_rack_app + output = draw do + mount MountedRackApp => '/foo' + end + + assert_equal [ + " Prefix Verb URI Pattern Controller#Action", + "mounted_rack_app /foo MountedRackApp" + ], output + end + + def test_rake_routes_shows_overridden_named_route_with_mounted_rack_app_with_name + output = draw do + mount MountedRackApp => '/foo', as: 'blog' + end + + assert_equal [ + "Prefix Verb URI Pattern Controller#Action", + " blog /foo MountedRackApp" ], output end @@ -185,24 +251,36 @@ module ActionDispatch output = draw do scope :constraint => constraint.new do - mount RackApp => '/foo' + mount MountedRackApp => '/foo' end end assert_equal [ - "Prefix Verb URI Pattern Controller#Action", - " /foo #{RackApp.name} {:constraint=>( my custom constraint )}" + " Prefix Verb URI Pattern Controller#Action", + "mounted_rack_app /foo MountedRackApp {:constraint=>( my custom constraint )}" ], output end def test_rake_routes_dont_show_app_mounted_in_assets_prefix output = draw do - get '/sprockets' => RackApp + get '/sprockets' => MountedRackApp end - assert_no_match(/RackApp/, output.first) + assert_no_match(/MountedRackApp/, output.first) assert_no_match(/\/sprockets/, output.first) end + def test_rake_routes_shows_route_defined_in_under_assets_prefix + output = draw do + scope '/sprockets' do + get '/foo' => 'foo#bar' + end + end + assert_equal [ + "Prefix Verb URI Pattern Controller#Action", + " foo GET /sprockets/foo(.:format) foo#bar" + ], output + end + def test_redirect output = draw do get "/foo" => redirect("/foo/bar"), :constraints => { :subdomain => "admin" } diff --git a/actionpack/test/dispatch/routing/route_set_test.rb b/actionpack/test/dispatch/routing/route_set_test.rb index 0e488d2b88..8bdb5733dd 100644 --- a/actionpack/test/dispatch/routing/route_set_test.rb +++ b/actionpack/test/dispatch/routing/route_set_test.rb @@ -69,6 +69,86 @@ module ActionDispatch end end + test "only_path: true with *_url and no :host option" do + draw do + get 'foo', to: SimpleApp.new('foo#index') + end + + assert_equal '/foo', url_helpers.foo_url(only_path: true) + end + + test "only_path: false with *_url and no :host option" do + draw do + get 'foo', to: SimpleApp.new('foo#index') + end + + assert_raises ArgumentError do + assert_equal 'http://example.com/foo', url_helpers.foo_url(only_path: false) + end + end + + test "only_path: false with *_url and local :host option" do + draw do + get 'foo', to: SimpleApp.new('foo#index') + end + + assert_equal 'http://example.com/foo', url_helpers.foo_url(only_path: false, host: 'example.com') + end + + test "only_path: false with *_url and global :host option" do + @set.default_url_options = { host: 'example.com' } + + draw do + get 'foo', to: SimpleApp.new('foo#index') + end + + assert_equal 'http://example.com/foo', url_helpers.foo_url(only_path: false) + end + + test "only_path: true with *_path" do + draw do + get 'foo', to: SimpleApp.new('foo#index') + end + + assert_deprecated do + assert_equal '/foo', url_helpers.foo_path(only_path: true) + end + end + + test "only_path: false with *_path with global :host option" do + @set.default_url_options = { host: 'example.com' } + + draw do + get 'foo', to: SimpleApp.new('foo#index') + end + + assert_deprecated do + assert_equal 'http://example.com/foo', url_helpers.foo_path(only_path: false) + end + end + + test "only_path: false with *_path with local :host option" do + draw do + get 'foo', to: SimpleApp.new('foo#index') + end + + assert_deprecated do + assert_equal 'http://example.com/foo', url_helpers.foo_path(only_path: false, host: 'example.com') + end + end + + test "only_path: false with *_path with no :host option" do + draw do + get 'foo', to: SimpleApp.new('foo#index') + end + + assert_deprecated do + assert_raises ArgumentError do + assert_equal 'http://example.com/foo', url_helpers.foo_path(only_path: false) + end + end + end + test "explicit keys win over implicit keys" do draw do resources :foo do @@ -80,11 +160,39 @@ module ActionDispatch assert_equal '/foo/1/bar/2', url_helpers.foo_bar_path(2, foo_id: 1) end - private - def clear! - @set.clear! + test "having an optional scope with resources" do + draw do + scope "(/:foo)" do + resources :users + end + end + + assert_equal '/users/1', url_helpers.user_path(1) + assert_equal '/users/1', url_helpers.user_path(1, foo: nil) + assert_equal '/a/users/1', url_helpers.user_path(1, foo: 'a') + end + + test "stringified controller and action keys are properly symbolized" do + draw do + root 'foo#bar' + end + + assert_deprecated do + assert_equal '/', url_helpers.root_path('controller' => 'foo', 'action' => 'bar') + end + end + + test "mix of string and symbol keys are properly symbolized" do + draw do + root 'foo#bar' end + assert_deprecated do + assert_equal '/', url_helpers.root_path('controller' => 'foo', :action => 'bar') + end + end + + private def draw(&block) @set.draw(&block) end diff --git a/actionpack/test/dispatch/routing_assertions_test.rb b/actionpack/test/dispatch/routing_assertions_test.rb index aea4489852..56ea644f22 100644 --- a/actionpack/test/dispatch/routing_assertions_test.rb +++ b/actionpack/test/dispatch/routing_assertions_test.rb @@ -74,10 +74,26 @@ class RoutingAssertionsTest < ActionController::TestCase assert_recognizes({ :controller => 'query_articles', :action => 'index', :use_query => 'true' }, '/query/articles', { :use_query => 'true' }) end + def test_assert_recognizes_raises_message + err = assert_raise(Assertion) do + assert_recognizes({ :controller => 'secure_articles', :action => 'index' }, 'http://test.host/secure/articles', {}, "This is a really bad msg") + end + + assert_match err.message, "This is a really bad msg" + end + def test_assert_routing assert_routing('/articles', :controller => 'articles', :action => 'index') end + def test_assert_routing_raises_message + err = assert_raise(Assertion) do + assert_routing('/thisIsNotARoute', { :controller => 'articles', :action => 'edit', :id => '1' }, { :id => '1' }, {}, "This is a really bad msg") + end + + assert_match err.message, "This is a really bad msg" + end + def test_assert_routing_with_defaults assert_routing('/articles/1/edit', { :controller => 'articles', :action => 'edit', :id => '1' }, { :id => '1' }) end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index aac808afda..aae95fb355 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -14,6 +14,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + class GrumpyRestrictor + def self.matches?(request) + false + end + end + class YoutubeFavoritesRedirector def self.call(params, request) "http://www.youtube.com/watch?v=#{params[:youtube_id]}" @@ -89,6 +95,24 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest verify_redirect 'http://www.example.com/private/index' end + def test_redirect_with_failing_constraint + draw do + get 'hi', to: redirect("/foo"), constraints: ::TestRoutingMapper::GrumpyRestrictor + end + + get '/hi' + assert_equal 404, status + end + + def test_redirect_with_passing_constraint + draw do + get 'hi', to: redirect("/foo"), constraints: ->(req) { true } + end + + get '/hi' + assert_equal 301, status + end + def test_namespace_with_controller_segment assert_raise(ArgumentError) do draw do @@ -99,6 +123,16 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_namespace_without_controller_segment + draw do + namespace :admin do + get 'hello/:controllers/:action' + end + end + get '/admin/hello/foo/new' + assert_equal 'foo', @request.params["controllers"] + end + def test_session_singleton_resource draw do resource :session do @@ -351,8 +385,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest draw do controller(:global) do get 'global/hide_notice' - get 'global/export', :to => :export, :as => :export_request - get '/export/:id/:file', :to => :export, :as => :export_download, :constraints => { :file => /.*/ } + get 'global/export', :action => :export, :as => :export_request + get '/export/:id/:file', :action => :export, :as => :export_download, :constraints => { :file => /.*/ } get 'global/:action' end end @@ -720,8 +754,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest draw do resources :replies do member do - put :answer, :to => :mark_as_answer - delete :answer, :to => :unmark_as_answer + put :answer, :action => :mark_as_answer + delete :answer, :action => :unmark_as_answer end end end @@ -1031,6 +1065,136 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 'users/home#index', @response.body end + def test_namespaced_shallow_routes_with_module_option + draw do + namespace :foo, module: 'bar' do + resources :posts, only: [:index, :show] do + resources :comments, only: [:index, :show], shallow: true + end + end + end + + get '/foo/posts' + assert_equal '/foo/posts', foo_posts_path + assert_equal 'bar/posts#index', @response.body + + get '/foo/posts/1' + assert_equal '/foo/posts/1', foo_post_path('1') + assert_equal 'bar/posts#show', @response.body + + get '/foo/posts/1/comments' + assert_equal '/foo/posts/1/comments', foo_post_comments_path('1') + assert_equal 'bar/comments#index', @response.body + + get '/foo/comments/2' + assert_equal '/foo/comments/2', foo_comment_path('2') + assert_equal 'bar/comments#show', @response.body + end + + def test_namespaced_shallow_routes_with_path_option + draw do + namespace :foo, path: 'bar' do + resources :posts, only: [:index, :show] do + resources :comments, only: [:index, :show], shallow: true + end + end + end + + get '/bar/posts' + assert_equal '/bar/posts', foo_posts_path + assert_equal 'foo/posts#index', @response.body + + get '/bar/posts/1' + assert_equal '/bar/posts/1', foo_post_path('1') + assert_equal 'foo/posts#show', @response.body + + get '/bar/posts/1/comments' + assert_equal '/bar/posts/1/comments', foo_post_comments_path('1') + assert_equal 'foo/comments#index', @response.body + + get '/bar/comments/2' + assert_equal '/bar/comments/2', foo_comment_path('2') + assert_equal 'foo/comments#show', @response.body + end + + def test_namespaced_shallow_routes_with_as_option + draw do + namespace :foo, as: 'bar' do + resources :posts, only: [:index, :show] do + resources :comments, only: [:index, :show], shallow: true + end + end + end + + get '/foo/posts' + assert_equal '/foo/posts', bar_posts_path + assert_equal 'foo/posts#index', @response.body + + get '/foo/posts/1' + assert_equal '/foo/posts/1', bar_post_path('1') + assert_equal 'foo/posts#show', @response.body + + get '/foo/posts/1/comments' + assert_equal '/foo/posts/1/comments', bar_post_comments_path('1') + assert_equal 'foo/comments#index', @response.body + + get '/foo/comments/2' + assert_equal '/foo/comments/2', bar_comment_path('2') + assert_equal 'foo/comments#show', @response.body + end + + def test_namespaced_shallow_routes_with_shallow_path_option + draw do + namespace :foo, shallow_path: 'bar' do + resources :posts, only: [:index, :show] do + resources :comments, only: [:index, :show], shallow: true + end + end + end + + get '/foo/posts' + assert_equal '/foo/posts', foo_posts_path + assert_equal 'foo/posts#index', @response.body + + get '/foo/posts/1' + assert_equal '/foo/posts/1', foo_post_path('1') + assert_equal 'foo/posts#show', @response.body + + get '/foo/posts/1/comments' + assert_equal '/foo/posts/1/comments', foo_post_comments_path('1') + assert_equal 'foo/comments#index', @response.body + + get '/bar/comments/2' + assert_equal '/bar/comments/2', foo_comment_path('2') + assert_equal 'foo/comments#show', @response.body + end + + def test_namespaced_shallow_routes_with_shallow_prefix_option + draw do + namespace :foo, shallow_prefix: 'bar' do + resources :posts, only: [:index, :show] do + resources :comments, only: [:index, :show], shallow: true + end + end + end + + get '/foo/posts' + assert_equal '/foo/posts', foo_posts_path + assert_equal 'foo/posts#index', @response.body + + get '/foo/posts/1' + assert_equal '/foo/posts/1', foo_post_path('1') + assert_equal 'foo/posts#show', @response.body + + get '/foo/posts/1/comments' + assert_equal '/foo/posts/1/comments', foo_post_comments_path('1') + assert_equal 'foo/comments#index', @response.body + + get '/foo/comments/2' + assert_equal '/foo/comments/2', bar_comment_path('2') + assert_equal 'foo/comments#show', @response.body + end + def test_namespace_containing_numbers draw do namespace :v2 do @@ -1048,7 +1212,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest controller :articles do scope '/articles', :as => 'article' do scope :path => '/:title', :title => /[a-z]+/, :as => :with_title do - get '/:id', :to => :with_id, :as => "" + get '/:id', :action => :with_id, :as => "" end end end @@ -1295,7 +1459,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest def test_scoped_controller_with_namespace_and_action draw do namespace :account do - get ':action/callback', :action => /twitter|github/, :to => "callbacks", :as => :callback + get ':action/callback', :action => /twitter|github/, :controller => "callbacks", :as => :callback end end @@ -1352,7 +1516,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest def test_normalize_namespaced_matches draw do namespace :account do - get 'description', :to => :description, :as => "description" + get 'description', :action => :description, :as => "description" end end @@ -1593,7 +1757,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get "whatever/:controller(/:action(/:id))" end - get 'whatever/foo/bar' + get '/whatever/foo/bar' assert_equal 'foo#bar', @response.body assert_equal 'http://www.example.com/whatever/foo/bar/1', @@ -1605,10 +1769,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get "whatever/:controller(/:action(/:id))", :id => /\d+/ end - get 'whatever/foo/bar/show' + get '/whatever/foo/bar/show' assert_equal 'foo/bar#show', @response.body - get 'whatever/foo/bar/show/1' + get '/whatever/foo/bar/show/1' assert_equal 'foo/bar#show', @response.body assert_equal 'http://www.example.com/whatever/foo/bar/show', @@ -1828,6 +1992,82 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal '/comments/3/preview', preview_comment_path(:id => '3') end + def test_shallow_nested_resources_inside_resource + draw do + resource :membership, shallow: true do + resources :cards + end + end + + get '/membership/cards' + assert_equal 'cards#index', @response.body + assert_equal '/membership/cards', membership_cards_path + + get '/membership/cards/new' + assert_equal 'cards#new', @response.body + assert_equal '/membership/cards/new', new_membership_card_path + + post '/membership/cards' + assert_equal 'cards#create', @response.body + + get '/cards/1' + assert_equal 'cards#show', @response.body + assert_equal '/cards/1', card_path('1') + + get '/cards/1/edit' + assert_equal 'cards#edit', @response.body + assert_equal '/cards/1/edit', edit_card_path('1') + + put '/cards/1' + assert_equal 'cards#update', @response.body + + patch '/cards/1' + assert_equal 'cards#update', @response.body + + delete '/cards/1' + assert_equal 'cards#destroy', @response.body + end + + def test_shallow_deeply_nested_resources + draw do + resources :blogs do + resources :posts do + resources :comments, shallow: true + end + end + end + + get '/comments/1' + assert_equal 'comments#show', @response.body + + assert_equal '/comments/1', comment_path('1') + assert_equal '/blogs/new', new_blog_path + assert_equal '/blogs/1/posts/new', new_blog_post_path(:blog_id => 1) + assert_equal '/blogs/1/posts/2/comments/new', new_blog_post_comment_path(:blog_id => 1, :post_id => 2) + end + + def test_direct_children_of_shallow_resources + draw do + resources :blogs do + resources :posts, shallow: true do + resources :comments + end + end + end + + post '/posts/1/comments' + assert_equal 'comments#create', @response.body + assert_equal '/posts/1/comments', post_comments_path('1') + + get '/posts/2/comments/new' + assert_equal 'comments#new', @response.body + assert_equal '/posts/2/comments/new', new_post_comment_path('2') + + get '/posts/1/comments' + assert_equal 'comments#index', @response.body + assert_equal '/posts/1/comments', post_comments_path('1') + end + def test_shallow_nested_resources_within_scope draw do scope '/hello' do @@ -1889,6 +2129,65 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 'notes#destroy', @response.body end + def test_shallow_option_nested_resources_within_scope + draw do + scope '/hello' do + resources :notes, :shallow => true do + resources :trackbacks + end + end + end + + get '/hello/notes/1/trackbacks' + assert_equal 'trackbacks#index', @response.body + assert_equal '/hello/notes/1/trackbacks', note_trackbacks_path(:note_id => 1) + + get '/hello/notes/1/edit' + assert_equal 'notes#edit', @response.body + assert_equal '/hello/notes/1/edit', edit_note_path(:id => '1') + + get '/hello/notes/1/trackbacks/new' + assert_equal 'trackbacks#new', @response.body + assert_equal '/hello/notes/1/trackbacks/new', new_note_trackback_path(:note_id => 1) + + get '/hello/trackbacks/1' + assert_equal 'trackbacks#show', @response.body + assert_equal '/hello/trackbacks/1', trackback_path(:id => '1') + + get '/hello/trackbacks/1/edit' + assert_equal 'trackbacks#edit', @response.body + assert_equal '/hello/trackbacks/1/edit', edit_trackback_path(:id => '1') + + put '/hello/trackbacks/1' + assert_equal 'trackbacks#update', @response.body + + post '/hello/notes/1/trackbacks' + assert_equal 'trackbacks#create', @response.body + + delete '/hello/trackbacks/1' + assert_equal 'trackbacks#destroy', @response.body + + get '/hello/notes' + assert_equal 'notes#index', @response.body + + post '/hello/notes' + assert_equal 'notes#create', @response.body + + get '/hello/notes/new' + assert_equal 'notes#new', @response.body + assert_equal '/hello/notes/new', new_note_path + + get '/hello/notes/1' + assert_equal 'notes#show', @response.body + assert_equal '/hello/notes/1', note_path(:id => 1) + + put '/hello/notes/1' + assert_equal 'notes#update', @response.body + + delete '/hello/notes/1' + assert_equal 'notes#destroy', @response.body + end + def test_custom_resource_routes_are_scoped draw do resources :customers do @@ -1901,7 +2200,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end resources :invoices do get "outstanding" => "invoices#outstanding", :on => :collection - get "overdue", :to => :overdue, :on => :collection + get "overdue", :action => :overdue, :on => :collection get "print" => "invoices#print", :as => :print, :on => :member post "preview" => "invoices#preview", :as => :preview, :on => :new end @@ -1989,6 +2288,22 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal '/api/1.0/users/first.last.xml', api_user_path(:version => '1.0', :id => 'first.last', :format => :xml) end + def test_match_without_via + assert_raises(ArgumentError) do + draw do + match '/foo/bar', :to => 'files#show' + end + end + end + + def test_match_with_empty_via + assert_raises(ArgumentError) do + draw do + match '/foo/bar', :to => 'files#show', :via => [] + end + end + end + def test_glob_parameter_accepts_regexp draw do get '/:locale/*file.:format', :to => 'files#show', :file => /path\/to\/existing\/file/ @@ -2044,12 +2359,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get "(/user/:username)/photos" => "photos#index" end - get 'user/bob/photos' + get '/user/bob/photos' assert_equal 'photos#index', @response.body assert_equal 'http://www.example.com/user/bob/photos', url_for(:controller => "photos", :action => "index", :username => "bob") - get 'photos' + get '/photos' assert_equal 'photos#index', @response.body assert_equal 'http://www.example.com/photos', url_for(:controller => "photos", :action => "index", :username => nil) @@ -2562,7 +2877,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end - def test_symbolized_path_parameters_is_not_stale + def test_path_parameters_is_not_stale draw do scope '/countries/:country', :constraints => lambda { |params, req| %w(all France).include?(params[:country]) } do get '/', :to => 'countries#index' @@ -2727,7 +3042,9 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end assert_raise(ArgumentError) do - draw { controller("/feeds") { get '/feeds/:service', :to => :show } } + assert_deprecated do + draw { controller("/feeds") { get '/feeds/:service', :to => :show } } + end end assert_raise(ArgumentError) do @@ -2864,23 +3181,305 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert !@request.params[:action].frozen? end + def test_multiple_positional_args_with_the_same_name + draw do + get '/downloads/:id/:id.tar' => 'downloads#show', as: :download, format: false + end + + expected_params = { + controller: 'downloads', + action: 'show', + id: '1' + } + + get '/downloads/1/1.tar' + assert_equal 'downloads#show', @response.body + assert_equal expected_params, @request.path_parameters + assert_equal '/downloads/1/1.tar', download_path('1') + assert_equal '/downloads/1/1.tar', download_path('1', '1') + end + + def test_absolute_controller_namespace + draw do + namespace :foo do + get '/', to: '/bar#index', as: 'root' + end + end + + get '/foo' + assert_equal 'bar#index', @response.body + assert_equal '/foo', foo_root_path + end + + def test_namespace_as_controller + draw do + namespace :foo do + get '/', to: '/bar#index', as: 'root' + end + end + + get '/foo' + assert_equal 'bar#index', @response.body + assert_equal '/foo', foo_root_path + end + + def test_trailing_slash + draw do + resources :streams + end + + get '/streams' + assert @response.ok?, 'route without trailing slash should work' + + get '/streams/' + assert @response.ok?, 'route with trailing slash should work' + + get '/streams?foobar' + assert @response.ok?, 'route without trailing slash and with QUERY_STRING should work' + + get '/streams/?foobar' + assert @response.ok?, 'route with trailing slash and with QUERY_STRING should work' + end + + def test_route_with_dashes_in_path + draw do + get '/contact-us', to: 'pages#contact_us' + end + + get '/contact-us' + assert_equal 'pages#contact_us', @response.body + assert_equal '/contact-us', contact_us_path + end + + def test_shorthand_route_with_dashes_in_path + draw do + get '/about-us/index' + end + + get '/about-us/index' + assert_equal 'about_us#index', @response.body + assert_equal '/about-us/index', about_us_index_path + end + + def test_resource_routes_with_dashes_in_path + draw do + resources :photos, only: [:show] do + get 'user-favorites', on: :collection + get 'preview-photo', on: :member + get 'summary-text' + end + end + + get '/photos/user-favorites' + assert_equal 'photos#user_favorites', @response.body + assert_equal '/photos/user-favorites', user_favorites_photos_path + + get '/photos/1/preview-photo' + assert_equal 'photos#preview_photo', @response.body + assert_equal '/photos/1/preview-photo', preview_photo_photo_path('1') + + get '/photos/1/summary-text' + assert_equal 'photos#summary_text', @response.body + assert_equal '/photos/1/summary-text', photo_summary_text_path('1') + + get '/photos/1' + assert_equal 'photos#show', @response.body + assert_equal '/photos/1', photo_path('1') + end + + def test_shallow_path_inside_namespace_is_not_added_twice + draw do + namespace :admin do + shallow do + resources :posts do + resources :comments + end + end + end + end + + get '/admin/posts/1/comments' + assert_equal 'admin/comments#index', @response.body + assert_equal '/admin/posts/1/comments', admin_post_comments_path('1') + end + + def test_mix_string_to_controller_action + draw do + get '/projects', controller: 'project_files', + action: 'index', + to: 'comments#index' + end + get '/projects' + assert_equal 'comments#index', @response.body + end + + def test_mix_string_to_controller + draw do + get '/projects', controller: 'project_files', + to: 'comments#index' + end + get '/projects' + assert_equal 'comments#index', @response.body + end + + def test_mix_string_to_action + draw do + get '/projects', action: 'index', + to: 'comments#index' + end + get '/projects' + assert_equal 'comments#index', @response.body + end + + def test_mix_symbol_to_controller_action + assert_deprecated do + draw do + get '/projects', controller: 'project_files', + action: 'index', + to: :show + end + end + get '/projects' + assert_equal 'project_files#show', @response.body + end + + def test_mix_string_to_controller_action_no_hash + assert_deprecated do + draw do + get '/projects', controller: 'project_files', + action: 'index', + to: 'show' + end + end + get '/projects' + assert_equal 'show#index', @response.body + end + + def test_shallow_path_and_prefix_are_not_added_to_non_shallow_routes + draw do + scope shallow_path: 'projects', shallow_prefix: 'project' do + resources :projects do + resources :files, controller: 'project_files', shallow: true + end + end + end + + get '/projects' + assert_equal 'projects#index', @response.body + assert_equal '/projects', projects_path + + get '/projects/new' + assert_equal 'projects#new', @response.body + assert_equal '/projects/new', new_project_path + + post '/projects' + assert_equal 'projects#create', @response.body + + get '/projects/1' + assert_equal 'projects#show', @response.body + assert_equal '/projects/1', project_path('1') + + get '/projects/1/edit' + assert_equal 'projects#edit', @response.body + assert_equal '/projects/1/edit', edit_project_path('1') + + patch '/projects/1' + assert_equal 'projects#update', @response.body + + delete '/projects/1' + assert_equal 'projects#destroy', @response.body + + get '/projects/1/files' + assert_equal 'project_files#index', @response.body + assert_equal '/projects/1/files', project_files_path('1') + + get '/projects/1/files/new' + assert_equal 'project_files#new', @response.body + assert_equal '/projects/1/files/new', new_project_file_path('1') + + post '/projects/1/files' + assert_equal 'project_files#create', @response.body + + get '/projects/files/2' + assert_equal 'project_files#show', @response.body + assert_equal '/projects/files/2', project_file_path('2') + + get '/projects/files/2/edit' + assert_equal 'project_files#edit', @response.body + assert_equal '/projects/files/2/edit', edit_project_file_path('2') + + patch '/projects/files/2' + assert_equal 'project_files#update', @response.body + + delete '/projects/files/2' + assert_equal 'project_files#destroy', @response.body + end + + def test_scope_path_is_copied_to_shallow_path + draw do + scope path: 'foo' do + resources :posts do + resources :comments, shallow: true + end + end + end + + assert_equal '/foo/comments/1', comment_path('1') + end + + def test_scope_as_is_copied_to_shallow_prefix + draw do + scope as: 'foo' do + resources :posts do + resources :comments, shallow: true + end + end + end + + assert_equal '/comments/1', foo_comment_path('1') + end + + def test_scope_shallow_prefix_is_not_overwritten_by_as + draw do + scope as: 'foo', shallow_prefix: 'bar' do + resources :posts do + resources :comments, shallow: true + end + end + end + + assert_equal '/comments/1', bar_comment_path('1') + end + + def test_scope_shallow_path_is_not_overwritten_by_path + draw do + scope path: 'foo', shallow_path: 'bar' do + resources :posts do + resources :comments, shallow: true + end + end + end + + assert_equal '/bar/comments/1', comment_path('1') + end + private def draw(&block) self.class.stub_controllers do |routes| - @app = routes - @app.default_url_options = { host: 'www.example.com' } - @app.draw(&block) + routes.default_url_options = { host: 'www.example.com' } + routes.draw(&block) + @app = RoutedRackApp.new routes end end def url_for(options = {}) - @app.url_helpers.url_for(options) + @app.routes.url_helpers.url_for(options) end def method_missing(method, *args, &block) if method.to_s =~ /_(path|url)$/ - @app.url_helpers.send(method, *args, &block) + @app.routes.url_helpers.send(method, *args, &block) else super end @@ -2907,12 +3506,14 @@ end class TestAltApp < ActionDispatch::IntegrationTest class AltRequest + attr_accessor :path_parameters, :path_info, :script_name + attr_reader :env + def initialize(env) + @path_parameters = {} @env = env - end - - def path_info - "/" + @path_info = "/" + @script_name = "" end def request_method @@ -2946,8 +3547,10 @@ class TestAltApp < ActionDispatch::IntegrationTest get "/" => TestAltApp::AltApp.new end + APP = build_app AltRoutes + def app - AltRoutes + APP end def test_alt_request_without_header @@ -2974,15 +3577,16 @@ class TestAppendingRoutes < ActionDispatch::IntegrationTest def setup super s = self - @app = ActionDispatch::Routing::RouteSet.new - @app.append do + routes = ActionDispatch::Routing::RouteSet.new + routes.append do get '/hello' => s.simple_app('fail') get '/goodbye' => s.simple_app('goodbye') end - @app.draw do + routes.draw do get '/hello' => s.simple_app('hello') end + @app = self.class.build_app routes end def test_goodbye_should_be_available @@ -3011,8 +3615,38 @@ class TestNamespaceWithControllerOption < ActionDispatch::IntegrationTest end def draw(&block) - @app = ActionDispatch::Routing::RouteSet.new - @app.draw(&block) + routes = ActionDispatch::Routing::RouteSet.new + routes.draw(&block) + @app = self.class.build_app routes + end + + def test_missing_controller + ex = assert_raises(ArgumentError) { + draw do + get '/foo/bar', :action => :index + end + } + assert_match(/Missing :controller/, ex.message) + end + + def test_missing_action + ex = assert_raises(ArgumentError) { + assert_deprecated do + draw do + get '/foo/bar', :to => 'foo' + end + end + } + assert_match(/Missing :action/, ex.message) + end + + def test_missing_action_on_hash + ex = assert_raises(ArgumentError) { + draw do + get '/foo/bar', :to => 'foo#' + end + } + assert_match(/Missing :action/, ex.message) end def test_valid_controller_options_inside_namespace @@ -3031,7 +3665,7 @@ class TestNamespaceWithControllerOption < ActionDispatch::IntegrationTest resources :storage_files, :controller => 'admin/storage_files' end - get 'storage_files' + get '/storage_files' assert_equal "admin/storage_files#index", @response.body end @@ -3056,6 +3690,16 @@ class TestNamespaceWithControllerOption < ActionDispatch::IntegrationTest assert_match "'Admin::StorageFiles' is not a supported controller name", e.message end + + def test_warn_with_ruby_constant_syntax_no_colons + e = assert_raise(ArgumentError) do + draw do + resources :storage_files, :controller => 'Admin' + end + end + + assert_match "'Admin' is not a supported controller name", e.message + end end class TestDefaultScope < ActionDispatch::IntegrationTest @@ -3073,8 +3717,10 @@ class TestDefaultScope < ActionDispatch::IntegrationTest resources :posts end + APP = build_app DefaultScopeRoutes + def app - DefaultScopeRoutes + APP end include DefaultScopeRoutes.url_helpers @@ -3092,24 +3738,28 @@ class TestHttpMethods < ActionDispatch::IntegrationTest RFC3648 = %w(ORDERPATCH) RFC3744 = %w(ACL) RFC5323 = %w(SEARCH) + RFC4791 = %w(MKCALENDAR) RFC5789 = %w(PATCH) def simple_app(response) lambda { |env| [ 200, { 'Content-Type' => 'text/plain' }, [response] ] } end - setup do + attr_reader :app + + def setup s = self - @app = ActionDispatch::Routing::RouteSet.new + routes = ActionDispatch::Routing::RouteSet.new + @app = RoutedRackApp.new routes - @app.draw do - (RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789).each do |method| + routes.draw do + (RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789).each do |method| match '/' => s.simple_app(method), :via => method.underscore.to_sym end end end - (RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789).each do |method| + (RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789).each do |method| test "request method #{method.underscore} can be matched" do get '/', nil, 'REQUEST_METHOD' => method assert_equal method, @response.body @@ -3133,10 +3783,11 @@ class TestUriPathEscaping < ActionDispatch::IntegrationTest end include Routes.url_helpers - def app; Routes end + APP = build_app Routes + def app; APP end - test 'escapes generated path segment' do - assert_equal '/a%20b/c+d', segment_path(:segment => 'a b/c+d') + test 'escapes slash in generated path segment' do + assert_equal '/a%20b%2Fc+d', segment_path(:segment => 'a b/c+d') end test 'unescapes recognized path segment' do @@ -3144,7 +3795,7 @@ class TestUriPathEscaping < ActionDispatch::IntegrationTest assert_equal 'a b/c+d', @response.body end - test 'escapes generated path splat' do + test 'does not escape slash in generated path splat' do assert_equal '/a%20b/c+d', splat_path(:splat => 'a b/c+d') end @@ -3164,7 +3815,8 @@ class TestUnicodePaths < ActionDispatch::IntegrationTest end include Routes.url_helpers - def app; Routes end + APP = build_app Routes + def app; APP end test 'recognizes unicode path' do get "/#{Rack::Utils.escape("ほげ")}" @@ -3195,7 +3847,8 @@ class TestMultipleNestedController < ActionDispatch::IntegrationTest end include Routes.url_helpers - def app; Routes end + APP = build_app Routes + def app; APP end test "controller option which starts with '/' from multiple nested controller" do get "/foo/bar/baz" @@ -3214,7 +3867,8 @@ class TestTildeAndMinusPaths < ActionDispatch::IntegrationTest end include Routes.url_helpers - def app; Routes end + APP = build_app Routes + def app; APP end test 'recognizes tilde path' do get "/~user" @@ -3241,7 +3895,8 @@ class TestRedirectInterpolation < ActionDispatch::IntegrationTest end end - def app; Routes end + APP = build_app Routes + def app; APP end test "redirect escapes interpolated parameters with redirect proc" do get "/foo/1%3E" @@ -3283,7 +3938,8 @@ class TestConstraintsAccessingParameters < ActionDispatch::IntegrationTest end end - def app; Routes end + APP = build_app Routes + def app; APP end test "parameters are reset between constraint checks" do get "/bar" @@ -3303,7 +3959,8 @@ class TestGlobRoutingMapper < ActionDispatch::IntegrationTest end #include Routes.url_helpers - def app; Routes end + APP = build_app Routes + def app; APP end def test_glob_constraint get "/dummy" @@ -3327,11 +3984,16 @@ class TestOptimizedNamedRoutes < ActionDispatch::IntegrationTest ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] } get '/foo' => ok, as: :foo get '/post(/:action(/:id))' => ok, as: :posts + get '/:foo/:foo_type/bars/:id' => ok, as: :bar + get '/projects/:id.:format' => ok, as: :project + get '/pages/:id' => ok, as: :page + get '/wiki/*page' => ok, as: :wiki end end include Routes.url_helpers - def app; Routes end + APP = build_app Routes + def app; APP end test 'enabled when not mounted and default_url_options is empty' do assert Routes.url_helpers.optimize_routes_generation? @@ -3349,6 +4011,36 @@ class TestOptimizedNamedRoutes < ActionDispatch::IntegrationTest assert_equal '/post', Routes.url_helpers.posts_path assert_equal '/post', posts_path end + + test 'segments with same prefix are replaced correctly' do + assert_equal '/foo/baz/bars/1', Routes.url_helpers.bar_path('foo', 'baz', '1') + assert_equal '/foo/baz/bars/1', bar_path('foo', 'baz', '1') + end + + test 'segments separated with a period are replaced correctly' do + assert_equal '/projects/1.json', Routes.url_helpers.project_path(1, :json) + assert_equal '/projects/1.json', project_path(1, :json) + end + + test 'segments with question marks are escaped' do + assert_equal '/pages/foo%3Fbar', Routes.url_helpers.page_path('foo?bar') + assert_equal '/pages/foo%3Fbar', page_path('foo?bar') + end + + test 'segments with slashes are escaped' do + assert_equal '/pages/foo%2Fbar', Routes.url_helpers.page_path('foo/bar') + assert_equal '/pages/foo%2Fbar', page_path('foo/bar') + end + + test 'glob segments with question marks are escaped' do + assert_equal '/wiki/foo%3Fbar', Routes.url_helpers.wiki_path('foo?bar') + assert_equal '/wiki/foo%3Fbar', wiki_path('foo?bar') + end + + test 'glob segments with slashes are not escaped' do + assert_equal '/wiki/foo/bar', Routes.url_helpers.wiki_path('foo/bar') + assert_equal '/wiki/foo/bar', wiki_path('foo/bar') + end end class TestNamedRouteUrlHelpers < ActionDispatch::IntegrationTest @@ -3373,7 +4065,8 @@ class TestNamedRouteUrlHelpers < ActionDispatch::IntegrationTest end end - def app; Routes end + APP = build_app Routes + def app; APP end include Routes.url_helpers @@ -3408,7 +4101,8 @@ class TestUrlConstraints < ActionDispatch::IntegrationTest end include Routes.url_helpers - def app; Routes end + APP = build_app Routes + def app; APP end test "constraints are copied to defaults when using constraints method" do assert_equal 'http://admin.example.com/', admin_root_url @@ -3462,7 +4156,7 @@ class TestInvalidUrls < ActionDispatch::IntegrationTest set.draw do get "/bar/:id", :to => redirect("/foo/show/%{id}") get "/foo/show(/:id)", :to => "test_invalid_urls/foo#show" - get "/foo(/:action(/:id))", :to => "test_invalid_urls/foo" + get "/foo(/:action(/:id))", :controller => "test_invalid_urls/foo" get "/:controller(/:action(/:id))" end @@ -3489,8 +4183,9 @@ class TestOptionalRootSegments < ActionDispatch::IntegrationTest end end + APP = build_app Routes def app - Routes + APP end include Routes.url_helpers @@ -3521,7 +4216,8 @@ class TestPortConstraints < ActionDispatch::IntegrationTest end include Routes.url_helpers - def app; Routes end + APP = build_app Routes + def app; APP end def test_integer_port_constraints get 'http://www.example.com/integer' @@ -3569,7 +4265,8 @@ class TestFormatConstraints < ActionDispatch::IntegrationTest end include Routes.url_helpers - def app; Routes end + APP = build_app Routes + def app; APP end def test_string_format_constraints get 'http://www.example.com/string' @@ -3616,6 +4313,17 @@ class TestFormatConstraints < ActionDispatch::IntegrationTest end end +class TestCallableConstraintValidation < ActionDispatch::IntegrationTest + def test_constraint_with_object_not_callable + assert_raises(ArgumentError) do + ActionDispatch::Routing::RouteSet.new.draw do + ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] } + get '/test', to: ok, constraints: Object.new + end + end + end +end + class TestRouteDefaults < ActionDispatch::IntegrationTest stub_controllers do |routes| Routes = routes @@ -3625,8 +4333,9 @@ class TestRouteDefaults < ActionDispatch::IntegrationTest end end + APP = build_app Routes def app - Routes + APP end include Routes.url_helpers @@ -3654,8 +4363,9 @@ class TestRackAppRouteGeneration < ActionDispatch::IntegrationTest end end + APP = build_app Routes def app - Routes + APP end include Routes.url_helpers @@ -3680,8 +4390,9 @@ class TestRedirectRouteGeneration < ActionDispatch::IntegrationTest end end + APP = build_app Routes def app - Routes + APP end include Routes.url_helpers @@ -3696,3 +4407,57 @@ class TestRedirectRouteGeneration < ActionDispatch::IntegrationTest end end end + +class TestUrlGenerationErrors < ActionDispatch::IntegrationTest + Routes = ActionDispatch::Routing::RouteSet.new.tap do |app| + app.draw do + get "/products/:id" => 'products#show', :as => :product + end + end + + APP = build_app Routes + def app; APP end + + include Routes.url_helpers + + test "url helpers raise a helpful error message when generation fails" do + url, missing = { action: 'show', controller: 'products', id: nil }, [:id] + message = "No route matches #{url.inspect} missing required keys: #{missing.inspect}" + + # Optimized url helper + error = assert_raises(ActionController::UrlGenerationError){ product_path(nil) } + assert_equal message, error.message + + # Non-optimized url helper + error = assert_raises(ActionController::UrlGenerationError, message){ product_path(id: nil) } + assert_equal message, error.message + end +end + +class TestDefaultUrlOptions < ActionDispatch::IntegrationTest + class PostsController < ActionController::Base + def archive + render :text => "posts#archive" + end + end + + Routes = ActionDispatch::Routing::RouteSet.new + Routes.draw do + default_url_options locale: 'en' + scope ':locale', format: false do + get '/posts/:year/:month/:day', to: 'posts#archive', as: 'archived_posts' + end + end + + APP = build_app Routes + + def app + APP + end + + include Routes.url_helpers + + def test_positional_args_with_format_false + assert_equal '/en/posts/2014/12/13', archived_posts_path(2014, 12, 13) + end +end diff --git a/actionpack/test/dispatch/session/cache_store_test.rb b/actionpack/test/dispatch/session/cache_store_test.rb index b8479e8836..9f810cad01 100644 --- a/actionpack/test/dispatch/session/cache_store_test.rb +++ b/actionpack/test/dispatch/session/cache_store_test.rb @@ -148,16 +148,15 @@ class CacheStoreTest < ActionDispatch::IntegrationTest def test_prevents_session_fixation with_test_route_set do - get '/get_session_value' - assert_response :success - assert_equal 'foo: nil', response.body - session_id = cookies['_session_id'] + assert_equal nil, @cache.read('_session_id:0xhax') - reset! + cookies['_session_id'] = '0xhax' + get '/set_session_value' - get '/set_session_value', :_session_id => session_id assert_response :success - assert_not_equal session_id, cookies['_session_id'] + assert_not_equal '0xhax', cookies['_session_id'] + assert_equal nil, @cache.read('_session_id:0xhax') + assert_equal({'foo' => 'bar'}, @cache.read("_session_id:#{cookies['_session_id']}")) end end @@ -169,8 +168,8 @@ class CacheStoreTest < ActionDispatch::IntegrationTest end @app = self.class.build_app(set) do |middleware| - cache = ActiveSupport::Cache::MemoryStore.new - middleware.use ActionDispatch::Session::CacheStore, :key => '_session_id', :cache => cache + @cache = ActiveSupport::Cache::MemoryStore.new + middleware.use ActionDispatch::Session::CacheStore, :key => '_session_id', :cache => @cache middleware.delete "ActionDispatch::ShowExceptions" end diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index e99ff46edf..c5cd24d06e 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -86,7 +86,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest cookies[SessionKey] = SignedBar get '/persistent_session_id' assert_response :success - assert_equal response.body.size, 32 + assert_equal 32, response.body.size session_id = response.body get '/get_session_id' @@ -247,7 +247,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest cookies[SessionKey] = SignedBar get '/persistent_session_id' assert_response :success - assert_equal response.body.size, 32 + assert_equal 32, response.body.size session_id = response.body get '/persistent_session_id' assert_equal session_id, response.body @@ -263,7 +263,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest get "/get_session_id" sid = response.body - assert_equal sid.size, 36 + assert_equal 36, sid.size get "/change_session_id" assert_not_equal sid, response.body diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb index e53ce4195b..f7a06cfed4 100644 --- a/actionpack/test/dispatch/session/mem_cache_store_test.rb +++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'securerandom' # You need to start a memcached server inorder to run these tests class MemCacheStoreTest < ActionDispatch::IntegrationTest @@ -48,6 +49,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest assert_response :success assert_equal 'foo: "bar"', response.body end + rescue Dalli::RingError => ex + skip ex.message, ex.backtrace end def test_getting_nil_session_value @@ -56,6 +59,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest assert_response :success assert_equal 'foo: nil', response.body end + rescue Dalli::RingError => ex + skip ex.message, ex.backtrace end def test_getting_session_value_after_session_reset @@ -75,6 +80,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest assert_response :success assert_equal 'foo: nil', response.body, "data for this session should have been obliterated from memcached" end + rescue Dalli::RingError => ex + skip ex.message, ex.backtrace end def test_getting_from_nonexistent_session @@ -84,6 +91,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest assert_equal 'foo: nil', response.body assert_nil cookies['_session_id'], "should only create session on write, not read" end + rescue Dalli::RingError => ex + skip ex.message, ex.backtrace end def test_setting_session_value_after_session_reset @@ -105,6 +114,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest assert_response :success assert_not_equal session_id, response.body end + rescue Dalli::RingError => ex + skip ex.message, ex.backtrace end def test_getting_session_id @@ -118,6 +129,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest assert_response :success assert_equal session_id, response.body, "should be able to read session id without accessing the session hash" end + rescue Dalli::RingError => ex + skip ex.message, ex.backtrace end def test_deserializes_unloaded_class @@ -132,6 +145,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest assert_response :success end end + rescue Dalli::RingError => ex + skip ex.message, ex.backtrace end def test_doesnt_write_session_cookie_if_session_id_is_already_exists @@ -144,6 +159,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest assert_response :success assert_equal nil, headers['Set-Cookie'], "should not resend the cookie again if session_id cookie is already exists" end + rescue Dalli::RingError => ex + skip ex.message, ex.backtrace end def test_prevents_session_fixation @@ -159,6 +176,8 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest assert_response :success assert_not_equal session_id, cookies['_session_id'] end + rescue Dalli::RingError => ex + skip ex.message, ex.backtrace end rescue LoadError, RuntimeError, Dalli::DalliError $stderr.puts "Skipping MemCacheStoreTest tests. Start memcached and try again." @@ -172,7 +191,7 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest end @app = self.class.build_app(set) do |middleware| - middleware.use ActionDispatch::Session::MemCacheStore, :key => '_session_id' + middleware.use ActionDispatch::Session::MemCacheStore, :key => '_session_id', :namespace => "mem_cache_store_test:#{SecureRandom.hex(10)}" middleware.delete "ActionDispatch::ShowExceptions" end diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb index 38bd234f37..323fbc285e 100644 --- a/actionpack/test/dispatch/show_exceptions_test.rb +++ b/actionpack/test/dispatch/show_exceptions_test.rb @@ -37,7 +37,7 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest get "/", {}, {'action_dispatch.show_exceptions' => true} assert_response 500 assert_equal "500 error fixture\n", body - + get "/bad_params", {}, {'action_dispatch.show_exceptions' => true} assert_response 400 assert_equal "400 error fixture\n", body @@ -92,6 +92,7 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest exceptions_app = lambda do |env| assert_kind_of AbstractController::ActionNotFound, env["action_dispatch.exception"] assert_equal "/404", env["PATH_INFO"] + assert_equal "/not_found_original_exception", env["action_dispatch.original_path"] [404, { "Content-Type" => "text/plain" }, ["YOU FAILED BRO"]] end diff --git a/actionpack/test/dispatch/ssl_test.rb b/actionpack/test/dispatch/ssl_test.rb index 94969f795a..c3598c5e8e 100644 --- a/actionpack/test/dispatch/ssl_test.rb +++ b/actionpack/test/dispatch/ssl_test.rb @@ -196,6 +196,13 @@ class SSLTest < ActionDispatch::IntegrationTest response.headers['Location'] end + def test_redirect_to_host_with_port + self.app = ActionDispatch::SSL.new(default_app, :host => "ssl.example.org:443") + get "http://example.org/path?key=value" + assert_equal "https://ssl.example.org:443/path?key=value", + response.headers['Location'] + end + def test_redirect_to_secure_host_when_on_subdomain self.app = ActionDispatch::SSL.new(default_app, :host => "ssl.example.org") get "http://ssl.example.org/path?key=value" diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb index acccbcb2e6..7f1207eaed 100644 --- a/actionpack/test/dispatch/static_test.rb +++ b/actionpack/test/dispatch/static_test.rb @@ -1,6 +1,7 @@ # encoding: utf-8 require 'abstract_unit' require 'rbconfig' +require 'zlib' module StaticTests def test_serves_dynamic_content @@ -36,11 +37,13 @@ module StaticTests assert_html "/foo/index.html", get("/foo") end + def test_serves_file_with_same_name_before_index_in_directory + assert_html "/bar.html", get("/bar") + end + def test_served_static_file_with_non_english_filename - if RUBY_ENGINE == 'jruby ' - skip "Stop skipping if following bug gets fixed: " \ + jruby_skip "Stop skipping if following bug gets fixed: " \ "http://jira.codehaus.org/browse/JRUBY-7192" - end assert_html "means hello in Japanese\n", get("/foo/#{Rack::Utils.escape("こんにちは.html")}") end @@ -108,6 +111,40 @@ module StaticTests end end + def test_serves_gzip_files_when_header_set + file_name = "/gzip/application-a71b3024f80aea3181c09774ca17e712.js" + response = get(file_name, 'HTTP_ACCEPT_ENCODING' => 'gzip') + assert_gzip file_name, response + assert_equal 'application/javascript', response.headers['Content-Type'] + assert_equal 'Accept-Encoding', response.headers["Vary"] + assert_equal 'gzip', response.headers["Content-Encoding"] + + response = get(file_name, 'HTTP_ACCEPT_ENCODING' => 'Gzip') + assert_gzip file_name, response + + response = get(file_name, 'HTTP_ACCEPT_ENCODING' => 'GZIP') + assert_gzip file_name, response + + response = get(file_name, 'HTTP_ACCEPT_ENCODING' => '') + assert_not_equal 'gzip', response.headers["Content-Encoding"] + end + + def test_does_not_modify_path_info + file_name = "/gzip/application-a71b3024f80aea3181c09774ca17e712.js" + env = {'PATH_INFO' => file_name, 'HTTP_ACCEPT_ENCODING' => 'gzip'} + @app.call(env) + assert_equal file_name, env['PATH_INFO'] + end + + def test_serves_gzip_with_propper_content_type_fallback + file_name = "/gzip/foo.zoo" + response = get(file_name, 'HTTP_ACCEPT_ENCODING' => 'gzip') + assert_gzip file_name, response + + default_response = get(file_name) # no gzip + assert_equal default_response.headers['Content-Type'], response.headers['Content-Type'] + end + # Windows doesn't allow \ / : * ? " < > | in filenames unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ def test_serves_static_file_with_colon @@ -127,21 +164,33 @@ module StaticTests private + def assert_gzip(file_name, response) + expected = File.read("#{FIXTURE_LOAD_PATH}/#{public_path}" + file_name) + actual = Zlib::GzipReader.new(StringIO.new(response.body)).read + assert_equal expected, actual + end + def assert_html(body, response) assert_equal body, response.body assert_equal "text/html", response.headers["Content-Type"] + assert_nil response.headers["Vary"] end - def get(path) - Rack::MockRequest.new(@app).request("GET", path) + def get(path, headers = {}) + Rack::MockRequest.new(@app).request("GET", path, headers) end def with_static_file(file) - path = "#{FIXTURE_LOAD_PATH}/public" + file - File.open(path, "wb+") { |f| f.write(file) } + path = "#{FIXTURE_LOAD_PATH}/#{public_path}" + file + begin + File.open(path, "wb+") { |f| f.write(file) } + rescue Errno::EPROTO + skip "Couldn't create a file #{path}" + end + yield file ensure - File.delete(path) + File.delete(path) if File.exist? path end end @@ -149,11 +198,42 @@ class StaticTest < ActiveSupport::TestCase DummyApp = lambda { |env| [200, {"Content-Type" => "text/plain"}, ["Hello, World!"]] } - App = ActionDispatch::Static.new(DummyApp, "#{FIXTURE_LOAD_PATH}/public", "public, max-age=60") def setup - @app = App + @root = "#{FIXTURE_LOAD_PATH}/public" + @app = ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60") + end + + def public_path + "public" end include StaticTests + + def test_custom_handler_called_when_file_is_outside_root + filename = 'shared.html.erb' + assert File.exist?(File.join(@root, '..', filename)) + env = { + "REQUEST_METHOD"=>"GET", + "REQUEST_PATH"=>"/..%2F#{filename}", + "PATH_INFO"=>"/..%2F#{filename}", + "REQUEST_URI"=>"/..%2F#{filename}", + "HTTP_VERSION"=>"HTTP/1.1", + "SERVER_NAME"=>"localhost", + "SERVER_PORT"=>"8080", + "QUERY_STRING"=>"" + } + assert_equal(DummyApp.call(nil), @app.call(env)) + end +end + +class StaticEncodingTest < StaticTest + def setup + @root = "#{FIXTURE_LOAD_PATH}/公共" + @app = ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60") + end + + def public_path + "公共" + end end diff --git a/actionpack/test/dispatch/template_assertions_test.rb b/actionpack/test/dispatch/template_assertions_test.rb new file mode 100644 index 0000000000..7278754b49 --- /dev/null +++ b/actionpack/test/dispatch/template_assertions_test.rb @@ -0,0 +1,110 @@ +require 'abstract_unit' + +class AssertTemplateController < ActionController::Base + def render_with_partial + render partial: 'test/partial' + end + + def render_with_template + render 'test/hello_world' + end + + def render_with_layout + @variable_for_layout = 'hello' + render 'test/hello_world', layout: "layouts/standard" + end + + def render_with_file + render file: 'README.rdoc' + end + + def render_nothing + head :ok + end +end + +class AssertTemplateControllerTest < ActionDispatch::IntegrationTest + def test_template_reset_between_requests + get '/assert_template/render_with_template' + assert_template 'test/hello_world' + + get '/assert_template/render_nothing' + assert_template nil + end + + def test_partial_reset_between_requests + get '/assert_template/render_with_partial' + assert_template partial: 'test/_partial' + + get '/assert_template/render_nothing' + assert_template partial: nil + end + + def test_layout_reset_between_requests + get '/assert_template/render_with_layout' + assert_template layout: 'layouts/standard' + + get '/assert_template/render_nothing' + assert_template layout: nil + end + + def test_file_reset_between_requests + get '/assert_template/render_with_file' + assert_template file: 'README.rdoc' + + get '/assert_template/render_nothing' + assert_template file: nil + end + + def test_template_reset_between_requests_when_opening_a_session + open_session do |session| + session.get '/assert_template/render_with_template' + session.assert_template 'test/hello_world' + + session.get '/assert_template/render_nothing' + session.assert_template nil + end + end + + def test_partial_reset_between_requests_when_opening_a_session + open_session do |session| + session.get '/assert_template/render_with_partial' + session.assert_template partial: 'test/_partial' + + session.get '/assert_template/render_nothing' + session.assert_template partial: nil + end + end + + def test_layout_reset_between_requests_when_opening_a_session + open_session do |session| + session.get '/assert_template/render_with_layout' + session.assert_template layout: 'layouts/standard' + + session.get '/assert_template/render_nothing' + session.assert_template layout: nil + end + end + + def test_file_reset_between_requests_when_opening_a_session + open_session do |session| + session.get '/assert_template/render_with_file' + session.assert_template file: 'README.rdoc' + + session.get '/assert_template/render_nothing' + session.assert_template file: nil + end + end + + def test_assigns_do_not_reset_template_assertion + get '/assert_template/render_with_layout' + assert_equal 'hello', assigns(:variable_for_layout) + assert_template layout: 'layouts/standard' + end + + def test_cookies_do_not_reset_template_assertion + get '/assert_template/render_with_layout' + cookies + assert_template layout: 'layouts/standard' + end +end diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb index 65ad8677f3..cc35d4594e 100644 --- a/actionpack/test/dispatch/test_request_test.rb +++ b/actionpack/test/dispatch/test_request_test.rb @@ -18,7 +18,7 @@ class TestRequestTest < ActiveSupport::TestCase assert_equal "0.0.0.0", env.delete("REMOTE_ADDR") assert_equal "Rails Testing", env.delete("HTTP_USER_AGENT") - assert_equal [1, 2], env.delete("rack.version") + assert_equal [1, 3], env.delete("rack.version") assert_equal "", env.delete("rack.input").string assert_kind_of StringIO, env.delete("rack.errors") assert_equal true, env.delete("rack.multithread") diff --git a/actionpack/test/dispatch/uploaded_file_test.rb b/actionpack/test/dispatch/uploaded_file_test.rb index 72f3d1db0d..55ebbd5143 100644 --- a/actionpack/test/dispatch/uploaded_file_test.rb +++ b/actionpack/test/dispatch/uploaded_file_test.rb @@ -18,6 +18,12 @@ module ActionDispatch assert_equal "UTF-8", uf.original_filename.encoding.to_s end + def test_filename_should_always_be_in_utf_8 + uf = Http::UploadedFile.new(:filename => 'foo'.encode(Encoding::SHIFT_JIS), + :tempfile => Object.new) + assert_equal "UTF-8", uf.original_filename.encoding.to_s + end + def test_content_type uf = Http::UploadedFile.new(:type => 'foo', :tempfile => Object.new) assert_equal 'foo', uf.content_type @@ -33,6 +39,12 @@ module ActionDispatch assert_equal 'foo', uf.tempfile end + def test_to_io_returns_the_tempfile + tf = Object.new + uf = Http::UploadedFile.new(:tempfile => tf) + assert_equal tf, uf.to_io + end + def test_delegates_path_to_tempfile tf = Class.new { def path; 'thunderhorse' end } uf = Http::UploadedFile.new(:tempfile => tf.new) diff --git a/actionpack/test/dispatch/url_generation_test.rb b/actionpack/test/dispatch/url_generation_test.rb index f919592d24..8f79e7bf9a 100644 --- a/actionpack/test/dispatch/url_generation_test.rb +++ b/actionpack/test/dispatch/url_generation_test.rb @@ -15,15 +15,19 @@ module TestUrlGeneration Routes.draw do get "/foo", :to => "my_route_generating#index", :as => :foo + resources :bars + mount MyRouteGeneratingController.action(:index), at: '/bar' end + APP = build_app Routes + def _routes Routes end def app - Routes + APP end test "generating URLS normally" do @@ -64,18 +68,30 @@ module TestUrlGeneration test "port is extracted from the host" do assert_equal "http://www.example.com:8080/foo", foo_url(host: "www.example.com:8080", protocol: "http://") + assert_equal "//www.example.com:8080/foo", foo_url(host: "www.example.com:8080", protocol: "//") + assert_equal "//www.example.com:80/foo", foo_url(host: "www.example.com:80", protocol: "//") + end + + test "port option is used" do + assert_equal "http://www.example.com:8080/foo", foo_url(host: "www.example.com", protocol: "http://", port: 8080) + assert_equal "//www.example.com:8080/foo", foo_url(host: "www.example.com", protocol: "//", port: 8080) + assert_equal "//www.example.com:80/foo", foo_url(host: "www.example.com", protocol: "//", port: 80) end - test "port option overides the host" do + test "port option overrides the host" do assert_equal "http://www.example.com:8080/foo", foo_url(host: "www.example.com:8443", protocol: "http://", port: 8080) + assert_equal "//www.example.com:8080/foo", foo_url(host: "www.example.com:8443", protocol: "//", port: 8080) + assert_equal "//www.example.com:80/foo", foo_url(host: "www.example.com:443", protocol: "//", port: 80) end test "port option disables the host when set to nil" do assert_equal "http://www.example.com/foo", foo_url(host: "www.example.com:8443", protocol: "http://", port: nil) + assert_equal "//www.example.com/foo", foo_url(host: "www.example.com:8443", protocol: "//", port: nil) end test "port option disables the host when set to false" do assert_equal "http://www.example.com/foo", foo_url(host: "www.example.com:8443", protocol: "http://", port: false) + assert_equal "//www.example.com/foo", foo_url(host: "www.example.com:8443", protocol: "//", port: false) end test "keep subdomain when key is true" do @@ -97,6 +113,22 @@ module TestUrlGeneration test "omit subdomain when key is blank" do assert_equal "http://example.com/foo", foo_url(subdomain: "") end + + test "generating URLs with trailing slashes" do + assert_equal "/bars.json", bars_path( + trailing_slash: true, + format: 'json' + ) + end + + test "generating URLS with querystring and trailing slashes" do + assert_equal "/bars.json?a=b", bars_path( + trailing_slash: true, + a: 'b', + format: 'json' + ) + end + end end diff --git a/actionpack/test/fixtures/functional_caching/formatted_fragment_cached_with_variant.html+phone.erb b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached_with_variant.html+phone.erb new file mode 100644 index 0000000000..e523b74ae3 --- /dev/null +++ b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached_with_variant.html+phone.erb @@ -0,0 +1,3 @@ +<body> +<%= cache do %><p>PHONE</p><% end %> +</body> diff --git a/actionpack/test/fixtures/helpers_typo/admin/users_helper.rb b/actionpack/test/fixtures/helpers_typo/admin/users_helper.rb new file mode 100644 index 0000000000..7d2326e04d --- /dev/null +++ b/actionpack/test/fixtures/helpers_typo/admin/users_helper.rb @@ -0,0 +1,5 @@ +module Admin + module UsersHelpeR + end +end + diff --git a/actionpack/test/fixtures/localized/hello_world.de.html b/actionpack/test/fixtures/localized/hello_world.de.html index 4727d7a7e0..a8fc612c60 100644 --- a/actionpack/test/fixtures/localized/hello_world.de.html +++ b/actionpack/test/fixtures/localized/hello_world.de.html @@ -1 +1 @@ -Gutten Tag
\ No newline at end of file +Guten Tag
\ No newline at end of file diff --git a/actionpack/test/fixtures/public/bar.html b/actionpack/test/fixtures/public/bar.html new file mode 100644 index 0000000000..67fc57079b --- /dev/null +++ b/actionpack/test/fixtures/public/bar.html @@ -0,0 +1 @@ +/bar.html
\ No newline at end of file diff --git a/actionpack/test/fixtures/public/bar/index.html b/actionpack/test/fixtures/public/bar/index.html new file mode 100644 index 0000000000..d5bb8f898d --- /dev/null +++ b/actionpack/test/fixtures/public/bar/index.html @@ -0,0 +1 @@ +/bar/index.html
\ No newline at end of file diff --git a/actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js b/actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js new file mode 100644 index 0000000000..1826a7660e --- /dev/null +++ b/actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js @@ -0,0 +1,4 @@ +!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=e.length,n=it.type(e);return"function"===n||it.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e}function r(e,t,n){if(it.isFunction(t))return it.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return it.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(ft.test(t))return it.filter(t,e,n);t=it.filter(t,e)}return it.grep(e,function(e){return it.inArray(e,t)>=0!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t=xt[e]={};return it.each(e.match(bt)||[],function(e,n){t[n]=!0}),t}function a(){ht.addEventListener?(ht.removeEventListener("DOMContentLoaded",s,!1),e.removeEventListener("load",s,!1)):(ht.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(ht.addEventListener||"load"===event.type||"complete"===ht.readyState)&&(a(),it.ready())}function l(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(Ct,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:kt.test(n)?it.parseJSON(n):n}catch(i){}it.data(e,t,n)}else n=void 0}return n}function u(e){var t;for(t in e)if(("data"!==t||!it.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(it.acceptData(e)){var i,o,a=it.expando,s=e.nodeType,l=s?it.cache:e,u=s?e[a]:e[a]&&a;if(u&&l[u]&&(r||l[u].data)||void 0!==n||"string"!=typeof t)return u||(u=s?e[a]=G.pop()||it.guid++:a),l[u]||(l[u]=s?{}:{toJSON:it.noop}),("object"==typeof t||"function"==typeof t)&&(r?l[u]=it.extend(l[u],t):l[u].data=it.extend(l[u].data,t)),o=l[u],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[it.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[it.camelCase(t)])):i=o,i}}function d(e,t,n){if(it.acceptData(e)){var r,i,o=e.nodeType,a=o?it.cache:e,s=o?e[it.expando]:it.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){it.isArray(t)?t=t.concat(it.map(t,it.camelCase)):t in r?t=[t]:(t=it.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!u(r):!it.isEmptyObject(r))return}(n||(delete a[s].data,u(a[s])))&&(o?it.cleanData([e],!0):nt.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}function f(){return!0}function p(){return!1}function h(){try{return ht.activeElement}catch(e){}}function m(e){var t=Mt.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function g(e,t){var n,r,i=0,o=typeof e.getElementsByTagName!==Et?e.getElementsByTagName(t||"*"):typeof e.querySelectorAll!==Et?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||it.nodeName(r,t)?o.push(r):it.merge(o,g(r,t));return void 0===t||t&&it.nodeName(e,t)?it.merge([e],o):o}function v(e){Dt.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t){return it.nodeName(e,"table")&&it.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function b(e){return e.type=(null!==it.find.attr(e,"type"))+"/"+e.type,e}function x(e){var t=Vt.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function w(e,t){for(var n,r=0;null!=(n=e[r]);r++)it._data(n,"globalEval",!t||it._data(t[r],"globalEval"))}function T(e,t){if(1===t.nodeType&&it.hasData(e)){var n,r,i,o=it._data(e),a=it._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)it.event.add(t,n,s[n][r])}a.data&&(a.data=it.extend({},a.data))}}function E(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!nt.noCloneEvent&&t[it.expando]){i=it._data(t);for(r in i.events)it.removeEvent(t,r,i.handle);t.removeAttribute(it.expando)}"script"===n&&t.text!==e.text?(b(t).text=e.text,x(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),nt.html5Clone&&e.innerHTML&&!it.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Dt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}function k(t,n){var r,i=it(n.createElement(t)).appendTo(n.body),o=e.getDefaultComputedStyle&&(r=e.getDefaultComputedStyle(i[0]))?r.display:it.css(i[0],"display");return i.detach(),o}function C(e){var t=ht,n=Zt[e];return n||(n=k(e,t),"none"!==n&&n||(Qt=(Qt||it("<iframe frameborder='0' width='0' height='0'/>")).appendTo(t.documentElement),t=(Qt[0].contentWindow||Qt[0].contentDocument).document,t.write(),t.close(),n=k(e,t),Qt.detach()),Zt[e]=n),n}function N(e,t){return{get:function(){var n=e();if(null!=n)return n?void delete this.get:(this.get=t).apply(this,arguments)}}}function S(e,t){if(t in e)return t;for(var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=pn.length;i--;)if(t=pn[i]+n,t in e)return t;return r}function j(e,t){for(var n,r,i,o=[],a=0,s=e.length;s>a;a++)r=e[a],r.style&&(o[a]=it._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&jt(r)&&(o[a]=it._data(r,"olddisplay",C(r.nodeName)))):(i=jt(r),(n&&"none"!==n||!i)&&it._data(r,"olddisplay",i?n:it.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}function A(e,t,n){var r=un.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function D(e,t,n,r,i){for(var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;4>o;o+=2)"margin"===n&&(a+=it.css(e,n+St[o],!0,i)),r?("content"===n&&(a-=it.css(e,"padding"+St[o],!0,i)),"margin"!==n&&(a-=it.css(e,"border"+St[o]+"Width",!0,i))):(a+=it.css(e,"padding"+St[o],!0,i),"padding"!==n&&(a+=it.css(e,"border"+St[o]+"Width",!0,i)));return a}function L(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=en(e),a=nt.boxSizing&&"border-box"===it.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=tn(e,t,o),(0>i||null==i)&&(i=e.style[t]),rn.test(i))return i;r=a&&(nt.boxSizingReliable()||i===e.style[t]),i=parseFloat(i)||0}return i+D(e,t,n||(a?"border":"content"),r,o)+"px"}function H(e,t,n,r,i){return new H.prototype.init(e,t,n,r,i)}function _(){return setTimeout(function(){hn=void 0}),hn=it.now()}function q(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=St[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}function F(e,t,n){for(var r,i=(xn[t]||[]).concat(xn["*"]),o=0,a=i.length;a>o;o++)if(r=i[o].call(n,t,e))return r}function M(e,t,n){var r,i,o,a,s,l,u,c,d=this,f={},p=e.style,h=e.nodeType&&jt(e),m=it._data(e,"fxshow");n.queue||(s=it._queueHooks(e,"fx"),null==s.unqueued&&(s.unqueued=0,l=s.empty.fire,s.empty.fire=function(){s.unqueued||l()}),s.unqueued++,d.always(function(){d.always(function(){s.unqueued--,it.queue(e,"fx").length||s.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],u=it.css(e,"display"),c="none"===u?it._data(e,"olddisplay")||C(e.nodeName):u,"inline"===c&&"none"===it.css(e,"float")&&(nt.inlineBlockNeedsLayout&&"inline"!==C(e.nodeName)?p.zoom=1:p.display="inline-block")),n.overflow&&(p.overflow="hidden",nt.shrinkWrapBlocks()||d.always(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],gn.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(h?"hide":"show")){if("show"!==i||!m||void 0===m[r])continue;h=!0}f[r]=m&&m[r]||it.style(e,r)}else u=void 0;if(it.isEmptyObject(f))"inline"===("none"===u?C(e.nodeName):u)&&(p.display=u);else{m?"hidden"in m&&(h=m.hidden):m=it._data(e,"fxshow",{}),o&&(m.hidden=!h),h?it(e).show():d.done(function(){it(e).hide()}),d.done(function(){var t;it._removeData(e,"fxshow");for(t in f)it.style(e,t,f[t])});for(r in f)a=F(h?m[r]:0,r,d),r in m||(m[r]=a.start,h&&(a.end=a.start,a.start="width"===r||"height"===r?1:0))}}function O(e,t){var n,r,i,o,a;for(n in e)if(r=it.camelCase(n),i=t[r],o=e[n],it.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=it.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}function R(e,t,n){var r,i,o=0,a=bn.length,s=it.Deferred().always(function(){delete l.elem}),l=function(){if(i)return!1;for(var t=hn||_(),n=Math.max(0,u.startTime+u.duration-t),r=n/u.duration||0,o=1-r,a=0,l=u.tweens.length;l>a;a++)u.tweens[a].run(o);return s.notifyWith(e,[u,o,n]),1>o&&l?n:(s.resolveWith(e,[u]),!1)},u=s.promise({elem:e,props:it.extend({},t),opts:it.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:hn||_(),duration:n.duration,tweens:[],createTween:function(t,n){var r=it.Tween(e,u.opts,t,n,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(r),r},stop:function(t){var n=0,r=t?u.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)u.tweens[n].run(1);return t?s.resolveWith(e,[u,t]):s.rejectWith(e,[u,t]),this}}),c=u.props;for(O(c,u.opts.specialEasing);a>o;o++)if(r=bn[o].call(u,e,c,u.opts))return r;return it.map(c,F,u),it.isFunction(u.opts.start)&&u.opts.start.call(e,u),it.fx.timer(it.extend(l,{elem:e,anim:u,queue:u.opts.queue})),u.progress(u.opts.progress).done(u.opts.done,u.opts.complete).fail(u.opts.fail).always(u.opts.always)}function B(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(bt)||[];if(it.isFunction(n))for(;r=o[i++];)"+"===r.charAt(0)?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function P(e,t,n,r){function i(s){var l;return o[s]=!0,it.each(e[s]||[],function(e,s){var u=s(t,n,r);return"string"!=typeof u||a||o[u]?a?!(l=u):void 0:(t.dataTypes.unshift(u),i(u),!1)}),l}var o={},a=e===zn;return i(t.dataTypes[0])||!o["*"]&&i("*")}function I(e,t){var n,r,i=it.ajaxSettings.flatOptions||{};for(r in t)void 0!==t[r]&&((i[r]?e:n||(n={}))[r]=t[r]);return n&&it.extend(!0,e,n),e}function W(e,t,n){for(var r,i,o,a,s=e.contents,l=e.dataTypes;"*"===l[0];)l.shift(),void 0===i&&(i=e.mimeType||t.getResponseHeader("Content-Type"));if(i)for(a in s)if(s[a]&&s[a].test(i)){l.unshift(a);break}if(l[0]in n)o=l[0];else{for(a in n){if(!l[0]||e.converters[a+" "+l[0]]){o=a;break}r||(r=a)}o=o||r}return o?(o!==l[0]&&l.unshift(o),n[o]):void 0}function $(e,t,n,r){var i,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(a=u[l+" "+o]||u["* "+o],!a)for(i in u)if(s=i.split(" "),s[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){a===!0?a=u[i]:u[i]!==!0&&(o=s[0],c.unshift(s[1]));break}if(a!==!0)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(d){return{state:"parsererror",error:a?d:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}function z(e,t,n,r){var i;if(it.isArray(t))it.each(t,function(t,i){n||Gn.test(e)?r(e,i):z(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==it.type(t))r(e,t);else for(i in t)z(e+"["+i+"]",t[i],n,r)}function X(){try{return new e.XMLHttpRequest}catch(t){}}function U(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}function V(e){return it.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}var G=[],Y=G.slice,J=G.concat,K=G.push,Q=G.indexOf,Z={},et=Z.toString,tt=Z.hasOwnProperty,nt={},rt="1.11.1",it=function(e,t){return new it.fn.init(e,t)},ot=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,at=/^-ms-/,st=/-([\da-z])/gi,lt=function(e,t){return t.toUpperCase()};it.fn=it.prototype={jquery:rt,constructor:it,selector:"",length:0,toArray:function(){return Y.call(this)},get:function(e){return null!=e?0>e?this[e+this.length]:this[e]:Y.call(this)},pushStack:function(e){var t=it.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return it.each(this,e,t)},map:function(e){return this.pushStack(it.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(Y.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:K,sort:G.sort,splice:G.splice},it.extend=it.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,l=arguments.length,u=!1;for("boolean"==typeof a&&(u=a,a=arguments[s]||{},s++),"object"==typeof a||it.isFunction(a)||(a={}),s===l&&(a=this,s--);l>s;s++)if(null!=(i=arguments[s]))for(r in i)e=a[r],n=i[r],a!==n&&(u&&n&&(it.isPlainObject(n)||(t=it.isArray(n)))?(t?(t=!1,o=e&&it.isArray(e)?e:[]):o=e&&it.isPlainObject(e)?e:{},a[r]=it.extend(u,o,n)):void 0!==n&&(a[r]=n));return a},it.extend({expando:"jQuery"+(rt+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isFunction:function(e){return"function"===it.type(e)},isArray:Array.isArray||function(e){return"array"===it.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!it.isArray(e)&&e-parseFloat(e)>=0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},isPlainObject:function(e){var t;if(!e||"object"!==it.type(e)||e.nodeType||it.isWindow(e))return!1;try{if(e.constructor&&!tt.call(e,"constructor")&&!tt.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}if(nt.ownLast)for(t in e)return tt.call(e,t);for(t in e);return void 0===t||tt.call(e,t)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?Z[et.call(e)]||"object":typeof e},globalEval:function(t){t&&it.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(at,"ms-").replace(st,lt)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,r){var i,o=0,a=e.length,s=n(e);if(r){if(s)for(;a>o&&(i=t.apply(e[o],r),i!==!1);o++);else for(o in e)if(i=t.apply(e[o],r),i===!1)break}else if(s)for(;a>o&&(i=t.call(e[o],o,e[o]),i!==!1);o++);else for(o in e)if(i=t.call(e[o],o,e[o]),i===!1)break;return e},trim:function(e){return null==e?"":(e+"").replace(ot,"")},makeArray:function(e,t){var r=t||[];return null!=e&&(n(Object(e))?it.merge(r,"string"==typeof e?[e]:e):K.call(r,e)),r},inArray:function(e,t,n){var r;if(t){if(Q)return Q.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;n>r;)e[i++]=t[r++];if(n!==n)for(;void 0!==t[r];)e[i++]=t[r++];return e.length=i,e},grep:function(e,t,n){for(var r,i=[],o=0,a=e.length,s=!n;a>o;o++)r=!t(e[o],o),r!==s&&i.push(e[o]);return i},map:function(e,t,r){var i,o=0,a=e.length,s=n(e),l=[];if(s)for(;a>o;o++)i=t(e[o],o,r),null!=i&&l.push(i);else for(o in e)i=t(e[o],o,r),null!=i&&l.push(i);return J.apply([],l)},guid:1,proxy:function(e,t){var n,r,i;return"string"==typeof t&&(i=e[t],t=e,e=i),it.isFunction(e)?(n=Y.call(arguments,2),r=function(){return e.apply(t||this,n.concat(Y.call(arguments)))},r.guid=e.guid=e.guid||it.guid++,r):void 0},now:function(){return+new Date},support:nt}),it.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){Z["[object "+t+"]"]=t.toLowerCase()});var ut=function(e){function t(e,t,n,r){var i,o,a,s,l,u,d,p,h,m;if((t?t.ownerDocument||t:P)!==H&&L(t),t=t||H,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(q&&!r){if(i=yt.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&R(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return Z.apply(n,t.getElementsByTagName(e)),n;if((a=i[3])&&w.getElementsByClassName&&t.getElementsByClassName)return Z.apply(n,t.getElementsByClassName(a)),n}if(w.qsa&&(!F||!F.test(e))){if(p=d=B,h=t,m=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){for(u=C(e),(d=t.getAttribute("id"))?p=d.replace(xt,"\\$&"):t.setAttribute("id",p),p="[id='"+p+"'] ",l=u.length;l--;)u[l]=p+f(u[l]);h=bt.test(e)&&c(t.parentNode)||t,m=u.join(",")}if(m)try{return Z.apply(n,h.querySelectorAll(m)),n}catch(g){}finally{d||t.removeAttribute("id")}}}return S(e.replace(lt,"$1"),t,n,r)}function n(){function e(n,r){return t.push(n+" ")>T.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[B]=!0,e}function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=e.length;r--;)T.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||G)-(~e.sourceIndex||G);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function l(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function u(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function c(e){return e&&typeof e.getElementsByTagName!==V&&e}function d(){}function f(e){for(var t=0,n=e.length,r="";n>t;t++)r+=e[t].value;return r}function p(e,t,n){var r=t.dir,i=n&&"parentNode"===r,o=W++;return t.first?function(t,n,o){for(;t=t[r];)if(1===t.nodeType||i)return e(t,n,o)}:function(t,n,a){var s,l,u=[I,o];if(a){for(;t=t[r];)if((1===t.nodeType||i)&&e(t,n,a))return!0}else for(;t=t[r];)if(1===t.nodeType||i){if(l=t[B]||(t[B]={}),(s=l[r])&&s[0]===I&&s[1]===o)return u[2]=s[2];if(l[r]=u,u[2]=e(t,n,a))return!0}}}function h(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function m(e,n,r){for(var i=0,o=n.length;o>i;i++)t(e,n[i],r);return r}function g(e,t,n,r,i){for(var o,a=[],s=0,l=e.length,u=null!=t;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function v(e,t,n,i,o,a){return i&&!i[B]&&(i=v(i)),o&&!o[B]&&(o=v(o,a)),r(function(r,a,s,l){var u,c,d,f=[],p=[],h=a.length,v=r||m(t||"*",s.nodeType?[s]:s,[]),y=!e||!r&&t?v:g(v,f,e,s,l),b=n?o||(r?e:h||i)?[]:a:y;if(n&&n(y,b,s,l),i)for(u=g(b,p),i(u,[],s,l),c=u.length;c--;)(d=u[c])&&(b[p[c]]=!(y[p[c]]=d));if(r){if(o||e){if(o){for(u=[],c=b.length;c--;)(d=b[c])&&u.push(y[c]=d);o(null,b=[],u,l)}for(c=b.length;c--;)(d=b[c])&&(u=o?tt.call(r,d):f[c])>-1&&(r[u]=!(a[u]=d))}}else b=g(b===a?b.splice(h,b.length):b),o?o(null,a,b,l):Z.apply(a,b)})}function y(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.relative[" "],s=o?1:0,l=p(function(e){return e===t},a,!0),u=p(function(e){return tt.call(t,e)>-1},a,!0),c=[function(e,n,r){return!o&&(r||n!==j)||((t=n).nodeType?l(e,n,r):u(e,n,r))}];i>s;s++)if(n=T.relative[e[s].type])c=[p(h(c),n)];else{if(n=T.filter[e[s].type].apply(null,e[s].matches),n[B]){for(r=++s;i>r&&!T.relative[e[r].type];r++);return v(s>1&&h(c),s>1&&f(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(lt,"$1"),n,r>s&&y(e.slice(s,r)),i>r&&y(e=e.slice(r)),i>r&&f(e))}c.push(n)}return h(c)}function b(e,n){var i=n.length>0,o=e.length>0,a=function(r,a,s,l,u){var c,d,f,p=0,h="0",m=r&&[],v=[],y=j,b=r||o&&T.find.TAG("*",u),x=I+=null==y?1:Math.random()||.1,w=b.length;for(u&&(j=a!==H&&a);h!==w&&null!=(c=b[h]);h++){if(o&&c){for(d=0;f=e[d++];)if(f(c,a,s)){l.push(c);break}u&&(I=x)}i&&((c=!f&&c)&&p--,r&&m.push(c))}if(p+=h,i&&h!==p){for(d=0;f=n[d++];)f(m,v,a,s);if(r){if(p>0)for(;h--;)m[h]||v[h]||(v[h]=K.call(l));v=g(v)}Z.apply(l,v),u&&!r&&v.length>0&&p+n.length>1&&t.uniqueSort(l)}return u&&(I=x,j=y),m};return i?r(a):a}var x,w,T,E,k,C,N,S,j,A,D,L,H,_,q,F,M,O,R,B="sizzle"+-new Date,P=e.document,I=0,W=0,$=n(),z=n(),X=n(),U=function(e,t){return e===t&&(D=!0),0},V="undefined",G=1<<31,Y={}.hasOwnProperty,J=[],K=J.pop,Q=J.push,Z=J.push,et=J.slice,tt=J.indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(this[t]===e)return t;return-1},nt="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",rt="[\\x20\\t\\r\\n\\f]",it="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",ot=it.replace("w","w#"),at="\\["+rt+"*("+it+")(?:"+rt+"*([*^$|!~]?=)"+rt+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+ot+"))|)"+rt+"*\\]",st=":("+it+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+at+")*)|.*)\\)|)",lt=new RegExp("^"+rt+"+|((?:^|[^\\\\])(?:\\\\.)*)"+rt+"+$","g"),ut=new RegExp("^"+rt+"*,"+rt+"*"),ct=new RegExp("^"+rt+"*([>+~]|"+rt+")"+rt+"*"),dt=new RegExp("="+rt+"*([^\\]'\"]*?)"+rt+"*\\]","g"),ft=new RegExp(st),pt=new RegExp("^"+ot+"$"),ht={ID:new RegExp("^#("+it+")"),CLASS:new RegExp("^\\.("+it+")"),TAG:new RegExp("^("+it.replace("w","w*")+")"),ATTR:new RegExp("^"+at),PSEUDO:new RegExp("^"+st),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+rt+"*(even|odd|(([+-]|)(\\d*)n|)"+rt+"*(?:([+-]|)"+rt+"*(\\d+)|))"+rt+"*\\)|)","i"),bool:new RegExp("^(?:"+nt+")$","i"),needsContext:new RegExp("^"+rt+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+rt+"*((?:-\\d)?\\d*)"+rt+"*\\)|)(?=[^-]|$)","i")},mt=/^(?:input|select|textarea|button)$/i,gt=/^h\d$/i,vt=/^[^{]+\{\s*\[native \w/,yt=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,bt=/[+~]/,xt=/'|\\/g,wt=new RegExp("\\\\([\\da-f]{1,6}"+rt+"?|("+rt+")|.)","ig"),Tt=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)};try{Z.apply(J=et.call(P.childNodes),P.childNodes),J[P.childNodes.length].nodeType}catch(Et){Z={apply:J.length?function(e,t){Q.apply(e,et.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}w=t.support={},k=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},L=t.setDocument=function(e){var t,n=e?e.ownerDocument||e:P,r=n.defaultView;return n!==H&&9===n.nodeType&&n.documentElement?(H=n,_=n.documentElement,q=!k(n),r&&r!==r.top&&(r.addEventListener?r.addEventListener("unload",function(){L()},!1):r.attachEvent&&r.attachEvent("onunload",function(){L()})),w.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),w.getElementsByTagName=i(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),w.getElementsByClassName=vt.test(n.getElementsByClassName)&&i(function(e){return e.innerHTML="<div class='a'></div><div class='a i'></div>",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),w.getById=i(function(e){return _.appendChild(e).id=B,!n.getElementsByName||!n.getElementsByName(B).length}),w.getById?(T.find.ID=function(e,t){if(typeof t.getElementById!==V&&q){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},T.filter.ID=function(e){var t=e.replace(wt,Tt);return function(e){return e.getAttribute("id")===t}}):(delete T.find.ID,T.filter.ID=function(e){var t=e.replace(wt,Tt);return function(e){var n=typeof e.getAttributeNode!==V&&e.getAttributeNode("id");return n&&n.value===t}}),T.find.TAG=w.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==V?t.getElementsByTagName(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},T.find.CLASS=w.getElementsByClassName&&function(e,t){return typeof t.getElementsByClassName!==V&&q?t.getElementsByClassName(e):void 0},M=[],F=[],(w.qsa=vt.test(n.querySelectorAll))&&(i(function(e){e.innerHTML="<select msallowclip=''><option selected=''></option></select>",e.querySelectorAll("[msallowclip^='']").length&&F.push("[*^$]="+rt+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+rt+"*(?:value|"+nt+")"),e.querySelectorAll(":checked").length||F.push(":checked")}),i(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+rt+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(w.matchesSelector=vt.test(O=_.matches||_.webkitMatchesSelector||_.mozMatchesSelector||_.oMatchesSelector||_.msMatchesSelector))&&i(function(e){w.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),M.push("!=",st)}),F=F.length&&new RegExp(F.join("|")),M=M.length&&new RegExp(M.join("|")),t=vt.test(_.compareDocumentPosition),R=t||vt.test(_.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},U=t?function(e,t){if(e===t)return D=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r?r:(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&r||!w.sortDetached&&t.compareDocumentPosition(e)===r?e===n||e.ownerDocument===P&&R(P,e)?-1:t===n||t.ownerDocument===P&&R(P,t)?1:A?tt.call(A,e)-tt.call(A,t):0:4&r?-1:1)}:function(e,t){if(e===t)return D=!0,0;var r,i=0,o=e.parentNode,s=t.parentNode,l=[e],u=[t];if(!o||!s)return e===n?-1:t===n?1:o?-1:s?1:A?tt.call(A,e)-tt.call(A,t):0;if(o===s)return a(e,t);for(r=e;r=r.parentNode;)l.unshift(r);for(r=t;r=r.parentNode;)u.unshift(r);for(;l[i]===u[i];)i++;return i?a(l[i],u[i]):l[i]===P?-1:u[i]===P?1:0},n):H},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==H&&L(e),n=n.replace(dt,"='$1']"),!(!w.matchesSelector||!q||M&&M.test(n)||F&&F.test(n)))try{var r=O.call(e,n);if(r||w.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return t(n,H,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==H&&L(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==H&&L(e);var n=T.attrHandle[t.toLowerCase()],r=n&&Y.call(T.attrHandle,t.toLowerCase())?n(e,t,!q):void 0;return void 0!==r?r:w.attributes||!q?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(D=!w.detectDuplicates,A=!w.sortStable&&e.slice(0),e.sort(U),D){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return A=null,e},E=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=E(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=E(t);return n},T=t.selectors={cacheLength:50,createPseudo:r,match:ht,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(wt,Tt),e[3]=(e[3]||e[4]||e[5]||"").replace(wt,Tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return ht.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&ft.test(n)&&(t=C(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(wt,Tt).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=$[e+" "];return t||(t=new RegExp("(^|"+rt+")"+e+"("+rt+"|$)"))&&$(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==V&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:n?(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o+" ").indexOf(r)>-1:"|="===n?o===r||o.slice(0,r.length+1)===r+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,d,f,p,h,m=o!==a?"nextSibling":"previousSibling",g=t.parentNode,v=s&&t.nodeName.toLowerCase(),y=!l&&!s;if(g){if(o){for(;m;){for(d=t;d=d[m];)if(s?d.nodeName.toLowerCase()===v:1===d.nodeType)return!1;h=m="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?g.firstChild:g.lastChild],a&&y){for(c=g[B]||(g[B]={}),u=c[e]||[],p=u[0]===I&&u[1],f=u[0]===I&&u[2],d=p&&g.childNodes[p];d=++p&&d&&d[m]||(f=p=0)||h.pop();)if(1===d.nodeType&&++f&&d===t){c[e]=[I,p,f];break}}else if(y&&(u=(t[B]||(t[B]={}))[e])&&u[0]===I)f=u[1];else for(;(d=++p&&d&&d[m]||(f=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==v:1!==d.nodeType)||!++f||(y&&((d[B]||(d[B]={}))[e]=[I,f]),d!==t)););return f-=i,f===r||f%r===0&&f/r>=0}}},PSEUDO:function(e,n){var i,o=T.pseudos[e]||T.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[B]?o(n):o.length>1?(i=[e,e,"",n],T.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=tt.call(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=N(e.replace(lt,"$1"));return i[B]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return function(t){return(t.textContent||t.innerText||E(t)).indexOf(e)>-1}}),lang:r(function(e){return pt.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(wt,Tt).toLowerCase(),function(t){var n;do if(n=q?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===_},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.pseudos.empty(e)},header:function(e){return gt.test(e.nodeName)},input:function(e){return mt.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:u(function(){return[0]}),last:u(function(e,t){return[t-1]}),eq:u(function(e,t,n){return[0>n?n+t:n]}),even:u(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:u(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:u(function(e,t,n){for(var r=0>n?n+t:n;--r>=0;)e.push(r);return e}),gt:u(function(e,t,n){for(var r=0>n?n+t:n;++r<t;)e.push(r);return e})}},T.pseudos.nth=T.pseudos.eq;for(x in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})T.pseudos[x]=s(x);for(x in{submit:!0,reset:!0})T.pseudos[x]=l(x);return d.prototype=T.filters=T.pseudos,T.setFilters=new d,C=t.tokenize=function(e,n){var r,i,o,a,s,l,u,c=z[e+" "];if(c)return n?0:c.slice(0);for(s=e,l=[],u=T.preFilter;s;){(!r||(i=ut.exec(s)))&&(i&&(s=s.slice(i[0].length)||s),l.push(o=[])),r=!1,(i=ct.exec(s))&&(r=i.shift(),o.push({value:r,type:i[0].replace(lt," ")}),s=s.slice(r.length));for(a in T.filter)!(i=ht[a].exec(s))||u[a]&&!(i=u[a](i))||(r=i.shift(),o.push({value:r,type:a,matches:i}),s=s.slice(r.length));if(!r)break}return n?s.length:s?t.error(e):z(e,l).slice(0)},N=t.compile=function(e,t){var n,r=[],i=[],o=X[e+" "];if(!o){for(t||(t=C(e)),n=t.length;n--;)o=y(t[n]),o[B]?r.push(o):i.push(o);o=X(e,b(i,r)),o.selector=e}return o},S=t.select=function(e,t,n,r){var i,o,a,s,l,u="function"==typeof e&&e,d=!r&&C(e=u.selector||e);if(n=n||[],1===d.length){if(o=d[0]=d[0].slice(0),o.length>2&&"ID"===(a=o[0]).type&&w.getById&&9===t.nodeType&&q&&T.relative[o[1].type]){if(t=(T.find.ID(a.matches[0].replace(wt,Tt),t)||[])[0],!t)return n;u&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=ht.needsContext.test(e)?0:o.length;i--&&(a=o[i],!T.relative[s=a.type]);)if((l=T.find[s])&&(r=l(a.matches[0].replace(wt,Tt),bt.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&f(o),!e)return Z.apply(n,r),n; +break}}return(u||N(e,d))(r,t,!q,n,bt.test(e)&&c(t.parentNode)||t),n},w.sortStable=B.split("").sort(U).join("")===B,w.detectDuplicates=!!D,L(),w.sortDetached=i(function(e){return 1&e.compareDocumentPosition(H.createElement("div"))}),i(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){return n?void 0:e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),w.attributes&&i(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){return n||"input"!==e.nodeName.toLowerCase()?void 0:e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(nt,function(e,t,n){var r;return n?void 0:e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);it.find=ut,it.expr=ut.selectors,it.expr[":"]=it.expr.pseudos,it.unique=ut.uniqueSort,it.text=ut.getText,it.isXMLDoc=ut.isXML,it.contains=ut.contains;var ct=it.expr.match.needsContext,dt=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,ft=/^.[^:#\[\.,]*$/;it.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?it.find.matchesSelector(r,e)?[r]:[]:it.find.matches(e,it.grep(t,function(e){return 1===e.nodeType}))},it.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(it(e).filter(function(){for(t=0;i>t;t++)if(it.contains(r[t],this))return!0}));for(t=0;i>t;t++)it.find(e,r[t],n);return n=this.pushStack(i>1?it.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},filter:function(e){return this.pushStack(r(this,e||[],!1))},not:function(e){return this.pushStack(r(this,e||[],!0))},is:function(e){return!!r(this,"string"==typeof e&&ct.test(e)?it(e):e||[],!1).length}});var pt,ht=e.document,mt=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,gt=it.fn.init=function(e,t){var n,r;if(!e)return this;if("string"==typeof e){if(n="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:mt.exec(e),!n||!n[1]&&t)return!t||t.jquery?(t||pt).find(e):this.constructor(t).find(e);if(n[1]){if(t=t instanceof it?t[0]:t,it.merge(this,it.parseHTML(n[1],t&&t.nodeType?t.ownerDocument||t:ht,!0)),dt.test(n[1])&&it.isPlainObject(t))for(n in t)it.isFunction(this[n])?this[n](t[n]):this.attr(n,t[n]);return this}if(r=ht.getElementById(n[2]),r&&r.parentNode){if(r.id!==n[2])return pt.find(e);this.length=1,this[0]=r}return this.context=ht,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):it.isFunction(e)?"undefined"!=typeof pt.ready?pt.ready(e):e(it):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),it.makeArray(e,this))};gt.prototype=it.fn,pt=it(ht);var vt=/^(?:parents|prev(?:Until|All))/,yt={children:!0,contents:!0,next:!0,prev:!0};it.extend({dir:function(e,t,n){for(var r=[],i=e[t];i&&9!==i.nodeType&&(void 0===n||1!==i.nodeType||!it(i).is(n));)1===i.nodeType&&r.push(i),i=i[t];return r},sibling:function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}}),it.fn.extend({has:function(e){var t,n=it(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(it.contains(this,n[t]))return!0})},closest:function(e,t){for(var n,r=0,i=this.length,o=[],a=ct.test(e)||"string"!=typeof e?it(e,t||this.context):0;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?a.index(n)>-1:1===n.nodeType&&it.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?it.unique(o):o)},index:function(e){return e?"string"==typeof e?it.inArray(this[0],it(e)):it.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(it.unique(it.merge(this.get(),it(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),it.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return it.dir(e,"parentNode")},parentsUntil:function(e,t,n){return it.dir(e,"parentNode",n)},next:function(e){return i(e,"nextSibling")},prev:function(e){return i(e,"previousSibling")},nextAll:function(e){return it.dir(e,"nextSibling")},prevAll:function(e){return it.dir(e,"previousSibling")},nextUntil:function(e,t,n){return it.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return it.dir(e,"previousSibling",n)},siblings:function(e){return it.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return it.sibling(e.firstChild)},contents:function(e){return it.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:it.merge([],e.childNodes)}},function(e,t){it.fn[e]=function(n,r){var i=it.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=it.filter(r,i)),this.length>1&&(yt[e]||(i=it.unique(i)),vt.test(e)&&(i=i.reverse())),this.pushStack(i)}});var bt=/\S+/g,xt={};it.Callbacks=function(e){e="string"==typeof e?xt[e]||o(e):it.extend({},e);var t,n,r,i,a,s,l=[],u=!e.once&&[],c=function(o){for(n=e.memory&&o,r=!0,a=s||0,s=0,i=l.length,t=!0;l&&i>a;a++)if(l[a].apply(o[0],o[1])===!1&&e.stopOnFalse){n=!1;break}t=!1,l&&(u?u.length&&c(u.shift()):n?l=[]:d.disable())},d={add:function(){if(l){var r=l.length;!function o(t){it.each(t,function(t,n){var r=it.type(n);"function"===r?e.unique&&d.has(n)||l.push(n):n&&n.length&&"string"!==r&&o(n)})}(arguments),t?i=l.length:n&&(s=r,c(n))}return this},remove:function(){return l&&it.each(arguments,function(e,n){for(var r;(r=it.inArray(n,l,r))>-1;)l.splice(r,1),t&&(i>=r&&i--,a>=r&&a--)}),this},has:function(e){return e?it.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],i=0,this},disable:function(){return l=u=n=void 0,this},disabled:function(){return!l},lock:function(){return u=void 0,n||d.disable(),this},locked:function(){return!u},fireWith:function(e,n){return!l||r&&!u||(n=n||[],n=[e,n.slice?n.slice():n],t?u.push(n):c(n)),this},fire:function(){return d.fireWith(this,arguments),this},fired:function(){return!!r}};return d},it.extend({Deferred:function(e){var t=[["resolve","done",it.Callbacks("once memory"),"resolved"],["reject","fail",it.Callbacks("once memory"),"rejected"],["notify","progress",it.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return it.Deferred(function(n){it.each(t,function(t,o){var a=it.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&it.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[o[0]+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?it.extend(e,r):r}},i={};return r.pipe=r.then,it.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=Y.call(arguments),a=o.length,s=1!==a||e&&it.isFunction(e.promise)?a:0,l=1===s?e:it.Deferred(),u=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?Y.call(arguments):i,r===t?l.notifyWith(n,r):--s||l.resolveWith(n,r)}};if(a>1)for(t=new Array(a),n=new Array(a),r=new Array(a);a>i;i++)o[i]&&it.isFunction(o[i].promise)?o[i].promise().done(u(i,r,o)).fail(l.reject).progress(u(i,n,t)):--s;return s||l.resolveWith(r,o),l.promise()}});var wt;it.fn.ready=function(e){return it.ready.promise().done(e),this},it.extend({isReady:!1,readyWait:1,holdReady:function(e){e?it.readyWait++:it.ready(!0)},ready:function(e){if(e===!0?!--it.readyWait:!it.isReady){if(!ht.body)return setTimeout(it.ready);it.isReady=!0,e!==!0&&--it.readyWait>0||(wt.resolveWith(ht,[it]),it.fn.triggerHandler&&(it(ht).triggerHandler("ready"),it(ht).off("ready")))}}}),it.ready.promise=function(t){if(!wt)if(wt=it.Deferred(),"complete"===ht.readyState)setTimeout(it.ready);else if(ht.addEventListener)ht.addEventListener("DOMContentLoaded",s,!1),e.addEventListener("load",s,!1);else{ht.attachEvent("onreadystatechange",s),e.attachEvent("onload",s);var n=!1;try{n=null==e.frameElement&&ht.documentElement}catch(r){}n&&n.doScroll&&!function i(){if(!it.isReady){try{n.doScroll("left")}catch(e){return setTimeout(i,50)}a(),it.ready()}}()}return wt.promise(t)};var Tt,Et="undefined";for(Tt in it(nt))break;nt.ownLast="0"!==Tt,nt.inlineBlockNeedsLayout=!1,it(function(){var e,t,n,r;n=ht.getElementsByTagName("body")[0],n&&n.style&&(t=ht.createElement("div"),r=ht.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),typeof t.style.zoom!==Et&&(t.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",nt.inlineBlockNeedsLayout=e=3===t.offsetWidth,e&&(n.style.zoom=1)),n.removeChild(r))}),function(){var e=ht.createElement("div");if(null==nt.deleteExpando){nt.deleteExpando=!0;try{delete e.test}catch(t){nt.deleteExpando=!1}}e=null}(),it.acceptData=function(e){var t=it.noData[(e.nodeName+" ").toLowerCase()],n=+e.nodeType||1;return 1!==n&&9!==n?!1:!t||t!==!0&&e.getAttribute("classid")===t};var kt=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Ct=/([A-Z])/g;it.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?it.cache[e[it.expando]]:e[it.expando],!!e&&!u(e)},data:function(e,t,n){return c(e,t,n)},removeData:function(e,t){return d(e,t)},_data:function(e,t,n){return c(e,t,n,!0)},_removeData:function(e,t){return d(e,t,!0)}}),it.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=it.data(o),1===o.nodeType&&!it._data(o,"parsedAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=it.camelCase(r.slice(5)),l(o,r,i[r])));it._data(o,"parsedAttrs",!0)}return i}return"object"==typeof e?this.each(function(){it.data(this,e)}):arguments.length>1?this.each(function(){it.data(this,e,t)}):o?l(o,e,it.data(o,e)):void 0},removeData:function(e){return this.each(function(){it.removeData(this,e)})}}),it.extend({queue:function(e,t,n){var r;return e?(t=(t||"fx")+"queue",r=it._data(e,t),n&&(!r||it.isArray(n)?r=it._data(e,t,it.makeArray(n)):r.push(n)),r||[]):void 0},dequeue:function(e,t){t=t||"fx";var n=it.queue(e,t),r=n.length,i=n.shift(),o=it._queueHooks(e,t),a=function(){it.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return it._data(e,n)||it._data(e,n,{empty:it.Callbacks("once memory").add(function(){it._removeData(e,t+"queue"),it._removeData(e,n)})})}}),it.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length<n?it.queue(this[0],e):void 0===t?this:this.each(function(){var n=it.queue(this,e,t);it._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&it.dequeue(this,e)})},dequeue:function(e){return this.each(function(){it.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=it.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};for("string"!=typeof e&&(t=e,e=void 0),e=e||"fx";a--;)n=it._data(o[a],e+"queueHooks"),n&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var Nt=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,St=["Top","Right","Bottom","Left"],jt=function(e,t){return e=t||e,"none"===it.css(e,"display")||!it.contains(e.ownerDocument,e)},At=it.access=function(e,t,n,r,i,o,a){var s=0,l=e.length,u=null==n;if("object"===it.type(n)){i=!0;for(s in n)it.access(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,it.isFunction(r)||(a=!0),u&&(a?(t.call(e,r),t=null):(u=t,t=function(e,t,n){return u.call(it(e),n)})),t))for(;l>s;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:u?t.call(e):l?t(e[0],n):o},Dt=/^(?:checkbox|radio)$/i;!function(){var e=ht.createElement("input"),t=ht.createElement("div"),n=ht.createDocumentFragment();if(t.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",nt.leadingWhitespace=3===t.firstChild.nodeType,nt.tbody=!t.getElementsByTagName("tbody").length,nt.htmlSerialize=!!t.getElementsByTagName("link").length,nt.html5Clone="<:nav></:nav>"!==ht.createElement("nav").cloneNode(!0).outerHTML,e.type="checkbox",e.checked=!0,n.appendChild(e),nt.appendChecked=e.checked,t.innerHTML="<textarea>x</textarea>",nt.noCloneChecked=!!t.cloneNode(!0).lastChild.defaultValue,n.appendChild(t),t.innerHTML="<input type='radio' checked='checked' name='t'/>",nt.checkClone=t.cloneNode(!0).cloneNode(!0).lastChild.checked,nt.noCloneEvent=!0,t.attachEvent&&(t.attachEvent("onclick",function(){nt.noCloneEvent=!1}),t.cloneNode(!0).click()),null==nt.deleteExpando){nt.deleteExpando=!0;try{delete t.test}catch(r){nt.deleteExpando=!1}}}(),function(){var t,n,r=ht.createElement("div");for(t in{submit:!0,change:!0,focusin:!0})n="on"+t,(nt[t+"Bubbles"]=n in e)||(r.setAttribute(n,"t"),nt[t+"Bubbles"]=r.attributes[n].expando===!1);r=null}();var Lt=/^(?:input|select|textarea)$/i,Ht=/^key/,_t=/^(?:mouse|pointer|contextmenu)|click/,qt=/^(?:focusinfocus|focusoutblur)$/,Ft=/^([^.]*)(?:\.(.+)|)$/;it.event={global:{},add:function(e,t,n,r,i){var o,a,s,l,u,c,d,f,p,h,m,g=it._data(e);if(g){for(n.handler&&(l=n,n=l.handler,i=l.selector),n.guid||(n.guid=it.guid++),(a=g.events)||(a=g.events={}),(c=g.handle)||(c=g.handle=function(e){return typeof it===Et||e&&it.event.triggered===e.type?void 0:it.event.dispatch.apply(c.elem,arguments)},c.elem=e),t=(t||"").match(bt)||[""],s=t.length;s--;)o=Ft.exec(t[s])||[],p=m=o[1],h=(o[2]||"").split(".").sort(),p&&(u=it.event.special[p]||{},p=(i?u.delegateType:u.bindType)||p,u=it.event.special[p]||{},d=it.extend({type:p,origType:m,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&it.expr.match.needsContext.test(i),namespace:h.join(".")},l),(f=a[p])||(f=a[p]=[],f.delegateCount=0,u.setup&&u.setup.call(e,r,h,c)!==!1||(e.addEventListener?e.addEventListener(p,c,!1):e.attachEvent&&e.attachEvent("on"+p,c))),u.add&&(u.add.call(e,d),d.handler.guid||(d.handler.guid=n.guid)),i?f.splice(f.delegateCount++,0,d):f.push(d),it.event.global[p]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,d,f,p,h,m,g=it.hasData(e)&&it._data(e);if(g&&(c=g.events)){for(t=(t||"").match(bt)||[""],u=t.length;u--;)if(s=Ft.exec(t[u])||[],p=m=s[1],h=(s[2]||"").split(".").sort(),p){for(d=it.event.special[p]||{},p=(r?d.delegateType:d.bindType)||p,f=c[p]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;o--;)a=f[o],!i&&m!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,d.remove&&d.remove.call(e,a));l&&!f.length&&(d.teardown&&d.teardown.call(e,h,g.handle)!==!1||it.removeEvent(e,p,g.handle),delete c[p])}else for(p in c)it.event.remove(e,p+t[u],n,r,!0);it.isEmptyObject(c)&&(delete g.handle,it._removeData(e,"events"))}},trigger:function(t,n,r,i){var o,a,s,l,u,c,d,f=[r||ht],p=tt.call(t,"type")?t.type:t,h=tt.call(t,"namespace")?t.namespace.split("."):[];if(s=c=r=r||ht,3!==r.nodeType&&8!==r.nodeType&&!qt.test(p+it.event.triggered)&&(p.indexOf(".")>=0&&(h=p.split("."),p=h.shift(),h.sort()),a=p.indexOf(":")<0&&"on"+p,t=t[it.expando]?t:new it.Event(p,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.namespace_re=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:it.makeArray(n,[t]),u=it.event.special[p]||{},i||!u.trigger||u.trigger.apply(r,n)!==!1)){if(!i&&!u.noBubble&&!it.isWindow(r)){for(l=u.delegateType||p,qt.test(l+p)||(s=s.parentNode);s;s=s.parentNode)f.push(s),c=s;c===(r.ownerDocument||ht)&&f.push(c.defaultView||c.parentWindow||e)}for(d=0;(s=f[d++])&&!t.isPropagationStopped();)t.type=d>1?l:u.bindType||p,o=(it._data(s,"events")||{})[t.type]&&it._data(s,"handle"),o&&o.apply(s,n),o=a&&s[a],o&&o.apply&&it.acceptData(s)&&(t.result=o.apply(s,n),t.result===!1&&t.preventDefault());if(t.type=p,!i&&!t.isDefaultPrevented()&&(!u._default||u._default.apply(f.pop(),n)===!1)&&it.acceptData(r)&&a&&r[p]&&!it.isWindow(r)){c=r[a],c&&(r[a]=null),it.event.triggered=p;try{r[p]()}catch(m){}it.event.triggered=void 0,c&&(r[a]=c)}return t.result}},dispatch:function(e){e=it.event.fix(e);var t,n,r,i,o,a=[],s=Y.call(arguments),l=(it._data(this,"events")||{})[e.type]||[],u=it.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!u.preDispatch||u.preDispatch.call(this,e)!==!1){for(a=it.event.handlers.call(this,e,l),t=0;(i=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=i.elem,o=0;(r=i.handlers[o++])&&!e.isImmediatePropagationStopped();)(!e.namespace_re||e.namespace_re.test(r.namespace))&&(e.handleObj=r,e.data=r.data,n=((it.event.special[r.origType]||{}).handle||r.handler).apply(i.elem,s),void 0!==n&&(e.result=n)===!1&&(e.preventDefault(),e.stopPropagation()));return u.postDispatch&&u.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,a=[],s=t.delegateCount,l=e.target;if(s&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(i=[],o=0;s>o;o++)r=t[o],n=r.selector+" ",void 0===i[n]&&(i[n]=r.needsContext?it(n,this).index(l)>=0:it.find(n,this,null,[l]).length),i[n]&&i.push(r);i.length&&a.push({elem:l,handlers:i})}return s<t.length&&a.push({elem:this,handlers:t.slice(s)}),a},fix:function(e){if(e[it.expando])return e;var t,n,r,i=e.type,o=e,a=this.fixHooks[i];for(a||(this.fixHooks[i]=a=_t.test(i)?this.mouseHooks:Ht.test(i)?this.keyHooks:{}),r=a.props?this.props.concat(a.props):this.props,e=new it.Event(o),t=r.length;t--;)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||ht),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,a.filter?a.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,t){var n,r,i,o=t.button,a=t.fromElement;return null==e.pageX&&null!=t.clientX&&(r=e.target.ownerDocument||ht,i=r.documentElement,n=r.body,e.pageX=t.clientX+(i&&i.scrollLeft||n&&n.scrollLeft||0)-(i&&i.clientLeft||n&&n.clientLeft||0),e.pageY=t.clientY+(i&&i.scrollTop||n&&n.scrollTop||0)-(i&&i.clientTop||n&&n.clientTop||0)),!e.relatedTarget&&a&&(e.relatedTarget=a===e.target?t.toElement:a),e.which||void 0===o||(e.which=1&o?1:2&o?3:4&o?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==h()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===h()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return it.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(e){return it.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=it.extend(new it.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?it.event.trigger(i,null,t):it.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},it.removeEvent=ht.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===Et&&(e[r]=null),e.detachEvent(r,n))},it.Event=function(e,t){return this instanceof it.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&e.returnValue===!1?f:p):this.type=e,t&&it.extend(this,t),this.timeStamp=e&&e.timeStamp||it.now(),void(this[it.expando]=!0)):new it.Event(e,t)},it.Event.prototype={isDefaultPrevented:p,isPropagationStopped:p,isImmediatePropagationStopped:p,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=f,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=f,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=f,e&&e.stopImmediatePropagation&&e.stopImmediatePropagation(),this.stopPropagation()}},it.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,t){it.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!it.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),nt.submitBubbles||(it.event.special.submit={setup:function(){return it.nodeName(this,"form")?!1:void it.event.add(this,"click._submit keypress._submit",function(e){var t=e.target,n=it.nodeName(t,"input")||it.nodeName(t,"button")?t.form:void 0;n&&!it._data(n,"submitBubbles")&&(it.event.add(n,"submit._submit",function(e){e._submit_bubble=!0}),it._data(n,"submitBubbles",!0))})},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&it.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return it.nodeName(this,"form")?!1:void it.event.remove(this,"._submit")}}),nt.changeBubbles||(it.event.special.change={setup:function(){return Lt.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(it.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),it.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),it.event.simulate("change",this,e,!0)})),!1):void it.event.add(this,"beforeactivate._change",function(e){var t=e.target;Lt.test(t.nodeName)&&!it._data(t,"changeBubbles")&&(it.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||it.event.simulate("change",this.parentNode,e,!0)}),it._data(t,"changeBubbles",!0))})},handle:function(e){var t=e.target;return this!==t||e.isSimulated||e.isTrigger||"radio"!==t.type&&"checkbox"!==t.type?e.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return it.event.remove(this,"._change"),!Lt.test(this.nodeName)}}),nt.focusinBubbles||it.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){it.event.simulate(t,e.target,it.event.fix(e),!0)};it.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=it._data(r,t);i||r.addEventListener(e,n,!0),it._data(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=it._data(r,t)-1;i?it._data(r,t,i):(r.removeEventListener(e,n,!0),it._removeData(r,t))}}}),it.fn.extend({on:function(e,t,n,r,i){var o,a;if("object"==typeof e){"string"!=typeof t&&(n=n||t,t=void 0);for(o in e)this.on(o,t,n,e[o],i);return this}if(null==n&&null==r?(r=t,n=t=void 0):null==r&&("string"==typeof t?(r=n,n=void 0):(r=n,n=t,t=void 0)),r===!1)r=p;else if(!r)return this;return 1===i&&(a=r,r=function(e){return it().off(e),a.apply(this,arguments)},r.guid=a.guid||(a.guid=it.guid++)),this.each(function(){it.event.add(this,e,r,n,t)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,it(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return(t===!1||"function"==typeof t)&&(n=t,t=void 0),n===!1&&(n=p),this.each(function(){it.event.remove(this,e,n,t)})},trigger:function(e,t){return this.each(function(){it.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?it.event.trigger(e,t,n,!0):void 0}});var Mt="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",Ot=/ jQuery\d+="(?:null|\d+)"/g,Rt=new RegExp("<(?:"+Mt+")[\\s/>]","i"),Bt=/^\s+/,Pt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,It=/<([\w:]+)/,Wt=/<tbody/i,$t=/<|&#?\w+;/,zt=/<(?:script|style|link)/i,Xt=/checked\s*(?:[^=]|=\s*.checked.)/i,Ut=/^$|\/(?:java|ecma)script/i,Vt=/^true\/(.*)/,Gt=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,Yt={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:nt.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},Jt=m(ht),Kt=Jt.appendChild(ht.createElement("div"));Yt.optgroup=Yt.option,Yt.tbody=Yt.tfoot=Yt.colgroup=Yt.caption=Yt.thead,Yt.th=Yt.td,it.extend({clone:function(e,t,n){var r,i,o,a,s,l=it.contains(e.ownerDocument,e);if(nt.html5Clone||it.isXMLDoc(e)||!Rt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Kt.innerHTML=e.outerHTML,Kt.removeChild(o=Kt.firstChild)),!(nt.noCloneEvent&&nt.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||it.isXMLDoc(e)))for(r=g(o),s=g(e),a=0;null!=(i=s[a]);++a)r[a]&&E(i,r[a]);if(t)if(n)for(s=s||g(e),r=r||g(o),a=0;null!=(i=s[a]);a++)T(i,r[a]);else T(e,o);return r=g(o,"script"),r.length>0&&w(r,!l&&g(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){for(var i,o,a,s,l,u,c,d=e.length,f=m(t),p=[],h=0;d>h;h++)if(o=e[h],o||0===o)if("object"===it.type(o))it.merge(p,o.nodeType?[o]:o);else if($t.test(o)){for(s=s||f.appendChild(t.createElement("div")),l=(It.exec(o)||["",""])[1].toLowerCase(),c=Yt[l]||Yt._default,s.innerHTML=c[1]+o.replace(Pt,"<$1></$2>")+c[2],i=c[0];i--;)s=s.lastChild;if(!nt.leadingWhitespace&&Bt.test(o)&&p.push(t.createTextNode(Bt.exec(o)[0])),!nt.tbody)for(o="table"!==l||Wt.test(o)?"<table>"!==c[1]||Wt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;i--;)it.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u);for(it.merge(p,s.childNodes),s.textContent="";s.firstChild;)s.removeChild(s.firstChild);s=f.lastChild}else p.push(t.createTextNode(o));for(s&&f.removeChild(s),nt.appendChecked||it.grep(g(p,"input"),v),h=0;o=p[h++];)if((!r||-1===it.inArray(o,r))&&(a=it.contains(o.ownerDocument,o),s=g(f.appendChild(o),"script"),a&&w(s),n))for(i=0;o=s[i++];)Ut.test(o.type||"")&&n.push(o);return s=null,f},cleanData:function(e,t){for(var n,r,i,o,a=0,s=it.expando,l=it.cache,u=nt.deleteExpando,c=it.event.special;null!=(n=e[a]);a++)if((t||it.acceptData(n))&&(i=n[s],o=i&&l[i])){if(o.events)for(r in o.events)c[r]?it.event.remove(n,r):it.removeEvent(n,r,o.handle);l[i]&&(delete l[i],u?delete n[s]:typeof n.removeAttribute!==Et?n.removeAttribute(s):n[s]=null,G.push(i))}}}),it.fn.extend({text:function(e){return At(this,function(e){return void 0===e?it.text(this):this.empty().append((this[0]&&this[0].ownerDocument||ht).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=y(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=y(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){for(var n,r=e?it.filter(e,this):this,i=0;null!=(n=r[i]);i++)t||1!==n.nodeType||it.cleanData(g(n)),n.parentNode&&(t&&it.contains(n.ownerDocument,n)&&w(g(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&it.cleanData(g(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&it.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return it.clone(this,e,t)})},html:function(e){return At(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e)return 1===t.nodeType?t.innerHTML.replace(Ot,""):void 0;if(!("string"!=typeof e||zt.test(e)||!nt.htmlSerialize&&Rt.test(e)||!nt.leadingWhitespace&&Bt.test(e)||Yt[(It.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(Pt,"<$1></$2>");try{for(;r>n;n++)t=this[n]||{},1===t.nodeType&&(it.cleanData(g(t,!1)),t.innerHTML=e);t=0}catch(i){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=arguments[0];return this.domManip(arguments,function(t){e=this.parentNode,it.cleanData(g(this)),e&&e.replaceChild(t,this)}),e&&(e.length||e.nodeType)?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t){e=J.apply([],e);var n,r,i,o,a,s,l=0,u=this.length,c=this,d=u-1,f=e[0],p=it.isFunction(f);if(p||u>1&&"string"==typeof f&&!nt.checkClone&&Xt.test(f))return this.each(function(n){var r=c.eq(n);p&&(e[0]=f.call(this,n,r.html())),r.domManip(e,t)});if(u&&(s=it.buildFragment(e,this[0].ownerDocument,!1,this),n=s.firstChild,1===s.childNodes.length&&(s=n),n)){for(o=it.map(g(s,"script"),b),i=o.length;u>l;l++)r=s,l!==d&&(r=it.clone(r,!0,!0),i&&it.merge(o,g(r,"script"))),t.call(this[l],r,l);if(i)for(a=o[o.length-1].ownerDocument,it.map(o,x),l=0;i>l;l++)r=o[l],Ut.test(r.type||"")&&!it._data(r,"globalEval")&&it.contains(a,r)&&(r.src?it._evalUrl&&it._evalUrl(r.src):it.globalEval((r.text||r.textContent||r.innerHTML||"").replace(Gt,"")));s=n=null}return this}}),it.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){it.fn[e]=function(e){for(var n,r=0,i=[],o=it(e),a=o.length-1;a>=r;r++)n=r===a?this:this.clone(!0),it(o[r])[t](n),K.apply(i,n.get());return this.pushStack(i)}});var Qt,Zt={};!function(){var e;nt.shrinkWrapBlocks=function(){if(null!=e)return e;e=!1;var t,n,r;return n=ht.getElementsByTagName("body")[0],n&&n.style?(t=ht.createElement("div"),r=ht.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),typeof t.style.zoom!==Et&&(t.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",t.appendChild(ht.createElement("div")).style.width="5px",e=3!==t.offsetWidth),n.removeChild(r),e):void 0}}();var en,tn,nn=/^margin/,rn=new RegExp("^("+Nt+")(?!px)[a-z%]+$","i"),on=/^(top|right|bottom|left)$/;e.getComputedStyle?(en=function(e){return e.ownerDocument.defaultView.getComputedStyle(e,null)},tn=function(e,t,n){var r,i,o,a,s=e.style;return n=n||en(e),a=n?n.getPropertyValue(t)||n[t]:void 0,n&&(""!==a||it.contains(e.ownerDocument,e)||(a=it.style(e,t)),rn.test(a)&&nn.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0===a?a:a+""}):ht.documentElement.currentStyle&&(en=function(e){return e.currentStyle},tn=function(e,t,n){var r,i,o,a,s=e.style;return n=n||en(e),a=n?n[t]:void 0,null==a&&s&&s[t]&&(a=s[t]),rn.test(a)&&!on.test(t)&&(r=s.left,i=e.runtimeStyle,o=i&&i.left,o&&(i.left=e.currentStyle.left),s.left="fontSize"===t?"1em":a,a=s.pixelLeft+"px",s.left=r,o&&(i.left=o)),void 0===a?a:a+""||"auto"}),function(){function t(){var t,n,r,i;n=ht.getElementsByTagName("body")[0],n&&n.style&&(t=ht.createElement("div"),r=ht.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),t.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",o=a=!1,l=!0,e.getComputedStyle&&(o="1%"!==(e.getComputedStyle(t,null)||{}).top,a="4px"===(e.getComputedStyle(t,null)||{width:"4px"}).width,i=t.appendChild(ht.createElement("div")),i.style.cssText=t.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",t.style.width="1px",l=!parseFloat((e.getComputedStyle(i,null)||{}).marginRight)),t.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=t.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",s=0===i[0].offsetHeight,s&&(i[0].style.display="",i[1].style.display="none",s=0===i[0].offsetHeight),n.removeChild(r))}var n,r,i,o,a,s,l;n=ht.createElement("div"),n.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",i=n.getElementsByTagName("a")[0],r=i&&i.style,r&&(r.cssText="float:left;opacity:.5",nt.opacity="0.5"===r.opacity,nt.cssFloat=!!r.cssFloat,n.style.backgroundClip="content-box",n.cloneNode(!0).style.backgroundClip="",nt.clearCloneStyle="content-box"===n.style.backgroundClip,nt.boxSizing=""===r.boxSizing||""===r.MozBoxSizing||""===r.WebkitBoxSizing,it.extend(nt,{reliableHiddenOffsets:function(){return null==s&&t(),s +},boxSizingReliable:function(){return null==a&&t(),a},pixelPosition:function(){return null==o&&t(),o},reliableMarginRight:function(){return null==l&&t(),l}}))}(),it.swap=function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i};var an=/alpha\([^)]*\)/i,sn=/opacity\s*=\s*([^)]*)/,ln=/^(none|table(?!-c[ea]).+)/,un=new RegExp("^("+Nt+")(.*)$","i"),cn=new RegExp("^([+-])=("+Nt+")","i"),dn={position:"absolute",visibility:"hidden",display:"block"},fn={letterSpacing:"0",fontWeight:"400"},pn=["Webkit","O","Moz","ms"];it.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=tn(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":nt.cssFloat?"cssFloat":"styleFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=it.camelCase(t),l=e.style;if(t=it.cssProps[s]||(it.cssProps[s]=S(l,s)),a=it.cssHooks[t]||it.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];if(o=typeof n,"string"===o&&(i=cn.exec(n))&&(n=(i[1]+1)*i[2]+parseFloat(it.css(e,t)),o="number"),null!=n&&n===n&&("number"!==o||it.cssNumber[s]||(n+="px"),nt.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),!(a&&"set"in a&&void 0===(n=a.set(e,n,r)))))try{l[t]=n}catch(u){}}},css:function(e,t,n,r){var i,o,a,s=it.camelCase(t);return t=it.cssProps[s]||(it.cssProps[s]=S(e.style,s)),a=it.cssHooks[t]||it.cssHooks[s],a&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=tn(e,t,r)),"normal"===o&&t in fn&&(o=fn[t]),""===n||n?(i=parseFloat(o),n===!0||it.isNumeric(i)?i||0:o):o}}),it.each(["height","width"],function(e,t){it.cssHooks[t]={get:function(e,n,r){return n?ln.test(it.css(e,"display"))&&0===e.offsetWidth?it.swap(e,dn,function(){return L(e,t,r)}):L(e,t,r):void 0},set:function(e,n,r){var i=r&&en(e);return A(e,n,r?D(e,t,r,nt.boxSizing&&"border-box"===it.css(e,"boxSizing",!1,i),i):0)}}}),nt.opacity||(it.cssHooks.opacity={get:function(e,t){return sn.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=it.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===it.trim(o.replace(an,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=an.test(o)?o.replace(an,i):o+" "+i)}}),it.cssHooks.marginRight=N(nt.reliableMarginRight,function(e,t){return t?it.swap(e,{display:"inline-block"},tn,[e,"marginRight"]):void 0}),it.each({margin:"",padding:"",border:"Width"},function(e,t){it.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];4>r;r++)i[e+St[r]+t]=o[r]||o[r-2]||o[0];return i}},nn.test(e)||(it.cssHooks[e+t].set=A)}),it.fn.extend({css:function(e,t){return At(this,function(e,t,n){var r,i,o={},a=0;if(it.isArray(t)){for(r=en(e),i=t.length;i>a;a++)o[t[a]]=it.css(e,t[a],!1,r);return o}return void 0!==n?it.style(e,t,n):it.css(e,t)},e,t,arguments.length>1)},show:function(){return j(this,!0)},hide:function(){return j(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){jt(this)?it(this).show():it(this).hide()})}}),it.Tween=H,H.prototype={constructor:H,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(it.cssNumber[n]?"":"px")},cur:function(){var e=H.propHooks[this.prop];return e&&e.get?e.get(this):H.propHooks._default.get(this)},run:function(e){var t,n=H.propHooks[this.prop];return this.pos=t=this.options.duration?it.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):H.propHooks._default.set(this),this}},H.prototype.init.prototype=H.prototype,H.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=it.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){it.fx.step[e.prop]?it.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[it.cssProps[e.prop]]||it.cssHooks[e.prop])?it.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},H.propHooks.scrollTop=H.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},it.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},it.fx=H.prototype.init,it.fx.step={};var hn,mn,gn=/^(?:toggle|show|hide)$/,vn=new RegExp("^(?:([+-])=|)("+Nt+")([a-z%]*)$","i"),yn=/queueHooks$/,bn=[M],xn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=vn.exec(t),o=i&&i[3]||(it.cssNumber[e]?"":"px"),a=(it.cssNumber[e]||"px"!==o&&+r)&&vn.exec(it.css(n.elem,e)),s=1,l=20;if(a&&a[3]!==o){o=o||a[3],i=i||[],a=+r||1;do s=s||".5",a/=s,it.style(n.elem,e,a+o);while(s!==(s=n.cur()/r)&&1!==s&&--l)}return i&&(a=n.start=+a||+r||0,n.unit=o,n.end=i[1]?a+(i[1]+1)*i[2]:+i[2]),n}]};it.Animation=it.extend(R,{tweener:function(e,t){it.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");for(var n,r=0,i=e.length;i>r;r++)n=e[r],xn[n]=xn[n]||[],xn[n].unshift(t)},prefilter:function(e,t){t?bn.unshift(e):bn.push(e)}}),it.speed=function(e,t,n){var r=e&&"object"==typeof e?it.extend({},e):{complete:n||!n&&t||it.isFunction(e)&&e,duration:e,easing:n&&t||t&&!it.isFunction(t)&&t};return r.duration=it.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in it.fx.speeds?it.fx.speeds[r.duration]:it.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){it.isFunction(r.old)&&r.old.call(this),r.queue&&it.dequeue(this,r.queue)},r},it.fn.extend({fadeTo:function(e,t,n,r){return this.filter(jt).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=it.isEmptyObject(e),o=it.speed(t,n,r),a=function(){var t=R(this,it.extend({},e),o);(i||it._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=void 0),t&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=it.timers,a=it._data(this);if(i)a[i]&&a[i].stop&&r(a[i]);else for(i in a)a[i]&&a[i].stop&&yn.test(i)&&r(a[i]);for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));(t||!n)&&it.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=it._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=it.timers,a=r?r.length:0;for(n.finish=!0,it.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),it.each(["toggle","show","hide"],function(e,t){var n=it.fn[t];it.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(q(t,!0),e,r,i)}}),it.each({slideDown:q("show"),slideUp:q("hide"),slideToggle:q("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){it.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),it.timers=[],it.fx.tick=function(){var e,t=it.timers,n=0;for(hn=it.now();n<t.length;n++)e=t[n],e()||t[n]!==e||t.splice(n--,1);t.length||it.fx.stop(),hn=void 0},it.fx.timer=function(e){it.timers.push(e),e()?it.fx.start():it.timers.pop()},it.fx.interval=13,it.fx.start=function(){mn||(mn=setInterval(it.fx.tick,it.fx.interval))},it.fx.stop=function(){clearInterval(mn),mn=null},it.fx.speeds={slow:600,fast:200,_default:400},it.fn.delay=function(e,t){return e=it.fx?it.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},function(){var e,t,n,r,i;t=ht.createElement("div"),t.setAttribute("className","t"),t.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",r=t.getElementsByTagName("a")[0],n=ht.createElement("select"),i=n.appendChild(ht.createElement("option")),e=t.getElementsByTagName("input")[0],r.style.cssText="top:1px",nt.getSetAttribute="t"!==t.className,nt.style=/top/.test(r.getAttribute("style")),nt.hrefNormalized="/a"===r.getAttribute("href"),nt.checkOn=!!e.value,nt.optSelected=i.selected,nt.enctype=!!ht.createElement("form").enctype,n.disabled=!0,nt.optDisabled=!i.disabled,e=ht.createElement("input"),e.setAttribute("value",""),nt.input=""===e.getAttribute("value"),e.value="t",e.setAttribute("type","radio"),nt.radioValue="t"===e.value}();var wn=/\r/g;it.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=it.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,it(this).val()):e,null==i?i="":"number"==typeof i?i+="":it.isArray(i)&&(i=it.map(i,function(e){return null==e?"":e+""})),t=it.valHooks[this.type]||it.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return t=it.valHooks[i.type]||it.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(wn,""):null==n?"":n)}}}),it.extend({valHooks:{option:{get:function(e){var t=it.find.attr(e,"value");return null!=t?t:it.trim(it.text(e))}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(nt.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&it.nodeName(n.parentNode,"optgroup"))){if(t=it(n).val(),o)return t;a.push(t)}return a},set:function(e,t){for(var n,r,i=e.options,o=it.makeArray(t),a=i.length;a--;)if(r=i[a],it.inArray(it.valHooks.option.get(r),o)>=0)try{r.selected=n=!0}catch(s){r.scrollHeight}else r.selected=!1;return n||(e.selectedIndex=-1),i}}}}),it.each(["radio","checkbox"],function(){it.valHooks[this]={set:function(e,t){return it.isArray(t)?e.checked=it.inArray(it(e).val(),t)>=0:void 0}},nt.checkOn||(it.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Tn,En,kn=it.expr.attrHandle,Cn=/^(?:checked|selected)$/i,Nn=nt.getSetAttribute,Sn=nt.input;it.fn.extend({attr:function(e,t){return At(this,it.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){it.removeAttr(this,e)})}}),it.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(e&&3!==o&&8!==o&&2!==o)return typeof e.getAttribute===Et?it.prop(e,t,n):(1===o&&it.isXMLDoc(e)||(t=t.toLowerCase(),r=it.attrHooks[t]||(it.expr.match.bool.test(t)?En:Tn)),void 0===n?r&&"get"in r&&null!==(i=r.get(e,t))?i:(i=it.find.attr(e,t),null==i?void 0:i):null!==n?r&&"set"in r&&void 0!==(i=r.set(e,n,t))?i:(e.setAttribute(t,n+""),n):void it.removeAttr(e,t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(bt);if(o&&1===e.nodeType)for(;n=o[i++];)r=it.propFix[n]||n,it.expr.match.bool.test(n)?Sn&&Nn||!Cn.test(n)?e[r]=!1:e[it.camelCase("default-"+n)]=e[r]=!1:it.attr(e,n,""),e.removeAttribute(Nn?n:r)},attrHooks:{type:{set:function(e,t){if(!nt.radioValue&&"radio"===t&&it.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}}}),En={set:function(e,t,n){return t===!1?it.removeAttr(e,n):Sn&&Nn||!Cn.test(n)?e.setAttribute(!Nn&&it.propFix[n]||n,n):e[it.camelCase("default-"+n)]=e[n]=!0,n}},it.each(it.expr.match.bool.source.match(/\w+/g),function(e,t){var n=kn[t]||it.find.attr;kn[t]=Sn&&Nn||!Cn.test(t)?function(e,t,r){var i,o;return r||(o=kn[t],kn[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,kn[t]=o),i}:function(e,t,n){return n?void 0:e[it.camelCase("default-"+t)]?t.toLowerCase():null}}),Sn&&Nn||(it.attrHooks.value={set:function(e,t,n){return it.nodeName(e,"input")?void(e.defaultValue=t):Tn&&Tn.set(e,t,n)}}),Nn||(Tn={set:function(e,t,n){var r=e.getAttributeNode(n);return r||e.setAttributeNode(r=e.ownerDocument.createAttribute(n)),r.value=t+="","value"===n||t===e.getAttribute(n)?t:void 0}},kn.id=kn.name=kn.coords=function(e,t,n){var r;return n?void 0:(r=e.getAttributeNode(t))&&""!==r.value?r.value:null},it.valHooks.button={get:function(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:void 0},set:Tn.set},it.attrHooks.contenteditable={set:function(e,t,n){Tn.set(e,""===t?!1:t,n)}},it.each(["width","height"],function(e,t){it.attrHooks[t]={set:function(e,n){return""===n?(e.setAttribute(t,"auto"),n):void 0}}})),nt.style||(it.attrHooks.style={get:function(e){return e.style.cssText||void 0},set:function(e,t){return e.style.cssText=t+""}});var jn=/^(?:input|select|textarea|button|object)$/i,An=/^(?:a|area)$/i;it.fn.extend({prop:function(e,t){return At(this,it.prop,e,t,arguments.length>1)},removeProp:function(e){return e=it.propFix[e]||e,this.each(function(){try{this[e]=void 0,delete this[e]}catch(t){}})}}),it.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(e,t,n){var r,i,o,a=e.nodeType;if(e&&3!==a&&8!==a&&2!==a)return o=1!==a||!it.isXMLDoc(e),o&&(t=it.propFix[t]||t,i=it.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=it.find.attr(e,"tabindex");return t?parseInt(t,10):jn.test(e.nodeName)||An.test(e.nodeName)&&e.href?0:-1}}}}),nt.hrefNormalized||it.each(["href","src"],function(e,t){it.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),nt.optSelected||(it.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),it.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){it.propFix[this.toLowerCase()]=this}),nt.enctype||(it.propFix.enctype="encoding");var Dn=/[\t\r\n\f]/g;it.fn.extend({addClass:function(e){var t,n,r,i,o,a,s=0,l=this.length,u="string"==typeof e&&e;if(it.isFunction(e))return this.each(function(t){it(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(bt)||[];l>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(Dn," "):" ")){for(o=0;i=t[o++];)r.indexOf(" "+i+" ")<0&&(r+=i+" ");a=it.trim(r),n.className!==a&&(n.className=a)}return this},removeClass:function(e){var t,n,r,i,o,a,s=0,l=this.length,u=0===arguments.length||"string"==typeof e&&e;if(it.isFunction(e))return this.each(function(t){it(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(bt)||[];l>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(Dn," "):"")){for(o=0;i=t[o++];)for(;r.indexOf(" "+i+" ")>=0;)r=r.replace(" "+i+" "," ");a=e?it.trim(r):"",n.className!==a&&(n.className=a)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):this.each(it.isFunction(e)?function(n){it(this).toggleClass(e.call(this,n,this.className,t),t)}:function(){if("string"===n)for(var t,r=0,i=it(this),o=e.match(bt)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else(n===Et||"boolean"===n)&&(this.className&&it._data(this,"__className__",this.className),this.className=this.className||e===!1?"":it._data(this,"__className__")||"")})},hasClass:function(e){for(var t=" "+e+" ",n=0,r=this.length;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(Dn," ").indexOf(t)>=0)return!0;return!1}}),it.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 contextmenu".split(" "),function(e,t){it.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),it.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var Ln=it.now(),Hn=/\?/,_n=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;it.parseJSON=function(t){if(e.JSON&&e.JSON.parse)return e.JSON.parse(t+"");var n,r=null,i=it.trim(t+"");return i&&!it.trim(i.replace(_n,function(e,t,i,o){return n&&t&&(r=0),0===r?e:(n=i||t,r+=!o-!i,"")}))?Function("return "+i)():it.error("Invalid JSON: "+t)},it.parseXML=function(t){var n,r;if(!t||"string"!=typeof t)return null;try{e.DOMParser?(r=new DOMParser,n=r.parseFromString(t,"text/xml")):(n=new ActiveXObject("Microsoft.XMLDOM"),n.async="false",n.loadXML(t))}catch(i){n=void 0}return n&&n.documentElement&&!n.getElementsByTagName("parsererror").length||it.error("Invalid XML: "+t),n};var qn,Fn,Mn=/#.*$/,On=/([?&])_=[^&]*/,Rn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Bn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Pn=/^(?:GET|HEAD)$/,In=/^\/\//,Wn=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,$n={},zn={},Xn="*/".concat("*");try{Fn=location.href}catch(Un){Fn=ht.createElement("a"),Fn.href="",Fn=Fn.href}qn=Wn.exec(Fn.toLowerCase())||[],it.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Fn,type:"GET",isLocal:Bn.test(qn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Xn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":it.parseJSON,"text xml":it.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?I(I(e,it.ajaxSettings),t):I(it.ajaxSettings,e)},ajaxPrefilter:B($n),ajaxTransport:B(zn),ajax:function(e,t){function n(e,t,n,r){var i,c,v,y,x,T=t;2!==b&&(b=2,s&&clearTimeout(s),u=void 0,a=r||"",w.readyState=e>0?4:0,i=e>=200&&300>e||304===e,n&&(y=W(d,w,n)),y=$(d,y,w,i),i?(d.ifModified&&(x=w.getResponseHeader("Last-Modified"),x&&(it.lastModified[o]=x),x=w.getResponseHeader("etag"),x&&(it.etag[o]=x)),204===e||"HEAD"===d.type?T="nocontent":304===e?T="notmodified":(T=y.state,c=y.data,v=y.error,i=!v)):(v=T,(e||!T)&&(T="error",0>e&&(e=0))),w.status=e,w.statusText=(t||T)+"",i?h.resolveWith(f,[c,T,w]):h.rejectWith(f,[w,T,v]),w.statusCode(g),g=void 0,l&&p.trigger(i?"ajaxSuccess":"ajaxError",[w,d,i?c:v]),m.fireWith(f,[w,T]),l&&(p.trigger("ajaxComplete",[w,d]),--it.active||it.event.trigger("ajaxStop")))}"object"==typeof e&&(t=e,e=void 0),t=t||{};var r,i,o,a,s,l,u,c,d=it.ajaxSetup({},t),f=d.context||d,p=d.context&&(f.nodeType||f.jquery)?it(f):it.event,h=it.Deferred(),m=it.Callbacks("once memory"),g=d.statusCode||{},v={},y={},b=0,x="canceled",w={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!c)for(c={};t=Rn.exec(a);)c[t[1].toLowerCase()]=t[2];t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=y[n]=y[n]||e,v[e]=t),this},overrideMimeType:function(e){return b||(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>b)for(t in e)g[t]=[g[t],e[t]];else w.always(e[w.status]);return this},abort:function(e){var t=e||x;return u&&u.abort(t),n(0,t),this}};if(h.promise(w).complete=m.add,w.success=w.done,w.error=w.fail,d.url=((e||d.url||Fn)+"").replace(Mn,"").replace(In,qn[1]+"//"),d.type=t.method||t.type||d.method||d.type,d.dataTypes=it.trim(d.dataType||"*").toLowerCase().match(bt)||[""],null==d.crossDomain&&(r=Wn.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]===qn[1]&&r[2]===qn[2]&&(r[3]||("http:"===r[1]?"80":"443"))===(qn[3]||("http:"===qn[1]?"80":"443")))),d.data&&d.processData&&"string"!=typeof d.data&&(d.data=it.param(d.data,d.traditional)),P($n,d,t,w),2===b)return w;l=d.global,l&&0===it.active++&&it.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Pn.test(d.type),o=d.url,d.hasContent||(d.data&&(o=d.url+=(Hn.test(o)?"&":"?")+d.data,delete d.data),d.cache===!1&&(d.url=On.test(o)?o.replace(On,"$1_="+Ln++):o+(Hn.test(o)?"&":"?")+"_="+Ln++)),d.ifModified&&(it.lastModified[o]&&w.setRequestHeader("If-Modified-Since",it.lastModified[o]),it.etag[o]&&w.setRequestHeader("If-None-Match",it.etag[o])),(d.data&&d.hasContent&&d.contentType!==!1||t.contentType)&&w.setRequestHeader("Content-Type",d.contentType),w.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Xn+"; q=0.01":""):d.accepts["*"]);for(i in d.headers)w.setRequestHeader(i,d.headers[i]);if(d.beforeSend&&(d.beforeSend.call(f,w,d)===!1||2===b))return w.abort();x="abort";for(i in{success:1,error:1,complete:1})w[i](d[i]);if(u=P(zn,d,t,w)){w.readyState=1,l&&p.trigger("ajaxSend",[w,d]),d.async&&d.timeout>0&&(s=setTimeout(function(){w.abort("timeout")},d.timeout));try{b=1,u.send(v,n)}catch(T){if(!(2>b))throw T;n(-1,T)}}else n(-1,"No Transport");return w},getJSON:function(e,t,n){return it.get(e,t,n,"json")},getScript:function(e,t){return it.get(e,void 0,t,"script")}}),it.each(["get","post"],function(e,t){it[t]=function(e,n,r,i){return it.isFunction(n)&&(i=i||r,r=n,n=void 0),it.ajax({url:e,type:t,dataType:i,data:n,success:r})}}),it.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){it.fn[t]=function(e){return this.on(t,e)}}),it._evalUrl=function(e){return it.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},it.fn.extend({wrapAll:function(e){if(it.isFunction(e))return this.each(function(t){it(this).wrapAll(e.call(this,t))});if(this[0]){var t=it(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return this.each(it.isFunction(e)?function(t){it(this).wrapInner(e.call(this,t))}:function(){var t=it(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=it.isFunction(e);return this.each(function(n){it(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){it.nodeName(this,"body")||it(this).replaceWith(this.childNodes)}).end()}}),it.expr.filters.hidden=function(e){return e.offsetWidth<=0&&e.offsetHeight<=0||!nt.reliableHiddenOffsets()&&"none"===(e.style&&e.style.display||it.css(e,"display"))},it.expr.filters.visible=function(e){return!it.expr.filters.hidden(e)};var Vn=/%20/g,Gn=/\[\]$/,Yn=/\r?\n/g,Jn=/^(?:submit|button|image|reset|file)$/i,Kn=/^(?:input|select|textarea|keygen)/i;it.param=function(e,t){var n,r=[],i=function(e,t){t=it.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=it.ajaxSettings&&it.ajaxSettings.traditional),it.isArray(e)||e.jquery&&!it.isPlainObject(e))it.each(e,function(){i(this.name,this.value)});else for(n in e)z(n,e[n],t,i);return r.join("&").replace(Vn,"+")},it.fn.extend({serialize:function(){return it.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=it.prop(this,"elements");return e?it.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!it(this).is(":disabled")&&Kn.test(this.nodeName)&&!Jn.test(e)&&(this.checked||!Dt.test(e))}).map(function(e,t){var n=it(this).val();return null==n?null:it.isArray(n)?it.map(n,function(e){return{name:t.name,value:e.replace(Yn,"\r\n")}}):{name:t.name,value:n.replace(Yn,"\r\n")}}).get()}}),it.ajaxSettings.xhr=void 0!==e.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&X()||U()}:X;var Qn=0,Zn={},er=it.ajaxSettings.xhr();e.ActiveXObject&&it(e).on("unload",function(){for(var e in Zn)Zn[e](void 0,!0)}),nt.cors=!!er&&"withCredentials"in er,er=nt.ajax=!!er,er&&it.ajaxTransport(function(e){if(!e.crossDomain||nt.cors){var t;return{send:function(n,r){var i,o=e.xhr(),a=++Qn;if(o.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(i in e.xhrFields)o[i]=e.xhrFields[i];e.mimeType&&o.overrideMimeType&&o.overrideMimeType(e.mimeType),e.crossDomain||n["X-Requested-With"]||(n["X-Requested-With"]="XMLHttpRequest");for(i in n)void 0!==n[i]&&o.setRequestHeader(i,n[i]+"");o.send(e.hasContent&&e.data||null),t=function(n,i){var s,l,u;if(t&&(i||4===o.readyState))if(delete Zn[a],t=void 0,o.onreadystatechange=it.noop,i)4!==o.readyState&&o.abort();else{u={},s=o.status,"string"==typeof o.responseText&&(u.text=o.responseText);try{l=o.statusText}catch(c){l=""}s||!e.isLocal||e.crossDomain?1223===s&&(s=204):s=u.text?200:404}u&&r(s,l,u,o.getAllResponseHeaders())},e.async?4===o.readyState?setTimeout(t):o.onreadystatechange=Zn[a]=t:t()},abort:function(){t&&t(void 0,!0)}}}}),it.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return it.globalEval(e),e}}}),it.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),it.ajaxTransport("script",function(e){if(e.crossDomain){var t,n=ht.head||it("head")[0]||ht.documentElement;return{send:function(r,i){t=ht.createElement("script"),t.async=!0,e.scriptCharset&&(t.charset=e.scriptCharset),t.src=e.url,t.onload=t.onreadystatechange=function(e,n){(n||!t.readyState||/loaded|complete/.test(t.readyState))&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),t=null,n||i(200,"success"))},n.insertBefore(t,n.firstChild)},abort:function(){t&&t.onload(void 0,!0)}}}});var tr=[],nr=/(=)\?(?=&|$)|\?\?/;it.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=tr.pop()||it.expando+"_"+Ln++;return this[e]=!0,e}}),it.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=t.jsonp!==!1&&(nr.test(t.url)?"url":"string"==typeof t.data&&!(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&nr.test(t.data)&&"data");return s||"jsonp"===t.dataTypes[0]?(i=t.jsonpCallback=it.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(nr,"$1"+i):t.jsonp!==!1&&(t.url+=(Hn.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||it.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,tr.push(i)),a&&it.isFunction(o)&&o(a[0]),a=o=void 0}),"script"):void 0}),it.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||ht;var r=dt.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=it.buildFragment([e],t,i),i&&i.length&&it(i).remove(),it.merge([],r.childNodes))};var rr=it.fn.load;it.fn.load=function(e,t,n){if("string"!=typeof e&&rr)return rr.apply(this,arguments);var r,i,o,a=this,s=e.indexOf(" ");return s>=0&&(r=it.trim(e.slice(s,e.length)),e=e.slice(0,s)),it.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(o="POST"),a.length>0&&it.ajax({url:e,type:o,dataType:"html",data:t}).done(function(e){i=arguments,a.html(r?it("<div>").append(it.parseHTML(e)).find(r):e)}).complete(n&&function(e,t){a.each(n,i||[e.responseText,t,e])}),this},it.expr.filters.animated=function(e){return it.grep(it.timers,function(t){return e===t.elem}).length};var ir=e.document.documentElement;it.offset={setOffset:function(e,t,n){var r,i,o,a,s,l,u,c=it.css(e,"position"),d=it(e),f={};"static"===c&&(e.style.position="relative"),s=d.offset(),o=it.css(e,"top"),l=it.css(e,"left"),u=("absolute"===c||"fixed"===c)&&it.inArray("auto",[o,l])>-1,u?(r=d.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(l)||0),it.isFunction(t)&&(t=t.call(e,n,s)),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):d.css(f)}},it.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){it.offset.setOffset(this,e,t)});var t,n,r={top:0,left:0},i=this[0],o=i&&i.ownerDocument;if(o)return t=o.documentElement,it.contains(t,i)?(typeof i.getBoundingClientRect!==Et&&(r=i.getBoundingClientRect()),n=V(o),{top:r.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:r.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):r},position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===it.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),it.nodeName(e[0],"html")||(n=e.offset()),n.top+=it.css(e[0],"borderTopWidth",!0),n.left+=it.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-it.css(r,"marginTop",!0),left:t.left-n.left-it.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent||ir;e&&!it.nodeName(e,"html")&&"static"===it.css(e,"position");)e=e.offsetParent;return e||ir})}}),it.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);it.fn[e]=function(r){return At(this,function(e,r,i){var o=V(e);return void 0===i?o?t in o?o[t]:o.document.documentElement[r]:e[r]:void(o?o.scrollTo(n?it(o).scrollLeft():i,n?i:it(o).scrollTop()):e[r]=i)},e,r,arguments.length,null)}}),it.each(["top","left"],function(e,t){it.cssHooks[t]=N(nt.pixelPosition,function(e,n){return n?(n=tn(e,t),rn.test(n)?it(e).position()[t]+"px":n):void 0})}),it.each({Height:"height",Width:"width"},function(e,t){it.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){it.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return At(this,function(t,n,r){var i;return it.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?it.css(t,n,a):it.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),it.fn.size=function(){return this.length},it.fn.andSelf=it.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return it});var or=e.jQuery,ar=e.$;return it.noConflict=function(t){return e.$===it&&(e.$=ar),t&&e.jQuery===it&&(e.jQuery=or),it},typeof t===Et&&(e.jQuery=e.$=it),it}),function(e,t){e.rails!==t&&e.error("jquery-ujs has already been loaded!");var n,r=e(document);e.rails=n={linkClickSelector:"a[data-confirm], a[data-method], a[data-remote], a[data-disable-with], a[data-disable]",buttonClickSelector:"button[data-remote], button[data-confirm]",inputChangeSelector:"select[data-remote], input[data-remote], textarea[data-remote]",formSubmitSelector:"form",formInputClickSelector:"form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])",disableSelector:"input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled",enableSelector:"input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled",requiredInputSelector:"input[name][required]:not([disabled]),textarea[name][required]:not([disabled])",fileInputSelector:"input[type=file]",linkDisableSelector:"a[data-disable-with], a[data-disable]",buttonDisableSelector:"button[data-remote][data-disable-with], button[data-remote][data-disable]",CSRFProtection:function(t){var n=e('meta[name="csrf-token"]').attr("content");n&&t.setRequestHeader("X-CSRF-Token",n)},refreshCSRFTokens:function(){var t=e("meta[name=csrf-token]").attr("content"),n=e("meta[name=csrf-param]").attr("content");e('form input[name="'+n+'"]').val(t)},fire:function(t,n,r){var i=e.Event(n);return t.trigger(i,r),i.result!==!1},confirm:function(e){return confirm(e)},ajax:function(t){return e.ajax(t)},href:function(e){return e.attr("href")},handleRemote:function(r){var i,o,a,s,l,u,c,d;if(n.fire(r,"ajax:before")){if(s=r.data("cross-domain"),l=s===t?null:s,u=r.data("with-credentials")||null,c=r.data("type")||e.ajaxSettings&&e.ajaxSettings.dataType,r.is("form")){i=r.attr("method"),o=r.attr("action"),a=r.serializeArray();var f=r.data("ujs:submit-button");f&&(a.push(f),r.data("ujs:submit-button",null))}else r.is(n.inputChangeSelector)?(i=r.data("method"),o=r.data("url"),a=r.serialize(),r.data("params")&&(a=a+"&"+r.data("params"))):r.is(n.buttonClickSelector)?(i=r.data("method")||"get",o=r.data("url"),a=r.serialize(),r.data("params")&&(a=a+"&"+r.data("params"))):(i=r.data("method"),o=n.href(r),a=r.data("params")||null);return d={type:i||"GET",data:a,dataType:c,beforeSend:function(e,i){return i.dataType===t&&e.setRequestHeader("accept","*/*;q=0.5, "+i.accepts.script),n.fire(r,"ajax:beforeSend",[e,i])?void r.trigger("ajax:send",e):!1 +},success:function(e,t,n){r.trigger("ajax:success",[e,t,n])},complete:function(e,t){r.trigger("ajax:complete",[e,t])},error:function(e,t,n){r.trigger("ajax:error",[e,t,n])},crossDomain:l},u&&(d.xhrFields={withCredentials:u}),o&&(d.url=o),n.ajax(d)}return!1},handleMethod:function(r){var i=n.href(r),o=r.data("method"),a=r.attr("target"),s=e("meta[name=csrf-token]").attr("content"),l=e("meta[name=csrf-param]").attr("content"),u=e('<form method="post" action="'+i+'"></form>'),c='<input name="_method" value="'+o+'" type="hidden" />';l!==t&&s!==t&&(c+='<input name="'+l+'" value="'+s+'" type="hidden" />'),a&&u.attr("target",a),u.hide().append(c).appendTo("body"),u.submit()},formElements:function(t,n){return t.is("form")?e(t[0].elements).filter(n):t.find(n)},disableFormElements:function(t){n.formElements(t,n.disableSelector).each(function(){n.disableFormElement(e(this))})},disableFormElement:function(e){var n,r;n=e.is("button")?"html":"val",r=e.data("disable-with"),e.data("ujs:enable-with",e[n]()),r!==t&&e[n](r),e.prop("disabled",!0)},enableFormElements:function(t){n.formElements(t,n.enableSelector).each(function(){n.enableFormElement(e(this))})},enableFormElement:function(e){var t=e.is("button")?"html":"val";e.data("ujs:enable-with")&&e[t](e.data("ujs:enable-with")),e.prop("disabled",!1)},allowAction:function(e){var t,r=e.data("confirm"),i=!1;return r?(n.fire(e,"confirm")&&(i=n.confirm(r),t=n.fire(e,"confirm:complete",[i])),i&&t):!0},blankInputs:function(t,n,r){var i,o,a=e(),s=n||"input,textarea",l=t.find(s);return l.each(function(){if(i=e(this),o=i.is("input[type=checkbox],input[type=radio]")?i.is(":checked"):i.val(),!o==!r){if(i.is("input[type=radio]")&&l.filter('input[type=radio]:checked[name="'+i.attr("name")+'"]').length)return!0;a=a.add(i)}}),a.length?a:!1},nonBlankInputs:function(e,t){return n.blankInputs(e,t,!0)},stopEverything:function(t){return e(t.target).trigger("ujs:everythingStopped"),t.stopImmediatePropagation(),!1},disableElement:function(e){var r=e.data("disable-with");e.data("ujs:enable-with",e.html()),r!==t&&e.html(r),e.bind("click.railsDisable",function(e){return n.stopEverything(e)})},enableElement:function(e){e.data("ujs:enable-with")!==t&&(e.html(e.data("ujs:enable-with")),e.removeData("ujs:enable-with")),e.unbind("click.railsDisable")}},n.fire(r,"rails:attachBindings")&&(e.ajaxPrefilter(function(e,t,r){e.crossDomain||n.CSRFProtection(r)}),r.delegate(n.linkDisableSelector,"ajax:complete",function(){n.enableElement(e(this))}),r.delegate(n.buttonDisableSelector,"ajax:complete",function(){n.enableFormElement(e(this))}),r.delegate(n.linkClickSelector,"click.rails",function(r){var i=e(this),o=i.data("method"),a=i.data("params"),s=r.metaKey||r.ctrlKey;if(!n.allowAction(i))return n.stopEverything(r);if(!s&&i.is(n.linkDisableSelector)&&n.disableElement(i),i.data("remote")!==t){if(s&&(!o||"GET"===o)&&!a)return!0;var l=n.handleRemote(i);return l===!1?n.enableElement(i):l.error(function(){n.enableElement(i)}),!1}return i.data("method")?(n.handleMethod(i),!1):void 0}),r.delegate(n.buttonClickSelector,"click.rails",function(t){var r=e(this);if(!n.allowAction(r))return n.stopEverything(t);r.is(n.buttonDisableSelector)&&n.disableFormElement(r);var i=n.handleRemote(r);return i===!1?n.enableFormElement(r):i.error(function(){n.enableFormElement(r)}),!1}),r.delegate(n.inputChangeSelector,"change.rails",function(t){var r=e(this);return n.allowAction(r)?(n.handleRemote(r),!1):n.stopEverything(t)}),r.delegate(n.formSubmitSelector,"submit.rails",function(r){var i,o,a=e(this),s=a.data("remote")!==t;if(!n.allowAction(a))return n.stopEverything(r);if(a.attr("novalidate")==t&&(i=n.blankInputs(a,n.requiredInputSelector),i&&n.fire(a,"ajax:aborted:required",[i])))return n.stopEverything(r);if(s){if(o=n.nonBlankInputs(a,n.fileInputSelector)){setTimeout(function(){n.disableFormElements(a)},13);var l=n.fire(a,"ajax:aborted:file",[o]);return l||setTimeout(function(){n.enableFormElements(a)},13),l}return n.handleRemote(a),!1}setTimeout(function(){n.disableFormElements(a)},13)}),r.delegate(n.formInputClickSelector,"click.rails",function(t){var r=e(this);if(!n.allowAction(r))return n.stopEverything(t);var i=r.attr("name"),o=i?{name:i,value:r.val()}:null;r.closest("form").data("ujs:submit-button",o)}),r.delegate(n.formSubmitSelector,"ajax:send.rails",function(t){this==t.target&&n.disableFormElements(e(this))}),r.delegate(n.formSubmitSelector,"ajax:complete.rails",function(t){this==t.target&&n.enableFormElements(e(this))}),e(function(){n.refreshCSRFTokens()}))}(jQuery),function(){var e,t,n,r,i,o,a,s,l,u,c,d,f,p,h,m,g,v,y,b,x,w,T,E,k,C,N,S,j,A,D,L,H,_,q,F,M,O,R,B,P,I,W,$,z,X,U,V,G,Y=[].indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(t in this&&this[t]===e)return t;return-1},J={}.hasOwnProperty,K=function(e,t){function n(){this.constructor=e}for(var r in t)J.call(t,r)&&(e[r]=t[r]);return n.prototype=t.prototype,e.prototype=new n,e.__super__=t.prototype,e},Q=[].slice;j={},d=10,$=!1,m=null,S=null,q=null,h=null,V=null,b=function(e){var t;return e=new n(e),B(),c(),F(e),$&&(t=z(e.absolute))?(x(t),w(e)):w(e,W)},z=function(e){var t;return t=j[e],t&&!t.transitionCacheDisabled?t:void 0},g=function(e){return null==e&&(e=!0),$=e},w=function(e,t){return null==t&&(t=function(){return function(){}}(this)),X("page:fetch",{url:e.absolute}),null!=V&&V.abort(),V=new XMLHttpRequest,V.open("GET",e.withoutHashForIE10compatibility(),!0),V.setRequestHeader("Accept","text/html, application/xhtml+xml, application/xml"),V.setRequestHeader("X-XHR-Referer",q),V.onload=function(){var n;return X("page:receive"),(n=H())?(f.apply(null,y(n)),M(),t(),X("page:load")):document.location.href=e.absolute},V.onloadend=function(){return V=null},V.onerror=function(){return document.location.href=e.absolute},V.send()},x=function(e){return null!=V&&V.abort(),f(e.title,e.body),_(e),X("page:restore")},c=function(){var e;return e=new n(m.url),j[e.absolute]={url:e.relative,body:document.body,title:document.title,positionY:window.pageYOffset,positionX:window.pageXOffset,cachedAt:(new Date).getTime(),transitionCacheDisabled:null!=document.querySelector("[data-no-transition-cache]")},p(d)},D=function(e){return null==e&&(e=d),/^[\d]+$/.test(e)?d=parseInt(e):void 0},p=function(e){var t,n,r,i,o,a;for(r=Object.keys(j),t=r.map(function(e){return j[e].cachedAt}).sort(function(e,t){return t-e}),a=[],i=0,o=r.length;o>i;i++)n=r[i],j[n].cachedAt<=t[e]&&(X("page:expire",j[n]),a.push(delete j[n]));return a},f=function(t,n,r,i){return document.title=t,document.documentElement.replaceChild(n,document.body),null!=r&&e.update(r),i&&v(),m=window.history.state,X("page:change"),X("page:update")},v=function(){var e,t,n,r,i,o,a,s,l,u,c,d;for(o=Array.prototype.slice.call(document.body.querySelectorAll('script:not([data-turbolinks-eval="false"])')),a=0,l=o.length;l>a;a++)if(i=o[a],""===(c=i.type)||"text/javascript"===c){for(t=document.createElement("script"),d=i.attributes,s=0,u=d.length;u>s;s++)e=d[s],t.setAttribute(e.name,e.value);t.appendChild(document.createTextNode(i.innerHTML)),r=i.parentNode,n=i.nextSibling,r.removeChild(i),r.insertBefore(t,n)}},P=function(e){return e.innerHTML=e.innerHTML.replace(/<noscript[\S\s]*?<\/noscript>/gi,""),e},F=function(e){return(e=new n(e)).absolute!==q?window.history.pushState({turbolinks:!0,url:e.absolute},"",e.absolute):void 0},M=function(){var e,t;return(e=V.getResponseHeader("X-XHR-Redirected-To"))?(e=new n(e),t=e.hasNoHash()?document.location.hash:"",window.history.replaceState(m,"",e.href+t)):void 0},B=function(){return q=document.location.href},R=function(){return window.history.replaceState({turbolinks:!0,url:document.location.href},"",document.location.href)},O=function(){return m=window.history.state},_=function(e){return window.scrollTo(e.positionX,e.positionY)},W=function(){return document.location.hash?document.location.href=document.location.href:window.scrollTo(0,0)},L=function(e){var t,n;return t=(null!=(n=document.cookie.match(new RegExp(e+"=(\\w+)")))?n[1].toUpperCase():void 0)||"",document.cookie=e+"=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/",t},X=function(e,t){var n;return n=document.createEvent("Events"),t&&(n.data=t),n.initEvent(e,!0,!0),document.dispatchEvent(n)},A=function(){return!X("page:before-change")},H=function(){var e,t,n,r,i,o;return t=function(){var e;return 400<=(e=V.status)&&600>e},o=function(){return V.getResponseHeader("Content-Type").match(/^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/)},r=function(e){var t,n,r,i,o;for(i=e.head.childNodes,o=[],n=0,r=i.length;r>n;n++)t=i[n],null!=("function"==typeof t.getAttribute?t.getAttribute("data-turbolinks-track"):void 0)&&o.push(t.getAttribute("src")||t.getAttribute("href"));return o},e=function(e){var t;return S||(S=r(document)),t=r(e),t.length!==S.length||i(t,S).length!==S.length},i=function(e,t){var n,r,i,o,a;for(e.length>t.length&&(o=[t,e],e=o[0],t=o[1]),a=[],r=0,i=e.length;i>r;r++)n=e[r],Y.call(t,n)>=0&&a.push(n);return a},!t()&&o()&&(n=h(V.responseText),n&&!e(n))?n:void 0},y=function(t){var n;return n=t.querySelector("title"),[null!=n?n.textContent:void 0,P(t.body),e.get(t).token,"runScripts"]},e={get:function(e){var t;return null==e&&(e=document),{node:t=e.querySelector('meta[name="csrf-token"]'),token:null!=t&&"function"==typeof t.getAttribute?t.getAttribute("content"):void 0}},update:function(e){var t;return t=this.get(),null!=t.token&&null!=e&&t.token!==e?t.node.setAttribute("content",e):void 0}},i=function(){var e,t,n,r,i,o;t=function(e){return(new DOMParser).parseFromString(e,"text/html")},e=function(e){var t;return t=document.implementation.createHTMLDocument(""),t.documentElement.innerHTML=e,t},n=function(e){var t;return t=document.implementation.createHTMLDocument(""),t.open("replace"),t.write(e),t.close(),t};try{if(window.DOMParser)return i=t("<html><body><p>test"),t}catch(a){return r=a,i=e("<html><body><p>test"),e}finally{if(1!==(null!=i&&null!=(o=i.body)?o.childNodes.length:void 0))return n}},n=function(){function e(t){return this.original=null!=t?t:document.location.href,this.original.constructor===e?this.original:void this._parse()}return e.prototype.withoutHash=function(){return this.href.replace(this.hash,"")},e.prototype.withoutHashForIE10compatibility=function(){return this.withoutHash()},e.prototype.hasNoHash=function(){return 0===this.hash.length},e.prototype._parse=function(){var e;return(null!=this.link?this.link:this.link=document.createElement("a")).href=this.original,e=this.link,this.href=e.href,this.protocol=e.protocol,this.host=e.host,this.hostname=e.hostname,this.port=e.port,this.pathname=e.pathname,this.search=e.search,this.hash=e.hash,this.origin=[this.protocol,"//",this.hostname].join(""),0!==this.port.length&&(this.origin+=":"+this.port),this.relative=[this.pathname,this.search,this.hash].join(""),this.absolute=this.href},e}(),r=function(e){function t(e){return this.link=e,this.link.constructor===t?this.link:(this.original=this.link.href,void t.__super__.constructor.apply(this,arguments))}return K(t,e),t.HTML_EXTENSIONS=["html"],t.allowExtensions=function(){var e,n,r,i;for(n=1<=arguments.length?Q.call(arguments,0):[],r=0,i=n.length;i>r;r++)e=n[r],t.HTML_EXTENSIONS.push(e);return t.HTML_EXTENSIONS},t.prototype.shouldIgnore=function(){return this._crossOrigin()||this._anchored()||this._nonHtml()||this._optOut()||this._target()},t.prototype._crossOrigin=function(){return this.origin!==(new n).origin},t.prototype._anchored=function(){var e;return(this.hash&&this.withoutHash())===(e=new n).withoutHash()||this.href===e.href+"#"},t.prototype._nonHtml=function(){return this.pathname.match(/\.[a-z]+$/g)&&!this.pathname.match(new RegExp("\\.(?:"+t.HTML_EXTENSIONS.join("|")+")?$","g"))},t.prototype._optOut=function(){var e,t;for(t=this.link;!e&&t!==document;)e=null!=t.getAttribute("data-no-turbolink"),t=t.parentNode;return e},t.prototype._target=function(){return 0!==this.link.target.length},t}(n),t=function(){function e(e){this.event=e,this.event.defaultPrevented||(this._extractLink(),this._validForTurbolinks()&&(A()||U(this.link.href),this.event.preventDefault()))}return e.installHandlerLast=function(t){return t.defaultPrevented?void 0:(document.removeEventListener("click",e.handle,!1),document.addEventListener("click",e.handle,!1))},e.handle=function(t){return new e(t)},e.prototype._extractLink=function(){var e;for(e=this.event.target;e.parentNode&&"A"!==e.nodeName;)e=e.parentNode;return"A"===e.nodeName&&0!==e.href.length?this.link=new r(e):void 0},e.prototype._validForTurbolinks=function(){return null!=this.link&&!(this.link.shouldIgnore()||this._nonStandardClick())},e.prototype._nonStandardClick=function(){return this.event.which>1||this.event.metaKey||this.event.ctrlKey||this.event.shiftKey||this.event.altKey},e}(),u=function(e){return setTimeout(e,500)},k=function(){return document.addEventListener("DOMContentLoaded",function(){return X("page:change"),X("page:update")},!0)},N=function(){return"undefined"!=typeof jQuery?jQuery(document).on("ajaxSuccess",function(e,t){return jQuery.trim(t.responseText)?X("page:update"):void 0}):void 0},C=function(e){var t,r;return(null!=(r=e.state)?r.turbolinks:void 0)?(t=j[new n(e.state.url).absolute])?(c(),x(t)):U(e.target.location.href):void 0},E=function(){return R(),O(),h=i(),document.addEventListener("click",t.installHandlerLast,!0),u(function(){return window.addEventListener("popstate",C,!1)})},T=void 0!==window.history.state||navigator.userAgent.match(/Firefox\/2[6|7]/),s=window.history&&window.history.pushState&&window.history.replaceState&&T,o=!navigator.userAgent.match(/CriOS\//),I="GET"===(G=L("request_method"))||""===G,l=s&&o&&I,a=document.addEventListener&&document.createEvent,a&&(k(),N()),l?(U=b,E()):U=function(e){return document.location.href=e},this.Turbolinks={visit:U,pagesCached:D,enableTransitionCache:g,allowLinkExtensions:r.allowExtensions,supported:l}}.call(this),function(){}.call(this);
\ No newline at end of file diff --git a/actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js.gz b/actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js.gz Binary files differnew file mode 100644 index 0000000000..f62c656dc8 --- /dev/null +++ b/actionpack/test/fixtures/public/gzip/application-a71b3024f80aea3181c09774ca17e712.js.gz diff --git a/actionpack/test/fixtures/public/gzip/foo.zoo b/actionpack/test/fixtures/public/gzip/foo.zoo new file mode 100644 index 0000000000..1826a7660e --- /dev/null +++ b/actionpack/test/fixtures/public/gzip/foo.zoo @@ -0,0 +1,4 @@ +!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=e.length,n=it.type(e);return"function"===n||it.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e}function r(e,t,n){if(it.isFunction(t))return it.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return it.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(ft.test(t))return it.filter(t,e,n);t=it.filter(t,e)}return it.grep(e,function(e){return it.inArray(e,t)>=0!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t=xt[e]={};return it.each(e.match(bt)||[],function(e,n){t[n]=!0}),t}function a(){ht.addEventListener?(ht.removeEventListener("DOMContentLoaded",s,!1),e.removeEventListener("load",s,!1)):(ht.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(ht.addEventListener||"load"===event.type||"complete"===ht.readyState)&&(a(),it.ready())}function l(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(Ct,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:kt.test(n)?it.parseJSON(n):n}catch(i){}it.data(e,t,n)}else n=void 0}return n}function u(e){var t;for(t in e)if(("data"!==t||!it.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(it.acceptData(e)){var i,o,a=it.expando,s=e.nodeType,l=s?it.cache:e,u=s?e[a]:e[a]&&a;if(u&&l[u]&&(r||l[u].data)||void 0!==n||"string"!=typeof t)return u||(u=s?e[a]=G.pop()||it.guid++:a),l[u]||(l[u]=s?{}:{toJSON:it.noop}),("object"==typeof t||"function"==typeof t)&&(r?l[u]=it.extend(l[u],t):l[u].data=it.extend(l[u].data,t)),o=l[u],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[it.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[it.camelCase(t)])):i=o,i}}function d(e,t,n){if(it.acceptData(e)){var r,i,o=e.nodeType,a=o?it.cache:e,s=o?e[it.expando]:it.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){it.isArray(t)?t=t.concat(it.map(t,it.camelCase)):t in r?t=[t]:(t=it.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!u(r):!it.isEmptyObject(r))return}(n||(delete a[s].data,u(a[s])))&&(o?it.cleanData([e],!0):nt.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}function f(){return!0}function p(){return!1}function h(){try{return ht.activeElement}catch(e){}}function m(e){var t=Mt.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function g(e,t){var n,r,i=0,o=typeof e.getElementsByTagName!==Et?e.getElementsByTagName(t||"*"):typeof e.querySelectorAll!==Et?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||it.nodeName(r,t)?o.push(r):it.merge(o,g(r,t));return void 0===t||t&&it.nodeName(e,t)?it.merge([e],o):o}function v(e){Dt.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t){return it.nodeName(e,"table")&&it.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function b(e){return e.type=(null!==it.find.attr(e,"type"))+"/"+e.type,e}function x(e){var t=Vt.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function w(e,t){for(var n,r=0;null!=(n=e[r]);r++)it._data(n,"globalEval",!t||it._data(t[r],"globalEval"))}function T(e,t){if(1===t.nodeType&&it.hasData(e)){var n,r,i,o=it._data(e),a=it._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)it.event.add(t,n,s[n][r])}a.data&&(a.data=it.extend({},a.data))}}function E(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!nt.noCloneEvent&&t[it.expando]){i=it._data(t);for(r in i.events)it.removeEvent(t,r,i.handle);t.removeAttribute(it.expando)}"script"===n&&t.text!==e.text?(b(t).text=e.text,x(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),nt.html5Clone&&e.innerHTML&&!it.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Dt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}function k(t,n){var r,i=it(n.createElement(t)).appendTo(n.body),o=e.getDefaultComputedStyle&&(r=e.getDefaultComputedStyle(i[0]))?r.display:it.css(i[0],"display");return i.detach(),o}function C(e){var t=ht,n=Zt[e];return n||(n=k(e,t),"none"!==n&&n||(Qt=(Qt||it("<iframe frameborder='0' width='0' height='0'/>")).appendTo(t.documentElement),t=(Qt[0].contentWindow||Qt[0].contentDocument).document,t.write(),t.close(),n=k(e,t),Qt.detach()),Zt[e]=n),n}function N(e,t){return{get:function(){var n=e();if(null!=n)return n?void delete this.get:(this.get=t).apply(this,arguments)}}}function S(e,t){if(t in e)return t;for(var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=pn.length;i--;)if(t=pn[i]+n,t in e)return t;return r}function j(e,t){for(var n,r,i,o=[],a=0,s=e.length;s>a;a++)r=e[a],r.style&&(o[a]=it._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&jt(r)&&(o[a]=it._data(r,"olddisplay",C(r.nodeName)))):(i=jt(r),(n&&"none"!==n||!i)&&it._data(r,"olddisplay",i?n:it.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}function A(e,t,n){var r=un.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function D(e,t,n,r,i){for(var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;4>o;o+=2)"margin"===n&&(a+=it.css(e,n+St[o],!0,i)),r?("content"===n&&(a-=it.css(e,"padding"+St[o],!0,i)),"margin"!==n&&(a-=it.css(e,"border"+St[o]+"Width",!0,i))):(a+=it.css(e,"padding"+St[o],!0,i),"padding"!==n&&(a+=it.css(e,"border"+St[o]+"Width",!0,i)));return a}function L(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=en(e),a=nt.boxSizing&&"border-box"===it.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=tn(e,t,o),(0>i||null==i)&&(i=e.style[t]),rn.test(i))return i;r=a&&(nt.boxSizingReliable()||i===e.style[t]),i=parseFloat(i)||0}return i+D(e,t,n||(a?"border":"content"),r,o)+"px"}function H(e,t,n,r,i){return new H.prototype.init(e,t,n,r,i)}function _(){return setTimeout(function(){hn=void 0}),hn=it.now()}function q(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=St[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}function F(e,t,n){for(var r,i=(xn[t]||[]).concat(xn["*"]),o=0,a=i.length;a>o;o++)if(r=i[o].call(n,t,e))return r}function M(e,t,n){var r,i,o,a,s,l,u,c,d=this,f={},p=e.style,h=e.nodeType&&jt(e),m=it._data(e,"fxshow");n.queue||(s=it._queueHooks(e,"fx"),null==s.unqueued&&(s.unqueued=0,l=s.empty.fire,s.empty.fire=function(){s.unqueued||l()}),s.unqueued++,d.always(function(){d.always(function(){s.unqueued--,it.queue(e,"fx").length||s.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],u=it.css(e,"display"),c="none"===u?it._data(e,"olddisplay")||C(e.nodeName):u,"inline"===c&&"none"===it.css(e,"float")&&(nt.inlineBlockNeedsLayout&&"inline"!==C(e.nodeName)?p.zoom=1:p.display="inline-block")),n.overflow&&(p.overflow="hidden",nt.shrinkWrapBlocks()||d.always(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],gn.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(h?"hide":"show")){if("show"!==i||!m||void 0===m[r])continue;h=!0}f[r]=m&&m[r]||it.style(e,r)}else u=void 0;if(it.isEmptyObject(f))"inline"===("none"===u?C(e.nodeName):u)&&(p.display=u);else{m?"hidden"in m&&(h=m.hidden):m=it._data(e,"fxshow",{}),o&&(m.hidden=!h),h?it(e).show():d.done(function(){it(e).hide()}),d.done(function(){var t;it._removeData(e,"fxshow");for(t in f)it.style(e,t,f[t])});for(r in f)a=F(h?m[r]:0,r,d),r in m||(m[r]=a.start,h&&(a.end=a.start,a.start="width"===r||"height"===r?1:0))}}function O(e,t){var n,r,i,o,a;for(n in e)if(r=it.camelCase(n),i=t[r],o=e[n],it.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=it.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}function R(e,t,n){var r,i,o=0,a=bn.length,s=it.Deferred().always(function(){delete l.elem}),l=function(){if(i)return!1;for(var t=hn||_(),n=Math.max(0,u.startTime+u.duration-t),r=n/u.duration||0,o=1-r,a=0,l=u.tweens.length;l>a;a++)u.tweens[a].run(o);return s.notifyWith(e,[u,o,n]),1>o&&l?n:(s.resolveWith(e,[u]),!1)},u=s.promise({elem:e,props:it.extend({},t),opts:it.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:hn||_(),duration:n.duration,tweens:[],createTween:function(t,n){var r=it.Tween(e,u.opts,t,n,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(r),r},stop:function(t){var n=0,r=t?u.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)u.tweens[n].run(1);return t?s.resolveWith(e,[u,t]):s.rejectWith(e,[u,t]),this}}),c=u.props;for(O(c,u.opts.specialEasing);a>o;o++)if(r=bn[o].call(u,e,c,u.opts))return r;return it.map(c,F,u),it.isFunction(u.opts.start)&&u.opts.start.call(e,u),it.fx.timer(it.extend(l,{elem:e,anim:u,queue:u.opts.queue})),u.progress(u.opts.progress).done(u.opts.done,u.opts.complete).fail(u.opts.fail).always(u.opts.always)}function B(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(bt)||[];if(it.isFunction(n))for(;r=o[i++];)"+"===r.charAt(0)?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function P(e,t,n,r){function i(s){var l;return o[s]=!0,it.each(e[s]||[],function(e,s){var u=s(t,n,r);return"string"!=typeof u||a||o[u]?a?!(l=u):void 0:(t.dataTypes.unshift(u),i(u),!1)}),l}var o={},a=e===zn;return i(t.dataTypes[0])||!o["*"]&&i("*")}function I(e,t){var n,r,i=it.ajaxSettings.flatOptions||{};for(r in t)void 0!==t[r]&&((i[r]?e:n||(n={}))[r]=t[r]);return n&&it.extend(!0,e,n),e}function W(e,t,n){for(var r,i,o,a,s=e.contents,l=e.dataTypes;"*"===l[0];)l.shift(),void 0===i&&(i=e.mimeType||t.getResponseHeader("Content-Type"));if(i)for(a in s)if(s[a]&&s[a].test(i)){l.unshift(a);break}if(l[0]in n)o=l[0];else{for(a in n){if(!l[0]||e.converters[a+" "+l[0]]){o=a;break}r||(r=a)}o=o||r}return o?(o!==l[0]&&l.unshift(o),n[o]):void 0}function $(e,t,n,r){var i,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(a=u[l+" "+o]||u["* "+o],!a)for(i in u)if(s=i.split(" "),s[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){a===!0?a=u[i]:u[i]!==!0&&(o=s[0],c.unshift(s[1]));break}if(a!==!0)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(d){return{state:"parsererror",error:a?d:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}function z(e,t,n,r){var i;if(it.isArray(t))it.each(t,function(t,i){n||Gn.test(e)?r(e,i):z(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==it.type(t))r(e,t);else for(i in t)z(e+"["+i+"]",t[i],n,r)}function X(){try{return new e.XMLHttpRequest}catch(t){}}function U(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}function V(e){return it.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}var G=[],Y=G.slice,J=G.concat,K=G.push,Q=G.indexOf,Z={},et=Z.toString,tt=Z.hasOwnProperty,nt={},rt="1.11.1",it=function(e,t){return new it.fn.init(e,t)},ot=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,at=/^-ms-/,st=/-([\da-z])/gi,lt=function(e,t){return t.toUpperCase()};it.fn=it.prototype={jquery:rt,constructor:it,selector:"",length:0,toArray:function(){return Y.call(this)},get:function(e){return null!=e?0>e?this[e+this.length]:this[e]:Y.call(this)},pushStack:function(e){var t=it.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return it.each(this,e,t)},map:function(e){return this.pushStack(it.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(Y.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:K,sort:G.sort,splice:G.splice},it.extend=it.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,l=arguments.length,u=!1;for("boolean"==typeof a&&(u=a,a=arguments[s]||{},s++),"object"==typeof a||it.isFunction(a)||(a={}),s===l&&(a=this,s--);l>s;s++)if(null!=(i=arguments[s]))for(r in i)e=a[r],n=i[r],a!==n&&(u&&n&&(it.isPlainObject(n)||(t=it.isArray(n)))?(t?(t=!1,o=e&&it.isArray(e)?e:[]):o=e&&it.isPlainObject(e)?e:{},a[r]=it.extend(u,o,n)):void 0!==n&&(a[r]=n));return a},it.extend({expando:"jQuery"+(rt+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isFunction:function(e){return"function"===it.type(e)},isArray:Array.isArray||function(e){return"array"===it.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!it.isArray(e)&&e-parseFloat(e)>=0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},isPlainObject:function(e){var t;if(!e||"object"!==it.type(e)||e.nodeType||it.isWindow(e))return!1;try{if(e.constructor&&!tt.call(e,"constructor")&&!tt.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}if(nt.ownLast)for(t in e)return tt.call(e,t);for(t in e);return void 0===t||tt.call(e,t)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?Z[et.call(e)]||"object":typeof e},globalEval:function(t){t&&it.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(at,"ms-").replace(st,lt)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,r){var i,o=0,a=e.length,s=n(e);if(r){if(s)for(;a>o&&(i=t.apply(e[o],r),i!==!1);o++);else for(o in e)if(i=t.apply(e[o],r),i===!1)break}else if(s)for(;a>o&&(i=t.call(e[o],o,e[o]),i!==!1);o++);else for(o in e)if(i=t.call(e[o],o,e[o]),i===!1)break;return e},trim:function(e){return null==e?"":(e+"").replace(ot,"")},makeArray:function(e,t){var r=t||[];return null!=e&&(n(Object(e))?it.merge(r,"string"==typeof e?[e]:e):K.call(r,e)),r},inArray:function(e,t,n){var r;if(t){if(Q)return Q.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;n>r;)e[i++]=t[r++];if(n!==n)for(;void 0!==t[r];)e[i++]=t[r++];return e.length=i,e},grep:function(e,t,n){for(var r,i=[],o=0,a=e.length,s=!n;a>o;o++)r=!t(e[o],o),r!==s&&i.push(e[o]);return i},map:function(e,t,r){var i,o=0,a=e.length,s=n(e),l=[];if(s)for(;a>o;o++)i=t(e[o],o,r),null!=i&&l.push(i);else for(o in e)i=t(e[o],o,r),null!=i&&l.push(i);return J.apply([],l)},guid:1,proxy:function(e,t){var n,r,i;return"string"==typeof t&&(i=e[t],t=e,e=i),it.isFunction(e)?(n=Y.call(arguments,2),r=function(){return e.apply(t||this,n.concat(Y.call(arguments)))},r.guid=e.guid=e.guid||it.guid++,r):void 0},now:function(){return+new Date},support:nt}),it.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){Z["[object "+t+"]"]=t.toLowerCase()});var ut=function(e){function t(e,t,n,r){var i,o,a,s,l,u,d,p,h,m;if((t?t.ownerDocument||t:P)!==H&&L(t),t=t||H,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(q&&!r){if(i=yt.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&R(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return Z.apply(n,t.getElementsByTagName(e)),n;if((a=i[3])&&w.getElementsByClassName&&t.getElementsByClassName)return Z.apply(n,t.getElementsByClassName(a)),n}if(w.qsa&&(!F||!F.test(e))){if(p=d=B,h=t,m=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){for(u=C(e),(d=t.getAttribute("id"))?p=d.replace(xt,"\\$&"):t.setAttribute("id",p),p="[id='"+p+"'] ",l=u.length;l--;)u[l]=p+f(u[l]);h=bt.test(e)&&c(t.parentNode)||t,m=u.join(",")}if(m)try{return Z.apply(n,h.querySelectorAll(m)),n}catch(g){}finally{d||t.removeAttribute("id")}}}return S(e.replace(lt,"$1"),t,n,r)}function n(){function e(n,r){return t.push(n+" ")>T.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[B]=!0,e}function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=e.length;r--;)T.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||G)-(~e.sourceIndex||G);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function l(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function u(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function c(e){return e&&typeof e.getElementsByTagName!==V&&e}function d(){}function f(e){for(var t=0,n=e.length,r="";n>t;t++)r+=e[t].value;return r}function p(e,t,n){var r=t.dir,i=n&&"parentNode"===r,o=W++;return t.first?function(t,n,o){for(;t=t[r];)if(1===t.nodeType||i)return e(t,n,o)}:function(t,n,a){var s,l,u=[I,o];if(a){for(;t=t[r];)if((1===t.nodeType||i)&&e(t,n,a))return!0}else for(;t=t[r];)if(1===t.nodeType||i){if(l=t[B]||(t[B]={}),(s=l[r])&&s[0]===I&&s[1]===o)return u[2]=s[2];if(l[r]=u,u[2]=e(t,n,a))return!0}}}function h(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function m(e,n,r){for(var i=0,o=n.length;o>i;i++)t(e,n[i],r);return r}function g(e,t,n,r,i){for(var o,a=[],s=0,l=e.length,u=null!=t;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function v(e,t,n,i,o,a){return i&&!i[B]&&(i=v(i)),o&&!o[B]&&(o=v(o,a)),r(function(r,a,s,l){var u,c,d,f=[],p=[],h=a.length,v=r||m(t||"*",s.nodeType?[s]:s,[]),y=!e||!r&&t?v:g(v,f,e,s,l),b=n?o||(r?e:h||i)?[]:a:y;if(n&&n(y,b,s,l),i)for(u=g(b,p),i(u,[],s,l),c=u.length;c--;)(d=u[c])&&(b[p[c]]=!(y[p[c]]=d));if(r){if(o||e){if(o){for(u=[],c=b.length;c--;)(d=b[c])&&u.push(y[c]=d);o(null,b=[],u,l)}for(c=b.length;c--;)(d=b[c])&&(u=o?tt.call(r,d):f[c])>-1&&(r[u]=!(a[u]=d))}}else b=g(b===a?b.splice(h,b.length):b),o?o(null,a,b,l):Z.apply(a,b)})}function y(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.relative[" "],s=o?1:0,l=p(function(e){return e===t},a,!0),u=p(function(e){return tt.call(t,e)>-1},a,!0),c=[function(e,n,r){return!o&&(r||n!==j)||((t=n).nodeType?l(e,n,r):u(e,n,r))}];i>s;s++)if(n=T.relative[e[s].type])c=[p(h(c),n)];else{if(n=T.filter[e[s].type].apply(null,e[s].matches),n[B]){for(r=++s;i>r&&!T.relative[e[r].type];r++);return v(s>1&&h(c),s>1&&f(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(lt,"$1"),n,r>s&&y(e.slice(s,r)),i>r&&y(e=e.slice(r)),i>r&&f(e))}c.push(n)}return h(c)}function b(e,n){var i=n.length>0,o=e.length>0,a=function(r,a,s,l,u){var c,d,f,p=0,h="0",m=r&&[],v=[],y=j,b=r||o&&T.find.TAG("*",u),x=I+=null==y?1:Math.random()||.1,w=b.length;for(u&&(j=a!==H&&a);h!==w&&null!=(c=b[h]);h++){if(o&&c){for(d=0;f=e[d++];)if(f(c,a,s)){l.push(c);break}u&&(I=x)}i&&((c=!f&&c)&&p--,r&&m.push(c))}if(p+=h,i&&h!==p){for(d=0;f=n[d++];)f(m,v,a,s);if(r){if(p>0)for(;h--;)m[h]||v[h]||(v[h]=K.call(l));v=g(v)}Z.apply(l,v),u&&!r&&v.length>0&&p+n.length>1&&t.uniqueSort(l)}return u&&(I=x,j=y),m};return i?r(a):a}var x,w,T,E,k,C,N,S,j,A,D,L,H,_,q,F,M,O,R,B="sizzle"+-new Date,P=e.document,I=0,W=0,$=n(),z=n(),X=n(),U=function(e,t){return e===t&&(D=!0),0},V="undefined",G=1<<31,Y={}.hasOwnProperty,J=[],K=J.pop,Q=J.push,Z=J.push,et=J.slice,tt=J.indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(this[t]===e)return t;return-1},nt="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",rt="[\\x20\\t\\r\\n\\f]",it="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",ot=it.replace("w","w#"),at="\\["+rt+"*("+it+")(?:"+rt+"*([*^$|!~]?=)"+rt+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+ot+"))|)"+rt+"*\\]",st=":("+it+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+at+")*)|.*)\\)|)",lt=new RegExp("^"+rt+"+|((?:^|[^\\\\])(?:\\\\.)*)"+rt+"+$","g"),ut=new RegExp("^"+rt+"*,"+rt+"*"),ct=new RegExp("^"+rt+"*([>+~]|"+rt+")"+rt+"*"),dt=new RegExp("="+rt+"*([^\\]'\"]*?)"+rt+"*\\]","g"),ft=new RegExp(st),pt=new RegExp("^"+ot+"$"),ht={ID:new RegExp("^#("+it+")"),CLASS:new RegExp("^\\.("+it+")"),TAG:new RegExp("^("+it.replace("w","w*")+")"),ATTR:new RegExp("^"+at),PSEUDO:new RegExp("^"+st),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+rt+"*(even|odd|(([+-]|)(\\d*)n|)"+rt+"*(?:([+-]|)"+rt+"*(\\d+)|))"+rt+"*\\)|)","i"),bool:new RegExp("^(?:"+nt+")$","i"),needsContext:new RegExp("^"+rt+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+rt+"*((?:-\\d)?\\d*)"+rt+"*\\)|)(?=[^-]|$)","i")},mt=/^(?:input|select|textarea|button)$/i,gt=/^h\d$/i,vt=/^[^{]+\{\s*\[native \w/,yt=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,bt=/[+~]/,xt=/'|\\/g,wt=new RegExp("\\\\([\\da-f]{1,6}"+rt+"?|("+rt+")|.)","ig"),Tt=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)};try{Z.apply(J=et.call(P.childNodes),P.childNodes),J[P.childNodes.length].nodeType}catch(Et){Z={apply:J.length?function(e,t){Q.apply(e,et.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}w=t.support={},k=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},L=t.setDocument=function(e){var t,n=e?e.ownerDocument||e:P,r=n.defaultView;return n!==H&&9===n.nodeType&&n.documentElement?(H=n,_=n.documentElement,q=!k(n),r&&r!==r.top&&(r.addEventListener?r.addEventListener("unload",function(){L()},!1):r.attachEvent&&r.attachEvent("onunload",function(){L()})),w.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),w.getElementsByTagName=i(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),w.getElementsByClassName=vt.test(n.getElementsByClassName)&&i(function(e){return e.innerHTML="<div class='a'></div><div class='a i'></div>",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),w.getById=i(function(e){return _.appendChild(e).id=B,!n.getElementsByName||!n.getElementsByName(B).length}),w.getById?(T.find.ID=function(e,t){if(typeof t.getElementById!==V&&q){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},T.filter.ID=function(e){var t=e.replace(wt,Tt);return function(e){return e.getAttribute("id")===t}}):(delete T.find.ID,T.filter.ID=function(e){var t=e.replace(wt,Tt);return function(e){var n=typeof e.getAttributeNode!==V&&e.getAttributeNode("id");return n&&n.value===t}}),T.find.TAG=w.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==V?t.getElementsByTagName(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},T.find.CLASS=w.getElementsByClassName&&function(e,t){return typeof t.getElementsByClassName!==V&&q?t.getElementsByClassName(e):void 0},M=[],F=[],(w.qsa=vt.test(n.querySelectorAll))&&(i(function(e){e.innerHTML="<select msallowclip=''><option selected=''></option></select>",e.querySelectorAll("[msallowclip^='']").length&&F.push("[*^$]="+rt+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+rt+"*(?:value|"+nt+")"),e.querySelectorAll(":checked").length||F.push(":checked")}),i(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+rt+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(w.matchesSelector=vt.test(O=_.matches||_.webkitMatchesSelector||_.mozMatchesSelector||_.oMatchesSelector||_.msMatchesSelector))&&i(function(e){w.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),M.push("!=",st)}),F=F.length&&new RegExp(F.join("|")),M=M.length&&new RegExp(M.join("|")),t=vt.test(_.compareDocumentPosition),R=t||vt.test(_.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},U=t?function(e,t){if(e===t)return D=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r?r:(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&r||!w.sortDetached&&t.compareDocumentPosition(e)===r?e===n||e.ownerDocument===P&&R(P,e)?-1:t===n||t.ownerDocument===P&&R(P,t)?1:A?tt.call(A,e)-tt.call(A,t):0:4&r?-1:1)}:function(e,t){if(e===t)return D=!0,0;var r,i=0,o=e.parentNode,s=t.parentNode,l=[e],u=[t];if(!o||!s)return e===n?-1:t===n?1:o?-1:s?1:A?tt.call(A,e)-tt.call(A,t):0;if(o===s)return a(e,t);for(r=e;r=r.parentNode;)l.unshift(r);for(r=t;r=r.parentNode;)u.unshift(r);for(;l[i]===u[i];)i++;return i?a(l[i],u[i]):l[i]===P?-1:u[i]===P?1:0},n):H},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==H&&L(e),n=n.replace(dt,"='$1']"),!(!w.matchesSelector||!q||M&&M.test(n)||F&&F.test(n)))try{var r=O.call(e,n);if(r||w.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return t(n,H,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==H&&L(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==H&&L(e);var n=T.attrHandle[t.toLowerCase()],r=n&&Y.call(T.attrHandle,t.toLowerCase())?n(e,t,!q):void 0;return void 0!==r?r:w.attributes||!q?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(D=!w.detectDuplicates,A=!w.sortStable&&e.slice(0),e.sort(U),D){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return A=null,e},E=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=E(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=E(t);return n},T=t.selectors={cacheLength:50,createPseudo:r,match:ht,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(wt,Tt),e[3]=(e[3]||e[4]||e[5]||"").replace(wt,Tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return ht.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&ft.test(n)&&(t=C(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(wt,Tt).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=$[e+" "];return t||(t=new RegExp("(^|"+rt+")"+e+"("+rt+"|$)"))&&$(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==V&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:n?(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o+" ").indexOf(r)>-1:"|="===n?o===r||o.slice(0,r.length+1)===r+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,d,f,p,h,m=o!==a?"nextSibling":"previousSibling",g=t.parentNode,v=s&&t.nodeName.toLowerCase(),y=!l&&!s;if(g){if(o){for(;m;){for(d=t;d=d[m];)if(s?d.nodeName.toLowerCase()===v:1===d.nodeType)return!1;h=m="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?g.firstChild:g.lastChild],a&&y){for(c=g[B]||(g[B]={}),u=c[e]||[],p=u[0]===I&&u[1],f=u[0]===I&&u[2],d=p&&g.childNodes[p];d=++p&&d&&d[m]||(f=p=0)||h.pop();)if(1===d.nodeType&&++f&&d===t){c[e]=[I,p,f];break}}else if(y&&(u=(t[B]||(t[B]={}))[e])&&u[0]===I)f=u[1];else for(;(d=++p&&d&&d[m]||(f=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==v:1!==d.nodeType)||!++f||(y&&((d[B]||(d[B]={}))[e]=[I,f]),d!==t)););return f-=i,f===r||f%r===0&&f/r>=0}}},PSEUDO:function(e,n){var i,o=T.pseudos[e]||T.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[B]?o(n):o.length>1?(i=[e,e,"",n],T.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=tt.call(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=N(e.replace(lt,"$1"));return i[B]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return function(t){return(t.textContent||t.innerText||E(t)).indexOf(e)>-1}}),lang:r(function(e){return pt.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(wt,Tt).toLowerCase(),function(t){var n;do if(n=q?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===_},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.pseudos.empty(e)},header:function(e){return gt.test(e.nodeName)},input:function(e){return mt.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:u(function(){return[0]}),last:u(function(e,t){return[t-1]}),eq:u(function(e,t,n){return[0>n?n+t:n]}),even:u(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:u(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:u(function(e,t,n){for(var r=0>n?n+t:n;--r>=0;)e.push(r);return e}),gt:u(function(e,t,n){for(var r=0>n?n+t:n;++r<t;)e.push(r);return e})}},T.pseudos.nth=T.pseudos.eq;for(x in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})T.pseudos[x]=s(x);for(x in{submit:!0,reset:!0})T.pseudos[x]=l(x);return d.prototype=T.filters=T.pseudos,T.setFilters=new d,C=t.tokenize=function(e,n){var r,i,o,a,s,l,u,c=z[e+" "];if(c)return n?0:c.slice(0);for(s=e,l=[],u=T.preFilter;s;){(!r||(i=ut.exec(s)))&&(i&&(s=s.slice(i[0].length)||s),l.push(o=[])),r=!1,(i=ct.exec(s))&&(r=i.shift(),o.push({value:r,type:i[0].replace(lt," ")}),s=s.slice(r.length));for(a in T.filter)!(i=ht[a].exec(s))||u[a]&&!(i=u[a](i))||(r=i.shift(),o.push({value:r,type:a,matches:i}),s=s.slice(r.length));if(!r)break}return n?s.length:s?t.error(e):z(e,l).slice(0)},N=t.compile=function(e,t){var n,r=[],i=[],o=X[e+" "];if(!o){for(t||(t=C(e)),n=t.length;n--;)o=y(t[n]),o[B]?r.push(o):i.push(o);o=X(e,b(i,r)),o.selector=e}return o},S=t.select=function(e,t,n,r){var i,o,a,s,l,u="function"==typeof e&&e,d=!r&&C(e=u.selector||e);if(n=n||[],1===d.length){if(o=d[0]=d[0].slice(0),o.length>2&&"ID"===(a=o[0]).type&&w.getById&&9===t.nodeType&&q&&T.relative[o[1].type]){if(t=(T.find.ID(a.matches[0].replace(wt,Tt),t)||[])[0],!t)return n;u&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=ht.needsContext.test(e)?0:o.length;i--&&(a=o[i],!T.relative[s=a.type]);)if((l=T.find[s])&&(r=l(a.matches[0].replace(wt,Tt),bt.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&f(o),!e)return Z.apply(n,r),n; +break}}return(u||N(e,d))(r,t,!q,n,bt.test(e)&&c(t.parentNode)||t),n},w.sortStable=B.split("").sort(U).join("")===B,w.detectDuplicates=!!D,L(),w.sortDetached=i(function(e){return 1&e.compareDocumentPosition(H.createElement("div"))}),i(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){return n?void 0:e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),w.attributes&&i(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){return n||"input"!==e.nodeName.toLowerCase()?void 0:e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(nt,function(e,t,n){var r;return n?void 0:e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);it.find=ut,it.expr=ut.selectors,it.expr[":"]=it.expr.pseudos,it.unique=ut.uniqueSort,it.text=ut.getText,it.isXMLDoc=ut.isXML,it.contains=ut.contains;var ct=it.expr.match.needsContext,dt=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,ft=/^.[^:#\[\.,]*$/;it.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?it.find.matchesSelector(r,e)?[r]:[]:it.find.matches(e,it.grep(t,function(e){return 1===e.nodeType}))},it.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(it(e).filter(function(){for(t=0;i>t;t++)if(it.contains(r[t],this))return!0}));for(t=0;i>t;t++)it.find(e,r[t],n);return n=this.pushStack(i>1?it.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},filter:function(e){return this.pushStack(r(this,e||[],!1))},not:function(e){return this.pushStack(r(this,e||[],!0))},is:function(e){return!!r(this,"string"==typeof e&&ct.test(e)?it(e):e||[],!1).length}});var pt,ht=e.document,mt=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,gt=it.fn.init=function(e,t){var n,r;if(!e)return this;if("string"==typeof e){if(n="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:mt.exec(e),!n||!n[1]&&t)return!t||t.jquery?(t||pt).find(e):this.constructor(t).find(e);if(n[1]){if(t=t instanceof it?t[0]:t,it.merge(this,it.parseHTML(n[1],t&&t.nodeType?t.ownerDocument||t:ht,!0)),dt.test(n[1])&&it.isPlainObject(t))for(n in t)it.isFunction(this[n])?this[n](t[n]):this.attr(n,t[n]);return this}if(r=ht.getElementById(n[2]),r&&r.parentNode){if(r.id!==n[2])return pt.find(e);this.length=1,this[0]=r}return this.context=ht,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):it.isFunction(e)?"undefined"!=typeof pt.ready?pt.ready(e):e(it):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),it.makeArray(e,this))};gt.prototype=it.fn,pt=it(ht);var vt=/^(?:parents|prev(?:Until|All))/,yt={children:!0,contents:!0,next:!0,prev:!0};it.extend({dir:function(e,t,n){for(var r=[],i=e[t];i&&9!==i.nodeType&&(void 0===n||1!==i.nodeType||!it(i).is(n));)1===i.nodeType&&r.push(i),i=i[t];return r},sibling:function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}}),it.fn.extend({has:function(e){var t,n=it(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(it.contains(this,n[t]))return!0})},closest:function(e,t){for(var n,r=0,i=this.length,o=[],a=ct.test(e)||"string"!=typeof e?it(e,t||this.context):0;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?a.index(n)>-1:1===n.nodeType&&it.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?it.unique(o):o)},index:function(e){return e?"string"==typeof e?it.inArray(this[0],it(e)):it.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(it.unique(it.merge(this.get(),it(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),it.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return it.dir(e,"parentNode")},parentsUntil:function(e,t,n){return it.dir(e,"parentNode",n)},next:function(e){return i(e,"nextSibling")},prev:function(e){return i(e,"previousSibling")},nextAll:function(e){return it.dir(e,"nextSibling")},prevAll:function(e){return it.dir(e,"previousSibling")},nextUntil:function(e,t,n){return it.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return it.dir(e,"previousSibling",n)},siblings:function(e){return it.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return it.sibling(e.firstChild)},contents:function(e){return it.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:it.merge([],e.childNodes)}},function(e,t){it.fn[e]=function(n,r){var i=it.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=it.filter(r,i)),this.length>1&&(yt[e]||(i=it.unique(i)),vt.test(e)&&(i=i.reverse())),this.pushStack(i)}});var bt=/\S+/g,xt={};it.Callbacks=function(e){e="string"==typeof e?xt[e]||o(e):it.extend({},e);var t,n,r,i,a,s,l=[],u=!e.once&&[],c=function(o){for(n=e.memory&&o,r=!0,a=s||0,s=0,i=l.length,t=!0;l&&i>a;a++)if(l[a].apply(o[0],o[1])===!1&&e.stopOnFalse){n=!1;break}t=!1,l&&(u?u.length&&c(u.shift()):n?l=[]:d.disable())},d={add:function(){if(l){var r=l.length;!function o(t){it.each(t,function(t,n){var r=it.type(n);"function"===r?e.unique&&d.has(n)||l.push(n):n&&n.length&&"string"!==r&&o(n)})}(arguments),t?i=l.length:n&&(s=r,c(n))}return this},remove:function(){return l&&it.each(arguments,function(e,n){for(var r;(r=it.inArray(n,l,r))>-1;)l.splice(r,1),t&&(i>=r&&i--,a>=r&&a--)}),this},has:function(e){return e?it.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],i=0,this},disable:function(){return l=u=n=void 0,this},disabled:function(){return!l},lock:function(){return u=void 0,n||d.disable(),this},locked:function(){return!u},fireWith:function(e,n){return!l||r&&!u||(n=n||[],n=[e,n.slice?n.slice():n],t?u.push(n):c(n)),this},fire:function(){return d.fireWith(this,arguments),this},fired:function(){return!!r}};return d},it.extend({Deferred:function(e){var t=[["resolve","done",it.Callbacks("once memory"),"resolved"],["reject","fail",it.Callbacks("once memory"),"rejected"],["notify","progress",it.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return it.Deferred(function(n){it.each(t,function(t,o){var a=it.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&it.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[o[0]+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?it.extend(e,r):r}},i={};return r.pipe=r.then,it.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=Y.call(arguments),a=o.length,s=1!==a||e&&it.isFunction(e.promise)?a:0,l=1===s?e:it.Deferred(),u=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?Y.call(arguments):i,r===t?l.notifyWith(n,r):--s||l.resolveWith(n,r)}};if(a>1)for(t=new Array(a),n=new Array(a),r=new Array(a);a>i;i++)o[i]&&it.isFunction(o[i].promise)?o[i].promise().done(u(i,r,o)).fail(l.reject).progress(u(i,n,t)):--s;return s||l.resolveWith(r,o),l.promise()}});var wt;it.fn.ready=function(e){return it.ready.promise().done(e),this},it.extend({isReady:!1,readyWait:1,holdReady:function(e){e?it.readyWait++:it.ready(!0)},ready:function(e){if(e===!0?!--it.readyWait:!it.isReady){if(!ht.body)return setTimeout(it.ready);it.isReady=!0,e!==!0&&--it.readyWait>0||(wt.resolveWith(ht,[it]),it.fn.triggerHandler&&(it(ht).triggerHandler("ready"),it(ht).off("ready")))}}}),it.ready.promise=function(t){if(!wt)if(wt=it.Deferred(),"complete"===ht.readyState)setTimeout(it.ready);else if(ht.addEventListener)ht.addEventListener("DOMContentLoaded",s,!1),e.addEventListener("load",s,!1);else{ht.attachEvent("onreadystatechange",s),e.attachEvent("onload",s);var n=!1;try{n=null==e.frameElement&&ht.documentElement}catch(r){}n&&n.doScroll&&!function i(){if(!it.isReady){try{n.doScroll("left")}catch(e){return setTimeout(i,50)}a(),it.ready()}}()}return wt.promise(t)};var Tt,Et="undefined";for(Tt in it(nt))break;nt.ownLast="0"!==Tt,nt.inlineBlockNeedsLayout=!1,it(function(){var e,t,n,r;n=ht.getElementsByTagName("body")[0],n&&n.style&&(t=ht.createElement("div"),r=ht.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),typeof t.style.zoom!==Et&&(t.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",nt.inlineBlockNeedsLayout=e=3===t.offsetWidth,e&&(n.style.zoom=1)),n.removeChild(r))}),function(){var e=ht.createElement("div");if(null==nt.deleteExpando){nt.deleteExpando=!0;try{delete e.test}catch(t){nt.deleteExpando=!1}}e=null}(),it.acceptData=function(e){var t=it.noData[(e.nodeName+" ").toLowerCase()],n=+e.nodeType||1;return 1!==n&&9!==n?!1:!t||t!==!0&&e.getAttribute("classid")===t};var kt=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Ct=/([A-Z])/g;it.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?it.cache[e[it.expando]]:e[it.expando],!!e&&!u(e)},data:function(e,t,n){return c(e,t,n)},removeData:function(e,t){return d(e,t)},_data:function(e,t,n){return c(e,t,n,!0)},_removeData:function(e,t){return d(e,t,!0)}}),it.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=it.data(o),1===o.nodeType&&!it._data(o,"parsedAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=it.camelCase(r.slice(5)),l(o,r,i[r])));it._data(o,"parsedAttrs",!0)}return i}return"object"==typeof e?this.each(function(){it.data(this,e)}):arguments.length>1?this.each(function(){it.data(this,e,t)}):o?l(o,e,it.data(o,e)):void 0},removeData:function(e){return this.each(function(){it.removeData(this,e)})}}),it.extend({queue:function(e,t,n){var r;return e?(t=(t||"fx")+"queue",r=it._data(e,t),n&&(!r||it.isArray(n)?r=it._data(e,t,it.makeArray(n)):r.push(n)),r||[]):void 0},dequeue:function(e,t){t=t||"fx";var n=it.queue(e,t),r=n.length,i=n.shift(),o=it._queueHooks(e,t),a=function(){it.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return it._data(e,n)||it._data(e,n,{empty:it.Callbacks("once memory").add(function(){it._removeData(e,t+"queue"),it._removeData(e,n)})})}}),it.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length<n?it.queue(this[0],e):void 0===t?this:this.each(function(){var n=it.queue(this,e,t);it._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&it.dequeue(this,e)})},dequeue:function(e){return this.each(function(){it.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=it.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};for("string"!=typeof e&&(t=e,e=void 0),e=e||"fx";a--;)n=it._data(o[a],e+"queueHooks"),n&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var Nt=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,St=["Top","Right","Bottom","Left"],jt=function(e,t){return e=t||e,"none"===it.css(e,"display")||!it.contains(e.ownerDocument,e)},At=it.access=function(e,t,n,r,i,o,a){var s=0,l=e.length,u=null==n;if("object"===it.type(n)){i=!0;for(s in n)it.access(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,it.isFunction(r)||(a=!0),u&&(a?(t.call(e,r),t=null):(u=t,t=function(e,t,n){return u.call(it(e),n)})),t))for(;l>s;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:u?t.call(e):l?t(e[0],n):o},Dt=/^(?:checkbox|radio)$/i;!function(){var e=ht.createElement("input"),t=ht.createElement("div"),n=ht.createDocumentFragment();if(t.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",nt.leadingWhitespace=3===t.firstChild.nodeType,nt.tbody=!t.getElementsByTagName("tbody").length,nt.htmlSerialize=!!t.getElementsByTagName("link").length,nt.html5Clone="<:nav></:nav>"!==ht.createElement("nav").cloneNode(!0).outerHTML,e.type="checkbox",e.checked=!0,n.appendChild(e),nt.appendChecked=e.checked,t.innerHTML="<textarea>x</textarea>",nt.noCloneChecked=!!t.cloneNode(!0).lastChild.defaultValue,n.appendChild(t),t.innerHTML="<input type='radio' checked='checked' name='t'/>",nt.checkClone=t.cloneNode(!0).cloneNode(!0).lastChild.checked,nt.noCloneEvent=!0,t.attachEvent&&(t.attachEvent("onclick",function(){nt.noCloneEvent=!1}),t.cloneNode(!0).click()),null==nt.deleteExpando){nt.deleteExpando=!0;try{delete t.test}catch(r){nt.deleteExpando=!1}}}(),function(){var t,n,r=ht.createElement("div");for(t in{submit:!0,change:!0,focusin:!0})n="on"+t,(nt[t+"Bubbles"]=n in e)||(r.setAttribute(n,"t"),nt[t+"Bubbles"]=r.attributes[n].expando===!1);r=null}();var Lt=/^(?:input|select|textarea)$/i,Ht=/^key/,_t=/^(?:mouse|pointer|contextmenu)|click/,qt=/^(?:focusinfocus|focusoutblur)$/,Ft=/^([^.]*)(?:\.(.+)|)$/;it.event={global:{},add:function(e,t,n,r,i){var o,a,s,l,u,c,d,f,p,h,m,g=it._data(e);if(g){for(n.handler&&(l=n,n=l.handler,i=l.selector),n.guid||(n.guid=it.guid++),(a=g.events)||(a=g.events={}),(c=g.handle)||(c=g.handle=function(e){return typeof it===Et||e&&it.event.triggered===e.type?void 0:it.event.dispatch.apply(c.elem,arguments)},c.elem=e),t=(t||"").match(bt)||[""],s=t.length;s--;)o=Ft.exec(t[s])||[],p=m=o[1],h=(o[2]||"").split(".").sort(),p&&(u=it.event.special[p]||{},p=(i?u.delegateType:u.bindType)||p,u=it.event.special[p]||{},d=it.extend({type:p,origType:m,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&it.expr.match.needsContext.test(i),namespace:h.join(".")},l),(f=a[p])||(f=a[p]=[],f.delegateCount=0,u.setup&&u.setup.call(e,r,h,c)!==!1||(e.addEventListener?e.addEventListener(p,c,!1):e.attachEvent&&e.attachEvent("on"+p,c))),u.add&&(u.add.call(e,d),d.handler.guid||(d.handler.guid=n.guid)),i?f.splice(f.delegateCount++,0,d):f.push(d),it.event.global[p]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,d,f,p,h,m,g=it.hasData(e)&&it._data(e);if(g&&(c=g.events)){for(t=(t||"").match(bt)||[""],u=t.length;u--;)if(s=Ft.exec(t[u])||[],p=m=s[1],h=(s[2]||"").split(".").sort(),p){for(d=it.event.special[p]||{},p=(r?d.delegateType:d.bindType)||p,f=c[p]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;o--;)a=f[o],!i&&m!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,d.remove&&d.remove.call(e,a));l&&!f.length&&(d.teardown&&d.teardown.call(e,h,g.handle)!==!1||it.removeEvent(e,p,g.handle),delete c[p])}else for(p in c)it.event.remove(e,p+t[u],n,r,!0);it.isEmptyObject(c)&&(delete g.handle,it._removeData(e,"events"))}},trigger:function(t,n,r,i){var o,a,s,l,u,c,d,f=[r||ht],p=tt.call(t,"type")?t.type:t,h=tt.call(t,"namespace")?t.namespace.split("."):[];if(s=c=r=r||ht,3!==r.nodeType&&8!==r.nodeType&&!qt.test(p+it.event.triggered)&&(p.indexOf(".")>=0&&(h=p.split("."),p=h.shift(),h.sort()),a=p.indexOf(":")<0&&"on"+p,t=t[it.expando]?t:new it.Event(p,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.namespace_re=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:it.makeArray(n,[t]),u=it.event.special[p]||{},i||!u.trigger||u.trigger.apply(r,n)!==!1)){if(!i&&!u.noBubble&&!it.isWindow(r)){for(l=u.delegateType||p,qt.test(l+p)||(s=s.parentNode);s;s=s.parentNode)f.push(s),c=s;c===(r.ownerDocument||ht)&&f.push(c.defaultView||c.parentWindow||e)}for(d=0;(s=f[d++])&&!t.isPropagationStopped();)t.type=d>1?l:u.bindType||p,o=(it._data(s,"events")||{})[t.type]&&it._data(s,"handle"),o&&o.apply(s,n),o=a&&s[a],o&&o.apply&&it.acceptData(s)&&(t.result=o.apply(s,n),t.result===!1&&t.preventDefault());if(t.type=p,!i&&!t.isDefaultPrevented()&&(!u._default||u._default.apply(f.pop(),n)===!1)&&it.acceptData(r)&&a&&r[p]&&!it.isWindow(r)){c=r[a],c&&(r[a]=null),it.event.triggered=p;try{r[p]()}catch(m){}it.event.triggered=void 0,c&&(r[a]=c)}return t.result}},dispatch:function(e){e=it.event.fix(e);var t,n,r,i,o,a=[],s=Y.call(arguments),l=(it._data(this,"events")||{})[e.type]||[],u=it.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!u.preDispatch||u.preDispatch.call(this,e)!==!1){for(a=it.event.handlers.call(this,e,l),t=0;(i=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=i.elem,o=0;(r=i.handlers[o++])&&!e.isImmediatePropagationStopped();)(!e.namespace_re||e.namespace_re.test(r.namespace))&&(e.handleObj=r,e.data=r.data,n=((it.event.special[r.origType]||{}).handle||r.handler).apply(i.elem,s),void 0!==n&&(e.result=n)===!1&&(e.preventDefault(),e.stopPropagation()));return u.postDispatch&&u.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,a=[],s=t.delegateCount,l=e.target;if(s&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(i=[],o=0;s>o;o++)r=t[o],n=r.selector+" ",void 0===i[n]&&(i[n]=r.needsContext?it(n,this).index(l)>=0:it.find(n,this,null,[l]).length),i[n]&&i.push(r);i.length&&a.push({elem:l,handlers:i})}return s<t.length&&a.push({elem:this,handlers:t.slice(s)}),a},fix:function(e){if(e[it.expando])return e;var t,n,r,i=e.type,o=e,a=this.fixHooks[i];for(a||(this.fixHooks[i]=a=_t.test(i)?this.mouseHooks:Ht.test(i)?this.keyHooks:{}),r=a.props?this.props.concat(a.props):this.props,e=new it.Event(o),t=r.length;t--;)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||ht),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,a.filter?a.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,t){var n,r,i,o=t.button,a=t.fromElement;return null==e.pageX&&null!=t.clientX&&(r=e.target.ownerDocument||ht,i=r.documentElement,n=r.body,e.pageX=t.clientX+(i&&i.scrollLeft||n&&n.scrollLeft||0)-(i&&i.clientLeft||n&&n.clientLeft||0),e.pageY=t.clientY+(i&&i.scrollTop||n&&n.scrollTop||0)-(i&&i.clientTop||n&&n.clientTop||0)),!e.relatedTarget&&a&&(e.relatedTarget=a===e.target?t.toElement:a),e.which||void 0===o||(e.which=1&o?1:2&o?3:4&o?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==h()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===h()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return it.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(e){return it.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=it.extend(new it.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?it.event.trigger(i,null,t):it.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},it.removeEvent=ht.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===Et&&(e[r]=null),e.detachEvent(r,n))},it.Event=function(e,t){return this instanceof it.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&e.returnValue===!1?f:p):this.type=e,t&&it.extend(this,t),this.timeStamp=e&&e.timeStamp||it.now(),void(this[it.expando]=!0)):new it.Event(e,t)},it.Event.prototype={isDefaultPrevented:p,isPropagationStopped:p,isImmediatePropagationStopped:p,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=f,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=f,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=f,e&&e.stopImmediatePropagation&&e.stopImmediatePropagation(),this.stopPropagation()}},it.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,t){it.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!it.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),nt.submitBubbles||(it.event.special.submit={setup:function(){return it.nodeName(this,"form")?!1:void it.event.add(this,"click._submit keypress._submit",function(e){var t=e.target,n=it.nodeName(t,"input")||it.nodeName(t,"button")?t.form:void 0;n&&!it._data(n,"submitBubbles")&&(it.event.add(n,"submit._submit",function(e){e._submit_bubble=!0}),it._data(n,"submitBubbles",!0))})},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&it.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return it.nodeName(this,"form")?!1:void it.event.remove(this,"._submit")}}),nt.changeBubbles||(it.event.special.change={setup:function(){return Lt.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(it.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),it.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),it.event.simulate("change",this,e,!0)})),!1):void it.event.add(this,"beforeactivate._change",function(e){var t=e.target;Lt.test(t.nodeName)&&!it._data(t,"changeBubbles")&&(it.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||it.event.simulate("change",this.parentNode,e,!0)}),it._data(t,"changeBubbles",!0))})},handle:function(e){var t=e.target;return this!==t||e.isSimulated||e.isTrigger||"radio"!==t.type&&"checkbox"!==t.type?e.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return it.event.remove(this,"._change"),!Lt.test(this.nodeName)}}),nt.focusinBubbles||it.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){it.event.simulate(t,e.target,it.event.fix(e),!0)};it.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=it._data(r,t);i||r.addEventListener(e,n,!0),it._data(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=it._data(r,t)-1;i?it._data(r,t,i):(r.removeEventListener(e,n,!0),it._removeData(r,t))}}}),it.fn.extend({on:function(e,t,n,r,i){var o,a;if("object"==typeof e){"string"!=typeof t&&(n=n||t,t=void 0);for(o in e)this.on(o,t,n,e[o],i);return this}if(null==n&&null==r?(r=t,n=t=void 0):null==r&&("string"==typeof t?(r=n,n=void 0):(r=n,n=t,t=void 0)),r===!1)r=p;else if(!r)return this;return 1===i&&(a=r,r=function(e){return it().off(e),a.apply(this,arguments)},r.guid=a.guid||(a.guid=it.guid++)),this.each(function(){it.event.add(this,e,r,n,t)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,it(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return(t===!1||"function"==typeof t)&&(n=t,t=void 0),n===!1&&(n=p),this.each(function(){it.event.remove(this,e,n,t)})},trigger:function(e,t){return this.each(function(){it.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?it.event.trigger(e,t,n,!0):void 0}});var Mt="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",Ot=/ jQuery\d+="(?:null|\d+)"/g,Rt=new RegExp("<(?:"+Mt+")[\\s/>]","i"),Bt=/^\s+/,Pt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,It=/<([\w:]+)/,Wt=/<tbody/i,$t=/<|&#?\w+;/,zt=/<(?:script|style|link)/i,Xt=/checked\s*(?:[^=]|=\s*.checked.)/i,Ut=/^$|\/(?:java|ecma)script/i,Vt=/^true\/(.*)/,Gt=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,Yt={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:nt.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},Jt=m(ht),Kt=Jt.appendChild(ht.createElement("div"));Yt.optgroup=Yt.option,Yt.tbody=Yt.tfoot=Yt.colgroup=Yt.caption=Yt.thead,Yt.th=Yt.td,it.extend({clone:function(e,t,n){var r,i,o,a,s,l=it.contains(e.ownerDocument,e);if(nt.html5Clone||it.isXMLDoc(e)||!Rt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Kt.innerHTML=e.outerHTML,Kt.removeChild(o=Kt.firstChild)),!(nt.noCloneEvent&&nt.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||it.isXMLDoc(e)))for(r=g(o),s=g(e),a=0;null!=(i=s[a]);++a)r[a]&&E(i,r[a]);if(t)if(n)for(s=s||g(e),r=r||g(o),a=0;null!=(i=s[a]);a++)T(i,r[a]);else T(e,o);return r=g(o,"script"),r.length>0&&w(r,!l&&g(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){for(var i,o,a,s,l,u,c,d=e.length,f=m(t),p=[],h=0;d>h;h++)if(o=e[h],o||0===o)if("object"===it.type(o))it.merge(p,o.nodeType?[o]:o);else if($t.test(o)){for(s=s||f.appendChild(t.createElement("div")),l=(It.exec(o)||["",""])[1].toLowerCase(),c=Yt[l]||Yt._default,s.innerHTML=c[1]+o.replace(Pt,"<$1></$2>")+c[2],i=c[0];i--;)s=s.lastChild;if(!nt.leadingWhitespace&&Bt.test(o)&&p.push(t.createTextNode(Bt.exec(o)[0])),!nt.tbody)for(o="table"!==l||Wt.test(o)?"<table>"!==c[1]||Wt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;i--;)it.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u);for(it.merge(p,s.childNodes),s.textContent="";s.firstChild;)s.removeChild(s.firstChild);s=f.lastChild}else p.push(t.createTextNode(o));for(s&&f.removeChild(s),nt.appendChecked||it.grep(g(p,"input"),v),h=0;o=p[h++];)if((!r||-1===it.inArray(o,r))&&(a=it.contains(o.ownerDocument,o),s=g(f.appendChild(o),"script"),a&&w(s),n))for(i=0;o=s[i++];)Ut.test(o.type||"")&&n.push(o);return s=null,f},cleanData:function(e,t){for(var n,r,i,o,a=0,s=it.expando,l=it.cache,u=nt.deleteExpando,c=it.event.special;null!=(n=e[a]);a++)if((t||it.acceptData(n))&&(i=n[s],o=i&&l[i])){if(o.events)for(r in o.events)c[r]?it.event.remove(n,r):it.removeEvent(n,r,o.handle);l[i]&&(delete l[i],u?delete n[s]:typeof n.removeAttribute!==Et?n.removeAttribute(s):n[s]=null,G.push(i))}}}),it.fn.extend({text:function(e){return At(this,function(e){return void 0===e?it.text(this):this.empty().append((this[0]&&this[0].ownerDocument||ht).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=y(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=y(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){for(var n,r=e?it.filter(e,this):this,i=0;null!=(n=r[i]);i++)t||1!==n.nodeType||it.cleanData(g(n)),n.parentNode&&(t&&it.contains(n.ownerDocument,n)&&w(g(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&it.cleanData(g(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&it.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return it.clone(this,e,t)})},html:function(e){return At(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e)return 1===t.nodeType?t.innerHTML.replace(Ot,""):void 0;if(!("string"!=typeof e||zt.test(e)||!nt.htmlSerialize&&Rt.test(e)||!nt.leadingWhitespace&&Bt.test(e)||Yt[(It.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(Pt,"<$1></$2>");try{for(;r>n;n++)t=this[n]||{},1===t.nodeType&&(it.cleanData(g(t,!1)),t.innerHTML=e);t=0}catch(i){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=arguments[0];return this.domManip(arguments,function(t){e=this.parentNode,it.cleanData(g(this)),e&&e.replaceChild(t,this)}),e&&(e.length||e.nodeType)?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t){e=J.apply([],e);var n,r,i,o,a,s,l=0,u=this.length,c=this,d=u-1,f=e[0],p=it.isFunction(f);if(p||u>1&&"string"==typeof f&&!nt.checkClone&&Xt.test(f))return this.each(function(n){var r=c.eq(n);p&&(e[0]=f.call(this,n,r.html())),r.domManip(e,t)});if(u&&(s=it.buildFragment(e,this[0].ownerDocument,!1,this),n=s.firstChild,1===s.childNodes.length&&(s=n),n)){for(o=it.map(g(s,"script"),b),i=o.length;u>l;l++)r=s,l!==d&&(r=it.clone(r,!0,!0),i&&it.merge(o,g(r,"script"))),t.call(this[l],r,l);if(i)for(a=o[o.length-1].ownerDocument,it.map(o,x),l=0;i>l;l++)r=o[l],Ut.test(r.type||"")&&!it._data(r,"globalEval")&&it.contains(a,r)&&(r.src?it._evalUrl&&it._evalUrl(r.src):it.globalEval((r.text||r.textContent||r.innerHTML||"").replace(Gt,"")));s=n=null}return this}}),it.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){it.fn[e]=function(e){for(var n,r=0,i=[],o=it(e),a=o.length-1;a>=r;r++)n=r===a?this:this.clone(!0),it(o[r])[t](n),K.apply(i,n.get());return this.pushStack(i)}});var Qt,Zt={};!function(){var e;nt.shrinkWrapBlocks=function(){if(null!=e)return e;e=!1;var t,n,r;return n=ht.getElementsByTagName("body")[0],n&&n.style?(t=ht.createElement("div"),r=ht.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),typeof t.style.zoom!==Et&&(t.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",t.appendChild(ht.createElement("div")).style.width="5px",e=3!==t.offsetWidth),n.removeChild(r),e):void 0}}();var en,tn,nn=/^margin/,rn=new RegExp("^("+Nt+")(?!px)[a-z%]+$","i"),on=/^(top|right|bottom|left)$/;e.getComputedStyle?(en=function(e){return e.ownerDocument.defaultView.getComputedStyle(e,null)},tn=function(e,t,n){var r,i,o,a,s=e.style;return n=n||en(e),a=n?n.getPropertyValue(t)||n[t]:void 0,n&&(""!==a||it.contains(e.ownerDocument,e)||(a=it.style(e,t)),rn.test(a)&&nn.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0===a?a:a+""}):ht.documentElement.currentStyle&&(en=function(e){return e.currentStyle},tn=function(e,t,n){var r,i,o,a,s=e.style;return n=n||en(e),a=n?n[t]:void 0,null==a&&s&&s[t]&&(a=s[t]),rn.test(a)&&!on.test(t)&&(r=s.left,i=e.runtimeStyle,o=i&&i.left,o&&(i.left=e.currentStyle.left),s.left="fontSize"===t?"1em":a,a=s.pixelLeft+"px",s.left=r,o&&(i.left=o)),void 0===a?a:a+""||"auto"}),function(){function t(){var t,n,r,i;n=ht.getElementsByTagName("body")[0],n&&n.style&&(t=ht.createElement("div"),r=ht.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),t.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",o=a=!1,l=!0,e.getComputedStyle&&(o="1%"!==(e.getComputedStyle(t,null)||{}).top,a="4px"===(e.getComputedStyle(t,null)||{width:"4px"}).width,i=t.appendChild(ht.createElement("div")),i.style.cssText=t.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",t.style.width="1px",l=!parseFloat((e.getComputedStyle(i,null)||{}).marginRight)),t.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=t.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",s=0===i[0].offsetHeight,s&&(i[0].style.display="",i[1].style.display="none",s=0===i[0].offsetHeight),n.removeChild(r))}var n,r,i,o,a,s,l;n=ht.createElement("div"),n.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",i=n.getElementsByTagName("a")[0],r=i&&i.style,r&&(r.cssText="float:left;opacity:.5",nt.opacity="0.5"===r.opacity,nt.cssFloat=!!r.cssFloat,n.style.backgroundClip="content-box",n.cloneNode(!0).style.backgroundClip="",nt.clearCloneStyle="content-box"===n.style.backgroundClip,nt.boxSizing=""===r.boxSizing||""===r.MozBoxSizing||""===r.WebkitBoxSizing,it.extend(nt,{reliableHiddenOffsets:function(){return null==s&&t(),s +},boxSizingReliable:function(){return null==a&&t(),a},pixelPosition:function(){return null==o&&t(),o},reliableMarginRight:function(){return null==l&&t(),l}}))}(),it.swap=function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i};var an=/alpha\([^)]*\)/i,sn=/opacity\s*=\s*([^)]*)/,ln=/^(none|table(?!-c[ea]).+)/,un=new RegExp("^("+Nt+")(.*)$","i"),cn=new RegExp("^([+-])=("+Nt+")","i"),dn={position:"absolute",visibility:"hidden",display:"block"},fn={letterSpacing:"0",fontWeight:"400"},pn=["Webkit","O","Moz","ms"];it.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=tn(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":nt.cssFloat?"cssFloat":"styleFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=it.camelCase(t),l=e.style;if(t=it.cssProps[s]||(it.cssProps[s]=S(l,s)),a=it.cssHooks[t]||it.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];if(o=typeof n,"string"===o&&(i=cn.exec(n))&&(n=(i[1]+1)*i[2]+parseFloat(it.css(e,t)),o="number"),null!=n&&n===n&&("number"!==o||it.cssNumber[s]||(n+="px"),nt.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),!(a&&"set"in a&&void 0===(n=a.set(e,n,r)))))try{l[t]=n}catch(u){}}},css:function(e,t,n,r){var i,o,a,s=it.camelCase(t);return t=it.cssProps[s]||(it.cssProps[s]=S(e.style,s)),a=it.cssHooks[t]||it.cssHooks[s],a&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=tn(e,t,r)),"normal"===o&&t in fn&&(o=fn[t]),""===n||n?(i=parseFloat(o),n===!0||it.isNumeric(i)?i||0:o):o}}),it.each(["height","width"],function(e,t){it.cssHooks[t]={get:function(e,n,r){return n?ln.test(it.css(e,"display"))&&0===e.offsetWidth?it.swap(e,dn,function(){return L(e,t,r)}):L(e,t,r):void 0},set:function(e,n,r){var i=r&&en(e);return A(e,n,r?D(e,t,r,nt.boxSizing&&"border-box"===it.css(e,"boxSizing",!1,i),i):0)}}}),nt.opacity||(it.cssHooks.opacity={get:function(e,t){return sn.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=it.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===it.trim(o.replace(an,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=an.test(o)?o.replace(an,i):o+" "+i)}}),it.cssHooks.marginRight=N(nt.reliableMarginRight,function(e,t){return t?it.swap(e,{display:"inline-block"},tn,[e,"marginRight"]):void 0}),it.each({margin:"",padding:"",border:"Width"},function(e,t){it.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];4>r;r++)i[e+St[r]+t]=o[r]||o[r-2]||o[0];return i}},nn.test(e)||(it.cssHooks[e+t].set=A)}),it.fn.extend({css:function(e,t){return At(this,function(e,t,n){var r,i,o={},a=0;if(it.isArray(t)){for(r=en(e),i=t.length;i>a;a++)o[t[a]]=it.css(e,t[a],!1,r);return o}return void 0!==n?it.style(e,t,n):it.css(e,t)},e,t,arguments.length>1)},show:function(){return j(this,!0)},hide:function(){return j(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){jt(this)?it(this).show():it(this).hide()})}}),it.Tween=H,H.prototype={constructor:H,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(it.cssNumber[n]?"":"px")},cur:function(){var e=H.propHooks[this.prop];return e&&e.get?e.get(this):H.propHooks._default.get(this)},run:function(e){var t,n=H.propHooks[this.prop];return this.pos=t=this.options.duration?it.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):H.propHooks._default.set(this),this}},H.prototype.init.prototype=H.prototype,H.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=it.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){it.fx.step[e.prop]?it.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[it.cssProps[e.prop]]||it.cssHooks[e.prop])?it.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},H.propHooks.scrollTop=H.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},it.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},it.fx=H.prototype.init,it.fx.step={};var hn,mn,gn=/^(?:toggle|show|hide)$/,vn=new RegExp("^(?:([+-])=|)("+Nt+")([a-z%]*)$","i"),yn=/queueHooks$/,bn=[M],xn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=vn.exec(t),o=i&&i[3]||(it.cssNumber[e]?"":"px"),a=(it.cssNumber[e]||"px"!==o&&+r)&&vn.exec(it.css(n.elem,e)),s=1,l=20;if(a&&a[3]!==o){o=o||a[3],i=i||[],a=+r||1;do s=s||".5",a/=s,it.style(n.elem,e,a+o);while(s!==(s=n.cur()/r)&&1!==s&&--l)}return i&&(a=n.start=+a||+r||0,n.unit=o,n.end=i[1]?a+(i[1]+1)*i[2]:+i[2]),n}]};it.Animation=it.extend(R,{tweener:function(e,t){it.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");for(var n,r=0,i=e.length;i>r;r++)n=e[r],xn[n]=xn[n]||[],xn[n].unshift(t)},prefilter:function(e,t){t?bn.unshift(e):bn.push(e)}}),it.speed=function(e,t,n){var r=e&&"object"==typeof e?it.extend({},e):{complete:n||!n&&t||it.isFunction(e)&&e,duration:e,easing:n&&t||t&&!it.isFunction(t)&&t};return r.duration=it.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in it.fx.speeds?it.fx.speeds[r.duration]:it.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){it.isFunction(r.old)&&r.old.call(this),r.queue&&it.dequeue(this,r.queue)},r},it.fn.extend({fadeTo:function(e,t,n,r){return this.filter(jt).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=it.isEmptyObject(e),o=it.speed(t,n,r),a=function(){var t=R(this,it.extend({},e),o);(i||it._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=void 0),t&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=it.timers,a=it._data(this);if(i)a[i]&&a[i].stop&&r(a[i]);else for(i in a)a[i]&&a[i].stop&&yn.test(i)&&r(a[i]);for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));(t||!n)&&it.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=it._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=it.timers,a=r?r.length:0;for(n.finish=!0,it.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),it.each(["toggle","show","hide"],function(e,t){var n=it.fn[t];it.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(q(t,!0),e,r,i)}}),it.each({slideDown:q("show"),slideUp:q("hide"),slideToggle:q("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){it.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),it.timers=[],it.fx.tick=function(){var e,t=it.timers,n=0;for(hn=it.now();n<t.length;n++)e=t[n],e()||t[n]!==e||t.splice(n--,1);t.length||it.fx.stop(),hn=void 0},it.fx.timer=function(e){it.timers.push(e),e()?it.fx.start():it.timers.pop()},it.fx.interval=13,it.fx.start=function(){mn||(mn=setInterval(it.fx.tick,it.fx.interval))},it.fx.stop=function(){clearInterval(mn),mn=null},it.fx.speeds={slow:600,fast:200,_default:400},it.fn.delay=function(e,t){return e=it.fx?it.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},function(){var e,t,n,r,i;t=ht.createElement("div"),t.setAttribute("className","t"),t.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",r=t.getElementsByTagName("a")[0],n=ht.createElement("select"),i=n.appendChild(ht.createElement("option")),e=t.getElementsByTagName("input")[0],r.style.cssText="top:1px",nt.getSetAttribute="t"!==t.className,nt.style=/top/.test(r.getAttribute("style")),nt.hrefNormalized="/a"===r.getAttribute("href"),nt.checkOn=!!e.value,nt.optSelected=i.selected,nt.enctype=!!ht.createElement("form").enctype,n.disabled=!0,nt.optDisabled=!i.disabled,e=ht.createElement("input"),e.setAttribute("value",""),nt.input=""===e.getAttribute("value"),e.value="t",e.setAttribute("type","radio"),nt.radioValue="t"===e.value}();var wn=/\r/g;it.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=it.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,it(this).val()):e,null==i?i="":"number"==typeof i?i+="":it.isArray(i)&&(i=it.map(i,function(e){return null==e?"":e+""})),t=it.valHooks[this.type]||it.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return t=it.valHooks[i.type]||it.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(wn,""):null==n?"":n)}}}),it.extend({valHooks:{option:{get:function(e){var t=it.find.attr(e,"value");return null!=t?t:it.trim(it.text(e))}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(nt.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&it.nodeName(n.parentNode,"optgroup"))){if(t=it(n).val(),o)return t;a.push(t)}return a},set:function(e,t){for(var n,r,i=e.options,o=it.makeArray(t),a=i.length;a--;)if(r=i[a],it.inArray(it.valHooks.option.get(r),o)>=0)try{r.selected=n=!0}catch(s){r.scrollHeight}else r.selected=!1;return n||(e.selectedIndex=-1),i}}}}),it.each(["radio","checkbox"],function(){it.valHooks[this]={set:function(e,t){return it.isArray(t)?e.checked=it.inArray(it(e).val(),t)>=0:void 0}},nt.checkOn||(it.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Tn,En,kn=it.expr.attrHandle,Cn=/^(?:checked|selected)$/i,Nn=nt.getSetAttribute,Sn=nt.input;it.fn.extend({attr:function(e,t){return At(this,it.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){it.removeAttr(this,e)})}}),it.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(e&&3!==o&&8!==o&&2!==o)return typeof e.getAttribute===Et?it.prop(e,t,n):(1===o&&it.isXMLDoc(e)||(t=t.toLowerCase(),r=it.attrHooks[t]||(it.expr.match.bool.test(t)?En:Tn)),void 0===n?r&&"get"in r&&null!==(i=r.get(e,t))?i:(i=it.find.attr(e,t),null==i?void 0:i):null!==n?r&&"set"in r&&void 0!==(i=r.set(e,n,t))?i:(e.setAttribute(t,n+""),n):void it.removeAttr(e,t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(bt);if(o&&1===e.nodeType)for(;n=o[i++];)r=it.propFix[n]||n,it.expr.match.bool.test(n)?Sn&&Nn||!Cn.test(n)?e[r]=!1:e[it.camelCase("default-"+n)]=e[r]=!1:it.attr(e,n,""),e.removeAttribute(Nn?n:r)},attrHooks:{type:{set:function(e,t){if(!nt.radioValue&&"radio"===t&&it.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}}}),En={set:function(e,t,n){return t===!1?it.removeAttr(e,n):Sn&&Nn||!Cn.test(n)?e.setAttribute(!Nn&&it.propFix[n]||n,n):e[it.camelCase("default-"+n)]=e[n]=!0,n}},it.each(it.expr.match.bool.source.match(/\w+/g),function(e,t){var n=kn[t]||it.find.attr;kn[t]=Sn&&Nn||!Cn.test(t)?function(e,t,r){var i,o;return r||(o=kn[t],kn[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,kn[t]=o),i}:function(e,t,n){return n?void 0:e[it.camelCase("default-"+t)]?t.toLowerCase():null}}),Sn&&Nn||(it.attrHooks.value={set:function(e,t,n){return it.nodeName(e,"input")?void(e.defaultValue=t):Tn&&Tn.set(e,t,n)}}),Nn||(Tn={set:function(e,t,n){var r=e.getAttributeNode(n);return r||e.setAttributeNode(r=e.ownerDocument.createAttribute(n)),r.value=t+="","value"===n||t===e.getAttribute(n)?t:void 0}},kn.id=kn.name=kn.coords=function(e,t,n){var r;return n?void 0:(r=e.getAttributeNode(t))&&""!==r.value?r.value:null},it.valHooks.button={get:function(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:void 0},set:Tn.set},it.attrHooks.contenteditable={set:function(e,t,n){Tn.set(e,""===t?!1:t,n)}},it.each(["width","height"],function(e,t){it.attrHooks[t]={set:function(e,n){return""===n?(e.setAttribute(t,"auto"),n):void 0}}})),nt.style||(it.attrHooks.style={get:function(e){return e.style.cssText||void 0},set:function(e,t){return e.style.cssText=t+""}});var jn=/^(?:input|select|textarea|button|object)$/i,An=/^(?:a|area)$/i;it.fn.extend({prop:function(e,t){return At(this,it.prop,e,t,arguments.length>1)},removeProp:function(e){return e=it.propFix[e]||e,this.each(function(){try{this[e]=void 0,delete this[e]}catch(t){}})}}),it.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(e,t,n){var r,i,o,a=e.nodeType;if(e&&3!==a&&8!==a&&2!==a)return o=1!==a||!it.isXMLDoc(e),o&&(t=it.propFix[t]||t,i=it.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=it.find.attr(e,"tabindex");return t?parseInt(t,10):jn.test(e.nodeName)||An.test(e.nodeName)&&e.href?0:-1}}}}),nt.hrefNormalized||it.each(["href","src"],function(e,t){it.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),nt.optSelected||(it.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),it.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){it.propFix[this.toLowerCase()]=this}),nt.enctype||(it.propFix.enctype="encoding");var Dn=/[\t\r\n\f]/g;it.fn.extend({addClass:function(e){var t,n,r,i,o,a,s=0,l=this.length,u="string"==typeof e&&e;if(it.isFunction(e))return this.each(function(t){it(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(bt)||[];l>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(Dn," "):" ")){for(o=0;i=t[o++];)r.indexOf(" "+i+" ")<0&&(r+=i+" ");a=it.trim(r),n.className!==a&&(n.className=a)}return this},removeClass:function(e){var t,n,r,i,o,a,s=0,l=this.length,u=0===arguments.length||"string"==typeof e&&e;if(it.isFunction(e))return this.each(function(t){it(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(bt)||[];l>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(Dn," "):"")){for(o=0;i=t[o++];)for(;r.indexOf(" "+i+" ")>=0;)r=r.replace(" "+i+" "," ");a=e?it.trim(r):"",n.className!==a&&(n.className=a)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):this.each(it.isFunction(e)?function(n){it(this).toggleClass(e.call(this,n,this.className,t),t)}:function(){if("string"===n)for(var t,r=0,i=it(this),o=e.match(bt)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else(n===Et||"boolean"===n)&&(this.className&&it._data(this,"__className__",this.className),this.className=this.className||e===!1?"":it._data(this,"__className__")||"")})},hasClass:function(e){for(var t=" "+e+" ",n=0,r=this.length;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(Dn," ").indexOf(t)>=0)return!0;return!1}}),it.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 contextmenu".split(" "),function(e,t){it.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),it.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var Ln=it.now(),Hn=/\?/,_n=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;it.parseJSON=function(t){if(e.JSON&&e.JSON.parse)return e.JSON.parse(t+"");var n,r=null,i=it.trim(t+"");return i&&!it.trim(i.replace(_n,function(e,t,i,o){return n&&t&&(r=0),0===r?e:(n=i||t,r+=!o-!i,"")}))?Function("return "+i)():it.error("Invalid JSON: "+t)},it.parseXML=function(t){var n,r;if(!t||"string"!=typeof t)return null;try{e.DOMParser?(r=new DOMParser,n=r.parseFromString(t,"text/xml")):(n=new ActiveXObject("Microsoft.XMLDOM"),n.async="false",n.loadXML(t))}catch(i){n=void 0}return n&&n.documentElement&&!n.getElementsByTagName("parsererror").length||it.error("Invalid XML: "+t),n};var qn,Fn,Mn=/#.*$/,On=/([?&])_=[^&]*/,Rn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Bn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Pn=/^(?:GET|HEAD)$/,In=/^\/\//,Wn=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,$n={},zn={},Xn="*/".concat("*");try{Fn=location.href}catch(Un){Fn=ht.createElement("a"),Fn.href="",Fn=Fn.href}qn=Wn.exec(Fn.toLowerCase())||[],it.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Fn,type:"GET",isLocal:Bn.test(qn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Xn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":it.parseJSON,"text xml":it.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?I(I(e,it.ajaxSettings),t):I(it.ajaxSettings,e)},ajaxPrefilter:B($n),ajaxTransport:B(zn),ajax:function(e,t){function n(e,t,n,r){var i,c,v,y,x,T=t;2!==b&&(b=2,s&&clearTimeout(s),u=void 0,a=r||"",w.readyState=e>0?4:0,i=e>=200&&300>e||304===e,n&&(y=W(d,w,n)),y=$(d,y,w,i),i?(d.ifModified&&(x=w.getResponseHeader("Last-Modified"),x&&(it.lastModified[o]=x),x=w.getResponseHeader("etag"),x&&(it.etag[o]=x)),204===e||"HEAD"===d.type?T="nocontent":304===e?T="notmodified":(T=y.state,c=y.data,v=y.error,i=!v)):(v=T,(e||!T)&&(T="error",0>e&&(e=0))),w.status=e,w.statusText=(t||T)+"",i?h.resolveWith(f,[c,T,w]):h.rejectWith(f,[w,T,v]),w.statusCode(g),g=void 0,l&&p.trigger(i?"ajaxSuccess":"ajaxError",[w,d,i?c:v]),m.fireWith(f,[w,T]),l&&(p.trigger("ajaxComplete",[w,d]),--it.active||it.event.trigger("ajaxStop")))}"object"==typeof e&&(t=e,e=void 0),t=t||{};var r,i,o,a,s,l,u,c,d=it.ajaxSetup({},t),f=d.context||d,p=d.context&&(f.nodeType||f.jquery)?it(f):it.event,h=it.Deferred(),m=it.Callbacks("once memory"),g=d.statusCode||{},v={},y={},b=0,x="canceled",w={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!c)for(c={};t=Rn.exec(a);)c[t[1].toLowerCase()]=t[2];t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=y[n]=y[n]||e,v[e]=t),this},overrideMimeType:function(e){return b||(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>b)for(t in e)g[t]=[g[t],e[t]];else w.always(e[w.status]);return this},abort:function(e){var t=e||x;return u&&u.abort(t),n(0,t),this}};if(h.promise(w).complete=m.add,w.success=w.done,w.error=w.fail,d.url=((e||d.url||Fn)+"").replace(Mn,"").replace(In,qn[1]+"//"),d.type=t.method||t.type||d.method||d.type,d.dataTypes=it.trim(d.dataType||"*").toLowerCase().match(bt)||[""],null==d.crossDomain&&(r=Wn.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]===qn[1]&&r[2]===qn[2]&&(r[3]||("http:"===r[1]?"80":"443"))===(qn[3]||("http:"===qn[1]?"80":"443")))),d.data&&d.processData&&"string"!=typeof d.data&&(d.data=it.param(d.data,d.traditional)),P($n,d,t,w),2===b)return w;l=d.global,l&&0===it.active++&&it.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Pn.test(d.type),o=d.url,d.hasContent||(d.data&&(o=d.url+=(Hn.test(o)?"&":"?")+d.data,delete d.data),d.cache===!1&&(d.url=On.test(o)?o.replace(On,"$1_="+Ln++):o+(Hn.test(o)?"&":"?")+"_="+Ln++)),d.ifModified&&(it.lastModified[o]&&w.setRequestHeader("If-Modified-Since",it.lastModified[o]),it.etag[o]&&w.setRequestHeader("If-None-Match",it.etag[o])),(d.data&&d.hasContent&&d.contentType!==!1||t.contentType)&&w.setRequestHeader("Content-Type",d.contentType),w.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Xn+"; q=0.01":""):d.accepts["*"]);for(i in d.headers)w.setRequestHeader(i,d.headers[i]);if(d.beforeSend&&(d.beforeSend.call(f,w,d)===!1||2===b))return w.abort();x="abort";for(i in{success:1,error:1,complete:1})w[i](d[i]);if(u=P(zn,d,t,w)){w.readyState=1,l&&p.trigger("ajaxSend",[w,d]),d.async&&d.timeout>0&&(s=setTimeout(function(){w.abort("timeout")},d.timeout));try{b=1,u.send(v,n)}catch(T){if(!(2>b))throw T;n(-1,T)}}else n(-1,"No Transport");return w},getJSON:function(e,t,n){return it.get(e,t,n,"json")},getScript:function(e,t){return it.get(e,void 0,t,"script")}}),it.each(["get","post"],function(e,t){it[t]=function(e,n,r,i){return it.isFunction(n)&&(i=i||r,r=n,n=void 0),it.ajax({url:e,type:t,dataType:i,data:n,success:r})}}),it.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){it.fn[t]=function(e){return this.on(t,e)}}),it._evalUrl=function(e){return it.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},it.fn.extend({wrapAll:function(e){if(it.isFunction(e))return this.each(function(t){it(this).wrapAll(e.call(this,t))});if(this[0]){var t=it(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return this.each(it.isFunction(e)?function(t){it(this).wrapInner(e.call(this,t))}:function(){var t=it(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=it.isFunction(e);return this.each(function(n){it(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){it.nodeName(this,"body")||it(this).replaceWith(this.childNodes)}).end()}}),it.expr.filters.hidden=function(e){return e.offsetWidth<=0&&e.offsetHeight<=0||!nt.reliableHiddenOffsets()&&"none"===(e.style&&e.style.display||it.css(e,"display"))},it.expr.filters.visible=function(e){return!it.expr.filters.hidden(e)};var Vn=/%20/g,Gn=/\[\]$/,Yn=/\r?\n/g,Jn=/^(?:submit|button|image|reset|file)$/i,Kn=/^(?:input|select|textarea|keygen)/i;it.param=function(e,t){var n,r=[],i=function(e,t){t=it.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=it.ajaxSettings&&it.ajaxSettings.traditional),it.isArray(e)||e.jquery&&!it.isPlainObject(e))it.each(e,function(){i(this.name,this.value)});else for(n in e)z(n,e[n],t,i);return r.join("&").replace(Vn,"+")},it.fn.extend({serialize:function(){return it.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=it.prop(this,"elements");return e?it.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!it(this).is(":disabled")&&Kn.test(this.nodeName)&&!Jn.test(e)&&(this.checked||!Dt.test(e))}).map(function(e,t){var n=it(this).val();return null==n?null:it.isArray(n)?it.map(n,function(e){return{name:t.name,value:e.replace(Yn,"\r\n")}}):{name:t.name,value:n.replace(Yn,"\r\n")}}).get()}}),it.ajaxSettings.xhr=void 0!==e.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&X()||U()}:X;var Qn=0,Zn={},er=it.ajaxSettings.xhr();e.ActiveXObject&&it(e).on("unload",function(){for(var e in Zn)Zn[e](void 0,!0)}),nt.cors=!!er&&"withCredentials"in er,er=nt.ajax=!!er,er&&it.ajaxTransport(function(e){if(!e.crossDomain||nt.cors){var t;return{send:function(n,r){var i,o=e.xhr(),a=++Qn;if(o.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(i in e.xhrFields)o[i]=e.xhrFields[i];e.mimeType&&o.overrideMimeType&&o.overrideMimeType(e.mimeType),e.crossDomain||n["X-Requested-With"]||(n["X-Requested-With"]="XMLHttpRequest");for(i in n)void 0!==n[i]&&o.setRequestHeader(i,n[i]+"");o.send(e.hasContent&&e.data||null),t=function(n,i){var s,l,u;if(t&&(i||4===o.readyState))if(delete Zn[a],t=void 0,o.onreadystatechange=it.noop,i)4!==o.readyState&&o.abort();else{u={},s=o.status,"string"==typeof o.responseText&&(u.text=o.responseText);try{l=o.statusText}catch(c){l=""}s||!e.isLocal||e.crossDomain?1223===s&&(s=204):s=u.text?200:404}u&&r(s,l,u,o.getAllResponseHeaders())},e.async?4===o.readyState?setTimeout(t):o.onreadystatechange=Zn[a]=t:t()},abort:function(){t&&t(void 0,!0)}}}}),it.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return it.globalEval(e),e}}}),it.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),it.ajaxTransport("script",function(e){if(e.crossDomain){var t,n=ht.head||it("head")[0]||ht.documentElement;return{send:function(r,i){t=ht.createElement("script"),t.async=!0,e.scriptCharset&&(t.charset=e.scriptCharset),t.src=e.url,t.onload=t.onreadystatechange=function(e,n){(n||!t.readyState||/loaded|complete/.test(t.readyState))&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),t=null,n||i(200,"success"))},n.insertBefore(t,n.firstChild)},abort:function(){t&&t.onload(void 0,!0)}}}});var tr=[],nr=/(=)\?(?=&|$)|\?\?/;it.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=tr.pop()||it.expando+"_"+Ln++;return this[e]=!0,e}}),it.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=t.jsonp!==!1&&(nr.test(t.url)?"url":"string"==typeof t.data&&!(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&nr.test(t.data)&&"data");return s||"jsonp"===t.dataTypes[0]?(i=t.jsonpCallback=it.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(nr,"$1"+i):t.jsonp!==!1&&(t.url+=(Hn.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||it.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,tr.push(i)),a&&it.isFunction(o)&&o(a[0]),a=o=void 0}),"script"):void 0}),it.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||ht;var r=dt.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=it.buildFragment([e],t,i),i&&i.length&&it(i).remove(),it.merge([],r.childNodes))};var rr=it.fn.load;it.fn.load=function(e,t,n){if("string"!=typeof e&&rr)return rr.apply(this,arguments);var r,i,o,a=this,s=e.indexOf(" ");return s>=0&&(r=it.trim(e.slice(s,e.length)),e=e.slice(0,s)),it.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(o="POST"),a.length>0&&it.ajax({url:e,type:o,dataType:"html",data:t}).done(function(e){i=arguments,a.html(r?it("<div>").append(it.parseHTML(e)).find(r):e)}).complete(n&&function(e,t){a.each(n,i||[e.responseText,t,e])}),this},it.expr.filters.animated=function(e){return it.grep(it.timers,function(t){return e===t.elem}).length};var ir=e.document.documentElement;it.offset={setOffset:function(e,t,n){var r,i,o,a,s,l,u,c=it.css(e,"position"),d=it(e),f={};"static"===c&&(e.style.position="relative"),s=d.offset(),o=it.css(e,"top"),l=it.css(e,"left"),u=("absolute"===c||"fixed"===c)&&it.inArray("auto",[o,l])>-1,u?(r=d.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(l)||0),it.isFunction(t)&&(t=t.call(e,n,s)),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):d.css(f)}},it.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){it.offset.setOffset(this,e,t)});var t,n,r={top:0,left:0},i=this[0],o=i&&i.ownerDocument;if(o)return t=o.documentElement,it.contains(t,i)?(typeof i.getBoundingClientRect!==Et&&(r=i.getBoundingClientRect()),n=V(o),{top:r.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:r.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):r},position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===it.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),it.nodeName(e[0],"html")||(n=e.offset()),n.top+=it.css(e[0],"borderTopWidth",!0),n.left+=it.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-it.css(r,"marginTop",!0),left:t.left-n.left-it.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent||ir;e&&!it.nodeName(e,"html")&&"static"===it.css(e,"position");)e=e.offsetParent;return e||ir})}}),it.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);it.fn[e]=function(r){return At(this,function(e,r,i){var o=V(e);return void 0===i?o?t in o?o[t]:o.document.documentElement[r]:e[r]:void(o?o.scrollTo(n?it(o).scrollLeft():i,n?i:it(o).scrollTop()):e[r]=i)},e,r,arguments.length,null)}}),it.each(["top","left"],function(e,t){it.cssHooks[t]=N(nt.pixelPosition,function(e,n){return n?(n=tn(e,t),rn.test(n)?it(e).position()[t]+"px":n):void 0})}),it.each({Height:"height",Width:"width"},function(e,t){it.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){it.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return At(this,function(t,n,r){var i;return it.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?it.css(t,n,a):it.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),it.fn.size=function(){return this.length},it.fn.andSelf=it.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return it});var or=e.jQuery,ar=e.$;return it.noConflict=function(t){return e.$===it&&(e.$=ar),t&&e.jQuery===it&&(e.jQuery=or),it},typeof t===Et&&(e.jQuery=e.$=it),it}),function(e,t){e.rails!==t&&e.error("jquery-ujs has already been loaded!");var n,r=e(document);e.rails=n={linkClickSelector:"a[data-confirm], a[data-method], a[data-remote], a[data-disable-with], a[data-disable]",buttonClickSelector:"button[data-remote], button[data-confirm]",inputChangeSelector:"select[data-remote], input[data-remote], textarea[data-remote]",formSubmitSelector:"form",formInputClickSelector:"form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])",disableSelector:"input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled",enableSelector:"input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled",requiredInputSelector:"input[name][required]:not([disabled]),textarea[name][required]:not([disabled])",fileInputSelector:"input[type=file]",linkDisableSelector:"a[data-disable-with], a[data-disable]",buttonDisableSelector:"button[data-remote][data-disable-with], button[data-remote][data-disable]",CSRFProtection:function(t){var n=e('meta[name="csrf-token"]').attr("content");n&&t.setRequestHeader("X-CSRF-Token",n)},refreshCSRFTokens:function(){var t=e("meta[name=csrf-token]").attr("content"),n=e("meta[name=csrf-param]").attr("content");e('form input[name="'+n+'"]').val(t)},fire:function(t,n,r){var i=e.Event(n);return t.trigger(i,r),i.result!==!1},confirm:function(e){return confirm(e)},ajax:function(t){return e.ajax(t)},href:function(e){return e.attr("href")},handleRemote:function(r){var i,o,a,s,l,u,c,d;if(n.fire(r,"ajax:before")){if(s=r.data("cross-domain"),l=s===t?null:s,u=r.data("with-credentials")||null,c=r.data("type")||e.ajaxSettings&&e.ajaxSettings.dataType,r.is("form")){i=r.attr("method"),o=r.attr("action"),a=r.serializeArray();var f=r.data("ujs:submit-button");f&&(a.push(f),r.data("ujs:submit-button",null))}else r.is(n.inputChangeSelector)?(i=r.data("method"),o=r.data("url"),a=r.serialize(),r.data("params")&&(a=a+"&"+r.data("params"))):r.is(n.buttonClickSelector)?(i=r.data("method")||"get",o=r.data("url"),a=r.serialize(),r.data("params")&&(a=a+"&"+r.data("params"))):(i=r.data("method"),o=n.href(r),a=r.data("params")||null);return d={type:i||"GET",data:a,dataType:c,beforeSend:function(e,i){return i.dataType===t&&e.setRequestHeader("accept","*/*;q=0.5, "+i.accepts.script),n.fire(r,"ajax:beforeSend",[e,i])?void r.trigger("ajax:send",e):!1 +},success:function(e,t,n){r.trigger("ajax:success",[e,t,n])},complete:function(e,t){r.trigger("ajax:complete",[e,t])},error:function(e,t,n){r.trigger("ajax:error",[e,t,n])},crossDomain:l},u&&(d.xhrFields={withCredentials:u}),o&&(d.url=o),n.ajax(d)}return!1},handleMethod:function(r){var i=n.href(r),o=r.data("method"),a=r.attr("target"),s=e("meta[name=csrf-token]").attr("content"),l=e("meta[name=csrf-param]").attr("content"),u=e('<form method="post" action="'+i+'"></form>'),c='<input name="_method" value="'+o+'" type="hidden" />';l!==t&&s!==t&&(c+='<input name="'+l+'" value="'+s+'" type="hidden" />'),a&&u.attr("target",a),u.hide().append(c).appendTo("body"),u.submit()},formElements:function(t,n){return t.is("form")?e(t[0].elements).filter(n):t.find(n)},disableFormElements:function(t){n.formElements(t,n.disableSelector).each(function(){n.disableFormElement(e(this))})},disableFormElement:function(e){var n,r;n=e.is("button")?"html":"val",r=e.data("disable-with"),e.data("ujs:enable-with",e[n]()),r!==t&&e[n](r),e.prop("disabled",!0)},enableFormElements:function(t){n.formElements(t,n.enableSelector).each(function(){n.enableFormElement(e(this))})},enableFormElement:function(e){var t=e.is("button")?"html":"val";e.data("ujs:enable-with")&&e[t](e.data("ujs:enable-with")),e.prop("disabled",!1)},allowAction:function(e){var t,r=e.data("confirm"),i=!1;return r?(n.fire(e,"confirm")&&(i=n.confirm(r),t=n.fire(e,"confirm:complete",[i])),i&&t):!0},blankInputs:function(t,n,r){var i,o,a=e(),s=n||"input,textarea",l=t.find(s);return l.each(function(){if(i=e(this),o=i.is("input[type=checkbox],input[type=radio]")?i.is(":checked"):i.val(),!o==!r){if(i.is("input[type=radio]")&&l.filter('input[type=radio]:checked[name="'+i.attr("name")+'"]').length)return!0;a=a.add(i)}}),a.length?a:!1},nonBlankInputs:function(e,t){return n.blankInputs(e,t,!0)},stopEverything:function(t){return e(t.target).trigger("ujs:everythingStopped"),t.stopImmediatePropagation(),!1},disableElement:function(e){var r=e.data("disable-with");e.data("ujs:enable-with",e.html()),r!==t&&e.html(r),e.bind("click.railsDisable",function(e){return n.stopEverything(e)})},enableElement:function(e){e.data("ujs:enable-with")!==t&&(e.html(e.data("ujs:enable-with")),e.removeData("ujs:enable-with")),e.unbind("click.railsDisable")}},n.fire(r,"rails:attachBindings")&&(e.ajaxPrefilter(function(e,t,r){e.crossDomain||n.CSRFProtection(r)}),r.delegate(n.linkDisableSelector,"ajax:complete",function(){n.enableElement(e(this))}),r.delegate(n.buttonDisableSelector,"ajax:complete",function(){n.enableFormElement(e(this))}),r.delegate(n.linkClickSelector,"click.rails",function(r){var i=e(this),o=i.data("method"),a=i.data("params"),s=r.metaKey||r.ctrlKey;if(!n.allowAction(i))return n.stopEverything(r);if(!s&&i.is(n.linkDisableSelector)&&n.disableElement(i),i.data("remote")!==t){if(s&&(!o||"GET"===o)&&!a)return!0;var l=n.handleRemote(i);return l===!1?n.enableElement(i):l.error(function(){n.enableElement(i)}),!1}return i.data("method")?(n.handleMethod(i),!1):void 0}),r.delegate(n.buttonClickSelector,"click.rails",function(t){var r=e(this);if(!n.allowAction(r))return n.stopEverything(t);r.is(n.buttonDisableSelector)&&n.disableFormElement(r);var i=n.handleRemote(r);return i===!1?n.enableFormElement(r):i.error(function(){n.enableFormElement(r)}),!1}),r.delegate(n.inputChangeSelector,"change.rails",function(t){var r=e(this);return n.allowAction(r)?(n.handleRemote(r),!1):n.stopEverything(t)}),r.delegate(n.formSubmitSelector,"submit.rails",function(r){var i,o,a=e(this),s=a.data("remote")!==t;if(!n.allowAction(a))return n.stopEverything(r);if(a.attr("novalidate")==t&&(i=n.blankInputs(a,n.requiredInputSelector),i&&n.fire(a,"ajax:aborted:required",[i])))return n.stopEverything(r);if(s){if(o=n.nonBlankInputs(a,n.fileInputSelector)){setTimeout(function(){n.disableFormElements(a)},13);var l=n.fire(a,"ajax:aborted:file",[o]);return l||setTimeout(function(){n.enableFormElements(a)},13),l}return n.handleRemote(a),!1}setTimeout(function(){n.disableFormElements(a)},13)}),r.delegate(n.formInputClickSelector,"click.rails",function(t){var r=e(this);if(!n.allowAction(r))return n.stopEverything(t);var i=r.attr("name"),o=i?{name:i,value:r.val()}:null;r.closest("form").data("ujs:submit-button",o)}),r.delegate(n.formSubmitSelector,"ajax:send.rails",function(t){this==t.target&&n.disableFormElements(e(this))}),r.delegate(n.formSubmitSelector,"ajax:complete.rails",function(t){this==t.target&&n.enableFormElements(e(this))}),e(function(){n.refreshCSRFTokens()}))}(jQuery),function(){var e,t,n,r,i,o,a,s,l,u,c,d,f,p,h,m,g,v,y,b,x,w,T,E,k,C,N,S,j,A,D,L,H,_,q,F,M,O,R,B,P,I,W,$,z,X,U,V,G,Y=[].indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(t in this&&this[t]===e)return t;return-1},J={}.hasOwnProperty,K=function(e,t){function n(){this.constructor=e}for(var r in t)J.call(t,r)&&(e[r]=t[r]);return n.prototype=t.prototype,e.prototype=new n,e.__super__=t.prototype,e},Q=[].slice;j={},d=10,$=!1,m=null,S=null,q=null,h=null,V=null,b=function(e){var t;return e=new n(e),B(),c(),F(e),$&&(t=z(e.absolute))?(x(t),w(e)):w(e,W)},z=function(e){var t;return t=j[e],t&&!t.transitionCacheDisabled?t:void 0},g=function(e){return null==e&&(e=!0),$=e},w=function(e,t){return null==t&&(t=function(){return function(){}}(this)),X("page:fetch",{url:e.absolute}),null!=V&&V.abort(),V=new XMLHttpRequest,V.open("GET",e.withoutHashForIE10compatibility(),!0),V.setRequestHeader("Accept","text/html, application/xhtml+xml, application/xml"),V.setRequestHeader("X-XHR-Referer",q),V.onload=function(){var n;return X("page:receive"),(n=H())?(f.apply(null,y(n)),M(),t(),X("page:load")):document.location.href=e.absolute},V.onloadend=function(){return V=null},V.onerror=function(){return document.location.href=e.absolute},V.send()},x=function(e){return null!=V&&V.abort(),f(e.title,e.body),_(e),X("page:restore")},c=function(){var e;return e=new n(m.url),j[e.absolute]={url:e.relative,body:document.body,title:document.title,positionY:window.pageYOffset,positionX:window.pageXOffset,cachedAt:(new Date).getTime(),transitionCacheDisabled:null!=document.querySelector("[data-no-transition-cache]")},p(d)},D=function(e){return null==e&&(e=d),/^[\d]+$/.test(e)?d=parseInt(e):void 0},p=function(e){var t,n,r,i,o,a;for(r=Object.keys(j),t=r.map(function(e){return j[e].cachedAt}).sort(function(e,t){return t-e}),a=[],i=0,o=r.length;o>i;i++)n=r[i],j[n].cachedAt<=t[e]&&(X("page:expire",j[n]),a.push(delete j[n]));return a},f=function(t,n,r,i){return document.title=t,document.documentElement.replaceChild(n,document.body),null!=r&&e.update(r),i&&v(),m=window.history.state,X("page:change"),X("page:update")},v=function(){var e,t,n,r,i,o,a,s,l,u,c,d;for(o=Array.prototype.slice.call(document.body.querySelectorAll('script:not([data-turbolinks-eval="false"])')),a=0,l=o.length;l>a;a++)if(i=o[a],""===(c=i.type)||"text/javascript"===c){for(t=document.createElement("script"),d=i.attributes,s=0,u=d.length;u>s;s++)e=d[s],t.setAttribute(e.name,e.value);t.appendChild(document.createTextNode(i.innerHTML)),r=i.parentNode,n=i.nextSibling,r.removeChild(i),r.insertBefore(t,n)}},P=function(e){return e.innerHTML=e.innerHTML.replace(/<noscript[\S\s]*?<\/noscript>/gi,""),e},F=function(e){return(e=new n(e)).absolute!==q?window.history.pushState({turbolinks:!0,url:e.absolute},"",e.absolute):void 0},M=function(){var e,t;return(e=V.getResponseHeader("X-XHR-Redirected-To"))?(e=new n(e),t=e.hasNoHash()?document.location.hash:"",window.history.replaceState(m,"",e.href+t)):void 0},B=function(){return q=document.location.href},R=function(){return window.history.replaceState({turbolinks:!0,url:document.location.href},"",document.location.href)},O=function(){return m=window.history.state},_=function(e){return window.scrollTo(e.positionX,e.positionY)},W=function(){return document.location.hash?document.location.href=document.location.href:window.scrollTo(0,0)},L=function(e){var t,n;return t=(null!=(n=document.cookie.match(new RegExp(e+"=(\\w+)")))?n[1].toUpperCase():void 0)||"",document.cookie=e+"=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/",t},X=function(e,t){var n;return n=document.createEvent("Events"),t&&(n.data=t),n.initEvent(e,!0,!0),document.dispatchEvent(n)},A=function(){return!X("page:before-change")},H=function(){var e,t,n,r,i,o;return t=function(){var e;return 400<=(e=V.status)&&600>e},o=function(){return V.getResponseHeader("Content-Type").match(/^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/)},r=function(e){var t,n,r,i,o;for(i=e.head.childNodes,o=[],n=0,r=i.length;r>n;n++)t=i[n],null!=("function"==typeof t.getAttribute?t.getAttribute("data-turbolinks-track"):void 0)&&o.push(t.getAttribute("src")||t.getAttribute("href"));return o},e=function(e){var t;return S||(S=r(document)),t=r(e),t.length!==S.length||i(t,S).length!==S.length},i=function(e,t){var n,r,i,o,a;for(e.length>t.length&&(o=[t,e],e=o[0],t=o[1]),a=[],r=0,i=e.length;i>r;r++)n=e[r],Y.call(t,n)>=0&&a.push(n);return a},!t()&&o()&&(n=h(V.responseText),n&&!e(n))?n:void 0},y=function(t){var n;return n=t.querySelector("title"),[null!=n?n.textContent:void 0,P(t.body),e.get(t).token,"runScripts"]},e={get:function(e){var t;return null==e&&(e=document),{node:t=e.querySelector('meta[name="csrf-token"]'),token:null!=t&&"function"==typeof t.getAttribute?t.getAttribute("content"):void 0}},update:function(e){var t;return t=this.get(),null!=t.token&&null!=e&&t.token!==e?t.node.setAttribute("content",e):void 0}},i=function(){var e,t,n,r,i,o;t=function(e){return(new DOMParser).parseFromString(e,"text/html")},e=function(e){var t;return t=document.implementation.createHTMLDocument(""),t.documentElement.innerHTML=e,t},n=function(e){var t;return t=document.implementation.createHTMLDocument(""),t.open("replace"),t.write(e),t.close(),t};try{if(window.DOMParser)return i=t("<html><body><p>test"),t}catch(a){return r=a,i=e("<html><body><p>test"),e}finally{if(1!==(null!=i&&null!=(o=i.body)?o.childNodes.length:void 0))return n}},n=function(){function e(t){return this.original=null!=t?t:document.location.href,this.original.constructor===e?this.original:void this._parse()}return e.prototype.withoutHash=function(){return this.href.replace(this.hash,"")},e.prototype.withoutHashForIE10compatibility=function(){return this.withoutHash()},e.prototype.hasNoHash=function(){return 0===this.hash.length},e.prototype._parse=function(){var e;return(null!=this.link?this.link:this.link=document.createElement("a")).href=this.original,e=this.link,this.href=e.href,this.protocol=e.protocol,this.host=e.host,this.hostname=e.hostname,this.port=e.port,this.pathname=e.pathname,this.search=e.search,this.hash=e.hash,this.origin=[this.protocol,"//",this.hostname].join(""),0!==this.port.length&&(this.origin+=":"+this.port),this.relative=[this.pathname,this.search,this.hash].join(""),this.absolute=this.href},e}(),r=function(e){function t(e){return this.link=e,this.link.constructor===t?this.link:(this.original=this.link.href,void t.__super__.constructor.apply(this,arguments))}return K(t,e),t.HTML_EXTENSIONS=["html"],t.allowExtensions=function(){var e,n,r,i;for(n=1<=arguments.length?Q.call(arguments,0):[],r=0,i=n.length;i>r;r++)e=n[r],t.HTML_EXTENSIONS.push(e);return t.HTML_EXTENSIONS},t.prototype.shouldIgnore=function(){return this._crossOrigin()||this._anchored()||this._nonHtml()||this._optOut()||this._target()},t.prototype._crossOrigin=function(){return this.origin!==(new n).origin},t.prototype._anchored=function(){var e;return(this.hash&&this.withoutHash())===(e=new n).withoutHash()||this.href===e.href+"#"},t.prototype._nonHtml=function(){return this.pathname.match(/\.[a-z]+$/g)&&!this.pathname.match(new RegExp("\\.(?:"+t.HTML_EXTENSIONS.join("|")+")?$","g"))},t.prototype._optOut=function(){var e,t;for(t=this.link;!e&&t!==document;)e=null!=t.getAttribute("data-no-turbolink"),t=t.parentNode;return e},t.prototype._target=function(){return 0!==this.link.target.length},t}(n),t=function(){function e(e){this.event=e,this.event.defaultPrevented||(this._extractLink(),this._validForTurbolinks()&&(A()||U(this.link.href),this.event.preventDefault()))}return e.installHandlerLast=function(t){return t.defaultPrevented?void 0:(document.removeEventListener("click",e.handle,!1),document.addEventListener("click",e.handle,!1))},e.handle=function(t){return new e(t)},e.prototype._extractLink=function(){var e;for(e=this.event.target;e.parentNode&&"A"!==e.nodeName;)e=e.parentNode;return"A"===e.nodeName&&0!==e.href.length?this.link=new r(e):void 0},e.prototype._validForTurbolinks=function(){return null!=this.link&&!(this.link.shouldIgnore()||this._nonStandardClick())},e.prototype._nonStandardClick=function(){return this.event.which>1||this.event.metaKey||this.event.ctrlKey||this.event.shiftKey||this.event.altKey},e}(),u=function(e){return setTimeout(e,500)},k=function(){return document.addEventListener("DOMContentLoaded",function(){return X("page:change"),X("page:update")},!0)},N=function(){return"undefined"!=typeof jQuery?jQuery(document).on("ajaxSuccess",function(e,t){return jQuery.trim(t.responseText)?X("page:update"):void 0}):void 0},C=function(e){var t,r;return(null!=(r=e.state)?r.turbolinks:void 0)?(t=j[new n(e.state.url).absolute])?(c(),x(t)):U(e.target.location.href):void 0},E=function(){return R(),O(),h=i(),document.addEventListener("click",t.installHandlerLast,!0),u(function(){return window.addEventListener("popstate",C,!1)})},T=void 0!==window.history.state||navigator.userAgent.match(/Firefox\/2[6|7]/),s=window.history&&window.history.pushState&&window.history.replaceState&&T,o=!navigator.userAgent.match(/CriOS\//),I="GET"===(G=L("request_method"))||""===G,l=s&&o&&I,a=document.addEventListener&&document.createEvent,a&&(k(),N()),l?(U=b,E()):U=function(e){return document.location.href=e},this.Turbolinks={visit:U,pagesCached:D,enableTransitionCache:g,allowLinkExtensions:r.allowExtensions,supported:l}}.call(this),function(){}.call(this);
\ No newline at end of file diff --git a/actionpack/test/fixtures/public/gzip/foo.zoo.gz b/actionpack/test/fixtures/public/gzip/foo.zoo.gz Binary files differnew file mode 100644 index 0000000000..f62c656dc8 --- /dev/null +++ b/actionpack/test/fixtures/public/gzip/foo.zoo.gz diff --git a/actionpack/test/fixtures/respond_to/variant_any_implicit_render.html+phablet.erb b/actionpack/test/fixtures/respond_to/variant_any_implicit_render.html+phablet.erb new file mode 100644 index 0000000000..e905d051bf --- /dev/null +++ b/actionpack/test/fixtures/respond_to/variant_any_implicit_render.html+phablet.erb @@ -0,0 +1 @@ +phablet
\ No newline at end of file diff --git a/actionpack/test/fixtures/respond_to/variant_any_implicit_render.html+tablet.erb b/actionpack/test/fixtures/respond_to/variant_any_implicit_render.html+tablet.erb new file mode 100644 index 0000000000..65526af8cf --- /dev/null +++ b/actionpack/test/fixtures/respond_to/variant_any_implicit_render.html+tablet.erb @@ -0,0 +1 @@ +tablet
\ No newline at end of file diff --git a/actionpack/test/fixtures/respond_to/variant_inline_syntax_without_block.html+phone.erb b/actionpack/test/fixtures/respond_to/variant_inline_syntax_without_block.html+phone.erb new file mode 100644 index 0000000000..cd222a4a49 --- /dev/null +++ b/actionpack/test/fixtures/respond_to/variant_inline_syntax_without_block.html+phone.erb @@ -0,0 +1 @@ +phone
\ No newline at end of file diff --git a/actionpack/test/fixtures/respond_to/variant_plus_none_for_format.html.erb b/actionpack/test/fixtures/respond_to/variant_plus_none_for_format.html.erb new file mode 100644 index 0000000000..c86c3f3551 --- /dev/null +++ b/actionpack/test/fixtures/respond_to/variant_plus_none_for_format.html.erb @@ -0,0 +1 @@ +none
\ No newline at end of file diff --git a/actionpack/test/fixtures/respond_with/edit.html.erb b/actionpack/test/fixtures/respond_with/edit.html.erb deleted file mode 100644 index ae82dfa4fc..0000000000 --- a/actionpack/test/fixtures/respond_with/edit.html.erb +++ /dev/null @@ -1 +0,0 @@ -Edit world! diff --git a/actionpack/test/fixtures/respond_with/new.html.erb b/actionpack/test/fixtures/respond_with/new.html.erb deleted file mode 100644 index 96c8f1b88b..0000000000 --- a/actionpack/test/fixtures/respond_with/new.html.erb +++ /dev/null @@ -1 +0,0 @@ -New world! diff --git a/actionpack/test/fixtures/respond_with/respond_with_additional_params.html.erb b/actionpack/test/fixtures/respond_with/respond_with_additional_params.html.erb deleted file mode 100644 index e69de29bb2..0000000000 --- a/actionpack/test/fixtures/respond_with/respond_with_additional_params.html.erb +++ /dev/null diff --git a/actionpack/test/fixtures/respond_with/using_invalid_resource_with_template.xml.erb b/actionpack/test/fixtures/respond_with/using_invalid_resource_with_template.xml.erb deleted file mode 100644 index bf5869ed22..0000000000 --- a/actionpack/test/fixtures/respond_with/using_invalid_resource_with_template.xml.erb +++ /dev/null @@ -1 +0,0 @@ -<content>I should not be displayed</content>
\ No newline at end of file diff --git a/actionpack/test/fixtures/respond_with/using_options_with_template.xml.erb b/actionpack/test/fixtures/respond_with/using_options_with_template.xml.erb deleted file mode 100644 index b313017913..0000000000 --- a/actionpack/test/fixtures/respond_with/using_options_with_template.xml.erb +++ /dev/null @@ -1 +0,0 @@ -<customer-name><%= @customer.name %></customer-name>
\ No newline at end of file diff --git a/actionpack/test/fixtures/respond_with/using_resource.js.erb b/actionpack/test/fixtures/respond_with/using_resource.js.erb deleted file mode 100644 index 4417680bce..0000000000 --- a/actionpack/test/fixtures/respond_with/using_resource.js.erb +++ /dev/null @@ -1 +0,0 @@ -alert("Hi");
\ No newline at end of file diff --git a/actionpack/test/fixtures/respond_with/using_resource_with_block.html.erb b/actionpack/test/fixtures/respond_with/using_resource_with_block.html.erb deleted file mode 100644 index 6769dd60bd..0000000000 --- a/actionpack/test/fixtures/respond_with/using_resource_with_block.html.erb +++ /dev/null @@ -1 +0,0 @@ -Hello world!
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_changing_priority.html.erb b/actionpack/test/fixtures/test/_changing_priority.html.erb deleted file mode 100644 index 3225efc49a..0000000000 --- a/actionpack/test/fixtures/test/_changing_priority.html.erb +++ /dev/null @@ -1 +0,0 @@ -HTML
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_changing_priority.json.erb b/actionpack/test/fixtures/test/_changing_priority.json.erb deleted file mode 100644 index 7fa41dce66..0000000000 --- a/actionpack/test/fixtures/test/_changing_priority.json.erb +++ /dev/null @@ -1 +0,0 @@ -JSON
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_counter.html.erb b/actionpack/test/fixtures/test/_counter.html.erb deleted file mode 100644 index fd245bfc70..0000000000 --- a/actionpack/test/fixtures/test/_counter.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= counter_counter %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_customer.erb b/actionpack/test/fixtures/test/_customer.erb deleted file mode 100644 index d8220afeda..0000000000 --- a/actionpack/test/fixtures/test/_customer.erb +++ /dev/null @@ -1 +0,0 @@ -Hello: <%= customer.name rescue "Anonymous" %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_customer_counter.erb b/actionpack/test/fixtures/test/_customer_counter.erb deleted file mode 100644 index 3435979dba..0000000000 --- a/actionpack/test/fixtures/test/_customer_counter.erb +++ /dev/null @@ -1 +0,0 @@ -<%= customer_counter.name %><%= customer_counter_counter %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_customer_counter_with_as.erb b/actionpack/test/fixtures/test/_customer_counter_with_as.erb deleted file mode 100644 index 1241eb604d..0000000000 --- a/actionpack/test/fixtures/test/_customer_counter_with_as.erb +++ /dev/null @@ -1 +0,0 @@ -<%= client.name %><%= client_counter %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_customer_greeting.erb b/actionpack/test/fixtures/test/_customer_greeting.erb deleted file mode 100644 index 6acbcb20c4..0000000000 --- a/actionpack/test/fixtures/test/_customer_greeting.erb +++ /dev/null @@ -1 +0,0 @@ -<%= greeting %>: <%= customer_greeting.name %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_customer_with_var.erb b/actionpack/test/fixtures/test/_customer_with_var.erb deleted file mode 100644 index 00047dd20e..0000000000 --- a/actionpack/test/fixtures/test/_customer_with_var.erb +++ /dev/null @@ -1 +0,0 @@ -<%= customer.name %> <%= customer.name %> <%= customer.name %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_directory/_partial_with_locales.html.erb b/actionpack/test/fixtures/test/_directory/_partial_with_locales.html.erb deleted file mode 100644 index 1cc8d41475..0000000000 --- a/actionpack/test/fixtures/test/_directory/_partial_with_locales.html.erb +++ /dev/null @@ -1 +0,0 @@ -Hello <%= name %> diff --git a/actionpack/test/fixtures/test/_first_json_partial.json.erb b/actionpack/test/fixtures/test/_first_json_partial.json.erb deleted file mode 100644 index 790ee896db..0000000000 --- a/actionpack/test/fixtures/test/_first_json_partial.json.erb +++ /dev/null @@ -1 +0,0 @@ -<%= render :partial => "test/second_json_partial" %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_form.erb b/actionpack/test/fixtures/test/_form.erb deleted file mode 100644 index 01107f1cb2..0000000000 --- a/actionpack/test/fixtures/test/_form.erb +++ /dev/null @@ -1 +0,0 @@ -<%= form.label :title %> diff --git a/actionpack/test/fixtures/test/_hash_greeting.erb b/actionpack/test/fixtures/test/_hash_greeting.erb deleted file mode 100644 index fc54a36f2a..0000000000 --- a/actionpack/test/fixtures/test/_hash_greeting.erb +++ /dev/null @@ -1 +0,0 @@ -<%= greeting %>: <%= hash_greeting[:first_name] %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_hash_object.erb b/actionpack/test/fixtures/test/_hash_object.erb deleted file mode 100644 index 34a92c6a56..0000000000 --- a/actionpack/test/fixtures/test/_hash_object.erb +++ /dev/null @@ -1,2 +0,0 @@ -<%= hash_object[:first_name] %> -<%= hash_object[:first_name].reverse %> diff --git a/actionpack/test/fixtures/test/_hello.builder b/actionpack/test/fixtures/test/_hello.builder deleted file mode 100644 index ef52f632d1..0000000000 --- a/actionpack/test/fixtures/test/_hello.builder +++ /dev/null @@ -1 +0,0 @@ -xm.hello
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_json_change_priority.json.erb b/actionpack/test/fixtures/test/_json_change_priority.json.erb deleted file mode 100644 index e69de29bb2..0000000000 --- a/actionpack/test/fixtures/test/_json_change_priority.json.erb +++ /dev/null diff --git a/actionpack/test/fixtures/test/_labelling_form.erb b/actionpack/test/fixtures/test/_labelling_form.erb deleted file mode 100644 index 1b95763165..0000000000 --- a/actionpack/test/fixtures/test/_labelling_form.erb +++ /dev/null @@ -1 +0,0 @@ -<%= labelling_form.label :title %> diff --git a/actionpack/test/fixtures/test/_layout_for_partial.html.erb b/actionpack/test/fixtures/test/_layout_for_partial.html.erb deleted file mode 100644 index 666efadbb6..0000000000 --- a/actionpack/test/fixtures/test/_layout_for_partial.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -Before (<%= name %>) -<%= yield %> -After
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_partial_for_use_in_layout.html.erb b/actionpack/test/fixtures/test/_partial_for_use_in_layout.html.erb deleted file mode 100644 index 3a03a64e31..0000000000 --- a/actionpack/test/fixtures/test/_partial_for_use_in_layout.html.erb +++ /dev/null @@ -1 +0,0 @@ -Inside from partial (<%= name %>)
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_partial_html_erb.html.erb b/actionpack/test/fixtures/test/_partial_html_erb.html.erb deleted file mode 100644 index 4b54875782..0000000000 --- a/actionpack/test/fixtures/test/_partial_html_erb.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= "partial.html.erb" %> diff --git a/actionpack/test/fixtures/test/_partial_name_local_variable.erb b/actionpack/test/fixtures/test/_partial_name_local_variable.erb deleted file mode 100644 index cc3a91c89f..0000000000 --- a/actionpack/test/fixtures/test/_partial_name_local_variable.erb +++ /dev/null @@ -1 +0,0 @@ -<%= partial_name_local_variable %> diff --git a/actionpack/test/fixtures/test/_partial_only.erb b/actionpack/test/fixtures/test/_partial_only.erb deleted file mode 100644 index a44b3eed40..0000000000 --- a/actionpack/test/fixtures/test/_partial_only.erb +++ /dev/null @@ -1 +0,0 @@ -only partial
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_partial_only_html.html b/actionpack/test/fixtures/test/_partial_only_html.html deleted file mode 100644 index d2d630bd40..0000000000 --- a/actionpack/test/fixtures/test/_partial_only_html.html +++ /dev/null @@ -1 +0,0 @@ -only html partial
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_partial_with_partial.erb b/actionpack/test/fixtures/test/_partial_with_partial.erb deleted file mode 100644 index ee0d5037b6..0000000000 --- a/actionpack/test/fixtures/test/_partial_with_partial.erb +++ /dev/null @@ -1,2 +0,0 @@ -<%= render 'test/partial' %> -partial with partial diff --git a/actionpack/test/fixtures/test/_person.erb b/actionpack/test/fixtures/test/_person.erb deleted file mode 100644 index b2e5688956..0000000000 --- a/actionpack/test/fixtures/test/_person.erb +++ /dev/null @@ -1,2 +0,0 @@ -Second: <%= name %> -Third: <%= @name %> diff --git a/actionpack/test/fixtures/test/_raise_indentation.html.erb b/actionpack/test/fixtures/test/_raise_indentation.html.erb deleted file mode 100644 index f9a93728fe..0000000000 --- a/actionpack/test/fixtures/test/_raise_indentation.html.erb +++ /dev/null @@ -1,13 +0,0 @@ -<p>First paragraph</p> -<p>Second paragraph</p> -<p>Third paragraph</p> -<p>Fourth paragraph</p> -<p>Fifth paragraph</p> -<p>Sixth paragraph</p> -<p>Seventh paragraph</p> -<p>Eight paragraph</p> -<p>Ninth paragraph</p> -<p>Tenth paragraph</p> -<%= raise "error here!" %> -<p>Eleventh paragraph</p> -<p>Twelfth paragraph</p>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/_second_json_partial.json.erb b/actionpack/test/fixtures/test/_second_json_partial.json.erb deleted file mode 100644 index 5ebb7f1afd..0000000000 --- a/actionpack/test/fixtures/test/_second_json_partial.json.erb +++ /dev/null @@ -1 +0,0 @@ -Third level
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/action_talk_to_layout.erb b/actionpack/test/fixtures/test/action_talk_to_layout.erb deleted file mode 100644 index 36e896daa8..0000000000 --- a/actionpack/test/fixtures/test/action_talk_to_layout.erb +++ /dev/null @@ -1,2 +0,0 @@ -<% @title = "Talking to the layout" -%> -Action was here!
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/calling_partial_with_layout.html.erb b/actionpack/test/fixtures/test/calling_partial_with_layout.html.erb deleted file mode 100644 index ac44bc0d81..0000000000 --- a/actionpack/test/fixtures/test/calling_partial_with_layout.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= render(:layout => "layout_for_partial", :partial => "partial_for_use_in_layout", :locals => { :name => "David" }) %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/capturing.erb b/actionpack/test/fixtures/test/capturing.erb deleted file mode 100644 index 1addaa40d9..0000000000 --- a/actionpack/test/fixtures/test/capturing.erb +++ /dev/null @@ -1,4 +0,0 @@ -<% days = capture do %> - Dreamy days -<% end %> -<%= days %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/change_priority.html.erb b/actionpack/test/fixtures/test/change_priority.html.erb deleted file mode 100644 index 5618977d05..0000000000 --- a/actionpack/test/fixtures/test/change_priority.html.erb +++ /dev/null @@ -1,2 +0,0 @@ -<%= render :partial => "test/json_change_priority", formats: :json %> -HTML Template, but <%= render :partial => "test/changing_priority" %> partial
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/content_for.erb b/actionpack/test/fixtures/test/content_for.erb deleted file mode 100644 index 1fb829f54c..0000000000 --- a/actionpack/test/fixtures/test/content_for.erb +++ /dev/null @@ -1 +0,0 @@ -<% content_for :title do -%>Putting stuff in the title!<% end -%>Great stuff!
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/content_for_concatenated.erb b/actionpack/test/fixtures/test/content_for_concatenated.erb deleted file mode 100644 index e65f629574..0000000000 --- a/actionpack/test/fixtures/test/content_for_concatenated.erb +++ /dev/null @@ -1,3 +0,0 @@ -<% content_for :title, "Putting stuff " - content_for :title, "in the title!" -%> -Great stuff!
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/content_for_with_parameter.erb b/actionpack/test/fixtures/test/content_for_with_parameter.erb deleted file mode 100644 index aeb6f73ce0..0000000000 --- a/actionpack/test/fixtures/test/content_for_with_parameter.erb +++ /dev/null @@ -1,2 +0,0 @@ -<% content_for :title, "Putting stuff in the title!" -%> -Great stuff!
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/formatted_html_erb.html.erb b/actionpack/test/fixtures/test/formatted_html_erb.html.erb deleted file mode 100644 index 1c64efabd8..0000000000 --- a/actionpack/test/fixtures/test/formatted_html_erb.html.erb +++ /dev/null @@ -1 +0,0 @@ -formatted html erb
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/greeting.html.erb b/actionpack/test/fixtures/test/greeting.html.erb deleted file mode 100644 index 62fb0293f0..0000000000 --- a/actionpack/test/fixtures/test/greeting.html.erb +++ /dev/null @@ -1 +0,0 @@ -<p>This is grand!</p> diff --git a/actionpack/test/fixtures/test/greeting.xml.erb b/actionpack/test/fixtures/test/greeting.xml.erb deleted file mode 100644 index 62fb0293f0..0000000000 --- a/actionpack/test/fixtures/test/greeting.xml.erb +++ /dev/null @@ -1 +0,0 @@ -<p>This is grand!</p> diff --git a/actionpack/test/fixtures/test/hello,world.erb b/actionpack/test/fixtures/test/hello,world.erb deleted file mode 100644 index bc8fa5e0ca..0000000000 --- a/actionpack/test/fixtures/test/hello,world.erb +++ /dev/null @@ -1 +0,0 @@ -Hello w*rld!
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/hello.builder b/actionpack/test/fixtures/test/hello.builder deleted file mode 100644 index a471553941..0000000000 --- a/actionpack/test/fixtures/test/hello.builder +++ /dev/null @@ -1,4 +0,0 @@ -xml.html do - xml.p "Hello #{@name}" - xml << render(:file => "test/greeting") -end
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/hello_world_container.builder b/actionpack/test/fixtures/test/hello_world_container.builder deleted file mode 100644 index e48d75c405..0000000000 --- a/actionpack/test/fixtures/test/hello_world_container.builder +++ /dev/null @@ -1,3 +0,0 @@ -xml.test do - render :partial => 'hello', :locals => { :xm => xml } -end
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/hello_world_from_rxml.builder b/actionpack/test/fixtures/test/hello_world_from_rxml.builder deleted file mode 100644 index 619a97ba96..0000000000 --- a/actionpack/test/fixtures/test/hello_world_from_rxml.builder +++ /dev/null @@ -1,3 +0,0 @@ -xml.html do - xml.p "Hello" -end diff --git a/actionpack/test/fixtures/test/hello_world_with_layout_false.erb b/actionpack/test/fixtures/test/hello_world_with_layout_false.erb deleted file mode 100644 index 6769dd60bd..0000000000 --- a/actionpack/test/fixtures/test/hello_world_with_layout_false.erb +++ /dev/null @@ -1 +0,0 @@ -Hello world!
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/html_template.html.erb b/actionpack/test/fixtures/test/html_template.html.erb deleted file mode 100644 index 1bbc2b7f09..0000000000 --- a/actionpack/test/fixtures/test/html_template.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= render :partial => "test/first_json_partial", formats: :json %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/hyphen-ated.erb b/actionpack/test/fixtures/test/hyphen-ated.erb deleted file mode 100644 index cd0875583a..0000000000 --- a/actionpack/test/fixtures/test/hyphen-ated.erb +++ /dev/null @@ -1 +0,0 @@ -Hello world! diff --git a/actionpack/test/fixtures/test/list.erb b/actionpack/test/fixtures/test/list.erb deleted file mode 100644 index 0a4bda58ee..0000000000 --- a/actionpack/test/fixtures/test/list.erb +++ /dev/null @@ -1 +0,0 @@ -<%= @test_unchanged = 'goodbye' %><%= render :partial => 'customer', :collection => @customers %><%= @test_unchanged %> diff --git a/actionpack/test/fixtures/test/non_erb_block_content_for.builder b/actionpack/test/fixtures/test/non_erb_block_content_for.builder deleted file mode 100644 index d539a425a4..0000000000 --- a/actionpack/test/fixtures/test/non_erb_block_content_for.builder +++ /dev/null @@ -1,4 +0,0 @@ -content_for :title do - 'Putting stuff in the title!' -end -xml << "Great stuff!" diff --git a/actionpack/test/fixtures/test/potential_conflicts.erb b/actionpack/test/fixtures/test/potential_conflicts.erb deleted file mode 100644 index a5e964e359..0000000000 --- a/actionpack/test/fixtures/test/potential_conflicts.erb +++ /dev/null @@ -1,4 +0,0 @@ -First: <%= @name %> -<%= render :partial => "person", :locals => { :name => "Stephan" } -%> -Fourth: <%= @name %> -Fifth: <%= name %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/proper_block_detection.erb b/actionpack/test/fixtures/test/proper_block_detection.erb deleted file mode 100644 index b55efbb25d..0000000000 --- a/actionpack/test/fixtures/test/proper_block_detection.erb +++ /dev/null @@ -1 +0,0 @@ -<%= @todo %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/render_file_from_template.html.erb b/actionpack/test/fixtures/test/render_file_from_template.html.erb deleted file mode 100644 index fde9f4bb64..0000000000 --- a/actionpack/test/fixtures/test/render_file_from_template.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= render :file => @path %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/render_file_with_locals_and_default.erb b/actionpack/test/fixtures/test/render_file_with_locals_and_default.erb deleted file mode 100644 index 9b4900acc5..0000000000 --- a/actionpack/test/fixtures/test/render_file_with_locals_and_default.erb +++ /dev/null @@ -1 +0,0 @@ -<%= secret ||= 'one' %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/render_implicit_html_template_from_xhr_request.da.html.erb b/actionpack/test/fixtures/test/render_implicit_html_template_from_xhr_request.da.html.erb deleted file mode 100644 index 0740b2d07c..0000000000 --- a/actionpack/test/fixtures/test/render_implicit_html_template_from_xhr_request.da.html.erb +++ /dev/null @@ -1 +0,0 @@ -Hey HTML! diff --git a/actionpack/test/fixtures/test/render_implicit_html_template_from_xhr_request.html.erb b/actionpack/test/fixtures/test/render_implicit_html_template_from_xhr_request.html.erb deleted file mode 100644 index 4a11845cfe..0000000000 --- a/actionpack/test/fixtures/test/render_implicit_html_template_from_xhr_request.html.erb +++ /dev/null @@ -1 +0,0 @@ -Hello HTML!
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/render_implicit_js_template_without_layout.js.erb b/actionpack/test/fixtures/test/render_implicit_js_template_without_layout.js.erb deleted file mode 100644 index 892ae5eca2..0000000000 --- a/actionpack/test/fixtures/test/render_implicit_js_template_without_layout.js.erb +++ /dev/null @@ -1 +0,0 @@ -alert('hello');
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/render_partial_inside_directory.html.erb b/actionpack/test/fixtures/test/render_partial_inside_directory.html.erb deleted file mode 100644 index 1461b95186..0000000000 --- a/actionpack/test/fixtures/test/render_partial_inside_directory.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= render partial: 'test/_directory/partial_with_locales', locals: {'name' => 'Jane'} %> diff --git a/actionpack/test/fixtures/test/render_to_string_test.erb b/actionpack/test/fixtures/test/render_to_string_test.erb deleted file mode 100644 index 6e267e8634..0000000000 --- a/actionpack/test/fixtures/test/render_to_string_test.erb +++ /dev/null @@ -1 +0,0 @@ -The value of foo is: ::<%= @foo %>:: diff --git a/actionpack/test/fixtures/test/render_two_partials.html.erb b/actionpack/test/fixtures/test/render_two_partials.html.erb deleted file mode 100644 index 3db6025860..0000000000 --- a/actionpack/test/fixtures/test/render_two_partials.html.erb +++ /dev/null @@ -1,2 +0,0 @@ -<%= render :partial => 'partial', :locals => {'first' => '1'} %> -<%= render :partial => 'partial', :locals => {'second' => '2'} %> diff --git a/actionpack/test/fixtures/test/using_layout_around_block.html.erb b/actionpack/test/fixtures/test/using_layout_around_block.html.erb deleted file mode 100644 index 3d6661df9a..0000000000 --- a/actionpack/test/fixtures/test/using_layout_around_block.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= render(:layout => "layout_for_partial", :locals => { :name => "David" }) do %>Inside from block<% end %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/with_html_partial.html.erb b/actionpack/test/fixtures/test/with_html_partial.html.erb deleted file mode 100644 index d84d909d64..0000000000 --- a/actionpack/test/fixtures/test/with_html_partial.html.erb +++ /dev/null @@ -1 +0,0 @@ -<strong><%= render :partial => "partial_only_html" %></strong> diff --git a/actionpack/test/fixtures/test/with_partial.html.erb b/actionpack/test/fixtures/test/with_partial.html.erb deleted file mode 100644 index 7502364cf5..0000000000 --- a/actionpack/test/fixtures/test/with_partial.html.erb +++ /dev/null @@ -1 +0,0 @@ -<strong><%= render :partial => "partial_only" %></strong> diff --git a/actionpack/test/fixtures/test/with_partial.text.erb b/actionpack/test/fixtures/test/with_partial.text.erb deleted file mode 100644 index 5f068ebf27..0000000000 --- a/actionpack/test/fixtures/test/with_partial.text.erb +++ /dev/null @@ -1 +0,0 @@ -**<%= render :partial => "partial_only" %>** diff --git a/actionpack/test/fixtures/test/with_xml_template.html.erb b/actionpack/test/fixtures/test/with_xml_template.html.erb deleted file mode 100644 index e54a7cd001..0000000000 --- a/actionpack/test/fixtures/test/with_xml_template.html.erb +++ /dev/null @@ -1 +0,0 @@ -<%= render :template => "test/greeting", :formats => :xml %> diff --git a/actionpack/test/fixtures/公共/bar.html b/actionpack/test/fixtures/公共/bar.html new file mode 100644 index 0000000000..67fc57079b --- /dev/null +++ b/actionpack/test/fixtures/公共/bar.html @@ -0,0 +1 @@ +/bar.html
\ No newline at end of file diff --git a/actionpack/test/fixtures/公共/bar/index.html b/actionpack/test/fixtures/公共/bar/index.html new file mode 100644 index 0000000000..d5bb8f898d --- /dev/null +++ b/actionpack/test/fixtures/公共/bar/index.html @@ -0,0 +1 @@ +/bar/index.html
\ No newline at end of file diff --git a/actionpack/test/fixtures/公共/foo/bar.html b/actionpack/test/fixtures/公共/foo/bar.html new file mode 100644 index 0000000000..9a35646205 --- /dev/null +++ b/actionpack/test/fixtures/公共/foo/bar.html @@ -0,0 +1 @@ +/foo/bar.html
\ No newline at end of file diff --git a/actionpack/test/fixtures/公共/foo/baz.css b/actionpack/test/fixtures/公共/foo/baz.css new file mode 100644 index 0000000000..b5173fbef2 --- /dev/null +++ b/actionpack/test/fixtures/公共/foo/baz.css @@ -0,0 +1,3 @@ +body { +background: #000; +} diff --git a/actionpack/test/fixtures/公共/foo/index.html b/actionpack/test/fixtures/公共/foo/index.html new file mode 100644 index 0000000000..497a2e898f --- /dev/null +++ b/actionpack/test/fixtures/公共/foo/index.html @@ -0,0 +1 @@ +/foo/index.html
\ No newline at end of file diff --git a/actionpack/test/fixtures/公共/foo/こんにちは.html b/actionpack/test/fixtures/公共/foo/こんにちは.html new file mode 100644 index 0000000000..1df9166522 --- /dev/null +++ b/actionpack/test/fixtures/公共/foo/こんにちは.html @@ -0,0 +1 @@ +means hello in Japanese diff --git a/actionpack/test/fixtures/公共/gzip/application-a71b3024f80aea3181c09774ca17e712.js b/actionpack/test/fixtures/公共/gzip/application-a71b3024f80aea3181c09774ca17e712.js new file mode 100644 index 0000000000..1826a7660e --- /dev/null +++ b/actionpack/test/fixtures/公共/gzip/application-a71b3024f80aea3181c09774ca17e712.js @@ -0,0 +1,4 @@ +!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=e.length,n=it.type(e);return"function"===n||it.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e}function r(e,t,n){if(it.isFunction(t))return it.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return it.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(ft.test(t))return it.filter(t,e,n);t=it.filter(t,e)}return it.grep(e,function(e){return it.inArray(e,t)>=0!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t=xt[e]={};return it.each(e.match(bt)||[],function(e,n){t[n]=!0}),t}function a(){ht.addEventListener?(ht.removeEventListener("DOMContentLoaded",s,!1),e.removeEventListener("load",s,!1)):(ht.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(ht.addEventListener||"load"===event.type||"complete"===ht.readyState)&&(a(),it.ready())}function l(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(Ct,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:kt.test(n)?it.parseJSON(n):n}catch(i){}it.data(e,t,n)}else n=void 0}return n}function u(e){var t;for(t in e)if(("data"!==t||!it.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(it.acceptData(e)){var i,o,a=it.expando,s=e.nodeType,l=s?it.cache:e,u=s?e[a]:e[a]&&a;if(u&&l[u]&&(r||l[u].data)||void 0!==n||"string"!=typeof t)return u||(u=s?e[a]=G.pop()||it.guid++:a),l[u]||(l[u]=s?{}:{toJSON:it.noop}),("object"==typeof t||"function"==typeof t)&&(r?l[u]=it.extend(l[u],t):l[u].data=it.extend(l[u].data,t)),o=l[u],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[it.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[it.camelCase(t)])):i=o,i}}function d(e,t,n){if(it.acceptData(e)){var r,i,o=e.nodeType,a=o?it.cache:e,s=o?e[it.expando]:it.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){it.isArray(t)?t=t.concat(it.map(t,it.camelCase)):t in r?t=[t]:(t=it.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!u(r):!it.isEmptyObject(r))return}(n||(delete a[s].data,u(a[s])))&&(o?it.cleanData([e],!0):nt.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}function f(){return!0}function p(){return!1}function h(){try{return ht.activeElement}catch(e){}}function m(e){var t=Mt.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function g(e,t){var n,r,i=0,o=typeof e.getElementsByTagName!==Et?e.getElementsByTagName(t||"*"):typeof e.querySelectorAll!==Et?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||it.nodeName(r,t)?o.push(r):it.merge(o,g(r,t));return void 0===t||t&&it.nodeName(e,t)?it.merge([e],o):o}function v(e){Dt.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t){return it.nodeName(e,"table")&&it.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function b(e){return e.type=(null!==it.find.attr(e,"type"))+"/"+e.type,e}function x(e){var t=Vt.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function w(e,t){for(var n,r=0;null!=(n=e[r]);r++)it._data(n,"globalEval",!t||it._data(t[r],"globalEval"))}function T(e,t){if(1===t.nodeType&&it.hasData(e)){var n,r,i,o=it._data(e),a=it._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)it.event.add(t,n,s[n][r])}a.data&&(a.data=it.extend({},a.data))}}function E(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!nt.noCloneEvent&&t[it.expando]){i=it._data(t);for(r in i.events)it.removeEvent(t,r,i.handle);t.removeAttribute(it.expando)}"script"===n&&t.text!==e.text?(b(t).text=e.text,x(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),nt.html5Clone&&e.innerHTML&&!it.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Dt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}function k(t,n){var r,i=it(n.createElement(t)).appendTo(n.body),o=e.getDefaultComputedStyle&&(r=e.getDefaultComputedStyle(i[0]))?r.display:it.css(i[0],"display");return i.detach(),o}function C(e){var t=ht,n=Zt[e];return n||(n=k(e,t),"none"!==n&&n||(Qt=(Qt||it("<iframe frameborder='0' width='0' height='0'/>")).appendTo(t.documentElement),t=(Qt[0].contentWindow||Qt[0].contentDocument).document,t.write(),t.close(),n=k(e,t),Qt.detach()),Zt[e]=n),n}function N(e,t){return{get:function(){var n=e();if(null!=n)return n?void delete this.get:(this.get=t).apply(this,arguments)}}}function S(e,t){if(t in e)return t;for(var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=pn.length;i--;)if(t=pn[i]+n,t in e)return t;return r}function j(e,t){for(var n,r,i,o=[],a=0,s=e.length;s>a;a++)r=e[a],r.style&&(o[a]=it._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&jt(r)&&(o[a]=it._data(r,"olddisplay",C(r.nodeName)))):(i=jt(r),(n&&"none"!==n||!i)&&it._data(r,"olddisplay",i?n:it.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}function A(e,t,n){var r=un.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function D(e,t,n,r,i){for(var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;4>o;o+=2)"margin"===n&&(a+=it.css(e,n+St[o],!0,i)),r?("content"===n&&(a-=it.css(e,"padding"+St[o],!0,i)),"margin"!==n&&(a-=it.css(e,"border"+St[o]+"Width",!0,i))):(a+=it.css(e,"padding"+St[o],!0,i),"padding"!==n&&(a+=it.css(e,"border"+St[o]+"Width",!0,i)));return a}function L(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=en(e),a=nt.boxSizing&&"border-box"===it.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=tn(e,t,o),(0>i||null==i)&&(i=e.style[t]),rn.test(i))return i;r=a&&(nt.boxSizingReliable()||i===e.style[t]),i=parseFloat(i)||0}return i+D(e,t,n||(a?"border":"content"),r,o)+"px"}function H(e,t,n,r,i){return new H.prototype.init(e,t,n,r,i)}function _(){return setTimeout(function(){hn=void 0}),hn=it.now()}function q(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=St[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}function F(e,t,n){for(var r,i=(xn[t]||[]).concat(xn["*"]),o=0,a=i.length;a>o;o++)if(r=i[o].call(n,t,e))return r}function M(e,t,n){var r,i,o,a,s,l,u,c,d=this,f={},p=e.style,h=e.nodeType&&jt(e),m=it._data(e,"fxshow");n.queue||(s=it._queueHooks(e,"fx"),null==s.unqueued&&(s.unqueued=0,l=s.empty.fire,s.empty.fire=function(){s.unqueued||l()}),s.unqueued++,d.always(function(){d.always(function(){s.unqueued--,it.queue(e,"fx").length||s.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],u=it.css(e,"display"),c="none"===u?it._data(e,"olddisplay")||C(e.nodeName):u,"inline"===c&&"none"===it.css(e,"float")&&(nt.inlineBlockNeedsLayout&&"inline"!==C(e.nodeName)?p.zoom=1:p.display="inline-block")),n.overflow&&(p.overflow="hidden",nt.shrinkWrapBlocks()||d.always(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],gn.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(h?"hide":"show")){if("show"!==i||!m||void 0===m[r])continue;h=!0}f[r]=m&&m[r]||it.style(e,r)}else u=void 0;if(it.isEmptyObject(f))"inline"===("none"===u?C(e.nodeName):u)&&(p.display=u);else{m?"hidden"in m&&(h=m.hidden):m=it._data(e,"fxshow",{}),o&&(m.hidden=!h),h?it(e).show():d.done(function(){it(e).hide()}),d.done(function(){var t;it._removeData(e,"fxshow");for(t in f)it.style(e,t,f[t])});for(r in f)a=F(h?m[r]:0,r,d),r in m||(m[r]=a.start,h&&(a.end=a.start,a.start="width"===r||"height"===r?1:0))}}function O(e,t){var n,r,i,o,a;for(n in e)if(r=it.camelCase(n),i=t[r],o=e[n],it.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=it.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}function R(e,t,n){var r,i,o=0,a=bn.length,s=it.Deferred().always(function(){delete l.elem}),l=function(){if(i)return!1;for(var t=hn||_(),n=Math.max(0,u.startTime+u.duration-t),r=n/u.duration||0,o=1-r,a=0,l=u.tweens.length;l>a;a++)u.tweens[a].run(o);return s.notifyWith(e,[u,o,n]),1>o&&l?n:(s.resolveWith(e,[u]),!1)},u=s.promise({elem:e,props:it.extend({},t),opts:it.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:hn||_(),duration:n.duration,tweens:[],createTween:function(t,n){var r=it.Tween(e,u.opts,t,n,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(r),r},stop:function(t){var n=0,r=t?u.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)u.tweens[n].run(1);return t?s.resolveWith(e,[u,t]):s.rejectWith(e,[u,t]),this}}),c=u.props;for(O(c,u.opts.specialEasing);a>o;o++)if(r=bn[o].call(u,e,c,u.opts))return r;return it.map(c,F,u),it.isFunction(u.opts.start)&&u.opts.start.call(e,u),it.fx.timer(it.extend(l,{elem:e,anim:u,queue:u.opts.queue})),u.progress(u.opts.progress).done(u.opts.done,u.opts.complete).fail(u.opts.fail).always(u.opts.always)}function B(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(bt)||[];if(it.isFunction(n))for(;r=o[i++];)"+"===r.charAt(0)?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function P(e,t,n,r){function i(s){var l;return o[s]=!0,it.each(e[s]||[],function(e,s){var u=s(t,n,r);return"string"!=typeof u||a||o[u]?a?!(l=u):void 0:(t.dataTypes.unshift(u),i(u),!1)}),l}var o={},a=e===zn;return i(t.dataTypes[0])||!o["*"]&&i("*")}function I(e,t){var n,r,i=it.ajaxSettings.flatOptions||{};for(r in t)void 0!==t[r]&&((i[r]?e:n||(n={}))[r]=t[r]);return n&&it.extend(!0,e,n),e}function W(e,t,n){for(var r,i,o,a,s=e.contents,l=e.dataTypes;"*"===l[0];)l.shift(),void 0===i&&(i=e.mimeType||t.getResponseHeader("Content-Type"));if(i)for(a in s)if(s[a]&&s[a].test(i)){l.unshift(a);break}if(l[0]in n)o=l[0];else{for(a in n){if(!l[0]||e.converters[a+" "+l[0]]){o=a;break}r||(r=a)}o=o||r}return o?(o!==l[0]&&l.unshift(o),n[o]):void 0}function $(e,t,n,r){var i,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(a=u[l+" "+o]||u["* "+o],!a)for(i in u)if(s=i.split(" "),s[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){a===!0?a=u[i]:u[i]!==!0&&(o=s[0],c.unshift(s[1]));break}if(a!==!0)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(d){return{state:"parsererror",error:a?d:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}function z(e,t,n,r){var i;if(it.isArray(t))it.each(t,function(t,i){n||Gn.test(e)?r(e,i):z(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==it.type(t))r(e,t);else for(i in t)z(e+"["+i+"]",t[i],n,r)}function X(){try{return new e.XMLHttpRequest}catch(t){}}function U(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}function V(e){return it.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}var G=[],Y=G.slice,J=G.concat,K=G.push,Q=G.indexOf,Z={},et=Z.toString,tt=Z.hasOwnProperty,nt={},rt="1.11.1",it=function(e,t){return new it.fn.init(e,t)},ot=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,at=/^-ms-/,st=/-([\da-z])/gi,lt=function(e,t){return t.toUpperCase()};it.fn=it.prototype={jquery:rt,constructor:it,selector:"",length:0,toArray:function(){return Y.call(this)},get:function(e){return null!=e?0>e?this[e+this.length]:this[e]:Y.call(this)},pushStack:function(e){var t=it.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return it.each(this,e,t)},map:function(e){return this.pushStack(it.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(Y.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:K,sort:G.sort,splice:G.splice},it.extend=it.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,l=arguments.length,u=!1;for("boolean"==typeof a&&(u=a,a=arguments[s]||{},s++),"object"==typeof a||it.isFunction(a)||(a={}),s===l&&(a=this,s--);l>s;s++)if(null!=(i=arguments[s]))for(r in i)e=a[r],n=i[r],a!==n&&(u&&n&&(it.isPlainObject(n)||(t=it.isArray(n)))?(t?(t=!1,o=e&&it.isArray(e)?e:[]):o=e&&it.isPlainObject(e)?e:{},a[r]=it.extend(u,o,n)):void 0!==n&&(a[r]=n));return a},it.extend({expando:"jQuery"+(rt+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isFunction:function(e){return"function"===it.type(e)},isArray:Array.isArray||function(e){return"array"===it.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!it.isArray(e)&&e-parseFloat(e)>=0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},isPlainObject:function(e){var t;if(!e||"object"!==it.type(e)||e.nodeType||it.isWindow(e))return!1;try{if(e.constructor&&!tt.call(e,"constructor")&&!tt.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}if(nt.ownLast)for(t in e)return tt.call(e,t);for(t in e);return void 0===t||tt.call(e,t)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?Z[et.call(e)]||"object":typeof e},globalEval:function(t){t&&it.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(at,"ms-").replace(st,lt)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,r){var i,o=0,a=e.length,s=n(e);if(r){if(s)for(;a>o&&(i=t.apply(e[o],r),i!==!1);o++);else for(o in e)if(i=t.apply(e[o],r),i===!1)break}else if(s)for(;a>o&&(i=t.call(e[o],o,e[o]),i!==!1);o++);else for(o in e)if(i=t.call(e[o],o,e[o]),i===!1)break;return e},trim:function(e){return null==e?"":(e+"").replace(ot,"")},makeArray:function(e,t){var r=t||[];return null!=e&&(n(Object(e))?it.merge(r,"string"==typeof e?[e]:e):K.call(r,e)),r},inArray:function(e,t,n){var r;if(t){if(Q)return Q.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;n>r;)e[i++]=t[r++];if(n!==n)for(;void 0!==t[r];)e[i++]=t[r++];return e.length=i,e},grep:function(e,t,n){for(var r,i=[],o=0,a=e.length,s=!n;a>o;o++)r=!t(e[o],o),r!==s&&i.push(e[o]);return i},map:function(e,t,r){var i,o=0,a=e.length,s=n(e),l=[];if(s)for(;a>o;o++)i=t(e[o],o,r),null!=i&&l.push(i);else for(o in e)i=t(e[o],o,r),null!=i&&l.push(i);return J.apply([],l)},guid:1,proxy:function(e,t){var n,r,i;return"string"==typeof t&&(i=e[t],t=e,e=i),it.isFunction(e)?(n=Y.call(arguments,2),r=function(){return e.apply(t||this,n.concat(Y.call(arguments)))},r.guid=e.guid=e.guid||it.guid++,r):void 0},now:function(){return+new Date},support:nt}),it.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){Z["[object "+t+"]"]=t.toLowerCase()});var ut=function(e){function t(e,t,n,r){var i,o,a,s,l,u,d,p,h,m;if((t?t.ownerDocument||t:P)!==H&&L(t),t=t||H,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(q&&!r){if(i=yt.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&R(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return Z.apply(n,t.getElementsByTagName(e)),n;if((a=i[3])&&w.getElementsByClassName&&t.getElementsByClassName)return Z.apply(n,t.getElementsByClassName(a)),n}if(w.qsa&&(!F||!F.test(e))){if(p=d=B,h=t,m=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){for(u=C(e),(d=t.getAttribute("id"))?p=d.replace(xt,"\\$&"):t.setAttribute("id",p),p="[id='"+p+"'] ",l=u.length;l--;)u[l]=p+f(u[l]);h=bt.test(e)&&c(t.parentNode)||t,m=u.join(",")}if(m)try{return Z.apply(n,h.querySelectorAll(m)),n}catch(g){}finally{d||t.removeAttribute("id")}}}return S(e.replace(lt,"$1"),t,n,r)}function n(){function e(n,r){return t.push(n+" ")>T.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[B]=!0,e}function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=e.length;r--;)T.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||G)-(~e.sourceIndex||G);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function l(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function u(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function c(e){return e&&typeof e.getElementsByTagName!==V&&e}function d(){}function f(e){for(var t=0,n=e.length,r="";n>t;t++)r+=e[t].value;return r}function p(e,t,n){var r=t.dir,i=n&&"parentNode"===r,o=W++;return t.first?function(t,n,o){for(;t=t[r];)if(1===t.nodeType||i)return e(t,n,o)}:function(t,n,a){var s,l,u=[I,o];if(a){for(;t=t[r];)if((1===t.nodeType||i)&&e(t,n,a))return!0}else for(;t=t[r];)if(1===t.nodeType||i){if(l=t[B]||(t[B]={}),(s=l[r])&&s[0]===I&&s[1]===o)return u[2]=s[2];if(l[r]=u,u[2]=e(t,n,a))return!0}}}function h(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function m(e,n,r){for(var i=0,o=n.length;o>i;i++)t(e,n[i],r);return r}function g(e,t,n,r,i){for(var o,a=[],s=0,l=e.length,u=null!=t;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function v(e,t,n,i,o,a){return i&&!i[B]&&(i=v(i)),o&&!o[B]&&(o=v(o,a)),r(function(r,a,s,l){var u,c,d,f=[],p=[],h=a.length,v=r||m(t||"*",s.nodeType?[s]:s,[]),y=!e||!r&&t?v:g(v,f,e,s,l),b=n?o||(r?e:h||i)?[]:a:y;if(n&&n(y,b,s,l),i)for(u=g(b,p),i(u,[],s,l),c=u.length;c--;)(d=u[c])&&(b[p[c]]=!(y[p[c]]=d));if(r){if(o||e){if(o){for(u=[],c=b.length;c--;)(d=b[c])&&u.push(y[c]=d);o(null,b=[],u,l)}for(c=b.length;c--;)(d=b[c])&&(u=o?tt.call(r,d):f[c])>-1&&(r[u]=!(a[u]=d))}}else b=g(b===a?b.splice(h,b.length):b),o?o(null,a,b,l):Z.apply(a,b)})}function y(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.relative[" "],s=o?1:0,l=p(function(e){return e===t},a,!0),u=p(function(e){return tt.call(t,e)>-1},a,!0),c=[function(e,n,r){return!o&&(r||n!==j)||((t=n).nodeType?l(e,n,r):u(e,n,r))}];i>s;s++)if(n=T.relative[e[s].type])c=[p(h(c),n)];else{if(n=T.filter[e[s].type].apply(null,e[s].matches),n[B]){for(r=++s;i>r&&!T.relative[e[r].type];r++);return v(s>1&&h(c),s>1&&f(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(lt,"$1"),n,r>s&&y(e.slice(s,r)),i>r&&y(e=e.slice(r)),i>r&&f(e))}c.push(n)}return h(c)}function b(e,n){var i=n.length>0,o=e.length>0,a=function(r,a,s,l,u){var c,d,f,p=0,h="0",m=r&&[],v=[],y=j,b=r||o&&T.find.TAG("*",u),x=I+=null==y?1:Math.random()||.1,w=b.length;for(u&&(j=a!==H&&a);h!==w&&null!=(c=b[h]);h++){if(o&&c){for(d=0;f=e[d++];)if(f(c,a,s)){l.push(c);break}u&&(I=x)}i&&((c=!f&&c)&&p--,r&&m.push(c))}if(p+=h,i&&h!==p){for(d=0;f=n[d++];)f(m,v,a,s);if(r){if(p>0)for(;h--;)m[h]||v[h]||(v[h]=K.call(l));v=g(v)}Z.apply(l,v),u&&!r&&v.length>0&&p+n.length>1&&t.uniqueSort(l)}return u&&(I=x,j=y),m};return i?r(a):a}var x,w,T,E,k,C,N,S,j,A,D,L,H,_,q,F,M,O,R,B="sizzle"+-new Date,P=e.document,I=0,W=0,$=n(),z=n(),X=n(),U=function(e,t){return e===t&&(D=!0),0},V="undefined",G=1<<31,Y={}.hasOwnProperty,J=[],K=J.pop,Q=J.push,Z=J.push,et=J.slice,tt=J.indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(this[t]===e)return t;return-1},nt="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",rt="[\\x20\\t\\r\\n\\f]",it="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",ot=it.replace("w","w#"),at="\\["+rt+"*("+it+")(?:"+rt+"*([*^$|!~]?=)"+rt+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+ot+"))|)"+rt+"*\\]",st=":("+it+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+at+")*)|.*)\\)|)",lt=new RegExp("^"+rt+"+|((?:^|[^\\\\])(?:\\\\.)*)"+rt+"+$","g"),ut=new RegExp("^"+rt+"*,"+rt+"*"),ct=new RegExp("^"+rt+"*([>+~]|"+rt+")"+rt+"*"),dt=new RegExp("="+rt+"*([^\\]'\"]*?)"+rt+"*\\]","g"),ft=new RegExp(st),pt=new RegExp("^"+ot+"$"),ht={ID:new RegExp("^#("+it+")"),CLASS:new RegExp("^\\.("+it+")"),TAG:new RegExp("^("+it.replace("w","w*")+")"),ATTR:new RegExp("^"+at),PSEUDO:new RegExp("^"+st),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+rt+"*(even|odd|(([+-]|)(\\d*)n|)"+rt+"*(?:([+-]|)"+rt+"*(\\d+)|))"+rt+"*\\)|)","i"),bool:new RegExp("^(?:"+nt+")$","i"),needsContext:new RegExp("^"+rt+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+rt+"*((?:-\\d)?\\d*)"+rt+"*\\)|)(?=[^-]|$)","i")},mt=/^(?:input|select|textarea|button)$/i,gt=/^h\d$/i,vt=/^[^{]+\{\s*\[native \w/,yt=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,bt=/[+~]/,xt=/'|\\/g,wt=new RegExp("\\\\([\\da-f]{1,6}"+rt+"?|("+rt+")|.)","ig"),Tt=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)};try{Z.apply(J=et.call(P.childNodes),P.childNodes),J[P.childNodes.length].nodeType}catch(Et){Z={apply:J.length?function(e,t){Q.apply(e,et.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}w=t.support={},k=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},L=t.setDocument=function(e){var t,n=e?e.ownerDocument||e:P,r=n.defaultView;return n!==H&&9===n.nodeType&&n.documentElement?(H=n,_=n.documentElement,q=!k(n),r&&r!==r.top&&(r.addEventListener?r.addEventListener("unload",function(){L()},!1):r.attachEvent&&r.attachEvent("onunload",function(){L()})),w.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),w.getElementsByTagName=i(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),w.getElementsByClassName=vt.test(n.getElementsByClassName)&&i(function(e){return e.innerHTML="<div class='a'></div><div class='a i'></div>",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),w.getById=i(function(e){return _.appendChild(e).id=B,!n.getElementsByName||!n.getElementsByName(B).length}),w.getById?(T.find.ID=function(e,t){if(typeof t.getElementById!==V&&q){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},T.filter.ID=function(e){var t=e.replace(wt,Tt);return function(e){return e.getAttribute("id")===t}}):(delete T.find.ID,T.filter.ID=function(e){var t=e.replace(wt,Tt);return function(e){var n=typeof e.getAttributeNode!==V&&e.getAttributeNode("id");return n&&n.value===t}}),T.find.TAG=w.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==V?t.getElementsByTagName(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},T.find.CLASS=w.getElementsByClassName&&function(e,t){return typeof t.getElementsByClassName!==V&&q?t.getElementsByClassName(e):void 0},M=[],F=[],(w.qsa=vt.test(n.querySelectorAll))&&(i(function(e){e.innerHTML="<select msallowclip=''><option selected=''></option></select>",e.querySelectorAll("[msallowclip^='']").length&&F.push("[*^$]="+rt+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+rt+"*(?:value|"+nt+")"),e.querySelectorAll(":checked").length||F.push(":checked")}),i(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+rt+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(w.matchesSelector=vt.test(O=_.matches||_.webkitMatchesSelector||_.mozMatchesSelector||_.oMatchesSelector||_.msMatchesSelector))&&i(function(e){w.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),M.push("!=",st)}),F=F.length&&new RegExp(F.join("|")),M=M.length&&new RegExp(M.join("|")),t=vt.test(_.compareDocumentPosition),R=t||vt.test(_.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},U=t?function(e,t){if(e===t)return D=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r?r:(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&r||!w.sortDetached&&t.compareDocumentPosition(e)===r?e===n||e.ownerDocument===P&&R(P,e)?-1:t===n||t.ownerDocument===P&&R(P,t)?1:A?tt.call(A,e)-tt.call(A,t):0:4&r?-1:1)}:function(e,t){if(e===t)return D=!0,0;var r,i=0,o=e.parentNode,s=t.parentNode,l=[e],u=[t];if(!o||!s)return e===n?-1:t===n?1:o?-1:s?1:A?tt.call(A,e)-tt.call(A,t):0;if(o===s)return a(e,t);for(r=e;r=r.parentNode;)l.unshift(r);for(r=t;r=r.parentNode;)u.unshift(r);for(;l[i]===u[i];)i++;return i?a(l[i],u[i]):l[i]===P?-1:u[i]===P?1:0},n):H},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==H&&L(e),n=n.replace(dt,"='$1']"),!(!w.matchesSelector||!q||M&&M.test(n)||F&&F.test(n)))try{var r=O.call(e,n);if(r||w.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return t(n,H,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==H&&L(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==H&&L(e);var n=T.attrHandle[t.toLowerCase()],r=n&&Y.call(T.attrHandle,t.toLowerCase())?n(e,t,!q):void 0;return void 0!==r?r:w.attributes||!q?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(D=!w.detectDuplicates,A=!w.sortStable&&e.slice(0),e.sort(U),D){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return A=null,e},E=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=E(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=E(t);return n},T=t.selectors={cacheLength:50,createPseudo:r,match:ht,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(wt,Tt),e[3]=(e[3]||e[4]||e[5]||"").replace(wt,Tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return ht.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&ft.test(n)&&(t=C(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(wt,Tt).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=$[e+" "];return t||(t=new RegExp("(^|"+rt+")"+e+"("+rt+"|$)"))&&$(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==V&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:n?(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o+" ").indexOf(r)>-1:"|="===n?o===r||o.slice(0,r.length+1)===r+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,d,f,p,h,m=o!==a?"nextSibling":"previousSibling",g=t.parentNode,v=s&&t.nodeName.toLowerCase(),y=!l&&!s;if(g){if(o){for(;m;){for(d=t;d=d[m];)if(s?d.nodeName.toLowerCase()===v:1===d.nodeType)return!1;h=m="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?g.firstChild:g.lastChild],a&&y){for(c=g[B]||(g[B]={}),u=c[e]||[],p=u[0]===I&&u[1],f=u[0]===I&&u[2],d=p&&g.childNodes[p];d=++p&&d&&d[m]||(f=p=0)||h.pop();)if(1===d.nodeType&&++f&&d===t){c[e]=[I,p,f];break}}else if(y&&(u=(t[B]||(t[B]={}))[e])&&u[0]===I)f=u[1];else for(;(d=++p&&d&&d[m]||(f=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==v:1!==d.nodeType)||!++f||(y&&((d[B]||(d[B]={}))[e]=[I,f]),d!==t)););return f-=i,f===r||f%r===0&&f/r>=0}}},PSEUDO:function(e,n){var i,o=T.pseudos[e]||T.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[B]?o(n):o.length>1?(i=[e,e,"",n],T.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=tt.call(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=N(e.replace(lt,"$1"));return i[B]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return function(t){return(t.textContent||t.innerText||E(t)).indexOf(e)>-1}}),lang:r(function(e){return pt.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(wt,Tt).toLowerCase(),function(t){var n;do if(n=q?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===_},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.pseudos.empty(e)},header:function(e){return gt.test(e.nodeName)},input:function(e){return mt.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:u(function(){return[0]}),last:u(function(e,t){return[t-1]}),eq:u(function(e,t,n){return[0>n?n+t:n]}),even:u(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:u(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:u(function(e,t,n){for(var r=0>n?n+t:n;--r>=0;)e.push(r);return e}),gt:u(function(e,t,n){for(var r=0>n?n+t:n;++r<t;)e.push(r);return e})}},T.pseudos.nth=T.pseudos.eq;for(x in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})T.pseudos[x]=s(x);for(x in{submit:!0,reset:!0})T.pseudos[x]=l(x);return d.prototype=T.filters=T.pseudos,T.setFilters=new d,C=t.tokenize=function(e,n){var r,i,o,a,s,l,u,c=z[e+" "];if(c)return n?0:c.slice(0);for(s=e,l=[],u=T.preFilter;s;){(!r||(i=ut.exec(s)))&&(i&&(s=s.slice(i[0].length)||s),l.push(o=[])),r=!1,(i=ct.exec(s))&&(r=i.shift(),o.push({value:r,type:i[0].replace(lt," ")}),s=s.slice(r.length));for(a in T.filter)!(i=ht[a].exec(s))||u[a]&&!(i=u[a](i))||(r=i.shift(),o.push({value:r,type:a,matches:i}),s=s.slice(r.length));if(!r)break}return n?s.length:s?t.error(e):z(e,l).slice(0)},N=t.compile=function(e,t){var n,r=[],i=[],o=X[e+" "];if(!o){for(t||(t=C(e)),n=t.length;n--;)o=y(t[n]),o[B]?r.push(o):i.push(o);o=X(e,b(i,r)),o.selector=e}return o},S=t.select=function(e,t,n,r){var i,o,a,s,l,u="function"==typeof e&&e,d=!r&&C(e=u.selector||e);if(n=n||[],1===d.length){if(o=d[0]=d[0].slice(0),o.length>2&&"ID"===(a=o[0]).type&&w.getById&&9===t.nodeType&&q&&T.relative[o[1].type]){if(t=(T.find.ID(a.matches[0].replace(wt,Tt),t)||[])[0],!t)return n;u&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=ht.needsContext.test(e)?0:o.length;i--&&(a=o[i],!T.relative[s=a.type]);)if((l=T.find[s])&&(r=l(a.matches[0].replace(wt,Tt),bt.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&f(o),!e)return Z.apply(n,r),n; +break}}return(u||N(e,d))(r,t,!q,n,bt.test(e)&&c(t.parentNode)||t),n},w.sortStable=B.split("").sort(U).join("")===B,w.detectDuplicates=!!D,L(),w.sortDetached=i(function(e){return 1&e.compareDocumentPosition(H.createElement("div"))}),i(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){return n?void 0:e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),w.attributes&&i(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){return n||"input"!==e.nodeName.toLowerCase()?void 0:e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(nt,function(e,t,n){var r;return n?void 0:e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);it.find=ut,it.expr=ut.selectors,it.expr[":"]=it.expr.pseudos,it.unique=ut.uniqueSort,it.text=ut.getText,it.isXMLDoc=ut.isXML,it.contains=ut.contains;var ct=it.expr.match.needsContext,dt=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,ft=/^.[^:#\[\.,]*$/;it.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?it.find.matchesSelector(r,e)?[r]:[]:it.find.matches(e,it.grep(t,function(e){return 1===e.nodeType}))},it.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(it(e).filter(function(){for(t=0;i>t;t++)if(it.contains(r[t],this))return!0}));for(t=0;i>t;t++)it.find(e,r[t],n);return n=this.pushStack(i>1?it.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},filter:function(e){return this.pushStack(r(this,e||[],!1))},not:function(e){return this.pushStack(r(this,e||[],!0))},is:function(e){return!!r(this,"string"==typeof e&&ct.test(e)?it(e):e||[],!1).length}});var pt,ht=e.document,mt=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,gt=it.fn.init=function(e,t){var n,r;if(!e)return this;if("string"==typeof e){if(n="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:mt.exec(e),!n||!n[1]&&t)return!t||t.jquery?(t||pt).find(e):this.constructor(t).find(e);if(n[1]){if(t=t instanceof it?t[0]:t,it.merge(this,it.parseHTML(n[1],t&&t.nodeType?t.ownerDocument||t:ht,!0)),dt.test(n[1])&&it.isPlainObject(t))for(n in t)it.isFunction(this[n])?this[n](t[n]):this.attr(n,t[n]);return this}if(r=ht.getElementById(n[2]),r&&r.parentNode){if(r.id!==n[2])return pt.find(e);this.length=1,this[0]=r}return this.context=ht,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):it.isFunction(e)?"undefined"!=typeof pt.ready?pt.ready(e):e(it):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),it.makeArray(e,this))};gt.prototype=it.fn,pt=it(ht);var vt=/^(?:parents|prev(?:Until|All))/,yt={children:!0,contents:!0,next:!0,prev:!0};it.extend({dir:function(e,t,n){for(var r=[],i=e[t];i&&9!==i.nodeType&&(void 0===n||1!==i.nodeType||!it(i).is(n));)1===i.nodeType&&r.push(i),i=i[t];return r},sibling:function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}}),it.fn.extend({has:function(e){var t,n=it(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(it.contains(this,n[t]))return!0})},closest:function(e,t){for(var n,r=0,i=this.length,o=[],a=ct.test(e)||"string"!=typeof e?it(e,t||this.context):0;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?a.index(n)>-1:1===n.nodeType&&it.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?it.unique(o):o)},index:function(e){return e?"string"==typeof e?it.inArray(this[0],it(e)):it.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(it.unique(it.merge(this.get(),it(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),it.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return it.dir(e,"parentNode")},parentsUntil:function(e,t,n){return it.dir(e,"parentNode",n)},next:function(e){return i(e,"nextSibling")},prev:function(e){return i(e,"previousSibling")},nextAll:function(e){return it.dir(e,"nextSibling")},prevAll:function(e){return it.dir(e,"previousSibling")},nextUntil:function(e,t,n){return it.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return it.dir(e,"previousSibling",n)},siblings:function(e){return it.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return it.sibling(e.firstChild)},contents:function(e){return it.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:it.merge([],e.childNodes)}},function(e,t){it.fn[e]=function(n,r){var i=it.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=it.filter(r,i)),this.length>1&&(yt[e]||(i=it.unique(i)),vt.test(e)&&(i=i.reverse())),this.pushStack(i)}});var bt=/\S+/g,xt={};it.Callbacks=function(e){e="string"==typeof e?xt[e]||o(e):it.extend({},e);var t,n,r,i,a,s,l=[],u=!e.once&&[],c=function(o){for(n=e.memory&&o,r=!0,a=s||0,s=0,i=l.length,t=!0;l&&i>a;a++)if(l[a].apply(o[0],o[1])===!1&&e.stopOnFalse){n=!1;break}t=!1,l&&(u?u.length&&c(u.shift()):n?l=[]:d.disable())},d={add:function(){if(l){var r=l.length;!function o(t){it.each(t,function(t,n){var r=it.type(n);"function"===r?e.unique&&d.has(n)||l.push(n):n&&n.length&&"string"!==r&&o(n)})}(arguments),t?i=l.length:n&&(s=r,c(n))}return this},remove:function(){return l&&it.each(arguments,function(e,n){for(var r;(r=it.inArray(n,l,r))>-1;)l.splice(r,1),t&&(i>=r&&i--,a>=r&&a--)}),this},has:function(e){return e?it.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],i=0,this},disable:function(){return l=u=n=void 0,this},disabled:function(){return!l},lock:function(){return u=void 0,n||d.disable(),this},locked:function(){return!u},fireWith:function(e,n){return!l||r&&!u||(n=n||[],n=[e,n.slice?n.slice():n],t?u.push(n):c(n)),this},fire:function(){return d.fireWith(this,arguments),this},fired:function(){return!!r}};return d},it.extend({Deferred:function(e){var t=[["resolve","done",it.Callbacks("once memory"),"resolved"],["reject","fail",it.Callbacks("once memory"),"rejected"],["notify","progress",it.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return it.Deferred(function(n){it.each(t,function(t,o){var a=it.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&it.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[o[0]+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?it.extend(e,r):r}},i={};return r.pipe=r.then,it.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=Y.call(arguments),a=o.length,s=1!==a||e&&it.isFunction(e.promise)?a:0,l=1===s?e:it.Deferred(),u=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?Y.call(arguments):i,r===t?l.notifyWith(n,r):--s||l.resolveWith(n,r)}};if(a>1)for(t=new Array(a),n=new Array(a),r=new Array(a);a>i;i++)o[i]&&it.isFunction(o[i].promise)?o[i].promise().done(u(i,r,o)).fail(l.reject).progress(u(i,n,t)):--s;return s||l.resolveWith(r,o),l.promise()}});var wt;it.fn.ready=function(e){return it.ready.promise().done(e),this},it.extend({isReady:!1,readyWait:1,holdReady:function(e){e?it.readyWait++:it.ready(!0)},ready:function(e){if(e===!0?!--it.readyWait:!it.isReady){if(!ht.body)return setTimeout(it.ready);it.isReady=!0,e!==!0&&--it.readyWait>0||(wt.resolveWith(ht,[it]),it.fn.triggerHandler&&(it(ht).triggerHandler("ready"),it(ht).off("ready")))}}}),it.ready.promise=function(t){if(!wt)if(wt=it.Deferred(),"complete"===ht.readyState)setTimeout(it.ready);else if(ht.addEventListener)ht.addEventListener("DOMContentLoaded",s,!1),e.addEventListener("load",s,!1);else{ht.attachEvent("onreadystatechange",s),e.attachEvent("onload",s);var n=!1;try{n=null==e.frameElement&&ht.documentElement}catch(r){}n&&n.doScroll&&!function i(){if(!it.isReady){try{n.doScroll("left")}catch(e){return setTimeout(i,50)}a(),it.ready()}}()}return wt.promise(t)};var Tt,Et="undefined";for(Tt in it(nt))break;nt.ownLast="0"!==Tt,nt.inlineBlockNeedsLayout=!1,it(function(){var e,t,n,r;n=ht.getElementsByTagName("body")[0],n&&n.style&&(t=ht.createElement("div"),r=ht.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),typeof t.style.zoom!==Et&&(t.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",nt.inlineBlockNeedsLayout=e=3===t.offsetWidth,e&&(n.style.zoom=1)),n.removeChild(r))}),function(){var e=ht.createElement("div");if(null==nt.deleteExpando){nt.deleteExpando=!0;try{delete e.test}catch(t){nt.deleteExpando=!1}}e=null}(),it.acceptData=function(e){var t=it.noData[(e.nodeName+" ").toLowerCase()],n=+e.nodeType||1;return 1!==n&&9!==n?!1:!t||t!==!0&&e.getAttribute("classid")===t};var kt=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Ct=/([A-Z])/g;it.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?it.cache[e[it.expando]]:e[it.expando],!!e&&!u(e)},data:function(e,t,n){return c(e,t,n)},removeData:function(e,t){return d(e,t)},_data:function(e,t,n){return c(e,t,n,!0)},_removeData:function(e,t){return d(e,t,!0)}}),it.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=it.data(o),1===o.nodeType&&!it._data(o,"parsedAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=it.camelCase(r.slice(5)),l(o,r,i[r])));it._data(o,"parsedAttrs",!0)}return i}return"object"==typeof e?this.each(function(){it.data(this,e)}):arguments.length>1?this.each(function(){it.data(this,e,t)}):o?l(o,e,it.data(o,e)):void 0},removeData:function(e){return this.each(function(){it.removeData(this,e)})}}),it.extend({queue:function(e,t,n){var r;return e?(t=(t||"fx")+"queue",r=it._data(e,t),n&&(!r||it.isArray(n)?r=it._data(e,t,it.makeArray(n)):r.push(n)),r||[]):void 0},dequeue:function(e,t){t=t||"fx";var n=it.queue(e,t),r=n.length,i=n.shift(),o=it._queueHooks(e,t),a=function(){it.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return it._data(e,n)||it._data(e,n,{empty:it.Callbacks("once memory").add(function(){it._removeData(e,t+"queue"),it._removeData(e,n)})})}}),it.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length<n?it.queue(this[0],e):void 0===t?this:this.each(function(){var n=it.queue(this,e,t);it._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&it.dequeue(this,e)})},dequeue:function(e){return this.each(function(){it.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=it.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};for("string"!=typeof e&&(t=e,e=void 0),e=e||"fx";a--;)n=it._data(o[a],e+"queueHooks"),n&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var Nt=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,St=["Top","Right","Bottom","Left"],jt=function(e,t){return e=t||e,"none"===it.css(e,"display")||!it.contains(e.ownerDocument,e)},At=it.access=function(e,t,n,r,i,o,a){var s=0,l=e.length,u=null==n;if("object"===it.type(n)){i=!0;for(s in n)it.access(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,it.isFunction(r)||(a=!0),u&&(a?(t.call(e,r),t=null):(u=t,t=function(e,t,n){return u.call(it(e),n)})),t))for(;l>s;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:u?t.call(e):l?t(e[0],n):o},Dt=/^(?:checkbox|radio)$/i;!function(){var e=ht.createElement("input"),t=ht.createElement("div"),n=ht.createDocumentFragment();if(t.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",nt.leadingWhitespace=3===t.firstChild.nodeType,nt.tbody=!t.getElementsByTagName("tbody").length,nt.htmlSerialize=!!t.getElementsByTagName("link").length,nt.html5Clone="<:nav></:nav>"!==ht.createElement("nav").cloneNode(!0).outerHTML,e.type="checkbox",e.checked=!0,n.appendChild(e),nt.appendChecked=e.checked,t.innerHTML="<textarea>x</textarea>",nt.noCloneChecked=!!t.cloneNode(!0).lastChild.defaultValue,n.appendChild(t),t.innerHTML="<input type='radio' checked='checked' name='t'/>",nt.checkClone=t.cloneNode(!0).cloneNode(!0).lastChild.checked,nt.noCloneEvent=!0,t.attachEvent&&(t.attachEvent("onclick",function(){nt.noCloneEvent=!1}),t.cloneNode(!0).click()),null==nt.deleteExpando){nt.deleteExpando=!0;try{delete t.test}catch(r){nt.deleteExpando=!1}}}(),function(){var t,n,r=ht.createElement("div");for(t in{submit:!0,change:!0,focusin:!0})n="on"+t,(nt[t+"Bubbles"]=n in e)||(r.setAttribute(n,"t"),nt[t+"Bubbles"]=r.attributes[n].expando===!1);r=null}();var Lt=/^(?:input|select|textarea)$/i,Ht=/^key/,_t=/^(?:mouse|pointer|contextmenu)|click/,qt=/^(?:focusinfocus|focusoutblur)$/,Ft=/^([^.]*)(?:\.(.+)|)$/;it.event={global:{},add:function(e,t,n,r,i){var o,a,s,l,u,c,d,f,p,h,m,g=it._data(e);if(g){for(n.handler&&(l=n,n=l.handler,i=l.selector),n.guid||(n.guid=it.guid++),(a=g.events)||(a=g.events={}),(c=g.handle)||(c=g.handle=function(e){return typeof it===Et||e&&it.event.triggered===e.type?void 0:it.event.dispatch.apply(c.elem,arguments)},c.elem=e),t=(t||"").match(bt)||[""],s=t.length;s--;)o=Ft.exec(t[s])||[],p=m=o[1],h=(o[2]||"").split(".").sort(),p&&(u=it.event.special[p]||{},p=(i?u.delegateType:u.bindType)||p,u=it.event.special[p]||{},d=it.extend({type:p,origType:m,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&it.expr.match.needsContext.test(i),namespace:h.join(".")},l),(f=a[p])||(f=a[p]=[],f.delegateCount=0,u.setup&&u.setup.call(e,r,h,c)!==!1||(e.addEventListener?e.addEventListener(p,c,!1):e.attachEvent&&e.attachEvent("on"+p,c))),u.add&&(u.add.call(e,d),d.handler.guid||(d.handler.guid=n.guid)),i?f.splice(f.delegateCount++,0,d):f.push(d),it.event.global[p]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,d,f,p,h,m,g=it.hasData(e)&&it._data(e);if(g&&(c=g.events)){for(t=(t||"").match(bt)||[""],u=t.length;u--;)if(s=Ft.exec(t[u])||[],p=m=s[1],h=(s[2]||"").split(".").sort(),p){for(d=it.event.special[p]||{},p=(r?d.delegateType:d.bindType)||p,f=c[p]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;o--;)a=f[o],!i&&m!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,d.remove&&d.remove.call(e,a));l&&!f.length&&(d.teardown&&d.teardown.call(e,h,g.handle)!==!1||it.removeEvent(e,p,g.handle),delete c[p])}else for(p in c)it.event.remove(e,p+t[u],n,r,!0);it.isEmptyObject(c)&&(delete g.handle,it._removeData(e,"events"))}},trigger:function(t,n,r,i){var o,a,s,l,u,c,d,f=[r||ht],p=tt.call(t,"type")?t.type:t,h=tt.call(t,"namespace")?t.namespace.split("."):[];if(s=c=r=r||ht,3!==r.nodeType&&8!==r.nodeType&&!qt.test(p+it.event.triggered)&&(p.indexOf(".")>=0&&(h=p.split("."),p=h.shift(),h.sort()),a=p.indexOf(":")<0&&"on"+p,t=t[it.expando]?t:new it.Event(p,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.namespace_re=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:it.makeArray(n,[t]),u=it.event.special[p]||{},i||!u.trigger||u.trigger.apply(r,n)!==!1)){if(!i&&!u.noBubble&&!it.isWindow(r)){for(l=u.delegateType||p,qt.test(l+p)||(s=s.parentNode);s;s=s.parentNode)f.push(s),c=s;c===(r.ownerDocument||ht)&&f.push(c.defaultView||c.parentWindow||e)}for(d=0;(s=f[d++])&&!t.isPropagationStopped();)t.type=d>1?l:u.bindType||p,o=(it._data(s,"events")||{})[t.type]&&it._data(s,"handle"),o&&o.apply(s,n),o=a&&s[a],o&&o.apply&&it.acceptData(s)&&(t.result=o.apply(s,n),t.result===!1&&t.preventDefault());if(t.type=p,!i&&!t.isDefaultPrevented()&&(!u._default||u._default.apply(f.pop(),n)===!1)&&it.acceptData(r)&&a&&r[p]&&!it.isWindow(r)){c=r[a],c&&(r[a]=null),it.event.triggered=p;try{r[p]()}catch(m){}it.event.triggered=void 0,c&&(r[a]=c)}return t.result}},dispatch:function(e){e=it.event.fix(e);var t,n,r,i,o,a=[],s=Y.call(arguments),l=(it._data(this,"events")||{})[e.type]||[],u=it.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!u.preDispatch||u.preDispatch.call(this,e)!==!1){for(a=it.event.handlers.call(this,e,l),t=0;(i=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=i.elem,o=0;(r=i.handlers[o++])&&!e.isImmediatePropagationStopped();)(!e.namespace_re||e.namespace_re.test(r.namespace))&&(e.handleObj=r,e.data=r.data,n=((it.event.special[r.origType]||{}).handle||r.handler).apply(i.elem,s),void 0!==n&&(e.result=n)===!1&&(e.preventDefault(),e.stopPropagation()));return u.postDispatch&&u.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,a=[],s=t.delegateCount,l=e.target;if(s&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(i=[],o=0;s>o;o++)r=t[o],n=r.selector+" ",void 0===i[n]&&(i[n]=r.needsContext?it(n,this).index(l)>=0:it.find(n,this,null,[l]).length),i[n]&&i.push(r);i.length&&a.push({elem:l,handlers:i})}return s<t.length&&a.push({elem:this,handlers:t.slice(s)}),a},fix:function(e){if(e[it.expando])return e;var t,n,r,i=e.type,o=e,a=this.fixHooks[i];for(a||(this.fixHooks[i]=a=_t.test(i)?this.mouseHooks:Ht.test(i)?this.keyHooks:{}),r=a.props?this.props.concat(a.props):this.props,e=new it.Event(o),t=r.length;t--;)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||ht),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,a.filter?a.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,t){var n,r,i,o=t.button,a=t.fromElement;return null==e.pageX&&null!=t.clientX&&(r=e.target.ownerDocument||ht,i=r.documentElement,n=r.body,e.pageX=t.clientX+(i&&i.scrollLeft||n&&n.scrollLeft||0)-(i&&i.clientLeft||n&&n.clientLeft||0),e.pageY=t.clientY+(i&&i.scrollTop||n&&n.scrollTop||0)-(i&&i.clientTop||n&&n.clientTop||0)),!e.relatedTarget&&a&&(e.relatedTarget=a===e.target?t.toElement:a),e.which||void 0===o||(e.which=1&o?1:2&o?3:4&o?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==h()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===h()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return it.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(e){return it.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=it.extend(new it.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?it.event.trigger(i,null,t):it.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},it.removeEvent=ht.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===Et&&(e[r]=null),e.detachEvent(r,n))},it.Event=function(e,t){return this instanceof it.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&e.returnValue===!1?f:p):this.type=e,t&&it.extend(this,t),this.timeStamp=e&&e.timeStamp||it.now(),void(this[it.expando]=!0)):new it.Event(e,t)},it.Event.prototype={isDefaultPrevented:p,isPropagationStopped:p,isImmediatePropagationStopped:p,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=f,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=f,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=f,e&&e.stopImmediatePropagation&&e.stopImmediatePropagation(),this.stopPropagation()}},it.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,t){it.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!it.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),nt.submitBubbles||(it.event.special.submit={setup:function(){return it.nodeName(this,"form")?!1:void it.event.add(this,"click._submit keypress._submit",function(e){var t=e.target,n=it.nodeName(t,"input")||it.nodeName(t,"button")?t.form:void 0;n&&!it._data(n,"submitBubbles")&&(it.event.add(n,"submit._submit",function(e){e._submit_bubble=!0}),it._data(n,"submitBubbles",!0))})},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&it.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return it.nodeName(this,"form")?!1:void it.event.remove(this,"._submit")}}),nt.changeBubbles||(it.event.special.change={setup:function(){return Lt.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(it.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),it.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),it.event.simulate("change",this,e,!0)})),!1):void it.event.add(this,"beforeactivate._change",function(e){var t=e.target;Lt.test(t.nodeName)&&!it._data(t,"changeBubbles")&&(it.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||it.event.simulate("change",this.parentNode,e,!0)}),it._data(t,"changeBubbles",!0))})},handle:function(e){var t=e.target;return this!==t||e.isSimulated||e.isTrigger||"radio"!==t.type&&"checkbox"!==t.type?e.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return it.event.remove(this,"._change"),!Lt.test(this.nodeName)}}),nt.focusinBubbles||it.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){it.event.simulate(t,e.target,it.event.fix(e),!0)};it.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=it._data(r,t);i||r.addEventListener(e,n,!0),it._data(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=it._data(r,t)-1;i?it._data(r,t,i):(r.removeEventListener(e,n,!0),it._removeData(r,t))}}}),it.fn.extend({on:function(e,t,n,r,i){var o,a;if("object"==typeof e){"string"!=typeof t&&(n=n||t,t=void 0);for(o in e)this.on(o,t,n,e[o],i);return this}if(null==n&&null==r?(r=t,n=t=void 0):null==r&&("string"==typeof t?(r=n,n=void 0):(r=n,n=t,t=void 0)),r===!1)r=p;else if(!r)return this;return 1===i&&(a=r,r=function(e){return it().off(e),a.apply(this,arguments)},r.guid=a.guid||(a.guid=it.guid++)),this.each(function(){it.event.add(this,e,r,n,t)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,it(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return(t===!1||"function"==typeof t)&&(n=t,t=void 0),n===!1&&(n=p),this.each(function(){it.event.remove(this,e,n,t)})},trigger:function(e,t){return this.each(function(){it.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?it.event.trigger(e,t,n,!0):void 0}});var Mt="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",Ot=/ jQuery\d+="(?:null|\d+)"/g,Rt=new RegExp("<(?:"+Mt+")[\\s/>]","i"),Bt=/^\s+/,Pt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,It=/<([\w:]+)/,Wt=/<tbody/i,$t=/<|&#?\w+;/,zt=/<(?:script|style|link)/i,Xt=/checked\s*(?:[^=]|=\s*.checked.)/i,Ut=/^$|\/(?:java|ecma)script/i,Vt=/^true\/(.*)/,Gt=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,Yt={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:nt.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},Jt=m(ht),Kt=Jt.appendChild(ht.createElement("div"));Yt.optgroup=Yt.option,Yt.tbody=Yt.tfoot=Yt.colgroup=Yt.caption=Yt.thead,Yt.th=Yt.td,it.extend({clone:function(e,t,n){var r,i,o,a,s,l=it.contains(e.ownerDocument,e);if(nt.html5Clone||it.isXMLDoc(e)||!Rt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Kt.innerHTML=e.outerHTML,Kt.removeChild(o=Kt.firstChild)),!(nt.noCloneEvent&&nt.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||it.isXMLDoc(e)))for(r=g(o),s=g(e),a=0;null!=(i=s[a]);++a)r[a]&&E(i,r[a]);if(t)if(n)for(s=s||g(e),r=r||g(o),a=0;null!=(i=s[a]);a++)T(i,r[a]);else T(e,o);return r=g(o,"script"),r.length>0&&w(r,!l&&g(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){for(var i,o,a,s,l,u,c,d=e.length,f=m(t),p=[],h=0;d>h;h++)if(o=e[h],o||0===o)if("object"===it.type(o))it.merge(p,o.nodeType?[o]:o);else if($t.test(o)){for(s=s||f.appendChild(t.createElement("div")),l=(It.exec(o)||["",""])[1].toLowerCase(),c=Yt[l]||Yt._default,s.innerHTML=c[1]+o.replace(Pt,"<$1></$2>")+c[2],i=c[0];i--;)s=s.lastChild;if(!nt.leadingWhitespace&&Bt.test(o)&&p.push(t.createTextNode(Bt.exec(o)[0])),!nt.tbody)for(o="table"!==l||Wt.test(o)?"<table>"!==c[1]||Wt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;i--;)it.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u);for(it.merge(p,s.childNodes),s.textContent="";s.firstChild;)s.removeChild(s.firstChild);s=f.lastChild}else p.push(t.createTextNode(o));for(s&&f.removeChild(s),nt.appendChecked||it.grep(g(p,"input"),v),h=0;o=p[h++];)if((!r||-1===it.inArray(o,r))&&(a=it.contains(o.ownerDocument,o),s=g(f.appendChild(o),"script"),a&&w(s),n))for(i=0;o=s[i++];)Ut.test(o.type||"")&&n.push(o);return s=null,f},cleanData:function(e,t){for(var n,r,i,o,a=0,s=it.expando,l=it.cache,u=nt.deleteExpando,c=it.event.special;null!=(n=e[a]);a++)if((t||it.acceptData(n))&&(i=n[s],o=i&&l[i])){if(o.events)for(r in o.events)c[r]?it.event.remove(n,r):it.removeEvent(n,r,o.handle);l[i]&&(delete l[i],u?delete n[s]:typeof n.removeAttribute!==Et?n.removeAttribute(s):n[s]=null,G.push(i))}}}),it.fn.extend({text:function(e){return At(this,function(e){return void 0===e?it.text(this):this.empty().append((this[0]&&this[0].ownerDocument||ht).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=y(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=y(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){for(var n,r=e?it.filter(e,this):this,i=0;null!=(n=r[i]);i++)t||1!==n.nodeType||it.cleanData(g(n)),n.parentNode&&(t&&it.contains(n.ownerDocument,n)&&w(g(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&it.cleanData(g(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&it.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return it.clone(this,e,t)})},html:function(e){return At(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e)return 1===t.nodeType?t.innerHTML.replace(Ot,""):void 0;if(!("string"!=typeof e||zt.test(e)||!nt.htmlSerialize&&Rt.test(e)||!nt.leadingWhitespace&&Bt.test(e)||Yt[(It.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(Pt,"<$1></$2>");try{for(;r>n;n++)t=this[n]||{},1===t.nodeType&&(it.cleanData(g(t,!1)),t.innerHTML=e);t=0}catch(i){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=arguments[0];return this.domManip(arguments,function(t){e=this.parentNode,it.cleanData(g(this)),e&&e.replaceChild(t,this)}),e&&(e.length||e.nodeType)?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t){e=J.apply([],e);var n,r,i,o,a,s,l=0,u=this.length,c=this,d=u-1,f=e[0],p=it.isFunction(f);if(p||u>1&&"string"==typeof f&&!nt.checkClone&&Xt.test(f))return this.each(function(n){var r=c.eq(n);p&&(e[0]=f.call(this,n,r.html())),r.domManip(e,t)});if(u&&(s=it.buildFragment(e,this[0].ownerDocument,!1,this),n=s.firstChild,1===s.childNodes.length&&(s=n),n)){for(o=it.map(g(s,"script"),b),i=o.length;u>l;l++)r=s,l!==d&&(r=it.clone(r,!0,!0),i&&it.merge(o,g(r,"script"))),t.call(this[l],r,l);if(i)for(a=o[o.length-1].ownerDocument,it.map(o,x),l=0;i>l;l++)r=o[l],Ut.test(r.type||"")&&!it._data(r,"globalEval")&&it.contains(a,r)&&(r.src?it._evalUrl&&it._evalUrl(r.src):it.globalEval((r.text||r.textContent||r.innerHTML||"").replace(Gt,"")));s=n=null}return this}}),it.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){it.fn[e]=function(e){for(var n,r=0,i=[],o=it(e),a=o.length-1;a>=r;r++)n=r===a?this:this.clone(!0),it(o[r])[t](n),K.apply(i,n.get());return this.pushStack(i)}});var Qt,Zt={};!function(){var e;nt.shrinkWrapBlocks=function(){if(null!=e)return e;e=!1;var t,n,r;return n=ht.getElementsByTagName("body")[0],n&&n.style?(t=ht.createElement("div"),r=ht.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),typeof t.style.zoom!==Et&&(t.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",t.appendChild(ht.createElement("div")).style.width="5px",e=3!==t.offsetWidth),n.removeChild(r),e):void 0}}();var en,tn,nn=/^margin/,rn=new RegExp("^("+Nt+")(?!px)[a-z%]+$","i"),on=/^(top|right|bottom|left)$/;e.getComputedStyle?(en=function(e){return e.ownerDocument.defaultView.getComputedStyle(e,null)},tn=function(e,t,n){var r,i,o,a,s=e.style;return n=n||en(e),a=n?n.getPropertyValue(t)||n[t]:void 0,n&&(""!==a||it.contains(e.ownerDocument,e)||(a=it.style(e,t)),rn.test(a)&&nn.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0===a?a:a+""}):ht.documentElement.currentStyle&&(en=function(e){return e.currentStyle},tn=function(e,t,n){var r,i,o,a,s=e.style;return n=n||en(e),a=n?n[t]:void 0,null==a&&s&&s[t]&&(a=s[t]),rn.test(a)&&!on.test(t)&&(r=s.left,i=e.runtimeStyle,o=i&&i.left,o&&(i.left=e.currentStyle.left),s.left="fontSize"===t?"1em":a,a=s.pixelLeft+"px",s.left=r,o&&(i.left=o)),void 0===a?a:a+""||"auto"}),function(){function t(){var t,n,r,i;n=ht.getElementsByTagName("body")[0],n&&n.style&&(t=ht.createElement("div"),r=ht.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),t.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",o=a=!1,l=!0,e.getComputedStyle&&(o="1%"!==(e.getComputedStyle(t,null)||{}).top,a="4px"===(e.getComputedStyle(t,null)||{width:"4px"}).width,i=t.appendChild(ht.createElement("div")),i.style.cssText=t.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",t.style.width="1px",l=!parseFloat((e.getComputedStyle(i,null)||{}).marginRight)),t.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=t.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",s=0===i[0].offsetHeight,s&&(i[0].style.display="",i[1].style.display="none",s=0===i[0].offsetHeight),n.removeChild(r))}var n,r,i,o,a,s,l;n=ht.createElement("div"),n.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",i=n.getElementsByTagName("a")[0],r=i&&i.style,r&&(r.cssText="float:left;opacity:.5",nt.opacity="0.5"===r.opacity,nt.cssFloat=!!r.cssFloat,n.style.backgroundClip="content-box",n.cloneNode(!0).style.backgroundClip="",nt.clearCloneStyle="content-box"===n.style.backgroundClip,nt.boxSizing=""===r.boxSizing||""===r.MozBoxSizing||""===r.WebkitBoxSizing,it.extend(nt,{reliableHiddenOffsets:function(){return null==s&&t(),s +},boxSizingReliable:function(){return null==a&&t(),a},pixelPosition:function(){return null==o&&t(),o},reliableMarginRight:function(){return null==l&&t(),l}}))}(),it.swap=function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i};var an=/alpha\([^)]*\)/i,sn=/opacity\s*=\s*([^)]*)/,ln=/^(none|table(?!-c[ea]).+)/,un=new RegExp("^("+Nt+")(.*)$","i"),cn=new RegExp("^([+-])=("+Nt+")","i"),dn={position:"absolute",visibility:"hidden",display:"block"},fn={letterSpacing:"0",fontWeight:"400"},pn=["Webkit","O","Moz","ms"];it.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=tn(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":nt.cssFloat?"cssFloat":"styleFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=it.camelCase(t),l=e.style;if(t=it.cssProps[s]||(it.cssProps[s]=S(l,s)),a=it.cssHooks[t]||it.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];if(o=typeof n,"string"===o&&(i=cn.exec(n))&&(n=(i[1]+1)*i[2]+parseFloat(it.css(e,t)),o="number"),null!=n&&n===n&&("number"!==o||it.cssNumber[s]||(n+="px"),nt.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),!(a&&"set"in a&&void 0===(n=a.set(e,n,r)))))try{l[t]=n}catch(u){}}},css:function(e,t,n,r){var i,o,a,s=it.camelCase(t);return t=it.cssProps[s]||(it.cssProps[s]=S(e.style,s)),a=it.cssHooks[t]||it.cssHooks[s],a&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=tn(e,t,r)),"normal"===o&&t in fn&&(o=fn[t]),""===n||n?(i=parseFloat(o),n===!0||it.isNumeric(i)?i||0:o):o}}),it.each(["height","width"],function(e,t){it.cssHooks[t]={get:function(e,n,r){return n?ln.test(it.css(e,"display"))&&0===e.offsetWidth?it.swap(e,dn,function(){return L(e,t,r)}):L(e,t,r):void 0},set:function(e,n,r){var i=r&&en(e);return A(e,n,r?D(e,t,r,nt.boxSizing&&"border-box"===it.css(e,"boxSizing",!1,i),i):0)}}}),nt.opacity||(it.cssHooks.opacity={get:function(e,t){return sn.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=it.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===it.trim(o.replace(an,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=an.test(o)?o.replace(an,i):o+" "+i)}}),it.cssHooks.marginRight=N(nt.reliableMarginRight,function(e,t){return t?it.swap(e,{display:"inline-block"},tn,[e,"marginRight"]):void 0}),it.each({margin:"",padding:"",border:"Width"},function(e,t){it.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];4>r;r++)i[e+St[r]+t]=o[r]||o[r-2]||o[0];return i}},nn.test(e)||(it.cssHooks[e+t].set=A)}),it.fn.extend({css:function(e,t){return At(this,function(e,t,n){var r,i,o={},a=0;if(it.isArray(t)){for(r=en(e),i=t.length;i>a;a++)o[t[a]]=it.css(e,t[a],!1,r);return o}return void 0!==n?it.style(e,t,n):it.css(e,t)},e,t,arguments.length>1)},show:function(){return j(this,!0)},hide:function(){return j(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){jt(this)?it(this).show():it(this).hide()})}}),it.Tween=H,H.prototype={constructor:H,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(it.cssNumber[n]?"":"px")},cur:function(){var e=H.propHooks[this.prop];return e&&e.get?e.get(this):H.propHooks._default.get(this)},run:function(e){var t,n=H.propHooks[this.prop];return this.pos=t=this.options.duration?it.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):H.propHooks._default.set(this),this}},H.prototype.init.prototype=H.prototype,H.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=it.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){it.fx.step[e.prop]?it.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[it.cssProps[e.prop]]||it.cssHooks[e.prop])?it.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},H.propHooks.scrollTop=H.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},it.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},it.fx=H.prototype.init,it.fx.step={};var hn,mn,gn=/^(?:toggle|show|hide)$/,vn=new RegExp("^(?:([+-])=|)("+Nt+")([a-z%]*)$","i"),yn=/queueHooks$/,bn=[M],xn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=vn.exec(t),o=i&&i[3]||(it.cssNumber[e]?"":"px"),a=(it.cssNumber[e]||"px"!==o&&+r)&&vn.exec(it.css(n.elem,e)),s=1,l=20;if(a&&a[3]!==o){o=o||a[3],i=i||[],a=+r||1;do s=s||".5",a/=s,it.style(n.elem,e,a+o);while(s!==(s=n.cur()/r)&&1!==s&&--l)}return i&&(a=n.start=+a||+r||0,n.unit=o,n.end=i[1]?a+(i[1]+1)*i[2]:+i[2]),n}]};it.Animation=it.extend(R,{tweener:function(e,t){it.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");for(var n,r=0,i=e.length;i>r;r++)n=e[r],xn[n]=xn[n]||[],xn[n].unshift(t)},prefilter:function(e,t){t?bn.unshift(e):bn.push(e)}}),it.speed=function(e,t,n){var r=e&&"object"==typeof e?it.extend({},e):{complete:n||!n&&t||it.isFunction(e)&&e,duration:e,easing:n&&t||t&&!it.isFunction(t)&&t};return r.duration=it.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in it.fx.speeds?it.fx.speeds[r.duration]:it.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){it.isFunction(r.old)&&r.old.call(this),r.queue&&it.dequeue(this,r.queue)},r},it.fn.extend({fadeTo:function(e,t,n,r){return this.filter(jt).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=it.isEmptyObject(e),o=it.speed(t,n,r),a=function(){var t=R(this,it.extend({},e),o);(i||it._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=void 0),t&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=it.timers,a=it._data(this);if(i)a[i]&&a[i].stop&&r(a[i]);else for(i in a)a[i]&&a[i].stop&&yn.test(i)&&r(a[i]);for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));(t||!n)&&it.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=it._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=it.timers,a=r?r.length:0;for(n.finish=!0,it.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),it.each(["toggle","show","hide"],function(e,t){var n=it.fn[t];it.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(q(t,!0),e,r,i)}}),it.each({slideDown:q("show"),slideUp:q("hide"),slideToggle:q("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){it.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),it.timers=[],it.fx.tick=function(){var e,t=it.timers,n=0;for(hn=it.now();n<t.length;n++)e=t[n],e()||t[n]!==e||t.splice(n--,1);t.length||it.fx.stop(),hn=void 0},it.fx.timer=function(e){it.timers.push(e),e()?it.fx.start():it.timers.pop()},it.fx.interval=13,it.fx.start=function(){mn||(mn=setInterval(it.fx.tick,it.fx.interval))},it.fx.stop=function(){clearInterval(mn),mn=null},it.fx.speeds={slow:600,fast:200,_default:400},it.fn.delay=function(e,t){return e=it.fx?it.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},function(){var e,t,n,r,i;t=ht.createElement("div"),t.setAttribute("className","t"),t.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",r=t.getElementsByTagName("a")[0],n=ht.createElement("select"),i=n.appendChild(ht.createElement("option")),e=t.getElementsByTagName("input")[0],r.style.cssText="top:1px",nt.getSetAttribute="t"!==t.className,nt.style=/top/.test(r.getAttribute("style")),nt.hrefNormalized="/a"===r.getAttribute("href"),nt.checkOn=!!e.value,nt.optSelected=i.selected,nt.enctype=!!ht.createElement("form").enctype,n.disabled=!0,nt.optDisabled=!i.disabled,e=ht.createElement("input"),e.setAttribute("value",""),nt.input=""===e.getAttribute("value"),e.value="t",e.setAttribute("type","radio"),nt.radioValue="t"===e.value}();var wn=/\r/g;it.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=it.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,it(this).val()):e,null==i?i="":"number"==typeof i?i+="":it.isArray(i)&&(i=it.map(i,function(e){return null==e?"":e+""})),t=it.valHooks[this.type]||it.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return t=it.valHooks[i.type]||it.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(wn,""):null==n?"":n)}}}),it.extend({valHooks:{option:{get:function(e){var t=it.find.attr(e,"value");return null!=t?t:it.trim(it.text(e))}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(nt.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&it.nodeName(n.parentNode,"optgroup"))){if(t=it(n).val(),o)return t;a.push(t)}return a},set:function(e,t){for(var n,r,i=e.options,o=it.makeArray(t),a=i.length;a--;)if(r=i[a],it.inArray(it.valHooks.option.get(r),o)>=0)try{r.selected=n=!0}catch(s){r.scrollHeight}else r.selected=!1;return n||(e.selectedIndex=-1),i}}}}),it.each(["radio","checkbox"],function(){it.valHooks[this]={set:function(e,t){return it.isArray(t)?e.checked=it.inArray(it(e).val(),t)>=0:void 0}},nt.checkOn||(it.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Tn,En,kn=it.expr.attrHandle,Cn=/^(?:checked|selected)$/i,Nn=nt.getSetAttribute,Sn=nt.input;it.fn.extend({attr:function(e,t){return At(this,it.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){it.removeAttr(this,e)})}}),it.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(e&&3!==o&&8!==o&&2!==o)return typeof e.getAttribute===Et?it.prop(e,t,n):(1===o&&it.isXMLDoc(e)||(t=t.toLowerCase(),r=it.attrHooks[t]||(it.expr.match.bool.test(t)?En:Tn)),void 0===n?r&&"get"in r&&null!==(i=r.get(e,t))?i:(i=it.find.attr(e,t),null==i?void 0:i):null!==n?r&&"set"in r&&void 0!==(i=r.set(e,n,t))?i:(e.setAttribute(t,n+""),n):void it.removeAttr(e,t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(bt);if(o&&1===e.nodeType)for(;n=o[i++];)r=it.propFix[n]||n,it.expr.match.bool.test(n)?Sn&&Nn||!Cn.test(n)?e[r]=!1:e[it.camelCase("default-"+n)]=e[r]=!1:it.attr(e,n,""),e.removeAttribute(Nn?n:r)},attrHooks:{type:{set:function(e,t){if(!nt.radioValue&&"radio"===t&&it.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}}}),En={set:function(e,t,n){return t===!1?it.removeAttr(e,n):Sn&&Nn||!Cn.test(n)?e.setAttribute(!Nn&&it.propFix[n]||n,n):e[it.camelCase("default-"+n)]=e[n]=!0,n}},it.each(it.expr.match.bool.source.match(/\w+/g),function(e,t){var n=kn[t]||it.find.attr;kn[t]=Sn&&Nn||!Cn.test(t)?function(e,t,r){var i,o;return r||(o=kn[t],kn[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,kn[t]=o),i}:function(e,t,n){return n?void 0:e[it.camelCase("default-"+t)]?t.toLowerCase():null}}),Sn&&Nn||(it.attrHooks.value={set:function(e,t,n){return it.nodeName(e,"input")?void(e.defaultValue=t):Tn&&Tn.set(e,t,n)}}),Nn||(Tn={set:function(e,t,n){var r=e.getAttributeNode(n);return r||e.setAttributeNode(r=e.ownerDocument.createAttribute(n)),r.value=t+="","value"===n||t===e.getAttribute(n)?t:void 0}},kn.id=kn.name=kn.coords=function(e,t,n){var r;return n?void 0:(r=e.getAttributeNode(t))&&""!==r.value?r.value:null},it.valHooks.button={get:function(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:void 0},set:Tn.set},it.attrHooks.contenteditable={set:function(e,t,n){Tn.set(e,""===t?!1:t,n)}},it.each(["width","height"],function(e,t){it.attrHooks[t]={set:function(e,n){return""===n?(e.setAttribute(t,"auto"),n):void 0}}})),nt.style||(it.attrHooks.style={get:function(e){return e.style.cssText||void 0},set:function(e,t){return e.style.cssText=t+""}});var jn=/^(?:input|select|textarea|button|object)$/i,An=/^(?:a|area)$/i;it.fn.extend({prop:function(e,t){return At(this,it.prop,e,t,arguments.length>1)},removeProp:function(e){return e=it.propFix[e]||e,this.each(function(){try{this[e]=void 0,delete this[e]}catch(t){}})}}),it.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(e,t,n){var r,i,o,a=e.nodeType;if(e&&3!==a&&8!==a&&2!==a)return o=1!==a||!it.isXMLDoc(e),o&&(t=it.propFix[t]||t,i=it.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=it.find.attr(e,"tabindex");return t?parseInt(t,10):jn.test(e.nodeName)||An.test(e.nodeName)&&e.href?0:-1}}}}),nt.hrefNormalized||it.each(["href","src"],function(e,t){it.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),nt.optSelected||(it.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),it.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){it.propFix[this.toLowerCase()]=this}),nt.enctype||(it.propFix.enctype="encoding");var Dn=/[\t\r\n\f]/g;it.fn.extend({addClass:function(e){var t,n,r,i,o,a,s=0,l=this.length,u="string"==typeof e&&e;if(it.isFunction(e))return this.each(function(t){it(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(bt)||[];l>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(Dn," "):" ")){for(o=0;i=t[o++];)r.indexOf(" "+i+" ")<0&&(r+=i+" ");a=it.trim(r),n.className!==a&&(n.className=a)}return this},removeClass:function(e){var t,n,r,i,o,a,s=0,l=this.length,u=0===arguments.length||"string"==typeof e&&e;if(it.isFunction(e))return this.each(function(t){it(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(bt)||[];l>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(Dn," "):"")){for(o=0;i=t[o++];)for(;r.indexOf(" "+i+" ")>=0;)r=r.replace(" "+i+" "," ");a=e?it.trim(r):"",n.className!==a&&(n.className=a)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):this.each(it.isFunction(e)?function(n){it(this).toggleClass(e.call(this,n,this.className,t),t)}:function(){if("string"===n)for(var t,r=0,i=it(this),o=e.match(bt)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else(n===Et||"boolean"===n)&&(this.className&&it._data(this,"__className__",this.className),this.className=this.className||e===!1?"":it._data(this,"__className__")||"")})},hasClass:function(e){for(var t=" "+e+" ",n=0,r=this.length;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(Dn," ").indexOf(t)>=0)return!0;return!1}}),it.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 contextmenu".split(" "),function(e,t){it.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),it.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var Ln=it.now(),Hn=/\?/,_n=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;it.parseJSON=function(t){if(e.JSON&&e.JSON.parse)return e.JSON.parse(t+"");var n,r=null,i=it.trim(t+"");return i&&!it.trim(i.replace(_n,function(e,t,i,o){return n&&t&&(r=0),0===r?e:(n=i||t,r+=!o-!i,"")}))?Function("return "+i)():it.error("Invalid JSON: "+t)},it.parseXML=function(t){var n,r;if(!t||"string"!=typeof t)return null;try{e.DOMParser?(r=new DOMParser,n=r.parseFromString(t,"text/xml")):(n=new ActiveXObject("Microsoft.XMLDOM"),n.async="false",n.loadXML(t))}catch(i){n=void 0}return n&&n.documentElement&&!n.getElementsByTagName("parsererror").length||it.error("Invalid XML: "+t),n};var qn,Fn,Mn=/#.*$/,On=/([?&])_=[^&]*/,Rn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Bn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Pn=/^(?:GET|HEAD)$/,In=/^\/\//,Wn=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,$n={},zn={},Xn="*/".concat("*");try{Fn=location.href}catch(Un){Fn=ht.createElement("a"),Fn.href="",Fn=Fn.href}qn=Wn.exec(Fn.toLowerCase())||[],it.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Fn,type:"GET",isLocal:Bn.test(qn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Xn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":it.parseJSON,"text xml":it.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?I(I(e,it.ajaxSettings),t):I(it.ajaxSettings,e)},ajaxPrefilter:B($n),ajaxTransport:B(zn),ajax:function(e,t){function n(e,t,n,r){var i,c,v,y,x,T=t;2!==b&&(b=2,s&&clearTimeout(s),u=void 0,a=r||"",w.readyState=e>0?4:0,i=e>=200&&300>e||304===e,n&&(y=W(d,w,n)),y=$(d,y,w,i),i?(d.ifModified&&(x=w.getResponseHeader("Last-Modified"),x&&(it.lastModified[o]=x),x=w.getResponseHeader("etag"),x&&(it.etag[o]=x)),204===e||"HEAD"===d.type?T="nocontent":304===e?T="notmodified":(T=y.state,c=y.data,v=y.error,i=!v)):(v=T,(e||!T)&&(T="error",0>e&&(e=0))),w.status=e,w.statusText=(t||T)+"",i?h.resolveWith(f,[c,T,w]):h.rejectWith(f,[w,T,v]),w.statusCode(g),g=void 0,l&&p.trigger(i?"ajaxSuccess":"ajaxError",[w,d,i?c:v]),m.fireWith(f,[w,T]),l&&(p.trigger("ajaxComplete",[w,d]),--it.active||it.event.trigger("ajaxStop")))}"object"==typeof e&&(t=e,e=void 0),t=t||{};var r,i,o,a,s,l,u,c,d=it.ajaxSetup({},t),f=d.context||d,p=d.context&&(f.nodeType||f.jquery)?it(f):it.event,h=it.Deferred(),m=it.Callbacks("once memory"),g=d.statusCode||{},v={},y={},b=0,x="canceled",w={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!c)for(c={};t=Rn.exec(a);)c[t[1].toLowerCase()]=t[2];t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=y[n]=y[n]||e,v[e]=t),this},overrideMimeType:function(e){return b||(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>b)for(t in e)g[t]=[g[t],e[t]];else w.always(e[w.status]);return this},abort:function(e){var t=e||x;return u&&u.abort(t),n(0,t),this}};if(h.promise(w).complete=m.add,w.success=w.done,w.error=w.fail,d.url=((e||d.url||Fn)+"").replace(Mn,"").replace(In,qn[1]+"//"),d.type=t.method||t.type||d.method||d.type,d.dataTypes=it.trim(d.dataType||"*").toLowerCase().match(bt)||[""],null==d.crossDomain&&(r=Wn.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]===qn[1]&&r[2]===qn[2]&&(r[3]||("http:"===r[1]?"80":"443"))===(qn[3]||("http:"===qn[1]?"80":"443")))),d.data&&d.processData&&"string"!=typeof d.data&&(d.data=it.param(d.data,d.traditional)),P($n,d,t,w),2===b)return w;l=d.global,l&&0===it.active++&&it.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Pn.test(d.type),o=d.url,d.hasContent||(d.data&&(o=d.url+=(Hn.test(o)?"&":"?")+d.data,delete d.data),d.cache===!1&&(d.url=On.test(o)?o.replace(On,"$1_="+Ln++):o+(Hn.test(o)?"&":"?")+"_="+Ln++)),d.ifModified&&(it.lastModified[o]&&w.setRequestHeader("If-Modified-Since",it.lastModified[o]),it.etag[o]&&w.setRequestHeader("If-None-Match",it.etag[o])),(d.data&&d.hasContent&&d.contentType!==!1||t.contentType)&&w.setRequestHeader("Content-Type",d.contentType),w.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Xn+"; q=0.01":""):d.accepts["*"]);for(i in d.headers)w.setRequestHeader(i,d.headers[i]);if(d.beforeSend&&(d.beforeSend.call(f,w,d)===!1||2===b))return w.abort();x="abort";for(i in{success:1,error:1,complete:1})w[i](d[i]);if(u=P(zn,d,t,w)){w.readyState=1,l&&p.trigger("ajaxSend",[w,d]),d.async&&d.timeout>0&&(s=setTimeout(function(){w.abort("timeout")},d.timeout));try{b=1,u.send(v,n)}catch(T){if(!(2>b))throw T;n(-1,T)}}else n(-1,"No Transport");return w},getJSON:function(e,t,n){return it.get(e,t,n,"json")},getScript:function(e,t){return it.get(e,void 0,t,"script")}}),it.each(["get","post"],function(e,t){it[t]=function(e,n,r,i){return it.isFunction(n)&&(i=i||r,r=n,n=void 0),it.ajax({url:e,type:t,dataType:i,data:n,success:r})}}),it.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){it.fn[t]=function(e){return this.on(t,e)}}),it._evalUrl=function(e){return it.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},it.fn.extend({wrapAll:function(e){if(it.isFunction(e))return this.each(function(t){it(this).wrapAll(e.call(this,t))});if(this[0]){var t=it(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return this.each(it.isFunction(e)?function(t){it(this).wrapInner(e.call(this,t))}:function(){var t=it(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=it.isFunction(e);return this.each(function(n){it(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){it.nodeName(this,"body")||it(this).replaceWith(this.childNodes)}).end()}}),it.expr.filters.hidden=function(e){return e.offsetWidth<=0&&e.offsetHeight<=0||!nt.reliableHiddenOffsets()&&"none"===(e.style&&e.style.display||it.css(e,"display"))},it.expr.filters.visible=function(e){return!it.expr.filters.hidden(e)};var Vn=/%20/g,Gn=/\[\]$/,Yn=/\r?\n/g,Jn=/^(?:submit|button|image|reset|file)$/i,Kn=/^(?:input|select|textarea|keygen)/i;it.param=function(e,t){var n,r=[],i=function(e,t){t=it.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=it.ajaxSettings&&it.ajaxSettings.traditional),it.isArray(e)||e.jquery&&!it.isPlainObject(e))it.each(e,function(){i(this.name,this.value)});else for(n in e)z(n,e[n],t,i);return r.join("&").replace(Vn,"+")},it.fn.extend({serialize:function(){return it.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=it.prop(this,"elements");return e?it.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!it(this).is(":disabled")&&Kn.test(this.nodeName)&&!Jn.test(e)&&(this.checked||!Dt.test(e))}).map(function(e,t){var n=it(this).val();return null==n?null:it.isArray(n)?it.map(n,function(e){return{name:t.name,value:e.replace(Yn,"\r\n")}}):{name:t.name,value:n.replace(Yn,"\r\n")}}).get()}}),it.ajaxSettings.xhr=void 0!==e.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&X()||U()}:X;var Qn=0,Zn={},er=it.ajaxSettings.xhr();e.ActiveXObject&&it(e).on("unload",function(){for(var e in Zn)Zn[e](void 0,!0)}),nt.cors=!!er&&"withCredentials"in er,er=nt.ajax=!!er,er&&it.ajaxTransport(function(e){if(!e.crossDomain||nt.cors){var t;return{send:function(n,r){var i,o=e.xhr(),a=++Qn;if(o.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(i in e.xhrFields)o[i]=e.xhrFields[i];e.mimeType&&o.overrideMimeType&&o.overrideMimeType(e.mimeType),e.crossDomain||n["X-Requested-With"]||(n["X-Requested-With"]="XMLHttpRequest");for(i in n)void 0!==n[i]&&o.setRequestHeader(i,n[i]+"");o.send(e.hasContent&&e.data||null),t=function(n,i){var s,l,u;if(t&&(i||4===o.readyState))if(delete Zn[a],t=void 0,o.onreadystatechange=it.noop,i)4!==o.readyState&&o.abort();else{u={},s=o.status,"string"==typeof o.responseText&&(u.text=o.responseText);try{l=o.statusText}catch(c){l=""}s||!e.isLocal||e.crossDomain?1223===s&&(s=204):s=u.text?200:404}u&&r(s,l,u,o.getAllResponseHeaders())},e.async?4===o.readyState?setTimeout(t):o.onreadystatechange=Zn[a]=t:t()},abort:function(){t&&t(void 0,!0)}}}}),it.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return it.globalEval(e),e}}}),it.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),it.ajaxTransport("script",function(e){if(e.crossDomain){var t,n=ht.head||it("head")[0]||ht.documentElement;return{send:function(r,i){t=ht.createElement("script"),t.async=!0,e.scriptCharset&&(t.charset=e.scriptCharset),t.src=e.url,t.onload=t.onreadystatechange=function(e,n){(n||!t.readyState||/loaded|complete/.test(t.readyState))&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),t=null,n||i(200,"success"))},n.insertBefore(t,n.firstChild)},abort:function(){t&&t.onload(void 0,!0)}}}});var tr=[],nr=/(=)\?(?=&|$)|\?\?/;it.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=tr.pop()||it.expando+"_"+Ln++;return this[e]=!0,e}}),it.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=t.jsonp!==!1&&(nr.test(t.url)?"url":"string"==typeof t.data&&!(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&nr.test(t.data)&&"data");return s||"jsonp"===t.dataTypes[0]?(i=t.jsonpCallback=it.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(nr,"$1"+i):t.jsonp!==!1&&(t.url+=(Hn.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||it.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,tr.push(i)),a&&it.isFunction(o)&&o(a[0]),a=o=void 0}),"script"):void 0}),it.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||ht;var r=dt.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=it.buildFragment([e],t,i),i&&i.length&&it(i).remove(),it.merge([],r.childNodes))};var rr=it.fn.load;it.fn.load=function(e,t,n){if("string"!=typeof e&&rr)return rr.apply(this,arguments);var r,i,o,a=this,s=e.indexOf(" ");return s>=0&&(r=it.trim(e.slice(s,e.length)),e=e.slice(0,s)),it.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(o="POST"),a.length>0&&it.ajax({url:e,type:o,dataType:"html",data:t}).done(function(e){i=arguments,a.html(r?it("<div>").append(it.parseHTML(e)).find(r):e)}).complete(n&&function(e,t){a.each(n,i||[e.responseText,t,e])}),this},it.expr.filters.animated=function(e){return it.grep(it.timers,function(t){return e===t.elem}).length};var ir=e.document.documentElement;it.offset={setOffset:function(e,t,n){var r,i,o,a,s,l,u,c=it.css(e,"position"),d=it(e),f={};"static"===c&&(e.style.position="relative"),s=d.offset(),o=it.css(e,"top"),l=it.css(e,"left"),u=("absolute"===c||"fixed"===c)&&it.inArray("auto",[o,l])>-1,u?(r=d.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(l)||0),it.isFunction(t)&&(t=t.call(e,n,s)),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):d.css(f)}},it.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){it.offset.setOffset(this,e,t)});var t,n,r={top:0,left:0},i=this[0],o=i&&i.ownerDocument;if(o)return t=o.documentElement,it.contains(t,i)?(typeof i.getBoundingClientRect!==Et&&(r=i.getBoundingClientRect()),n=V(o),{top:r.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:r.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):r},position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===it.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),it.nodeName(e[0],"html")||(n=e.offset()),n.top+=it.css(e[0],"borderTopWidth",!0),n.left+=it.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-it.css(r,"marginTop",!0),left:t.left-n.left-it.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent||ir;e&&!it.nodeName(e,"html")&&"static"===it.css(e,"position");)e=e.offsetParent;return e||ir})}}),it.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);it.fn[e]=function(r){return At(this,function(e,r,i){var o=V(e);return void 0===i?o?t in o?o[t]:o.document.documentElement[r]:e[r]:void(o?o.scrollTo(n?it(o).scrollLeft():i,n?i:it(o).scrollTop()):e[r]=i)},e,r,arguments.length,null)}}),it.each(["top","left"],function(e,t){it.cssHooks[t]=N(nt.pixelPosition,function(e,n){return n?(n=tn(e,t),rn.test(n)?it(e).position()[t]+"px":n):void 0})}),it.each({Height:"height",Width:"width"},function(e,t){it.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){it.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return At(this,function(t,n,r){var i;return it.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?it.css(t,n,a):it.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),it.fn.size=function(){return this.length},it.fn.andSelf=it.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return it});var or=e.jQuery,ar=e.$;return it.noConflict=function(t){return e.$===it&&(e.$=ar),t&&e.jQuery===it&&(e.jQuery=or),it},typeof t===Et&&(e.jQuery=e.$=it),it}),function(e,t){e.rails!==t&&e.error("jquery-ujs has already been loaded!");var n,r=e(document);e.rails=n={linkClickSelector:"a[data-confirm], a[data-method], a[data-remote], a[data-disable-with], a[data-disable]",buttonClickSelector:"button[data-remote], button[data-confirm]",inputChangeSelector:"select[data-remote], input[data-remote], textarea[data-remote]",formSubmitSelector:"form",formInputClickSelector:"form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])",disableSelector:"input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled",enableSelector:"input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled",requiredInputSelector:"input[name][required]:not([disabled]),textarea[name][required]:not([disabled])",fileInputSelector:"input[type=file]",linkDisableSelector:"a[data-disable-with], a[data-disable]",buttonDisableSelector:"button[data-remote][data-disable-with], button[data-remote][data-disable]",CSRFProtection:function(t){var n=e('meta[name="csrf-token"]').attr("content");n&&t.setRequestHeader("X-CSRF-Token",n)},refreshCSRFTokens:function(){var t=e("meta[name=csrf-token]").attr("content"),n=e("meta[name=csrf-param]").attr("content");e('form input[name="'+n+'"]').val(t)},fire:function(t,n,r){var i=e.Event(n);return t.trigger(i,r),i.result!==!1},confirm:function(e){return confirm(e)},ajax:function(t){return e.ajax(t)},href:function(e){return e.attr("href")},handleRemote:function(r){var i,o,a,s,l,u,c,d;if(n.fire(r,"ajax:before")){if(s=r.data("cross-domain"),l=s===t?null:s,u=r.data("with-credentials")||null,c=r.data("type")||e.ajaxSettings&&e.ajaxSettings.dataType,r.is("form")){i=r.attr("method"),o=r.attr("action"),a=r.serializeArray();var f=r.data("ujs:submit-button");f&&(a.push(f),r.data("ujs:submit-button",null))}else r.is(n.inputChangeSelector)?(i=r.data("method"),o=r.data("url"),a=r.serialize(),r.data("params")&&(a=a+"&"+r.data("params"))):r.is(n.buttonClickSelector)?(i=r.data("method")||"get",o=r.data("url"),a=r.serialize(),r.data("params")&&(a=a+"&"+r.data("params"))):(i=r.data("method"),o=n.href(r),a=r.data("params")||null);return d={type:i||"GET",data:a,dataType:c,beforeSend:function(e,i){return i.dataType===t&&e.setRequestHeader("accept","*/*;q=0.5, "+i.accepts.script),n.fire(r,"ajax:beforeSend",[e,i])?void r.trigger("ajax:send",e):!1 +},success:function(e,t,n){r.trigger("ajax:success",[e,t,n])},complete:function(e,t){r.trigger("ajax:complete",[e,t])},error:function(e,t,n){r.trigger("ajax:error",[e,t,n])},crossDomain:l},u&&(d.xhrFields={withCredentials:u}),o&&(d.url=o),n.ajax(d)}return!1},handleMethod:function(r){var i=n.href(r),o=r.data("method"),a=r.attr("target"),s=e("meta[name=csrf-token]").attr("content"),l=e("meta[name=csrf-param]").attr("content"),u=e('<form method="post" action="'+i+'"></form>'),c='<input name="_method" value="'+o+'" type="hidden" />';l!==t&&s!==t&&(c+='<input name="'+l+'" value="'+s+'" type="hidden" />'),a&&u.attr("target",a),u.hide().append(c).appendTo("body"),u.submit()},formElements:function(t,n){return t.is("form")?e(t[0].elements).filter(n):t.find(n)},disableFormElements:function(t){n.formElements(t,n.disableSelector).each(function(){n.disableFormElement(e(this))})},disableFormElement:function(e){var n,r;n=e.is("button")?"html":"val",r=e.data("disable-with"),e.data("ujs:enable-with",e[n]()),r!==t&&e[n](r),e.prop("disabled",!0)},enableFormElements:function(t){n.formElements(t,n.enableSelector).each(function(){n.enableFormElement(e(this))})},enableFormElement:function(e){var t=e.is("button")?"html":"val";e.data("ujs:enable-with")&&e[t](e.data("ujs:enable-with")),e.prop("disabled",!1)},allowAction:function(e){var t,r=e.data("confirm"),i=!1;return r?(n.fire(e,"confirm")&&(i=n.confirm(r),t=n.fire(e,"confirm:complete",[i])),i&&t):!0},blankInputs:function(t,n,r){var i,o,a=e(),s=n||"input,textarea",l=t.find(s);return l.each(function(){if(i=e(this),o=i.is("input[type=checkbox],input[type=radio]")?i.is(":checked"):i.val(),!o==!r){if(i.is("input[type=radio]")&&l.filter('input[type=radio]:checked[name="'+i.attr("name")+'"]').length)return!0;a=a.add(i)}}),a.length?a:!1},nonBlankInputs:function(e,t){return n.blankInputs(e,t,!0)},stopEverything:function(t){return e(t.target).trigger("ujs:everythingStopped"),t.stopImmediatePropagation(),!1},disableElement:function(e){var r=e.data("disable-with");e.data("ujs:enable-with",e.html()),r!==t&&e.html(r),e.bind("click.railsDisable",function(e){return n.stopEverything(e)})},enableElement:function(e){e.data("ujs:enable-with")!==t&&(e.html(e.data("ujs:enable-with")),e.removeData("ujs:enable-with")),e.unbind("click.railsDisable")}},n.fire(r,"rails:attachBindings")&&(e.ajaxPrefilter(function(e,t,r){e.crossDomain||n.CSRFProtection(r)}),r.delegate(n.linkDisableSelector,"ajax:complete",function(){n.enableElement(e(this))}),r.delegate(n.buttonDisableSelector,"ajax:complete",function(){n.enableFormElement(e(this))}),r.delegate(n.linkClickSelector,"click.rails",function(r){var i=e(this),o=i.data("method"),a=i.data("params"),s=r.metaKey||r.ctrlKey;if(!n.allowAction(i))return n.stopEverything(r);if(!s&&i.is(n.linkDisableSelector)&&n.disableElement(i),i.data("remote")!==t){if(s&&(!o||"GET"===o)&&!a)return!0;var l=n.handleRemote(i);return l===!1?n.enableElement(i):l.error(function(){n.enableElement(i)}),!1}return i.data("method")?(n.handleMethod(i),!1):void 0}),r.delegate(n.buttonClickSelector,"click.rails",function(t){var r=e(this);if(!n.allowAction(r))return n.stopEverything(t);r.is(n.buttonDisableSelector)&&n.disableFormElement(r);var i=n.handleRemote(r);return i===!1?n.enableFormElement(r):i.error(function(){n.enableFormElement(r)}),!1}),r.delegate(n.inputChangeSelector,"change.rails",function(t){var r=e(this);return n.allowAction(r)?(n.handleRemote(r),!1):n.stopEverything(t)}),r.delegate(n.formSubmitSelector,"submit.rails",function(r){var i,o,a=e(this),s=a.data("remote")!==t;if(!n.allowAction(a))return n.stopEverything(r);if(a.attr("novalidate")==t&&(i=n.blankInputs(a,n.requiredInputSelector),i&&n.fire(a,"ajax:aborted:required",[i])))return n.stopEverything(r);if(s){if(o=n.nonBlankInputs(a,n.fileInputSelector)){setTimeout(function(){n.disableFormElements(a)},13);var l=n.fire(a,"ajax:aborted:file",[o]);return l||setTimeout(function(){n.enableFormElements(a)},13),l}return n.handleRemote(a),!1}setTimeout(function(){n.disableFormElements(a)},13)}),r.delegate(n.formInputClickSelector,"click.rails",function(t){var r=e(this);if(!n.allowAction(r))return n.stopEverything(t);var i=r.attr("name"),o=i?{name:i,value:r.val()}:null;r.closest("form").data("ujs:submit-button",o)}),r.delegate(n.formSubmitSelector,"ajax:send.rails",function(t){this==t.target&&n.disableFormElements(e(this))}),r.delegate(n.formSubmitSelector,"ajax:complete.rails",function(t){this==t.target&&n.enableFormElements(e(this))}),e(function(){n.refreshCSRFTokens()}))}(jQuery),function(){var e,t,n,r,i,o,a,s,l,u,c,d,f,p,h,m,g,v,y,b,x,w,T,E,k,C,N,S,j,A,D,L,H,_,q,F,M,O,R,B,P,I,W,$,z,X,U,V,G,Y=[].indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(t in this&&this[t]===e)return t;return-1},J={}.hasOwnProperty,K=function(e,t){function n(){this.constructor=e}for(var r in t)J.call(t,r)&&(e[r]=t[r]);return n.prototype=t.prototype,e.prototype=new n,e.__super__=t.prototype,e},Q=[].slice;j={},d=10,$=!1,m=null,S=null,q=null,h=null,V=null,b=function(e){var t;return e=new n(e),B(),c(),F(e),$&&(t=z(e.absolute))?(x(t),w(e)):w(e,W)},z=function(e){var t;return t=j[e],t&&!t.transitionCacheDisabled?t:void 0},g=function(e){return null==e&&(e=!0),$=e},w=function(e,t){return null==t&&(t=function(){return function(){}}(this)),X("page:fetch",{url:e.absolute}),null!=V&&V.abort(),V=new XMLHttpRequest,V.open("GET",e.withoutHashForIE10compatibility(),!0),V.setRequestHeader("Accept","text/html, application/xhtml+xml, application/xml"),V.setRequestHeader("X-XHR-Referer",q),V.onload=function(){var n;return X("page:receive"),(n=H())?(f.apply(null,y(n)),M(),t(),X("page:load")):document.location.href=e.absolute},V.onloadend=function(){return V=null},V.onerror=function(){return document.location.href=e.absolute},V.send()},x=function(e){return null!=V&&V.abort(),f(e.title,e.body),_(e),X("page:restore")},c=function(){var e;return e=new n(m.url),j[e.absolute]={url:e.relative,body:document.body,title:document.title,positionY:window.pageYOffset,positionX:window.pageXOffset,cachedAt:(new Date).getTime(),transitionCacheDisabled:null!=document.querySelector("[data-no-transition-cache]")},p(d)},D=function(e){return null==e&&(e=d),/^[\d]+$/.test(e)?d=parseInt(e):void 0},p=function(e){var t,n,r,i,o,a;for(r=Object.keys(j),t=r.map(function(e){return j[e].cachedAt}).sort(function(e,t){return t-e}),a=[],i=0,o=r.length;o>i;i++)n=r[i],j[n].cachedAt<=t[e]&&(X("page:expire",j[n]),a.push(delete j[n]));return a},f=function(t,n,r,i){return document.title=t,document.documentElement.replaceChild(n,document.body),null!=r&&e.update(r),i&&v(),m=window.history.state,X("page:change"),X("page:update")},v=function(){var e,t,n,r,i,o,a,s,l,u,c,d;for(o=Array.prototype.slice.call(document.body.querySelectorAll('script:not([data-turbolinks-eval="false"])')),a=0,l=o.length;l>a;a++)if(i=o[a],""===(c=i.type)||"text/javascript"===c){for(t=document.createElement("script"),d=i.attributes,s=0,u=d.length;u>s;s++)e=d[s],t.setAttribute(e.name,e.value);t.appendChild(document.createTextNode(i.innerHTML)),r=i.parentNode,n=i.nextSibling,r.removeChild(i),r.insertBefore(t,n)}},P=function(e){return e.innerHTML=e.innerHTML.replace(/<noscript[\S\s]*?<\/noscript>/gi,""),e},F=function(e){return(e=new n(e)).absolute!==q?window.history.pushState({turbolinks:!0,url:e.absolute},"",e.absolute):void 0},M=function(){var e,t;return(e=V.getResponseHeader("X-XHR-Redirected-To"))?(e=new n(e),t=e.hasNoHash()?document.location.hash:"",window.history.replaceState(m,"",e.href+t)):void 0},B=function(){return q=document.location.href},R=function(){return window.history.replaceState({turbolinks:!0,url:document.location.href},"",document.location.href)},O=function(){return m=window.history.state},_=function(e){return window.scrollTo(e.positionX,e.positionY)},W=function(){return document.location.hash?document.location.href=document.location.href:window.scrollTo(0,0)},L=function(e){var t,n;return t=(null!=(n=document.cookie.match(new RegExp(e+"=(\\w+)")))?n[1].toUpperCase():void 0)||"",document.cookie=e+"=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/",t},X=function(e,t){var n;return n=document.createEvent("Events"),t&&(n.data=t),n.initEvent(e,!0,!0),document.dispatchEvent(n)},A=function(){return!X("page:before-change")},H=function(){var e,t,n,r,i,o;return t=function(){var e;return 400<=(e=V.status)&&600>e},o=function(){return V.getResponseHeader("Content-Type").match(/^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/)},r=function(e){var t,n,r,i,o;for(i=e.head.childNodes,o=[],n=0,r=i.length;r>n;n++)t=i[n],null!=("function"==typeof t.getAttribute?t.getAttribute("data-turbolinks-track"):void 0)&&o.push(t.getAttribute("src")||t.getAttribute("href"));return o},e=function(e){var t;return S||(S=r(document)),t=r(e),t.length!==S.length||i(t,S).length!==S.length},i=function(e,t){var n,r,i,o,a;for(e.length>t.length&&(o=[t,e],e=o[0],t=o[1]),a=[],r=0,i=e.length;i>r;r++)n=e[r],Y.call(t,n)>=0&&a.push(n);return a},!t()&&o()&&(n=h(V.responseText),n&&!e(n))?n:void 0},y=function(t){var n;return n=t.querySelector("title"),[null!=n?n.textContent:void 0,P(t.body),e.get(t).token,"runScripts"]},e={get:function(e){var t;return null==e&&(e=document),{node:t=e.querySelector('meta[name="csrf-token"]'),token:null!=t&&"function"==typeof t.getAttribute?t.getAttribute("content"):void 0}},update:function(e){var t;return t=this.get(),null!=t.token&&null!=e&&t.token!==e?t.node.setAttribute("content",e):void 0}},i=function(){var e,t,n,r,i,o;t=function(e){return(new DOMParser).parseFromString(e,"text/html")},e=function(e){var t;return t=document.implementation.createHTMLDocument(""),t.documentElement.innerHTML=e,t},n=function(e){var t;return t=document.implementation.createHTMLDocument(""),t.open("replace"),t.write(e),t.close(),t};try{if(window.DOMParser)return i=t("<html><body><p>test"),t}catch(a){return r=a,i=e("<html><body><p>test"),e}finally{if(1!==(null!=i&&null!=(o=i.body)?o.childNodes.length:void 0))return n}},n=function(){function e(t){return this.original=null!=t?t:document.location.href,this.original.constructor===e?this.original:void this._parse()}return e.prototype.withoutHash=function(){return this.href.replace(this.hash,"")},e.prototype.withoutHashForIE10compatibility=function(){return this.withoutHash()},e.prototype.hasNoHash=function(){return 0===this.hash.length},e.prototype._parse=function(){var e;return(null!=this.link?this.link:this.link=document.createElement("a")).href=this.original,e=this.link,this.href=e.href,this.protocol=e.protocol,this.host=e.host,this.hostname=e.hostname,this.port=e.port,this.pathname=e.pathname,this.search=e.search,this.hash=e.hash,this.origin=[this.protocol,"//",this.hostname].join(""),0!==this.port.length&&(this.origin+=":"+this.port),this.relative=[this.pathname,this.search,this.hash].join(""),this.absolute=this.href},e}(),r=function(e){function t(e){return this.link=e,this.link.constructor===t?this.link:(this.original=this.link.href,void t.__super__.constructor.apply(this,arguments))}return K(t,e),t.HTML_EXTENSIONS=["html"],t.allowExtensions=function(){var e,n,r,i;for(n=1<=arguments.length?Q.call(arguments,0):[],r=0,i=n.length;i>r;r++)e=n[r],t.HTML_EXTENSIONS.push(e);return t.HTML_EXTENSIONS},t.prototype.shouldIgnore=function(){return this._crossOrigin()||this._anchored()||this._nonHtml()||this._optOut()||this._target()},t.prototype._crossOrigin=function(){return this.origin!==(new n).origin},t.prototype._anchored=function(){var e;return(this.hash&&this.withoutHash())===(e=new n).withoutHash()||this.href===e.href+"#"},t.prototype._nonHtml=function(){return this.pathname.match(/\.[a-z]+$/g)&&!this.pathname.match(new RegExp("\\.(?:"+t.HTML_EXTENSIONS.join("|")+")?$","g"))},t.prototype._optOut=function(){var e,t;for(t=this.link;!e&&t!==document;)e=null!=t.getAttribute("data-no-turbolink"),t=t.parentNode;return e},t.prototype._target=function(){return 0!==this.link.target.length},t}(n),t=function(){function e(e){this.event=e,this.event.defaultPrevented||(this._extractLink(),this._validForTurbolinks()&&(A()||U(this.link.href),this.event.preventDefault()))}return e.installHandlerLast=function(t){return t.defaultPrevented?void 0:(document.removeEventListener("click",e.handle,!1),document.addEventListener("click",e.handle,!1))},e.handle=function(t){return new e(t)},e.prototype._extractLink=function(){var e;for(e=this.event.target;e.parentNode&&"A"!==e.nodeName;)e=e.parentNode;return"A"===e.nodeName&&0!==e.href.length?this.link=new r(e):void 0},e.prototype._validForTurbolinks=function(){return null!=this.link&&!(this.link.shouldIgnore()||this._nonStandardClick())},e.prototype._nonStandardClick=function(){return this.event.which>1||this.event.metaKey||this.event.ctrlKey||this.event.shiftKey||this.event.altKey},e}(),u=function(e){return setTimeout(e,500)},k=function(){return document.addEventListener("DOMContentLoaded",function(){return X("page:change"),X("page:update")},!0)},N=function(){return"undefined"!=typeof jQuery?jQuery(document).on("ajaxSuccess",function(e,t){return jQuery.trim(t.responseText)?X("page:update"):void 0}):void 0},C=function(e){var t,r;return(null!=(r=e.state)?r.turbolinks:void 0)?(t=j[new n(e.state.url).absolute])?(c(),x(t)):U(e.target.location.href):void 0},E=function(){return R(),O(),h=i(),document.addEventListener("click",t.installHandlerLast,!0),u(function(){return window.addEventListener("popstate",C,!1)})},T=void 0!==window.history.state||navigator.userAgent.match(/Firefox\/2[6|7]/),s=window.history&&window.history.pushState&&window.history.replaceState&&T,o=!navigator.userAgent.match(/CriOS\//),I="GET"===(G=L("request_method"))||""===G,l=s&&o&&I,a=document.addEventListener&&document.createEvent,a&&(k(),N()),l?(U=b,E()):U=function(e){return document.location.href=e},this.Turbolinks={visit:U,pagesCached:D,enableTransitionCache:g,allowLinkExtensions:r.allowExtensions,supported:l}}.call(this),function(){}.call(this);
\ No newline at end of file diff --git a/actionpack/test/fixtures/公共/gzip/application-a71b3024f80aea3181c09774ca17e712.js.gz b/actionpack/test/fixtures/公共/gzip/application-a71b3024f80aea3181c09774ca17e712.js.gz Binary files differnew file mode 100644 index 0000000000..f62c656dc8 --- /dev/null +++ b/actionpack/test/fixtures/公共/gzip/application-a71b3024f80aea3181c09774ca17e712.js.gz diff --git a/actionpack/test/fixtures/公共/gzip/foo.zoo b/actionpack/test/fixtures/公共/gzip/foo.zoo new file mode 100644 index 0000000000..1826a7660e --- /dev/null +++ b/actionpack/test/fixtures/公共/gzip/foo.zoo @@ -0,0 +1,4 @@ +!function(e,t){"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){function n(e){var t=e.length,n=it.type(e);return"function"===n||it.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e}function r(e,t,n){if(it.isFunction(t))return it.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return it.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(ft.test(t))return it.filter(t,e,n);t=it.filter(t,e)}return it.grep(e,function(e){return it.inArray(e,t)>=0!==n})}function i(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function o(e){var t=xt[e]={};return it.each(e.match(bt)||[],function(e,n){t[n]=!0}),t}function a(){ht.addEventListener?(ht.removeEventListener("DOMContentLoaded",s,!1),e.removeEventListener("load",s,!1)):(ht.detachEvent("onreadystatechange",s),e.detachEvent("onload",s))}function s(){(ht.addEventListener||"load"===event.type||"complete"===ht.readyState)&&(a(),it.ready())}function l(e,t,n){if(void 0===n&&1===e.nodeType){var r="data-"+t.replace(Ct,"-$1").toLowerCase();if(n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:kt.test(n)?it.parseJSON(n):n}catch(i){}it.data(e,t,n)}else n=void 0}return n}function u(e){var t;for(t in e)if(("data"!==t||!it.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function c(e,t,n,r){if(it.acceptData(e)){var i,o,a=it.expando,s=e.nodeType,l=s?it.cache:e,u=s?e[a]:e[a]&&a;if(u&&l[u]&&(r||l[u].data)||void 0!==n||"string"!=typeof t)return u||(u=s?e[a]=G.pop()||it.guid++:a),l[u]||(l[u]=s?{}:{toJSON:it.noop}),("object"==typeof t||"function"==typeof t)&&(r?l[u]=it.extend(l[u],t):l[u].data=it.extend(l[u].data,t)),o=l[u],r||(o.data||(o.data={}),o=o.data),void 0!==n&&(o[it.camelCase(t)]=n),"string"==typeof t?(i=o[t],null==i&&(i=o[it.camelCase(t)])):i=o,i}}function d(e,t,n){if(it.acceptData(e)){var r,i,o=e.nodeType,a=o?it.cache:e,s=o?e[it.expando]:it.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){it.isArray(t)?t=t.concat(it.map(t,it.camelCase)):t in r?t=[t]:(t=it.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;for(;i--;)delete r[t[i]];if(n?!u(r):!it.isEmptyObject(r))return}(n||(delete a[s].data,u(a[s])))&&(o?it.cleanData([e],!0):nt.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}function f(){return!0}function p(){return!1}function h(){try{return ht.activeElement}catch(e){}}function m(e){var t=Mt.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function g(e,t){var n,r,i=0,o=typeof e.getElementsByTagName!==Et?e.getElementsByTagName(t||"*"):typeof e.querySelectorAll!==Et?e.querySelectorAll(t||"*"):void 0;if(!o)for(o=[],n=e.childNodes||e;null!=(r=n[i]);i++)!t||it.nodeName(r,t)?o.push(r):it.merge(o,g(r,t));return void 0===t||t&&it.nodeName(e,t)?it.merge([e],o):o}function v(e){Dt.test(e.type)&&(e.defaultChecked=e.checked)}function y(e,t){return it.nodeName(e,"table")&&it.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function b(e){return e.type=(null!==it.find.attr(e,"type"))+"/"+e.type,e}function x(e){var t=Vt.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function w(e,t){for(var n,r=0;null!=(n=e[r]);r++)it._data(n,"globalEval",!t||it._data(t[r],"globalEval"))}function T(e,t){if(1===t.nodeType&&it.hasData(e)){var n,r,i,o=it._data(e),a=it._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)it.event.add(t,n,s[n][r])}a.data&&(a.data=it.extend({},a.data))}}function E(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!nt.noCloneEvent&&t[it.expando]){i=it._data(t);for(r in i.events)it.removeEvent(t,r,i.handle);t.removeAttribute(it.expando)}"script"===n&&t.text!==e.text?(b(t).text=e.text,x(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),nt.html5Clone&&e.innerHTML&&!it.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Dt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}function k(t,n){var r,i=it(n.createElement(t)).appendTo(n.body),o=e.getDefaultComputedStyle&&(r=e.getDefaultComputedStyle(i[0]))?r.display:it.css(i[0],"display");return i.detach(),o}function C(e){var t=ht,n=Zt[e];return n||(n=k(e,t),"none"!==n&&n||(Qt=(Qt||it("<iframe frameborder='0' width='0' height='0'/>")).appendTo(t.documentElement),t=(Qt[0].contentWindow||Qt[0].contentDocument).document,t.write(),t.close(),n=k(e,t),Qt.detach()),Zt[e]=n),n}function N(e,t){return{get:function(){var n=e();if(null!=n)return n?void delete this.get:(this.get=t).apply(this,arguments)}}}function S(e,t){if(t in e)return t;for(var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=pn.length;i--;)if(t=pn[i]+n,t in e)return t;return r}function j(e,t){for(var n,r,i,o=[],a=0,s=e.length;s>a;a++)r=e[a],r.style&&(o[a]=it._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&jt(r)&&(o[a]=it._data(r,"olddisplay",C(r.nodeName)))):(i=jt(r),(n&&"none"!==n||!i)&&it._data(r,"olddisplay",i?n:it.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}function A(e,t,n){var r=un.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function D(e,t,n,r,i){for(var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;4>o;o+=2)"margin"===n&&(a+=it.css(e,n+St[o],!0,i)),r?("content"===n&&(a-=it.css(e,"padding"+St[o],!0,i)),"margin"!==n&&(a-=it.css(e,"border"+St[o]+"Width",!0,i))):(a+=it.css(e,"padding"+St[o],!0,i),"padding"!==n&&(a+=it.css(e,"border"+St[o]+"Width",!0,i)));return a}function L(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=en(e),a=nt.boxSizing&&"border-box"===it.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=tn(e,t,o),(0>i||null==i)&&(i=e.style[t]),rn.test(i))return i;r=a&&(nt.boxSizingReliable()||i===e.style[t]),i=parseFloat(i)||0}return i+D(e,t,n||(a?"border":"content"),r,o)+"px"}function H(e,t,n,r,i){return new H.prototype.init(e,t,n,r,i)}function _(){return setTimeout(function(){hn=void 0}),hn=it.now()}function q(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=St[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}function F(e,t,n){for(var r,i=(xn[t]||[]).concat(xn["*"]),o=0,a=i.length;a>o;o++)if(r=i[o].call(n,t,e))return r}function M(e,t,n){var r,i,o,a,s,l,u,c,d=this,f={},p=e.style,h=e.nodeType&&jt(e),m=it._data(e,"fxshow");n.queue||(s=it._queueHooks(e,"fx"),null==s.unqueued&&(s.unqueued=0,l=s.empty.fire,s.empty.fire=function(){s.unqueued||l()}),s.unqueued++,d.always(function(){d.always(function(){s.unqueued--,it.queue(e,"fx").length||s.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],u=it.css(e,"display"),c="none"===u?it._data(e,"olddisplay")||C(e.nodeName):u,"inline"===c&&"none"===it.css(e,"float")&&(nt.inlineBlockNeedsLayout&&"inline"!==C(e.nodeName)?p.zoom=1:p.display="inline-block")),n.overflow&&(p.overflow="hidden",nt.shrinkWrapBlocks()||d.always(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],gn.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(h?"hide":"show")){if("show"!==i||!m||void 0===m[r])continue;h=!0}f[r]=m&&m[r]||it.style(e,r)}else u=void 0;if(it.isEmptyObject(f))"inline"===("none"===u?C(e.nodeName):u)&&(p.display=u);else{m?"hidden"in m&&(h=m.hidden):m=it._data(e,"fxshow",{}),o&&(m.hidden=!h),h?it(e).show():d.done(function(){it(e).hide()}),d.done(function(){var t;it._removeData(e,"fxshow");for(t in f)it.style(e,t,f[t])});for(r in f)a=F(h?m[r]:0,r,d),r in m||(m[r]=a.start,h&&(a.end=a.start,a.start="width"===r||"height"===r?1:0))}}function O(e,t){var n,r,i,o,a;for(n in e)if(r=it.camelCase(n),i=t[r],o=e[n],it.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=it.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}function R(e,t,n){var r,i,o=0,a=bn.length,s=it.Deferred().always(function(){delete l.elem}),l=function(){if(i)return!1;for(var t=hn||_(),n=Math.max(0,u.startTime+u.duration-t),r=n/u.duration||0,o=1-r,a=0,l=u.tweens.length;l>a;a++)u.tweens[a].run(o);return s.notifyWith(e,[u,o,n]),1>o&&l?n:(s.resolveWith(e,[u]),!1)},u=s.promise({elem:e,props:it.extend({},t),opts:it.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:hn||_(),duration:n.duration,tweens:[],createTween:function(t,n){var r=it.Tween(e,u.opts,t,n,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(r),r},stop:function(t){var n=0,r=t?u.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)u.tweens[n].run(1);return t?s.resolveWith(e,[u,t]):s.rejectWith(e,[u,t]),this}}),c=u.props;for(O(c,u.opts.specialEasing);a>o;o++)if(r=bn[o].call(u,e,c,u.opts))return r;return it.map(c,F,u),it.isFunction(u.opts.start)&&u.opts.start.call(e,u),it.fx.timer(it.extend(l,{elem:e,anim:u,queue:u.opts.queue})),u.progress(u.opts.progress).done(u.opts.done,u.opts.complete).fail(u.opts.fail).always(u.opts.always)}function B(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(bt)||[];if(it.isFunction(n))for(;r=o[i++];)"+"===r.charAt(0)?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function P(e,t,n,r){function i(s){var l;return o[s]=!0,it.each(e[s]||[],function(e,s){var u=s(t,n,r);return"string"!=typeof u||a||o[u]?a?!(l=u):void 0:(t.dataTypes.unshift(u),i(u),!1)}),l}var o={},a=e===zn;return i(t.dataTypes[0])||!o["*"]&&i("*")}function I(e,t){var n,r,i=it.ajaxSettings.flatOptions||{};for(r in t)void 0!==t[r]&&((i[r]?e:n||(n={}))[r]=t[r]);return n&&it.extend(!0,e,n),e}function W(e,t,n){for(var r,i,o,a,s=e.contents,l=e.dataTypes;"*"===l[0];)l.shift(),void 0===i&&(i=e.mimeType||t.getResponseHeader("Content-Type"));if(i)for(a in s)if(s[a]&&s[a].test(i)){l.unshift(a);break}if(l[0]in n)o=l[0];else{for(a in n){if(!l[0]||e.converters[a+" "+l[0]]){o=a;break}r||(r=a)}o=o||r}return o?(o!==l[0]&&l.unshift(o),n[o]):void 0}function $(e,t,n,r){var i,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(a=u[l+" "+o]||u["* "+o],!a)for(i in u)if(s=i.split(" "),s[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){a===!0?a=u[i]:u[i]!==!0&&(o=s[0],c.unshift(s[1]));break}if(a!==!0)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(d){return{state:"parsererror",error:a?d:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}function z(e,t,n,r){var i;if(it.isArray(t))it.each(t,function(t,i){n||Gn.test(e)?r(e,i):z(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==it.type(t))r(e,t);else for(i in t)z(e+"["+i+"]",t[i],n,r)}function X(){try{return new e.XMLHttpRequest}catch(t){}}function U(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}function V(e){return it.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}var G=[],Y=G.slice,J=G.concat,K=G.push,Q=G.indexOf,Z={},et=Z.toString,tt=Z.hasOwnProperty,nt={},rt="1.11.1",it=function(e,t){return new it.fn.init(e,t)},ot=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,at=/^-ms-/,st=/-([\da-z])/gi,lt=function(e,t){return t.toUpperCase()};it.fn=it.prototype={jquery:rt,constructor:it,selector:"",length:0,toArray:function(){return Y.call(this)},get:function(e){return null!=e?0>e?this[e+this.length]:this[e]:Y.call(this)},pushStack:function(e){var t=it.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return it.each(this,e,t)},map:function(e){return this.pushStack(it.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(Y.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:K,sort:G.sort,splice:G.splice},it.extend=it.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,l=arguments.length,u=!1;for("boolean"==typeof a&&(u=a,a=arguments[s]||{},s++),"object"==typeof a||it.isFunction(a)||(a={}),s===l&&(a=this,s--);l>s;s++)if(null!=(i=arguments[s]))for(r in i)e=a[r],n=i[r],a!==n&&(u&&n&&(it.isPlainObject(n)||(t=it.isArray(n)))?(t?(t=!1,o=e&&it.isArray(e)?e:[]):o=e&&it.isPlainObject(e)?e:{},a[r]=it.extend(u,o,n)):void 0!==n&&(a[r]=n));return a},it.extend({expando:"jQuery"+(rt+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isFunction:function(e){return"function"===it.type(e)},isArray:Array.isArray||function(e){return"array"===it.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!it.isArray(e)&&e-parseFloat(e)>=0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},isPlainObject:function(e){var t;if(!e||"object"!==it.type(e)||e.nodeType||it.isWindow(e))return!1;try{if(e.constructor&&!tt.call(e,"constructor")&&!tt.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}if(nt.ownLast)for(t in e)return tt.call(e,t);for(t in e);return void 0===t||tt.call(e,t)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?Z[et.call(e)]||"object":typeof e},globalEval:function(t){t&&it.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(at,"ms-").replace(st,lt)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,r){var i,o=0,a=e.length,s=n(e);if(r){if(s)for(;a>o&&(i=t.apply(e[o],r),i!==!1);o++);else for(o in e)if(i=t.apply(e[o],r),i===!1)break}else if(s)for(;a>o&&(i=t.call(e[o],o,e[o]),i!==!1);o++);else for(o in e)if(i=t.call(e[o],o,e[o]),i===!1)break;return e},trim:function(e){return null==e?"":(e+"").replace(ot,"")},makeArray:function(e,t){var r=t||[];return null!=e&&(n(Object(e))?it.merge(r,"string"==typeof e?[e]:e):K.call(r,e)),r},inArray:function(e,t,n){var r;if(t){if(Q)return Q.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;n>r;)e[i++]=t[r++];if(n!==n)for(;void 0!==t[r];)e[i++]=t[r++];return e.length=i,e},grep:function(e,t,n){for(var r,i=[],o=0,a=e.length,s=!n;a>o;o++)r=!t(e[o],o),r!==s&&i.push(e[o]);return i},map:function(e,t,r){var i,o=0,a=e.length,s=n(e),l=[];if(s)for(;a>o;o++)i=t(e[o],o,r),null!=i&&l.push(i);else for(o in e)i=t(e[o],o,r),null!=i&&l.push(i);return J.apply([],l)},guid:1,proxy:function(e,t){var n,r,i;return"string"==typeof t&&(i=e[t],t=e,e=i),it.isFunction(e)?(n=Y.call(arguments,2),r=function(){return e.apply(t||this,n.concat(Y.call(arguments)))},r.guid=e.guid=e.guid||it.guid++,r):void 0},now:function(){return+new Date},support:nt}),it.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){Z["[object "+t+"]"]=t.toLowerCase()});var ut=function(e){function t(e,t,n,r){var i,o,a,s,l,u,d,p,h,m;if((t?t.ownerDocument||t:P)!==H&&L(t),t=t||H,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(q&&!r){if(i=yt.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&R(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return Z.apply(n,t.getElementsByTagName(e)),n;if((a=i[3])&&w.getElementsByClassName&&t.getElementsByClassName)return Z.apply(n,t.getElementsByClassName(a)),n}if(w.qsa&&(!F||!F.test(e))){if(p=d=B,h=t,m=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){for(u=C(e),(d=t.getAttribute("id"))?p=d.replace(xt,"\\$&"):t.setAttribute("id",p),p="[id='"+p+"'] ",l=u.length;l--;)u[l]=p+f(u[l]);h=bt.test(e)&&c(t.parentNode)||t,m=u.join(",")}if(m)try{return Z.apply(n,h.querySelectorAll(m)),n}catch(g){}finally{d||t.removeAttribute("id")}}}return S(e.replace(lt,"$1"),t,n,r)}function n(){function e(n,r){return t.push(n+" ")>T.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[B]=!0,e}function i(e){var t=H.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=e.length;r--;)T.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||G)-(~e.sourceIndex||G);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function l(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function u(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function c(e){return e&&typeof e.getElementsByTagName!==V&&e}function d(){}function f(e){for(var t=0,n=e.length,r="";n>t;t++)r+=e[t].value;return r}function p(e,t,n){var r=t.dir,i=n&&"parentNode"===r,o=W++;return t.first?function(t,n,o){for(;t=t[r];)if(1===t.nodeType||i)return e(t,n,o)}:function(t,n,a){var s,l,u=[I,o];if(a){for(;t=t[r];)if((1===t.nodeType||i)&&e(t,n,a))return!0}else for(;t=t[r];)if(1===t.nodeType||i){if(l=t[B]||(t[B]={}),(s=l[r])&&s[0]===I&&s[1]===o)return u[2]=s[2];if(l[r]=u,u[2]=e(t,n,a))return!0}}}function h(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function m(e,n,r){for(var i=0,o=n.length;o>i;i++)t(e,n[i],r);return r}function g(e,t,n,r,i){for(var o,a=[],s=0,l=e.length,u=null!=t;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function v(e,t,n,i,o,a){return i&&!i[B]&&(i=v(i)),o&&!o[B]&&(o=v(o,a)),r(function(r,a,s,l){var u,c,d,f=[],p=[],h=a.length,v=r||m(t||"*",s.nodeType?[s]:s,[]),y=!e||!r&&t?v:g(v,f,e,s,l),b=n?o||(r?e:h||i)?[]:a:y;if(n&&n(y,b,s,l),i)for(u=g(b,p),i(u,[],s,l),c=u.length;c--;)(d=u[c])&&(b[p[c]]=!(y[p[c]]=d));if(r){if(o||e){if(o){for(u=[],c=b.length;c--;)(d=b[c])&&u.push(y[c]=d);o(null,b=[],u,l)}for(c=b.length;c--;)(d=b[c])&&(u=o?tt.call(r,d):f[c])>-1&&(r[u]=!(a[u]=d))}}else b=g(b===a?b.splice(h,b.length):b),o?o(null,a,b,l):Z.apply(a,b)})}function y(e){for(var t,n,r,i=e.length,o=T.relative[e[0].type],a=o||T.relative[" "],s=o?1:0,l=p(function(e){return e===t},a,!0),u=p(function(e){return tt.call(t,e)>-1},a,!0),c=[function(e,n,r){return!o&&(r||n!==j)||((t=n).nodeType?l(e,n,r):u(e,n,r))}];i>s;s++)if(n=T.relative[e[s].type])c=[p(h(c),n)];else{if(n=T.filter[e[s].type].apply(null,e[s].matches),n[B]){for(r=++s;i>r&&!T.relative[e[r].type];r++);return v(s>1&&h(c),s>1&&f(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(lt,"$1"),n,r>s&&y(e.slice(s,r)),i>r&&y(e=e.slice(r)),i>r&&f(e))}c.push(n)}return h(c)}function b(e,n){var i=n.length>0,o=e.length>0,a=function(r,a,s,l,u){var c,d,f,p=0,h="0",m=r&&[],v=[],y=j,b=r||o&&T.find.TAG("*",u),x=I+=null==y?1:Math.random()||.1,w=b.length;for(u&&(j=a!==H&&a);h!==w&&null!=(c=b[h]);h++){if(o&&c){for(d=0;f=e[d++];)if(f(c,a,s)){l.push(c);break}u&&(I=x)}i&&((c=!f&&c)&&p--,r&&m.push(c))}if(p+=h,i&&h!==p){for(d=0;f=n[d++];)f(m,v,a,s);if(r){if(p>0)for(;h--;)m[h]||v[h]||(v[h]=K.call(l));v=g(v)}Z.apply(l,v),u&&!r&&v.length>0&&p+n.length>1&&t.uniqueSort(l)}return u&&(I=x,j=y),m};return i?r(a):a}var x,w,T,E,k,C,N,S,j,A,D,L,H,_,q,F,M,O,R,B="sizzle"+-new Date,P=e.document,I=0,W=0,$=n(),z=n(),X=n(),U=function(e,t){return e===t&&(D=!0),0},V="undefined",G=1<<31,Y={}.hasOwnProperty,J=[],K=J.pop,Q=J.push,Z=J.push,et=J.slice,tt=J.indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(this[t]===e)return t;return-1},nt="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",rt="[\\x20\\t\\r\\n\\f]",it="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",ot=it.replace("w","w#"),at="\\["+rt+"*("+it+")(?:"+rt+"*([*^$|!~]?=)"+rt+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+ot+"))|)"+rt+"*\\]",st=":("+it+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+at+")*)|.*)\\)|)",lt=new RegExp("^"+rt+"+|((?:^|[^\\\\])(?:\\\\.)*)"+rt+"+$","g"),ut=new RegExp("^"+rt+"*,"+rt+"*"),ct=new RegExp("^"+rt+"*([>+~]|"+rt+")"+rt+"*"),dt=new RegExp("="+rt+"*([^\\]'\"]*?)"+rt+"*\\]","g"),ft=new RegExp(st),pt=new RegExp("^"+ot+"$"),ht={ID:new RegExp("^#("+it+")"),CLASS:new RegExp("^\\.("+it+")"),TAG:new RegExp("^("+it.replace("w","w*")+")"),ATTR:new RegExp("^"+at),PSEUDO:new RegExp("^"+st),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+rt+"*(even|odd|(([+-]|)(\\d*)n|)"+rt+"*(?:([+-]|)"+rt+"*(\\d+)|))"+rt+"*\\)|)","i"),bool:new RegExp("^(?:"+nt+")$","i"),needsContext:new RegExp("^"+rt+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+rt+"*((?:-\\d)?\\d*)"+rt+"*\\)|)(?=[^-]|$)","i")},mt=/^(?:input|select|textarea|button)$/i,gt=/^h\d$/i,vt=/^[^{]+\{\s*\[native \w/,yt=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,bt=/[+~]/,xt=/'|\\/g,wt=new RegExp("\\\\([\\da-f]{1,6}"+rt+"?|("+rt+")|.)","ig"),Tt=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)};try{Z.apply(J=et.call(P.childNodes),P.childNodes),J[P.childNodes.length].nodeType}catch(Et){Z={apply:J.length?function(e,t){Q.apply(e,et.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}w=t.support={},k=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},L=t.setDocument=function(e){var t,n=e?e.ownerDocument||e:P,r=n.defaultView;return n!==H&&9===n.nodeType&&n.documentElement?(H=n,_=n.documentElement,q=!k(n),r&&r!==r.top&&(r.addEventListener?r.addEventListener("unload",function(){L()},!1):r.attachEvent&&r.attachEvent("onunload",function(){L()})),w.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),w.getElementsByTagName=i(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),w.getElementsByClassName=vt.test(n.getElementsByClassName)&&i(function(e){return e.innerHTML="<div class='a'></div><div class='a i'></div>",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),w.getById=i(function(e){return _.appendChild(e).id=B,!n.getElementsByName||!n.getElementsByName(B).length}),w.getById?(T.find.ID=function(e,t){if(typeof t.getElementById!==V&&q){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},T.filter.ID=function(e){var t=e.replace(wt,Tt);return function(e){return e.getAttribute("id")===t}}):(delete T.find.ID,T.filter.ID=function(e){var t=e.replace(wt,Tt);return function(e){var n=typeof e.getAttributeNode!==V&&e.getAttributeNode("id");return n&&n.value===t}}),T.find.TAG=w.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==V?t.getElementsByTagName(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},T.find.CLASS=w.getElementsByClassName&&function(e,t){return typeof t.getElementsByClassName!==V&&q?t.getElementsByClassName(e):void 0},M=[],F=[],(w.qsa=vt.test(n.querySelectorAll))&&(i(function(e){e.innerHTML="<select msallowclip=''><option selected=''></option></select>",e.querySelectorAll("[msallowclip^='']").length&&F.push("[*^$]="+rt+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||F.push("\\["+rt+"*(?:value|"+nt+")"),e.querySelectorAll(":checked").length||F.push(":checked")}),i(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&F.push("name"+rt+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||F.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),F.push(",.*:")})),(w.matchesSelector=vt.test(O=_.matches||_.webkitMatchesSelector||_.mozMatchesSelector||_.oMatchesSelector||_.msMatchesSelector))&&i(function(e){w.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),M.push("!=",st)}),F=F.length&&new RegExp(F.join("|")),M=M.length&&new RegExp(M.join("|")),t=vt.test(_.compareDocumentPosition),R=t||vt.test(_.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},U=t?function(e,t){if(e===t)return D=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r?r:(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&r||!w.sortDetached&&t.compareDocumentPosition(e)===r?e===n||e.ownerDocument===P&&R(P,e)?-1:t===n||t.ownerDocument===P&&R(P,t)?1:A?tt.call(A,e)-tt.call(A,t):0:4&r?-1:1)}:function(e,t){if(e===t)return D=!0,0;var r,i=0,o=e.parentNode,s=t.parentNode,l=[e],u=[t];if(!o||!s)return e===n?-1:t===n?1:o?-1:s?1:A?tt.call(A,e)-tt.call(A,t):0;if(o===s)return a(e,t);for(r=e;r=r.parentNode;)l.unshift(r);for(r=t;r=r.parentNode;)u.unshift(r);for(;l[i]===u[i];)i++;return i?a(l[i],u[i]):l[i]===P?-1:u[i]===P?1:0},n):H},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==H&&L(e),n=n.replace(dt,"='$1']"),!(!w.matchesSelector||!q||M&&M.test(n)||F&&F.test(n)))try{var r=O.call(e,n);if(r||w.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return t(n,H,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==H&&L(e),R(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==H&&L(e);var n=T.attrHandle[t.toLowerCase()],r=n&&Y.call(T.attrHandle,t.toLowerCase())?n(e,t,!q):void 0;return void 0!==r?r:w.attributes||!q?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(D=!w.detectDuplicates,A=!w.sortStable&&e.slice(0),e.sort(U),D){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return A=null,e},E=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=E(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=E(t);return n},T=t.selectors={cacheLength:50,createPseudo:r,match:ht,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(wt,Tt),e[3]=(e[3]||e[4]||e[5]||"").replace(wt,Tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return ht.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&ft.test(n)&&(t=C(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(wt,Tt).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=$[e+" "];return t||(t=new RegExp("(^|"+rt+")"+e+"("+rt+"|$)"))&&$(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==V&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:n?(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o+" ").indexOf(r)>-1:"|="===n?o===r||o.slice(0,r.length+1)===r+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,d,f,p,h,m=o!==a?"nextSibling":"previousSibling",g=t.parentNode,v=s&&t.nodeName.toLowerCase(),y=!l&&!s;if(g){if(o){for(;m;){for(d=t;d=d[m];)if(s?d.nodeName.toLowerCase()===v:1===d.nodeType)return!1;h=m="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?g.firstChild:g.lastChild],a&&y){for(c=g[B]||(g[B]={}),u=c[e]||[],p=u[0]===I&&u[1],f=u[0]===I&&u[2],d=p&&g.childNodes[p];d=++p&&d&&d[m]||(f=p=0)||h.pop();)if(1===d.nodeType&&++f&&d===t){c[e]=[I,p,f];break}}else if(y&&(u=(t[B]||(t[B]={}))[e])&&u[0]===I)f=u[1];else for(;(d=++p&&d&&d[m]||(f=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==v:1!==d.nodeType)||!++f||(y&&((d[B]||(d[B]={}))[e]=[I,f]),d!==t)););return f-=i,f===r||f%r===0&&f/r>=0}}},PSEUDO:function(e,n){var i,o=T.pseudos[e]||T.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[B]?o(n):o.length>1?(i=[e,e,"",n],T.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=tt.call(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=N(e.replace(lt,"$1"));return i[B]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return function(t){return(t.textContent||t.innerText||E(t)).indexOf(e)>-1}}),lang:r(function(e){return pt.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(wt,Tt).toLowerCase(),function(t){var n;do if(n=q?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===_},focus:function(e){return e===H.activeElement&&(!H.hasFocus||H.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!T.pseudos.empty(e)},header:function(e){return gt.test(e.nodeName)},input:function(e){return mt.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:u(function(){return[0]}),last:u(function(e,t){return[t-1]}),eq:u(function(e,t,n){return[0>n?n+t:n]}),even:u(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:u(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:u(function(e,t,n){for(var r=0>n?n+t:n;--r>=0;)e.push(r);return e}),gt:u(function(e,t,n){for(var r=0>n?n+t:n;++r<t;)e.push(r);return e})}},T.pseudos.nth=T.pseudos.eq;for(x in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})T.pseudos[x]=s(x);for(x in{submit:!0,reset:!0})T.pseudos[x]=l(x);return d.prototype=T.filters=T.pseudos,T.setFilters=new d,C=t.tokenize=function(e,n){var r,i,o,a,s,l,u,c=z[e+" "];if(c)return n?0:c.slice(0);for(s=e,l=[],u=T.preFilter;s;){(!r||(i=ut.exec(s)))&&(i&&(s=s.slice(i[0].length)||s),l.push(o=[])),r=!1,(i=ct.exec(s))&&(r=i.shift(),o.push({value:r,type:i[0].replace(lt," ")}),s=s.slice(r.length));for(a in T.filter)!(i=ht[a].exec(s))||u[a]&&!(i=u[a](i))||(r=i.shift(),o.push({value:r,type:a,matches:i}),s=s.slice(r.length));if(!r)break}return n?s.length:s?t.error(e):z(e,l).slice(0)},N=t.compile=function(e,t){var n,r=[],i=[],o=X[e+" "];if(!o){for(t||(t=C(e)),n=t.length;n--;)o=y(t[n]),o[B]?r.push(o):i.push(o);o=X(e,b(i,r)),o.selector=e}return o},S=t.select=function(e,t,n,r){var i,o,a,s,l,u="function"==typeof e&&e,d=!r&&C(e=u.selector||e);if(n=n||[],1===d.length){if(o=d[0]=d[0].slice(0),o.length>2&&"ID"===(a=o[0]).type&&w.getById&&9===t.nodeType&&q&&T.relative[o[1].type]){if(t=(T.find.ID(a.matches[0].replace(wt,Tt),t)||[])[0],!t)return n;u&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=ht.needsContext.test(e)?0:o.length;i--&&(a=o[i],!T.relative[s=a.type]);)if((l=T.find[s])&&(r=l(a.matches[0].replace(wt,Tt),bt.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&f(o),!e)return Z.apply(n,r),n; +break}}return(u||N(e,d))(r,t,!q,n,bt.test(e)&&c(t.parentNode)||t),n},w.sortStable=B.split("").sort(U).join("")===B,w.detectDuplicates=!!D,L(),w.sortDetached=i(function(e){return 1&e.compareDocumentPosition(H.createElement("div"))}),i(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){return n?void 0:e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),w.attributes&&i(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){return n||"input"!==e.nodeName.toLowerCase()?void 0:e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(nt,function(e,t,n){var r;return n?void 0:e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);it.find=ut,it.expr=ut.selectors,it.expr[":"]=it.expr.pseudos,it.unique=ut.uniqueSort,it.text=ut.getText,it.isXMLDoc=ut.isXML,it.contains=ut.contains;var ct=it.expr.match.needsContext,dt=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,ft=/^.[^:#\[\.,]*$/;it.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?it.find.matchesSelector(r,e)?[r]:[]:it.find.matches(e,it.grep(t,function(e){return 1===e.nodeType}))},it.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(it(e).filter(function(){for(t=0;i>t;t++)if(it.contains(r[t],this))return!0}));for(t=0;i>t;t++)it.find(e,r[t],n);return n=this.pushStack(i>1?it.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},filter:function(e){return this.pushStack(r(this,e||[],!1))},not:function(e){return this.pushStack(r(this,e||[],!0))},is:function(e){return!!r(this,"string"==typeof e&&ct.test(e)?it(e):e||[],!1).length}});var pt,ht=e.document,mt=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,gt=it.fn.init=function(e,t){var n,r;if(!e)return this;if("string"==typeof e){if(n="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:mt.exec(e),!n||!n[1]&&t)return!t||t.jquery?(t||pt).find(e):this.constructor(t).find(e);if(n[1]){if(t=t instanceof it?t[0]:t,it.merge(this,it.parseHTML(n[1],t&&t.nodeType?t.ownerDocument||t:ht,!0)),dt.test(n[1])&&it.isPlainObject(t))for(n in t)it.isFunction(this[n])?this[n](t[n]):this.attr(n,t[n]);return this}if(r=ht.getElementById(n[2]),r&&r.parentNode){if(r.id!==n[2])return pt.find(e);this.length=1,this[0]=r}return this.context=ht,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):it.isFunction(e)?"undefined"!=typeof pt.ready?pt.ready(e):e(it):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),it.makeArray(e,this))};gt.prototype=it.fn,pt=it(ht);var vt=/^(?:parents|prev(?:Until|All))/,yt={children:!0,contents:!0,next:!0,prev:!0};it.extend({dir:function(e,t,n){for(var r=[],i=e[t];i&&9!==i.nodeType&&(void 0===n||1!==i.nodeType||!it(i).is(n));)1===i.nodeType&&r.push(i),i=i[t];return r},sibling:function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}}),it.fn.extend({has:function(e){var t,n=it(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(it.contains(this,n[t]))return!0})},closest:function(e,t){for(var n,r=0,i=this.length,o=[],a=ct.test(e)||"string"!=typeof e?it(e,t||this.context):0;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?a.index(n)>-1:1===n.nodeType&&it.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?it.unique(o):o)},index:function(e){return e?"string"==typeof e?it.inArray(this[0],it(e)):it.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(it.unique(it.merge(this.get(),it(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),it.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return it.dir(e,"parentNode")},parentsUntil:function(e,t,n){return it.dir(e,"parentNode",n)},next:function(e){return i(e,"nextSibling")},prev:function(e){return i(e,"previousSibling")},nextAll:function(e){return it.dir(e,"nextSibling")},prevAll:function(e){return it.dir(e,"previousSibling")},nextUntil:function(e,t,n){return it.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return it.dir(e,"previousSibling",n)},siblings:function(e){return it.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return it.sibling(e.firstChild)},contents:function(e){return it.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:it.merge([],e.childNodes)}},function(e,t){it.fn[e]=function(n,r){var i=it.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=it.filter(r,i)),this.length>1&&(yt[e]||(i=it.unique(i)),vt.test(e)&&(i=i.reverse())),this.pushStack(i)}});var bt=/\S+/g,xt={};it.Callbacks=function(e){e="string"==typeof e?xt[e]||o(e):it.extend({},e);var t,n,r,i,a,s,l=[],u=!e.once&&[],c=function(o){for(n=e.memory&&o,r=!0,a=s||0,s=0,i=l.length,t=!0;l&&i>a;a++)if(l[a].apply(o[0],o[1])===!1&&e.stopOnFalse){n=!1;break}t=!1,l&&(u?u.length&&c(u.shift()):n?l=[]:d.disable())},d={add:function(){if(l){var r=l.length;!function o(t){it.each(t,function(t,n){var r=it.type(n);"function"===r?e.unique&&d.has(n)||l.push(n):n&&n.length&&"string"!==r&&o(n)})}(arguments),t?i=l.length:n&&(s=r,c(n))}return this},remove:function(){return l&&it.each(arguments,function(e,n){for(var r;(r=it.inArray(n,l,r))>-1;)l.splice(r,1),t&&(i>=r&&i--,a>=r&&a--)}),this},has:function(e){return e?it.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],i=0,this},disable:function(){return l=u=n=void 0,this},disabled:function(){return!l},lock:function(){return u=void 0,n||d.disable(),this},locked:function(){return!u},fireWith:function(e,n){return!l||r&&!u||(n=n||[],n=[e,n.slice?n.slice():n],t?u.push(n):c(n)),this},fire:function(){return d.fireWith(this,arguments),this},fired:function(){return!!r}};return d},it.extend({Deferred:function(e){var t=[["resolve","done",it.Callbacks("once memory"),"resolved"],["reject","fail",it.Callbacks("once memory"),"rejected"],["notify","progress",it.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return it.Deferred(function(n){it.each(t,function(t,o){var a=it.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&it.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[o[0]+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?it.extend(e,r):r}},i={};return r.pipe=r.then,it.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=Y.call(arguments),a=o.length,s=1!==a||e&&it.isFunction(e.promise)?a:0,l=1===s?e:it.Deferred(),u=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?Y.call(arguments):i,r===t?l.notifyWith(n,r):--s||l.resolveWith(n,r)}};if(a>1)for(t=new Array(a),n=new Array(a),r=new Array(a);a>i;i++)o[i]&&it.isFunction(o[i].promise)?o[i].promise().done(u(i,r,o)).fail(l.reject).progress(u(i,n,t)):--s;return s||l.resolveWith(r,o),l.promise()}});var wt;it.fn.ready=function(e){return it.ready.promise().done(e),this},it.extend({isReady:!1,readyWait:1,holdReady:function(e){e?it.readyWait++:it.ready(!0)},ready:function(e){if(e===!0?!--it.readyWait:!it.isReady){if(!ht.body)return setTimeout(it.ready);it.isReady=!0,e!==!0&&--it.readyWait>0||(wt.resolveWith(ht,[it]),it.fn.triggerHandler&&(it(ht).triggerHandler("ready"),it(ht).off("ready")))}}}),it.ready.promise=function(t){if(!wt)if(wt=it.Deferred(),"complete"===ht.readyState)setTimeout(it.ready);else if(ht.addEventListener)ht.addEventListener("DOMContentLoaded",s,!1),e.addEventListener("load",s,!1);else{ht.attachEvent("onreadystatechange",s),e.attachEvent("onload",s);var n=!1;try{n=null==e.frameElement&&ht.documentElement}catch(r){}n&&n.doScroll&&!function i(){if(!it.isReady){try{n.doScroll("left")}catch(e){return setTimeout(i,50)}a(),it.ready()}}()}return wt.promise(t)};var Tt,Et="undefined";for(Tt in it(nt))break;nt.ownLast="0"!==Tt,nt.inlineBlockNeedsLayout=!1,it(function(){var e,t,n,r;n=ht.getElementsByTagName("body")[0],n&&n.style&&(t=ht.createElement("div"),r=ht.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),typeof t.style.zoom!==Et&&(t.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",nt.inlineBlockNeedsLayout=e=3===t.offsetWidth,e&&(n.style.zoom=1)),n.removeChild(r))}),function(){var e=ht.createElement("div");if(null==nt.deleteExpando){nt.deleteExpando=!0;try{delete e.test}catch(t){nt.deleteExpando=!1}}e=null}(),it.acceptData=function(e){var t=it.noData[(e.nodeName+" ").toLowerCase()],n=+e.nodeType||1;return 1!==n&&9!==n?!1:!t||t!==!0&&e.getAttribute("classid")===t};var kt=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Ct=/([A-Z])/g;it.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?it.cache[e[it.expando]]:e[it.expando],!!e&&!u(e)},data:function(e,t,n){return c(e,t,n)},removeData:function(e,t){return d(e,t)},_data:function(e,t,n){return c(e,t,n,!0)},_removeData:function(e,t){return d(e,t,!0)}}),it.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=it.data(o),1===o.nodeType&&!it._data(o,"parsedAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=it.camelCase(r.slice(5)),l(o,r,i[r])));it._data(o,"parsedAttrs",!0)}return i}return"object"==typeof e?this.each(function(){it.data(this,e)}):arguments.length>1?this.each(function(){it.data(this,e,t)}):o?l(o,e,it.data(o,e)):void 0},removeData:function(e){return this.each(function(){it.removeData(this,e)})}}),it.extend({queue:function(e,t,n){var r;return e?(t=(t||"fx")+"queue",r=it._data(e,t),n&&(!r||it.isArray(n)?r=it._data(e,t,it.makeArray(n)):r.push(n)),r||[]):void 0},dequeue:function(e,t){t=t||"fx";var n=it.queue(e,t),r=n.length,i=n.shift(),o=it._queueHooks(e,t),a=function(){it.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return it._data(e,n)||it._data(e,n,{empty:it.Callbacks("once memory").add(function(){it._removeData(e,t+"queue"),it._removeData(e,n)})})}}),it.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length<n?it.queue(this[0],e):void 0===t?this:this.each(function(){var n=it.queue(this,e,t);it._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&it.dequeue(this,e)})},dequeue:function(e){return this.each(function(){it.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=it.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};for("string"!=typeof e&&(t=e,e=void 0),e=e||"fx";a--;)n=it._data(o[a],e+"queueHooks"),n&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var Nt=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,St=["Top","Right","Bottom","Left"],jt=function(e,t){return e=t||e,"none"===it.css(e,"display")||!it.contains(e.ownerDocument,e)},At=it.access=function(e,t,n,r,i,o,a){var s=0,l=e.length,u=null==n;if("object"===it.type(n)){i=!0;for(s in n)it.access(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,it.isFunction(r)||(a=!0),u&&(a?(t.call(e,r),t=null):(u=t,t=function(e,t,n){return u.call(it(e),n)})),t))for(;l>s;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:u?t.call(e):l?t(e[0],n):o},Dt=/^(?:checkbox|radio)$/i;!function(){var e=ht.createElement("input"),t=ht.createElement("div"),n=ht.createDocumentFragment();if(t.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",nt.leadingWhitespace=3===t.firstChild.nodeType,nt.tbody=!t.getElementsByTagName("tbody").length,nt.htmlSerialize=!!t.getElementsByTagName("link").length,nt.html5Clone="<:nav></:nav>"!==ht.createElement("nav").cloneNode(!0).outerHTML,e.type="checkbox",e.checked=!0,n.appendChild(e),nt.appendChecked=e.checked,t.innerHTML="<textarea>x</textarea>",nt.noCloneChecked=!!t.cloneNode(!0).lastChild.defaultValue,n.appendChild(t),t.innerHTML="<input type='radio' checked='checked' name='t'/>",nt.checkClone=t.cloneNode(!0).cloneNode(!0).lastChild.checked,nt.noCloneEvent=!0,t.attachEvent&&(t.attachEvent("onclick",function(){nt.noCloneEvent=!1}),t.cloneNode(!0).click()),null==nt.deleteExpando){nt.deleteExpando=!0;try{delete t.test}catch(r){nt.deleteExpando=!1}}}(),function(){var t,n,r=ht.createElement("div");for(t in{submit:!0,change:!0,focusin:!0})n="on"+t,(nt[t+"Bubbles"]=n in e)||(r.setAttribute(n,"t"),nt[t+"Bubbles"]=r.attributes[n].expando===!1);r=null}();var Lt=/^(?:input|select|textarea)$/i,Ht=/^key/,_t=/^(?:mouse|pointer|contextmenu)|click/,qt=/^(?:focusinfocus|focusoutblur)$/,Ft=/^([^.]*)(?:\.(.+)|)$/;it.event={global:{},add:function(e,t,n,r,i){var o,a,s,l,u,c,d,f,p,h,m,g=it._data(e);if(g){for(n.handler&&(l=n,n=l.handler,i=l.selector),n.guid||(n.guid=it.guid++),(a=g.events)||(a=g.events={}),(c=g.handle)||(c=g.handle=function(e){return typeof it===Et||e&&it.event.triggered===e.type?void 0:it.event.dispatch.apply(c.elem,arguments)},c.elem=e),t=(t||"").match(bt)||[""],s=t.length;s--;)o=Ft.exec(t[s])||[],p=m=o[1],h=(o[2]||"").split(".").sort(),p&&(u=it.event.special[p]||{},p=(i?u.delegateType:u.bindType)||p,u=it.event.special[p]||{},d=it.extend({type:p,origType:m,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&it.expr.match.needsContext.test(i),namespace:h.join(".")},l),(f=a[p])||(f=a[p]=[],f.delegateCount=0,u.setup&&u.setup.call(e,r,h,c)!==!1||(e.addEventListener?e.addEventListener(p,c,!1):e.attachEvent&&e.attachEvent("on"+p,c))),u.add&&(u.add.call(e,d),d.handler.guid||(d.handler.guid=n.guid)),i?f.splice(f.delegateCount++,0,d):f.push(d),it.event.global[p]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,d,f,p,h,m,g=it.hasData(e)&&it._data(e);if(g&&(c=g.events)){for(t=(t||"").match(bt)||[""],u=t.length;u--;)if(s=Ft.exec(t[u])||[],p=m=s[1],h=(s[2]||"").split(".").sort(),p){for(d=it.event.special[p]||{},p=(r?d.delegateType:d.bindType)||p,f=c[p]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;o--;)a=f[o],!i&&m!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,d.remove&&d.remove.call(e,a));l&&!f.length&&(d.teardown&&d.teardown.call(e,h,g.handle)!==!1||it.removeEvent(e,p,g.handle),delete c[p])}else for(p in c)it.event.remove(e,p+t[u],n,r,!0);it.isEmptyObject(c)&&(delete g.handle,it._removeData(e,"events"))}},trigger:function(t,n,r,i){var o,a,s,l,u,c,d,f=[r||ht],p=tt.call(t,"type")?t.type:t,h=tt.call(t,"namespace")?t.namespace.split("."):[];if(s=c=r=r||ht,3!==r.nodeType&&8!==r.nodeType&&!qt.test(p+it.event.triggered)&&(p.indexOf(".")>=0&&(h=p.split("."),p=h.shift(),h.sort()),a=p.indexOf(":")<0&&"on"+p,t=t[it.expando]?t:new it.Event(p,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=h.join("."),t.namespace_re=t.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:it.makeArray(n,[t]),u=it.event.special[p]||{},i||!u.trigger||u.trigger.apply(r,n)!==!1)){if(!i&&!u.noBubble&&!it.isWindow(r)){for(l=u.delegateType||p,qt.test(l+p)||(s=s.parentNode);s;s=s.parentNode)f.push(s),c=s;c===(r.ownerDocument||ht)&&f.push(c.defaultView||c.parentWindow||e)}for(d=0;(s=f[d++])&&!t.isPropagationStopped();)t.type=d>1?l:u.bindType||p,o=(it._data(s,"events")||{})[t.type]&&it._data(s,"handle"),o&&o.apply(s,n),o=a&&s[a],o&&o.apply&&it.acceptData(s)&&(t.result=o.apply(s,n),t.result===!1&&t.preventDefault());if(t.type=p,!i&&!t.isDefaultPrevented()&&(!u._default||u._default.apply(f.pop(),n)===!1)&&it.acceptData(r)&&a&&r[p]&&!it.isWindow(r)){c=r[a],c&&(r[a]=null),it.event.triggered=p;try{r[p]()}catch(m){}it.event.triggered=void 0,c&&(r[a]=c)}return t.result}},dispatch:function(e){e=it.event.fix(e);var t,n,r,i,o,a=[],s=Y.call(arguments),l=(it._data(this,"events")||{})[e.type]||[],u=it.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!u.preDispatch||u.preDispatch.call(this,e)!==!1){for(a=it.event.handlers.call(this,e,l),t=0;(i=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=i.elem,o=0;(r=i.handlers[o++])&&!e.isImmediatePropagationStopped();)(!e.namespace_re||e.namespace_re.test(r.namespace))&&(e.handleObj=r,e.data=r.data,n=((it.event.special[r.origType]||{}).handle||r.handler).apply(i.elem,s),void 0!==n&&(e.result=n)===!1&&(e.preventDefault(),e.stopPropagation()));return u.postDispatch&&u.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,a=[],s=t.delegateCount,l=e.target;if(s&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(i=[],o=0;s>o;o++)r=t[o],n=r.selector+" ",void 0===i[n]&&(i[n]=r.needsContext?it(n,this).index(l)>=0:it.find(n,this,null,[l]).length),i[n]&&i.push(r);i.length&&a.push({elem:l,handlers:i})}return s<t.length&&a.push({elem:this,handlers:t.slice(s)}),a},fix:function(e){if(e[it.expando])return e;var t,n,r,i=e.type,o=e,a=this.fixHooks[i];for(a||(this.fixHooks[i]=a=_t.test(i)?this.mouseHooks:Ht.test(i)?this.keyHooks:{}),r=a.props?this.props.concat(a.props):this.props,e=new it.Event(o),t=r.length;t--;)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||ht),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,a.filter?a.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,t){var n,r,i,o=t.button,a=t.fromElement;return null==e.pageX&&null!=t.clientX&&(r=e.target.ownerDocument||ht,i=r.documentElement,n=r.body,e.pageX=t.clientX+(i&&i.scrollLeft||n&&n.scrollLeft||0)-(i&&i.clientLeft||n&&n.clientLeft||0),e.pageY=t.clientY+(i&&i.scrollTop||n&&n.scrollTop||0)-(i&&i.clientTop||n&&n.clientTop||0)),!e.relatedTarget&&a&&(e.relatedTarget=a===e.target?t.toElement:a),e.which||void 0===o||(e.which=1&o?1:2&o?3:4&o?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==h()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===h()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return it.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(e){return it.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=it.extend(new it.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?it.event.trigger(i,null,t):it.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},it.removeEvent=ht.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===Et&&(e[r]=null),e.detachEvent(r,n))},it.Event=function(e,t){return this instanceof it.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&e.returnValue===!1?f:p):this.type=e,t&&it.extend(this,t),this.timeStamp=e&&e.timeStamp||it.now(),void(this[it.expando]=!0)):new it.Event(e,t)},it.Event.prototype={isDefaultPrevented:p,isPropagationStopped:p,isImmediatePropagationStopped:p,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=f,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=f,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=f,e&&e.stopImmediatePropagation&&e.stopImmediatePropagation(),this.stopPropagation()}},it.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,t){it.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!it.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),nt.submitBubbles||(it.event.special.submit={setup:function(){return it.nodeName(this,"form")?!1:void it.event.add(this,"click._submit keypress._submit",function(e){var t=e.target,n=it.nodeName(t,"input")||it.nodeName(t,"button")?t.form:void 0;n&&!it._data(n,"submitBubbles")&&(it.event.add(n,"submit._submit",function(e){e._submit_bubble=!0}),it._data(n,"submitBubbles",!0))})},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&it.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return it.nodeName(this,"form")?!1:void it.event.remove(this,"._submit")}}),nt.changeBubbles||(it.event.special.change={setup:function(){return Lt.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(it.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),it.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),it.event.simulate("change",this,e,!0)})),!1):void it.event.add(this,"beforeactivate._change",function(e){var t=e.target;Lt.test(t.nodeName)&&!it._data(t,"changeBubbles")&&(it.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||it.event.simulate("change",this.parentNode,e,!0)}),it._data(t,"changeBubbles",!0))})},handle:function(e){var t=e.target;return this!==t||e.isSimulated||e.isTrigger||"radio"!==t.type&&"checkbox"!==t.type?e.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return it.event.remove(this,"._change"),!Lt.test(this.nodeName)}}),nt.focusinBubbles||it.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){it.event.simulate(t,e.target,it.event.fix(e),!0)};it.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=it._data(r,t);i||r.addEventListener(e,n,!0),it._data(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=it._data(r,t)-1;i?it._data(r,t,i):(r.removeEventListener(e,n,!0),it._removeData(r,t))}}}),it.fn.extend({on:function(e,t,n,r,i){var o,a;if("object"==typeof e){"string"!=typeof t&&(n=n||t,t=void 0);for(o in e)this.on(o,t,n,e[o],i);return this}if(null==n&&null==r?(r=t,n=t=void 0):null==r&&("string"==typeof t?(r=n,n=void 0):(r=n,n=t,t=void 0)),r===!1)r=p;else if(!r)return this;return 1===i&&(a=r,r=function(e){return it().off(e),a.apply(this,arguments)},r.guid=a.guid||(a.guid=it.guid++)),this.each(function(){it.event.add(this,e,r,n,t)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,it(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return(t===!1||"function"==typeof t)&&(n=t,t=void 0),n===!1&&(n=p),this.each(function(){it.event.remove(this,e,n,t)})},trigger:function(e,t){return this.each(function(){it.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?it.event.trigger(e,t,n,!0):void 0}});var Mt="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",Ot=/ jQuery\d+="(?:null|\d+)"/g,Rt=new RegExp("<(?:"+Mt+")[\\s/>]","i"),Bt=/^\s+/,Pt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,It=/<([\w:]+)/,Wt=/<tbody/i,$t=/<|&#?\w+;/,zt=/<(?:script|style|link)/i,Xt=/checked\s*(?:[^=]|=\s*.checked.)/i,Ut=/^$|\/(?:java|ecma)script/i,Vt=/^true\/(.*)/,Gt=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,Yt={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:nt.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},Jt=m(ht),Kt=Jt.appendChild(ht.createElement("div"));Yt.optgroup=Yt.option,Yt.tbody=Yt.tfoot=Yt.colgroup=Yt.caption=Yt.thead,Yt.th=Yt.td,it.extend({clone:function(e,t,n){var r,i,o,a,s,l=it.contains(e.ownerDocument,e);if(nt.html5Clone||it.isXMLDoc(e)||!Rt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Kt.innerHTML=e.outerHTML,Kt.removeChild(o=Kt.firstChild)),!(nt.noCloneEvent&&nt.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||it.isXMLDoc(e)))for(r=g(o),s=g(e),a=0;null!=(i=s[a]);++a)r[a]&&E(i,r[a]);if(t)if(n)for(s=s||g(e),r=r||g(o),a=0;null!=(i=s[a]);a++)T(i,r[a]);else T(e,o);return r=g(o,"script"),r.length>0&&w(r,!l&&g(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){for(var i,o,a,s,l,u,c,d=e.length,f=m(t),p=[],h=0;d>h;h++)if(o=e[h],o||0===o)if("object"===it.type(o))it.merge(p,o.nodeType?[o]:o);else if($t.test(o)){for(s=s||f.appendChild(t.createElement("div")),l=(It.exec(o)||["",""])[1].toLowerCase(),c=Yt[l]||Yt._default,s.innerHTML=c[1]+o.replace(Pt,"<$1></$2>")+c[2],i=c[0];i--;)s=s.lastChild;if(!nt.leadingWhitespace&&Bt.test(o)&&p.push(t.createTextNode(Bt.exec(o)[0])),!nt.tbody)for(o="table"!==l||Wt.test(o)?"<table>"!==c[1]||Wt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;i--;)it.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u);for(it.merge(p,s.childNodes),s.textContent="";s.firstChild;)s.removeChild(s.firstChild);s=f.lastChild}else p.push(t.createTextNode(o));for(s&&f.removeChild(s),nt.appendChecked||it.grep(g(p,"input"),v),h=0;o=p[h++];)if((!r||-1===it.inArray(o,r))&&(a=it.contains(o.ownerDocument,o),s=g(f.appendChild(o),"script"),a&&w(s),n))for(i=0;o=s[i++];)Ut.test(o.type||"")&&n.push(o);return s=null,f},cleanData:function(e,t){for(var n,r,i,o,a=0,s=it.expando,l=it.cache,u=nt.deleteExpando,c=it.event.special;null!=(n=e[a]);a++)if((t||it.acceptData(n))&&(i=n[s],o=i&&l[i])){if(o.events)for(r in o.events)c[r]?it.event.remove(n,r):it.removeEvent(n,r,o.handle);l[i]&&(delete l[i],u?delete n[s]:typeof n.removeAttribute!==Et?n.removeAttribute(s):n[s]=null,G.push(i))}}}),it.fn.extend({text:function(e){return At(this,function(e){return void 0===e?it.text(this):this.empty().append((this[0]&&this[0].ownerDocument||ht).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=y(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=y(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){for(var n,r=e?it.filter(e,this):this,i=0;null!=(n=r[i]);i++)t||1!==n.nodeType||it.cleanData(g(n)),n.parentNode&&(t&&it.contains(n.ownerDocument,n)&&w(g(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&it.cleanData(g(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&it.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return it.clone(this,e,t)})},html:function(e){return At(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e)return 1===t.nodeType?t.innerHTML.replace(Ot,""):void 0;if(!("string"!=typeof e||zt.test(e)||!nt.htmlSerialize&&Rt.test(e)||!nt.leadingWhitespace&&Bt.test(e)||Yt[(It.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(Pt,"<$1></$2>");try{for(;r>n;n++)t=this[n]||{},1===t.nodeType&&(it.cleanData(g(t,!1)),t.innerHTML=e);t=0}catch(i){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=arguments[0];return this.domManip(arguments,function(t){e=this.parentNode,it.cleanData(g(this)),e&&e.replaceChild(t,this)}),e&&(e.length||e.nodeType)?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t){e=J.apply([],e);var n,r,i,o,a,s,l=0,u=this.length,c=this,d=u-1,f=e[0],p=it.isFunction(f);if(p||u>1&&"string"==typeof f&&!nt.checkClone&&Xt.test(f))return this.each(function(n){var r=c.eq(n);p&&(e[0]=f.call(this,n,r.html())),r.domManip(e,t)});if(u&&(s=it.buildFragment(e,this[0].ownerDocument,!1,this),n=s.firstChild,1===s.childNodes.length&&(s=n),n)){for(o=it.map(g(s,"script"),b),i=o.length;u>l;l++)r=s,l!==d&&(r=it.clone(r,!0,!0),i&&it.merge(o,g(r,"script"))),t.call(this[l],r,l);if(i)for(a=o[o.length-1].ownerDocument,it.map(o,x),l=0;i>l;l++)r=o[l],Ut.test(r.type||"")&&!it._data(r,"globalEval")&&it.contains(a,r)&&(r.src?it._evalUrl&&it._evalUrl(r.src):it.globalEval((r.text||r.textContent||r.innerHTML||"").replace(Gt,"")));s=n=null}return this}}),it.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){it.fn[e]=function(e){for(var n,r=0,i=[],o=it(e),a=o.length-1;a>=r;r++)n=r===a?this:this.clone(!0),it(o[r])[t](n),K.apply(i,n.get());return this.pushStack(i)}});var Qt,Zt={};!function(){var e;nt.shrinkWrapBlocks=function(){if(null!=e)return e;e=!1;var t,n,r;return n=ht.getElementsByTagName("body")[0],n&&n.style?(t=ht.createElement("div"),r=ht.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),typeof t.style.zoom!==Et&&(t.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",t.appendChild(ht.createElement("div")).style.width="5px",e=3!==t.offsetWidth),n.removeChild(r),e):void 0}}();var en,tn,nn=/^margin/,rn=new RegExp("^("+Nt+")(?!px)[a-z%]+$","i"),on=/^(top|right|bottom|left)$/;e.getComputedStyle?(en=function(e){return e.ownerDocument.defaultView.getComputedStyle(e,null)},tn=function(e,t,n){var r,i,o,a,s=e.style;return n=n||en(e),a=n?n.getPropertyValue(t)||n[t]:void 0,n&&(""!==a||it.contains(e.ownerDocument,e)||(a=it.style(e,t)),rn.test(a)&&nn.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0===a?a:a+""}):ht.documentElement.currentStyle&&(en=function(e){return e.currentStyle},tn=function(e,t,n){var r,i,o,a,s=e.style;return n=n||en(e),a=n?n[t]:void 0,null==a&&s&&s[t]&&(a=s[t]),rn.test(a)&&!on.test(t)&&(r=s.left,i=e.runtimeStyle,o=i&&i.left,o&&(i.left=e.currentStyle.left),s.left="fontSize"===t?"1em":a,a=s.pixelLeft+"px",s.left=r,o&&(i.left=o)),void 0===a?a:a+""||"auto"}),function(){function t(){var t,n,r,i;n=ht.getElementsByTagName("body")[0],n&&n.style&&(t=ht.createElement("div"),r=ht.createElement("div"),r.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",n.appendChild(r).appendChild(t),t.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",o=a=!1,l=!0,e.getComputedStyle&&(o="1%"!==(e.getComputedStyle(t,null)||{}).top,a="4px"===(e.getComputedStyle(t,null)||{width:"4px"}).width,i=t.appendChild(ht.createElement("div")),i.style.cssText=t.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",t.style.width="1px",l=!parseFloat((e.getComputedStyle(i,null)||{}).marginRight)),t.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=t.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",s=0===i[0].offsetHeight,s&&(i[0].style.display="",i[1].style.display="none",s=0===i[0].offsetHeight),n.removeChild(r))}var n,r,i,o,a,s,l;n=ht.createElement("div"),n.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",i=n.getElementsByTagName("a")[0],r=i&&i.style,r&&(r.cssText="float:left;opacity:.5",nt.opacity="0.5"===r.opacity,nt.cssFloat=!!r.cssFloat,n.style.backgroundClip="content-box",n.cloneNode(!0).style.backgroundClip="",nt.clearCloneStyle="content-box"===n.style.backgroundClip,nt.boxSizing=""===r.boxSizing||""===r.MozBoxSizing||""===r.WebkitBoxSizing,it.extend(nt,{reliableHiddenOffsets:function(){return null==s&&t(),s +},boxSizingReliable:function(){return null==a&&t(),a},pixelPosition:function(){return null==o&&t(),o},reliableMarginRight:function(){return null==l&&t(),l}}))}(),it.swap=function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i};var an=/alpha\([^)]*\)/i,sn=/opacity\s*=\s*([^)]*)/,ln=/^(none|table(?!-c[ea]).+)/,un=new RegExp("^("+Nt+")(.*)$","i"),cn=new RegExp("^([+-])=("+Nt+")","i"),dn={position:"absolute",visibility:"hidden",display:"block"},fn={letterSpacing:"0",fontWeight:"400"},pn=["Webkit","O","Moz","ms"];it.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=tn(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":nt.cssFloat?"cssFloat":"styleFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=it.camelCase(t),l=e.style;if(t=it.cssProps[s]||(it.cssProps[s]=S(l,s)),a=it.cssHooks[t]||it.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];if(o=typeof n,"string"===o&&(i=cn.exec(n))&&(n=(i[1]+1)*i[2]+parseFloat(it.css(e,t)),o="number"),null!=n&&n===n&&("number"!==o||it.cssNumber[s]||(n+="px"),nt.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),!(a&&"set"in a&&void 0===(n=a.set(e,n,r)))))try{l[t]=n}catch(u){}}},css:function(e,t,n,r){var i,o,a,s=it.camelCase(t);return t=it.cssProps[s]||(it.cssProps[s]=S(e.style,s)),a=it.cssHooks[t]||it.cssHooks[s],a&&"get"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=tn(e,t,r)),"normal"===o&&t in fn&&(o=fn[t]),""===n||n?(i=parseFloat(o),n===!0||it.isNumeric(i)?i||0:o):o}}),it.each(["height","width"],function(e,t){it.cssHooks[t]={get:function(e,n,r){return n?ln.test(it.css(e,"display"))&&0===e.offsetWidth?it.swap(e,dn,function(){return L(e,t,r)}):L(e,t,r):void 0},set:function(e,n,r){var i=r&&en(e);return A(e,n,r?D(e,t,r,nt.boxSizing&&"border-box"===it.css(e,"boxSizing",!1,i),i):0)}}}),nt.opacity||(it.cssHooks.opacity={get:function(e,t){return sn.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=it.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===it.trim(o.replace(an,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=an.test(o)?o.replace(an,i):o+" "+i)}}),it.cssHooks.marginRight=N(nt.reliableMarginRight,function(e,t){return t?it.swap(e,{display:"inline-block"},tn,[e,"marginRight"]):void 0}),it.each({margin:"",padding:"",border:"Width"},function(e,t){it.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];4>r;r++)i[e+St[r]+t]=o[r]||o[r-2]||o[0];return i}},nn.test(e)||(it.cssHooks[e+t].set=A)}),it.fn.extend({css:function(e,t){return At(this,function(e,t,n){var r,i,o={},a=0;if(it.isArray(t)){for(r=en(e),i=t.length;i>a;a++)o[t[a]]=it.css(e,t[a],!1,r);return o}return void 0!==n?it.style(e,t,n):it.css(e,t)},e,t,arguments.length>1)},show:function(){return j(this,!0)},hide:function(){return j(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){jt(this)?it(this).show():it(this).hide()})}}),it.Tween=H,H.prototype={constructor:H,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(it.cssNumber[n]?"":"px")},cur:function(){var e=H.propHooks[this.prop];return e&&e.get?e.get(this):H.propHooks._default.get(this)},run:function(e){var t,n=H.propHooks[this.prop];return this.pos=t=this.options.duration?it.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):H.propHooks._default.set(this),this}},H.prototype.init.prototype=H.prototype,H.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=it.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){it.fx.step[e.prop]?it.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[it.cssProps[e.prop]]||it.cssHooks[e.prop])?it.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},H.propHooks.scrollTop=H.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},it.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},it.fx=H.prototype.init,it.fx.step={};var hn,mn,gn=/^(?:toggle|show|hide)$/,vn=new RegExp("^(?:([+-])=|)("+Nt+")([a-z%]*)$","i"),yn=/queueHooks$/,bn=[M],xn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=vn.exec(t),o=i&&i[3]||(it.cssNumber[e]?"":"px"),a=(it.cssNumber[e]||"px"!==o&&+r)&&vn.exec(it.css(n.elem,e)),s=1,l=20;if(a&&a[3]!==o){o=o||a[3],i=i||[],a=+r||1;do s=s||".5",a/=s,it.style(n.elem,e,a+o);while(s!==(s=n.cur()/r)&&1!==s&&--l)}return i&&(a=n.start=+a||+r||0,n.unit=o,n.end=i[1]?a+(i[1]+1)*i[2]:+i[2]),n}]};it.Animation=it.extend(R,{tweener:function(e,t){it.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");for(var n,r=0,i=e.length;i>r;r++)n=e[r],xn[n]=xn[n]||[],xn[n].unshift(t)},prefilter:function(e,t){t?bn.unshift(e):bn.push(e)}}),it.speed=function(e,t,n){var r=e&&"object"==typeof e?it.extend({},e):{complete:n||!n&&t||it.isFunction(e)&&e,duration:e,easing:n&&t||t&&!it.isFunction(t)&&t};return r.duration=it.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in it.fx.speeds?it.fx.speeds[r.duration]:it.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){it.isFunction(r.old)&&r.old.call(this),r.queue&&it.dequeue(this,r.queue)},r},it.fn.extend({fadeTo:function(e,t,n,r){return this.filter(jt).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=it.isEmptyObject(e),o=it.speed(t,n,r),a=function(){var t=R(this,it.extend({},e),o);(i||it._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=void 0),t&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=it.timers,a=it._data(this);if(i)a[i]&&a[i].stop&&r(a[i]);else for(i in a)a[i]&&a[i].stop&&yn.test(i)&&r(a[i]);for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));(t||!n)&&it.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=it._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=it.timers,a=r?r.length:0;for(n.finish=!0,it.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),it.each(["toggle","show","hide"],function(e,t){var n=it.fn[t];it.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(q(t,!0),e,r,i)}}),it.each({slideDown:q("show"),slideUp:q("hide"),slideToggle:q("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){it.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),it.timers=[],it.fx.tick=function(){var e,t=it.timers,n=0;for(hn=it.now();n<t.length;n++)e=t[n],e()||t[n]!==e||t.splice(n--,1);t.length||it.fx.stop(),hn=void 0},it.fx.timer=function(e){it.timers.push(e),e()?it.fx.start():it.timers.pop()},it.fx.interval=13,it.fx.start=function(){mn||(mn=setInterval(it.fx.tick,it.fx.interval))},it.fx.stop=function(){clearInterval(mn),mn=null},it.fx.speeds={slow:600,fast:200,_default:400},it.fn.delay=function(e,t){return e=it.fx?it.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},function(){var e,t,n,r,i;t=ht.createElement("div"),t.setAttribute("className","t"),t.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",r=t.getElementsByTagName("a")[0],n=ht.createElement("select"),i=n.appendChild(ht.createElement("option")),e=t.getElementsByTagName("input")[0],r.style.cssText="top:1px",nt.getSetAttribute="t"!==t.className,nt.style=/top/.test(r.getAttribute("style")),nt.hrefNormalized="/a"===r.getAttribute("href"),nt.checkOn=!!e.value,nt.optSelected=i.selected,nt.enctype=!!ht.createElement("form").enctype,n.disabled=!0,nt.optDisabled=!i.disabled,e=ht.createElement("input"),e.setAttribute("value",""),nt.input=""===e.getAttribute("value"),e.value="t",e.setAttribute("type","radio"),nt.radioValue="t"===e.value}();var wn=/\r/g;it.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=it.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,it(this).val()):e,null==i?i="":"number"==typeof i?i+="":it.isArray(i)&&(i=it.map(i,function(e){return null==e?"":e+""})),t=it.valHooks[this.type]||it.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return t=it.valHooks[i.type]||it.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(wn,""):null==n?"":n)}}}),it.extend({valHooks:{option:{get:function(e){var t=it.find.attr(e,"value");return null!=t?t:it.trim(it.text(e))}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(nt.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&it.nodeName(n.parentNode,"optgroup"))){if(t=it(n).val(),o)return t;a.push(t)}return a},set:function(e,t){for(var n,r,i=e.options,o=it.makeArray(t),a=i.length;a--;)if(r=i[a],it.inArray(it.valHooks.option.get(r),o)>=0)try{r.selected=n=!0}catch(s){r.scrollHeight}else r.selected=!1;return n||(e.selectedIndex=-1),i}}}}),it.each(["radio","checkbox"],function(){it.valHooks[this]={set:function(e,t){return it.isArray(t)?e.checked=it.inArray(it(e).val(),t)>=0:void 0}},nt.checkOn||(it.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Tn,En,kn=it.expr.attrHandle,Cn=/^(?:checked|selected)$/i,Nn=nt.getSetAttribute,Sn=nt.input;it.fn.extend({attr:function(e,t){return At(this,it.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){it.removeAttr(this,e)})}}),it.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(e&&3!==o&&8!==o&&2!==o)return typeof e.getAttribute===Et?it.prop(e,t,n):(1===o&&it.isXMLDoc(e)||(t=t.toLowerCase(),r=it.attrHooks[t]||(it.expr.match.bool.test(t)?En:Tn)),void 0===n?r&&"get"in r&&null!==(i=r.get(e,t))?i:(i=it.find.attr(e,t),null==i?void 0:i):null!==n?r&&"set"in r&&void 0!==(i=r.set(e,n,t))?i:(e.setAttribute(t,n+""),n):void it.removeAttr(e,t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(bt);if(o&&1===e.nodeType)for(;n=o[i++];)r=it.propFix[n]||n,it.expr.match.bool.test(n)?Sn&&Nn||!Cn.test(n)?e[r]=!1:e[it.camelCase("default-"+n)]=e[r]=!1:it.attr(e,n,""),e.removeAttribute(Nn?n:r)},attrHooks:{type:{set:function(e,t){if(!nt.radioValue&&"radio"===t&&it.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}}}),En={set:function(e,t,n){return t===!1?it.removeAttr(e,n):Sn&&Nn||!Cn.test(n)?e.setAttribute(!Nn&&it.propFix[n]||n,n):e[it.camelCase("default-"+n)]=e[n]=!0,n}},it.each(it.expr.match.bool.source.match(/\w+/g),function(e,t){var n=kn[t]||it.find.attr;kn[t]=Sn&&Nn||!Cn.test(t)?function(e,t,r){var i,o;return r||(o=kn[t],kn[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,kn[t]=o),i}:function(e,t,n){return n?void 0:e[it.camelCase("default-"+t)]?t.toLowerCase():null}}),Sn&&Nn||(it.attrHooks.value={set:function(e,t,n){return it.nodeName(e,"input")?void(e.defaultValue=t):Tn&&Tn.set(e,t,n)}}),Nn||(Tn={set:function(e,t,n){var r=e.getAttributeNode(n);return r||e.setAttributeNode(r=e.ownerDocument.createAttribute(n)),r.value=t+="","value"===n||t===e.getAttribute(n)?t:void 0}},kn.id=kn.name=kn.coords=function(e,t,n){var r;return n?void 0:(r=e.getAttributeNode(t))&&""!==r.value?r.value:null},it.valHooks.button={get:function(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:void 0},set:Tn.set},it.attrHooks.contenteditable={set:function(e,t,n){Tn.set(e,""===t?!1:t,n)}},it.each(["width","height"],function(e,t){it.attrHooks[t]={set:function(e,n){return""===n?(e.setAttribute(t,"auto"),n):void 0}}})),nt.style||(it.attrHooks.style={get:function(e){return e.style.cssText||void 0},set:function(e,t){return e.style.cssText=t+""}});var jn=/^(?:input|select|textarea|button|object)$/i,An=/^(?:a|area)$/i;it.fn.extend({prop:function(e,t){return At(this,it.prop,e,t,arguments.length>1)},removeProp:function(e){return e=it.propFix[e]||e,this.each(function(){try{this[e]=void 0,delete this[e]}catch(t){}})}}),it.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(e,t,n){var r,i,o,a=e.nodeType;if(e&&3!==a&&8!==a&&2!==a)return o=1!==a||!it.isXMLDoc(e),o&&(t=it.propFix[t]||t,i=it.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=it.find.attr(e,"tabindex");return t?parseInt(t,10):jn.test(e.nodeName)||An.test(e.nodeName)&&e.href?0:-1}}}}),nt.hrefNormalized||it.each(["href","src"],function(e,t){it.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),nt.optSelected||(it.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),it.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){it.propFix[this.toLowerCase()]=this}),nt.enctype||(it.propFix.enctype="encoding");var Dn=/[\t\r\n\f]/g;it.fn.extend({addClass:function(e){var t,n,r,i,o,a,s=0,l=this.length,u="string"==typeof e&&e;if(it.isFunction(e))return this.each(function(t){it(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(bt)||[];l>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(Dn," "):" ")){for(o=0;i=t[o++];)r.indexOf(" "+i+" ")<0&&(r+=i+" ");a=it.trim(r),n.className!==a&&(n.className=a)}return this},removeClass:function(e){var t,n,r,i,o,a,s=0,l=this.length,u=0===arguments.length||"string"==typeof e&&e;if(it.isFunction(e))return this.each(function(t){it(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(bt)||[];l>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(Dn," "):"")){for(o=0;i=t[o++];)for(;r.indexOf(" "+i+" ")>=0;)r=r.replace(" "+i+" "," ");a=e?it.trim(r):"",n.className!==a&&(n.className=a)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):this.each(it.isFunction(e)?function(n){it(this).toggleClass(e.call(this,n,this.className,t),t)}:function(){if("string"===n)for(var t,r=0,i=it(this),o=e.match(bt)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else(n===Et||"boolean"===n)&&(this.className&&it._data(this,"__className__",this.className),this.className=this.className||e===!1?"":it._data(this,"__className__")||"")})},hasClass:function(e){for(var t=" "+e+" ",n=0,r=this.length;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(Dn," ").indexOf(t)>=0)return!0;return!1}}),it.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 contextmenu".split(" "),function(e,t){it.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),it.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var Ln=it.now(),Hn=/\?/,_n=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;it.parseJSON=function(t){if(e.JSON&&e.JSON.parse)return e.JSON.parse(t+"");var n,r=null,i=it.trim(t+"");return i&&!it.trim(i.replace(_n,function(e,t,i,o){return n&&t&&(r=0),0===r?e:(n=i||t,r+=!o-!i,"")}))?Function("return "+i)():it.error("Invalid JSON: "+t)},it.parseXML=function(t){var n,r;if(!t||"string"!=typeof t)return null;try{e.DOMParser?(r=new DOMParser,n=r.parseFromString(t,"text/xml")):(n=new ActiveXObject("Microsoft.XMLDOM"),n.async="false",n.loadXML(t))}catch(i){n=void 0}return n&&n.documentElement&&!n.getElementsByTagName("parsererror").length||it.error("Invalid XML: "+t),n};var qn,Fn,Mn=/#.*$/,On=/([?&])_=[^&]*/,Rn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Bn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Pn=/^(?:GET|HEAD)$/,In=/^\/\//,Wn=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,$n={},zn={},Xn="*/".concat("*");try{Fn=location.href}catch(Un){Fn=ht.createElement("a"),Fn.href="",Fn=Fn.href}qn=Wn.exec(Fn.toLowerCase())||[],it.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Fn,type:"GET",isLocal:Bn.test(qn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Xn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":it.parseJSON,"text xml":it.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?I(I(e,it.ajaxSettings),t):I(it.ajaxSettings,e)},ajaxPrefilter:B($n),ajaxTransport:B(zn),ajax:function(e,t){function n(e,t,n,r){var i,c,v,y,x,T=t;2!==b&&(b=2,s&&clearTimeout(s),u=void 0,a=r||"",w.readyState=e>0?4:0,i=e>=200&&300>e||304===e,n&&(y=W(d,w,n)),y=$(d,y,w,i),i?(d.ifModified&&(x=w.getResponseHeader("Last-Modified"),x&&(it.lastModified[o]=x),x=w.getResponseHeader("etag"),x&&(it.etag[o]=x)),204===e||"HEAD"===d.type?T="nocontent":304===e?T="notmodified":(T=y.state,c=y.data,v=y.error,i=!v)):(v=T,(e||!T)&&(T="error",0>e&&(e=0))),w.status=e,w.statusText=(t||T)+"",i?h.resolveWith(f,[c,T,w]):h.rejectWith(f,[w,T,v]),w.statusCode(g),g=void 0,l&&p.trigger(i?"ajaxSuccess":"ajaxError",[w,d,i?c:v]),m.fireWith(f,[w,T]),l&&(p.trigger("ajaxComplete",[w,d]),--it.active||it.event.trigger("ajaxStop")))}"object"==typeof e&&(t=e,e=void 0),t=t||{};var r,i,o,a,s,l,u,c,d=it.ajaxSetup({},t),f=d.context||d,p=d.context&&(f.nodeType||f.jquery)?it(f):it.event,h=it.Deferred(),m=it.Callbacks("once memory"),g=d.statusCode||{},v={},y={},b=0,x="canceled",w={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!c)for(c={};t=Rn.exec(a);)c[t[1].toLowerCase()]=t[2];t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=y[n]=y[n]||e,v[e]=t),this},overrideMimeType:function(e){return b||(d.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>b)for(t in e)g[t]=[g[t],e[t]];else w.always(e[w.status]);return this},abort:function(e){var t=e||x;return u&&u.abort(t),n(0,t),this}};if(h.promise(w).complete=m.add,w.success=w.done,w.error=w.fail,d.url=((e||d.url||Fn)+"").replace(Mn,"").replace(In,qn[1]+"//"),d.type=t.method||t.type||d.method||d.type,d.dataTypes=it.trim(d.dataType||"*").toLowerCase().match(bt)||[""],null==d.crossDomain&&(r=Wn.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]===qn[1]&&r[2]===qn[2]&&(r[3]||("http:"===r[1]?"80":"443"))===(qn[3]||("http:"===qn[1]?"80":"443")))),d.data&&d.processData&&"string"!=typeof d.data&&(d.data=it.param(d.data,d.traditional)),P($n,d,t,w),2===b)return w;l=d.global,l&&0===it.active++&&it.event.trigger("ajaxStart"),d.type=d.type.toUpperCase(),d.hasContent=!Pn.test(d.type),o=d.url,d.hasContent||(d.data&&(o=d.url+=(Hn.test(o)?"&":"?")+d.data,delete d.data),d.cache===!1&&(d.url=On.test(o)?o.replace(On,"$1_="+Ln++):o+(Hn.test(o)?"&":"?")+"_="+Ln++)),d.ifModified&&(it.lastModified[o]&&w.setRequestHeader("If-Modified-Since",it.lastModified[o]),it.etag[o]&&w.setRequestHeader("If-None-Match",it.etag[o])),(d.data&&d.hasContent&&d.contentType!==!1||t.contentType)&&w.setRequestHeader("Content-Type",d.contentType),w.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+("*"!==d.dataTypes[0]?", "+Xn+"; q=0.01":""):d.accepts["*"]);for(i in d.headers)w.setRequestHeader(i,d.headers[i]);if(d.beforeSend&&(d.beforeSend.call(f,w,d)===!1||2===b))return w.abort();x="abort";for(i in{success:1,error:1,complete:1})w[i](d[i]);if(u=P(zn,d,t,w)){w.readyState=1,l&&p.trigger("ajaxSend",[w,d]),d.async&&d.timeout>0&&(s=setTimeout(function(){w.abort("timeout")},d.timeout));try{b=1,u.send(v,n)}catch(T){if(!(2>b))throw T;n(-1,T)}}else n(-1,"No Transport");return w},getJSON:function(e,t,n){return it.get(e,t,n,"json")},getScript:function(e,t){return it.get(e,void 0,t,"script")}}),it.each(["get","post"],function(e,t){it[t]=function(e,n,r,i){return it.isFunction(n)&&(i=i||r,r=n,n=void 0),it.ajax({url:e,type:t,dataType:i,data:n,success:r})}}),it.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){it.fn[t]=function(e){return this.on(t,e)}}),it._evalUrl=function(e){return it.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},it.fn.extend({wrapAll:function(e){if(it.isFunction(e))return this.each(function(t){it(this).wrapAll(e.call(this,t))});if(this[0]){var t=it(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return this.each(it.isFunction(e)?function(t){it(this).wrapInner(e.call(this,t))}:function(){var t=it(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=it.isFunction(e);return this.each(function(n){it(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){it.nodeName(this,"body")||it(this).replaceWith(this.childNodes)}).end()}}),it.expr.filters.hidden=function(e){return e.offsetWidth<=0&&e.offsetHeight<=0||!nt.reliableHiddenOffsets()&&"none"===(e.style&&e.style.display||it.css(e,"display"))},it.expr.filters.visible=function(e){return!it.expr.filters.hidden(e)};var Vn=/%20/g,Gn=/\[\]$/,Yn=/\r?\n/g,Jn=/^(?:submit|button|image|reset|file)$/i,Kn=/^(?:input|select|textarea|keygen)/i;it.param=function(e,t){var n,r=[],i=function(e,t){t=it.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=it.ajaxSettings&&it.ajaxSettings.traditional),it.isArray(e)||e.jquery&&!it.isPlainObject(e))it.each(e,function(){i(this.name,this.value)});else for(n in e)z(n,e[n],t,i);return r.join("&").replace(Vn,"+")},it.fn.extend({serialize:function(){return it.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=it.prop(this,"elements");return e?it.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!it(this).is(":disabled")&&Kn.test(this.nodeName)&&!Jn.test(e)&&(this.checked||!Dt.test(e))}).map(function(e,t){var n=it(this).val();return null==n?null:it.isArray(n)?it.map(n,function(e){return{name:t.name,value:e.replace(Yn,"\r\n")}}):{name:t.name,value:n.replace(Yn,"\r\n")}}).get()}}),it.ajaxSettings.xhr=void 0!==e.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&X()||U()}:X;var Qn=0,Zn={},er=it.ajaxSettings.xhr();e.ActiveXObject&&it(e).on("unload",function(){for(var e in Zn)Zn[e](void 0,!0)}),nt.cors=!!er&&"withCredentials"in er,er=nt.ajax=!!er,er&&it.ajaxTransport(function(e){if(!e.crossDomain||nt.cors){var t;return{send:function(n,r){var i,o=e.xhr(),a=++Qn;if(o.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(i in e.xhrFields)o[i]=e.xhrFields[i];e.mimeType&&o.overrideMimeType&&o.overrideMimeType(e.mimeType),e.crossDomain||n["X-Requested-With"]||(n["X-Requested-With"]="XMLHttpRequest");for(i in n)void 0!==n[i]&&o.setRequestHeader(i,n[i]+"");o.send(e.hasContent&&e.data||null),t=function(n,i){var s,l,u;if(t&&(i||4===o.readyState))if(delete Zn[a],t=void 0,o.onreadystatechange=it.noop,i)4!==o.readyState&&o.abort();else{u={},s=o.status,"string"==typeof o.responseText&&(u.text=o.responseText);try{l=o.statusText}catch(c){l=""}s||!e.isLocal||e.crossDomain?1223===s&&(s=204):s=u.text?200:404}u&&r(s,l,u,o.getAllResponseHeaders())},e.async?4===o.readyState?setTimeout(t):o.onreadystatechange=Zn[a]=t:t()},abort:function(){t&&t(void 0,!0)}}}}),it.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return it.globalEval(e),e}}}),it.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),it.ajaxTransport("script",function(e){if(e.crossDomain){var t,n=ht.head||it("head")[0]||ht.documentElement;return{send:function(r,i){t=ht.createElement("script"),t.async=!0,e.scriptCharset&&(t.charset=e.scriptCharset),t.src=e.url,t.onload=t.onreadystatechange=function(e,n){(n||!t.readyState||/loaded|complete/.test(t.readyState))&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),t=null,n||i(200,"success"))},n.insertBefore(t,n.firstChild)},abort:function(){t&&t.onload(void 0,!0)}}}});var tr=[],nr=/(=)\?(?=&|$)|\?\?/;it.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=tr.pop()||it.expando+"_"+Ln++;return this[e]=!0,e}}),it.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=t.jsonp!==!1&&(nr.test(t.url)?"url":"string"==typeof t.data&&!(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&nr.test(t.data)&&"data");return s||"jsonp"===t.dataTypes[0]?(i=t.jsonpCallback=it.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(nr,"$1"+i):t.jsonp!==!1&&(t.url+=(Hn.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||it.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,tr.push(i)),a&&it.isFunction(o)&&o(a[0]),a=o=void 0}),"script"):void 0}),it.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||ht;var r=dt.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=it.buildFragment([e],t,i),i&&i.length&&it(i).remove(),it.merge([],r.childNodes))};var rr=it.fn.load;it.fn.load=function(e,t,n){if("string"!=typeof e&&rr)return rr.apply(this,arguments);var r,i,o,a=this,s=e.indexOf(" ");return s>=0&&(r=it.trim(e.slice(s,e.length)),e=e.slice(0,s)),it.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(o="POST"),a.length>0&&it.ajax({url:e,type:o,dataType:"html",data:t}).done(function(e){i=arguments,a.html(r?it("<div>").append(it.parseHTML(e)).find(r):e)}).complete(n&&function(e,t){a.each(n,i||[e.responseText,t,e])}),this},it.expr.filters.animated=function(e){return it.grep(it.timers,function(t){return e===t.elem}).length};var ir=e.document.documentElement;it.offset={setOffset:function(e,t,n){var r,i,o,a,s,l,u,c=it.css(e,"position"),d=it(e),f={};"static"===c&&(e.style.position="relative"),s=d.offset(),o=it.css(e,"top"),l=it.css(e,"left"),u=("absolute"===c||"fixed"===c)&&it.inArray("auto",[o,l])>-1,u?(r=d.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(l)||0),it.isFunction(t)&&(t=t.call(e,n,s)),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):d.css(f)}},it.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){it.offset.setOffset(this,e,t)});var t,n,r={top:0,left:0},i=this[0],o=i&&i.ownerDocument;if(o)return t=o.documentElement,it.contains(t,i)?(typeof i.getBoundingClientRect!==Et&&(r=i.getBoundingClientRect()),n=V(o),{top:r.top+(n.pageYOffset||t.scrollTop)-(t.clientTop||0),left:r.left+(n.pageXOffset||t.scrollLeft)-(t.clientLeft||0)}):r},position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===it.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),it.nodeName(e[0],"html")||(n=e.offset()),n.top+=it.css(e[0],"borderTopWidth",!0),n.left+=it.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-it.css(r,"marginTop",!0),left:t.left-n.left-it.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent||ir;e&&!it.nodeName(e,"html")&&"static"===it.css(e,"position");)e=e.offsetParent;return e||ir})}}),it.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n=/Y/.test(t);it.fn[e]=function(r){return At(this,function(e,r,i){var o=V(e);return void 0===i?o?t in o?o[t]:o.document.documentElement[r]:e[r]:void(o?o.scrollTo(n?it(o).scrollLeft():i,n?i:it(o).scrollTop()):e[r]=i)},e,r,arguments.length,null)}}),it.each(["top","left"],function(e,t){it.cssHooks[t]=N(nt.pixelPosition,function(e,n){return n?(n=tn(e,t),rn.test(n)?it(e).position()[t]+"px":n):void 0})}),it.each({Height:"height",Width:"width"},function(e,t){it.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){it.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return At(this,function(t,n,r){var i;return it.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?it.css(t,n,a):it.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),it.fn.size=function(){return this.length},it.fn.andSelf=it.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return it});var or=e.jQuery,ar=e.$;return it.noConflict=function(t){return e.$===it&&(e.$=ar),t&&e.jQuery===it&&(e.jQuery=or),it},typeof t===Et&&(e.jQuery=e.$=it),it}),function(e,t){e.rails!==t&&e.error("jquery-ujs has already been loaded!");var n,r=e(document);e.rails=n={linkClickSelector:"a[data-confirm], a[data-method], a[data-remote], a[data-disable-with], a[data-disable]",buttonClickSelector:"button[data-remote], button[data-confirm]",inputChangeSelector:"select[data-remote], input[data-remote], textarea[data-remote]",formSubmitSelector:"form",formInputClickSelector:"form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])",disableSelector:"input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled",enableSelector:"input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled",requiredInputSelector:"input[name][required]:not([disabled]),textarea[name][required]:not([disabled])",fileInputSelector:"input[type=file]",linkDisableSelector:"a[data-disable-with], a[data-disable]",buttonDisableSelector:"button[data-remote][data-disable-with], button[data-remote][data-disable]",CSRFProtection:function(t){var n=e('meta[name="csrf-token"]').attr("content");n&&t.setRequestHeader("X-CSRF-Token",n)},refreshCSRFTokens:function(){var t=e("meta[name=csrf-token]").attr("content"),n=e("meta[name=csrf-param]").attr("content");e('form input[name="'+n+'"]').val(t)},fire:function(t,n,r){var i=e.Event(n);return t.trigger(i,r),i.result!==!1},confirm:function(e){return confirm(e)},ajax:function(t){return e.ajax(t)},href:function(e){return e.attr("href")},handleRemote:function(r){var i,o,a,s,l,u,c,d;if(n.fire(r,"ajax:before")){if(s=r.data("cross-domain"),l=s===t?null:s,u=r.data("with-credentials")||null,c=r.data("type")||e.ajaxSettings&&e.ajaxSettings.dataType,r.is("form")){i=r.attr("method"),o=r.attr("action"),a=r.serializeArray();var f=r.data("ujs:submit-button");f&&(a.push(f),r.data("ujs:submit-button",null))}else r.is(n.inputChangeSelector)?(i=r.data("method"),o=r.data("url"),a=r.serialize(),r.data("params")&&(a=a+"&"+r.data("params"))):r.is(n.buttonClickSelector)?(i=r.data("method")||"get",o=r.data("url"),a=r.serialize(),r.data("params")&&(a=a+"&"+r.data("params"))):(i=r.data("method"),o=n.href(r),a=r.data("params")||null);return d={type:i||"GET",data:a,dataType:c,beforeSend:function(e,i){return i.dataType===t&&e.setRequestHeader("accept","*/*;q=0.5, "+i.accepts.script),n.fire(r,"ajax:beforeSend",[e,i])?void r.trigger("ajax:send",e):!1 +},success:function(e,t,n){r.trigger("ajax:success",[e,t,n])},complete:function(e,t){r.trigger("ajax:complete",[e,t])},error:function(e,t,n){r.trigger("ajax:error",[e,t,n])},crossDomain:l},u&&(d.xhrFields={withCredentials:u}),o&&(d.url=o),n.ajax(d)}return!1},handleMethod:function(r){var i=n.href(r),o=r.data("method"),a=r.attr("target"),s=e("meta[name=csrf-token]").attr("content"),l=e("meta[name=csrf-param]").attr("content"),u=e('<form method="post" action="'+i+'"></form>'),c='<input name="_method" value="'+o+'" type="hidden" />';l!==t&&s!==t&&(c+='<input name="'+l+'" value="'+s+'" type="hidden" />'),a&&u.attr("target",a),u.hide().append(c).appendTo("body"),u.submit()},formElements:function(t,n){return t.is("form")?e(t[0].elements).filter(n):t.find(n)},disableFormElements:function(t){n.formElements(t,n.disableSelector).each(function(){n.disableFormElement(e(this))})},disableFormElement:function(e){var n,r;n=e.is("button")?"html":"val",r=e.data("disable-with"),e.data("ujs:enable-with",e[n]()),r!==t&&e[n](r),e.prop("disabled",!0)},enableFormElements:function(t){n.formElements(t,n.enableSelector).each(function(){n.enableFormElement(e(this))})},enableFormElement:function(e){var t=e.is("button")?"html":"val";e.data("ujs:enable-with")&&e[t](e.data("ujs:enable-with")),e.prop("disabled",!1)},allowAction:function(e){var t,r=e.data("confirm"),i=!1;return r?(n.fire(e,"confirm")&&(i=n.confirm(r),t=n.fire(e,"confirm:complete",[i])),i&&t):!0},blankInputs:function(t,n,r){var i,o,a=e(),s=n||"input,textarea",l=t.find(s);return l.each(function(){if(i=e(this),o=i.is("input[type=checkbox],input[type=radio]")?i.is(":checked"):i.val(),!o==!r){if(i.is("input[type=radio]")&&l.filter('input[type=radio]:checked[name="'+i.attr("name")+'"]').length)return!0;a=a.add(i)}}),a.length?a:!1},nonBlankInputs:function(e,t){return n.blankInputs(e,t,!0)},stopEverything:function(t){return e(t.target).trigger("ujs:everythingStopped"),t.stopImmediatePropagation(),!1},disableElement:function(e){var r=e.data("disable-with");e.data("ujs:enable-with",e.html()),r!==t&&e.html(r),e.bind("click.railsDisable",function(e){return n.stopEverything(e)})},enableElement:function(e){e.data("ujs:enable-with")!==t&&(e.html(e.data("ujs:enable-with")),e.removeData("ujs:enable-with")),e.unbind("click.railsDisable")}},n.fire(r,"rails:attachBindings")&&(e.ajaxPrefilter(function(e,t,r){e.crossDomain||n.CSRFProtection(r)}),r.delegate(n.linkDisableSelector,"ajax:complete",function(){n.enableElement(e(this))}),r.delegate(n.buttonDisableSelector,"ajax:complete",function(){n.enableFormElement(e(this))}),r.delegate(n.linkClickSelector,"click.rails",function(r){var i=e(this),o=i.data("method"),a=i.data("params"),s=r.metaKey||r.ctrlKey;if(!n.allowAction(i))return n.stopEverything(r);if(!s&&i.is(n.linkDisableSelector)&&n.disableElement(i),i.data("remote")!==t){if(s&&(!o||"GET"===o)&&!a)return!0;var l=n.handleRemote(i);return l===!1?n.enableElement(i):l.error(function(){n.enableElement(i)}),!1}return i.data("method")?(n.handleMethod(i),!1):void 0}),r.delegate(n.buttonClickSelector,"click.rails",function(t){var r=e(this);if(!n.allowAction(r))return n.stopEverything(t);r.is(n.buttonDisableSelector)&&n.disableFormElement(r);var i=n.handleRemote(r);return i===!1?n.enableFormElement(r):i.error(function(){n.enableFormElement(r)}),!1}),r.delegate(n.inputChangeSelector,"change.rails",function(t){var r=e(this);return n.allowAction(r)?(n.handleRemote(r),!1):n.stopEverything(t)}),r.delegate(n.formSubmitSelector,"submit.rails",function(r){var i,o,a=e(this),s=a.data("remote")!==t;if(!n.allowAction(a))return n.stopEverything(r);if(a.attr("novalidate")==t&&(i=n.blankInputs(a,n.requiredInputSelector),i&&n.fire(a,"ajax:aborted:required",[i])))return n.stopEverything(r);if(s){if(o=n.nonBlankInputs(a,n.fileInputSelector)){setTimeout(function(){n.disableFormElements(a)},13);var l=n.fire(a,"ajax:aborted:file",[o]);return l||setTimeout(function(){n.enableFormElements(a)},13),l}return n.handleRemote(a),!1}setTimeout(function(){n.disableFormElements(a)},13)}),r.delegate(n.formInputClickSelector,"click.rails",function(t){var r=e(this);if(!n.allowAction(r))return n.stopEverything(t);var i=r.attr("name"),o=i?{name:i,value:r.val()}:null;r.closest("form").data("ujs:submit-button",o)}),r.delegate(n.formSubmitSelector,"ajax:send.rails",function(t){this==t.target&&n.disableFormElements(e(this))}),r.delegate(n.formSubmitSelector,"ajax:complete.rails",function(t){this==t.target&&n.enableFormElements(e(this))}),e(function(){n.refreshCSRFTokens()}))}(jQuery),function(){var e,t,n,r,i,o,a,s,l,u,c,d,f,p,h,m,g,v,y,b,x,w,T,E,k,C,N,S,j,A,D,L,H,_,q,F,M,O,R,B,P,I,W,$,z,X,U,V,G,Y=[].indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(t in this&&this[t]===e)return t;return-1},J={}.hasOwnProperty,K=function(e,t){function n(){this.constructor=e}for(var r in t)J.call(t,r)&&(e[r]=t[r]);return n.prototype=t.prototype,e.prototype=new n,e.__super__=t.prototype,e},Q=[].slice;j={},d=10,$=!1,m=null,S=null,q=null,h=null,V=null,b=function(e){var t;return e=new n(e),B(),c(),F(e),$&&(t=z(e.absolute))?(x(t),w(e)):w(e,W)},z=function(e){var t;return t=j[e],t&&!t.transitionCacheDisabled?t:void 0},g=function(e){return null==e&&(e=!0),$=e},w=function(e,t){return null==t&&(t=function(){return function(){}}(this)),X("page:fetch",{url:e.absolute}),null!=V&&V.abort(),V=new XMLHttpRequest,V.open("GET",e.withoutHashForIE10compatibility(),!0),V.setRequestHeader("Accept","text/html, application/xhtml+xml, application/xml"),V.setRequestHeader("X-XHR-Referer",q),V.onload=function(){var n;return X("page:receive"),(n=H())?(f.apply(null,y(n)),M(),t(),X("page:load")):document.location.href=e.absolute},V.onloadend=function(){return V=null},V.onerror=function(){return document.location.href=e.absolute},V.send()},x=function(e){return null!=V&&V.abort(),f(e.title,e.body),_(e),X("page:restore")},c=function(){var e;return e=new n(m.url),j[e.absolute]={url:e.relative,body:document.body,title:document.title,positionY:window.pageYOffset,positionX:window.pageXOffset,cachedAt:(new Date).getTime(),transitionCacheDisabled:null!=document.querySelector("[data-no-transition-cache]")},p(d)},D=function(e){return null==e&&(e=d),/^[\d]+$/.test(e)?d=parseInt(e):void 0},p=function(e){var t,n,r,i,o,a;for(r=Object.keys(j),t=r.map(function(e){return j[e].cachedAt}).sort(function(e,t){return t-e}),a=[],i=0,o=r.length;o>i;i++)n=r[i],j[n].cachedAt<=t[e]&&(X("page:expire",j[n]),a.push(delete j[n]));return a},f=function(t,n,r,i){return document.title=t,document.documentElement.replaceChild(n,document.body),null!=r&&e.update(r),i&&v(),m=window.history.state,X("page:change"),X("page:update")},v=function(){var e,t,n,r,i,o,a,s,l,u,c,d;for(o=Array.prototype.slice.call(document.body.querySelectorAll('script:not([data-turbolinks-eval="false"])')),a=0,l=o.length;l>a;a++)if(i=o[a],""===(c=i.type)||"text/javascript"===c){for(t=document.createElement("script"),d=i.attributes,s=0,u=d.length;u>s;s++)e=d[s],t.setAttribute(e.name,e.value);t.appendChild(document.createTextNode(i.innerHTML)),r=i.parentNode,n=i.nextSibling,r.removeChild(i),r.insertBefore(t,n)}},P=function(e){return e.innerHTML=e.innerHTML.replace(/<noscript[\S\s]*?<\/noscript>/gi,""),e},F=function(e){return(e=new n(e)).absolute!==q?window.history.pushState({turbolinks:!0,url:e.absolute},"",e.absolute):void 0},M=function(){var e,t;return(e=V.getResponseHeader("X-XHR-Redirected-To"))?(e=new n(e),t=e.hasNoHash()?document.location.hash:"",window.history.replaceState(m,"",e.href+t)):void 0},B=function(){return q=document.location.href},R=function(){return window.history.replaceState({turbolinks:!0,url:document.location.href},"",document.location.href)},O=function(){return m=window.history.state},_=function(e){return window.scrollTo(e.positionX,e.positionY)},W=function(){return document.location.hash?document.location.href=document.location.href:window.scrollTo(0,0)},L=function(e){var t,n;return t=(null!=(n=document.cookie.match(new RegExp(e+"=(\\w+)")))?n[1].toUpperCase():void 0)||"",document.cookie=e+"=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/",t},X=function(e,t){var n;return n=document.createEvent("Events"),t&&(n.data=t),n.initEvent(e,!0,!0),document.dispatchEvent(n)},A=function(){return!X("page:before-change")},H=function(){var e,t,n,r,i,o;return t=function(){var e;return 400<=(e=V.status)&&600>e},o=function(){return V.getResponseHeader("Content-Type").match(/^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/)},r=function(e){var t,n,r,i,o;for(i=e.head.childNodes,o=[],n=0,r=i.length;r>n;n++)t=i[n],null!=("function"==typeof t.getAttribute?t.getAttribute("data-turbolinks-track"):void 0)&&o.push(t.getAttribute("src")||t.getAttribute("href"));return o},e=function(e){var t;return S||(S=r(document)),t=r(e),t.length!==S.length||i(t,S).length!==S.length},i=function(e,t){var n,r,i,o,a;for(e.length>t.length&&(o=[t,e],e=o[0],t=o[1]),a=[],r=0,i=e.length;i>r;r++)n=e[r],Y.call(t,n)>=0&&a.push(n);return a},!t()&&o()&&(n=h(V.responseText),n&&!e(n))?n:void 0},y=function(t){var n;return n=t.querySelector("title"),[null!=n?n.textContent:void 0,P(t.body),e.get(t).token,"runScripts"]},e={get:function(e){var t;return null==e&&(e=document),{node:t=e.querySelector('meta[name="csrf-token"]'),token:null!=t&&"function"==typeof t.getAttribute?t.getAttribute("content"):void 0}},update:function(e){var t;return t=this.get(),null!=t.token&&null!=e&&t.token!==e?t.node.setAttribute("content",e):void 0}},i=function(){var e,t,n,r,i,o;t=function(e){return(new DOMParser).parseFromString(e,"text/html")},e=function(e){var t;return t=document.implementation.createHTMLDocument(""),t.documentElement.innerHTML=e,t},n=function(e){var t;return t=document.implementation.createHTMLDocument(""),t.open("replace"),t.write(e),t.close(),t};try{if(window.DOMParser)return i=t("<html><body><p>test"),t}catch(a){return r=a,i=e("<html><body><p>test"),e}finally{if(1!==(null!=i&&null!=(o=i.body)?o.childNodes.length:void 0))return n}},n=function(){function e(t){return this.original=null!=t?t:document.location.href,this.original.constructor===e?this.original:void this._parse()}return e.prototype.withoutHash=function(){return this.href.replace(this.hash,"")},e.prototype.withoutHashForIE10compatibility=function(){return this.withoutHash()},e.prototype.hasNoHash=function(){return 0===this.hash.length},e.prototype._parse=function(){var e;return(null!=this.link?this.link:this.link=document.createElement("a")).href=this.original,e=this.link,this.href=e.href,this.protocol=e.protocol,this.host=e.host,this.hostname=e.hostname,this.port=e.port,this.pathname=e.pathname,this.search=e.search,this.hash=e.hash,this.origin=[this.protocol,"//",this.hostname].join(""),0!==this.port.length&&(this.origin+=":"+this.port),this.relative=[this.pathname,this.search,this.hash].join(""),this.absolute=this.href},e}(),r=function(e){function t(e){return this.link=e,this.link.constructor===t?this.link:(this.original=this.link.href,void t.__super__.constructor.apply(this,arguments))}return K(t,e),t.HTML_EXTENSIONS=["html"],t.allowExtensions=function(){var e,n,r,i;for(n=1<=arguments.length?Q.call(arguments,0):[],r=0,i=n.length;i>r;r++)e=n[r],t.HTML_EXTENSIONS.push(e);return t.HTML_EXTENSIONS},t.prototype.shouldIgnore=function(){return this._crossOrigin()||this._anchored()||this._nonHtml()||this._optOut()||this._target()},t.prototype._crossOrigin=function(){return this.origin!==(new n).origin},t.prototype._anchored=function(){var e;return(this.hash&&this.withoutHash())===(e=new n).withoutHash()||this.href===e.href+"#"},t.prototype._nonHtml=function(){return this.pathname.match(/\.[a-z]+$/g)&&!this.pathname.match(new RegExp("\\.(?:"+t.HTML_EXTENSIONS.join("|")+")?$","g"))},t.prototype._optOut=function(){var e,t;for(t=this.link;!e&&t!==document;)e=null!=t.getAttribute("data-no-turbolink"),t=t.parentNode;return e},t.prototype._target=function(){return 0!==this.link.target.length},t}(n),t=function(){function e(e){this.event=e,this.event.defaultPrevented||(this._extractLink(),this._validForTurbolinks()&&(A()||U(this.link.href),this.event.preventDefault()))}return e.installHandlerLast=function(t){return t.defaultPrevented?void 0:(document.removeEventListener("click",e.handle,!1),document.addEventListener("click",e.handle,!1))},e.handle=function(t){return new e(t)},e.prototype._extractLink=function(){var e;for(e=this.event.target;e.parentNode&&"A"!==e.nodeName;)e=e.parentNode;return"A"===e.nodeName&&0!==e.href.length?this.link=new r(e):void 0},e.prototype._validForTurbolinks=function(){return null!=this.link&&!(this.link.shouldIgnore()||this._nonStandardClick())},e.prototype._nonStandardClick=function(){return this.event.which>1||this.event.metaKey||this.event.ctrlKey||this.event.shiftKey||this.event.altKey},e}(),u=function(e){return setTimeout(e,500)},k=function(){return document.addEventListener("DOMContentLoaded",function(){return X("page:change"),X("page:update")},!0)},N=function(){return"undefined"!=typeof jQuery?jQuery(document).on("ajaxSuccess",function(e,t){return jQuery.trim(t.responseText)?X("page:update"):void 0}):void 0},C=function(e){var t,r;return(null!=(r=e.state)?r.turbolinks:void 0)?(t=j[new n(e.state.url).absolute])?(c(),x(t)):U(e.target.location.href):void 0},E=function(){return R(),O(),h=i(),document.addEventListener("click",t.installHandlerLast,!0),u(function(){return window.addEventListener("popstate",C,!1)})},T=void 0!==window.history.state||navigator.userAgent.match(/Firefox\/2[6|7]/),s=window.history&&window.history.pushState&&window.history.replaceState&&T,o=!navigator.userAgent.match(/CriOS\//),I="GET"===(G=L("request_method"))||""===G,l=s&&o&&I,a=document.addEventListener&&document.createEvent,a&&(k(),N()),l?(U=b,E()):U=function(e){return document.location.href=e},this.Turbolinks={visit:U,pagesCached:D,enableTransitionCache:g,allowLinkExtensions:r.allowExtensions,supported:l}}.call(this),function(){}.call(this);
\ No newline at end of file diff --git a/actionpack/test/fixtures/公共/gzip/foo.zoo.gz b/actionpack/test/fixtures/公共/gzip/foo.zoo.gz Binary files differnew file mode 100644 index 0000000000..f62c656dc8 --- /dev/null +++ b/actionpack/test/fixtures/公共/gzip/foo.zoo.gz diff --git a/actionpack/test/fixtures/公共/index.html b/actionpack/test/fixtures/公共/index.html new file mode 100644 index 0000000000..525950ba6b --- /dev/null +++ b/actionpack/test/fixtures/公共/index.html @@ -0,0 +1 @@ +/index.html
\ No newline at end of file diff --git a/actionpack/test/journey/path/pattern_test.rb b/actionpack/test/journey/path/pattern_test.rb index ce02104181..6939b426f6 100644 --- a/actionpack/test/journey/path/pattern_test.rb +++ b/actionpack/test/journey/path/pattern_test.rb @@ -16,9 +16,10 @@ module ActionDispatch '/:controller(.:format)' => %r{\A/(#{x})(?:\.([^/.?]+))?\Z}, '/:controller/*foo' => %r{\A/(#{x})/(.+)\Z}, '/:controller/*foo/bar' => %r{\A/(#{x})/(.+)/bar\Z}, + '/:foo|*bar' => %r{\A/(?:([^/.?]+)|(.+))\Z}, }.each do |path, expected| define_method(:"test_to_regexp_#{path}") do - strexp = Router::Strexp.new( + strexp = Router::Strexp.build( path, { :controller => /.+/ }, ["/", ".", "?"] @@ -39,9 +40,10 @@ module ActionDispatch '/:controller(.:format)' => %r{\A/(#{x})(?:\.([^/.?]+))?}, '/:controller/*foo' => %r{\A/(#{x})/(.+)}, '/:controller/*foo/bar' => %r{\A/(#{x})/(.+)/bar}, + '/:foo|*bar' => %r{\A/(?:([^/.?]+)|(.+))}, }.each do |path, expected| define_method(:"test_to_non_anchored_regexp_#{path}") do - strexp = Router::Strexp.new( + strexp = Router::Strexp.build( path, { :controller => /.+/ }, ["/", ".", "?"], @@ -65,7 +67,7 @@ module ActionDispatch '/:controller/*foo/bar' => %w{ controller foo }, }.each do |path, expected| define_method(:"test_names_#{path}") do - strexp = Router::Strexp.new( + strexp = Router::Strexp.build( path, { :controller => /.+/ }, ["/", ".", "?"] @@ -75,12 +77,8 @@ module ActionDispatch end end - def test_to_raise_exception_with_bad_expression - assert_raise(ArgumentError, "Bad expression: []") { Pattern.new [] } - end - def test_to_regexp_with_extended_group - strexp = Router::Strexp.new( + strexp = Router::Strexp.build( '/page/:name', { :name => / #ROFL @@ -101,13 +99,13 @@ module ActionDispatch ['/:foo(/:bar)', %w{ bar }], ['/:foo(/:bar)/:lol(/:baz)', %w{ bar baz }], ].each do |pattern, list| - path = Pattern.new pattern + path = Pattern.from_string pattern assert_equal list.sort, path.optional_names.sort end end def test_to_regexp_match_non_optional - strexp = Router::Strexp.new( + strexp = Router::Strexp.build( '/:name', { :name => /\d+/ }, ["/", ".", "?"] @@ -118,7 +116,7 @@ module ActionDispatch end def test_to_regexp_with_group - strexp = Router::Strexp.new( + strexp = Router::Strexp.build( '/page/:name', { :name => /(tender|love)/ }, ["/", ".", "?"] @@ -131,7 +129,7 @@ module ActionDispatch def test_ast_sets_regular_expressions requirements = { :name => /(tender|love)/, :value => /./ } - strexp = Router::Strexp.new( + strexp = Router::Strexp.build( '/page/:name/:value', requirements, ["/", ".", "?"] @@ -148,7 +146,7 @@ module ActionDispatch end def test_match_data_with_group - strexp = Router::Strexp.new( + strexp = Router::Strexp.build( '/page/:name', { :name => /(tender|love)/ }, ["/", ".", "?"] @@ -160,7 +158,7 @@ module ActionDispatch end def test_match_data_with_multi_group - strexp = Router::Strexp.new( + strexp = Router::Strexp.build( '/page/:name/:id', { :name => /t(((ender|love)))()/ }, ["/", ".", "?"] @@ -175,7 +173,7 @@ module ActionDispatch def test_star_with_custom_re z = /\d+/ - strexp = Router::Strexp.new( + strexp = Router::Strexp.build( '/page/*foo', { :foo => z }, ["/", ".", "?"] @@ -185,7 +183,7 @@ module ActionDispatch end def test_insensitive_regexp_with_group - strexp = Router::Strexp.new( + strexp = Router::Strexp.build( '/page/:name/aaron', { :name => /(tender|love)/i }, ["/", ".", "?"] @@ -197,7 +195,7 @@ module ActionDispatch end def test_to_regexp_with_strexp - strexp = Router::Strexp.new('/:controller', { }, ["/", ".", "?"]) + strexp = Router::Strexp.build('/:controller', { }, ["/", ".", "?"]) path = Pattern.new strexp x = %r{\A/([^/.?]+)\Z} @@ -205,20 +203,20 @@ module ActionDispatch end def test_to_regexp_defaults - path = Pattern.new '/:controller(/:action(/:id))' + path = Pattern.from_string '/:controller(/:action(/:id))' expected = %r{\A/([^/.?]+)(?:/([^/.?]+)(?:/([^/.?]+))?)?\Z} assert_equal expected, path.to_regexp end def test_failed_match - path = Pattern.new '/:controller(/:action(/:id(.:format)))' + path = Pattern.from_string '/:controller(/:action(/:id(.:format)))' uri = 'content' assert_not path =~ uri end def test_match_controller - path = Pattern.new '/:controller(/:action(/:id(.:format)))' + path = Pattern.from_string '/:controller(/:action(/:id(.:format)))' uri = '/content' match = path =~ uri @@ -230,7 +228,7 @@ module ActionDispatch end def test_match_controller_action - path = Pattern.new '/:controller(/:action(/:id(.:format)))' + path = Pattern.from_string '/:controller(/:action(/:id(.:format)))' uri = '/content/list' match = path =~ uri @@ -242,7 +240,7 @@ module ActionDispatch end def test_match_controller_action_id - path = Pattern.new '/:controller(/:action(/:id(.:format)))' + path = Pattern.from_string '/:controller(/:action(/:id(.:format)))' uri = '/content/list/10' match = path =~ uri @@ -254,7 +252,7 @@ module ActionDispatch end def test_match_literal - path = Path::Pattern.new "/books(/:action(.:format))" + path = Path::Pattern.from_string "/books(/:action(.:format))" uri = '/books' match = path =~ uri @@ -264,7 +262,7 @@ module ActionDispatch end def test_match_literal_with_action - path = Path::Pattern.new "/books(/:action(.:format))" + path = Path::Pattern.from_string "/books(/:action(.:format))" uri = '/books/list' match = path =~ uri @@ -274,7 +272,7 @@ module ActionDispatch end def test_match_literal_with_action_and_format - path = Path::Pattern.new "/books(/:action(.:format))" + path = Path::Pattern.from_string "/books(/:action(.:format))" uri = '/books/list.rss' match = path =~ uri diff --git a/actionpack/test/journey/route/definition/scanner_test.rb b/actionpack/test/journey/route/definition/scanner_test.rb index 624e6df51a..7a510f1e07 100644 --- a/actionpack/test/journey/route/definition/scanner_test.rb +++ b/actionpack/test/journey/route/definition/scanner_test.rb @@ -11,12 +11,25 @@ module ActionDispatch # /page/:id(/:action)(.:format) def test_tokens [ - ['/', [[:SLASH, '/']]], - ['*omg', [[:STAR, '*omg']]], - ['/page', [[:SLASH, '/'], [:LITERAL, 'page']]], - ['/~page', [[:SLASH, '/'], [:LITERAL, '~page']]], - ['/pa-ge', [[:SLASH, '/'], [:LITERAL, 'pa-ge']]], - ['/:page', [[:SLASH, '/'], [:SYMBOL, ':page']]], + ['/', [[:SLASH, '/']]], + ['*omg', [[:STAR, '*omg']]], + ['/page', [[:SLASH, '/'], [:LITERAL, 'page']]], + ['/page!', [[:SLASH, '/'], [:LITERAL, 'page!']]], + ['/page$', [[:SLASH, '/'], [:LITERAL, 'page$']]], + ['/page&', [[:SLASH, '/'], [:LITERAL, 'page&']]], + ["/page'", [[:SLASH, '/'], [:LITERAL, "page'"]]], + ['/page*', [[:SLASH, '/'], [:LITERAL, 'page*']]], + ['/page+', [[:SLASH, '/'], [:LITERAL, 'page+']]], + ['/page,', [[:SLASH, '/'], [:LITERAL, 'page,']]], + ['/page;', [[:SLASH, '/'], [:LITERAL, 'page;']]], + ['/page=', [[:SLASH, '/'], [:LITERAL, 'page=']]], + ['/page@', [[:SLASH, '/'], [:LITERAL, 'page@']]], + ['/page\:', [[:SLASH, '/'], [:LITERAL, 'page:']]], + ['/page\(', [[:SLASH, '/'], [:LITERAL, 'page(']]], + ['/page\)', [[:SLASH, '/'], [:LITERAL, 'page)']]], + ['/~page', [[:SLASH, '/'], [:LITERAL, '~page']]], + ['/pa-ge', [[:SLASH, '/'], [:LITERAL, 'pa-ge']]], + ['/:page', [[:SLASH, '/'], [:SYMBOL, ':page']]], ['/(:page)', [ [:SLASH, '/'], [:LPAREN, '('], diff --git a/actionpack/test/journey/route_test.rb b/actionpack/test/journey/route_test.rb index cbe6284714..21d867aca0 100644 --- a/actionpack/test/journey/route_test.rb +++ b/actionpack/test/journey/route_test.rb @@ -5,7 +5,7 @@ module ActionDispatch class TestRoute < ActiveSupport::TestCase def test_initialize app = Object.new - path = Path::Pattern.new '/:controller(/:action(/:id(.:format)))' + path = Path::Pattern.from_string '/:controller(/:action(/:id(.:format)))' defaults = {} route = Route.new("name", app, path, {}, defaults) @@ -16,7 +16,7 @@ module ActionDispatch def test_route_adds_itself_as_memo app = Object.new - path = Path::Pattern.new '/:controller(/:action(/:id(.:format)))' + path = Path::Pattern.from_string '/:controller(/:action(/:id(.:format)))' defaults = {} route = Route.new("name", app, path, {}, defaults) @@ -26,21 +26,21 @@ module ActionDispatch end def test_ip_address - path = Path::Pattern.new '/messages/:id(.:format)' + path = Path::Pattern.from_string '/messages/:id(.:format)' route = Route.new("name", nil, path, {:ip => '192.168.1.1'}, { :controller => 'foo', :action => 'bar' }) assert_equal '192.168.1.1', route.ip end def test_default_ip - path = Path::Pattern.new '/messages/:id(.:format)' + path = Path::Pattern.from_string '/messages/:id(.:format)' route = Route.new("name", nil, path, {}, { :controller => 'foo', :action => 'bar' }) assert_equal(//, route.ip) end def test_format_with_star - path = Path::Pattern.new '/:controller/*extra' + path = Path::Pattern.from_string '/:controller/*extra' route = Route.new("name", nil, path, {}, { :controller => 'foo', :action => 'bar' }) assert_equal '/foo/himom', route.format({ @@ -50,7 +50,7 @@ module ActionDispatch end def test_connects_all_match - path = Path::Pattern.new '/:controller(/:action(/:id(.:format)))' + path = Path::Pattern.from_string '/:controller(/:action(/:id(.:format)))' route = Route.new("name", nil, path, {:action => 'bar'}, { :controller => 'foo' }) assert_equal '/foo/bar/10', route.format({ @@ -61,21 +61,21 @@ module ActionDispatch end def test_extras_are_not_included_if_optional - path = Path::Pattern.new '/page/:id(/:action)' + path = Path::Pattern.from_string '/page/:id(/:action)' route = Route.new("name", nil, path, { }, { :action => 'show' }) assert_equal '/page/10', route.format({ :id => 10 }) end def test_extras_are_not_included_if_optional_with_parameter - path = Path::Pattern.new '(/sections/:section)/pages/:id' + path = Path::Pattern.from_string '(/sections/:section)/pages/:id' route = Route.new("name", nil, path, { }, { :action => 'show' }) assert_equal '/pages/10', route.format({:id => 10}) end def test_extras_are_not_included_if_optional_parameter_is_nil - path = Path::Pattern.new '(/sections/:section)/pages/:id' + path = Path::Pattern.from_string '(/sections/:section)/pages/:id' route = Route.new("name", nil, path, { }, { :action => 'show' }) assert_equal '/pages/10', route.format({:id => 10, :section => nil}) @@ -85,10 +85,10 @@ module ActionDispatch constraints = {:required_defaults => [:controller, :action]} defaults = {:controller=>"pages", :action=>"show"} - path = Path::Pattern.new "/page/:id(/:action)(.:format)" + path = Path::Pattern.from_string "/page/:id(/:action)(.:format)" specific = Route.new "name", nil, path, constraints, defaults - path = Path::Pattern.new "/:controller(/:action(/:id))(.:format)" + path = Path::Pattern.from_string "/:controller(/:action(/:id))(.:format)" generic = Route.new "name", nil, path, constraints knowledge = {:id=>20, :controller=>"pages", :action=>"show"} diff --git a/actionpack/test/journey/router/strexp_test.rb b/actionpack/test/journey/router/strexp_test.rb deleted file mode 100644 index 7ccdfb7b4d..0000000000 --- a/actionpack/test/journey/router/strexp_test.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'abstract_unit' - -module ActionDispatch - module Journey - class Router - class TestStrexp < ActiveSupport::TestCase - def test_many_names - exp = Strexp.new( - "/:controller(/:action(/:id(.:format)))", - {:controller=>/.+?/}, - ["/", ".", "?"], - true) - - assert_equal ["controller", "action", "id", "format"], exp.names - end - - def test_names - { - "/bar(.:format)" => %w{ format }, - ":format" => %w{ format }, - ":format-" => %w{ format }, - ":format0" => %w{ format0 }, - ":format1,:format2" => %w{ format1 format2 }, - }.each do |string, expected| - exp = Strexp.new(string, {}, ["/", ".", "?"]) - assert_equal expected, exp.names - end - end - end - end - end -end diff --git a/actionpack/test/journey/router/utils_test.rb b/actionpack/test/journey/router/utils_test.rb index 93348f4647..9b2b85ec73 100644 --- a/actionpack/test/journey/router/utils_test.rb +++ b/actionpack/test/journey/router/utils_test.rb @@ -1,3 +1,4 @@ +# coding: utf-8 require 'abstract_unit' module ActionDispatch @@ -5,17 +6,25 @@ module ActionDispatch class Router class TestUtils < ActiveSupport::TestCase def test_path_escape - assert_equal "a/b%20c+d", Utils.escape_path("a/b c+d") + assert_equal "a/b%20c+d%25", Utils.escape_path("a/b c+d%") + end + + def test_segment_escape + assert_equal "a%2Fb%20c+d%25", Utils.escape_segment("a/b c+d%") end def test_fragment_escape - assert_equal "a/b%20c+d?e", Utils.escape_fragment("a/b c+d?e") + assert_equal "a/b%20c+d%25?e", Utils.escape_fragment("a/b c+d%?e") end def test_uri_unescape assert_equal "a/b c+d", Utils.unescape_uri("a%2Fb%20c+d") end + def test_uri_unescape_with_utf8_string + assert_equal "Šašinková", Utils.unescape_uri("%C5%A0a%C5%A1inkov%C3%A1".force_encoding(Encoding::US_ASCII)) + end + def test_normalize_path_not_greedy assert_equal "/foo%20bar%20baz", Utils.normalize_path("/foo%20bar%20baz") end diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb index a286f77633..19c61b5914 100644 --- a/actionpack/test/journey/router_test.rb +++ b/actionpack/test/journey/router_test.rb @@ -4,24 +4,15 @@ require 'abstract_unit' module ActionDispatch module Journey class TestRouter < ActiveSupport::TestCase - # TODO : clean up routing tests so we don't need this hack - class StubDispatcher < Routing::RouteSet::Dispatcher; end - attr_reader :routes def setup - @app = StubDispatcher.new + @app = Routing::RouteSet::Dispatcher.new({}) @routes = Routes.new - @router = Router.new(@routes, {}) + @router = Router.new(@routes) @formatter = Formatter.new(@routes) end - def test_request_class_reader - klass = Object.new - router = Router.new(routes, :request_class => klass) - assert_equal klass, router.request_class - end - class FakeRequestFeeler < Struct.new(:env, :called) def new env self.env = env @@ -39,33 +30,33 @@ module ActionDispatch end def test_dashes - router = Router.new(routes, {}) + router = Router.new(routes) - exp = Router::Strexp.new '/foo-bar-baz', {}, ['/.?'] + exp = Router::Strexp.build '/foo-bar-baz', {}, ['/.?'] path = Path::Pattern.new exp routes.add_route nil, path, {}, {:id => nil}, {} env = rails_env 'PATH_INFO' => '/foo-bar-baz' called = false - router.recognize(env) do |r, _, params| + router.recognize(env) do |r, params| called = true end assert called end def test_unicode - router = Router.new(routes, {}) + router = Router.new(routes) #match the escaped version of /ほげ - exp = Router::Strexp.new '/%E3%81%BB%E3%81%92', {}, ['/.?'] + exp = Router::Strexp.build '/%E3%81%BB%E3%81%92', {}, ['/.?'] path = Path::Pattern.new exp routes.add_route nil, path, {}, {:id => nil}, {} env = rails_env 'PATH_INFO' => '/%E3%81%BB%E3%81%92' called = false - router.recognize(env) do |r, _, params| + router.recognize(env) do |r, params| called = true end assert called @@ -73,17 +64,17 @@ module ActionDispatch def test_request_class_and_requirements_success klass = FakeRequestFeeler.new nil - router = Router.new(routes, {:request_class => klass }) + router = Router.new(routes) requirements = { :hello => /world/ } - exp = Router::Strexp.new '/foo(/:id)', {}, ['/.?'] + exp = Router::Strexp.build '/foo(/:id)', {}, ['/.?'] path = Path::Pattern.new exp routes.add_route nil, path, requirements, {:id => nil}, {} - env = rails_env 'PATH_INFO' => '/foo/10' - router.recognize(env) do |r, _, params| + env = rails_env({'PATH_INFO' => '/foo/10'}, klass) + router.recognize(env) do |r, params| assert_equal({:id => '10'}, params) end @@ -93,17 +84,17 @@ module ActionDispatch def test_request_class_and_requirements_fail klass = FakeRequestFeeler.new nil - router = Router.new(routes, {:request_class => klass }) + router = Router.new(routes) requirements = { :hello => /mom/ } - exp = Router::Strexp.new '/foo(/:id)', {}, ['/.?'] + exp = Router::Strexp.build '/foo(/:id)', {}, ['/.?'] path = Path::Pattern.new exp router.routes.add_route nil, path, requirements, {:id => nil}, {} - env = rails_env 'PATH_INFO' => '/foo/10' - router.recognize(env) do |r, _, params| + env = rails_env({'PATH_INFO' => '/foo/10'}, klass) + router.recognize(env) do |r, params| flunk 'route should not be found' end @@ -111,24 +102,29 @@ module ActionDispatch assert_equal env.env, klass.env end - class CustomPathRequest < Router::NullReq + class CustomPathRequest < ActionDispatch::Request def path_info env['custom.path_info'] end + + def path_info=(x) + env['custom.path_info'] = x + end end def test_request_class_overrides_path_info - router = Router.new(routes, {:request_class => CustomPathRequest }) + router = Router.new(routes) - exp = Router::Strexp.new '/bar', {}, ['/.?'] + exp = Router::Strexp.build '/bar', {}, ['/.?'] path = Path::Pattern.new exp routes.add_route nil, path, {}, {}, {} - env = rails_env 'PATH_INFO' => '/foo', 'custom.path_info' => '/bar' + env = rails_env({'PATH_INFO' => '/foo', + 'custom.path_info' => '/bar'}, CustomPathRequest) recognized = false - router.recognize(env) do |r, _, params| + router.recognize(env) do |r, params| recognized = true end @@ -137,14 +133,14 @@ module ActionDispatch def test_regexp_first_precedence add_routes @router, [ - Router::Strexp.new("/whois/:domain", {:domain => /\w+\.[\w\.]+/}, ['/', '.', '?']), - Router::Strexp.new("/whois/:id(.:format)", {}, ['/', '.', '?']) + Router::Strexp.build("/whois/:domain", {:domain => /\w+\.[\w\.]+/}, ['/', '.', '?']), + Router::Strexp.build("/whois/:id(.:format)", {}, ['/', '.', '?']) ] env = rails_env 'PATH_INFO' => '/whois/example.com' list = [] - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| list << r end assert_equal 2, list.length @@ -156,93 +152,104 @@ module ActionDispatch def test_required_parts_verified_are_anchored add_routes @router, [ - Router::Strexp.new("/foo/:id", { :id => /\d/ }, ['/', '.', '?'], false) + Router::Strexp.build("/foo/:id", { :id => /\d/ }, ['/', '.', '?'], false) ] assert_raises(ActionController::UrlGenerationError) do - @formatter.generate(:path_info, nil, { :id => '10' }, { }) + @formatter.generate(nil, { :id => '10' }, { }) end end def test_required_parts_are_verified_when_building add_routes @router, [ - Router::Strexp.new("/foo/:id", { :id => /\d+/ }, ['/', '.', '?'], false) + Router::Strexp.build("/foo/:id", { :id => /\d+/ }, ['/', '.', '?'], false) ] - path, _ = @formatter.generate(:path_info, nil, { :id => '10' }, { }) + path, _ = @formatter.generate(nil, { :id => '10' }, { }) assert_equal '/foo/10', path assert_raises(ActionController::UrlGenerationError) do - @formatter.generate(:path_info, nil, { :id => 'aa' }, { }) + @formatter.generate(nil, { :id => 'aa' }, { }) end end def test_only_required_parts_are_verified add_routes @router, [ - Router::Strexp.new("/foo(/:id)", {:id => /\d/}, ['/', '.', '?'], false) + Router::Strexp.build("/foo(/:id)", {:id => /\d/}, ['/', '.', '?'], false) ] - path, _ = @formatter.generate(:path_info, nil, { :id => '10' }, { }) + path, _ = @formatter.generate(nil, { :id => '10' }, { }) assert_equal '/foo/10', path - path, _ = @formatter.generate(:path_info, nil, { }, { }) + path, _ = @formatter.generate(nil, { }, { }) assert_equal '/foo', path - path, _ = @formatter.generate(:path_info, nil, { :id => 'aa' }, { }) + path, _ = @formatter.generate(nil, { :id => 'aa' }, { }) assert_equal '/foo/aa', path end def test_knows_what_parts_are_missing_from_named_route route_name = "gorby_thunderhorse" - pattern = Router::Strexp.new("/foo/:id", { :id => /\d+/ }, ['/', '.', '?'], false) + pattern = Router::Strexp.build("/foo/:id", { :id => /\d+/ }, ['/', '.', '?'], false) path = Path::Pattern.new pattern @router.routes.add_route nil, path, {}, {}, route_name error = assert_raises(ActionController::UrlGenerationError) do - @formatter.generate(:path_info, route_name, { }, { }) + @formatter.generate(route_name, { }, { }) end assert_match(/missing required keys: \[:id\]/, error.message) end + def test_does_not_include_missing_keys_message + route_name = "gorby_thunderhorse" + + error = assert_raises(ActionController::UrlGenerationError) do + @formatter.generate(route_name, { }, { }) + end + + assert_no_match(/missing required keys: \[\]/, error.message) + end + def test_X_Cascade add_routes @router, [ "/messages(.:format)" ] - resp = @router.call({ 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/lol' }) + resp = @router.serve(rails_env({ 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/lol' })) assert_equal ['Not Found'], resp.last assert_equal 'pass', resp[1]['X-Cascade'] assert_equal 404, resp.first end def test_clear_trailing_slash_from_script_name_on_root_unanchored_routes - strexp = Router::Strexp.new("/", {}, ['/', '.', '?'], false) - path = Path::Pattern.new strexp + route_set = Routing::RouteSet.new + mapper = Routing::Mapper.new route_set + app = lambda { |env| [200, {}, ['success!']] } - @router.routes.add_route(app, path, {}, {}, {}) + mapper.get '/weblog', :to => app env = rack_env('SCRIPT_NAME' => '', 'PATH_INFO' => '/weblog') - resp = @router.call(env) + resp = route_set.call env assert_equal ['success!'], resp.last assert_equal '', env['SCRIPT_NAME'] end def test_defaults_merge_correctly - path = Path::Pattern.new '/foo(/:id)' + path = Path::Pattern.from_string '/foo(/:id)' @router.routes.add_route nil, path, {}, {:id => nil}, {} env = rails_env 'PATH_INFO' => '/foo/10' - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| assert_equal({:id => '10'}, params) end env = rails_env 'PATH_INFO' => '/foo' - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| assert_equal({:id => nil}, params) end end def test_recognize_with_unbound_regexp add_routes @router, [ - Router::Strexp.new("/foo", { }, ['/', '.', '?'], false) + Router::Strexp.build("/foo", { }, ['/', '.', '?'], false) ] env = rails_env 'PATH_INFO' => '/foo/bar' @@ -255,7 +262,7 @@ module ActionDispatch def test_bound_regexp_keeps_path_info add_routes @router, [ - Router::Strexp.new("/foo", { }, ['/', '.', '?'], true) + Router::Strexp.build("/foo", { }, ['/', '.', '?'], true) ] env = rails_env 'PATH_INFO' => '/foo' @@ -287,14 +294,14 @@ module ActionDispatch def test_required_part_in_recall add_routes @router, [ "/messages/:a/:b" ] - path, _ = @formatter.generate(:path_info, nil, { :a => 'a' }, { :b => 'b' }) + path, _ = @formatter.generate(nil, { :a => 'a' }, { :b => 'b' }) assert_equal "/messages/a/b", path end def test_splat_in_recall add_routes @router, [ "/*path" ] - path, _ = @formatter.generate(:path_info, nil, { }, { :path => 'b' }) + path, _ = @formatter.generate(nil, { }, { :path => 'b' }) assert_equal "/b", path end @@ -304,35 +311,35 @@ module ActionDispatch "/messages/:id(.:format)" ] - path, _ = @formatter.generate(:path_info, nil, { :id => 10 }, { :action => 'index' }) + path, _ = @formatter.generate(nil, { :id => 10 }, { :action => 'index' }) assert_equal "/messages/index/10", path end def test_nil_path_parts_are_ignored - path = Path::Pattern.new "/:controller(/:action(.:format))" + path = Path::Pattern.from_string "/:controller(/:action(.:format))" @router.routes.add_route @app, path, {}, {}, {} params = { :controller => "tasks", :format => nil } extras = { :action => 'lol' } - path, _ = @formatter.generate(:path_info, nil, params, extras) + path, _ = @formatter.generate(nil, params, extras) assert_equal '/tasks', path end def test_generate_slash params = [ [:controller, "tasks"], [:action, "show"] ] - str = Router::Strexp.new("/", Hash[params], ['/', '.', '?'], true) + str = Router::Strexp.build("/", Hash[params], ['/', '.', '?'], true) path = Path::Pattern.new str @router.routes.add_route @app, path, {}, {}, {} - path, _ = @formatter.generate(:path_info, nil, Hash[params], {}) + path, _ = @formatter.generate(nil, Hash[params], {}) assert_equal '/', path end def test_generate_calls_param_proc - path = Path::Pattern.new '/:controller(/:action)' + path = Path::Pattern.from_string '/:controller(/:action)' @router.routes.add_route @app, path, {}, {}, {} parameterized = [] @@ -340,7 +347,6 @@ module ActionDispatch [:action, "show"] ] @formatter.generate( - :path_info, nil, Hash[params], {}, @@ -350,31 +356,42 @@ module ActionDispatch end def test_generate_id - path = Path::Pattern.new '/:controller(/:action)' + path = Path::Pattern.from_string '/:controller(/:action)' @router.routes.add_route @app, path, {}, {}, {} path, params = @formatter.generate( - :path_info, nil, {:id=>1, :controller=>"tasks", :action=>"show"}, {}) + nil, {:id=>1, :controller=>"tasks", :action=>"show"}, {}) assert_equal '/tasks/show', path assert_equal({:id => 1}, params) end def test_generate_escapes - path = Path::Pattern.new '/:controller(/:action)' + path = Path::Pattern.from_string '/:controller(/:action)' @router.routes.add_route @app, path, {}, {}, {} - path, _ = @formatter.generate(:path_info, - nil, { :controller => "tasks", + path, _ = @formatter.generate(nil, + { :controller => "tasks", :action => "a/b c+d", }, {}) - assert_equal '/tasks/a/b%20c+d', path + assert_equal '/tasks/a%2Fb%20c+d', path + end + + def test_generate_escapes_with_namespaced_controller + path = Path::Pattern.from_string '/:controller(/:action)' + @router.routes.add_route @app, path, {}, {}, {} + + path, _ = @formatter.generate( + nil, { :controller => "admin/tasks", + :action => "a/b c+d", + }, {}) + assert_equal '/admin/tasks/a%2Fb%20c+d', path end def test_generate_extra_params - path = Path::Pattern.new '/:controller(/:action)' + path = Path::Pattern.from_string '/:controller(/:action)' @router.routes.add_route @app, path, {}, {}, {} - path, params = @formatter.generate(:path_info, + path, params = @formatter.generate( nil, { :id => 1, :controller => "tasks", :action => "show", @@ -385,10 +402,10 @@ module ActionDispatch end def test_generate_uses_recall_if_needed - path = Path::Pattern.new '/:controller(/:action(/:id))' + path = Path::Pattern.from_string '/:controller(/:action(/:id))' @router.routes.add_route @app, path, {}, {}, {} - path, params = @formatter.generate(:path_info, + path, params = @formatter.generate( nil, {:controller =>"tasks", :id => 10}, {:action =>"index"}) @@ -397,10 +414,10 @@ module ActionDispatch end def test_generate_with_name - path = Path::Pattern.new '/:controller(/:action)' - @router.routes.add_route @app, path, {}, {}, {} + path = Path::Pattern.from_string '/:controller(/:action)' + @router.routes.add_route @app, path, {}, {}, "tasks" - path, params = @formatter.generate(:path_info, + path, params = @formatter.generate( "tasks", {:controller=>"tasks"}, {:controller=>"tasks", :action=>"index"}) @@ -414,14 +431,14 @@ module ActionDispatch '/content/show/10' => { :controller => 'content', :action => 'show', :id => "10" }, }.each do |request_path, expected| define_method("test_recognize_#{expected.keys.map(&:to_s).join('_')}") do - path = Path::Pattern.new "/:controller(/:action(/:id))" + path = Path::Pattern.from_string "/:controller(/:action(/:id))" app = Object.new route = @router.routes.add_route(app, path, {}, {}, {}) env = rails_env 'PATH_INFO' => request_path called = false - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| assert_equal route, r assert_equal(expected, params) called = true @@ -436,14 +453,14 @@ module ActionDispatch :splat => ['/segment/a/b%20c+d', { :segment => 'segment', :splat => 'a/b c+d' }] }.each do |name, (request_path, expected)| define_method("test_recognize_#{name}") do - path = Path::Pattern.new '/:segment/*splat' + path = Path::Pattern.from_string '/:segment/*splat' app = Object.new route = @router.routes.add_route(app, path, {}, {}, {}) env = rails_env 'PATH_INFO' => request_path called = false - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| assert_equal route, r assert_equal(expected, params) called = true @@ -454,7 +471,7 @@ module ActionDispatch end def test_namespaced_controller - strexp = Router::Strexp.new( + strexp = Router::Strexp.build( "/:controller(/:action(/:id))", { :controller => /.+?/ }, ["/", ".", "?"] @@ -471,7 +488,7 @@ module ActionDispatch :id => '10' } - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| assert_equal route, r assert_equal(expected, params) called = true @@ -480,14 +497,14 @@ module ActionDispatch end def test_recognize_literal - path = Path::Pattern.new "/books(/:action(.:format))" + path = Path::Pattern.from_string "/books(/:action(.:format))" app = Object.new route = @router.routes.add_route(app, path, {}, {:controller => 'books'}) env = rails_env 'PATH_INFO' => '/books/list.rss' expected = { :controller => 'books', :action => 'list', :format => 'rss' } called = false - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| assert_equal route, r assert_equal(expected, params) called = true @@ -496,8 +513,27 @@ module ActionDispatch assert called end + def test_recognize_head_route + path = Path::Pattern.from_string "/books(/:action(.:format))" + app = Object.new + conditions = { request_method: 'HEAD' } + @router.routes.add_route(app, path, conditions, {}) + + env = rails_env( + 'PATH_INFO' => '/books/list.rss', + 'REQUEST_METHOD' => 'HEAD' + ) + + called = false + @router.recognize(env) do |r, params| + called = true + end + + assert called + end + def test_recognize_head_request_as_get_route - path = Path::Pattern.new "/books(/:action(.:format))" + path = Path::Pattern.from_string "/books(/:action(.:format))" app = Object.new conditions = { :request_method => 'GET' @@ -508,7 +544,7 @@ module ActionDispatch "REQUEST_METHOD" => "HEAD" called = false - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| called = true end @@ -516,23 +552,28 @@ module ActionDispatch end def test_recognize_cares_about_verbs - path = Path::Pattern.new "/books(/:action(.:format))" + path = Path::Pattern.from_string "/books(/:action(.:format))" app = Object.new - conditions = { - :request_method => 'GET' - } + conditions = { request_method: 'GET' } @router.routes.add_route(app, path, conditions, {}) + env = rails_env 'PATH_INFO' => '/books/list.rss', + "REQUEST_METHOD" => "POST" + + called = false + @router.recognize(env) do |r, params| + called = true + end + + assert_not called + conditions = conditions.dup conditions[:request_method] = 'POST' post = @router.routes.add_route(app, path, conditions, {}) - env = rails_env 'PATH_INFO' => '/books/list.rss', - "REQUEST_METHOD" => "POST" - called = false - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| assert_equal post, r called = true end @@ -544,15 +585,17 @@ module ActionDispatch def add_routes router, paths paths.each do |path| - path = Path::Pattern.new path + if String === path + path = Path::Pattern.from_string path + else + path = Path::Pattern.new path + end router.routes.add_route @app, path, {}, {}, {} end end - RailsEnv = Struct.new(:env) - - def rails_env env - RailsEnv.new rack_env env + def rails_env env, klass = ActionDispatch::Request + klass.new(rack_env(env)) end def rack_env env diff --git a/actionpack/test/journey/routes_test.rb b/actionpack/test/journey/routes_test.rb index 25e0321d31..a4efc82b8c 100644 --- a/actionpack/test/journey/routes_test.rb +++ b/actionpack/test/journey/routes_test.rb @@ -5,7 +5,7 @@ module ActionDispatch class TestRoutes < ActiveSupport::TestCase def test_clear routes = Routes.new - exp = Router::Strexp.new '/foo(/:id)', {}, ['/.?'] + exp = Router::Strexp.build '/foo(/:id)', {}, ['/.?'] path = Path::Pattern.new exp requirements = { :hello => /world/ } @@ -18,7 +18,7 @@ module ActionDispatch def test_ast routes = Routes.new - path = Path::Pattern.new '/hello' + path = Path::Pattern.from_string '/hello' routes.add_route nil, path, {}, {}, {} ast = routes.ast @@ -28,7 +28,7 @@ module ActionDispatch def test_simulator_changes routes = Routes.new - path = Path::Pattern.new '/hello' + path = Path::Pattern.from_string '/hello' routes.add_route nil, path, {}, {}, {} sim = routes.simulator @@ -40,8 +40,8 @@ module ActionDispatch #def add_route app, path, conditions, defaults, name = nil routes = Routes.new - one = Path::Pattern.new '/hello' - two = Path::Pattern.new '/aaron' + one = Path::Pattern.from_string '/hello' + two = Path::Pattern.from_string '/aaron' routes.add_route nil, one, {}, {}, 'aaron' routes.add_route nil, two, {}, {}, 'aaron' diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb index b8b51d86c2..ce9522d12a 100644 --- a/actionpack/test/lib/controller/fake_models.rb +++ b/actionpack/test/lib/controller/fake_models.rb @@ -28,30 +28,6 @@ class Customer < Struct.new(:name, :id) end end -class ValidatedCustomer < Customer - def errors - if name =~ /Sikachu/i - [] - else - [{:name => "is invalid"}] - end - end -end - -module Quiz - class Question < Struct.new(:name, :id) - extend ActiveModel::Naming - include ActiveModel::Conversion - - def persisted? - id.present? - end - end - - class Store < Question - end -end - class Post < Struct.new(:title, :author_name, :body, :secret, :persisted, :written_on, :cost) extend ActiveModel::Naming include ActiveModel::Conversion @@ -95,24 +71,3 @@ class Comment attr_accessor :body end - -module Blog - def self.use_relative_model_naming? - true - end - - class Post < Struct.new(:title, :id) - extend ActiveModel::Naming - include ActiveModel::Conversion - - def persisted? - id.present? - end - end -end - -class RenderJsonTestException < Exception - def as_json(options = nil) - { :error => self.class.name, :message => self.to_s } - end -end diff --git a/actionpack/test/routing/helper_test.rb b/actionpack/test/routing/helper_test.rb index 0028aaa629..09ca7ff73b 100644 --- a/actionpack/test/routing/helper_test.rb +++ b/actionpack/test/routing/helper_test.rb @@ -26,6 +26,20 @@ module ActionDispatch x.new.pond_duck_path Duck.new end end + + def test_path_deprecation + rs = ::ActionDispatch::Routing::RouteSet.new + rs.draw do + resources :ducks + end + + x = Class.new { + include rs.url_helpers(false) + } + assert_deprecated do + assert_equal '/ducks', x.new.ducks_path + end + end end end end |