The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Changes 19268
MANIFEST 35
META.json 58
META.yml 58
Makefile.PL 513
README.md 3929
examples/connect-proxy.pl 41
examples/entities.pl 51
examples/fast.pl 20
examples/hello-template.pl 010
examples/hello.pl 20
examples/microhttpd.pl 41
examples/websocket.pl 20
lib/Mojo/Asset/File.pm 1514
lib/Mojo/Asset/Memory.pm 011
lib/Mojo/Asset.pm 08
lib/Mojo/Base.pm 1618
lib/Mojo/ByteStream.pm 46
lib/Mojo/Cache.pm 25
lib/Mojo/Collection.pm 5677
lib/Mojo/Content.pm 5051
lib/Mojo/Cookie.pm 11
lib/Mojo/DOM/CSS.pm 167
lib/Mojo/DOM/HTML.pm 3621
lib/Mojo/DOM.pm 131154
lib/Mojo/Date.pm 1758
lib/Mojo/EventEmitter.pm 157
lib/Mojo/Exception.pm 11
lib/Mojo/Headers.pm 745
lib/Mojo/Home.pm 11
lib/Mojo/IOLoop/Client.pm 36134
lib/Mojo/IOLoop/Delay.pm 56
lib/Mojo/IOLoop/Server.pm 1611
lib/Mojo/IOLoop/Stream.pm 1413
lib/Mojo/IOLoop/certs/server.crt 021
lib/Mojo/IOLoop/certs/server.key 015
lib/Mojo/IOLoop/server.crt 210
lib/Mojo/IOLoop/server.key 150
lib/Mojo/IOLoop.pm 45
lib/Mojo/JSON/Pointer.pm 2123
lib/Mojo/JSON.pm 7766
lib/Mojo/Loader.pm 11
lib/Mojo/Log.pm 85
lib/Mojo/Message/Request.pm 2535
lib/Mojo/Message/Response.pm 67
lib/Mojo/Message.pm 3242
lib/Mojo/Parameters.pm 4152
lib/Mojo/Path.pm 1910
lib/Mojo/Reactor/EV.pm 99
lib/Mojo/Reactor/Poll.pm 35
lib/Mojo/Reactor.pm 103
lib/Mojo/Server/CGI.pm 43
lib/Mojo/Server/Daemon.pm 1010
lib/Mojo/Server/Hypnotoad.pm 1111
lib/Mojo/Server/Morbo.pm 1011
lib/Mojo/Server/PSGI.pm 32
lib/Mojo/Server/Prefork.pm 910
lib/Mojo/Server.pm 129
lib/Mojo/Template.pm 9983
lib/Mojo/Transaction/WebSocket.pm 5241
lib/Mojo/Transaction.pm 1416
lib/Mojo/URL.pm 96
lib/Mojo/UserAgent/CookieJar.pm 012
lib/Mojo/UserAgent/Proxy.pm 55
lib/Mojo/UserAgent/Server.pm 22
lib/Mojo/UserAgent/Transactor.pm 2969
lib/Mojo/UserAgent.pm 120127
lib/Mojo/Util.pm 242302
lib/Mojo/entities.txt 22310
lib/Mojo.pm 468
lib/Mojolicious/Command/cgi.pm 32
lib/Mojolicious/Command/cpanify.pm 11
lib/Mojolicious/Command/daemon.pm 01
lib/Mojolicious/Command/generate/app.pm 6160
lib/Mojolicious/Command/generate/lite_app.pm 3130
lib/Mojolicious/Command/generate/makefile.pm 1514
lib/Mojolicious/Command/generate/plugin.pm 5958
lib/Mojolicious/Command/get.pm 74
lib/Mojolicious/Command/inflate.pm 11
lib/Mojolicious/Command/prefork.pm 01
lib/Mojolicious/Command/routes.pm 96
lib/Mojolicious/Command/test.pm 186
lib/Mojolicious/Command/version.pm 1413
lib/Mojolicious/Command.pm 54
lib/Mojolicious/Commands.pm 1313
lib/Mojolicious/Controller.pm 151149
lib/Mojolicious/Guides/Contributing.pod 122
lib/Mojolicious/Guides/Cookbook.pod 38144
lib/Mojolicious/Guides/FAQ.pod 89
lib/Mojolicious/Guides/Growing.pod 51136
lib/Mojolicious/Guides/Rendering.pod 112227
lib/Mojolicious/Guides/Routing.pod 279245
lib/Mojolicious/Guides.pod 915
lib/Mojolicious/Lite.pm 2949
lib/Mojolicious/Plugin/Config.pm 11
lib/Mojolicious/Plugin/DefaultHelpers.pm 30162
lib/Mojolicious/Plugin/EPLRenderer.pm 137
lib/Mojolicious/Plugin/EPRenderer.pm 2326
lib/Mojolicious/Plugin/JSONConfig.pm 106
lib/Mojolicious/Plugin/Mount.pm 31
lib/Mojolicious/Plugin/PODRenderer.pm 3823
lib/Mojolicious/Plugin/TagHelpers.pm 1716
lib/Mojolicious/Plugins.pm 117
lib/Mojolicious/Renderer.pm 3759
lib/Mojolicious/Routes/Match.pm 3530
lib/Mojolicious/Routes/Pattern.pm 4845
lib/Mojolicious/Routes/Route.pm 7353
lib/Mojolicious/Routes.pm 2213
lib/Mojolicious/Sessions.pm 01
lib/Mojolicious/Static.pm 4898
lib/Mojolicious/Validator/Validation.pm 1535
lib/Mojolicious/templates/development.html.ep 99
lib/Mojolicious/templates/mojobar.html.ep 55
lib/Mojolicious/templates/perldoc.html.ep 411
lib/Mojolicious.pm 3458
lib/Test/Mojo.pm 3646
lib/ojo.pm 1425
script/hypnotoad 96
script/mojo 30
script/morbo 97
t/mojo/asset.t 1211
t/mojo/base.t 06
t/mojo/bytestream.t 116
t/mojo/cache.t 29
t/mojo/collection.t 6349
t/mojo/content.t 11
t/mojo/cookiejar.t 22
t/mojo/daemon.t 1749
t/mojo/date.t 035
t/mojo/delay.t 41
t/mojo/dom.t 126108
t/mojo/eventemitter.t 3111
t/mojo/headers.t 713
t/mojo/hypnotoad.t 41
t/mojo/ioloop.t 41
t/mojo/ioloop_ipv6.t 20
t/mojo/ioloop_tls.t 41
t/mojo/json.t 5465
t/mojo/json_pointer.t 2224
t/mojo/lib/myapp.pl 05
t/mojo/morbo.t 41
t/mojo/parameters.t 1115
t/mojo/path.t 01
t/mojo/prefork.t 41
t/mojo/reactor_ev.t 109
t/mojo/reactor_poll.t 1210
t/mojo/request.t 56
t/mojo/response.t 1127
t/mojo/template.t 1543
t/mojo/transactor.t 1051
t/mojo/user_agent.t 1532
t/mojo/user_agent_online.t 54
t/mojo/user_agent_socks.t 0169
t/mojo/user_agent_tls.t 41
t/mojo/util.t 319
t/mojo/websocket.t 74
t/mojo/websocket_frames.t 215
t/mojo/websocket_proxy.t 41
t/mojo/websocket_proxy_tls.t 626
t/mojolicious/app.t 4475
t/mojolicious/charset_lite_app.t 41
t/mojolicious/commands.t 118
t/mojolicious/dispatch.t 45
t/mojolicious/dispatcher_lite_app.t 41
t/mojolicious/embedded_app.t 10
t/mojolicious/embedded_lite_app.t 33
t/mojolicious/exception_lite_app.t 10
t/mojolicious/external/myapp.pl 11
t/mojolicious/external_app.t 10
t/mojolicious/external_lite_app.t 21
t/mojolicious/group_lite_app.t 1814
t/mojolicious/json_config_lite_app.t 36
t/mojolicious/json_config_mode_lite_app.t 10
t/mojolicious/layouted_lite_app.t 41
t/mojolicious/lib/SingleFileTestApp.pm 52
t/mojolicious/lite_app.t 4029
t/mojolicious/longpolling_lite_app.t 52
t/mojolicious/multipath_lite_app.t 41
t/mojolicious/ojo.t 114
t/mojolicious/pattern.t 318
t/mojolicious/pod_renderer_lite_app.t 97
t/mojolicious/production_app.t 14
t/mojolicious/rebased_lite_app.t 41
t/mojolicious/renderer.t 057
t/mojolicious/restful_lite_app.t 41
t/mojolicious/routes.t 393
t/mojolicious/static_lite_app.t 6103
t/mojolicious/tag_helper_lite_app.t 65
t/mojolicious/testing_app.t 10
t/mojolicious/tls_lite_app.t 41
t/mojolicious/twinkle_lite_app.t 74
t/mojolicious/upload_lite_app.t 52
t/mojolicious/upload_stream_lite_app.t 41
t/mojolicious/validation_lite_app.t 46
t/mojolicious/websocket_lite_app.t 810
t/pod_coverage.t 27
196 files changed (This is a version diff) 57017286
@@ -1,4 +1,254 @@
 
+5.62  2014-11-18
+  - Fixed bug in Mojolicious::Routes::Pattern where optional placeholders in
+    nested routes would sometimes not work correctly.
+  - Fixed bug where "handler" was not an allowed name for controller methods.
+
+5.61  2014-11-14
+  - Moved entities.txt into the DATA section of Mojo::Util to avoid
+    gratuitously breaking module bundlers.
+
+5.60  2014-11-11
+  - Added to_array method to Mojo::Collection.
+  - Added xss_escape function to Mojo::Util.
+  - Updated Net::DNS::Native requirement to 0.12 for some important bug fixes.
+
+5.59  2014-11-07
+  - Added support for non-blocking name resolution with Net::DNS::Native.
+
+5.58  2014-11-06
+  - Improved error handling in Mojo::IOLoop::Client.
+
+5.57  2014-11-02
+  - Deprecated stringification support in Mojo::Collection in favor of
+    Mojo::Collection::join.
+  - Deprecated Mojo::Collection::pluck in favor of Mojo::Collection::map.
+  - Deprecated Mojo::DOM::val.
+  - Improved map method in Mojo::Collection to be able to call methods.
+  - Improved tap method in Mojo::Base to be able to call methods.
+
+5.56  2014-10-29
+  - Deprecated Mojo::Collection::AUTOLOAD in favor of Mojo::Collection::pluck.
+  - Deprecated Mojo::DOM::AUTOLOAD in favor of Mojo::DOM::children.
+
+5.55  2014-10-28
+  - Deprecated support for data arguments in Mojo::JSON::Pointer.
+  - Added access_control_allow_origin, content_language, content_location and
+    strict_transport_security methods to Mojo::Headers.
+
+5.54  2014-10-23
+  - Deprecated object-oriented Mojo::JSON API.
+  - Added auto_decompress attribute to Mojo::Content.
+  - Improved Mojo::Content to parse content more defensively.
+  - Fixed chunked transfer encoding bug in Mojo::Content.
+  - Fixed bug where Mojo::UserAgent would try to follow redirects for
+    protocols other than HTTP and HTTPS.
+
+5.53  2014-10-20
+  - Fixed bug in Mojo::Server where secondary groups were not reassigned
+    correctly. (ksm, sri)
+
+5.52  2014-10-18
+  - Fixed read-only file system compatibility of Mojo::Asset::File.
+
+5.51  2014-10-17
+  - Fixed bug in Mojolicious::Validator::Validation where every_param would
+    sometimes return an array reference containing an undef value.
+  - Fixed Mojo::ByteStream and Mojo::Collection to always return true in
+    boolean context.
+
+5.50  2014-10-15
+  - Improved Mojo::DOM::HTML performance slightly.
+  - Fixed description list parsing bug in Mojo::DOM::HTML. (Trelane)
+
+5.49  2014-10-10
+  - Improved form content generator to allow custom content types.
+  - Improved Mojo::Server to load applications consistently for all servers.
+    (tianon, sri)
+  - Fixed Mojolicious::Static to hide files without extensions in DATA
+    sections.
+  - Fixed inflate command to ignore files without extensions.
+  - Fixed bug in Mojolicious::Routes::Route where formats could be rendered
+    twice for embedded applications.
+
+5.48  2014-10-07
+  - Emergency release for a serious security issue that can result in
+    parameter injection attacks, everybody should update!
+    Breaking change: Methods that previously worked differently in scalar than
+    in list context now always assume scalar context, and new methods have
+    been added to cover the list context functionality.
+  - Added every_cookie and every_upload methods to Mojo::Message.
+  - Added every_param method to Mojo::Message::Request.
+  - Added every_param method to Mojo::Parameters.
+  - Added every_cookie, every_param and every_signed_cookie methods to
+    Mojolicious::Controller.
+  - Added every_param method to Mojolicious::Validator::Validation.
+  - Added from_json and to_json functions to Mojo::JSON.
+  - Improved pluck method in Mojo::Collection to be able to extract values
+    from hash references.
+
+5.47  2014-09-28
+  - Improved url_for performance.
+
+5.46  2014-09-26
+  - PAUSE lost the previous release.
+
+5.45  2014-09-26
+  - Deprecated Mojolicious::Routes::Route::has_conditions.
+  - Added extracting attribute to Mojo::UserAgent::CookieJar.
+  - Improved performance of next, next_sibling, previous and previous_sibling
+    methods in Mojo::DOM significantly.
+  - Improved Mojo::Cache to allow caching to be disabled. (mvgrimes, sri)
+  - Fixed url_for bug where deeply nested WebSocket routes would not work
+    correctly.
+
+5.44  2014-09-23
+  - Fixed bug in Mojolicious::Renderer that prevented proxy objects from being
+    reused.
+
+5.43  2014-09-22
+  - Updated Makefile.PL for version 2 of the CPAN distribution metadata
+    specification.
+  - Improved get command to not depend on Content-Type headers for
+    differentiating between JSON and HTML/XML.
+
+5.42  2014-09-17
+  - Fixed url_for bug where an unnecessary slash could be rendered before
+    formats.
+
+5.41  2014-09-13
+  - Deprecated Mojolicious::Controller::render_static in favor of
+    reply->static helper.
+  - Added mtime attribute to Mojo::Asset::Memory.
+  - Added mtime method to Mojo::Asset and Mojo::Asset::File.
+  - Added reply->asset and reply->static helpers to
+    Mojolicious::Plugin::DefaultHelpers.
+  - Fixed bug in Mojo::UserAgent where connections would sometimes not get
+    closed correctly.
+
+5.40  2014-09-12
+  - Deprecated Mojo::EventEmitter::emit_safe.
+  - Added reply->exception and reply->not_found helpers to
+    Mojolicious::Plugin::DefaultHelpers.
+  - Improved all events to handle exceptions the same.
+
+5.39  2014-09-07
+  - Improved decamelize performance.
+  - Fixed bug in Mojo::Template where newline characters could get lost.
+
+5.38  2014-09-05
+  - Improved routes command to use new terminology for flags.
+  - Fixed bug in Mojo::Util where tablify could not handle empty columns.
+
+5.37  2014-09-03
+  - Improved Mojo::Template performance slightly.
+  - Fixed .ep template bug where the stash value "c" could no longer be used.
+
+5.36  2014-09-02
+  - Improved Mojo::Template performance.
+
+5.35  2014-08-30
+  - Improved monkey_patch to be able to name generated functions.
+
+5.34  2014-08-29
+  - Added original_remote_address attribute to Mojo::Transaction.
+  - Fixed bug where Mojolicious::Commands would change @ARGV when loaded.
+
+5.33  2014-08-24
+  - Improved Mojo::Date to be able to handle higher precision times.
+  - Improved Mojo::ByteStream performance.
+
+5.32  2014-08-21
+  - Added to_datetime method to Mojo::Date.
+  - Improved Mojo::Date to support RFC 3339.
+
+5.31  2014-08-19
+  - Improved Mojolicious::Static to allow custom content types.
+  - Improved url_for performance.
+
+5.30  2014-08-17
+  - Improved Mojolicious::Static to only handle GET and HEAD requests.
+  - Improved Mojo::URL performance.
+  - Improved url_for performance slightly.
+  - Fixed bug where DATA sections sometimes got corrupted after forking, which
+    caused applications to fail randomly.
+  - Fixed Mojo::IOLoop::Client to use a timeout for every connection.
+
+5.29  2014-08-16
+  - Added helpers method to Mojolicious::Controller.
+  - Improved performance of .ep templates slightly.
+  - Fixed "0" value bug in Mojolicious::Plugin::EPRenderer.
+
+5.28  2014-08-13
+  - Improved performance of nested helpers and helpers in templates
+    significantly.
+  - Improved Mojo::JSON to generate smaller JSON by not escaping the "/"
+    character.
+
+5.27  2014-08-11
+  - Added support for nested helpers.
+  - Added get_helper method to Mojolicious::Renderer.
+  - Added n function to ojo.
+  - Fixed bug in Mojolicious::Routes::Match where placeholder values got
+    merged too early.
+
+5.26  2014-08-09
+  - Improved WebSocket performance.
+  - Fixed proxy exception handling bug in Mojo::UserAgent.
+  - Fixed bug where Mojo::Transaction::WebSocket would build incorrect frames
+    if the FIN bit was not set.
+
+5.25  2014-08-07
+  - Added reduce method to Mojo::Collection. (sri, batman)
+  - Added if_none_match method to Mojo::Headers.
+  - Added is_fresh method to Mojolicious::Static.
+  - Added is_fresh helper to Mojolicious::Plugin::DefaultHelpers.
+  - Improved Mojolicious to use MyApp::Controller namespace by default and
+    encourage its use in the documentation.
+  - Improved sort method in Mojo::Collection to use $a and $b. (batman)
+  - Improved Mojolicious::Static to support ETag and If-None-Match headers.
+  - Improved documentation browser CSS.
+  - Fixed escaping bugs in Mojo::DOM::CSS.
+
+5.24  2014-08-02
+  - Improved url_escape performance slightly.
+  - Fixed memory leak in Mojo::IOLoop::Client.
+  - Fixed bug where ojo would sometimes die silently.
+
+5.23  2014-07-31
+  - Improved router performance.
+  - Improved routes command to show format regular expression separately.
+  - Fixed partial route bug in Mojolicious::Routes::Match.
+  - Fixed format detection bug in Mojolicious::Routes::Pattern.
+
+5.22  2014-07-30
+  - Added SOCKS5 support to Mojo::UserAgent.
+  - Added socks_address, socks_pass, socks_port and socks_user options to
+    Mojo::IOLoop::Client::connect.
+  - Improved documentation browser CSS.
+
+5.21  2014-07-27
+  - Improved handling of Pod::Simple::XHTML 3.09 dependency.
+  - Improved documentation browser CSS.
+
+5.20  2014-07-27
+  - Fixed a few bugs in Mojolicious::Plugin::PODRenderer by switching from
+    Pod::Simple::HTML to Pod::Simple::XHTML.
+  - Fixed Perl 5.18.x compatibility.
+
+5.19  2014-07-26
+  - Improved support for Unicode anchors in Mojolicious::Plugin::PODRenderer.
+  - Fixed is_readable scalability problems in Mojo::Reactor.
+
+5.18  2014-07-25
+  - Improved is_readable performance in Mojo::Reactor.
+
+5.17  2014-07-24
+  - Welcome to the Mojolicious core team Jan Henning Thorsen.
+  - Added val method to Mojo::DOM. (batman, sri)
+  - Improved Mojo::Collection performance.
+  - Fixed support for Unicode anchors in Mojolicious::Plugin::PODRenderer.
+
 5.16  2014-07-21
   - Improved Mojo::Asset::File to allow appending data to existing files.
     (iakuf, sri)
@@ -91,7 +341,7 @@
     invocant.
   - Changed return value of path_for method in Mojolicious::Routes::Match.
   - Changed return value and arguments of error method in Mojo::Message.
-  - Removed deprecated support for "X-Forwarded-HTTPS".
+  - Removed deprecated support for X-Forwarded-HTTPS.
   - Removed return values from wait method in Mojo::IOLoop::Delay.
   - Removed list context support from header method in Mojo::Headers.
   - Removed generate_port method from Mojo::IOLoop.
@@ -129,8 +379,7 @@
   - Improved accept performance in Mojo::IOLoop::Server.
 
 4.97  2014-04-30
-  - Deprecated support for "X-Forwarded-HTTPS" in favor of
-    "X-Forwarded-Proto".
+  - Deprecated support for X-Forwarded-HTTPS in favor of X-Forwarded-Proto.
   - Added multi-name support to param method in Mojo::Parameters.
 
 4.96  2014-04-28
@@ -312,7 +561,7 @@
   - Improved router to allow format detection for bridges.
 
 4.68  2014-01-22
-  - Added Mojo::DOM::Node.
+  - Added module Mojo::DOM::Node.
   - Added contents and node methods to Mojo::DOM.
   - Removed deprecated http_proxy, https_proxy, name and no_proxy attributes
     from Mojo::UserAgent.
@@ -1384,7 +1633,7 @@
 
 2.93  2012-05-05
   - Added remove method to Mojolicious::Routes::Route.
-  - Improved 32bit Perl support of Mojo::Transaction::WebSocket.
+  - Improved 32-bit Perl support of Mojo::Transaction::WebSocket.
     (mikemagowan, sri)
   - Improved exception handling of application and configuration file loaders.
   - Improved exception handling of Mojolicious::Plugin::I18N.
@@ -1539,7 +1788,7 @@
 2.65  2012-03-22
   - Deprecated Mojo::IOLoop::drop in favor of Mojo::IOLoop::remove.
   - Renamed Mojo::Reactor::drop to Mojo::Reactor::remove.
-  - Added Mojo::Reactor::Poll.
+  - Added module Mojo::Reactor::Poll.
   - Added one_tick method to Mojo::Reactor and Mojo::Reactor::EV.
   - Removed experimental status from Mojo::IOLoop::Client.
   - Removed experimental status from Mojo::IOLoop::Server.
@@ -1559,7 +1808,7 @@
 
 2.63  2012-03-20
   - Renamed Mojo::IOWatcher to Mojo::Reactor.
-  - Added Mojolicious::Routes::Route.
+  - Added module Mojolicious::Routes::Route.
   - Added find and root methods to Mojolicious::Routes::Route.
   - Improved form_for helper to automatically add method="POST" attributes
     when necessary.
@@ -2323,7 +2572,7 @@
   - Modernized Mojo::HelloWorld.
   - Improved HTML healing capabilities of Mojo::DOM::HTML.
   - Improved HTML rendering in Mojo::DOM::HTML.
-  - Improved 64bit support in Mojo::Transaction::WebSocket.
+  - Improved 64-bit support in Mojo::Transaction::WebSocket.
   - Fixed memory leak in Mojo::IOLoop::Client.
   - Fixed memory leak in Mojolicious.
   - Fixed small bug in Mojo::IOLoop::Server.
@@ -2568,7 +2817,7 @@
     available.
   - Updated jQuery to version 1.6.
   - Fixed PSGI read error handling.
-  - Fixed 64bit WebSocket message bug.
+  - Fixed 64-bit WebSocket message bug.
   - Fixed small Windows bug.
 
 1.22  2011-05-02
@@ -2601,7 +2850,7 @@
   - Fixed size limits in message parser.
 
 1.18  2011-04-19
-  - Added support for "X-Forwarded-HTTPS" and "X-Forwarded-Host" headers.
+  - Added support for X-Forwarded-HTTPS and X-Forwarded-Host headers.
   - Added argument localization to the include helper. (crab, moritz, sri)
   - Fixed test case.
 
@@ -3131,7 +3380,7 @@
       print $client->get('http://search.cpan.org')->res->body;
       my $tx = $client->post_form('http://kraih.com', {q => 'mojo'});
   - Made plugins much more configurable.
-  - Improved PSGI support and added "psgi" command.
+  - Improved PSGI support and added psgi command.
   - Added automatic environment detection for Plack based servers, there is
     no technical way to detect all PSGI compliant servers yet though. That
     means "plackup myapp.pl" and "plackup script/myapp" should just work.
@@ -3158,8 +3407,8 @@
   - Added I18N support. (vti, memowe)
   - Added template detection again, you can now just mix multiple template
     engines and the renderer will automatically pick the right template. So
-    you don't have to use the "handler" argument anymore, even though it's
-    still available and overrides auto detection.
+    you don't have to use the handler argument anymore, even though it's still
+    available and overrides auto detection.
   - Added Flash Policy Server example. (xantus)
   - Added more reference docs.
   - Added ".gitignore" generator command. (marcus)
@@ -3440,8 +3689,8 @@
   - Merged eplite and epl, this change is not backwards compatible, you will
     have to rename all your eplite templates to epl.
   - Simplified MojoX::Renderer, this change is not backwards compatible!
-    Handler can no longer be detected, that means "default_handler" or the
-    "handler" argument are required. The template argument can no longer
+    Handler can no longer be detected, that means default_handler or the
+    handler argument are required. The template argument can no longer
     contain format or handler.
       $self->render(template => 'foo.html.epl');
     becomes
@@ -3643,7 +3892,7 @@
   - Added layout support to MojoX::Renderer.
   - Made render call optional.
   - Added format support to MojoX::Routes.
-  - Added Mojo::Loader::Exception.
+  - Added module Mojo::Loader::Exception.
   - Added wildcard symbol support to MojoX::Routes and rewrote many routes
     internals.
   - Added Makefile.PL generator.
@@ -3654,7 +3903,7 @@
   - Added encoding support to Mojo::Template and made "utf8" the default.
   - Added HEAD support to Mojo::Server::Daemon. (acajou)
   - Added new relaxed placeholder to MojoX::Routes::Pattern.
-  - Added Mojo::Template::Exception.
+  - Added module Mojo::Template::Exception.
   - Added HEAD support to the Mojo::Transaction state machine and related
     modules. (acajou)
   - Added safe_post option to Mojo::Pipeline. (acajou)
@@ -3695,10 +3944,10 @@
 
 0.9  2008-12-01
   - Added modes to Mojolicious.
-  - Added Mojo::Log and log support for Mojo/Mojolicious.
+  - Added module Mojo::Log and log support for Mojo/Mojolicious.
+  - Added module MojoX::Context.
   - Changed MojoX::Renderer and Mojo::Template API to make catching errors
     easier, we now use a scalar ref for results like most template engines.
-  - Added MojoX::Context.
   - Added multi-level controller class support to Mojolicious.
   - MojoX::Dispatcher::Routes should be able to fail.
   - Added diagnostics functions to Mojo::HelloWorld.
@@ -4,6 +4,7 @@ CONTRIBUTING.md
 examples/connect-proxy.pl
 examples/entities.pl
 examples/fast.pl
+examples/hello-template.pl
 examples/hello.pl
 examples/microhttpd.pl
 examples/websocket.pl
@@ -25,17 +26,16 @@ lib/Mojo/Date.pm
 lib/Mojo/DOM.pm
 lib/Mojo/DOM/CSS.pm
 lib/Mojo/DOM/HTML.pm
-lib/Mojo/entities.txt
 lib/Mojo/EventEmitter.pm
 lib/Mojo/Exception.pm
 lib/Mojo/Headers.pm
 lib/Mojo/HelloWorld.pm
 lib/Mojo/Home.pm
 lib/Mojo/IOLoop.pm
+lib/Mojo/IOLoop/certs/server.crt
+lib/Mojo/IOLoop/certs/server.key
 lib/Mojo/IOLoop/Client.pm
 lib/Mojo/IOLoop/Delay.pm
-lib/Mojo/IOLoop/server.crt
-lib/Mojo/IOLoop/server.key
 lib/Mojo/IOLoop/Server.pm
 lib/Mojo/IOLoop/Stream.pm
 lib/Mojo/JSON.pm
@@ -216,6 +216,7 @@ t/mojo/lib/Mojo/LoaderException2.pm
 t/mojo/lib/Mojo/LoaderTest/A.pm
 t/mojo/lib/Mojo/LoaderTest/B.pm
 t/mojo/lib/Mojo/LoaderTest/C.pm
+t/mojo/lib/myapp.pl
 t/mojo/loader.t
 t/mojo/log.t
 t/mojo/morbo.t
@@ -237,6 +238,7 @@ t/mojo/transactor.t
 t/mojo/url.t
 t/mojo/user_agent.t
 t/mojo/user_agent_online.t
+t/mojo/user_agent_socks.t
 t/mojo/user_agent_tls.t
 t/mojo/util.t
 t/mojo/websocket.t
@@ -4,7 +4,7 @@
       "Sebastian Riedel <sri@cpan.org>"
    ],
    "dynamic_config" : 1,
-   "generated_by" : "ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.141520",
+   "generated_by" : "ExtUtils::MakeMaker version 7.02, CPAN::Meta::Converter version 2.142690",
    "license" : [
       "artistic_2"
    ],
@@ -33,6 +33,9 @@
       },
       "runtime" : {
          "requires" : {
+            "IO::Socket::IP" : "0.26",
+            "Pod::Simple" : "3.09",
+            "Time::Local" : "1.2",
             "perl" : "5.010001"
          }
       }
@@ -40,16 +43,16 @@
    "release_status" : "stable",
    "resources" : {
       "bugtracker" : {
-         "web" : "http://github.com/kraih/mojo/issues"
+         "web" : "https://github.com/kraih/mojo/issues"
       },
       "homepage" : "http://mojolicio.us",
       "license" : [
          "http://www.opensource.org/licenses/artistic-license-2.0"
       ],
       "repository" : {
-         "url" : "http://github.com/kraih/mojo"
+         "url" : "https://github.com/kraih/mojo.git"
       },
-      "x_MailingList" : "http://groups.google.com/group/mojolicious"
+      "x_IRC" : "irc://irc.perl.org/#mojo"
    },
-   "version" : "5.16"
+   "version" : "5.62"
 }
@@ -7,7 +7,7 @@ build_requires:
 configure_requires:
   ExtUtils::MakeMaker: '0'
 dynamic_config: 1
-generated_by: 'ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.141520'
+generated_by: 'ExtUtils::MakeMaker version 7.02, CPAN::Meta::Converter version 2.142690'
 license: artistic_2
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -19,11 +19,14 @@ no_index:
     - inc
     - t
 requires:
+  IO::Socket::IP: '0.26'
+  Pod::Simple: '3.09'
+  Time::Local: '1.2'
   perl: '5.010001'
 resources:
-  MailingList: http://groups.google.com/group/mojolicious
-  bugtracker: http://github.com/kraih/mojo/issues
+  IRC: irc://irc.perl.org/#mojo
+  bugtracker: https://github.com/kraih/mojo/issues
   homepage: http://mojolicio.us
   license: http://www.opensource.org/licenses/artistic-license-2.0
-  repository: http://github.com/kraih/mojo
-version: '5.16'
+  repository: https://github.com/kraih/mojo.git
+version: '5.62'
@@ -5,6 +5,9 @@ use warnings;
 
 use ExtUtils::MakeMaker;
 
+# Pod::Simple 3.09 first shipped with Perl 5.11.2
+# Time::Local 1.2 first shipped with Perl 5.13.9
+# IO::Socket::IP 0.26 first shipped with Perl 5.19.8
 WriteMakefile(
   NAME         => 'Mojolicious',
   VERSION_FROM => 'lib/Mojolicious.pm',
@@ -14,14 +17,19 @@ WriteMakefile(
   META_MERGE   => {
     requires  => {perl => '5.010001'},
     resources => {
-      homepage    => 'http://mojolicio.us',
-      license     => 'http://www.opensource.org/licenses/artistic-license-2.0',
-      MailingList => 'http://groups.google.com/group/mojolicious',
-      repository  => 'http://github.com/kraih/mojo',
-      bugtracker  => 'http://github.com/kraih/mojo/issues'
+      license    => 'http://www.opensource.org/licenses/artistic-license-2.0',
+      homepage   => 'http://mojolicio.us',
+      bugtracker => 'https://github.com/kraih/mojo/issues',
+      repository => 'https://github.com/kraih/mojo.git',
+      x_IRC      => 'irc://irc.perl.org/#mojo'
     },
     no_index => {directory => ['t']}
   },
+  PREREQ_PM => {
+    'IO::Socket::IP' => '0.26',
+    'Pod::Simple'    => '3.09',
+    'Time::Local'    => '1.2'
+  },
   EXE_FILES => ['script/hypnotoad', 'script/mojo', 'script/morbo'],
   test => {TESTS => 't/*.t t/*/*.t'}
 );
@@ -2,7 +2,7 @@
 # Mojolicious [![Build Status](https://travis-ci.org/kraih/mojo.svg?branch=master)](https://travis-ci.org/kraih/mojo)
 
   Back in the early days of the web, many people learned Perl because of a
-  wonderful Perl library called [CGI](http://metacpan.org/module/CGI). It was
+  wonderful Perl library called [CGI](https://metacpan.org/module/CGI). It was
   simple enough to get started without knowing much about the language and
   powerful enough to keep you going, learning by doing was much fun. While
   most of the techniques used are outdated now, the idea behind it is not.
@@ -12,19 +12,17 @@
 ## Features
 
   * An amazing real-time web framework, allowing you to easily grow single
-    file [Mojolicious::Lite](http://mojolicio.us/perldoc/Mojolicious/Lite)
-    prototypes into well structured web applications.
+    file prototypes into well-structured web applications.
     * Powerful out of the box with RESTful routes, plugins, commands, Perl-ish
       templates, content negotiation, session management, form validation,
       testing framework, static file server, first class Unicode support and
       much more for you to discover.
-  * Very clean, portable and Object Oriented pure-Perl API without any hidden
-    magic and no requirements besides Perl 5.10.1 (although 5.18+ is
-    recommended, and optional CPAN modules will be used to provide advanced
-    functionality if they are installed).
+  * Very clean, portable and object-oriented pure-Perl API with no hidden
+    magic and no requirements besides Perl 5.20.0 (versions as old as 5.10.1
+    can be used too, but may require additional CPAN modules to be installed)
   * Full stack HTTP and WebSocket client/server implementation with IPv6, TLS,
-    SNI, IDNA, Comet (long polling), keep-alive, connection pooling, timeout,
-    cookie, multipart, proxy, and gzip compression support.
+    SNI, IDNA, HTTP/SOCKS5 proxy, Comet (long polling), keep-alive, connection
+    pooling, timeout, cookie, multipart, and gzip compression support.
   * Built-in non-blocking I/O web server, supporting multiple event loops as
     well as optional preforking and hot deployment, perfect for embedding.
   * Automatic CGI and [PSGI](http://plackperl.org) detection.
@@ -36,7 +34,7 @@
 
   All you need is a one-liner, it takes less than a minute.
 
-    $ curl get.mojolicio.us | sh
+    $ curl -L https://cpanmin.us | perl - -M https://cpan.metacpan.org -n Mojolicious
 
   We recommend the use of a [Perlbrew](http://perlbrew.pl) environment.
 
@@ -47,7 +45,7 @@
 ```perl
 use Mojolicious::Lite;
 
-get '/' => {text => 'Hello World!'};
+get '/' => {text => 'I ♥ Mojolicious!'};
 
 app->start;
 ```
@@ -59,50 +57,42 @@ app->start;
     Server available at http://127.0.0.1:3000.
 
     $ curl http://127.0.0.1:3000/
-    Hello World!
+    I ♥ Mojolicious!
 
 ## Duct tape for the HTML5 web
 
-  Web development for humans, making hard things possible and everything fun.
+  Use all the latest Perl and HTML features in beautiful single file
+  prototypes like this one, and grow them easily into well-structured
+  applications.
 
 ```perl
 use Mojolicious::Lite;
+use 5.20.0;
+use experimental 'signatures';
 
-# Simple plain text response
-get '/' => {text => 'I ♥ Mojolicious!'};
-
-# Route associating "/time" with template in DATA section
-get '/time' => 'clock';
+# Render template "index.html.ep" from the DATA section
+get '/' => {template => 'index'};
 
-# Scrape information from remote sites
-post '/title' => sub {
-  my $c     = shift;
-  my $url   = $c->param('url') || 'http://mojolicio.us';
-  my $title = $c->ua->get($url)->res->dom->at('title')->text;
-  $c->render(json => {url => $url, title => $title});
-};
-
-# WebSocket echo service
-websocket '/echo' => sub {
-  my $c = shift;
-  $c->on(message => sub {
-    my ($c, $msg) = @_;
-    $c->send("echo: $msg");
+# WebSocket service used by the template to extract the title from a web site
+websocket '/title' => sub ($c) {
+  $c->on(message => sub ($c, $msg) {
+    my $title = $c->ua->get($msg)->res->dom->at('title')->text;
+    $c->send($title);
   });
 };
 
 app->start;
 __DATA__
 
-@@ clock.html.ep
-% use Time::Piece;
-% my $now = localtime;
-The time is <%= $now->hms %>.
+@@ index.html.ep
+% my $url = url_for 'title';
+<script>
+  var ws = new WebSocket('<%= $url->to_abs %>');
+  ws.onmessage = function (event) { document.body.innerHTML += event.data };
+  ws.onopen    = function (event) { ws.send('http://mojolicio.us') };
+</script>
 ```
 
-  Single file prototypes like this one can easily grow into well-structured
-  applications.
-
 ## Want to know more?
 
   Take a look at our excellent [documentation](http://mojolicio.us/perldoc>)!
@@ -1,7 +1,4 @@
-use FindBin;
-use lib "$FindBin::Bin/../lib";
 use Mojo::Base -strict;
-
 use Mojo::IOLoop;
 
 # Minimal CONNECT proxy server to test TLS tunneling
@@ -76,7 +73,7 @@ Mojo::IOLoop->server(
       }
     );
   }
-) or die "Couldn't create listen socket!\n";
+);
 
 print <<'EOF';
 Starting CONNECT proxy on port 3000.
@@ -1,13 +1,9 @@
-use FindBin;
-use lib "$FindBin::Bin/../lib";
 use Mojo::Base -strict;
-
 use Mojo::ByteStream 'b';
 use Mojo::UserAgent;
 
 # Extract named character references from HTML spec
-my $tx = Mojo::UserAgent->new->get(
-  'http://www.whatwg.org/specs/web-apps/current-work/');
+my $tx = Mojo::UserAgent->new->get('https://html.spec.whatwg.org');
 b($_->at('td > code')->text . ' ' . $_->children('td')->[1]->text)->trim->say
   for $tx->res->dom('#named-character-references-table tbody > tr')->each;
 
@@ -1,5 +1,3 @@
-use FindBin;
-use lib "$FindBin::Bin/../lib";
 use Mojo::Base 'Mojolicious';
 
 sub handler {
@@ -0,0 +1,10 @@
+use Mojolicious::Lite;
+
+get '/hello';
+
+# Minimal "Hello World" application with template for profiling
+app->start;
+__DATA__
+
+@@ hello.html.ep
+Hello World!
@@ -1,5 +1,3 @@
-use FindBin;
-use lib "$FindBin::Bin/../lib";
 use Mojolicious::Lite;
 
 get '/' => {data => 'Hello World!'};
@@ -1,7 +1,4 @@
-use FindBin;
-use lib "$FindBin::Bin/../lib";
 use Mojo::Base -strict;
-
 use Mojo::IOLoop;
 
 # Minimal ioloop example demonstrating how to cheat at HTTP benchmarks :)
@@ -32,7 +29,7 @@ Mojo::IOLoop->server(
     );
     $stream->on(close => sub { delete $buffer{$id} });
   }
-) or die "Couldn't create listen socket!\n";
+);
 
 print <<'EOF';
 Starting server on port 8080.
@@ -1,5 +1,3 @@
-use FindBin;
-use lib "$FindBin::Bin/../lib";
 use Mojolicious::Lite;
 
 websocket '/test' => sub {
@@ -17,8 +17,7 @@ has handle => sub {
   my $handle = IO::File->new;
   my $path   = $self->path;
   if (defined $path && -f $path) {
-    $handle->open($path, -w _ ? O_APPEND | O_RDWR : O_RDONLY)
-      or croak qq{Can't open file "$path": $!};
+    $handle->open($path, O_RDONLY) or croak qq{Can't open file "$path": $!};
     return $handle;
   }
 
@@ -78,8 +77,7 @@ sub contains {
     # Search window
     my $pos = index $window, $str;
     return $offset + $pos if $pos >= 0;
-    $offset += $read;
-    return -1 if $read == 0 || $offset == $end;
+    return -1 if $read == 0 || ($offset += $read) == $end;
 
     # Resize window
     substr $window, 0, $read, '';
@@ -98,8 +96,7 @@ sub get_chunk {
 
   my $buffer;
   if (defined(my $end = $self->end_range)) {
-    my $chunk = $end + 1 - $offset;
-    return '' if $chunk <= 0;
+    return '' if (my $chunk = $end + 1 - $offset) <= 0;
     $handle->sysread($buffer, $chunk > $max ? $max : $chunk);
   }
   else { $handle->sysread($buffer, $max) }
@@ -122,17 +119,13 @@ sub move_to {
   return $self->path($to)->cleanup(0);
 }
 
-sub size {
-  return 0 unless defined(my $file = shift->path);
-  return -s $file;
-}
+sub mtime { (stat shift->handle)[9] }
+
+sub size { -s shift->handle }
 
 sub slurp {
-  my $handle = shift->handle;
-  $handle->sysseek(0, SEEK_SET);
-  my $content = '';
-  while ($handle->sysread(my $buffer, 131072)) { $content .= $buffer }
-  return $content;
+  return '' unless defined(my $file = shift->path);
+  return Mojo::Util::slurp $file;
 }
 
 1;
@@ -238,6 +231,12 @@ True.
 
 Move asset data into a specific file and disable L</"cleanup">.
 
+=head2 mtime
+
+  my $mtime = $file->mtime;
+
+Modification time of asset.
+
 =head2 size
 
   my $size = $file->size;
@@ -4,8 +4,12 @@ use Mojo::Base 'Mojo::Asset';
 use Mojo::Asset::File;
 use Mojo::Util 'spurt';
 
+# Last modified default
+my $MTIME = time;
+
 has 'auto_upgrade';
 has max_memory_size => sub { $ENV{MOJO_MAX_MEMORY_SIZE} || 262144 };
+has mtime => sub {$MTIME};
 
 sub add_chunk {
   my ($self, $chunk) = @_;
@@ -114,6 +118,13 @@ Maximum size in bytes of data to keep in memory before automatically upgrading
 to a L<Mojo::Asset::File> object, defaults to the value of the
 C<MOJO_MAX_MEMORY_SIZE> environment variable or C<262144> (256KB).
 
+=head2 mtime
+
+  my $mtime = $mem->mtime;
+  $mem      = $mem->mtime(1408567500);
+
+Modification time of asset, defaults to the time this class was loaded.
+
 =head1 METHODS
 
 L<Mojo::Asset::Memory> inherits all methods from L<Mojo::Asset> and implements
@@ -15,6 +15,7 @@ sub is_file {undef}
 sub is_range { !!($_[0]->end_range || $_[0]->start_range) }
 
 sub move_to { croak 'Method "move_to" not implemented by subclass' }
+sub mtime   { croak 'Method "mtime" not implemented by subclass' }
 sub size    { croak 'Method "size" not implemented by subclass' }
 sub slurp   { croak 'Method "slurp" not implemented by subclass' }
 
@@ -35,6 +36,7 @@ Mojo::Asset - HTTP content storage base class
   sub contains  {...}
   sub get_chunk {...}
   sub move_to   {...}
+  sub mtime     {...}
   sub size      {...}
   sub slurp     {...}
 
@@ -108,6 +110,12 @@ Check if asset has a L</"start_range"> or L</"end_range">.
 
 Move asset data into a specific file. Meant to be overloaded in a subclass.
 
+=head2 mtime
+
+  my $mtime = $asset->mtime;
+
+Modification time of asset. Meant to be overloaded in a subclass.
+
 =head2 size
 
   my $size = $asset->size;
@@ -70,11 +70,8 @@ sub attr {
       $code .= ref $default eq 'CODE' ? '$default->($_[0]);' : '$default;';
     }
 
-    # Store value
-    $code .= "\n  }\n  \$_[0]{'$attr'} = \$_[1];\n";
-
-    # Footer (return invocant)
-    $code .= "  \$_[0];\n}";
+    # Footer (store value and return invocant)
+    $code .= "\n  }\n  \$_[0]{'$attr'} = \$_[1];\n  \$_[0];\n}";
 
     warn "-- Attribute $attr in $class\n$code\n\n" if $ENV{MOJO_BASE_DEBUG};
     Carp::croak "Mojo::Base error: $@" unless eval "$code;1";
@@ -87,8 +84,8 @@ sub new {
 }
 
 sub tap {
-  my ($self, $cb) = @_;
-  $_->$cb for $self;
+  my ($self, $cb) = (shift, shift);
+  $_->$cb(@_) for $self;
   return $self;
 }
 
@@ -186,12 +183,12 @@ L<Mojo::Base> implements the following methods.
 =head2 attr
 
   $object->attr('name');
-  BaseSubClass->attr('name');
-  BaseSubClass->attr([qw(name1 name2 name3)]);
-  BaseSubClass->attr(name => 'foo');
-  BaseSubClass->attr(name => sub {...});
-  BaseSubClass->attr([qw(name1 name2 name3)] => 'foo');
-  BaseSubClass->attr([qw(name1 name2 name3)] => sub {...});
+  SubClass->attr('name');
+  SubClass->attr([qw(name1 name2 name3)]);
+  SubClass->attr(name => 'foo');
+  SubClass->attr(name => sub {...});
+  SubClass->attr([qw(name1 name2 name3)] => 'foo');
+  SubClass->attr([qw(name1 name2 name3)] => sub {...});
 
 Create attribute accessor for hash-based objects, an array reference can be
 used to create more than one at a time. Pass an optional second argument to
@@ -202,9 +199,9 @@ argument.
 
 =head2 new
 
-  my $object = BaseSubClass->new;
-  my $object = BaseSubClass->new(name => 'value');
-  my $object = BaseSubClass->new({name => 'value'});
+  my $object = SubClass->new;
+  my $object = SubClass->new(name => 'value');
+  my $object = SubClass->new({name => 'value'});
 
 This base class provides a basic constructor for hash-based objects. You can
 pass it either a hash or a hash reference with attribute values.
@@ -212,11 +209,16 @@ pass it either a hash or a hash reference with attribute values.
 =head2 tap
 
   $object = $object->tap(sub {...});
+  $object = $object->tap($method);
+  $object = $object->tap($method, @args);
 
 K combinator, tap into a method chain to perform operations on an object
 within the chain. The object will be the first argument passed to the callback
 and is also available as C<$_>.
 
+  # Longer version
+  $object = $object->tap(sub { $_->$method(@args) });
+
 =head1 DEBUGGING
 
 You can set the C<MOJO_BASE_DEBUG> environment variable to get some advanced
@@ -1,6 +1,6 @@
 package Mojo::ByteStream;
 use Mojo::Base -strict;
-use overload '""' => sub { shift->to_string }, fallback => 1;
+use overload bool => sub {1}, '""' => sub { ${$_[0]} }, fallback => 1;
 
 use Exporter 'import';
 use Mojo::Collection;
@@ -11,8 +11,8 @@ our @EXPORT_OK = ('b');
 # Turn most functions from Mojo::Util into methods
 my @UTILS = (
   qw(b64_decode b64_encode camelize decamelize hmac_sha1_sum html_unescape),
-  qw(md5_bytes md5_sum punycode_decode punycode_encode quote secure_compare),
-  qw(sha1_bytes sha1_sum slurp spurt squish trim unindent unquote url_escape),
+  qw(md5_bytes md5_sum punycode_decode punycode_encode quote sha1_bytes),
+  qw(sha1_sum slurp spurt squish trim unindent unquote url_escape),
   qw(url_unescape xml_escape xor_encode)
 );
 for my $name (@UTILS) {
@@ -43,6 +43,8 @@ sub say {
   return $self;
 }
 
+sub secure_compare { Mojo::Util::secure_compare ${shift()}, shift }
+
 sub size { length ${$_[0]} }
 
 sub split {
@@ -355,7 +357,7 @@ Always true.
 
   my $str = "$bytestream";
 
-Alias for L</to_string>.
+Alias for L</"to_string">.
 
 =head1 SEE ALSO
 
@@ -8,9 +8,11 @@ sub get { (shift->{cache} || {})->{shift()} }
 sub set {
   my ($self, $key, $value) = @_;
 
+  return $self unless (my $max = $self->max_keys) > 0;
+
   my $cache = $self->{cache} ||= {};
   my $queue = $self->{queue} ||= [];
-  delete $cache->{shift @$queue} while @$queue >= $self->max_keys;
+  delete $cache->{shift @$queue} while @$queue >= $max;
   push @$queue, $key unless exists $cache->{$key};
   $cache->{$key} = $value;
 
@@ -46,7 +48,8 @@ L<Mojo::Cache> implements the following attributes.
   my $max = $cache->max_keys;
   $cache  = $cache->max_keys(50);
 
-Maximum number of cache keys, defaults to C<100>.
+Maximum number of cache keys, defaults to C<100>. Setting the value to C<0>
+will disable caching.
 
 =head1 METHODS
 
@@ -1,32 +1,41 @@
 package Mojo::Collection;
 use Mojo::Base -strict;
-use overload
-  bool     => sub { !!@{shift()} },
-  '""'     => sub { shift->join("\n") },
-  fallback => 1;
 
 use Carp 'croak';
 use Exporter 'import';
 use List::Util;
 use Mojo::ByteStream;
+use Mojo::Util 'deprecated';
 use Scalar::Util 'blessed';
 
+# DEPRECATED in Tiger Face!
+use overload '""' => sub {
+  deprecated 'Stringification support in Mojo::Collection is DEPRECATED'
+    . ' in favor of Mojo::Collection::join';
+  shift->join("\n");
+};
+use overload bool => sub {1}, fallback => 1;
+
 our @EXPORT_OK = ('c');
 
+# DEPRECATED in Tiger Face!
 sub AUTOLOAD {
   my $self = shift;
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
+  deprecated "Mojo::Collection::AUTOLOAD ($method) is DEPRECATED"
+    . ' in favor of Mojo::Collection::map';
   croak "Undefined subroutine &${package}::$method called"
     unless blessed $self && $self->isa(__PACKAGE__);
-  return $self->pluck($method, @_);
+  return $self->map($method, @_);
 }
 
+# DEPRECATED in Tiger Face!
 sub DESTROY { }
 
 sub c { __PACKAGE__->new(@_) }
 
 sub compact {
-  shift->grep(sub { length($_ // '') });
+  $_[0]->new(grep { defined && (ref || length) } @{$_[0]});
 }
 
 sub each {
@@ -59,8 +68,8 @@ sub join {
 sub last { shift->[-1] }
 
 sub map {
-  my ($self, $cb) = @_;
-  return $self->new(map { $_->$cb } @$self);
+  my ($self, $cb) = (shift, shift);
+  return $self->new(map { $_->$cb(@_) } @$self);
 }
 
 sub new {
@@ -68,9 +77,18 @@ sub new {
   return bless [@_], ref $class || $class;
 }
 
+# DEPRECATED in Tiger Face!
 sub pluck {
-  my ($self, $method, @args) = @_;
-  return $self->map(sub { $_->$method(@args) });
+  deprecated
+    'Mojo::Collection::pluck is DEPRECATED in favor of Mojo::Collection::map';
+  my ($self, $key) = (shift, shift);
+  return $self->new(map { ref eq 'HASH' ? $_->{$key} : $_->$key(@_) } @$self);
+}
+
+sub reduce {
+  my $self = shift;
+  @_ = (@_, @$self);
+  goto &List::Util::reduce;
 }
 
 sub reverse { $_[0]->new(reverse @{$_[0]}) }
@@ -86,14 +104,25 @@ sub slice {
 
 sub sort {
   my ($self, $cb) = @_;
-  return $self->new($cb ? sort { $a->$cb($b) } @$self : sort @$self);
+
+  return $self->new(sort @$self) unless $cb;
+
+  my $caller = caller;
+  no strict 'refs';
+  my @sorted = sort {
+    local (*{"${caller}::a"}, *{"${caller}::b"}) = (\$a, \$b);
+    $a->$cb($b);
+  } @$self;
+  return $self->new(@sorted);
 }
 
 sub tap { shift->Mojo::Base::tap(@_) }
 
+sub to_array { [@{shift()}] }
+
 sub uniq {
   my %seen;
-  return shift->grep(sub { !$seen{$_}++ });
+  return $_[0]->new(grep { !$seen{$_}++ } @{$_[0]});
 }
 
 sub _flatten {
@@ -117,6 +146,7 @@ Mojo::Collection - Collection
   # Manipulate collection
   my $collection = Mojo::Collection->new(qw(just works));
   unshift @$collection, 'it';
+  say $collection->join("\n");
 
   # Chain methods
   $collection->map(sub { ucfirst })->shuffle->each(sub {
@@ -124,10 +154,6 @@ Mojo::Collection - Collection
     say "$count: $word";
   });
 
-  # Stringify collection
-  say $collection->join("\n");
-  say "$collection";
-
   # Use the alternative constructor
   use Mojo::Collection 'c';
   c(qw(a b c))->join('/')->url_escape->say;
@@ -172,6 +198,7 @@ Evaluate callback for each element in collection or return all elements as a
 list if none has been provided. The element will be the first argument passed
 to the callback and is also available as C<$_>.
 
+  # Make a numbered list
   $collection->each(sub {
     my ($e, $count) = @_;
     say "$count: $e";
@@ -188,7 +215,8 @@ return the first one that matched the regular expression, or for which the
 callback returned true. The element will be the first argument passed to the
 callback and is also available as C<$_>.
 
-  my $five = $collection->first(sub { $_ == 5 });
+  # Find first value that is greater than 5
+  my $greater = $collection->first(sub { $_ > 5 });
 
 =head2 flatten
 
@@ -207,6 +235,7 @@ create a new collection with all elements that matched the regular expression,
 or for which the callback returned true. The element will be the first
 argument passed to the callback and is also available as C<$_>.
 
+  # Find all values that contain the word "mojo"
   my $interesting = $collection->grep(qr/mojo/i);
 
 =head2 join
@@ -216,7 +245,8 @@ argument passed to the callback and is also available as C<$_>.
 
 Turn collection into L<Mojo::ByteStream>.
 
-  $collection->join("\n")->say;
+  # Join all values with commas
+  $collection->join(', ')->say;
 
 =head2 last
 
@@ -227,12 +257,18 @@ Return the last element in collection.
 =head2 map
 
   my $new = $collection->map(sub {...});
+  my $new = $collection->map($method);
+  my $new = $collection->map($method, @args);
 
-Evaluate callback for each element in collection and create a new collection
-from the results. The element will be the first argument passed to the
-callback and is also available as C<$_>.
+Evaluate callback for, or call method on, each element in collection and
+create a new collection from the results. The element will be the first
+argument passed to the callback and is also available as C<$_>.
+
+  # Longer version
+  my $new = $collection->map(sub { $_->$method(@args) });
 
-  my $doubled = $collection->map(sub { $_ * 2 });
+  # Append the word "mojo" to all values
+  my $mojoified = $collection->map(sub { $_ . 'mojo' });
 
 =head2 new
 
@@ -240,16 +276,19 @@ callback and is also available as C<$_>.
 
 Construct a new array-based L<Mojo::Collection> object.
 
-=head2 pluck
+=head2 reduce
 
-  my $new = $collection->pluck($method);
-  my $new = $collection->pluck($method, @args);
+  my $result = $collection->reduce(sub {...});
+  my $result = $collection->reduce(sub {...}, $initial);
 
-Call method on each element in collection and create a new collection from the
-results.
+Reduce elements in collection with callback, the first element will be used as
+initial value if none has been provided.
 
-  # Equal to but more convenient than
-  my $new = $collection->map(sub { $_->$method(@args) });
+  # Calculate the sum of all values
+  my $sum = $collection->reduce(sub { $a + $b });
+
+  # Count how often each value occurs in collection
+  my $hash = $collection->reduce(sub { $a->{$b}++; $a }, {});
 
 =head2 reverse
 
@@ -283,7 +322,8 @@ Number of elements in collection.
 Sort elements based on return value of callback and create a new collection
 from the results.
 
-  my $insensitive = $collection->sort(sub { uc(shift) cmp uc(shift) });
+  # Sort values case insensitive
+  my $insensitive = $collection->sort(sub { uc($a) cmp uc($b) });
 
 =head2 tap
 
@@ -291,36 +331,17 @@ from the results.
 
 Alias for L<Mojo::Base/"tap">.
 
-=head2 uniq
-
-  my $new = $collection->uniq;
-
-Create a new collection without duplicate elements.
-
-=head1 AUTOLOAD
-
-In addition to the L</"METHODS"> above, you can also call methods provided by
-all elements in the collection directly and create a new collection from the
-results, similar to L</"pluck">.
+=head2 to_array
 
-  push @$collection, Mojo::DOM->new("<div><h1>$_</h1></div>") for 1 .. 9;
-  say $collection->find('h1')->type('h2')->prepend_content('Test ')->root;
+  my $array = $collection->to_array;
 
-=head1 OPERATORS
+Turn collection into array reference.
 
-L<Mojo::Collection> overloads the following operators.
-
-=head2 bool
-
-  my $bool = !!$collection;
-
-True or false, depending on if the collection is empty.
-
-=head2 stringify
+=head2 uniq
 
-  my $str = "$collection";
+  my $new = $collection->uniq;
 
-Stringify elements in collection and L</"join"> them with newlines.
+Create a new collection without duplicate elements.
 
 =head1 SEE ALSO
 
@@ -4,12 +4,16 @@ use Mojo::Base 'Mojo::EventEmitter';
 use Carp 'croak';
 use Compress::Raw::Zlib qw(WANT_GZIP Z_STREAM_END);
 use Mojo::Headers;
+use Scalar::Util 'looks_like_number';
 
-has [qw(auto_relax expect_close relaxed skip_body)];
+has [qw(auto_decompress auto_relax expect_close relaxed skip_body)];
 has headers           => sub { Mojo::Headers->new };
 has max_buffer_size   => sub { $ENV{MOJO_MAX_BUFFER_SIZE} || 262144 };
 has max_leftover_size => sub { $ENV{MOJO_MAX_LEFTOVER_SIZE} || 262144 };
 
+my $BOUNDARY_RE
+  = qr!multipart.*boundary\s*=\s*(?:"([^"]+)"|([\w'(),.:?\-+/]+))!i;
+
 sub body_contains {
   croak 'Method "body_contains" not implemented by subclass';
 }
@@ -17,10 +21,7 @@ sub body_contains {
 sub body_size { croak 'Method "body_size" not implemented by subclass' }
 
 sub boundary {
-  return undef unless my $type = shift->headers->content_type;
-  $type =~ m!multipart.*boundary\s*=\s*(?:"([^"]+)"|([\w'(),.:?\-+/]+))!i
-    and return $1 // $2;
-  return undef;
+  (shift->headers->content_type // '') =~ $BOUNDARY_RE ? $1 // $2 : undef;
 }
 
 sub build_body    { shift->_build('get_body_chunk') }
@@ -88,7 +89,6 @@ sub parse {
   # Headers
   $self->_parse_until_body(@_);
   return $self if $self->{state} eq 'headers';
-  $self->emit('body') unless $self->{body}++;
 
   # Chunked content
   $self->{real_size} //= 0;
@@ -123,22 +123,21 @@ sub parse {
 
   # Chunked or relaxed content
   if ($self->is_chunked || $self->relaxed) {
-    $self->{size} += length($self->{buffer} //= '');
-    $self->_uncompress($self->{buffer});
+    $self->_decompress($self->{buffer} //= '');
+    $self->{size} += length $self->{buffer};
     $self->{buffer} = '';
+    return $self;
   }
 
   # Normal content
-  else {
-    $self->{size} ||= 0;
-    if ((my $need = ($len ||= 0) - $self->{size}) > 0) {
-      my $len = length $self->{buffer};
-      my $chunk = substr $self->{buffer}, 0, $need > $len ? $len : $need, '';
-      $self->_uncompress($chunk);
-      $self->{size} += length $chunk;
-    }
-    $self->{state} = 'finished' if $len <= $self->progress;
+  $len = 0 unless looks_like_number $len;
+  if ((my $need = $len - ($self->{size} ||= 0)) > 0) {
+    my $len = length $self->{buffer};
+    my $chunk = substr $self->{buffer}, 0, $need > $len ? $len : $need, '';
+    $self->_decompress($chunk);
+    $self->{size} += length $chunk;
   }
+  $self->{state} = 'finished' if $len <= $self->progress;
 
   return $self;
 }
@@ -179,8 +178,7 @@ sub write_chunk {
 sub _build {
   my ($self, $method) = @_;
 
-  my $buffer = '';
-  my $offset = 0;
+  my ($buffer, $offset) = ('', 0);
   while (1) {
 
     # No chunk yet, try again
@@ -207,6 +205,29 @@ sub _build_chunk {
   return $crlf . sprintf('%x', length $chunk) . "\x0d\x0a$chunk";
 }
 
+sub _decompress {
+  my ($self, $chunk) = @_;
+
+  # No compression
+  return $self->emit(read => $chunk)
+    unless $self->auto_decompress && $self->is_compressed;
+
+  # Decompress
+  $self->{post_buffer} .= $chunk;
+  my $gz = $self->{gz}
+    //= Compress::Raw::Zlib::Inflate->new(WindowBits => WANT_GZIP);
+  my $status = $gz->inflate(\$self->{post_buffer}, my $out);
+  $self->emit(read => $out) if defined $out;
+
+  # Replace Content-Encoding with Content-Length
+  $self->headers->content_length($gz->total_out)->remove('Content-Encoding')
+    if $status == Z_STREAM_END;
+
+  # Check buffer size
+  @$self{qw(state limit)} = ('finished', 1)
+    if length($self->{post_buffer} // '') > $self->max_buffer_size;
+}
+
 sub _parse_chunked {
   my $self = shift;
 
@@ -250,7 +271,8 @@ sub _parse_chunked_trailing_headers {
   return unless $headers->is_finished;
   $self->{chunk_state} = 'finished';
 
-  # Replace Transfer-Encoding with Content-Length
+  # Take care of leftover and replace Transfer-Encoding with Content-Length
+  $self->{buffer} .= $headers->leftovers;
   $headers->remove('Transfer-Encoding');
   $headers->content_length($self->{real_size}) unless $headers->content_length;
 }
@@ -265,7 +287,6 @@ sub _parse_headers {
   # Take care of leftovers
   my $leftovers = $self->{pre_buffer} = $headers->leftovers;
   $self->{header_size} = $self->{raw_size} - length $leftovers;
-  $self->emit('body') unless $self->{body}++;
 }
 
 sub _parse_until_body {
@@ -273,35 +294,8 @@ sub _parse_until_body {
 
   $self->{raw_size} += length($chunk //= '');
   $self->{pre_buffer} .= $chunk;
-
-  unless ($self->{state}) {
-    $self->{header_size} = $self->{raw_size} - length $self->{pre_buffer};
-    $self->{state}       = 'headers';
-  }
-  $self->_parse_headers if ($self->{state} // '') eq 'headers';
-}
-
-sub _uncompress {
-  my ($self, $chunk) = @_;
-
-  # No compression
-  return $self->emit(read => $chunk)
-    unless $self->is_compressed && $self->auto_relax;
-
-  # Uncompress
-  $self->{post_buffer} .= $chunk;
-  my $gz = $self->{gz}
-    //= Compress::Raw::Zlib::Inflate->new(WindowBits => WANT_GZIP);
-  my $status = $gz->inflate(\$self->{post_buffer}, my $out);
-  $self->emit(read => $out) if defined $out;
-
-  # Replace Content-Encoding with Content-Length
-  $self->headers->content_length($gz->total_out)->remove('Content-Encoding')
-    if $status == Z_STREAM_END;
-
-  # Check buffer size
-  @$self{qw(state limit)} = ('finished', 1)
-    if length($self->{post_buffer} // '') > $self->max_buffer_size;
+  $self->_parse_headers if ($self->{state} ||= 'headers') eq 'headers';
+  $self->emit('body') if $self->{state} ne 'headers' && !$self->{body}++;
 }
 
 1;
@@ -379,6 +373,13 @@ Emitted when a new chunk of content arrives.
 
 L<Mojo::Content> implements the following attributes.
 
+=head2 auto_decompress
+
+  my $bool = $content->auto_decompress;
+  $content = $content->auto_decompress($bool);
+
+Decompress content automatically if L</"is_compressed"> is true.
+
 =head2 auto_relax
 
   my $bool = $content->auto_relax;
@@ -79,7 +79,7 @@ Always true.
 
   my $str = "$cookie";
 
-Alias for L</to_string>.
+Alias for L</"to_string">.
 
 =head1 SEE ALSO
 
@@ -14,13 +14,6 @@ my $ATTR_RE   = qr/
   )?
   \]
 /x;
-my $CLASS_ID_RE = qr/
-  (?:
-    (?:\.((?:\\\.|[^\#.])+))   # Class
-  |
-    (?:\#((?:\\\#|[^.\#])+))   # ID
-  )
-/x;
 my $PSEUDO_CLASS_RE = qr/(?::([\w\-]+)(?:\(((?:\([^)]+\)|[^)])+)\))?)/;
 my $TOKEN_RE        = qr/
   (\s*,\s*)?                         # Separator
@@ -35,8 +28,8 @@ sub match {
   return $tree->[0] ne 'tag' ? undef : _match(_compile(shift), $tree, $tree);
 }
 
-sub select     { shift->_select(0, @_) }
-sub select_one { shift->_select(1, @_) }
+sub select     { _select(0, shift->tree, _compile(@_)) }
+sub select_one { _select(1, shift->tree, _compile(@_)) }
 
 sub _ancestor {
   my ($selectors, $current, $tree) = @_;
@@ -112,9 +105,9 @@ sub _compile {
     push @$selector, ['tag', $tag];
 
     # Class or ID
-    while ($element =~ /$CLASS_ID_RE/go) {
-      push @$selector, ['attr', 'class', _regex('~', $1)] if defined $1;
-      push @$selector, ['attr', 'id',    _regex('',  $2)] if defined $2;
+    while ($element =~ /(?:([.#])((?:\\[.\#]|[^\#.])+))/g) {
+      my ($name, $op) = $1 eq '.' ? ('class', '~') : ('id', '');
+      push @$selector, ['attr', $name, _regex($op, $2)];
     }
 
     # Pseudo classes (":not" contains more selectors)
@@ -232,12 +225,10 @@ sub _regex {
 }
 
 sub _select {
-  my ($self, $one, $selector) = @_;
+  my ($one, $tree, $pattern) = @_;
 
   my @results;
-  my $pattern = _compile($selector);
-  my $tree    = $self->tree;
-  my @queue   = ($tree);
+  my @queue = ($tree);
   while (my $current = shift @queue) {
     my $type = $current->[0];
 
@@ -54,17 +54,10 @@ my %RAW = map { $_ => 1 } qw(script style);
 my %RCDATA = map { $_ => 1 } qw(title textarea);
 
 # HTML elements with optional end tags
-my %END = (
-  body => ['head'],
-  dd   => [qw(dt dd)],
-  dt   => [qw(dt dd)],
-  rp   => [qw(rt rp)],
-  rt   => [qw(rt rp)]
-);
-$END{$_} = [$_] for qw(optgroup option);
+my %END = (body => 'head', optgroup => 'optgroup', option => 'option');
 
 # HTML elements that break paragraphs
-map { $END{$_} = ['p'] } (
+map { $END{$_} = 'p' } (
   qw(address article aside blockquote dir div dl fieldset footer form h1 h2),
   qw(h3 h4 h5 h6 header hr main menu nav ol p pre section table ul)
 );
@@ -72,6 +65,14 @@ map { $END{$_} = ['p'] } (
 # HTML table elements with optional end tags
 my %TABLE = map { $_ => 1 } qw(colgroup tbody td tfoot th thead tr);
 
+# HTML elements with optional end tags and scoping rules
+my %CLOSE
+  = (li => [{li => 1}, {ul => 1, ol => 1}], tr => [{tr => 1}, {table => 1}]);
+$CLOSE{$_} = [\%TABLE, {table => 1}] for qw(colgroup tbody tfoot thead);
+$CLOSE{$_} = [{dd => 1, dt => 1}, {dl    => 1}] for qw(dd dt);
+$CLOSE{$_} = [{rp => 1, rt => 1}, {ruby  => 1}] for qw(rp rt);
+$CLOSE{$_} = [{th => 1, td => 1}, {table => 1}] for qw(td th);
+
 # HTML elements without end tags
 my %EMPTY = map { $_ => 1 } (
   qw(area base br col embed hr img input keygen link menuitem meta param),
@@ -171,17 +172,6 @@ sub parse {
 
 sub render { _render($_[0]->tree, $_[0]->xml) }
 
-sub _close {
-  my ($current, $allowed, $scope) = @_;
-
-  # Close allowed parent elements in scope
-  my $parent = $$current;
-  while ($parent->[0] ne 'root' && !$scope->{$parent->[1]}) {
-    _end($parent->[1], 0, $current) if $allowed->{$parent->[1]};
-    $parent = $parent->[3];
-  }
-}
-
 sub _end {
   my ($end, $xml, $current) = @_;
 
@@ -273,22 +263,17 @@ sub _start {
 
   # Autoclose optional HTML elements
   if (!$xml && $$current->[0] ne 'root') {
-    if (my $end = $END{$start}) { _end($_, 0, $current) for @$end }
-
-    # "li"
-    elsif ($start eq 'li') { _close($current, {li => 1}, {ul => 1, ol => 1}) }
+    if (my $end = $END{$start}) { _end($end, 0, $current) }
 
-    # "colgroup", "thead", "tbody" and "tfoot"
-    elsif ($start eq 'colgroup' || $start =~ /^t(?:head|body|foot)$/) {
-      _close($current, \%TABLE, {table => 1});
-    }
+    elsif (my $close = $CLOSE{$start}) {
+      my ($allowed, $scope) = @$close;
 
-    # "tr"
-    elsif ($start eq 'tr') { _close($current, {tr => 1}, {table => 1}) }
-
-    # "th" and "td"
-    elsif ($start eq 'th' || $start eq 'td') {
-      _close($current, {$_ => 1}, {table => 1}) for qw(th td);
+      # Close allowed parent elements in scope
+      my $parent = $$current;
+      while ($parent->[0] ne 'root' && !$scope->{$parent->[1]}) {
+        _end($parent->[1], 0, $current) if $allowed->{$parent->[1]};
+        $parent = $parent->[3];
+      }
     }
   }
 
@@ -312,13 +297,13 @@ Mojo::DOM::HTML - HTML/XML engine
 
   # Turn HTML into DOM tree
   my $html = Mojo::DOM::HTML->new;
-  $html->parse('<div><p id="a">A</p><p id="b">B</p></div>');
+  $html->parse('<div><p id="a">Test</p><p id="b">123</p></div>');
   my $tree = $html->tree;
 
 =head1 DESCRIPTION
 
 L<Mojo::DOM::HTML> is the HTML/XML engine used by L<Mojo::DOM> and based on
-the L<HTML Living Standard|http://www.whatwg.org/html> as well as the
+the L<HTML Living Standard|https://html.spec.whatwg.org> as well as the
 L<Extensible Markup Language (XML) 1.0|http://www.w3.org/TR/xml/>.
 
 =head1 ATTRIBUTES
@@ -14,22 +14,25 @@ use List::Util 'first';
 use Mojo::Collection;
 use Mojo::DOM::CSS;
 use Mojo::DOM::HTML;
-use Mojo::Util 'squish';
+use Mojo::Util qw(deprecated squish);
 use Scalar::Util qw(blessed weaken);
 
+# DEPRECATED in Tiger Face!
 sub AUTOLOAD {
   my $self = shift;
 
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
+  deprecated "Mojo::DOM::AUTOLOAD ($method) is DEPRECATED"
+    . ' in favor of Mojo::DOM::children';
   croak "Undefined subroutine &${package}::$method called"
     unless blessed $self && $self->isa(__PACKAGE__);
 
-  # Search children of current element
   my $children = $self->children($method);
   return @$children > 1 ? $children : $children->[0] if @$children;
   croak qq{Can't locate object method "$method" via package "$package"};
 }
 
+# DEPRECATED in Tiger Face!
 sub DESTROY { }
 
 sub all_contents { $_[0]->_collect(_all(_nodes($_[0]->tree))) }
@@ -45,7 +48,7 @@ sub append_content { shift->_content(1, 0, @_) }
 sub at {
   my $self = shift;
   return undef unless my $result = $self->_css->select_one(@_);
-  return _tag($self, $result, $self->xml);
+  return _build($self, $result, $self->xml);
 }
 
 sub attr {
@@ -60,7 +63,8 @@ sub attr {
   return $attrs->{$_[0]} // '' unless @_ > 1 || ref $_[0];
 
   # Set
-  %$attrs = (%$attrs, %{ref $_[0] ? $_[0] : {@_}});
+  my $values = ref $_[0] ? $_[0] : {@_};
+  @$attrs{keys %$values} = values %$values;
 
   return $self;
 }
@@ -118,15 +122,15 @@ sub new {
   return @_ ? $self->parse(@_) : $self;
 }
 
-sub next { shift->_siblings->[1][0] }
-sub next_sibling { shift->_siblings(0, 1)->[1][0] }
+sub next         { _maybe($_[0], $_[0]->_siblings(1)->[1]) }
+sub next_sibling { _maybe($_[0], $_[0]->_siblings->[1]) }
 
 sub node { shift->tree->[0] }
 
 sub parent {
   my $self = shift;
   return undef if $self->tree->[0] eq 'root';
-  return _tag($self, $self->_parent, $self->xml);
+  return _build($self, $self->_parent, $self->xml);
 }
 
 sub parse { shift->_delegate(parse => shift) }
@@ -135,8 +139,8 @@ sub prepend { shift->_add(0, @_) }
 
 sub prepend_content { shift->_content(0, 0, @_) }
 
-sub previous { shift->_siblings->[0][-1] }
-sub previous_sibling { shift->_siblings(0, 1)->[0][-1] }
+sub previous         { _maybe($_[0], $_[0]->_siblings(1)->[0]) }
+sub previous_sibling { _maybe($_[0], $_[0]->_siblings->[0]) }
 
 sub remove { shift->replace('') }
 
@@ -149,10 +153,10 @@ sub replace {
 sub root {
   my $self = shift;
   return $self unless my $tree = $self->_ancestors(1);
-  return _tag($self, $tree, $self->xml);
+  return _build($self, $tree, $self->xml);
 }
 
-sub siblings { _select(Mojo::Collection->new(@{_siblings($_[0], 1)}), $_[1]) }
+sub siblings { _select($_[0]->_collect(@{_siblings($_[0], 1, 1)}), $_[1]) }
 
 sub strip {
   my $self = shift;
@@ -176,6 +180,27 @@ sub type {
   return $self;
 }
 
+# DEPRECATED in Tiger Face!
+sub val {
+  deprecated 'Mojo::DOM::val is DEPRECATED';
+  my $self = shift;
+
+  # "option"
+  my $type = $self->type;
+  return Mojo::Collection->new($self->{value} // $self->text)
+    if $type eq 'option';
+
+  # "select"
+  return $self->find('option[selected]')->map('val')->flatten
+    if $type eq 'select';
+
+  # "textarea"
+  return Mojo::Collection->new($self->text) if $type eq 'textarea';
+
+  # "input" or "button"
+  return Mojo::Collection->new($self->{value} // ());
+}
+
 sub wrap         { shift->_wrap(0, @_) }
 sub wrap_content { shift->_wrap(1, @_) }
 
@@ -222,10 +247,12 @@ sub _ancestors {
   return $root ? $ancestors[-1] : @ancestors[0 .. $#ancestors - 1];
 }
 
+sub _build { shift->new->tree(shift)->xml(shift) }
+
 sub _collect {
   my $self = shift;
   my $xml  = $self->xml;
-  return Mojo::Collection->new(map { _tag($self, $_, $xml) } @_);
+  return Mojo::Collection->new(map { _build($self, $_, $xml) } @_);
 }
 
 sub _content {
@@ -268,6 +295,8 @@ sub _link {
   return @new;
 }
 
+sub _maybe { $_[1] ? _build($_[0], $_[1], $_[0]->xml) : undef }
+
 sub _nodes {
   return unless my $tree = shift;
   return @$tree[_start($tree) .. $#$tree];
@@ -297,25 +326,23 @@ sub _select {
 }
 
 sub _siblings {
-  my ($self, $merge, $all) = @_;
+  my ($self, $tags, $all) = @_;
 
-  return $merge ? [] : [[], []] unless my $parent = $self->parent;
+  return [] unless my $parent = $self->parent;
 
   my $tree = $self->tree;
   my (@before, @after, $match);
-  for my $child ($parent->contents->each) {
-    ++$match and next if $child->tree eq $tree;
-    next unless $all || $child->node eq 'tag';
-    $match ? push @after, $child : push @before, $child;
+  for my $node (_nodes($parent->tree)) {
+    ++$match and next if !$match && $node eq $tree;
+    next if $tags && $node->[0] ne 'tag';
+    $match ? push @after, $node : push @before, $node;
   }
 
-  return $merge ? [@before, @after] : [\@before, \@after];
+  return $all ? [@before, @after] : [$before[-1], $after[0]];
 }
 
 sub _start { $_[0][0] eq 'root' ? 1 : 4 }
 
-sub _tag { shift->new->tree(shift)->xml(shift) }
-
 sub _text {
   my ($nodes, $recurse, $trim) = @_;
 
@@ -391,19 +418,15 @@ Mojo::DOM - Minimalistic HTML/XML DOM parser with CSS selectors
   use Mojo::DOM;
 
   # Parse
-  my $dom = Mojo::DOM->new('<div><p id="a">A</p><p id="b">B</p></div>');
+  my $dom = Mojo::DOM->new('<div><p id="a">Test</p><p id="b">123</p></div>');
 
   # Find
   say $dom->at('#b')->text;
-  say $dom->find('p')->text;
-  say $dom->find('[id]')->attr('id');
-
-  # Walk
-  say $dom->div->p->[0]->text;
-  say $dom->div->children('p')->first->{id};
+  say $dom->find('p')->map('text')->join("\n");
+  say $dom->find('[id]')->map(attr => 'id')->join("\n");
 
   # Iterate
-  $dom->find('p[id]')->each(sub { say $_->{id} });
+  $dom->find('p[id]')->reverse->each(sub { say $_->{id} });
 
   # Loop
   for my $e ($dom->find('p[id]')->each) {
@@ -411,8 +434,8 @@ Mojo::DOM - Minimalistic HTML/XML DOM parser with CSS selectors
   }
 
   # Modify
-  $dom->div->p->last->append('<p id="c">C</p>');
-  $dom->find(':not(p)')->strip;
+  $dom->find('div p')->last->append('<p id="c">456</p>');
+  $dom->find(':not(p)')->map('strip');
 
   # Render
   say "$dom";
@@ -430,14 +453,12 @@ are lowercased and selectors need to be lowercase as well.
 
   my $dom = Mojo::DOM->new('<P ID="greeting">Hi!</P>');
   say $dom->at('p')->text;
-  say $dom->p->{id};
 
 If XML processing instructions are found, the parser will automatically switch
 into XML mode and everything becomes case sensitive.
 
   my $dom = Mojo::DOM->new('<?xml version="1.0"?><P ID="greeting">Hi!</P>');
   say $dom->at('P')->text;
-  say $dom->P->{ID};
 
 XML detection can also be disabled with the L</"xml"> method.
 
@@ -459,8 +480,8 @@ Return a L<Mojo::Collection> object containing all nodes in DOM structure as
 L<Mojo::DOM> objects.
 
   # "<p><b>123</b></p>"
-  $dom->parse('<p><!-- test --><b>123<!-- 456 --></b></p>')
-    ->all_contents->grep(sub { $_->node eq 'comment' })->remove->first;
+  $dom->parse('<p><!-- Test --><b>123<!-- 456 --></b></p>')->all_contents
+    ->grep(sub { $_->node eq 'comment' })->map('remove')->first;
 
 =head2 all_text
 
@@ -471,10 +492,10 @@ Extract all text content from DOM structure, smart whitespace trimming is
 enabled by default.
 
   # "foo bar baz"
-  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->div->all_text;
+  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->at('div')->all_text;
 
   # "foo\nbarbaz\n"
-  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->div->all_text(0);
+  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->at('div')->all_text(0);
 
 =head2 ancestors
 
@@ -486,7 +507,7 @@ L<Mojo::Collection> object containing these elements as L<Mojo::DOM> objects.
 All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
 
   # List types of ancestor elements
-  say $dom->ancestors->type;
+  say $dom->ancestors->map('type')->join("\n");
 
 =head2 append
 
@@ -494,27 +515,30 @@ All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
 
 Append HTML/XML fragment to this node.
 
-  # "<div><h1>A</h1><h2>B</h2></div>"
-  $dom->parse('<div><h1>A</h1></div>')->at('h1')->append('<h2>B</h2>')->root;
+  # "<div><h1>Test</h1><h2>123</h2></div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')
+    ->append('<h2>123</h2>')->root;
 
-  # "<p>test 123</p>"
-  $dom->parse('<p>test</p>')->at('p')->contents->first->append(' 123')->root;
+  # "<p>Test 123</p>"
+  $dom->parse('<p>Test</p>')->at('p')->contents->first->append(' 123')->root;
 
 =head2 append_content
 
   $dom = $dom->append_content('<p>I ♥ Mojolicious!</p>');
 
-Append HTML/XML fragment or raw content (depending on node type) to this
+Append HTML/XML fragment (for C<root> and C<tag> nodes) or raw content to this
 node's content.
 
-  # "<div><h1>AB</h1></div>"
-  $dom->parse('<div><h1>A</h1></div>')->at('h1')->append_content('B')->root;
+  # "<div><h1>Test123</h1></div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')
+    ->append_content('123')->root;
 
-  # "<!-- A B --><br>"
-  $dom->parse('<!-- A --><br>')->contents->first->append_content('B ')->root;
+  # "<!-- Test 123 --><br>"
+  $dom->parse('<!-- Test --><br>')
+    ->contents->first->append_content('123 ')->root;
 
-  # "<p>A<i>B</i></p>"
-  $dom->parse('<p>A</p>')->at('p')->append_content('<i>B</i>')->root;
+  # "<p>Test<i>123</i></p>"
+  $dom->parse('<p>Test</p>')->at('p')->append_content('<i>123</i>')->root;
 
 =head2 at
 
@@ -529,15 +553,15 @@ from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
 
 =head2 attr
 
-  my $attrs = $dom->attr;
-  my $foo   = $dom->attr('foo');
-  $dom      = $dom->attr({foo => 'bar'});
-  $dom      = $dom->attr(foo => 'bar');
+  my $hash = $dom->attr;
+  my $foo  = $dom->attr('foo');
+  $dom     = $dom->attr({foo => 'bar'});
+  $dom     = $dom->attr(foo => 'bar');
 
 This element's attributes.
 
   # List id attributes
-  say $dom->find('*')->attr('id')->compact;
+  say $dom->find('*')->map(attr => 'id')->compact->join("\n");
 
 =head2 children
 
@@ -556,26 +580,27 @@ All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
   my $str = $dom->content;
   $dom    = $dom->content('<p>I ♥ Mojolicious!</p>');
 
-Return this node's content or replace it with HTML/XML fragment or raw content
-(depending on node type).
+Return this node's content or replace it with HTML/XML fragment (for C<root>
+and C<tag> nodes) or raw content.
 
-  # "<b>test</b>"
-  $dom->parse('<div><b>test</b></div>')->div->content;
+  # "<b>Test</b>"
+  $dom->parse('<div><b>Test</b></div>')->at('div')->content;
 
-  # "<div><h1>B</h1></div>"
-  $dom->parse('<div><h1>A</h1></div>')->at('h1')->content('B')->root;
+  # "<div><h1>123</h1></div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->content('123')->root;
 
-  # "<div><h1></h1></div>"
-  $dom->parse('<div><h1>A</h1></div>')->at('h1')->content('')->root;
+  # "<p><i>123</i></p>"
+  $dom->parse('<p>Test</p>')->at('p')->content('<i>123</i>')->root;
 
-  # " A "
-  $dom->parse('<!-- A --><br>')->contents->first->content;
+  # "<div><h1></h1></div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->content('')->root;
 
-  # "<!-- B --><br>"
-  $dom->parse('<!-- A --><br>')->contents->first->content(' B ')->root;
+  # " Test "
+  $dom->parse('<!-- Test --><br>')->contents->first->content;
 
-  # "<p><i>B</i></p>"
-  $dom->parse('<p>A</p>')->at('p')->content('<i>B</i>')->root;
+  # "<div><!-- 123 -->456</div>"
+  $dom->parse('<div><!-- Test -->456</div>')->at('div')
+    ->contents->first->content(' 123 ')->root;
 
 =head2 contents
 
@@ -585,10 +610,10 @@ Return a L<Mojo::Collection> object containing the child nodes of this element
 as L<Mojo::DOM> objects.
 
   # "<p><b>123</b></p>"
-  $dom->parse('<p>test<b>123</b></p>')->at('p')->contents->first->remove;
+  $dom->parse('<p>Test<b>123</b></p>')->at('p')->contents->first->remove;
 
-  # "<!-- test -->"
-  $dom->parse('<!-- test --><b>123</b>')->contents->first;
+  # "<!-- Test -->"
+  $dom->parse('<!-- Test --><b>123</b>')->contents->first;
 
 =head2 find
 
@@ -602,8 +627,13 @@ All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
   my $id = $dom->find('div')->[23]{id};
 
   # Extract information from multiple elements
-  my @headers = $dom->find('h1, h2, h3')->text->each;
-  my @links   = $dom->find('a[href]')->attr('href')->each;
+  my @headers = $dom->find('h1, h2, h3')->map('text')->each;
+
+  # Count all the different tags
+  my $hash = $dom->find('*')->reduce(sub { $a->{$b->type}++; $a }, {});
+
+  # Find elements with a class that contains dots
+  my @divs = $dom->find('div.foo\.bar')->each;
 
 =head2 match
 
@@ -640,8 +670,8 @@ fragment if necessary.
 Return L<Mojo::DOM> object for next sibling element or C<undef> if there are
 no more siblings.
 
-  # "<h2>B</h2>"
-  $dom->parse('<div><h1>A</h1><h2>B</h2></div>')->at('h1')->next;
+  # "<h2>123</h2>"
+  $dom->parse('<div><h1>Test</h1><h2>123</h2></div>')->at('h1')->next;
 
 =head2 next_sibling
 
@@ -651,7 +681,7 @@ Return L<Mojo::DOM> object for next sibling node or C<undef> if there are no
 more siblings.
 
   # "456"
-  $dom->parse('<p><b>123</b><!-- test -->456</p>')->at('b')
+  $dom->parse('<p><b>123</b><!-- Test -->456</p>')->at('b')
     ->next_sibling->next_sibling;
 
 =head2 node
@@ -683,27 +713,30 @@ Parse HTML/XML fragment with L<Mojo::DOM::HTML>.
 
 Prepend HTML/XML fragment to this node.
 
-  # "<div><h1>A</h1><h2>B</h2></div>"
-  $dom->parse('<div><h2>B</h2></div>')->at('h2')->prepend('<h1>A</h1>')->root;
+  # "<div><h1>Test</h1><h2>123</h2></div>"
+  $dom->parse('<div><h2>123</h2></div>')->at('h2')
+    ->prepend('<h1>Test</h1>')->root;
 
-  # "<p>test 123</p>"
-  $dom->parse('<p>123</p>')->at('p')->contents->first->prepend('test ')->root;
+  # "<p>Test 123</p>"
+  $dom->parse('<p>123</p>')->at('p')->contents->first->prepend('Test ')->root;
 
 =head2 prepend_content
 
   $dom = $dom->prepend_content('<p>I ♥ Mojolicious!</p>');
 
-Prepend HTML/XML fragment or raw content (depending on node type) to this
-node's content.
+Prepend HTML/XML fragment (for C<root> and C<tag> nodes) or raw content to
+this node's content.
 
-  # "<div><h2>AB</h2></div>"
-  $dom->parse('<div><h2>B</h2></div>')->at('h2')->prepend_content('A')->root;
+  # "<div><h2>Test123</h2></div>"
+  $dom->parse('<div><h2>123</h2></div>')->at('h2')
+    ->prepend_content('Test')->root;
 
-  # "<!-- A B --><br>"
-  $dom->parse('<!-- B --><br>')->contents->first->prepend_content(' A')->root;
+  # "<!-- Test 123 --><br>"
+  $dom->parse('<!-- 123 --><br>')
+    ->contents->first->prepend_content(' Test')->root;
 
-  # "<p><i>B</i>A</p>"
-  $dom->parse('<p>A</p>')->at('p')->prepend_content('<i>B</i>')->root;
+  # "<p><i>123</i>Test</p>"
+  $dom->parse('<p>Test</p>')->at('p')->prepend_content('<i>123</i>')->root;
 
 =head2 previous
 
@@ -712,8 +745,8 @@ node's content.
 Return L<Mojo::DOM> object for previous sibling element or C<undef> if there
 are no more siblings.
 
-  # "<h1>A</h1>"
-  $dom->parse('<div><h1>A</h1><h2>B</h2></div>')->at('h2')->previous;
+  # "<h1>Test</h1>"
+  $dom->parse('<div><h1>Test</h1><h2>123</h2></div>')->at('h2')->previous;
 
 =head2 previous_sibling
 
@@ -723,7 +756,7 @@ Return L<Mojo::DOM> object for previous sibling node or C<undef> if there are
 no more siblings.
 
   # "123"
-  $dom->parse('<p>123<!-- test --><b>456</b></p>')->at('b')
+  $dom->parse('<p>123<!-- Test --><b>456</b></p>')->at('b')
     ->previous_sibling->previous_sibling;
 
 =head2 remove
@@ -733,7 +766,7 @@ no more siblings.
 Remove this node and return L</"parent">.
 
   # "<div></div>"
-  $dom->parse('<div><h1>A</h1></div>')->at('h1')->remove;
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->remove;
 
   # "<p><b>456</b></p>"
   $dom->parse('<p>123<b>456</b></p>')->at('p')->contents->first->remove->root;
@@ -744,11 +777,12 @@ Remove this node and return L</"parent">.
 
 Replace this node with HTML/XML fragment and return L</"parent">.
 
-  # "<div><h2>B</h2></div>"
-  $dom->parse('<div><h1>A</h1></div>')->at('h1')->replace('<h2>B</h2>');
+  # "<div><h2>123</h2></div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->replace('<h2>123</h2>');
 
-  # "<p><b>B</b></p>"
-  $dom->parse('<p>A</p>')->at('p')->contents->[0]->replace('<b>B</b>')->root;
+  # "<p><b>123</b></p>"
+  $dom->parse('<p>Test</p>')->at('p')
+    ->contents->[0]->replace('<b>123</b>')->root;
 
 =head2 root
 
@@ -766,7 +800,7 @@ L<Mojo::Collection> object containing these elements as L<Mojo::DOM> objects.
 All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
 
   # List types of sibling elements
-  say $dom->siblings->type;
+  say $dom->siblings->map('type')->join("\n");
 
 =head2 strip
 
@@ -774,8 +808,8 @@ All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
 
 Remove this element while preserving its content and return L</"parent">.
 
-  # "<div>A</div>"
-  $dom->parse('<div><h1>A</h1></div>')->at('h1')->strip;
+  # "<div>Test</div>"
+  $dom->parse('<div><h1>Test</h1></div>')->at('h1')->strip;
 
 =head2 tap
 
@@ -792,10 +826,10 @@ Extract text content from this element only (not including child elements),
 smart whitespace trimming is enabled by default.
 
   # "foo baz"
-  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->div->text;
+  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->at('div')->text;
 
   # "foo\nbaz\n"
-  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->div->text(0);
+  $dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->at('div')->text(0);
 
 =head2 to_string
 
@@ -803,8 +837,8 @@ smart whitespace trimming is enabled by default.
 
 Render this node and its content to HTML/XML.
 
-  # "<b>test</b>"
-  $dom->parse('<div><b>test</b></div>')->div->b->to_string;
+  # "<b>Test</b>"
+  $dom->parse('<div><b>Test</b></div>')->at('div b')->to_string;
 
 =head2 tree
 
@@ -822,7 +856,7 @@ carefully since it is very dynamic.
 This element's type.
 
   # List types of child elements
-  say $dom->children->type;
+  say $dom->children->map('type')->join("\n");
 
 =head2 wrap
 
@@ -831,17 +865,17 @@ This element's type.
 Wrap HTML/XML fragment around this node, placing it as the last child of the
 first innermost element.
 
-  # "<p>B<b>A</b></p>"
-  $dom->parse('<b>A</b>')->at('b')->wrap('<p>B</p>')->root;
+  # "<p>123<b>Test</b></p>"
+  $dom->parse('<b>Test</b>')->at('b')->wrap('<p>123</p>')->root;
 
-  # "<div><p><b>A</b></p>B</div>"
-  $dom->parse('<b>A</b>')->at('b')->wrap('<div><p></p>B</div>')->root;
+  # "<div><p><b>Test</b></p>123</div>"
+  $dom->parse('<b>Test</b>')->at('b')->wrap('<div><p></p>123</div>')->root;
 
-  # "<p><b>A</b></p><p>B</p>"
-  $dom->parse('<b>A</b>')->at('b')->wrap('<p></p><p>B</p>')->root;
+  # "<p><b>Test</b></p><p>123</p>"
+  $dom->parse('<b>Test</b>')->at('b')->wrap('<p></p><p>123</p>')->root;
 
-  # "<p><b>A</b></p>"
-  $dom->parse('<p>A</p>')->at('p')->contents->first->wrap('<b>')->root;
+  # "<p><b>Test</b></p>"
+  $dom->parse('<p>Test</p>')->at('p')->contents->first->wrap('<b>')->root;
 
 =head2 wrap_content
 
@@ -850,11 +884,11 @@ first innermost element.
 Wrap HTML/XML fragment around this node's content, placing it as the last
 children of the first innermost element.
 
-  # "<p><b>BA</b></p>"
-  $dom->parse('<p>A<p>')->at('p')->wrap_content('<b>B</b>')->root;
+  # "<p><b>123Test</b></p>"
+  $dom->parse('<p>Test<p>')->at('p')->wrap_content('<b>123</b>')->root;
 
-  # "<p><b>A</b></p><p>B</p>"
-  $dom->parse('<b>A</b>')->wrap_content('<p></p><p>B</p>');
+  # "<p><b>Test</b></p><p>123</p>"
+  $dom->parse('<b>Test</b>')->wrap_content('<p></p><p>123</p>');
 
 =head2 xml
 
@@ -864,17 +898,6 @@ children of the first innermost element.
 Disable HTML semantics in parser and activate case sensitivity, defaults to
 auto detection based on processing instructions.
 
-=head1 AUTOLOAD
-
-In addition to the L</"METHODS"> above, many child elements are also
-automatically available as object methods, which return a L<Mojo::DOM> or
-L<Mojo::Collection> object, depending on number of children. For more power
-and consistent results you can also use L</"children">.
-
-  say $dom->p->text;
-  say $dom->div->[23]->text;
-  say $dom->div->text;
-
 =head1 OPERATORS
 
 L<Mojo::DOM> overloads the following operators.
@@ -885,8 +908,8 @@ L<Mojo::DOM> overloads the following operators.
 
 Alias for L</"contents">.
 
-  # "<!-- test -->"
-  $dom->parse('<!-- test --><b>123</b>')->[0];
+  # "<!-- Test -->"
+  $dom->parse('<!-- Test --><b>123</b>')->[0];
 
 =head2 bool
 
@@ -901,7 +924,7 @@ Always true.
 Alias for L</"attr">.
 
   # "test"
-  $dom->parse('<div id="test">A</div>')->at('div')->{id};
+  $dom->parse('<div id="test">Test</div>')->at('div')->{id};
 
 =head2 stringify
 
@@ -2,9 +2,14 @@ package Mojo::Date;
 use Mojo::Base -base;
 use overload bool => sub {1}, '""' => sub { shift->to_string }, fallback => 1;
 
-use Time::Local 'timegm';
+use Time::Local 1.2 'timegm';
 
-has 'epoch';
+has epoch => sub {time};
+
+my $RFC3339_RE = qr/
+  ^(\d+)-(\d+)-(\d+)\D+(\d+):(\d+):(\d+(?:\.\d+)?)   # Date and time
+  (?:Z|([+-])(\d+):(\d+))?$                          # Offset
+/xi;
 
 my @DAYS   = qw(Sun Mon Tue Wed Thu Fri Sat);
 my @MONTHS = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
@@ -17,14 +22,21 @@ sub parse {
   my ($self, $date) = @_;
 
   # epoch (784111777)
-  return $self->epoch($date) if $date =~ /^\d+$/;
+  return $self->epoch($date) if $date =~ /^\d+$|^\d+\.\d+$/;
 
   # RFC 822/1123 (Sun, 06 Nov 1994 08:49:37 GMT)
+  my $offset = 0;
   my ($day, $month, $year, $h, $m, $s);
   if ($date =~ /^\w+\,\s+(\d+)\s+(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+GMT$/) {
     ($day, $month, $year, $h, $m, $s) = ($1, $MONTHS{$2}, $3, $4, $5, $6);
   }
 
+  # RFC 3339 (1994-11-06T08:49:37Z)
+  elsif ($date =~ $RFC3339_RE) {
+    ($year, $month, $day, $h, $m, $s) = ($1, $2 - 1, $3, $4, $5, $6);
+    $offset = (($8 * 3600) + ($9 * 60)) * ($7 eq '+' ? -1 : 1) if $7;
+  }
+
   # RFC 850/1036 (Sunday, 06-Nov-94 08:49:37 GMT)
   elsif ($date =~ /^\w+\,\s+(\d+)-(\w+)-(\d+)\s+(\d+):(\d+):(\d+)\s+GMT$/) {
     ($day, $month, $year, $h, $m, $s) = ($1, $MONTHS{$2}, $3, $4, $5, $6);
@@ -36,18 +48,27 @@ sub parse {
   }
 
   # Invalid
-  else { return $self }
+  else { return $self->epoch(undef) }
 
   # Prevent crash
-  my $epoch = eval { timegm($s, $m, $h, $day, $month, $year) };
-  return defined $epoch && $epoch >= 0 ? $self->epoch($epoch) : $self;
+  my $epoch = eval { timegm $s, $m, $h, $day, $month, $year };
+  return $self->epoch(
+    (defined $epoch && ($epoch += $offset) >= 0) ? $epoch : undef);
+}
+
+sub to_datetime {
+
+  # RFC 3339 (1994-11-06T08:49:37Z)
+  my ($s, $m, $h, $day, $month, $year) = gmtime(my $epoch = shift->epoch);
+  my $str = sprintf '%04d-%02d-%02dT%02d:%02d:%02d', $year + 1900, $month + 1,
+    $day, $h, $m, $s;
+  return $str . ($epoch =~ /(\.\d+)$/ ? "$1Z" : 'Z');
 }
 
 sub to_string {
-  my $self = shift;
 
   # RFC 7231 (Sun, 06 Nov 1994 08:49:37 GMT)
-  my ($s, $m, $h, $mday, $month, $year, $wday) = gmtime($self->epoch // time);
+  my ($s, $m, $h, $mday, $month, $year, $wday) = gmtime shift->epoch;
   return sprintf '%s, %02d %s %04d %02d:%02d:%02d GMT', $DAYS[$wday], $mday,
     $MONTHS[$month], $year + 1900, $h, $m, $s;
 }
@@ -69,18 +90,15 @@ Mojo::Date - HTTP date
   say $date->epoch;
 
   # Build
-  my $date = Mojo::Date->new(time);
+  my $date = Mojo::Date->new(time + 60);
   say "$date";
 
 =head1 DESCRIPTION
 
 L<Mojo::Date> implements HTTP date and time functions based on
-L<RFC 7230|http://tools.ietf.org/html/rfc7230> and
-L<RFC 7231|http://tools.ietf.org/html/rfc7231>.
-
-  Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
-  Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
-  Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
+L<RFC 7230|http://tools.ietf.org/html/rfc7230>,
+L<RFC 7231|http://tools.ietf.org/html/rfc7231> and
+L<RFC 3339|http://tools.ietf.org/html/rfc3339>.
 
 =head1 ATTRIBUTES
 
@@ -91,7 +109,7 @@ L<Mojo::Date> implements the following attributes.
   my $epoch = $date->epoch;
   $date     = $date->epoch(784111777);
 
-Epoch seconds.
+Epoch seconds, defaults to the current time.
 
 =head1 METHODS
 
@@ -113,6 +131,7 @@ Parse date.
 
   # Epoch
   say Mojo::Date->new('784111777')->epoch;
+  say Mojo::Date->new('784111777.21')->epoch;
 
   # RFC 822/1123
   say Mojo::Date->new('Sun, 06 Nov 1994 08:49:37 GMT')->epoch;
@@ -123,12 +142,34 @@ Parse date.
   # Ansi C asctime()
   say Mojo::Date->new('Sun Nov  6 08:49:37 1994')->epoch;
 
+  # RFC 3339
+  say Mojo::Date->new('1994-11-06T08:49:37Z')->epoch;
+  say Mojo::Date->new('1994-11-06T08:49:37')->epoch;
+  say Mojo::Date->new('1994-11-06T08:49:37.21Z')->epoch;
+  say Mojo::Date->new('1994-11-06T08:49:37+01:00')->epoch;
+  say Mojo::Date->new('1994-11-06T08:49:37-01:00')->epoch;
+
+=head2 to_datetime
+
+  my $str = $date->to_datetime;
+
+Render L<RFC 3339|http://tools.ietf.org/html/rfc3339> date and time.
+
+  # "1994-11-06T08:49:37Z"
+  Mojo::Date->new(784111777)->to_datetime;
+
+  # "1994-11-06T08:49:37.21Z"
+  Mojo::Date->new(784111777.21)->to_datetime;
+
 =head2 to_string
 
   my $str = $date->to_string;
 
 Render date suitable for HTTP messages.
 
+  # "Sun, 06 Nov 1994 08:49:37 GMT"
+  Mojo::Date->new(784111777)->to_string;
+
 =head1 OPERATORS
 
 L<Mojo::Date> overloads the following operators.
@@ -143,7 +184,7 @@ Always true.
 
   my $str = "$date";
 
-Alias for L</to_string>.
+Alias for L</"to_string">.
 
 =head1 SEE ALSO
 
@@ -1,6 +1,7 @@
 package Mojo::EventEmitter;
 use Mojo::Base -base;
 
+use Mojo::Util 'deprecated';
 use Scalar::Util qw(blessed weaken);
 
 use constant DEBUG => $ENV{MOJO_EVENTEMITTER_DEBUG} || 0;
@@ -22,26 +23,23 @@ sub emit {
   return $self;
 }
 
+# DEPRECATED in Tiger Face!
 sub emit_safe {
+  deprecated 'Mojo::EventEmitter::emit_safe is DEPRECATED';
   my ($self, $name) = (shift, shift);
 
   if (my $s = $self->{events}{$name}) {
-    warn "-- Emit $name in @{[blessed $self]} safely (@{[scalar @$s]})\n"
-      if DEBUG;
     for my $cb (@$s) {
       $self->emit(error => qq{Event "$name" failed: $@})
         unless eval { $self->$cb(@_); 1 };
     }
   }
-  else {
-    warn "-- Emit $name in @{[blessed $self]} safely (0)\n" if DEBUG;
-    die "@{[blessed $self]}: $_[0]" if $name eq 'error';
-  }
+  else { die "@{[blessed $self]}: $_[0]" if $name eq 'error' }
 
   return $self;
 }
 
-sub has_subscribers { !!@{shift->subscribers(shift)} }
+sub has_subscribers { !!@{shift->{events}{shift()} || []} }
 
 sub on {
   my ($self, $name, $cb) = @_;
@@ -125,7 +123,8 @@ L<Mojo::EventEmitter> can emit the following events.
     ...
   });
 
-Emitted for event errors, fatal if unhandled.
+This is a special event for errors, it will not be emitted directly by this
+class but is fatal if unhandled.
 
   $e->on(error => sub {
     my ($e, $err) = @_;
@@ -153,13 +152,6 @@ Subscribe to L</"error"> event.
 
 Emit event.
 
-=head2 emit_safe
-
-  $e = $e->emit_safe('foo');
-  $e = $e->emit_safe('foo', 123);
-
-Emit event safely and emit L</"error"> event on failure.
-
 =head2 has_subscribers
 
   my $bool = $e->has_subscribers('foo');
@@ -213,7 +213,7 @@ Always true.
 
   my $str = "$e";
 
-Alias for L</to_string>.
+Alias for L</"to_string">.
 
 =head1 SEE ALSO
 
@@ -8,12 +8,13 @@ has max_line_size => sub { $ENV{MOJO_MAX_LINE_SIZE} || 10240 };
 # Common headers
 my %NORMALCASE = map { lc($_) => $_ } (
   qw(Accept Accept-Charset Accept-Encoding Accept-Language Accept-Ranges),
-  qw(Allow Authorization Cache-Control Connection Content-Disposition),
-  qw(Content-Encoding Content-Length Content-Range Content-Type Cookie DNT),
-  qw(Date ETag Expect Expires Host If-Modified-Since Last-Modified Link),
-  qw(Location Origin Proxy-Authenticate Proxy-Authorization Range),
-  qw(Sec-WebSocket-Accept Sec-WebSocket-Extensions Sec-WebSocket-Key),
-  qw(Sec-WebSocket-Protocol Sec-WebSocket-Version Server Set-Cookie Status),
+  qw(Access-Control-Allow-Origin Allow Authorization Cache-Control Connection),
+  qw(Content-Disposition Content-Encoding Content-Language Content-Length),
+  qw(Content-Location Content-Range Content-Type Cookie DNT Date ETag Expect),
+  qw(Expires Host If-Modified-Since If-None-Match Last-Modified Link Location),
+  qw(Origin Proxy-Authenticate Proxy-Authorization Range Sec-WebSocket-Accept),
+  qw(Sec-WebSocket-Extensions Sec-WebSocket-Key Sec-WebSocket-Protocol),
+  qw(Sec-WebSocket-Version Server Set-Cookie Status Strict-Transport-Security),
   qw(TE Trailer Transfer-Encoding Upgrade User-Agent Vary WWW-Authenticate)
 );
 for my $header (values %NORMALCASE) {
@@ -223,6 +224,14 @@ Shortcut for the C<Accept-Language> header.
 
 Shortcut for the C<Accept-Ranges> header.
 
+=head2 access_control_allow_origin
+
+  my $origin = $headers->access_control_allow_origin;
+  $headers   = $headers->access_control_allow_origin('*');
+
+Shortcut for the C<Access-Control-Allow-Origin> header from
+L<Cross-Origin Resource Sharing|http://www.w3.org/TR/cors/>.
+
 =head2 add
 
   $headers = $headers->add(Foo => 'one value');
@@ -294,6 +303,13 @@ Shortcut for the C<Content-Disposition> header.
 
 Shortcut for the C<Content-Encoding> header.
 
+=head2 content_language
+
+  my $language = $headers->content_language;
+  $headers     = $headers->content_language('en');
+
+Shortcut for the C<Content-Language> header.
+
 =head2 content_length
 
   my $len  = $headers->content_length;
@@ -301,6 +317,13 @@ Shortcut for the C<Content-Encoding> header.
 
 Shortcut for the C<Content-Length> header.
 
+=head2 content_location
+
+  my $location = $headers->content_location;
+  $headers     = $headers->content_location('http://127.0.0.1/foo');
+
+Shortcut for the C<Content-Location> header.
+
 =head2 content_range
 
   my $range = $headers->content_range;
@@ -341,7 +364,7 @@ but is very commonly used.
 =head2 etag
 
   my $etag = $headers->etag;
-  $headers = $headers->etag('abc321');
+  $headers = $headers->etag('"abc321"');
 
 Shortcut for the C<ETag> header.
 
@@ -389,6 +412,13 @@ Shortcut for the C<Host> header.
 
 Shortcut for the C<If-Modified-Since> header.
 
+=head2 if_none_match
+
+  my $etag = $headers->if_none_match;
+  $headers = $headers->if_none_match('"abc321"');
+
+Shortcut for the C<If-None-Match> header.
+
 =head2 is_finished
 
   my $bool = $headers->is_finished;
@@ -551,6 +581,14 @@ L<RFC 6265|http://tools.ietf.org/html/rfc6265>.
 Shortcut for the C<Status> header from
 L<RFC 3875|http://tools.ietf.org/html/rfc3875>.
 
+=head2 strict_transport_security
+
+  my $policy = $headers->strict_transport_security;
+  $headers   = $headers->strict_transport_security('max-age=31536000');
+
+Shortcut for the C<Strict-Transport-Security> header from
+L<RFC 6797|http://tools.ietf.org/html/rfc6797>.
+
 =head2 te
 
   my $te   = $headers->te;
@@ -185,7 +185,7 @@ Always true.
 
   my $str = "$home";
 
-Alias for L</to_string>.
+Alias for L</"to_string">.
 
 =head1 SEE ALSO
 
@@ -2,15 +2,16 @@ package Mojo::IOLoop::Client;
 use Mojo::Base 'Mojo::EventEmitter';
 
 use Errno 'EINPROGRESS';
-use IO::Socket::INET;
+use IO::Socket::IP;
 use Mojo::IOLoop;
 use Scalar::Util 'weaken';
-use Socket qw(IPPROTO_TCP SO_ERROR TCP_NODELAY);
+use Socket qw(IPPROTO_TCP TCP_NODELAY);
 
-# IPv6 support requires IO::Socket::IP
-use constant IPV6 => $ENV{MOJO_NO_IPV6}
+# Non-blocking name resolution requires Net::DNS::Native
+use constant NDN => $ENV{MOJO_NO_NDN}
   ? 0
-  : eval 'use IO::Socket::IP 0.20 (); 1';
+  : eval 'use Net::DNS::Native 0.12 (); 1';
+my $NDN = NDN ? Net::DNS::Native->new(pool => 5, extra_thread => 1) : undef;
 
 # TLS support requires IO::Socket::SSL
 use constant TLS => $ENV{MOJO_NO_TLS}
@@ -19,6 +20,13 @@ use constant TLS => $ENV{MOJO_NO_TLS}
 use constant TLS_READ  => TLS ? IO::Socket::SSL::SSL_WANT_READ()  : 0;
 use constant TLS_WRITE => TLS ? IO::Socket::SSL::SSL_WANT_WRITE() : 0;
 
+# SOCKS support requires IO::Socket::Socks
+use constant SOCKS => $ENV{MOJO_NO_SOCKS}
+  ? 0
+  : eval 'use IO::Socket::Socks 0.64 (); 1';
+use constant SOCKS_READ  => SOCKS ? IO::Socket::Socks::SOCKS_WANT_READ()  : 0;
+use constant SOCKS_WRITE => SOCKS ? IO::Socket::Socks::SOCKS_WANT_WRITE() : 0;
+
 has reactor => sub { Mojo::IOLoop->singleton->reactor };
 
 sub DESTROY { shift->_cleanup }
@@ -26,14 +34,41 @@ sub DESTROY { shift->_cleanup }
 sub connect {
   my $self = shift;
   my $args = ref $_[0] ? $_[0] : {@_};
+
+  # Timeout
   weaken $self;
-  $self->reactor->next_tick(sub { $self && $self->_connect($args) });
+  my $reactor = $self->reactor;
+  $self->{timer} = $reactor->timer($args->{timeout} || 10,
+    sub { $self->emit(error => 'Connect timeout') });
+
+  # Blocking name resolution
+  $_ && s/[[\]]//g for @$args{qw(address socks_address)};
+  my $address = $args->{socks_address} || ($args->{address} ||= 'localhost');
+  return $reactor->next_tick(sub { $self && $self->_connect($args) })
+    unless NDN && $address ne 'localhost' && !$args->{handle};
+
+  # Non-blocking name resolution
+  my $handle = $self->{dns}
+    = $NDN->getaddrinfo($address, _port($args), {protocol => IPPROTO_TCP});
+  $reactor->io(
+    $handle => sub {
+      my $reactor = shift;
+
+      $reactor->remove($self->{dns});
+      my ($err, @res) = $NDN->get_result(delete $self->{dns});
+      return $self->emit(error => "Can't resolve: $err") if $err;
+
+      $args->{addr_info} = \@res;
+      $self->_connect($args);
+    }
+  )->watch($handle, 1, 0);
 }
 
 sub _cleanup {
   my $self = shift;
   return $self unless my $reactor = $self->reactor;
-  $self->{$_} && $reactor->remove(delete $self->{$_}) for qw(timer handle);
+  $NDN->timedout($self->{dns}) if $self->{dns};
+  $self->{$_} && $reactor->remove(delete $self->{$_}) for qw(dns timer handle);
   return $self;
 }
 
@@ -41,29 +76,55 @@ sub _connect {
   my ($self, $args) = @_;
 
   my $handle;
-  my $reactor = $self->reactor;
-  my $address = $args->{address} ||= 'localhost';
+  my $address = $args->{socks_address} || $args->{address};
   unless ($handle = $self->{handle} = $args->{handle}) {
     my %options = (
-      Blocking => 0,
       PeerAddr => $address eq 'localhost' ? '127.0.0.1' : $address,
-      PeerPort => $args->{port} || ($args->{tls} ? 443 : 80)
+      PeerPort => _port($args)
     );
+    %options = (PeerAddrInfo => $args->{addr_info}) if $args->{addr_info};
+    $options{Blocking} = 0;
     $options{LocalAddr} = $args->{local_address} if $args->{local_address};
-    $options{PeerAddr} =~ s/[\[\]]//g if $options{PeerAddr};
-    my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET';
-    return $self->emit(error => "Couldn't connect: $@")
-      unless $self->{handle} = $handle = $class->new(%options);
-
-    # Timeout
-    $self->{timer} = $reactor->timer($args->{timeout} || 10,
-      sub { $self->emit(error => 'Connect timeout') });
+    return $self->emit(error => "Can't connect: $@")
+      unless $self->{handle} = $handle = IO::Socket::IP->new(%options);
   }
   $handle->blocking(0);
 
   # Wait for handle to become writable
   weaken $self;
-  $reactor->io($handle => sub { $self->_try($args) })->watch($handle, 0, 1);
+  $self->reactor->io($handle => sub { $self->_ready($args) })
+    ->watch($handle, 0, 1);
+}
+
+sub _ready {
+  my ($self, $args) = @_;
+
+  # Retry or handle exceptions
+  my $handle = $self->{handle};
+  return $! == EINPROGRESS ? undef : $self->emit(error => $!)
+    if $handle->isa('IO::Socket::IP') && !$handle->connect;
+  return $self->emit(error => $! || 'Not connected') unless $handle->connected;
+
+  # Disable Nagle's algorithm
+  setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;
+
+  $self->_try_socks($args);
+}
+
+sub _port { $_[0]->{socks_port} || $_[0]->{port} || ($_[0]->{tls} ? 443 : 80) }
+
+sub _socks {
+  my ($self, $args) = @_;
+
+  # Connected
+  my $handle = $self->{handle};
+  return $self->_try_tls($args) if $handle->ready;
+
+  # Switch between reading and writing
+  my $err = $IO::Socket::Socks::SOCKS_ERROR;
+  if    ($err == SOCKS_READ)  { $self->reactor->watch($handle, 1, 0) }
+  elsif ($err == SOCKS_WRITE) { $self->reactor->watch($handle, 1, 1) }
+  else                        { $self->emit(error => $err) }
 }
 
 sub _tls {
@@ -71,8 +132,7 @@ sub _tls {
 
   # Connected
   my $handle = $self->{handle};
-  return $self->_cleanup->emit_safe(connect => $handle)
-    if $handle->connect_SSL;
+  return $self->_cleanup->emit(connect => $handle) if $handle->connect_SSL;
 
   # Switch between reading and writing
   my $err = $IO::Socket::SSL::SSL_ERROR;
@@ -80,20 +140,33 @@ sub _tls {
   elsif ($err == TLS_WRITE) { $self->reactor->watch($handle, 1, 1) }
 }
 
-sub _try {
+sub _try_socks {
   my ($self, $args) = @_;
 
-  # Retry or handle exceptions
   my $handle = $self->{handle};
-  return $! == EINPROGRESS ? undef : $self->emit(error => $!)
-    if $handle->isa('IO::Socket::IP') && !$handle->connect;
-  return $self->emit(error => $! = $handle->sockopt(SO_ERROR))
-    unless $handle->connected;
+  return $self->_try_tls($args) unless $args->{socks_address};
+  return $self->emit(
+    error => 'IO::Socket::Socks 0.64 required for SOCKS support')
+    unless SOCKS;
+
+  my %options
+    = (ConnectAddr => $args->{address}, ConnectPort => $args->{port});
+  @options{qw(AuthType Username Password)}
+    = ('userpass', @$args{qw(socks_user socks_pass)})
+    if $args->{socks_user};
+  my $reactor = $self->reactor;
+  $reactor->remove($handle);
+  return $self->emit(error => 'SOCKS upgrade failed')
+    unless IO::Socket::Socks->start_SOCKS($handle, %options);
+  weaken $self;
+  $reactor->io($handle => sub { $self->_socks($args) })->watch($handle, 0, 1);
+}
 
-  # Disable Nagle's algorithm
-  setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;
+sub _try_tls {
+  my ($self, $args) = @_;
 
-  return $self->_cleanup->emit_safe(connect => $handle)
+  my $handle = $self->{handle};
+  return $self->_cleanup->emit(connect => $handle)
     if !$args->{tls} || $handle->isa('IO::Socket::SSL');
   return $self->emit(error => 'IO::Socket::SSL 1.84 required for TLS support')
     unless TLS;
@@ -104,7 +177,7 @@ sub _try {
     SSL_ca_file => $args->{tls_ca}
       && -T $args->{tls_ca} ? $args->{tls_ca} : undef,
     SSL_cert_file  => $args->{tls_cert},
-    SSL_error_trap => sub { $self->_cleanup->emit(error => $_[1]) },
+    SSL_error_trap => sub { $self->emit(error => $_[1]) },
     SSL_hostname   => IO::Socket::SSL->can_client_sni ? $args->{address} : '',
     SSL_key_file   => $args->{tls_key},
     SSL_startHandshake  => 0,
@@ -115,7 +188,7 @@ sub _try {
   my $reactor = $self->reactor;
   $reactor->remove($handle);
   return $self->emit(error => 'TLS upgrade failed')
-    unless $handle = IO::Socket::SSL->start_SSL($handle, %options);
+    unless IO::Socket::SSL->start_SSL($handle, %options);
   $reactor->io($handle => sub { $self->_tls })->watch($handle, 0, 1);
 }
 
@@ -162,7 +235,7 @@ emit the following new ones.
     ...
   });
 
-Emitted safely once the connection is established.
+Emitted once the connection is established.
 
 =head2 error
 
@@ -194,8 +267,9 @@ implements the following new ones.
 
   $client->connect(address => '127.0.0.1', port => 3000);
 
-Open a socket connection to a remote host. Note that TLS support depends on
-L<IO::Socket::SSL> (1.84+) and IPv6 support on L<IO::Socket::IP> (0.20+).
+Open a socket connection to a remote host. Note that non-blocking name
+resolution depends on L<Net::DNS::Native> (0.12+) and TLS support on
+L<IO::Socket::SSL> (1.84+).
 
 These options are currently available:
 
@@ -225,6 +299,30 @@ Local address to bind to.
 
 Port to connect to, defaults to C<80> or C<443> with C<tls> option.
 
+=item socks_address
+
+  socks_address => '127.0.0.1'
+
+Address or host name of SOCKS5 proxy server to use for connection.
+
+=item socks_pass
+
+  socks_pass => 'secr3t'
+
+Password to use for SOCKS5 authentication.
+
+=item socks_port
+
+  socks_port => 9050
+
+Port of SOCKS5 proxy server to use for connection.
+
+=item socks_user
+
+  socks_user => 'sri'
+
+Username to use for SOCKS5 authentication.
+
 =item timeout
 
   timeout => 15
@@ -1,8 +1,8 @@
 package Mojo::IOLoop::Delay;
 use Mojo::Base 'Mojo::EventEmitter';
 
-use Mojo;
 use Mojo::IOLoop;
+use Mojo::Util;
 use Hash::Util::FieldHash 'fieldhash';
 
 has ioloop => sub { Mojo::IOLoop->singleton };
@@ -16,7 +16,7 @@ sub begin {
   return sub { $self->_step($id, $offset // 1, $len, @_) };
 }
 
-sub data { shift->Mojo::_dict(data => @_) }
+sub data { Mojo::Util::_stash(data => @_) }
 
 sub pass { $_[0]->begin->(@_) }
 
@@ -145,8 +145,8 @@ emit the following new ones.
     ...
   });
 
-Emitted if an error occurs in one of the steps, breaking the chain, fatal if
-unhandled.
+Emitted if an exception gets thrown in one of the steps, breaking the chain,
+fatal if unhandled.
 
 =head2 finish
 
@@ -250,7 +250,8 @@ Sequentialize multiple events, every time the active event counter reaches
 zero a callback will run, the first one automatically runs during the next
 reactor tick unless it is delayed by incrementing the active event counter.
 This chain will continue until there are no more callbacks, a callback does
-not increment the active event counter or an error occurs in a callback.
+not increment the active event counter or an exception gets thrown in a
+callback.
 
 =head2 wait
 
@@ -4,16 +4,11 @@ use Mojo::Base 'Mojo::EventEmitter';
 use Carp 'croak';
 use File::Basename 'dirname';
 use File::Spec::Functions 'catfile';
-use IO::Socket::INET;
+use IO::Socket::IP;
 use Mojo::IOLoop;
 use Scalar::Util 'weaken';
 use Socket qw(IPPROTO_TCP TCP_NODELAY);
 
-# IPv6 support requires IO::Socket::IP
-use constant IPV6 => $ENV{MOJO_NO_IPV6}
-  ? 0
-  : eval 'use IO::Socket::IP 0.20 (); 1';
-
 # TLS support requires IO::Socket::SSL
 use constant TLS => $ENV{MOJO_NO_TLS}
   ? 0
@@ -23,8 +18,8 @@ use constant TLS_WRITE => TLS ? IO::Socket::SSL::SSL_WANT_WRITE() : 0;
 
 # To regenerate the certificate run this command (18.04.2012)
 # openssl req -new -x509 -keyout server.key -out server.crt -nodes -days 7300
-my $CERT = catfile dirname(__FILE__), 'server.crt';
-my $KEY  = catfile dirname(__FILE__), 'server.key';
+my $CERT = catfile dirname(__FILE__), 'certs', 'server.crt';
+my $KEY  = catfile dirname(__FILE__), 'certs', 'server.key';
 
 has multi_accept => 50;
 has reactor => sub { Mojo::IOLoop->singleton->reactor };
@@ -38,7 +33,7 @@ sub DESTROY {
 }
 
 sub generate_port {
-  IO::Socket::INET->new(Listen => 5, LocalAddr => '127.0.0.1')->sockport;
+  IO::Socket::IP->new(Listen => 5, LocalAddr => '127.0.0.1')->sockport;
 }
 
 sub handle { shift->{handle} }
@@ -59,9 +54,8 @@ sub listen {
 
   # Reuse file descriptor
   my $handle;
-  my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET';
   if (defined $fd) {
-    $handle = $class->new_from_fd($fd, 'r')
+    $handle = IO::Socket::IP->new_from_fd($fd, 'r')
       or croak "Can't open file descriptor $fd: $!";
   }
 
@@ -76,7 +70,8 @@ sub listen {
     );
     $options{LocalPort} = $port if $port;
     $options{LocalAddr} =~ s/[\[\]]//g;
-    $handle = $class->new(%options) or croak "Can't create listen socket: $@";
+    $handle = IO::Socket::IP->new(%options)
+      or croak "Can't create listen socket: $@";
     $fd = fileno $handle;
     my $reuse = $self->{reuse} = join ':', $address, $handle->sockport, $fd;
     $ENV{MOJO_REUSE} .= length $ENV{MOJO_REUSE} ? ",$reuse" : "$reuse";
@@ -125,7 +120,7 @@ sub _accept {
     setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;
 
     # Start TLS handshake
-    $self->emit_safe(accept => $handle) and next unless my $tls = $self->{tls};
+    $self->emit(accept => $handle) and next unless my $tls = $self->{tls};
     $self->_handshake($self->{handles}{$handle} = $handle)
       if $handle = IO::Socket::SSL->start_SSL($handle, %$tls, SSL_server => 1);
   }
@@ -143,7 +138,7 @@ sub _tls {
   # Accepted
   if ($handle->accept_SSL) {
     $self->reactor->remove($handle);
-    return $self->emit_safe(accept => delete $self->{handles}{$handle});
+    return $self->emit(accept => delete $self->{handles}{$handle});
   }
 
   # Switch between reading and writing
@@ -195,7 +190,7 @@ emit the following new ones.
     ...
   });
 
-Emitted safely for each accepted connection.
+Emitted for each accepted connection.
 
 =head1 ATTRIBUTES
 
@@ -238,7 +233,7 @@ Get handle for server.
   $server->listen(port => 3000);
 
 Create a new listen socket. Note that TLS support depends on
-L<IO::Socket::SSL> (1.84+) and IPv6 support on L<IO::Socket::IP> (0.20+).
+L<IO::Socket::SSL> (1.84+).
 
 These options are currently available:
 
@@ -16,7 +16,7 @@ sub close {
   return unless my $handle  = delete $self->timeout(0)->{handle};
   $reactor->remove($handle);
   close $handle;
-  $self->emit_safe('close');
+  $self->emit('close');
 }
 
 sub close_gracefully {
@@ -76,7 +76,7 @@ sub timeout {
   return $self unless my $timeout = $self->{timeout} = shift;
   weaken $self;
   $self->{timer}
-    = $reactor->timer($timeout => sub { $self->emit_safe('timeout')->close });
+    = $reactor->timer($timeout => sub { $self->emit('timeout')->close });
 
   return $self;
 }
@@ -113,7 +113,7 @@ sub _read {
   my $read = $self->{handle}->sysread(my $buffer, 131072, 0);
   return $self->_error unless defined $read;
   return $self->close if $read == 0;
-  $self->emit_safe(read => $buffer)->_again;
+  $self->emit(read => $buffer)->_again;
 }
 
 sub _write {
@@ -123,13 +123,12 @@ sub _write {
   if (length $self->{buffer}) {
     my $written = $handle->syswrite($self->{buffer});
     return $self->_error unless defined $written;
-    $self->emit_safe(write => substr($self->{buffer}, 0, $written, ''));
-    $self->_again;
+    $self->emit(write => substr($self->{buffer}, 0, $written, ''))->_again;
   }
 
-  $self->emit_safe('drain') if !length $self->{buffer};
-  return                    if $self->is_writing;
-  return $self->close       if $self->{graceful};
+  $self->emit('drain') if !length $self->{buffer};
+  return               if $self->is_writing;
+  return $self->close  if $self->{graceful};
   $self->reactor->watch($handle, !$self->{paused}, 0) if $self->{handle};
 }
 
@@ -184,7 +183,7 @@ emit the following new ones.
     ...
   });
 
-Emitted safely if the stream gets closed.
+Emitted if the stream gets closed.
 
 =head2 drain
 
@@ -193,7 +192,7 @@ Emitted safely if the stream gets closed.
     ...
   });
 
-Emitted safely once all data has been written.
+Emitted once all data has been written.
 
 =head2 error
 
@@ -211,7 +210,7 @@ Emitted if an error occurs on the stream, fatal if unhandled.
     ...
   });
 
-Emitted safely if new data arrives on the stream.
+Emitted if new data arrives on the stream.
 
 =head2 timeout
 
@@ -220,8 +219,8 @@ Emitted safely if new data arrives on the stream.
     ...
   });
 
-Emitted safely if the stream has been inactive for too long and will get
-closed automatically.
+Emitted if the stream has been inactive for too long and will get closed
+automatically.
 
 =head2 write
 
@@ -230,7 +229,7 @@ closed automatically.
     ...
   });
 
-Emitted safely if new data has been written to the stream.
+Emitted if new data has been written to the stream.
 
 =head1 ATTRIBUTES
 
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDaTCCAtKgAwIBAgIJAI+AzotR68CTMA0GCSqGSIb3DQEBBQUAMIGAMQswCQYD
+VQQGEwJERTEWMBQGA1UECBMNTmllZGVyc2FjaHNlbjESMBAGA1UEBxMJSGFtYmVy
+Z2VuMRQwEgYDVQQKEwtNb2pvbGljaW91czESMBAGA1UEAxMJbG9jYWxob3N0MRsw
+GQYJKoZIhvcNAQkBFgxzcmlAY3Bhbi5vcmcwHhcNMTIwNDE4MTczOTU5WhcNMzIw
+NDEzMTczOTU5WjCBgDELMAkGA1UEBhMCREUxFjAUBgNVBAgTDU5pZWRlcnNhY2hz
+ZW4xEjAQBgNVBAcTCUhhbWJlcmdlbjEUMBIGA1UEChMLTW9qb2xpY2lvdXMxEjAQ
+BgNVBAMTCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMc3JpQGNwYW4ub3JnMIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCohcU0qG+hHn6JK8XdygAJo7EuRqG2
+5GSHaRRMyYgd89tEluInMH86tVcktJ1s/0VVvr5anAp8L7Pgu01Wr13OfgIzBxCz
+51ZIFxq4DtimBftXs9Z9M0sui2NuIPDrMEjkYUhUsxMEZcDSp2KJjDosZjSYUiiF
+G2ACvVGXSrS16QIDAQABo4HoMIHlMB0GA1UdDgQWBBSrZ+hIlPTgV7xx2O9wzdIO
+/d4osDCBtQYDVR0jBIGtMIGqgBSrZ+hIlPTgV7xx2O9wzdIO/d4osKGBhqSBgzCB
+gDELMAkGA1UEBhMCREUxFjAUBgNVBAgTDU5pZWRlcnNhY2hzZW4xEjAQBgNVBAcT
+CUhhbWJlcmdlbjEUMBIGA1UEChMLTW9qb2xpY2lvdXMxEjAQBgNVBAMTCWxvY2Fs
+aG9zdDEbMBkGCSqGSIb3DQEJARYMc3JpQGNwYW4ub3JnggkAj4DOi1HrwJMwDAYD
+VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAq6MXA7ZeO7B7vAcWxQKeLPKSy
+Jzkb1bC/agaISDbOwuZ1AoQSj6OQHKhNIdY5v/oLQJ0B8wB0dIigqn1WVacDtPgu
+PKSrxpqieDCh2bJ7+dyQIzQHgtZqPHi5k1PyNNXQxC94kPWdFp6PpF0M/y97aCxC
+ZQjKgDfncFWY3FHqUw==
+-----END CERTIFICATE-----
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCohcU0qG+hHn6JK8XdygAJo7EuRqG25GSHaRRMyYgd89tEluIn
+MH86tVcktJ1s/0VVvr5anAp8L7Pgu01Wr13OfgIzBxCz51ZIFxq4DtimBftXs9Z9
+M0sui2NuIPDrMEjkYUhUsxMEZcDSp2KJjDosZjSYUiiFG2ACvVGXSrS16QIDAQAB
+AoGALSdqp6lZ/7nD/c0Uv1CYofySROv3+KFJrl6hadG1/xCP99jVz9pWvMxKBTO/
+2qyrT0ZEitK0nIHLmLOXDVr/rxzbxP/kHmkOLKj45jW31BSap89tUpFjFQXFfjwT
+YnOgOB4+eqQuGwigCqabcQPtFC4fU7Qzk7pdz/kO4FjR0GECQQDdXthCKgS7E5Zy
+qqzjepxYvKgkWPD3G9H6I8LOtiVBdcehflF8Y61OGsEST3pbOhrijhY281VnD1AG
+pNL1rOhDAkEAwuKKTN+2GF3m1mPtGW9jpkP8gU2zcO945U0jxpn2srjQ9oIoB45Y
+gqtE6yybRY4BBd+hMdgeH5dXSwsZW+FMYwJASrFy5LhKylisndoq5cJ8OJDHZyQ/
+ghF4Ax/H3nmlDnZQOpRlqEP1uPHcDXKVxWxQn/rzUe0+9rw681Lv/4ctAwJAfyLO
+2muvHaJUr1QtH0S9m4AKwEfyYiC3m8+BIVTbzagoGki62IMSVtxob4uAGBYVsME9
+JYk5zZ4rgndRKdGGxQJBAIpbdLBKArvnpbYIqNJGG83mUZ/VZaQl0G+S3zGkgre9
+KjIuz10nNMNAKmGRrTbClLtvAQ9MVa3Xjnp+XmxPFho=
+-----END RSA PRIVATE KEY-----
@@ -1,21 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDaTCCAtKgAwIBAgIJAI+AzotR68CTMA0GCSqGSIb3DQEBBQUAMIGAMQswCQYD
-VQQGEwJERTEWMBQGA1UECBMNTmllZGVyc2FjaHNlbjESMBAGA1UEBxMJSGFtYmVy
-Z2VuMRQwEgYDVQQKEwtNb2pvbGljaW91czESMBAGA1UEAxMJbG9jYWxob3N0MRsw
-GQYJKoZIhvcNAQkBFgxzcmlAY3Bhbi5vcmcwHhcNMTIwNDE4MTczOTU5WhcNMzIw
-NDEzMTczOTU5WjCBgDELMAkGA1UEBhMCREUxFjAUBgNVBAgTDU5pZWRlcnNhY2hz
-ZW4xEjAQBgNVBAcTCUhhbWJlcmdlbjEUMBIGA1UEChMLTW9qb2xpY2lvdXMxEjAQ
-BgNVBAMTCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMc3JpQGNwYW4ub3JnMIGf
-MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCohcU0qG+hHn6JK8XdygAJo7EuRqG2
-5GSHaRRMyYgd89tEluInMH86tVcktJ1s/0VVvr5anAp8L7Pgu01Wr13OfgIzBxCz
-51ZIFxq4DtimBftXs9Z9M0sui2NuIPDrMEjkYUhUsxMEZcDSp2KJjDosZjSYUiiF
-G2ACvVGXSrS16QIDAQABo4HoMIHlMB0GA1UdDgQWBBSrZ+hIlPTgV7xx2O9wzdIO
-/d4osDCBtQYDVR0jBIGtMIGqgBSrZ+hIlPTgV7xx2O9wzdIO/d4osKGBhqSBgzCB
-gDELMAkGA1UEBhMCREUxFjAUBgNVBAgTDU5pZWRlcnNhY2hzZW4xEjAQBgNVBAcT
-CUhhbWJlcmdlbjEUMBIGA1UEChMLTW9qb2xpY2lvdXMxEjAQBgNVBAMTCWxvY2Fs
-aG9zdDEbMBkGCSqGSIb3DQEJARYMc3JpQGNwYW4ub3JnggkAj4DOi1HrwJMwDAYD
-VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAq6MXA7ZeO7B7vAcWxQKeLPKSy
-Jzkb1bC/agaISDbOwuZ1AoQSj6OQHKhNIdY5v/oLQJ0B8wB0dIigqn1WVacDtPgu
-PKSrxpqieDCh2bJ7+dyQIzQHgtZqPHi5k1PyNNXQxC94kPWdFp6PpF0M/y97aCxC
-ZQjKgDfncFWY3FHqUw==
------END CERTIFICATE-----
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQCohcU0qG+hHn6JK8XdygAJo7EuRqG25GSHaRRMyYgd89tEluIn
-MH86tVcktJ1s/0VVvr5anAp8L7Pgu01Wr13OfgIzBxCz51ZIFxq4DtimBftXs9Z9
-M0sui2NuIPDrMEjkYUhUsxMEZcDSp2KJjDosZjSYUiiFG2ACvVGXSrS16QIDAQAB
-AoGALSdqp6lZ/7nD/c0Uv1CYofySROv3+KFJrl6hadG1/xCP99jVz9pWvMxKBTO/
-2qyrT0ZEitK0nIHLmLOXDVr/rxzbxP/kHmkOLKj45jW31BSap89tUpFjFQXFfjwT
-YnOgOB4+eqQuGwigCqabcQPtFC4fU7Qzk7pdz/kO4FjR0GECQQDdXthCKgS7E5Zy
-qqzjepxYvKgkWPD3G9H6I8LOtiVBdcehflF8Y61OGsEST3pbOhrijhY281VnD1AG
-pNL1rOhDAkEAwuKKTN+2GF3m1mPtGW9jpkP8gU2zcO945U0jxpn2srjQ9oIoB45Y
-gqtE6yybRY4BBd+hMdgeH5dXSwsZW+FMYwJASrFy5LhKylisndoq5cJ8OJDHZyQ/
-ghF4Ax/H3nmlDnZQOpRlqEP1uPHcDXKVxWxQn/rzUe0+9rw681Lv/4ctAwJAfyLO
-2muvHaJUr1QtH0S9m4AKwEfyYiC3m8+BIVTbzagoGki62IMSVtxob4uAGBYVsME9
-JYk5zZ4rgndRKdGGxQJBAIpbdLBKArvnpbYIqNJGG83mUZ/VZaQl0G+S3zGkgre9
-KjIuz10nNMNAKmGRrTbClLtvAQ9MVa3Xjnp+XmxPFho=
------END RSA PRIVATE KEY-----
@@ -320,11 +320,12 @@ right in, to make writing test servers as easy as possible. Also note that for
 convenience the C<PIPE> signal will be set to C<IGNORE> when L<Mojo::IOLoop>
 is loaded.
 
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and
+For better scalability (epoll, kqueue) and to provide non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.12+), L<IO::Socket::Socks> (0.64+) and
 L<IO::Socket::SSL> (1.84+) will be used automatically if they are installed.
-Individual features can also be disabled with the C<MOJO_NO_IPV6> and
-C<MOJO_NO_TLS> environment variables.
+Individual features can also be disabled with the C<MOJO_NO_NDN>,
+C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
 
 See L<Mojolicious::Guides::Cookbook/"REAL-TIME WEB"> for more.
 
@@ -1,7 +1,7 @@
 package Mojo::JSON::Pointer;
 use Mojo::Base -base;
 
-use Scalar::Util 'looks_like_number';
+use Mojo::Util 'deprecated';
 
 has 'data';
 
@@ -11,8 +11,13 @@ sub get      { shift->_pointer(0, @_) }
 sub new { @_ > 1 ? shift->SUPER::new(data => shift) : shift->SUPER::new }
 
 sub _pointer {
-  my ($self, $contains, $data, $pointer) = @_;
-  ($data, $pointer) = ($self->data, $data) unless defined $pointer;
+  my ($self, $contains, $pointer) = @_;
+  my $data = $self->data;
+
+  # DEPRECATED in Tiger Face!
+  deprecated 'Support for data arguments in Mojo::JSON::Pointer is DEPRECATED'
+    and (($pointer, $data) = ($_[3], $pointer))
+    if defined $_[3];
 
   return $data unless $pointer =~ s!^/!!;
   for my $p ($pointer eq '' ? ($pointer) : (split '/', $pointer)) {
@@ -23,7 +28,7 @@ sub _pointer {
     if (ref $data eq 'HASH' && exists $data->{$p}) { $data = $data->{$p} }
 
     # Array
-    elsif (ref $data eq 'ARRAY' && looks_like_number($p) && @$data > $p) {
+    elsif (ref $data eq 'ARRAY' && $p =~ /^\d+$/ && @$data > $p) {
       $data = $data->[$p];
     }
 
@@ -64,7 +69,7 @@ L<Mojo::JSON::Pointer> implements the following attributes.
   my $data = $pointer->data;
   $pointer = $pointer->data({foo => 'bar'});
 
-Data to be processed.
+Data structure to be processed.
 
 =head1 METHODS
 
@@ -74,40 +79,37 @@ the following new ones.
 =head2 contains
 
   my $bool = $pointer->contains('/foo/1');
-  my $bool = $pointer->contains($data, '/foo/1');
 
-Check if Perl data structure contains a value that can be identified with the
-given JSON Pointer, defaults to using L</data>.
+Check if L</"data"> contains a value that can be identified with the given
+JSON Pointer.
 
   # True
-  $pointer->contains({'♥' => 'mojolicious'}, '/♥');
-  $pointer->contains({foo => 'bar', baz => [4, 5, 6]}, '/foo');
-  $pointer->contains({foo => 'bar', baz => [4, 5, 6]}, '/baz/2');
+  Mojo::JSON::Pointer->new({'♥' => 'mojolicious'})->contains('/♥');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/foo');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/baz/1');
 
   # False
-  $pointer->contains({'♥' => 'mojolicious'}, '/☃');
-  $pointer->contains({foo => 'bar', baz => [4, 5, 6]}, '/bar');
-  $pointer->contains({foo => 'bar', baz => [4, 5, 6]}, '/baz/9');
+  Mojo::JSON::Pointer->new({'♥' => 'mojolicious'})->contains('/☃');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/bar');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5]})->contains('/baz/9');
 
 =head2 get
 
   my $value = $pointer->get('/foo/bar');
-  my $value = $pointer->get($data, '/foo/bar');
 
-Extract value identified by the given JSON Pointer, defaults to using
-L</data>.
+Extract value from L</"data"> identified by the given JSON Pointer.
 
   # "mojolicious"
-  $pointer->get({'♥' => 'mojolicious'}, '/♥');
+  Mojo::JSON::Pointer->new({'♥' => 'mojolicious'})->get('/♥');
 
   # "bar"
-  $pointer->get({foo => 'bar', baz => [4, 5, 6]}, '/foo');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5, 6]})->get('/foo');
 
   # "4"
-  $pointer->get({foo => 'bar', baz => [4, 5, 6]}, '/baz/0');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5, 6]})->get('/baz/0');
 
   # "6"
-  $pointer->get({foo => 'bar', baz => [4, 5, 6]}, '/baz/2');
+  Mojo::JSON::Pointer->new({foo => 'bar', baz => [4, 5, 6]})->get('/baz/2');
 
 =head2 new
 
@@ -4,12 +4,13 @@ use Mojo::Base -base;
 use B;
 use Carp 'croak';
 use Exporter 'import';
-use Mojo::Util;
+use Mojo::Util 'deprecated';
 use Scalar::Util 'blessed';
 
+# DEPRECATED in Tiger Face!
 has 'error';
 
-our @EXPORT_OK = qw(decode_json encode_json j);
+our @EXPORT_OK = qw(decode_json encode_json false from_json j to_json true);
 
 # Literal names
 my $FALSE = bless \(my $false = 0), 'Mojo::JSON::_Bool';
@@ -31,51 +32,64 @@ my %ESCAPE = (
 my %REVERSE = map { $ESCAPE{$_} => "\\$_" } keys %ESCAPE;
 for (0x00 .. 0x1f) { $REVERSE{pack 'C', $_} //= sprintf '\u%.4X', $_ }
 
+# DEPRECATED in Tiger Face!
 sub decode {
-  my $self = shift->error(undef);
-  my $value;
-  return $value if eval { $value = _decode(shift); 1 };
-  $self->error(_chomp($@));
-  return undef;
+  shift->error(my $err = _catch(\my $value, pop));
+  return defined $err ? undef : $value;
 }
 
 sub decode_json {
-  my $value;
-  return eval { $value = _decode(shift); 1 } ? $value : croak _chomp($@);
+  my $err = _catch(\my $value, shift);
+  return defined $err ? croak $err : $value;
 }
 
+# DEPRECATED in Tiger Face!
 sub encode { encode_json($_[1]) }
 
 sub encode_json { Mojo::Util::encode 'UTF-8', _encode_value(shift) }
 
 sub false {$FALSE}
 
+sub from_json {
+  my $err = _catch(\my $value, shift, 1);
+  return defined $err ? croak $err : $value;
+}
+
 sub j {
   return encode_json($_[0]) if ref $_[0] eq 'ARRAY' || ref $_[0] eq 'HASH';
   return eval { _decode($_[0]) };
 }
 
+# DEPRECATED in Tiger Face!
+sub new {
+  deprecated 'Object-oriented Mojo::JSON API is DEPRECATED';
+  return shift->SUPER::new(@_);
+}
+
+sub to_json { _encode_value(shift) }
+
 sub true {$TRUE}
 
-sub _chomp { chomp $_[0] ? $_[0] : $_[0] }
+sub _catch {
+  my $valueref = shift;
+  eval { $$valueref = _decode(@_); 1 } ? return undef : chomp $@;
+  return $@;
+}
 
 sub _decode {
 
   # Missing input
-  die "Missing or empty input\n" unless length(my $bytes = shift);
-
-  # Wide characters
-  die "Wide character in input\n" unless utf8::downgrade($bytes, 1);
+  die "Missing or empty input\n" unless length(local $_ = shift);
 
   # UTF-8
-  die "Input is not UTF-8 encoded\n"
-    unless defined(local $_ = Mojo::Util::decode('UTF-8', $bytes));
+  $_ = Mojo::Util::decode 'UTF-8', $_ unless shift;
+  die "Input is not UTF-8 encoded\n" unless defined;
 
   # Value
   my $value = _decode_value();
 
   # Leftover data
-  _exception('Unexpected data') unless m/\G[\x20\x09\x0a\x0d]*\z/gc;
+  _throw('Unexpected data') unless m/\G[\x20\x09\x0a\x0d]*\z/gc;
 
   return $value;
 }
@@ -94,7 +108,7 @@ sub _decode_array {
     last if m/\G[\x20\x09\x0a\x0d]*\]/gc;
 
     # Invalid character
-    _exception('Expected comma or right square bracket while parsing array');
+    _throw('Expected comma or right square bracket while parsing array');
   }
 
   return \@array;
@@ -106,14 +120,14 @@ sub _decode_object {
 
     # Quote
     m/\G[\x20\x09\x0a\x0d]*"/gc
-      or _exception('Expected string while parsing object');
+      or _throw('Expected string while parsing object');
 
     # Key
     my $key = _decode_string();
 
     # Colon
     m/\G[\x20\x09\x0a\x0d]*:/gc
-      or _exception('Expected colon while parsing object');
+      or _throw('Expected colon while parsing object');
 
     # Value
     $hash{$key} = _decode_value();
@@ -125,7 +139,7 @@ sub _decode_object {
     last if m/\G[\x20\x09\x0a\x0d]*\}/gc;
 
     # Invalid character
-    _exception('Expected comma or right curly bracket while parsing object');
+    _throw('Expected comma or right curly bracket while parsing object');
   }
 
   return \%hash;
@@ -140,9 +154,9 @@ sub _decode_string {
 
   # Invalid character
   unless (m/\G"/gc) {
-    _exception('Unexpected character or invalid escape while parsing string')
+    _throw('Unexpected character or invalid escape while parsing string')
       if m/\G[\x00-\x1f\\]/;
-    _exception('Unterminated string');
+    _throw('Unterminated string');
   }
 
   # Unescape popular characters
@@ -168,11 +182,11 @@ sub _decode_string {
 
         # High surrogate
         ($ord & 0xfc00) == 0xd800
-          or pos($_) = $pos + pos($str), _exception('Missing high-surrogate');
+          or pos($_) = $pos + pos($str), _throw('Missing high-surrogate');
 
         # Low surrogate
         $str =~ m/\G\\u([Dd][C-Fc-f]..)/gc
-          or pos($_) = $pos + pos($str), _exception('Missing low-surrogate');
+          or pos($_) = $pos + pos($str), _throw('Missing low-surrogate');
 
         $ord = 0x10000 + ($ord - 0xd800) * 0x400 + (hex($1) - 0xdc00);
       }
@@ -214,12 +228,11 @@ sub _decode_value {
   return undef if m/\Gnull/gc;
 
   # Invalid character
-  _exception('Expected string, array, object, number, boolean or null');
+  _throw('Expected string, array, object, number, boolean or null');
 }
 
 sub _encode_array {
-  my $array = shift;
-  return '[' . join(',', map { _encode_value($_) } @$array) . ']';
+  '[' . join(',', map { _encode_value($_) } @{$_[0]}) . ']';
 }
 
 sub _encode_object {
@@ -231,7 +244,7 @@ sub _encode_object {
 
 sub _encode_string {
   my $str = shift;
-  $str =~ s!([\x00-\x1f\x{2028}\x{2029}\\"/])!$REVERSE{$1}!gs;
+  $str =~ s!([\x00-\x1f\x{2028}\x{2029}\\"])!$REVERSE{$1}!gs;
   return "\"$str\"";
 }
 
@@ -270,7 +283,7 @@ sub _encode_value {
   return _encode_string($value);
 }
 
-sub _exception {
+sub _throw {
 
   # Leading whitespace
   m/\G[\x20\x09\x0a\x0d]*/gc;
@@ -302,15 +315,8 @@ Mojo::JSON - Minimalistic JSON
 
   use Mojo::JSON qw(decode_json encode_json);
 
-  # Encode and decode JSON (die on errors)
-  my $bytes = encode_json({foo => [1, 2], bar => 'hello!', baz => \1});
-  my $hash  = decode_json($bytes);
-
-  # Handle errors
-  my $json = Mojo::JSON->new;
-  my $hash = $json->decode($bytes);
-  my $err  = $json->error;
-  say $err ? "Error: $err" : $hash->{message};
+  my $bytes = encode_json {foo => [1, 2], bar => 'hello!', baz => \1};
+  my $hash  = decode_json $bytes;
 
 =head1 DESCRIPTION
 
@@ -321,7 +327,7 @@ It supports normal Perl data types like scalar, array reference, hash
 reference and will try to call the C<TO_JSON> method on blessed references, or
 stringify them if it doesn't exist. Differentiating between strings and
 numbers in Perl is hard, depending on how it has been used, a scalar can be
-both at the same time. The string value gets precedence unless both
+both at the same time. The string value has a higher precedence unless both
 representations are equivalent.
 
   [1, -2, 3]     -> [1, -2, 3]
@@ -350,65 +356,48 @@ individually.
 
 =head2 decode_json
 
-  my $value = decode_json($bytes);
+  my $value = decode_json $bytes;
 
 Decode JSON to Perl value and die if decoding fails.
 
 =head2 encode_json
 
-  my $bytes = encode_json({foo => 'bar'});
+  my $bytes = encode_json {i => '♥ mojolicious'};
 
 Encode Perl value to JSON.
 
-=head2 j
-
-  my $bytes = j([1, 2, 3]);
-  my $bytes = j({foo => 'bar'});
-  my $value = j($bytes);
-
-Encode Perl data structure (which may only be an array reference or hash
-reference) or decode JSON, an C<undef> return value indicates a bare C<null>
-or that decoding failed.
-
-=head1 ATTRIBUTES
-
-L<Mojo::JSON> implements the following attributes.
-
-=head2 error
-
-  my $err = $json->error;
-  $json   = $json->error('Parser error');
-
-Parser error.
+=head2 false
 
-=head1 METHODS
+  my $false = false;
 
-L<Mojo::JSON> inherits all methods from L<Mojo::Base> and implements the
-following new ones.
+False value, used because Perl has no native equivalent.
 
-=head2 decode
+=head2 from_json
 
-  my $value = $json->decode($bytes);
+  my $value = from_json $chars;
 
-Decode JSON to Perl value and set L</"error"> if decoding failed.
+Decode JSON text that is not C<UTF-8> encoded to Perl value and die if
+decoding fails.
 
-=head2 encode
+=head2 j
 
-  my $bytes = $json->encode({foo => 'bar'});
+  my $bytes = j [1, 2, 3];
+  my $bytes = j {i => '♥ mojolicious'};
+  my $value = j $bytes;
 
-Encode Perl value to JSON.
+Encode Perl data structure (which may only be an array reference or hash
+reference) or decode JSON, an C<undef> return value indicates a bare C<null>
+or that decoding failed.
 
-=head2 false
+=head2 to_json
 
-  my $false = Mojo::JSON->false;
-  my $false = $json->false;
+  my $chars = to_json {i => '♥ mojolicious'};
 
-False value, used because Perl has no native equivalent.
+Encode Perl value to JSON text without C<UTF-8> encoding it.
 
 =head2 true
 
-  my $true = Mojo::JSON->true;
-  my $true = $json->true;
+  my $true = true;
 
 True value, used because Perl has no native equivalent.
 
@@ -55,7 +55,7 @@ sub _all {
   seek $handle, 0, 0;
   my $data = join '', <$handle>;
 
-  # Ignore everything before __DATA__ (Windows will seek to start of file)
+  # Ignore everything before __DATA__ (some versions seek to start of file)
   $data =~ s/^.*\n__DATA__\r?\n/\n/s;
 
   # Ignore everything after __END__
@@ -8,15 +8,12 @@ use Mojo::Util 'encode';
 has format => sub { \&_format };
 has handle => sub {
 
-  # File
-  if (my $path = shift->path) {
-    croak qq{Can't open log file "$path": $!}
-      unless open my $file, '>>', $path;
-    return $file;
-  }
-
   # STDERR
-  return \*STDERR;
+  return \*STDERR unless my $path = shift->path;
+
+  # File
+  croak qq{Can't open log file "$path": $!} unless open my $file, '>>', $path;
+  return $file;
 };
 has history => sub { [] };
 has level => 'debug';
@@ -11,11 +11,9 @@ has url => sub { Mojo::URL->new };
 has 'reverse_proxy';
 
 my $START_LINE_RE = qr/
-  ^
-  ([a-zA-Z]+)                                               # Method
+  ^([a-zA-Z]+)                                              # Method
   \s+([0-9a-zA-Z!#\$\%&'()*+,\-.\/:;=?\@[\\\]^_`\{|\}~]+)   # URL
-  \s+HTTP\/(\d\.\d)                                         # Version
-  $
+  \s+HTTP\/(\d\.\d)$                                        # Version
 /x;
 
 sub clone {
@@ -53,6 +51,8 @@ sub cookies {
   return $self;
 }
 
+sub every_param { shift->params->every_param(@_) }
+
 sub extract_start_line {
   my ($self, $bufref) = @_;
 
@@ -60,8 +60,7 @@ sub extract_start_line {
   return undef unless $$bufref =~ s/^\s*(.*?)\x0d?\x0a//;
 
   # We have a (hopefully) full request line
-  $self->error({message => 'Bad request start line', advice => 400})
-    and return undef
+  return !$self->error({message => 'Bad request start line', advice => 400})
     unless $1 =~ $START_LINE_RE;
   my $url = $self->method($1)->version($3)->url;
   return !!($1 eq 'CONNECT' ? $url->authority($2) : $url->parse($2));
@@ -107,9 +106,8 @@ sub get_start_line_chunk {
     }
 
     # Proxy
-    elsif ($self->proxy) {
-      $path = $url->clone->userinfo(undef)
-        unless $self->is_handshake || $url->protocol eq 'https';
+    elsif ($self->proxy && $url->protocol ne 'https') {
+      $path = $url->clone->userinfo(undef) unless $self->is_handshake;
     }
 
     $self->{start_buffer} = "$method $path HTTP/@{[$self->version]}\x0d\x0a";
@@ -225,10 +223,8 @@ sub _parse_env {
   $self->method($env->{REQUEST_METHOD}) if $env->{REQUEST_METHOD};
 
   # Scheme/Version
-  if (($env->{SERVER_PROTOCOL} // '') =~ m!^([^/]+)/([^/]+)$!) {
-    $base->scheme($1);
-    $self->version($2);
-  }
+  $base->scheme($1) and $self->version($2)
+    if ($env->{SERVER_PROTOCOL} // '') =~ m!^([^/]+)/([^/]+)$!;
 
   # HTTPS
   $base->scheme('https') if uc($env->{HTTPS} // '') eq 'ON';
@@ -357,6 +353,16 @@ Clone request if possible, otherwise return C<undef>.
 
 Access request cookies, usually L<Mojo::Cookie::Request> objects.
 
+=head2 every_param
+
+  my $values = $req->every_param('foo');
+
+Similar to L</"param">, but returns all values sharing the same name as an
+array reference.
+
+  # Get first value
+  say $req->every_param('foo')->[0];
+
 =head2 extract_start_line
 
   my $bool = $req->extract_start_line(\$str);
@@ -396,25 +402,29 @@ Check C<X-Requested-With> header for C<XMLHttpRequest> value.
 =head2 param
 
   my @names       = $req->param;
-  my $foo         = $req->param('foo');
-  my @foo         = $req->param('foo');
+  my $value       = $req->param('foo');
   my ($foo, $bar) = $req->param(['foo', 'bar']);
 
-Access C<GET> and C<POST> parameters. Note that this method caches all data,
-so it should not be called before the entire request body has been received.
-Parts of the request body need to be loaded into memory to parse C<POST>
-parameters, so you have to make sure it is not excessively large, there's a
-10MB limit by default.
+Access C<GET> and C<POST> parameters extracted from the query string and
+C<application/x-www-form-urlencoded> or C<multipart/form-data> message body.
+If there are multiple values sharing the same name, and you want to access
+more than just the last one, you can use L</"every_param">. Note that this
+method caches all data, so it should not be called before the entire request
+body has been received. Parts of the request body need to be loaded into
+memory to parse C<POST> parameters, so you have to make sure it is not
+excessively large, there's a 10MB limit by default.
 
 =head2 params
 
   my $params = $req->params;
 
-All C<GET> and C<POST> parameters, usually a L<Mojo::Parameters> object. Note
-that this method caches all data, so it should not be called before the entire
-request body has been received. Parts of the request body need to be loaded
-into memory to parse C<POST> parameters, so you have to make sure it is not
-excessively large, there's a 10MB limit by default.
+All C<GET> and C<POST> parameters extracted from the query string and
+C<application/x-www-form-urlencoded> or C<multipart/form-data> message body,
+usually a L<Mojo::Parameters> object. Note that this method caches all data,
+so it should not be called before the entire request body has been received.
+Parts of the request body need to be loaded into memory to parse C<POST>
+parameters, so you have to make sure it is not excessively large, there's a
+10MB limit by default.
 
   # Get parameter value
   say $req->params->param('foo');
@@ -56,7 +56,6 @@ my %MESSAGES = (
   428 => 'Precondition Required',              # RFC 6585
   429 => 'Too Many Requests',                  # RFC 6585
   431 => 'Request Header Fields Too Large',    # RFC 6585
-  451 => 'Unavailable For Legal Reasons',      # Draft
   500 => 'Internal Server Error',
   501 => 'Not Implemented',
   502 => 'Bad Gateway',
@@ -95,12 +94,12 @@ sub extract_start_line {
 
   # We have a full response line
   return undef unless $$bufref =~ s/^(.*?)\x0d?\x0a//;
-  $self->error({message => 'Bad response start line'}) and return undef
+  return !$self->error({message => 'Bad response start line'})
     unless $1 =~ m!^\s*HTTP/(\d\.\d)\s+(\d\d\d)\s*(.+)?$!;
 
   my $content = $self->content;
   $content->skip_body(1) if $self->code($2)->is_empty;
-  $content->auto_relax(1) unless defined $content->auto_relax;
+  defined $content->$_ or $content->$_(1) for qw(auto_decompress auto_relax);
   $content->expect_close(1) if $1 eq '1.0';
   return !!$self->version($1)->message($3);
 }
@@ -190,14 +189,14 @@ implements the following new ones.
   my $code = $res->code;
   $res     = $res->code(200);
 
-HTTP response code.
+HTTP response status code.
 
 =head2 message
 
   my $msg = $res->message;
   $res    = $res->message('OK');
 
-HTTP response message.
+HTTP response status message.
 
 =head1 METHODS
 
@@ -215,8 +214,10 @@ Access response cookies, usually L<Mojo::Cookie::Response> objects.
 =head2 default_message
 
   my $msg = $res->default_message;
+  my $msg = $res->default_message(418);
 
-Generate default response message for code.
+Generate default response message for status code, defaults to using
+L</"code">.
 
 =head2 extract_start_line
 
@@ -9,7 +9,7 @@ use Mojo::JSON 'j';
 use Mojo::JSON::Pointer;
 use Mojo::Parameters;
 use Mojo::Upload;
-use Mojo::Util 'decode';
+use Mojo::Util qw(decode split_header);
 
 has content => sub { Mojo::Content::Single->new };
 has default_charset  => 'UTF-8';
@@ -61,7 +61,7 @@ sub build_body       { shift->_build('get_body_chunk') }
 sub build_headers    { shift->_build('get_header_chunk') }
 sub build_start_line { shift->_build('get_start_line_chunk') }
 
-sub cookie { shift->_cache(cookie => @_) }
+sub cookie { shift->_cache('cookie', 0, @_) }
 
 sub cookies { croak 'Method "cookies" not implemented by subclass' }
 
@@ -79,6 +79,9 @@ sub error {
   return $self->finish;
 }
 
+sub every_cookie { shift->_cache('cookie', 1, @_) }
+sub every_upload { shift->_cache('upload', 1, @_) }
+
 sub extract_start_line {
   croak 'Method "extract_start_line" not implemented by subclass';
 }
@@ -139,8 +142,6 @@ sub json {
   return $pointer ? Mojo::JSON::Pointer->new($data)->get($pointer) : $data;
 }
 
-sub param { shift->body_params->param(@_) }
-
 sub parse {
   my ($self, $chunk) = @_;
 
@@ -194,7 +195,7 @@ sub to_string {
   return $self->build_start_line . $self->build_headers . $self->build_body;
 }
 
-sub upload { shift->_cache(upload => @_) }
+sub upload { shift->_cache('upload', 0, @_) }
 
 sub uploads {
   my $self = shift;
@@ -234,10 +235,10 @@ sub _build {
 }
 
 sub _cache {
-  my ($self, $method, $name) = @_;
+  my ($self, $method, $all, $name) = @_;
 
   # Multiple names
-  return map { scalar $self->$method($_) } @$name if ref $name eq 'ARRAY';
+  return map { $self->$method($_) } @$name if ref $name eq 'ARRAY';
 
   # Cache objects by name
   $method .= 's';
@@ -246,8 +247,8 @@ sub _cache {
     push @{$self->{$method}{$_->name}}, $_ for @{$self->$method};
   }
 
-  return unless my $objects = $self->{$method}{$name};
-  return wantarray ? @$objects : $objects->[0];
+  my $objects = $self->{$method}{$name} || [];
+  return $all ? $objects : $objects->[-1];
 }
 
 sub _limit {
@@ -456,13 +457,14 @@ Render start line.
 
 =head2 cookie
 
-  my $foo         = $msg->cookie('foo');
-  my @foo         = $msg->cookie('foo');
+  my $cookie      = $msg->cookie('foo');
   my ($foo, $bar) = $msg->cookie(['foo', 'bar']);
 
 Access message cookies, usually L<Mojo::Cookie::Request> or
-L<Mojo::Cookie::Response> objects. Note that this method caches all data, so
-it should not be called before all headers have been received.
+L<Mojo::Cookie::Response> objects. If there are multiple cookies sharing the
+same name, and you want to access more than just the last one, you can use
+L</"every_cookie">. Note that this method caches all data, so it should not be
+called before all headers have been received.
 
   # Get cookie value
   say $msg->cookie('foo')->value;
@@ -486,11 +488,11 @@ whole message body needs to be loaded into memory to parse it, so you have to
 make sure it is not excessively large, there's a 10MB limit by default.
 
   # Perform "find" right away
-  say $msg->dom('h1, h2, h3')->text;
+  say $msg->dom('h1, h2, h3')->map('text')->join("\n");
 
   # Use everything else Mojo::DOM has to offer
   say $msg->dom->at('title')->text;
-  say $msg->dom->html->body->children->type->uniq;
+  say $msg->dom->at('body')->children->map('type')->uniq->join("\n");
 
 =head2 error
 
@@ -500,6 +502,26 @@ make sure it is not excessively large, there's a 10MB limit by default.
 Get or set message error, an C<undef> return value indicates that there is no
 error.
 
+=head2 every_cookie
+
+  my $cookies = $msg->every_cookie('foo');
+
+Similar to L</"cookie">, but returns all message cookies sharing the same name
+as an array reference.
+
+  # Get first cookie value
+  say $msg->every_cookie('foo')->[0]->value;
+
+=head2 every_upload
+
+  my $uploads = $msg->every_upload('foo');
+
+Similar to L</"upload">, but returns all file uploads sharing the same name as
+an array reference.
+
+  # Get content of first uploaded file
+  say $msg->every_upload('foo')->[0]->asset->slurp;
+
 =head2 extract_start_line
 
   my $bool = $msg->extract_start_line(\$str);
@@ -578,19 +600,6 @@ sure it is not excessively large, there's a 10MB limit by default.
   say $msg->json->{foo}{bar}[23];
   say $msg->json('/foo/bar/23');
 
-=head2 param
-
-  my @names       = $msg->param;
-  my $foo         = $msg->param('foo');
-  my @foo         = $msg->param('foo');
-  my ($foo, $bar) = $msg->param(['foo', 'bar']);
-
-Access C<POST> parameters. Note that this method caches all data, so it should
-not be called before the entire message body has been received. Parts of the
-message body need to be loaded into memory to parse C<POST> parameters, so you
-have to make sure it is not excessively large, there's a 10MB limit by
-default.
-
 =head2 parse
 
   $msg = $msg->parse('HTTP/1.1 200 OK...');
@@ -618,13 +627,14 @@ Render whole message.
 
 =head2 upload
 
-  my $foo         = $msg->upload('foo');
-  my @foo         = $msg->upload('foo');
+  my $upload      = $msg->upload('foo');
   my ($foo, $bar) = $msg->upload(['foo', 'bar']);
 
 Access C<multipart/form-data> file uploads, usually L<Mojo::Upload> objects.
-Note that this method caches all data, so it should not be called before the
-entire message body has been received.
+If there are multiple uploads sharing the same name, and you want to access
+more than just the last one, you can use L</"every_upload">. Note that this
+method caches all data, so it should not be called before the entire message
+body has been received.
 
   # Get content of uploaded file
   say $msg->upload('foo')->asset->slurp;
@@ -11,18 +11,16 @@ use Mojo::Util qw(decode encode url_escape url_unescape);
 has charset => 'UTF-8';
 
 sub append {
-  my ($self, @pairs) = @_;
+  my $self = shift;
 
   my $params = $self->params;
-  for (my $i = 0; $i < @pairs; $i += 2) {
-    my $key   = $pairs[$i]     // '';
-    my $value = $pairs[$i + 1] // '';
+  while (my ($name, $value) = splice @_, 0, 2) {
 
     # Single value
-    if (ref $value ne 'ARRAY') { push @$params, $key => $value }
+    if (ref $value ne 'ARRAY') { push @$params, $name => $value }
 
     # Multiple values
-    else { push @$params, $key => (defined $_ ? "$_" : '') for @$value }
+    else { push @$params, $name => (defined $_ ? "$_" : '') for @$value }
   }
 
   return $self;
@@ -31,13 +29,16 @@ sub append {
 sub clone {
   my $self = shift;
 
-  my $clone = $self->new->charset($self->charset);
-  if (defined $self->{string}) { $clone->{string} = $self->{string} }
-  else                         { $clone->params([@{$self->params}]) }
+  my $clone = $self->new;
+  if   (exists $self->{charset}) { $clone->{charset} = $self->{charset} }
+  if   (defined $self->{string}) { $clone->{string}  = $self->{string} }
+  else                           { $clone->{params}  = [@{$self->params}] }
 
   return $clone;
 }
 
+sub every_param { shift->_param(@_) }
+
 sub merge {
   my $self = shift;
   push @{$self->params}, @{$_->params} for @_;
@@ -53,20 +54,14 @@ sub param {
   return sort keys %{$self->to_hash} unless $name;
 
   # Multiple names
-  return map { scalar $self->param($_) } @$name if ref $name eq 'ARRAY';
+  return map { $self->param($_) } @$name if ref $name eq 'ARRAY';
+
+  # Last value
+  return $self->_param($name)->[-1] unless @_;
 
   # Replace values
   $self->remove($name) if defined $_[0];
-  return $self->append($name => ref $_[0] eq 'ARRAY' ? $_[0] : [@_]) if @_;
-
-  # List values
-  my @values;
-  my $params = $self->params;
-  for (my $i = 0; $i < @$params; $i += 2) {
-    push @values, $params->[$i + 1] if $params->[$i] eq $name;
-  }
-
-  return wantarray ? @values : $values[0];
+  return $self->append($name => ref $_[0] eq 'ARRAY' ? $_[0] : [@_]);
 }
 
 sub params {
@@ -107,38 +102,35 @@ sub parse {
   my $self = shift;
 
   # Pairs
-  if (@_ > 1) { $self->append(@_) }
+  return $self->append(@_) if @_ > 1;
 
   # String
-  else { $self->{string} = shift }
-
+  $self->{string} = shift;
   return $self;
 }
 
 sub remove {
-  my $self = shift;
-  my $name = shift // '';
+  my ($self, $name) = @_;
 
   my $params = $self->params;
-  for (my $i = 0; $i < @$params;) {
-    if ($params->[$i] eq $name) { splice @$params, $i, 2 }
-    else                        { $i += 2 }
-  }
+  my $i      = 0;
+  $params->[$i] eq $name ? splice @$params, $i, 2 : ($i += 2)
+    while $i < @$params;
 
-  return $self->params($params);
+  return $self;
 }
 
 sub to_hash {
   my $self = shift;
 
-  my $params = $self->params;
   my %hash;
+  my $params = $self->params;
   for (my $i = 0; $i < @$params; $i += 2) {
     my ($name, $value) = @{$params}[$i, $i + 1];
 
     # Array
     if (exists $hash{$name}) {
-      $hash{$name} = [$hash{$name}] unless ref $hash{$name} eq 'ARRAY';
+      $hash{$name} = [$hash{$name}] if ref $hash{$name} ne 'ARRAY';
       push @{$hash{$name}}, $value;
     }
 
@@ -159,7 +151,7 @@ sub to_string {
     return url_escape $str, '^A-Za-z0-9\-._~!$&\'()*+,;=%:@/?';
   }
 
-  # Build pairs;
+  # Build pairs
   my $params = $self->params;
   return '' unless @$params;
   my @pairs;
@@ -179,6 +171,18 @@ sub to_string {
   return join '&', @pairs;
 }
 
+sub _param {
+  my ($self, $name) = @_;
+
+  my @values;
+  my $params = $self->params;
+  for (my $i = 0; $i < @$params; $i += 2) {
+    push @values, $params->[$i + 1] if $params->[$i] eq $name;
+  }
+
+  return \@values;
+}
+
 1;
 
 =encoding utf8
@@ -204,7 +208,7 @@ Mojo::Parameters - Parameters
 
 L<Mojo::Parameters> is a container for form parameters used by L<Mojo::URL>
 and based on L<RFC 3986|http://tools.ietf.org/html/rfc3986> as well as the
-L<HTML Living Standard|http://www.whatwg.org/html>.
+L<HTML Living Standard|https://html.spec.whatwg.org>.
 
 =head1 ATTRIBUTES
 
@@ -248,6 +252,16 @@ Append parameters. Note that this method will normalize the parameters.
 
 Clone parameters.
 
+=head2 every_param
+
+  my $values = $params->every_param('foo');
+
+Similar to L</"param">, but returns all values sharing the same name as an
+array reference. Note that this method will normalize the parameters.
+
+  # Get first value
+  say $params->every_param('foo')->[0];
+
 =head2 merge
 
   $params = $params->merge(Mojo::Parameters->new(foo => 'b&ar', baz => 23));
@@ -269,18 +283,15 @@ necessary.
 =head2 param
 
   my @names       = $params->param;
-  my $foo         = $params->param('foo');
-  my @foo         = $params->param('foo');
+  my $value       = $params->param('foo');
   my ($foo, $bar) = $params->param(['foo', 'bar']);
   $params         = $params->param(foo => 'ba&r');
   $params         = $params->param(foo => qw(ba&r baz));
   $params         = $params->param(foo => ['ba;r', 'baz']);
 
-Check and replace parameter value. Be aware that if you request a parameter by
-name in scalar context, you will receive only the I<first> value for that
-parameter, if there are multiple values for that name. In list context you
-will receive I<all> of the values for that name. Note that this method will
-normalize the parameters.
+Access parameter values. If there are multiple values sharing the same name,
+and you want to access more than just the last one, you can use
+L</"every_param">. Note that this method will normalize the parameters.
 
 =head2 params
 
@@ -13,26 +13,21 @@ has charset => 'UTF-8';
 sub canonicalize {
   my $self = shift;
 
-  my @parts;
-  for my $part (@{$self->parts}) {
-
-    # ".."
-    if ($part eq '..') {
-      (@parts && $parts[-1] ne '..') ? pop @parts : push @parts, '..';
-    }
-
-    # Something else than "."
-    elsif ($part ne '.' && $part ne '') { push @parts, $part }
+  my $parts = $self->parts;
+  for (my $i = 0; $i <= $#$parts;) {
+    if ($parts->[$i] eq '.' || $parts->[$i] eq '') { splice @$parts, $i, 1 }
+    elsif ($i < 1 || $parts->[$i] ne '..' || $parts->[$i - 1] eq '..') { $i++ }
+    else { splice @$parts, --$i, 2 }
   }
-  $self->trailing_slash(undef) unless @parts;
 
-  return $self->parts(\@parts);
+  return @$parts ? $self : $self->trailing_slash(undef);
 }
 
 sub clone {
   my $self = shift;
 
-  my $clone = $self->new->charset($self->charset);
+  my $clone = $self->new;
+  if (exists $self->{charset}) { $clone->{charset} = $self->{charset} }
   if (my $parts = $self->{parts}) {
     $clone->{$_} = $self->{$_} for qw(leading_slash trailing_slash);
     $clone->{parts} = [@$parts];
@@ -42,10 +37,7 @@ sub clone {
   return $clone;
 }
 
-sub contains {
-  my ($self, $path) = @_;
-  return $path eq '/' || $self->to_route =~ m!^\Q$path\E(?:/|$)!;
-}
+sub contains { $_[1] eq '/' || $_[0]->to_route =~ m!^\Q$_[1]\E(?:/|$)! }
 
 sub leading_slash { shift->_parse(leading_slash => @_) }
 
@@ -86,8 +78,7 @@ sub to_dir {
 
 sub to_route {
   my $clone = shift->clone;
-  my $route = join '/', @{$clone->parts};
-  return "/$route" . ($clone->trailing_slash ? '/' : '');
+  return '/' . join '/', @{$clone->parts}, $clone->trailing_slash ? '' : ();
 }
 
 sub to_string {
@@ -30,15 +30,15 @@ sub timer { shift->_timer(0, @_) }
 sub watch {
   my ($self, $handle, $read, $write) = @_;
 
+  my $mode = 0;
+  $mode |= EV::READ  if $read;
+  $mode |= EV::WRITE if $write;
+
   my $fd = fileno $handle;
   my $io = $self->{io}{$fd};
-  my $mode;
-  if ($read && $write) { $mode = EV::READ | EV::WRITE }
-  elsif ($read)  { $mode = EV::READ }
-  elsif ($write) { $mode = EV::WRITE }
-  else           { delete $io->{watcher} }
-  if (my $w = $io->{watcher}) { $w->set($fd, $mode) }
-  elsif ($mode) {
+  if ($mode == 0) { delete $io->{watcher} }
+  elsif (my $w = $io->{watcher}) { $w->set($fd, $mode) }
+  else {
     weaken $self;
     $io->{watcher} = EV::io($fd, $mode, sub { $self->_io($fd, @_) });
   }
@@ -49,9 +49,9 @@ sub watch {
 sub _io {
   my ($self, $fd, $w, $revents) = @_;
   my $io = $self->{io}{$fd};
-  $self->_sandbox('Read', $io->{cb}, 0) if EV::READ &$revents;
+  $self->_sandbox('Read', $io->{cb}, 0) if EV::READ & $revents;
   $self->_sandbox('Write', $io->{cb}, 1)
-    if EV::WRITE &$revents && $self->{io}{$fd};
+    if EV::WRITE & $revents && $self->{io}{$fd};
 }
 
 sub _timer {
@@ -99,11 +99,13 @@ sub timer { shift->_timer(0, @_) }
 sub watch {
   my ($self, $handle, $read, $write) = @_;
 
+  my $mode = 0;
+  $mode |= POLLIN | POLLPRI if $read;
+  $mode |= POLLOUT if $write;
+
   my $poll = $self->_poll;
   $poll->remove($handle);
-  if ($read && $write) { $poll->mask($handle, POLLIN | POLLPRI | POLLOUT) }
-  elsif ($read)  { $poll->mask($handle, POLLIN | POLLPRI) }
-  elsif ($write) { $poll->mask($handle, POLLOUT) }
+  $poll->mask($handle, $mode) if $mode != 0;
 
   return $self;
 }
@@ -2,7 +2,7 @@ package Mojo::Reactor;
 use Mojo::Base 'Mojo::EventEmitter';
 
 use Carp 'croak';
-use IO::Poll qw(POLLERR POLLHUP POLLIN POLLPRI);
+use IO::Poll qw(POLLIN POLLPRI);
 use Mojo::Loader;
 
 sub again { croak 'Method "again" not implemented by subclass' }
@@ -14,16 +14,9 @@ sub detect {
 
 sub io { croak 'Method "io" not implemented by subclass' }
 
+# This may break at some point in the future, but is worth it for performance
 sub is_readable {
-  my ($self, $handle) = @_;
-
-  my $test = $self->{test} ||= IO::Poll->new;
-  $test->mask($handle, POLLIN | POLLPRI);
-  $test->poll(0);
-  my $result = $test->handles(POLLIN | POLLPRI | POLLERR | POLLHUP);
-  $test->remove($handle);
-
-  return !!$result;
+  !(IO::Poll::_poll(0, fileno(pop), my $dummy = POLLIN | POLLPRI) == 0);
 }
 
 sub is_running { croak 'Method "is_running" not implemented by subclass' }
@@ -26,18 +26,17 @@ sub run {
   # Response start line
   STDOUT->autoflush(1);
   binmode STDOUT;
-  my $res = $tx->res;
+  my $res = $tx->res->fix_headers;
   return undef if $self->nph && !_write($res, 'get_start_line_chunk');
 
   # Response headers
-  $res->fix_headers;
   my $code = $res->code    || 404;
   my $msg  = $res->message || $res->default_message;
   $res->headers->status("$code $msg") unless $self->nph;
   return undef unless _write($res, 'get_header_chunk');
 
   # Response body
-  $tx->is_empty or _write($res, 'get_body_chunk') or return undef;
+  return undef unless $tx->is_empty || _write($res, 'get_body_chunk');
 
   # Finish transaction
   $tx->server_close;
@@ -79,7 +78,7 @@ Mojo::Server::CGI - CGI server
   use Mojo::Server::CGI;
 
   my $cgi = Mojo::Server::CGI->new;
-  $cgi->unsubscribe('request')
+  $cgi->unsubscribe('request');
   $cgi->on(request => sub {
     my ($cgi, $tx) = @_;
 
@@ -160,7 +160,7 @@ sub _listen {
       warn "-- Accept (@{[$stream->handle->peerhost]})\n" if DEBUG;
       $stream->timeout($self->inactivity_timeout);
 
-      $stream->on(close => sub { $self->_close($id) });
+      $stream->on(close => sub { $self && $self->_close($id) });
       $stream->on(error =>
           sub { $self && $self->app->log->error(pop) && $self->_close($id) });
       $stream->on(read => sub { $self->_read($id => pop) });
@@ -261,15 +261,15 @@ Mojo::Server::Daemon - Non-blocking I/O HTTP and WebSocket server
 =head1 DESCRIPTION
 
 L<Mojo::Server::Daemon> is a full featured, highly portable non-blocking I/O
-HTTP and WebSocket server, with IPv6, TLS, Comet (long polling), keep-alive,
-connection pooling, timeout, cookie, multipart and multiple event loop
-support.
-
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if
-they are installed. Individual features can also be disabled with the
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables.
+HTTP and WebSocket server, with IPv6, TLS, Comet (long polling), keep-alive
+and multiple event loop support.
+
+For better scalability (epoll, kqueue) and to provide non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.12+), L<IO::Socket::Socks> (0.64+) and
+L<IO::Socket::SSL> (1.84+) will be used automatically if they are installed.
+Individual features can also be disabled with the C<MOJO_NO_NDN>,
+C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
 
 See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
 
@@ -152,31 +152,31 @@ Mojo::Server::Hypnotoad - ALL GLORY TO THE HYPNOTOAD!
 L<Mojo::Server::Hypnotoad> is a full featured, UNIX optimized, preforking
 non-blocking I/O HTTP and WebSocket server, built around the very well tested
 and reliable L<Mojo::Server::Prefork>, with IPv6, TLS, Comet (long polling),
-keep-alive, connection pooling, timeout, cookie, multipart, multiple event
-loop and hot deployment support that just works. Note that the server uses
-signals for process management, so you should avoid modifying signal handlers
-in your applications.
+keep-alive, multiple event loop and hot deployment support that just works.
+Note that the server uses signals for process management, so you should avoid
+modifying signal handlers in your applications.
 
 To start applications with it you can use the L<hypnotoad> script, for
 L<Mojolicious> and L<Mojolicious::Lite> applications it will default to
 C<production> mode.
 
-  $ hypnotoad myapp.pl
+  $ hypnotoad ./myapp.pl
   Server available at http://127.0.0.1:8080.
 
 You can run the same command again for automatic hot deployment.
 
-  $ hypnotoad myapp.pl
+  $ hypnotoad ./myapp.pl
   Starting hot deployment for Hypnotoad server 31841.
 
 This second invocation will load the application again, detect the process id
 file with it, and send a L</"USR2"> signal to the already running server.
 
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if
-they are installed. Individual features can also be disabled with the
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables.
+For better scalability (epoll, kqueue) and to provide non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.12+), L<IO::Socket::Socks> (0.64+) and
+L<IO::Socket::SSL> (1.84+) will be used automatically if they are installed.
+Individual features can also be disabled with the C<MOJO_NO_NDN>,
+C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
 
 See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
 
@@ -120,21 +120,22 @@ Mojo::Server::Morbo - DOOOOOOOOOOOOOOOOOOM!
 
 L<Mojo::Server::Morbo> is a full featured, self-restart capable non-blocking
 I/O HTTP and WebSocket server, built around the very well tested and reliable
-L<Mojo::Server::Daemon>, with IPv6, TLS, Comet (long polling), keep-alive,
-connection pooling, timeout, cookie, multipart and multiple event loop
-support. Note that the server uses signals for process management, so you
-should avoid modifying signal handlers in your applications.
+L<Mojo::Server::Daemon>, with IPv6, TLS, Comet (long polling), keep-alive and
+multiple event loop support. Note that the server uses signals for process
+management, so you should avoid modifying signal handlers in your
+applications.
 
 To start applications with it you can use the L<morbo> script.
 
-  $ morbo myapp.pl
+  $ morbo ./myapp.pl
   Server available at http://127.0.0.1:3000.
 
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if
-they are installed. Individual features can also be disabled with the
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables.
+For better scalability (epoll, kqueue) and to provide non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.12+), L<IO::Socket::Socks> (0.64+) and
+L<IO::Socket::SSL> (1.84+) will be used automatically if they are installed.
+Individual features can also be disabled with the C<MOJO_NO_NDN>,
+C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
 
 See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
 
@@ -21,10 +21,9 @@ sub run {
   $self->emit(request => $tx);
 
   # Response headers
-  my $res     = $tx->res->fix_headers;
-  my $headers = $res->headers;
+  my $res  = $tx->res->fix_headers;
+  my $hash = $res->headers->to_hash(1);
   my @headers;
-  my $hash = $headers->to_hash(1);
   for my $name (keys %$hash) {
     push @headers, map { $name => $_ } @{$hash->{$name}};
   }
@@ -257,15 +257,16 @@ Mojo::Server::Prefork - Preforking non-blocking I/O HTTP and WebSocket server
 L<Mojo::Server::Prefork> is a full featured, UNIX optimized, preforking
 non-blocking I/O HTTP and WebSocket server, built around the very well tested
 and reliable L<Mojo::Server::Daemon>, with IPv6, TLS, Comet (long polling),
-keep-alive, connection pooling, timeout, cookie, multipart and multiple event
-loop support. Note that the server uses signals for process management, so you
-should avoid modifying signal handlers in your applications.
-
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if
-they are installed. Individual features can also be disabled with the
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables.
+keep-alive and multiple event loop support. Note that the server uses signals
+for process management, so you should avoid modifying signal handlers in your
+applications.
+
+For better scalability (epoll, kqueue) and to provide non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.12+), L<IO::Socket::Socks> (0.64+) and
+L<IO::Socket::SSL> (1.84+) will be used automatically if they are installed.
+Individual features can also be disabled with the C<MOJO_NO_NDN>,
+C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
 
 See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
 
@@ -2,6 +2,7 @@ package Mojo::Server;
 use Mojo::Base 'Mojo::EventEmitter';
 
 use Carp 'croak';
+use Cwd 'abs_path';
 use Mojo::Loader;
 use Mojo::Util 'md5_sum';
 use POSIX;
@@ -15,7 +16,7 @@ sub build_app {
   my ($self, $app) = @_;
   local $ENV{MOJO_EXE};
   return $app->new unless my $e = Mojo::Loader->new->load($app);
-  die ref $e ? $e : qq{Couldn't find application class "$app".\n};
+  die ref $e ? $e : qq{Can't find application class "$app" in \@INC. (@INC)\n};
 }
 
 sub build_tx {
@@ -43,20 +44,16 @@ sub load_app {
 
   # Clean environment (reset FindBin defensively)
   {
-    local $0 = $path;
+    local $0 = $path = abs_path $path;
     require FindBin;
     FindBin->again;
     local $ENV{MOJO_APP_LOADER} = 1;
     local $ENV{MOJO_EXE};
 
     # Try to load application from script into sandbox
-    my $app = eval sprintf <<'EOF', md5_sum($path . $$);
-package Mojo::Server::SandBox::%s;
-my $app = do $path;
-if (!$app && (my $e = $@ || $!)) { die $e }
-$app;
-EOF
-    die qq{Couldn't load application from file "$path": $@} if !$app && $@;
+    my $app = eval "package Mojo::Server::Sandbox::@{[md5_sum $path]};"
+      . 'return do($path) || die($@ || $!);';
+    die qq{Can't load application from file "$path": $@} if !$app && $@;
     die qq{File "$path" did not return an application object.\n}
       unless blessed $app && $app->isa('Mojo');
     $self->app($app);
@@ -77,12 +74,12 @@ sub run { croak 'Method "run" not implemented by subclass' }
 sub setuidgid {
   my $self = shift;
 
-  # Group
+  # Group (make sure secondary groups are reassigned too)
   if (my $group = $self->group) {
     return $self->_log(qq{Group "$group" does not exist.})
       unless defined(my $gid = getgrnam $group);
     return $self->_log(qq{Can't switch to group "$group": $!})
-      unless POSIX::setgid($gid);
+      unless ($( = $) = "$gid $gid") && $) eq "$gid $gid" && $( eq "$gid $gid";
   }
 
   # User
@@ -187,7 +184,7 @@ the following new ones.
 
 =head2 build_app
 
-  my $app = $server->build_app('Mojo::HelloWorld');
+  my $app = $server->build_app('MyApp');
 
 Build application from class.
 
@@ -8,13 +8,13 @@ use Mojo::Util qw(decode encode monkey_patch slurp);
 
 use constant DEBUG => $ENV{MOJO_TEMPLATE_DEBUG} || 0;
 
-has [qw(auto_escape compiled)];
 has [qw(append code prepend template)] => '';
+has [qw(auto_escape compiled)];
 has capture_end   => 'end';
 has capture_start => 'begin';
 has comment_mark  => '#';
 has encoding      => 'UTF-8';
-has escape        => sub { \&Mojo::Util::xml_escape };
+has escape        => sub { \&Mojo::Util::xss_escape };
 has [qw(escape_mark expression_mark trim_mark)] => '=';
 has [qw(line_start replace_mark)] => '%';
 has name      => 'template';
@@ -26,66 +26,61 @@ has tree      => sub { [] };
 sub build {
   my $self = shift;
 
-  my (@lines, $cpst, $multi);
+  my $tree   = $self->tree;
   my $escape = $self->auto_escape;
-  for my $line (@{$self->tree}) {
-    push @lines, '';
-    for (my $j = 0; $j < @{$line}; $j += 2) {
-      my ($op, $value) = @$line[$j, $j + 1];
-      my $newline = chomp($value //= '');
 
-      # Capture end
-      if ($op eq 'cpen') {
-        $lines[-1] .= 'return Mojo::ByteStream->new($_M) }';
-
-        # No following code
-        my $next = $line->[$j + 3];
-        $lines[-1] .= ';' if !defined $next || $next =~ /^\s*$/;
-      }
+  my @blocks = ('');
+  my ($i, $capture, $multi);
+  while (++$i <= @$tree && (my $next = $tree->[$i])) {
+    my ($op, $value) = @{$tree->[$i - 1]};
+    push @blocks, '' and next if $op eq 'line';
+    my $newline = chomp($value //= '');
+
+    # Text (quote and fix line ending)
+    if ($op eq 'text') {
+      $value = join "\n", map { quotemeta $_ } split("\n", $value, -1);
+      $value .= '\n' if $newline;
+      $blocks[-1] .= "\$_M .= \"" . $value . "\";" if length $value;
+    }
 
-      # Text (quote and fix line ending)
-      if ($op eq 'text') {
-        $value = $newline ? quotemeta($value) . '\n' : quotemeta $value;
-        $lines[-1] .= "\$_M .= \"" . $value . "\";" if length $value;
-      }
+    # Code or multiline expression
+    elsif ($op eq 'code' || $multi) { $blocks[-1] .= $value }
 
-      # Code or multiline expression
-      if ($op eq 'code' || $multi) { $lines[-1] .= "$value" }
+    # Capture end
+    elsif ($op eq 'cpen') {
+      $blocks[-1] .= 'return Mojo::ByteStream->new($_M) }';
 
-      # Expression
-      if ($op eq 'expr' || $op eq 'escp') {
+      # No following code
+      $blocks[-1] .= ';' if ($next->[1] // '') =~ /^\s*$/;
+    }
 
-        # Start
-        unless ($multi) {
+    # Expression
+    if ($op eq 'expr' || $op eq 'escp') {
 
-          # Escaped
-          if ($op eq 'escp' && !$escape || $op eq 'expr' && $escape) {
-            $lines[-1] .= "\$_M .= _escape";
-            $lines[-1] .= " scalar $value" if length $value;
-          }
+      # Escaped
+      if (!$multi && ($op eq 'escp' && !$escape || $op eq 'expr' && $escape)) {
+        $blocks[-1] .= "\$_M .= _escape scalar $value";
+      }
 
-          # Raw
-          else { $lines[-1] .= "\$_M .= scalar $value" }
-        }
+      # Raw
+      elsif (!$multi) { $blocks[-1] .= "\$_M .= scalar $value" }
 
-        # Multiline
-        $multi = !(($line->[$j + 2] // '') eq 'text'
-          && ($line->[$j + 3] // '') eq '');
+      # Multiline
+      $multi = !$next || $next->[0] ne 'text';
 
-        # Append semicolon
-        $lines[-1] .= ';' unless $multi || $cpst;
-      }
+      # Append semicolon
+      $blocks[-1] .= ';' unless $multi || $capture;
+    }
 
-      # Capture start
-      if ($cpst) {
-        $lines[-1] .= $cpst;
-        $cpst = undef;
-      }
-      $cpst = " sub { my \$_M = ''; " if $op eq 'cpst';
+    # Capture start
+    if ($op eq 'cpst') { $capture = 1 }
+    elsif ($capture) {
+      $blocks[-1] .= " sub { my \$_M = ''; ";
+      $capture = 0;
     }
   }
 
-  return $self->code(join "\n", @lines)->tree([]);
+  return $self->code(join "\n", @blocks)->tree([]);
 }
 
 sub compile {
@@ -121,7 +116,7 @@ sub parse {
   my ($self, $template) = @_;
 
   # Clean start
-  my $tree = $self->template($template)->tree([])->tree;
+  $self->template($template)->tree(\my @tree);
 
   my $tag     = $self->tag_start;
   my $replace = $self->replace_mark;
@@ -149,13 +144,12 @@ sub parse {
   my $end_re  = qr/^(?:(\Q$cpst\E)\s*)?(\Q$trim\E)?\Q$end\E$/;
 
   # Split lines
-  my $state = 'text';
-  my ($trimming, @capture_token);
+  my $op = 'text';
+  my ($trimming, $capture);
   for my $line (split "\n", $template) {
-    $trimming = 0 if $state eq 'text';
 
     # Turn Perl line into mixed line
-    if ($state eq 'text' && $line !~ s/^(\s*)\Q$start$replace\E/$1$start/) {
+    if ($op eq 'text' && $line !~ s/^(\s*)\Q$start$replace\E/$1$start/) {
       if ($line =~ s/^(\s*)\Q$start\E(?:(\Q$cmnt\E)|(\Q$expr\E))?//) {
 
         # Comment
@@ -167,63 +161,64 @@ sub parse {
     }
 
     # Escaped line ending
-    $line .= "\n" unless $line =~ s/\\\\$/\\\n/ || $line =~ s/\\$//;
+    $line .= "\n" if $line !~ s/\\\\$/\\\n/ && $line !~ s/\\$//;
 
     # Mixed line
-    my @token;
     for my $token (split $token_re, $line) {
 
       # Capture end
-      @capture_token = ('cpen', undef) if $token =~ s/$cpen_re/$1/;
+      $capture = 1 if $token =~ s/$cpen_re/$1/;
 
       # End
-      if ($state ne 'text' && $token =~ $end_re) {
-        $state = 'text';
+      if ($op ne 'text' && $token =~ $end_re) {
+        $op = 'text';
 
         # Capture start
-        splice @token, -2, 0, 'cpst', undef if $1;
+        splice @tree, -1, 0, ['cpst'] if $1;
 
         # Trim left side
-        if ($2) {
-          $trimming = 1;
-          $self->_trim(\@token);
-        }
+        _trim(\@tree) if ($trimming = $2) && @tree > 1;
 
         # Hint at end
-        push @token, 'text', '';
+        push @tree, ['text', ''];
       }
 
       # Code
-      elsif ($token =~ /^\Q$tag\E$/) { $state = 'code' }
+      elsif ($token =~ /^\Q$tag\E$/) { $op = 'code' }
 
       # Expression
-      elsif ($token =~ /^\Q$tag$expr\E$/) { $state = 'expr' }
+      elsif ($token =~ /^\Q$tag$expr\E$/) { $op = 'expr' }
 
       # Expression that needs to be escaped
-      elsif ($token =~ /^\Q$tag$expr$escp\E$/) { $state = 'escp' }
+      elsif ($token =~ /^\Q$tag$expr$escp\E$/) { $op = 'escp' }
 
       # Comment
-      elsif ($token =~ /^\Q$tag$cmnt\E$/) { $state = 'cmnt' }
+      elsif ($token =~ /^\Q$tag$cmnt\E$/) { $op = 'cmnt' }
 
-      # Text
-      else {
+      # Text (comments are just ignored)
+      elsif ($op ne 'cmnt') {
 
         # Replace
         $token = $tag if $token eq "$tag$replace";
 
         # Trim right side (convert whitespace to line noise)
         if ($trimming && $token =~ s/^(\s+)//) {
-          push @token, 'code', $1;
+          push @tree, ['code', $1];
           $trimming = 0;
         }
 
-        # Comments are ignored
-        next if $state eq 'cmnt';
-        push @token, @capture_token, $state, $token;
-        @capture_token = ();
+        # Token (with optional capture end)
+        push @tree, $capture ? ['cpen'] : (), [$op, $token];
+        $capture = 0;
       }
     }
-    push @$tree, \@token;
+
+    # Optimize successive text lines separated by a newline
+    push @tree, ['line'] and next
+      if $tree[-4] && $tree[-4][0] ne 'line'
+      || (!$tree[-3] || $tree[-3][0] ne 'text' || $tree[-3][1] !~ /\n$/)
+      || ($tree[-2][0] ne 'line' || $tree[-1][0] ne 'text');
+    $tree[-3][1] .= pop(@tree)->[1];
   }
 
   return $self;
@@ -253,41 +248,30 @@ sub _line {
 }
 
 sub _trim {
-  my ($self, $line) = @_;
-
-  # Walk line backwards
-  for (my $j = @$line - 4; $j >= 0; $j -= 2) {
-
-    # Skip captures
-    next if $line->[$j] eq 'cpst' || $line->[$j] eq 'cpen';
+  my $tree = shift;
 
-    # Only trim text
-    return unless $line->[$j] eq 'text';
+  # Skip captures
+  my $i = $tree->[-2][0] eq 'cpst' || $tree->[-2][0] eq 'cpen' ? -3 : -2;
 
-    # Convert whitespace text to line noise
-    splice @$line, $j, 0, 'code', $1 if $line->[$j + 1] =~ s/(\s+)$//;
+  # Only trim text
+  return unless $tree->[$i][0] eq 'text';
 
-    # Text left
-    return if length $line->[$j + 1];
-  }
+  # Convert whitespace text to line noise
+  splice @$tree, $i, 0, ['code', $1] if $tree->[$i][1] =~ s/(\s+)$//;
 }
 
 sub _wrap {
   my ($self, $code) = @_;
 
   # Escape function
-  my $escape = $self->escape;
-  monkey_patch $self->namespace, _escape => sub {
-    no warnings 'uninitialized';
-    ref $_[0] eq 'Mojo::ByteStream' ? $_[0] : $escape->("$_[0]");
-  };
+  monkey_patch $self->namespace, '_escape', $self->escape;
 
   # Wrap lines
   my $num = () = $code =~ /\n/g;
   my $head = $self->_line(1);
   $head .= "\npackage @{[$self->namespace]}; use Mojo::Base -strict;";
-  $code = "$head sub { my \$_M = ''; @{[$self->prepend]}; do { $code\n";
-  $code .= $self->_line($num + 1) . "\n@{[$self->append]}; \$_M } };";
+  $code = "$head sub { my \$_M = ''; @{[$self->prepend]}; { $code\n";
+  $code .= $self->_line($num + 1) . "\n@{[$self->append]}; } \$_M };";
 
   warn "-- Code for @{[$self->name]}\n@{[encode 'UTF-8', $code]}\n\n" if DEBUG;
   return $code;
@@ -505,7 +489,7 @@ Encoding used for template files.
   $mt    = $mt->escape(sub {...});
 
 A callback used to escape the results of escaped expressions, defaults to
-L<Mojo::Util/"xml_escape">.
+L<Mojo::Util/"xss_escape">.
 
   $mt->escape(sub {
     my $str = shift;
@@ -603,7 +587,7 @@ Raw unparsed template.
 =head2 tree
 
   my $tree = $mt->tree;
-  $mt      = $mt->tree([['text', 'foo']]);
+  $mt      = $mt->tree([['text', 'foo'], ['line']]);
 
 Template in parsed form. Note that this structure should only be used very
 carefully since it is very dynamic.
@@ -18,12 +18,12 @@ use constant GUID => '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
 
 # Opcodes
 use constant {
-  CONTINUATION => 0,
-  TEXT         => 1,
-  BINARY       => 2,
-  CLOSE        => 8,
-  PING         => 9,
-  PONG         => 10
+  CONTINUATION => 0x0,
+  TEXT         => 0x1,
+  BINARY       => 0x2,
+  CLOSE        => 0x8,
+  PING         => 0x9,
+  PONG         => 0xa
 };
 
 has [qw(compressed masked)];
@@ -35,35 +35,30 @@ sub build_frame {
   warn "-- Building frame ($fin, $rsv1, $rsv2, $rsv3, $op)\n" if DEBUG;
 
   # Head
-  my $frame = 0b00000000;
-  vec($frame, 0, 8) = $op | 0b10000000 if $fin;
-  vec($frame, 0, 8) |= 0b01000000 if $rsv1;
-  vec($frame, 0, 8) |= 0b00100000 if $rsv2;
-  vec($frame, 0, 8) |= 0b00010000 if $rsv3;
+  my $head = $op + ($fin ? 128 : 0);
+  $head |= 0b01000000 if $rsv1;
+  $head |= 0b00100000 if $rsv2;
+  $head |= 0b00010000 if $rsv3;
+  my $frame = pack 'C', $head;
 
   # Small payload
   my $len    = length $payload;
-  my $prefix = 0;
   my $masked = $self->masked;
   if ($len < 126) {
     warn "-- Small payload ($len)\n$payload\n" if DEBUG;
-    vec($prefix, 0, 8) = $masked ? ($len | 0b10000000) : $len;
-    $frame .= $prefix;
+    $frame .= pack 'C', $masked ? ($len | 128) : $len;
   }
 
-  # Extended payload (16bit)
+  # Extended payload (16-bit)
   elsif ($len < 65536) {
-    warn "-- Extended 16bit payload ($len)\n$payload\n" if DEBUG;
-    vec($prefix, 0, 8) = $masked ? (126 | 0b10000000) : 126;
-    $frame .= $prefix;
-    $frame .= pack 'n', $len;
+    warn "-- Extended 16-bit payload ($len)\n$payload\n" if DEBUG;
+    $frame .= pack 'Cn', $masked ? (126 | 128) : 126, $len;
   }
 
-  # Extended payload (64bit with 32bit fallback)
+  # Extended payload (64-bit with 32-bit fallback)
   else {
-    warn "-- Extended 64bit payload ($len)\n$payload\n" if DEBUG;
-    vec($prefix, 0, 8) = $masked ? (127 | 0b10000000) : 127;
-    $frame .= $prefix;
+    warn "-- Extended 64-bit payload ($len)\n$payload\n" if DEBUG;
+    $frame .= pack 'C', $masked ? (127 | 128) : 127;
     $frame .= MODERN ? pack('Q>', $len) : pack('NN', 0, $len & 0xffffffff);
   }
 
@@ -160,60 +155,54 @@ sub parse_frame {
   my ($self, $buffer) = @_;
 
   # Head
-  return undef unless length(my $clone = $$buffer) >= 2;
-  my $head = substr $clone, 0, 2;
+  return undef unless length $$buffer >= 2;
+  my ($first, $second) = unpack 'C*', substr($$buffer, 0, 2);
 
   # FIN
-  my $fin = (vec($head, 0, 8) & 0b10000000) == 0b10000000 ? 1 : 0;
+  my $fin = ($first & 0b10000000) == 0b10000000 ? 1 : 0;
 
   # RSV1-3
-  my $rsv1 = (vec($head, 0, 8) & 0b01000000) == 0b01000000 ? 1 : 0;
-  my $rsv2 = (vec($head, 0, 8) & 0b00100000) == 0b00100000 ? 1 : 0;
-  my $rsv3 = (vec($head, 0, 8) & 0b00010000) == 0b00010000 ? 1 : 0;
+  my $rsv1 = ($first & 0b01000000) == 0b01000000 ? 1 : 0;
+  my $rsv2 = ($first & 0b00100000) == 0b00100000 ? 1 : 0;
+  my $rsv3 = ($first & 0b00010000) == 0b00010000 ? 1 : 0;
 
   # Opcode
-  my $op = vec($head, 0, 8) & 0b00001111;
+  my $op = $first & 0b00001111;
   warn "-- Parsing frame ($fin, $rsv1, $rsv2, $rsv3, $op)\n" if DEBUG;
 
   # Small payload
-  my $len = vec($head, 1, 8) & 0b01111111;
-  my $hlen = 2;
+  my ($hlen, $len) = (2, $second & 0b01111111);
   if ($len < 126) { warn "-- Small payload ($len)\n" if DEBUG }
 
-  # Extended payload (16bit)
+  # Extended payload (16-bit)
   elsif ($len == 126) {
-    return undef unless length $clone > 4;
+    return undef unless length $$buffer > 4;
     $hlen = 4;
-    $len = unpack 'n', substr($clone, 2, 2);
-    warn "-- Extended 16bit payload ($len)\n" if DEBUG;
+    $len = unpack 'n', substr($$buffer, 2, 2);
+    warn "-- Extended 16-bit payload ($len)\n" if DEBUG;
   }
 
-  # Extended payload (64bit with 32bit fallback)
+  # Extended payload (64-bit with 32-bit fallback)
   elsif ($len == 127) {
-    return undef unless length $clone > 10;
+    return undef unless length $$buffer > 10;
     $hlen = 10;
-    my $ext = substr $clone, 2, 8;
+    my $ext = substr $$buffer, 2, 8;
     $len = MODERN ? unpack('Q>', $ext) : unpack('N', substr($ext, 4, 4));
-    warn "-- Extended 64bit payload ($len)\n" if DEBUG;
+    warn "-- Extended 64-bit payload ($len)\n" if DEBUG;
   }
 
   # Check message size
   $self->finish(1009) and return undef if $len > $self->max_websocket_size;
 
   # Check if whole packet has arrived
-  my $masked = vec($head, 1, 8) & 0b10000000;
-  return undef if length $clone < ($len + $hlen + ($masked ? 4 : 0));
-  substr $clone, 0, $hlen, '';
+  $len += 4 if my $masked = $second & 0b10000000;
+  return undef if length $$buffer < ($hlen + $len);
+  substr $$buffer, 0, $hlen, '';
 
   # Payload
-  $len += 4 if $masked;
-  return undef if length $clone < $len;
-  my $payload = $len ? substr($clone, 0, $len, '') : '';
-
-  # Unmask payload
+  my $payload = $len ? substr($$buffer, 0, $len, '') : '';
   $payload = xor_encode($payload, substr($payload, 0, 4, '') x 128) if $masked;
   warn "$payload\n" if DEBUG;
-  $$buffer = $clone;
 
   return [$fin, $rsv1, $rsv2, $rsv3, $op, $payload];
 }
@@ -365,8 +354,8 @@ Mojo::Transaction::WebSocket - WebSocket transaction
 =head1 DESCRIPTION
 
 L<Mojo::Transaction::WebSocket> is a container for WebSocket transactions
-based on L<RFC 6455|http://tools.ietf.org/html/rfc6455>. Note that 64bit
-frames require a Perl with support for quads or they are limited to 32bit.
+based on L<RFC 6455|http://tools.ietf.org/html/rfc6455>. Note that 64-bit
+frames require a Perl with support for quads or they are limited to 32-bit.
 
 =head1 EVENTS
 
@@ -501,7 +490,7 @@ object.
   my $bool = $ws->masked;
   $ws      = $ws->masked($bool);
 
-Mask outgoing frames with XOR cipher and a random 32bit key.
+Mask outgoing frames with XOR cipher and a random 32-bit key.
 
 =head2 max_websocket_size
 
@@ -5,7 +5,8 @@ use Carp 'croak';
 use Mojo::Message::Request;
 use Mojo::Message::Response;
 
-has [qw(kept_alive local_address local_port remote_port)];
+has [
+  qw(kept_alive local_address local_port original_remote_address remote_port)];
 has req => sub { Mojo::Message::Request->new };
 has res => sub { Mojo::Message::Response->new };
 
@@ -46,20 +47,12 @@ sub is_writing { (shift->{state} // 'write') eq 'write' }
 sub remote_address {
   my $self = shift;
 
-  # New address
-  if (@_) {
-    $self->{remote_address} = shift;
-    return $self;
-  }
+  return $self->original_remote_address(@_) if @_;
+  return $self->original_remote_address unless $self->req->reverse_proxy;
 
   # Reverse proxy
-  if ($self->req->reverse_proxy) {
-    return $self->{forwarded_for} if $self->{forwarded_for};
-    my $forwarded = $self->req->headers->header('X-Forwarded-For') // '';
-    $forwarded =~ /([^,\s]+)$/ and return $self->{forwarded_for} = $1;
-  }
-
-  return $self->{remote_address};
+  return ($self->req->headers->header('X-Forwarded-For') // '')
+    =~ /([^,\s]+)$/ ? $1 : $self->original_remote_address;
 }
 
 sub resume       { shift->_state(qw(write resume)) }
@@ -155,6 +148,13 @@ Local interface address.
 
 Local interface port.
 
+=head2 original_remote_address
+
+  my $address = $tx->original_remote_address;
+  $tx         = $tx->original_remote_address('127.0.0.1');
+
+Remote interface address.
+
 =head2 remote_port
 
   my $port = $tx->remote_port;
@@ -246,7 +246,9 @@ Resume transaction.
   my $address = $tx->remote_address;
   $tx         = $tx->remote_address('127.0.0.1');
 
-Remote interface address.
+Same as L</"original_remote_address"> or the last value of the
+C<X-Forwarded-For> header if L</"req"> has been performed through a reverse
+proxy.
 
 =head2 server_close
 
@@ -41,13 +41,10 @@ sub host_port {
 }
 
 sub clone {
-  my $self = shift;
-
+  my $self  = shift;
   my $clone = $self->new;
-  $clone->$_($self->$_) for qw(scheme userinfo host port fragment);
-  $clone->path($self->path->clone)->query($self->query->clone);
-  $clone->base($self->base->clone) if $self->{base};
-
+  @$clone{keys %$self} = values %$self;
+  $clone->{$_} && ($clone->{$_} = $clone->{$_}->clone) for qw(base path query);
   return $clone;
 }
 
@@ -296,7 +293,7 @@ Clone this URL.
 
 Normalized version of L</"host"> and L</"port">.
 
-  # "xn--da5b0n.net:8080"
+  # "xn--n3h.net:8080"
   Mojo::URL->new('http://☃.net:8080/test')->host_port;
 
 =head2 ihost
@@ -306,7 +303,7 @@ Normalized version of L</"host"> and L</"port">.
 
 Host part of this URL in punycode format.
 
-  # "xn--da5b0n.net"
+  # "xn--n3h.net"
   Mojo::URL->new('http://☃.net')->ihost;
 
 =head2 is_abs
@@ -440,7 +437,7 @@ Always true.
 
   my $str = "$url";
 
-Alias for L</to_string>.
+Alias for L</"to_string">.
 
 =head1 SEE ALSO
 
@@ -4,6 +4,7 @@ use Mojo::Base -base;
 use Mojo::Cookie::Request;
 use Mojo::Path;
 
+has extracting      => 1;
 has max_cookie_size => 4096;
 
 sub add {
@@ -40,6 +41,9 @@ sub empty { delete shift->{jar} }
 
 sub extract {
   my ($self, $tx) = @_;
+
+  return unless $self->extracting;
+
   my $url = $tx->req->url;
   for my $cookie (@{$tx->res->cookies}) {
 
@@ -146,6 +150,14 @@ L<RFC 6265|http://tools.ietf.org/html/rfc6265>.
 
 L<Mojo::UserAgent::CookieJar> implements the following attributes.
 
+=head2 extracting
+
+  my $bool = $jar->extracting;
+  $jar     = $jar->extracting($bool);
+
+Allow L</"extract"> to L</"add"> new cookies to the jar, defaults to a true
+value.
+
 =head2 max_cookie_size
 
   my $size = $jar->max_cookie_size;
@@ -58,22 +58,22 @@ L<Mojo::UserAgent::Proxy> implements the following attributes.
 
 =head2 http
 
-  my $http = $ua->http;
-  $ua      = $ua->http('http://sri:secret@127.0.0.1:8080');
+  my $http = $proxy->http;
+  $proxy   = $proxy->http('socks://sri:secret@127.0.0.1:8080');
 
 Proxy server to use for HTTP and WebSocket requests.
 
 =head2 https
 
-  my $https = $ua->https;
-  $ua       = $ua->https('http://sri:secret@127.0.0.1:8080');
+  my $https = $proxy->https;
+  $proxy    = $proxy->https('http://sri:secret@127.0.0.1:8080');
 
 Proxy server to use for HTTPS and WebSocket requests.
 
 =head2 not
 
   my $not = $proxy->not;
-  $ua     = $proxy->not([qw(localhost intranet.mojolicio.us)]);
+  $proxy  = $proxy->not([qw(localhost intranet.mojolicio.us)]);
 
 Domains that don't require a proxy server to be used.
 
@@ -95,9 +95,9 @@ implements the following new ones.
 =head2 app
 
   my $app = Mojo::UserAgent::Server->app;
-            Mojo::UserAgent::Server->app(MyApp->new);
+            Mojo::UserAgent::Server->app(Mojolicious->new);
   my $app = $server->app;
-  $server = $server->app(MyApp->new);
+  $server = $server->app(Mojolicious->new);
 
 Application this server handles, instance specific applications override the
 global default.
@@ -33,16 +33,15 @@ sub endpoint {
   my $port  = $url->port || ($proto eq 'https' ? 443 : 80);
 
   # Proxy for normal HTTP requests
+  my $socks;
+  if (my $proxy = $req->proxy) { $socks = $proxy->protocol eq 'socks' }
   return $self->_proxy($tx, $proto, $host, $port)
-    if $proto eq 'http' && !$req->is_handshake;
+    if $proto eq 'http' && !$req->is_handshake && !$socks;
 
   return $proto, $host, $port;
 }
 
-sub peer {
-  my ($self, $tx) = @_;
-  return $self->_proxy($tx, $self->endpoint($tx));
-}
+sub peer { $_[0]->_proxy($_[1], $_[0]->endpoint($_[1])) }
 
 sub proxy_connect {
   my ($self, $old) = @_;
@@ -53,6 +52,7 @@ sub proxy_connect {
 
   # No proxy
   return undef unless my $proxy = $req->proxy;
+  return undef if $proxy->protocol eq 'socks';
 
   # WebSocket and/or HTTPS
   my $url = $req->url;
@@ -75,9 +75,11 @@ sub redirect {
   return undef unless grep { $_ == $code } 301, 302, 303, 307, 308;
 
   # Fix location without authority and/or scheme
-  return unless my $location = $res->headers->location;
+  return undef unless my $location = $res->headers->location;
   $location = Mojo::URL->new($location);
   $location = $location->base($old->req->url)->to_abs unless $location->is_abs;
+  my $proto = $location->protocol;
+  return undef unless $proto eq 'http' || $proto eq 'https';
 
   # Clone request if necessary
   my $new = Mojo::Transaction::HTTP->new;
@@ -155,19 +157,19 @@ sub _form {
   my ($self, $tx, $form, %options) = @_;
 
   # Check for uploads and force multipart if necessary
-  my $multipart;
+  my $req       = $tx->req;
+  my $headers   = $req->headers;
+  my $multipart = ($headers->content_type // '') =~ m!multipart/form-data!i;
   for my $value (map { ref $_ eq 'ARRAY' ? @$_ : $_ } values %$form) {
     ++$multipart and last if ref $value eq 'HASH';
   }
-  my $req     = $tx->req;
-  my $headers = $req->headers;
-  $headers->content_type('multipart/form-data') if $multipart;
 
   # Multipart
-  if (($headers->content_type // '') eq 'multipart/form-data') {
+  if ($multipart) {
     my $parts = $self->_multipart($options{charset}, $form);
     $req->content(
       Mojo::Content::MultiPart->new(headers => $headers, parts => $parts));
+    _type($headers, 'multipart/form-data');
     return $tx;
   }
 
@@ -178,15 +180,14 @@ sub _form {
   if ($method eq 'GET' || $method eq 'HEAD') { $req->url->query->merge($p) }
   else {
     $req->body($p->to_string);
-    $headers->content_type('application/x-www-form-urlencoded');
+    _type($headers, 'application/x-www-form-urlencoded');
   }
   return $tx;
 }
 
 sub _json {
   my ($self, $tx, $data) = @_;
-  my $headers = $tx->req->body(encode_json($data))->headers;
-  $headers->content_type('application/json') unless $headers->content_type;
+  _type($tx->req->body(encode_json $data)->headers, 'application/json');
   return $tx;
 }
 
@@ -254,6 +255,8 @@ sub _proxy {
   return $proto, $host, $port;
 }
 
+sub _type { $_[0]->content_type($_[1]) unless $_[0]->content_type }
+
 1;
 
 =encoding utf8
@@ -284,6 +287,23 @@ Mojo::UserAgent::Transactor - User agent transactor
 L<Mojo::UserAgent::Transactor> is the transaction building and manipulation
 framework used by L<Mojo::UserAgent>.
 
+=head1 GENERATORS
+
+These content generators are available by default.
+
+=head2 form
+
+  $t->tx(POST => 'http://example.com' => form => {a => 'b'});
+
+Generate query string, C<application/x-www-form-urlencoded> or
+C<multipart/form-data> content.
+
+=head2 json
+
+  $t->tx(PATCH => 'http://example.com' => json => {a => 'b'});
+
+Generate JSON content with L<Mojo::JSON>.
+
 =head1 ATTRIBUTES
 
 L<Mojo::UserAgent::Transactor> implements the following attributes.
@@ -345,18 +365,18 @@ C<307> or C<308> redirect response if possible.
 
   my $tx = $t->tx(GET  => 'example.com');
   my $tx = $t->tx(POST => 'http://example.com');
-  my $tx = $t->tx(GET  => 'http://example.com' => {DNT => 1});
+  my $tx = $t->tx(GET  => 'http://example.com' => {Accept => '*/*'});
   my $tx = $t->tx(PUT  => 'http://example.com' => 'Hi!');
   my $tx = $t->tx(PUT  => 'http://example.com' => form => {a => 'b'});
   my $tx = $t->tx(PUT  => 'http://example.com' => json => {a => 'b'});
-  my $tx = $t->tx(POST => 'http://example.com' => {DNT => 1} => 'Hi!');
+  my $tx = $t->tx(POST => 'http://example.com' => {Accept => '*/*'} => 'Hi!');
   my $tx = $t->tx(
-    PUT  => 'http://example.com' => {DNT => 1} => form => {a => 'b'});
+    PUT  => 'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
   my $tx = $t->tx(
-    PUT  => 'http://example.com' => {DNT => 1} => json => {a => 'b'});
+    PUT  => 'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Versatile general purpose L<Mojo::Transaction::HTTP> transaction builder for
-requests, with support for content generators.
+requests, with support for L</"GENERATORS">.
 
   # Generate and inspect custom GET request with DNT header and content
   say $t->tx(GET => 'example.com' => {DNT => 1} => 'Bye!')->req->to_string;
@@ -373,28 +393,51 @@ requests, with support for content generators.
   my $tx = $t->tx(PUT => 'http://example.com');
   $tx->req->content->asset(Mojo::Asset::File->new(path => '/foo.txt'));
 
-  # GET request with query parameters
-  my $tx = $t->tx(GET => 'http://example.com' => form => {a => 'b'});
+The C<json> content generator uses L<Mojo::JSON> for encoding and sets the
+content type to C<application/json>.
 
   # POST request with "application/json" content
   my $tx = $t->tx(
     POST => 'http://example.com' => json => {a => 'b', c => [1, 2, 3]});
 
+The C<form> content generator will automatically use query parameters for
+C<GET> and C<HEAD> requests.
+
+  # GET request with query parameters
+  my $tx = $t->tx(GET => 'http://example.com' => form => {a => 'b'});
+
+For all other request methods the C<application/x-www-form-urlencoded> content
+type is used.
+
   # POST request with "application/x-www-form-urlencoded" content
   my $tx = $t->tx(
     POST => 'http://example.com' => form => {a => 'b', c => 'd'});
 
+Parameters may be encoded with the C<charset> option.
+
   # PUT request with Shift_JIS encoded form values
   my $tx = $t->tx(
     PUT => 'example.com' => form => {a => 'b'} => charset => 'Shift_JIS');
 
+An array reference can be used for multiple form values sharing the same name.
+
   # POST request with form values sharing the same name
   my $tx = $t->tx(POST => 'http://example.com' => form => {a => [qw(b c d)]});
 
+A hash reference with a C<content> or C<file> value can be used to switch to
+the C<multipart/form-data> content type for file uploads.
+
   # POST request with "multipart/form-data" content
   my $tx = $t->tx(
     POST => 'http://example.com' => form => {mytext => {content => 'lala'}});
 
+  # POST request with multiple files sharing the same name
+  my $tx = $t->tx(POST => 'http://example.com' =>
+    form => {mytext => [{content => 'first'}, {content => 'second'}]});
+
+The C<file> value should contain the path to the file you want to upload or an
+asset object, like L<Mojo::Asset::File> or L<Mojo::Asset::Memory>.
+
   # POST request with upload streamed from file
   my $tx = $t->tx(
     POST => 'http://example.com' => form => {mytext => {file => '/foo.txt'}});
@@ -404,9 +447,9 @@ requests, with support for content generators.
   my $tx    = $t->tx(
     POST => 'http://example.com' => form => {mytext => {file => $asset}});
 
-  # POST request with multiple files sharing the same name
-  my $tx = $t->tx(POST => 'http://example.com' =>
-    form => {mytext => [{content => 'first'}, {content => 'second'}]});
+A C<filename> value will be generated automatically, but can also be set
+manually if necessary. All remainging values in the hash reference get merged
+into the C<multipart/form-data> content as headers.
 
   # POST request with form values and customized upload (filename and header)
   my $tx = $t->tx(POST => 'http://example.com' => form => {
@@ -419,11 +462,8 @@ requests, with support for content generators.
     }
   });
 
-The C<form> content generator will automatically use query parameters for
-C<GET>/C<HEAD> requests and the C<application/x-www-form-urlencoded> content
-type for everything else. Both get upgraded automatically to using the
-C<multipart/form-data> content type when necessary or when the header has been
-set manually.
+The C<multipart/form-data> content type can also be enforced by setting the
+C<Content-Type> header manually.
 
   # Force "multipart/form-data"
   my $headers = {'Content-Type' => 'multipart/form-data'};
@@ -85,21 +85,33 @@ sub _cleanup {
 }
 
 sub _connect {
-  my ($self, $nb, $proto, $host, $port, $handle, $cb) = @_;
+  my ($self, $nb, $peer, $tx, $handle, $cb) = @_;
+
+  my $t = $self->transactor;
+  my ($proto, $host, $port) = $peer ? $t->peer($tx) : $t->endpoint($tx);
+  my %options
+    = (address => $host, port => $port, timeout => $self->connect_timeout);
+  if (my $local = $self->local_address) { $options{local_address} = $local }
+  $options{handle} = $handle if $handle;
+
+  # SOCKS
+  if ($proto eq 'socks') {
+    @options{qw(socks_address socks_port)} = @options{qw(address port)};
+    ($proto, @options{qw(address port)}) = $t->endpoint($tx);
+    my $req      = $tx->req;
+    my $userinfo = $req->proxy->userinfo;
+    $req->proxy(0);
+    @options{qw(socks_user socks_pass)} = split ':', $userinfo if $userinfo;
+  }
+
+  # TLS
+  map { $options{"tls_$_"} = $self->$_ } qw(ca cert key)
+    if ($options{tls} = $proto eq 'https');
 
   weaken $self;
   my $id;
   return $id = $self->_loop($nb)->client(
-    address       => $host,
-    handle        => $handle,
-    local_address => $self->local_address,
-    port          => $port,
-    timeout       => $self->connect_timeout,
-    tls           => $proto eq 'https',
-    tls_ca        => $self->ca,
-    tls_cert      => $self->cert,
-    tls_key       => $self->key,
-    sub {
+    %options => sub {
       my ($loop, $err, $stream) = @_;
 
       # Connection error
@@ -107,8 +119,7 @@ sub _connect {
       return $self->_error($id, $err) if $err;
 
       # Connection established
-      $stream->on(
-        timeout => sub { $self->_error($id, 'Inactivity timeout', 1) });
+      $stream->on(timeout => sub { $self->_error($id, 'Inactivity timeout') });
       $stream->on(close => sub { $self && $self->_finish($id, 1) });
       $stream->on(error => sub { $self && $self->_error($id, pop) });
       $stream->on(read => sub { $self->_read($id, pop) });
@@ -127,12 +138,11 @@ sub _connect_proxy {
       my ($self, $tx) = @_;
 
       # CONNECT failed (connection needs to be kept alive)
-      unless ($tx->keep_alive && $tx->res->is_status_class(200)) {
-        $old->req->error({message => 'Proxy connection failed'});
-        return $self->$cb($old);
-      }
+      $old->res->error({message => 'Proxy connection failed'})
+        and return $self->$cb($old)
+        if $tx->error || !$tx->res->is_status_class(200) || !$tx->keep_alive;
 
-      # Prevent proxy reassignment and start real transaction
+      # Start real transaction
       $old->req->proxy(0);
       my $id = $tx->connection;
       return $self->_start($nb, $old->connection($id), $cb)
@@ -141,11 +151,10 @@ sub _connect_proxy {
       # TLS upgrade
       my $loop   = $self->_loop($nb);
       my $handle = $loop->stream($id)->steal_handle;
-      my $c      = delete $self->{connections}{$id};
       $loop->remove($id);
-      $id = $self->_connect($nb, $self->transactor->endpoint($old),
-        $handle, sub { shift->_start($nb, $old->connection($id), $cb) });
-      $self->{connections}{$id} = $c;
+      $id = $self->_connect($nb, 0, $old, $handle,
+        sub { shift->_start($nb, $old->connection($id), $cb) });
+      $self->{connections}{$id} = {cb => $cb, nb => $nb, tx => $old};
     }
   );
 }
@@ -190,8 +199,7 @@ sub _connection {
 
   # Connect
   warn "-- Connect ($proto:$host:$port)\n" if DEBUG;
-  ($proto, $host, $port) = $self->transactor->peer($tx);
-  $id = $self->_connect(($nb, $proto, $host, $port, $id) => \&_connected);
+  $id = $self->_connect($nb, 1, $tx, $id, \&_connected);
   $self->{connections}{$id} = {cb => $cb, nb => $nb, tx => $tx};
 
   return $id;
@@ -226,12 +234,9 @@ sub _enqueue {
 }
 
 sub _error {
-  my ($self, $id, $err, $timeout) = @_;
-
-  if (my $tx = $self->{connections}{$id}{tx}) {
-    $tx->res->error({message => $err});
-  }
-  elsif (!$timeout) { return $self->emit(error => $err) }
+  my ($self, $id, $err) = @_;
+  my $tx = $self->{connections}{$id}{tx};
+  $tx->res->error({message => $err}) if $tx;
   $self->_finish($id, 1);
 }
 
@@ -239,7 +244,7 @@ sub _finish {
   my ($self, $id, $close) = @_;
 
   # Remove request timeout
-  my $c = $self->{connections}{$id};
+  return unless my $c    = $self->{connections}{$id};
   return unless my $loop = $self->_loop($c->{nb});
   $loop->remove($c->{timeout}) if $c->{timeout};
 
@@ -293,11 +298,8 @@ sub _remove {
   # Close connection
   my $c = delete $self->{connections}{$id} || {};
   my $tx = $c->{tx};
-  if ($close || !$tx || !$tx->keep_alive || $tx->error) {
-    $self->_dequeue($_, $id) for 1, 0;
-    $self->_loop($_)->remove($id) for 1, 0;
-    return;
-  }
+  return map { $self->_dequeue($_, $id); $self->_loop($_)->remove($id) } 1, 0
+    if $close || !$tx || !$tx->keep_alive || $tx->error;
 
   # Keep connection alive (CONNECT requests get upgraded)
   $self->_enqueue($c->{nb}, join(':', $self->transactor->endpoint($tx)), $id)
@@ -314,8 +316,7 @@ sub _start {
     $url->scheme($base->scheme)->authority($base->authority);
   }
 
-  $self->proxy->inject($tx);
-  if (my $jar = $self->cookie_jar) { $jar->inject($tx) }
+  $_ && $_->inject($tx) for $self->proxy, $self->cookie_jar;
 
   # Connect and add request timeout if necessary
   my $id = $self->emit(start => $tx)->_connection($nb, $tx, $cb);
@@ -377,17 +378,24 @@ Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent
     ->res->json('/results/0/title');
 
   # Extract data from HTML and XML resources
-  say $ua->get('www.perl.org')->res->dom->html->head->title->text;
+  say $ua->get('www.perl.org')->res->dom->at('title')->text;
 
   # Scrape the latest headlines from a news site with CSS selectors
-  say $ua->get('blogs.perl.org')->res->dom('h2 > a')->text->shuffle;
+  say $ua->get('blogs.perl.org')
+    ->res->dom->find('h2 > a')->map('text')->join("\n");
+
+  # Search DuckDuckGo anonymously through Tor
+  $ua->proxy->http('socks://127.0.0.1:9050');
+  say $ua->get('api.3g2upl4pq6kufc4m.onion/?q=mojolicious&format=json')
+    ->res->json('/Abstract');
 
   # IPv6 PUT request with content
   my $tx
     = $ua->put('[::1]:3000' => {'Content-Type' => 'text/plain'} => 'Hello!');
 
   # Follow redirects to grab the latest Mojolicious release :)
-  $ua->max_redirects(5)->get('latest.mojolicio.us')
+  $ua->max_redirects(5)
+    ->get('https://www.github.com/kraih/mojo/tarball/master')
     ->res->content->asset->move_to('/Users/sri/mojo.tar.gz');
 
   # TLS certificate authentication and JSON POST
@@ -395,18 +403,18 @@ Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent
     ->post('https://example.com' => json => {top => 'secret'});
 
   # Non-blocking concurrent requests
-  my $delay = Mojo::IOLoop->delay(sub {
-    my ($delay, @titles) = @_;
-    say for @titles;
-  });
-  for my $url ('mojolicio.us', 'cpan.org') {
-    my $end = $delay->begin(0);
-    $ua->get($url => sub {
-      my ($ua, $tx) = @_;
-      $end->($tx->res->dom->at('title')->text);
-    });
-  }
-  $delay->wait;
+  Mojo::IOLoop->delay(
+    sub {
+      my $delay = shift;
+      $ua->get('mojolicio.us' => $delay->begin);
+      $ua->get('cpan.org'     => $delay->begin);
+    },
+    sub {
+      my ($delay, $mojo, $cpan) = @_;
+      say $mojo->res->dom->at('title')->text;
+      say $cpan->res->dom->at('title')->text;
+    }
+  )->wait;
 
   # Non-blocking WebSocket connection sending and receiving JSON messages
   $ua->websocket('ws://example.com/echo.json' => sub {
@@ -424,19 +432,20 @@ Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent
 =head1 DESCRIPTION
 
 L<Mojo::UserAgent> is a full featured non-blocking I/O HTTP and WebSocket user
-agent, with IPv6, TLS, SNI, IDNA, Comet (long polling), keep-alive, connection
-pooling, timeout, cookie, multipart, proxy, gzip compression and multiple
-event loop support.
+agent, with IPv6, TLS, SNI, IDNA, HTTP/SOCKS5 proxy, Comet (long polling),
+keep-alive, connection pooling, timeout, cookie, multipart, gzip compression
+and multiple event loop support.
 
 All connections will be reset automatically if a new process has been forked,
 this allows multiple processes to share the same L<Mojo::UserAgent> object
 safely.
 
-For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS
-support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+) and
-L<IO::Socket::SSL> (1.84+) will be used automatically by L<Mojo::IOLoop> if
-they are installed. Individual features can also be disabled with the
-C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables.
+For better scalability (epoll, kqueue) and to provide non-blocking name
+resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
+L<Net::DNS::Native> (0.12+), L<IO::Socket::Socks> (0.64+) and
+L<IO::Socket::SSL> (1.84+) will be used automatically if they are installed.
+Individual features can also be disabled with the C<MOJO_NO_NDN>,
+C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
 
 See L<Mojolicious::Guides::Cookbook/"USER AGENT"> for more.
 
@@ -445,21 +454,6 @@ See L<Mojolicious::Guides::Cookbook/"USER AGENT"> for more.
 L<Mojo::UserAgent> inherits all events from L<Mojo::EventEmitter> and can emit
 the following new ones.
 
-=head2 error
-
-  $ua->on(error => sub {
-    my ($ua, $err) = @_;
-    ...
-  });
-
-Emitted if an error occurs that can't be associated with a transaction, fatal
-if unhandled.
-
-  $ua->on(error => sub {
-    my ($ua, $err) = @_;
-    say "This looks bad: $err";
-  });
-
 =head2 start
 
   $ua->on(start => sub {
@@ -513,11 +507,11 @@ environment variable or C<10>.
   my $cookie_jar = $ua->cookie_jar;
   $ua            = $ua->cookie_jar(Mojo::UserAgent::CookieJar->new);
 
-Cookie jar to use for this user agents requests, defaults to a
+Cookie jar to use for requests performed by this user agent, defaults to a
 L<Mojo::UserAgent::CookieJar> object.
 
-  # Disable cookie jar
-  $ua->cookie_jar(0);
+  # Disable extraction of cookies from responses
+  $ua->cookie_jar->extracting(0);
 
 =head2 inactivity_timeout
 
@@ -580,6 +574,12 @@ Proxy manager, defaults to a L<Mojo::UserAgent::Proxy> object.
   # Detect proxy servers from environment
   $ua->proxy->detect;
 
+  # Manually configure HTTP proxy (using CONNECT for HTTPS)
+  $ua->proxy->http('http://127.0.0.1:8080')->https('http://127.0.0.1:8080');
+
+  # Manually configure Tor (SOCKS5)
+  $ua->proxy->http('socks://127.0.0.1:9050')->https('socks://127.0.0.1:9050');
+
 =head2 request_timeout
 
   my $timeout = $ua->request_timeout;
@@ -632,11 +632,12 @@ implements the following new ones.
 =head2 build_tx
 
   my $tx = $ua->build_tx(GET => 'example.com');
-  my $tx = $ua->build_tx(PUT => 'http://example.com' => {DNT => 1} => 'Hi!');
   my $tx = $ua->build_tx(
-    PUT => 'http://example.com' => {DNT => 1} => form => {a => 'b'});
+    PUT => 'http://example.com' => {Accept => '*/*'} => 'Hi!');
+  my $tx = $ua->build_tx(
+    PUT => 'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
   my $tx = $ua->build_tx(
-    PUT => 'http://example.com' => {DNT => 1} => json => {a => 'b'});
+    PUT => 'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Generate L<Mojo::Transaction::HTTP> object with
 L<Mojo::UserAgent::Transactor/"tx">.
@@ -672,16 +673,16 @@ L<Mojo::UserAgent::Transactor/"websocket">.
 =head2 delete
 
   my $tx = $ua->delete('example.com');
-  my $tx = $ua->delete('http://example.com' => {DNT => 1} => 'Hi!');
+  my $tx = $ua->delete('http://example.com' => {Accept => '*/*'} => 'Hi!');
   my $tx = $ua->delete(
-    'http://example.com' => {DNT => 1} => form => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
   my $tx = $ua->delete(
-    'http://example.com' => {DNT => 1} => json => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform blocking C<DELETE> request and return resulting
 L<Mojo::Transaction::HTTP> object, takes the same arguments as
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also
-append a callback to perform requests non-blocking.
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<DELETE> method, which is
+implied). You can also append a callback to perform requests non-blocking.
 
   $ua->delete('http://example.com' => sub {
     my ($ua, $tx) = @_;
@@ -692,14 +693,16 @@ append a callback to perform requests non-blocking.
 =head2 get
 
   my $tx = $ua->get('example.com');
-  my $tx = $ua->get('http://example.com' => {DNT => 1} => 'Hi!');
-  my $tx = $ua->get('http://example.com' => {DNT => 1} => form => {a => 'b'});
-  my $tx = $ua->get('http://example.com' => {DNT => 1} => json => {a => 'b'});
+  my $tx = $ua->get('http://example.com' => {Accept => '*/*'} => 'Hi!');
+  my $tx = $ua->get(
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
+  my $tx = $ua->get(
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform blocking C<GET> request and return resulting
 L<Mojo::Transaction::HTTP> object, takes the same arguments as
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also
-append a callback to perform requests non-blocking.
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<GET> method, which is
+implied). You can also append a callback to perform requests non-blocking.
 
   $ua->get('http://example.com' => sub {
     my ($ua, $tx) = @_;
@@ -710,16 +713,16 @@ append a callback to perform requests non-blocking.
 =head2 head
 
   my $tx = $ua->head('example.com');
-  my $tx = $ua->head('http://example.com' => {DNT => 1} => 'Hi!');
+  my $tx = $ua->head('http://example.com' => {Accept => '*/*'} => 'Hi!');
   my $tx = $ua->head(
-    'http://example.com' => {DNT => 1} => form => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
   my $tx = $ua->head(
-    'http://example.com' => {DNT => 1} => json => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform blocking C<HEAD> request and return resulting
 L<Mojo::Transaction::HTTP> object, takes the same arguments as
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also
-append a callback to perform requests non-blocking.
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<HEAD> method, which is
+implied). You can also append a callback to perform requests non-blocking.
 
   $ua->head('http://example.com' => sub {
     my ($ua, $tx) = @_;
@@ -730,16 +733,16 @@ append a callback to perform requests non-blocking.
 =head2 options
 
   my $tx = $ua->options('example.com');
-  my $tx = $ua->options('http://example.com' => {DNT => 1} => 'Hi!');
+  my $tx = $ua->options('http://example.com' => {Accept => '*/*'} => 'Hi!');
   my $tx = $ua->options(
-    'http://example.com' => {DNT => 1} => form => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
   my $tx = $ua->options(
-    'http://example.com' => {DNT => 1} => json => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform blocking C<OPTIONS> request and return resulting
 L<Mojo::Transaction::HTTP> object, takes the same arguments as
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also
-append a callback to perform requests non-blocking.
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<OPTIONS> method, which
+is implied). You can also append a callback to perform requests non-blocking.
 
   $ua->options('http://example.com' => sub {
     my ($ua, $tx) = @_;
@@ -750,16 +753,16 @@ append a callback to perform requests non-blocking.
 =head2 patch
 
   my $tx = $ua->patch('example.com');
-  my $tx = $ua->patch('http://example.com' => {DNT => 1} => 'Hi!');
+  my $tx = $ua->patch('http://example.com' => {Accept => '*/*'} => 'Hi!');
   my $tx = $ua->patch(
-    'http://example.com' => {DNT => 1} => form => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
   my $tx = $ua->patch(
-    'http://example.com' => {DNT => 1} => json => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform blocking C<PATCH> request and return resulting
 L<Mojo::Transaction::HTTP> object, takes the same arguments as
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also
-append a callback to perform requests non-blocking.
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<PATCH> method, which is
+implied). You can also append a callback to perform requests non-blocking.
 
   $ua->patch('http://example.com' => sub {
     my ($ua, $tx) = @_;
@@ -770,16 +773,16 @@ append a callback to perform requests non-blocking.
 =head2 post
 
   my $tx = $ua->post('example.com');
-  my $tx = $ua->post('http://example.com' => {DNT => 1} => 'Hi!');
+  my $tx = $ua->post('http://example.com' => {Accept => '*/*'} => 'Hi!');
   my $tx = $ua->post(
-    'http://example.com' => {DNT => 1} => form => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
   my $tx = $ua->post(
-    'http://example.com' => {DNT => 1} => json => {a => 'b'});
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform blocking C<POST> request and return resulting
 L<Mojo::Transaction::HTTP> object, takes the same arguments as
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also
-append a callback to perform requests non-blocking.
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<POST> method, which is
+implied). You can also append a callback to perform requests non-blocking.
 
   $ua->post('http://example.com' => sub {
     my ($ua, $tx) = @_;
@@ -790,14 +793,16 @@ append a callback to perform requests non-blocking.
 =head2 put
 
   my $tx = $ua->put('example.com');
-  my $tx = $ua->put('http://example.com' => {DNT => 1} => 'Hi!');
-  my $tx = $ua->put('http://example.com' => {DNT => 1} => form => {a => 'b'});
-  my $tx = $ua->put('http://example.com' => {DNT => 1} => json => {a => 'b'});
+  my $tx = $ua->put('http://example.com' => {Accept => '*/*'} => 'Hi!');
+  my $tx = $ua->put(
+    'http://example.com' => {Accept => '*/*'} => form => {a => 'b'});
+  my $tx = $ua->put(
+    'http://example.com' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform blocking C<PUT> request and return resulting
 L<Mojo::Transaction::HTTP> object, takes the same arguments as
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also
-append a callback to perform requests non-blocking.
+L<Mojo::UserAgent::Transactor/"tx"> (except for the C<PUT> method, which is
+implied). You can also append a callback to perform requests non-blocking.
 
   $ua->put('http://example.com' => sub {
     my ($ua, $tx) = @_;
@@ -809,8 +814,9 @@ append a callback to perform requests non-blocking.
 
   my $tx = $ua->start(Mojo::Transaction::HTTP->new);
 
-Perform blocking request. You can also append a callback to perform requests
-non-blocking.
+Perform blocking request for a custom L<Mojo::Transaction::HTTP> object, which
+can be prepared manually or with L</"build_tx">. You can also append a
+callback to perform requests non-blocking.
 
   my $tx = $ua->build_tx(GET => 'http://example.com');
   $ua->start($tx => sub {
@@ -828,7 +834,8 @@ non-blocking.
 Open a non-blocking WebSocket connection with transparent handshake, takes the
 same arguments as L<Mojo::UserAgent::Transactor/"websocket">. The callback
 will receive either a L<Mojo::Transaction::WebSocket> or
-L<Mojo::Transaction::HTTP> object.
+L<Mojo::Transaction::HTTP> object, depending on if the handshake was
+successful.
 
   $ua->websocket('ws://example.com/echo' => sub {
     my ($ua, $tx) = @_;
@@ -7,10 +7,9 @@ use Digest::MD5 qw(md5 md5_hex);
 use Digest::SHA qw(hmac_sha1_hex sha1 sha1_hex);
 use Encode 'find_encoding';
 use Exporter 'import';
-use File::Basename 'dirname';
-use File::Spec::Functions 'catfile';
 use List::Util 'min';
 use MIME::Base64 qw(decode_base64 encode_base64);
+use Symbol 'delete_package';
 use Time::HiRes ();
 
 # Check for monotonic clock support
@@ -28,10 +27,13 @@ use constant {
   PC_INITIAL_N    => 128
 };
 
-# To update HTML entities run this command
-# perl examples/entities.pl > lib/Mojo/entities.txt
+# Will be shipping with Perl 5.22
+my $NAME = eval 'use Sub::Util; 1' ? \&Sub::Util::set_subname : sub { $_[1] };
+
+# To generate a new HTML entity table run this command
+# perl examples/entities.pl
 my %ENTITIES;
-for my $line (split "\x0a", slurp(catfile dirname(__FILE__), 'entities.txt')) {
+while (my $line = <DATA>) {
   next unless $line =~ /^(\S+)\s+U\+(\S+)(?:\s+U\+(\S+))?/;
   $ENTITIES{$1} = defined $3 ? (chr(hex $2) . chr(hex $3)) : chr(hex $2);
 }
@@ -54,7 +56,7 @@ our @EXPORT_OK = (
   qw(md5_sum monkey_patch punycode_decode punycode_encode quote),
   qw(secure_compare sha1_bytes sha1_sum slurp split_header spurt squish),
   qw(steady_time tablify trim unindent unquote url_escape url_unescape),
-  qw(xml_escape xor_encode)
+  qw(xml_escape xor_encode xss_escape)
 );
 
 sub b64_decode { decode_base64($_[0]) }
@@ -66,7 +68,7 @@ sub camelize {
 
   # CamelCase words
   return join '::', map {
-    join '', map { ucfirst lc } split '_', $_
+    join('', map { ucfirst lc } split '_')
   } split '-', $str;
 }
 
@@ -83,17 +85,10 @@ sub decamelize {
   my $str = shift;
   return $str if $str !~ /^[A-Z]/;
 
-  # Module parts
-  my @parts;
-  for my $part (split '::', $str) {
-
-    # snake_case words
-    my @words;
-    push @words, lc $1 while $part =~ s/([A-Z]{1}[^A-Z]*)//;
-    push @parts, join '_', @words;
-  }
-
-  return join '-', @parts;
+  # snake_case words
+  return join '-', map {
+    join('_', map {lc} grep {length} split /([A-Z]{1}[^A-Z]*)/)
+  } split '::', $str;
 }
 
 sub decode {
@@ -129,7 +124,7 @@ sub monkey_patch {
   my ($class, %patch) = @_;
   no strict 'refs';
   no warnings 'redefine';
-  *{"${class}::$_"} = $patch{$_} for keys %patch;
+  *{"${class}::$_"} = $NAME->("${class}::$_", $patch{$_}) for keys %patch;
 }
 
 # Direct translation of RFC 3492
@@ -295,7 +290,7 @@ sub tablify {
     for my $i (0 .. $#$row) {
       $row->[$i] =~ s/[\r\n]//g;
       my $len = length $row->[$i];
-      $spec[$i] = $len if $len > ($spec[$i] // 0);
+      $spec[$i] = $len if $len >= ($spec[$i] // 0);
     }
   }
 
@@ -327,8 +322,8 @@ sub unquote {
 
 sub url_escape {
   my ($str, $pattern) = @_;
-  $pattern ||= '^A-Za-z0-9\-._~';
-  $str =~ s/([$pattern])/sprintf('%%%02X',ord($1))/ge;
+  if ($pattern) { $str =~ s/([$pattern])/sprintf('%%%02X',ord($1))/ge }
+  else          { $str =~ s/([^A-Za-z0-9\-._~])/sprintf('%%%02X',ord($1))/ge }
   return $str;
 }
 
@@ -355,6 +350,11 @@ sub xor_encode {
   return $output .= $buffer ^ substr($key, 0, length $buffer, '');
 }
 
+sub xss_escape {
+  no warnings 'uninitialized';
+  ref $_[0] eq 'Mojo::ByteStream' ? $_[0] : xml_escape("$_[0]");
+}
+
 sub _adapt {
   my ($delta, $numpoints, $firsttime) = @_;
   use integer;
@@ -389,6 +389,44 @@ sub _encoding {
   $CACHE{$_[0]} //= find_encoding($_[0]) // croak "Unknown encoding '$_[0]'";
 }
 
+sub _options {
+
+  # Hash or name (one)
+  return ref $_[0] eq 'HASH' ? (undef, %{shift()}) : @_ if @_ == 1;
+
+  # Name and values (odd)
+  return shift, @_ if @_ % 2;
+
+  # Name and hash or just values (even)
+  return ref $_[1] eq 'HASH' ? (shift, %{shift()}) : (undef, @_);
+}
+
+sub _stash {
+  my ($name, $object) = (shift, shift);
+
+  # Hash
+  my $dict = $object->{$name} ||= {};
+  return $dict unless @_;
+
+  # Get
+  return $dict->{$_[0]} unless @_ > 1 || ref $_[0];
+
+  # Set
+  my $values = ref $_[0] ? $_[0] : {@_};
+  @$dict{keys %$values} = values %$values;
+
+  return $object;
+}
+
+sub _teardown {
+  return unless my $class = shift;
+
+  # @ISA has to be cleared first because of circular references
+  no strict 'refs';
+  @{"${class}::ISA"} = ();
+  delete_package $class;
+}
+
 1;
 
 =encoding utf8
@@ -625,8 +663,9 @@ consecutive groups of whitespace into one space each.
 
   my $time = steady_time;
 
-High resolution time, resilient to time jumps if a monotonic clock is
-available through L<Time::HiRes>.
+High resolution time elapsed from an arbitrary fixed point in the past,
+resilient to time jumps if a monotonic clock is available through
+L<Time::HiRes>.
 
 =head2 tablify
 
@@ -683,8 +722,2247 @@ Escape unsafe characters C<&>, C<E<lt>>, C<E<gt>>, C<"> and C<'> in string.
 
 XOR encode string with variable length key.
 
+=head2 xss_escape
+
+  my $escaped = xss_escape $str;
+
+Same as L</"xml_escape">, but does not escape L<Mojo::ByteStream> objects.
+
 =head1 SEE ALSO
 
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
 
 =cut
+
+__DATA__
+Aacute; U+000C1
+Aacute U+000C1
+aacute; U+000E1
+aacute U+000E1
+Abreve; U+00102
+abreve; U+00103
+ac; U+0223E
+acd; U+0223F
+acE; U+0223E U+00333
+Acirc; U+000C2
+Acirc U+000C2
+acirc; U+000E2
+acirc U+000E2
+acute; U+000B4
+acute U+000B4
+Acy; U+00410
+acy; U+00430
+AElig; U+000C6
+AElig U+000C6
+aelig; U+000E6
+aelig U+000E6
+af; U+02061
+Afr; U+1D504
+afr; U+1D51E
+Agrave; U+000C0
+Agrave U+000C0
+agrave; U+000E0
+agrave U+000E0
+alefsym; U+02135
+aleph; U+02135
+Alpha; U+00391
+alpha; U+003B1
+Amacr; U+00100
+amacr; U+00101
+amalg; U+02A3F
+AMP; U+00026
+AMP U+00026
+amp; U+00026
+amp U+00026
+And; U+02A53
+and; U+02227
+andand; U+02A55
+andd; U+02A5C
+andslope; U+02A58
+andv; U+02A5A
+ang; U+02220
+ange; U+029A4
+angle; U+02220
+angmsd; U+02221
+angmsdaa; U+029A8
+angmsdab; U+029A9
+angmsdac; U+029AA
+angmsdad; U+029AB
+angmsdae; U+029AC
+angmsdaf; U+029AD
+angmsdag; U+029AE
+angmsdah; U+029AF
+angrt; U+0221F
+angrtvb; U+022BE
+angrtvbd; U+0299D
+angsph; U+02222
+angst; U+000C5
+angzarr; U+0237C
+Aogon; U+00104
+aogon; U+00105
+Aopf; U+1D538
+aopf; U+1D552
+ap; U+02248
+apacir; U+02A6F
+apE; U+02A70
+ape; U+0224A
+apid; U+0224B
+apos; U+00027
+ApplyFunction; U+02061
+approx; U+02248
+approxeq; U+0224A
+Aring; U+000C5
+Aring U+000C5
+aring; U+000E5
+aring U+000E5
+Ascr; U+1D49C
+ascr; U+1D4B6
+Assign; U+02254
+ast; U+0002A
+asymp; U+02248
+asympeq; U+0224D
+Atilde; U+000C3
+Atilde U+000C3
+atilde; U+000E3
+atilde U+000E3
+Auml; U+000C4
+Auml U+000C4
+auml; U+000E4
+auml U+000E4
+awconint; U+02233
+awint; U+02A11
+backcong; U+0224C
+backepsilon; U+003F6
+backprime; U+02035
+backsim; U+0223D
+backsimeq; U+022CD
+Backslash; U+02216
+Barv; U+02AE7
+barvee; U+022BD
+Barwed; U+02306
+barwed; U+02305
+barwedge; U+02305
+bbrk; U+023B5
+bbrktbrk; U+023B6
+bcong; U+0224C
+Bcy; U+00411
+bcy; U+00431
+bdquo; U+0201E
+becaus; U+02235
+Because; U+02235
+because; U+02235
+bemptyv; U+029B0
+bepsi; U+003F6
+bernou; U+0212C
+Bernoullis; U+0212C
+Beta; U+00392
+beta; U+003B2
+beth; U+02136
+between; U+0226C
+Bfr; U+1D505
+bfr; U+1D51F
+bigcap; U+022C2
+bigcirc; U+025EF
+bigcup; U+022C3
+bigodot; U+02A00
+bigoplus; U+02A01
+bigotimes; U+02A02
+bigsqcup; U+02A06
+bigstar; U+02605
+bigtriangledown; U+025BD
+bigtriangleup; U+025B3
+biguplus; U+02A04
+bigvee; U+022C1
+bigwedge; U+022C0
+bkarow; U+0290D
+blacklozenge; U+029EB
+blacksquare; U+025AA
+blacktriangle; U+025B4
+blacktriangledown; U+025BE
+blacktriangleleft; U+025C2
+blacktriangleright; U+025B8
+blank; U+02423
+blk12; U+02592
+blk14; U+02591
+blk34; U+02593
+block; U+02588
+bne; U+0003D U+020E5
+bnequiv; U+02261 U+020E5
+bNot; U+02AED
+bnot; U+02310
+Bopf; U+1D539
+bopf; U+1D553
+bot; U+022A5
+bottom; U+022A5
+bowtie; U+022C8
+boxbox; U+029C9
+boxDL; U+02557
+boxDl; U+02556
+boxdL; U+02555
+boxdl; U+02510
+boxDR; U+02554
+boxDr; U+02553
+boxdR; U+02552
+boxdr; U+0250C
+boxH; U+02550
+boxh; U+02500
+boxHD; U+02566
+boxHd; U+02564
+boxhD; U+02565
+boxhd; U+0252C
+boxHU; U+02569
+boxHu; U+02567
+boxhU; U+02568
+boxhu; U+02534
+boxminus; U+0229F
+boxplus; U+0229E
+boxtimes; U+022A0
+boxUL; U+0255D
+boxUl; U+0255C
+boxuL; U+0255B
+boxul; U+02518
+boxUR; U+0255A
+boxUr; U+02559
+boxuR; U+02558
+boxur; U+02514
+boxV; U+02551
+boxv; U+02502
+boxVH; U+0256C
+boxVh; U+0256B
+boxvH; U+0256A
+boxvh; U+0253C
+boxVL; U+02563
+boxVl; U+02562
+boxvL; U+02561
+boxvl; U+02524
+boxVR; U+02560
+boxVr; U+0255F
+boxvR; U+0255E
+boxvr; U+0251C
+bprime; U+02035
+Breve; U+002D8
+breve; U+002D8
+brvbar; U+000A6
+brvbar U+000A6
+Bscr; U+0212C
+bscr; U+1D4B7
+bsemi; U+0204F
+bsim; U+0223D
+bsime; U+022CD
+bsol; U+0005C
+bsolb; U+029C5
+bsolhsub; U+027C8
+bull; U+02022
+bullet; U+02022
+bump; U+0224E
+bumpE; U+02AAE
+bumpe; U+0224F
+Bumpeq; U+0224E
+bumpeq; U+0224F
+Cacute; U+00106
+cacute; U+00107
+Cap; U+022D2
+cap; U+02229
+capand; U+02A44
+capbrcup; U+02A49
+capcap; U+02A4B
+capcup; U+02A47
+capdot; U+02A40
+CapitalDifferentialD; U+02145
+caps; U+02229 U+0FE00
+caret; U+02041
+caron; U+002C7
+Cayleys; U+0212D
+ccaps; U+02A4D
+Ccaron; U+0010C
+ccaron; U+0010D
+Ccedil; U+000C7
+Ccedil U+000C7
+ccedil; U+000E7
+ccedil U+000E7
+Ccirc; U+00108
+ccirc; U+00109
+Cconint; U+02230
+ccups; U+02A4C
+ccupssm; U+02A50
+Cdot; U+0010A
+cdot; U+0010B
+cedil; U+000B8
+cedil U+000B8
+Cedilla; U+000B8
+cemptyv; U+029B2
+cent; U+000A2
+cent U+000A2
+CenterDot; U+000B7
+centerdot; U+000B7
+Cfr; U+0212D
+cfr; U+1D520
+CHcy; U+00427
+chcy; U+00447
+check; U+02713
+checkmark; U+02713
+Chi; U+003A7
+chi; U+003C7
+cir; U+025CB
+circ; U+002C6
+circeq; U+02257
+circlearrowleft; U+021BA
+circlearrowright; U+021BB
+circledast; U+0229B
+circledcirc; U+0229A
+circleddash; U+0229D
+CircleDot; U+02299
+circledR; U+000AE
+circledS; U+024C8
+CircleMinus; U+02296
+CirclePlus; U+02295
+CircleTimes; U+02297
+cirE; U+029C3
+cire; U+02257
+cirfnint; U+02A10
+cirmid; U+02AEF
+cirscir; U+029C2
+ClockwiseContourIntegral; U+02232
+CloseCurlyDoubleQuote; U+0201D
+CloseCurlyQuote; U+02019
+clubs; U+02663
+clubsuit; U+02663
+Colon; U+02237
+colon; U+0003A
+Colone; U+02A74
+colone; U+02254
+coloneq; U+02254
+comma; U+0002C
+commat; U+00040
+comp; U+02201
+compfn; U+02218
+complement; U+02201
+complexes; U+02102
+cong; U+02245
+congdot; U+02A6D
+Congruent; U+02261
+Conint; U+0222F
+conint; U+0222E
+ContourIntegral; U+0222E
+Copf; U+02102
+copf; U+1D554
+coprod; U+02210
+Coproduct; U+02210
+COPY; U+000A9
+COPY U+000A9
+copy; U+000A9
+copy U+000A9
+copysr; U+02117
+CounterClockwiseContourIntegral; U+02233
+crarr; U+021B5
+Cross; U+02A2F
+cross; U+02717
+Cscr; U+1D49E
+cscr; U+1D4B8
+csub; U+02ACF
+csube; U+02AD1
+csup; U+02AD0
+csupe; U+02AD2
+ctdot; U+022EF
+cudarrl; U+02938
+cudarrr; U+02935
+cuepr; U+022DE
+cuesc; U+022DF
+cularr; U+021B6
+cularrp; U+0293D
+Cup; U+022D3
+cup; U+0222A
+cupbrcap; U+02A48
+CupCap; U+0224D
+cupcap; U+02A46
+cupcup; U+02A4A
+cupdot; U+0228D
+cupor; U+02A45
+cups; U+0222A U+0FE00
+curarr; U+021B7
+curarrm; U+0293C
+curlyeqprec; U+022DE
+curlyeqsucc; U+022DF
+curlyvee; U+022CE
+curlywedge; U+022CF
+curren; U+000A4
+curren U+000A4
+curvearrowleft; U+021B6
+curvearrowright; U+021B7
+cuvee; U+022CE
+cuwed; U+022CF
+cwconint; U+02232
+cwint; U+02231
+cylcty; U+0232D
+Dagger; U+02021
+dagger; U+02020
+daleth; U+02138
+Darr; U+021A1
+dArr; U+021D3
+darr; U+02193
+dash; U+02010
+Dashv; U+02AE4
+dashv; U+022A3
+dbkarow; U+0290F
+dblac; U+002DD
+Dcaron; U+0010E
+dcaron; U+0010F
+Dcy; U+00414
+dcy; U+00434
+DD; U+02145
+dd; U+02146
+ddagger; U+02021
+ddarr; U+021CA
+DDotrahd; U+02911
+ddotseq; U+02A77
+deg; U+000B0
+deg U+000B0
+Del; U+02207
+Delta; U+00394
+delta; U+003B4
+demptyv; U+029B1
+dfisht; U+0297F
+Dfr; U+1D507
+dfr; U+1D521
+dHar; U+02965
+dharl; U+021C3
+dharr; U+021C2
+DiacriticalAcute; U+000B4
+DiacriticalDot; U+002D9
+DiacriticalDoubleAcute; U+002DD
+DiacriticalGrave; U+00060
+DiacriticalTilde; U+002DC
+diam; U+022C4
+Diamond; U+022C4
+diamond; U+022C4
+diamondsuit; U+02666
+diams; U+02666
+die; U+000A8
+DifferentialD; U+02146
+digamma; U+003DD
+disin; U+022F2
+div; U+000F7
+divide; U+000F7
+divide U+000F7
+divideontimes; U+022C7
+divonx; U+022C7
+DJcy; U+00402
+djcy; U+00452
+dlcorn; U+0231E
+dlcrop; U+0230D
+dollar; U+00024
+Dopf; U+1D53B
+dopf; U+1D555
+Dot; U+000A8
+dot; U+002D9
+DotDot; U+020DC
+doteq; U+02250
+doteqdot; U+02251
+DotEqual; U+02250
+dotminus; U+02238
+dotplus; U+02214
+dotsquare; U+022A1
+doublebarwedge; U+02306
+DoubleContourIntegral; U+0222F
+DoubleDot; U+000A8
+DoubleDownArrow; U+021D3
+DoubleLeftArrow; U+021D0
+DoubleLeftRightArrow; U+021D4
+DoubleLeftTee; U+02AE4
+DoubleLongLeftArrow; U+027F8
+DoubleLongLeftRightArrow; U+027FA
+DoubleLongRightArrow; U+027F9
+DoubleRightArrow; U+021D2
+DoubleRightTee; U+022A8
+DoubleUpArrow; U+021D1
+DoubleUpDownArrow; U+021D5
+DoubleVerticalBar; U+02225
+DownArrow; U+02193
+Downarrow; U+021D3
+downarrow; U+02193
+DownArrowBar; U+02913
+DownArrowUpArrow; U+021F5
+DownBreve; U+00311
+downdownarrows; U+021CA
+downharpoonleft; U+021C3
+downharpoonright; U+021C2
+DownLeftRightVector; U+02950
+DownLeftTeeVector; U+0295E
+DownLeftVector; U+021BD
+DownLeftVectorBar; U+02956
+DownRightTeeVector; U+0295F
+DownRightVector; U+021C1
+DownRightVectorBar; U+02957
+DownTee; U+022A4
+DownTeeArrow; U+021A7
+drbkarow; U+02910
+drcorn; U+0231F
+drcrop; U+0230C
+Dscr; U+1D49F
+dscr; U+1D4B9
+DScy; U+00405
+dscy; U+00455
+dsol; U+029F6
+Dstrok; U+00110
+dstrok; U+00111
+dtdot; U+022F1
+dtri; U+025BF
+dtrif; U+025BE
+duarr; U+021F5
+duhar; U+0296F
+dwangle; U+029A6
+DZcy; U+0040F
+dzcy; U+0045F
+dzigrarr; U+027FF
+Eacute; U+000C9
+Eacute U+000C9
+eacute; U+000E9
+eacute U+000E9
+easter; U+02A6E
+Ecaron; U+0011A
+ecaron; U+0011B
+ecir; U+02256
+Ecirc; U+000CA
+Ecirc U+000CA
+ecirc; U+000EA
+ecirc U+000EA
+ecolon; U+02255
+Ecy; U+0042D
+ecy; U+0044D
+eDDot; U+02A77
+Edot; U+00116
+eDot; U+02251
+edot; U+00117
+ee; U+02147
+efDot; U+02252
+Efr; U+1D508
+efr; U+1D522
+eg; U+02A9A
+Egrave; U+000C8
+Egrave U+000C8
+egrave; U+000E8
+egrave U+000E8
+egs; U+02A96
+egsdot; U+02A98
+el; U+02A99
+Element; U+02208
+elinters; U+023E7
+ell; U+02113
+els; U+02A95
+elsdot; U+02A97
+Emacr; U+00112
+emacr; U+00113
+empty; U+02205
+emptyset; U+02205
+EmptySmallSquare; U+025FB
+emptyv; U+02205
+EmptyVerySmallSquare; U+025AB
+emsp; U+02003
+emsp13; U+02004
+emsp14; U+02005
+ENG; U+0014A
+eng; U+0014B
+ensp; U+02002
+Eogon; U+00118
+eogon; U+00119
+Eopf; U+1D53C
+eopf; U+1D556
+epar; U+022D5
+eparsl; U+029E3
+eplus; U+02A71
+epsi; U+003B5
+Epsilon; U+00395
+epsilon; U+003B5
+epsiv; U+003F5
+eqcirc; U+02256
+eqcolon; U+02255
+eqsim; U+02242
+eqslantgtr; U+02A96
+eqslantless; U+02A95
+Equal; U+02A75
+equals; U+0003D
+EqualTilde; U+02242
+equest; U+0225F
+Equilibrium; U+021CC
+equiv; U+02261
+equivDD; U+02A78
+eqvparsl; U+029E5
+erarr; U+02971
+erDot; U+02253
+Escr; U+02130
+escr; U+0212F
+esdot; U+02250
+Esim; U+02A73
+esim; U+02242
+Eta; U+00397
+eta; U+003B7
+ETH; U+000D0
+ETH U+000D0
+eth; U+000F0
+eth U+000F0
+Euml; U+000CB
+Euml U+000CB
+euml; U+000EB
+euml U+000EB
+euro; U+020AC
+excl; U+00021
+exist; U+02203
+Exists; U+02203
+expectation; U+02130
+ExponentialE; U+02147
+exponentiale; U+02147
+fallingdotseq; U+02252
+Fcy; U+00424
+fcy; U+00444
+female; U+02640
+ffilig; U+0FB03
+fflig; U+0FB00
+ffllig; U+0FB04
+Ffr; U+1D509
+ffr; U+1D523
+filig; U+0FB01
+FilledSmallSquare; U+025FC
+FilledVerySmallSquare; U+025AA
+fjlig; U+00066 U+0006A
+flat; U+0266D
+fllig; U+0FB02
+fltns; U+025B1
+fnof; U+00192
+Fopf; U+1D53D
+fopf; U+1D557
+ForAll; U+02200
+forall; U+02200
+fork; U+022D4
+forkv; U+02AD9
+Fouriertrf; U+02131
+fpartint; U+02A0D
+frac12; U+000BD
+frac12 U+000BD
+frac13; U+02153
+frac14; U+000BC
+frac14 U+000BC
+frac15; U+02155
+frac16; U+02159
+frac18; U+0215B
+frac23; U+02154
+frac25; U+02156
+frac34; U+000BE
+frac34 U+000BE
+frac35; U+02157
+frac38; U+0215C
+frac45; U+02158
+frac56; U+0215A
+frac58; U+0215D
+frac78; U+0215E
+frasl; U+02044
+frown; U+02322
+Fscr; U+02131
+fscr; U+1D4BB
+gacute; U+001F5
+Gamma; U+00393
+gamma; U+003B3
+Gammad; U+003DC
+gammad; U+003DD
+gap; U+02A86
+Gbreve; U+0011E
+gbreve; U+0011F
+Gcedil; U+00122
+Gcirc; U+0011C
+gcirc; U+0011D
+Gcy; U+00413
+gcy; U+00433
+Gdot; U+00120
+gdot; U+00121
+gE; U+02267
+ge; U+02265
+gEl; U+02A8C
+gel; U+022DB
+geq; U+02265
+geqq; U+02267
+geqslant; U+02A7E
+ges; U+02A7E
+gescc; U+02AA9
+gesdot; U+02A80
+gesdoto; U+02A82
+gesdotol; U+02A84
+gesl; U+022DB U+0FE00
+gesles; U+02A94
+Gfr; U+1D50A
+gfr; U+1D524
+Gg; U+022D9
+gg; U+0226B
+ggg; U+022D9
+gimel; U+02137
+GJcy; U+00403
+gjcy; U+00453
+gl; U+02277
+gla; U+02AA5
+glE; U+02A92
+glj; U+02AA4
+gnap; U+02A8A
+gnapprox; U+02A8A
+gnE; U+02269
+gne; U+02A88
+gneq; U+02A88
+gneqq; U+02269
+gnsim; U+022E7
+Gopf; U+1D53E
+gopf; U+1D558
+grave; U+00060
+GreaterEqual; U+02265
+GreaterEqualLess; U+022DB
+GreaterFullEqual; U+02267
+GreaterGreater; U+02AA2
+GreaterLess; U+02277
+GreaterSlantEqual; U+02A7E
+GreaterTilde; U+02273
+Gscr; U+1D4A2
+gscr; U+0210A
+gsim; U+02273
+gsime; U+02A8E
+gsiml; U+02A90
+GT; U+0003E
+GT U+0003E
+Gt; U+0226B
+gt; U+0003E
+gt U+0003E
+gtcc; U+02AA7
+gtcir; U+02A7A
+gtdot; U+022D7
+gtlPar; U+02995
+gtquest; U+02A7C
+gtrapprox; U+02A86
+gtrarr; U+02978
+gtrdot; U+022D7
+gtreqless; U+022DB
+gtreqqless; U+02A8C
+gtrless; U+02277
+gtrsim; U+02273
+gvertneqq; U+02269 U+0FE00
+gvnE; U+02269 U+0FE00
+Hacek; U+002C7
+hairsp; U+0200A
+half; U+000BD
+hamilt; U+0210B
+HARDcy; U+0042A
+hardcy; U+0044A
+hArr; U+021D4
+harr; U+02194
+harrcir; U+02948
+harrw; U+021AD
+Hat; U+0005E
+hbar; U+0210F
+Hcirc; U+00124
+hcirc; U+00125
+hearts; U+02665
+heartsuit; U+02665
+hellip; U+02026
+hercon; U+022B9
+Hfr; U+0210C
+hfr; U+1D525
+HilbertSpace; U+0210B
+hksearow; U+02925
+hkswarow; U+02926
+hoarr; U+021FF
+homtht; U+0223B
+hookleftarrow; U+021A9
+hookrightarrow; U+021AA
+Hopf; U+0210D
+hopf; U+1D559
+horbar; U+02015
+HorizontalLine; U+02500
+Hscr; U+0210B
+hscr; U+1D4BD
+hslash; U+0210F
+Hstrok; U+00126
+hstrok; U+00127
+HumpDownHump; U+0224E
+HumpEqual; U+0224F
+hybull; U+02043
+hyphen; U+02010
+Iacute; U+000CD
+Iacute U+000CD
+iacute; U+000ED
+iacute U+000ED
+ic; U+02063
+Icirc; U+000CE
+Icirc U+000CE
+icirc; U+000EE
+icirc U+000EE
+Icy; U+00418
+icy; U+00438
+Idot; U+00130
+IEcy; U+00415
+iecy; U+00435
+iexcl; U+000A1
+iexcl U+000A1
+iff; U+021D4
+Ifr; U+02111
+ifr; U+1D526
+Igrave; U+000CC
+Igrave U+000CC
+igrave; U+000EC
+igrave U+000EC
+ii; U+02148
+iiiint; U+02A0C
+iiint; U+0222D
+iinfin; U+029DC
+iiota; U+02129
+IJlig; U+00132
+ijlig; U+00133
+Im; U+02111
+Imacr; U+0012A
+imacr; U+0012B
+image; U+02111
+ImaginaryI; U+02148
+imagline; U+02110
+imagpart; U+02111
+imath; U+00131
+imof; U+022B7
+imped; U+001B5
+Implies; U+021D2
+in; U+02208
+incare; U+02105
+infin; U+0221E
+infintie; U+029DD
+inodot; U+00131
+Int; U+0222C
+int; U+0222B
+intcal; U+022BA
+integers; U+02124
+Integral; U+0222B
+intercal; U+022BA
+Intersection; U+022C2
+intlarhk; U+02A17
+intprod; U+02A3C
+InvisibleComma; U+02063
+InvisibleTimes; U+02062
+IOcy; U+00401
+iocy; U+00451
+Iogon; U+0012E
+iogon; U+0012F
+Iopf; U+1D540
+iopf; U+1D55A
+Iota; U+00399
+iota; U+003B9
+iprod; U+02A3C
+iquest; U+000BF
+iquest U+000BF
+Iscr; U+02110
+iscr; U+1D4BE
+isin; U+02208
+isindot; U+022F5
+isinE; U+022F9
+isins; U+022F4
+isinsv; U+022F3
+isinv; U+02208
+it; U+02062
+Itilde; U+00128
+itilde; U+00129
+Iukcy; U+00406
+iukcy; U+00456
+Iuml; U+000CF
+Iuml U+000CF
+iuml; U+000EF
+iuml U+000EF
+Jcirc; U+00134
+jcirc; U+00135
+Jcy; U+00419
+jcy; U+00439
+Jfr; U+1D50D
+jfr; U+1D527
+jmath; U+00237
+Jopf; U+1D541
+jopf; U+1D55B
+Jscr; U+1D4A5
+jscr; U+1D4BF
+Jsercy; U+00408
+jsercy; U+00458
+Jukcy; U+00404
+jukcy; U+00454
+Kappa; U+0039A
+kappa; U+003BA
+kappav; U+003F0
+Kcedil; U+00136
+kcedil; U+00137
+Kcy; U+0041A
+kcy; U+0043A
+Kfr; U+1D50E
+kfr; U+1D528
+kgreen; U+00138
+KHcy; U+00425
+khcy; U+00445
+KJcy; U+0040C
+kjcy; U+0045C
+Kopf; U+1D542
+kopf; U+1D55C
+Kscr; U+1D4A6
+kscr; U+1D4C0
+lAarr; U+021DA
+Lacute; U+00139
+lacute; U+0013A
+laemptyv; U+029B4
+lagran; U+02112
+Lambda; U+0039B
+lambda; U+003BB
+Lang; U+027EA
+lang; U+027E8
+langd; U+02991
+langle; U+027E8
+lap; U+02A85
+Laplacetrf; U+02112
+laquo; U+000AB
+laquo U+000AB
+Larr; U+0219E
+lArr; U+021D0
+larr; U+02190
+larrb; U+021E4
+larrbfs; U+0291F
+larrfs; U+0291D
+larrhk; U+021A9
+larrlp; U+021AB
+larrpl; U+02939
+larrsim; U+02973
+larrtl; U+021A2
+lat; U+02AAB
+lAtail; U+0291B
+latail; U+02919
+late; U+02AAD
+lates; U+02AAD U+0FE00
+lBarr; U+0290E
+lbarr; U+0290C
+lbbrk; U+02772
+lbrace; U+0007B
+lbrack; U+0005B
+lbrke; U+0298B
+lbrksld; U+0298F
+lbrkslu; U+0298D
+Lcaron; U+0013D
+lcaron; U+0013E
+Lcedil; U+0013B
+lcedil; U+0013C
+lceil; U+02308
+lcub; U+0007B
+Lcy; U+0041B
+lcy; U+0043B
+ldca; U+02936
+ldquo; U+0201C
+ldquor; U+0201E
+ldrdhar; U+02967
+ldrushar; U+0294B
+ldsh; U+021B2
+lE; U+02266
+le; U+02264
+LeftAngleBracket; U+027E8
+LeftArrow; U+02190
+Leftarrow; U+021D0
+leftarrow; U+02190
+LeftArrowBar; U+021E4
+LeftArrowRightArrow; U+021C6
+leftarrowtail; U+021A2
+LeftCeiling; U+02308
+LeftDoubleBracket; U+027E6
+LeftDownTeeVector; U+02961
+LeftDownVector; U+021C3
+LeftDownVectorBar; U+02959
+LeftFloor; U+0230A
+leftharpoondown; U+021BD
+leftharpoonup; U+021BC
+leftleftarrows; U+021C7
+LeftRightArrow; U+02194
+Leftrightarrow; U+021D4
+leftrightarrow; U+02194
+leftrightarrows; U+021C6
+leftrightharpoons; U+021CB
+leftrightsquigarrow; U+021AD
+LeftRightVector; U+0294E
+LeftTee; U+022A3
+LeftTeeArrow; U+021A4
+LeftTeeVector; U+0295A
+leftthreetimes; U+022CB
+LeftTriangle; U+022B2
+LeftTriangleBar; U+029CF
+LeftTriangleEqual; U+022B4
+LeftUpDownVector; U+02951
+LeftUpTeeVector; U+02960
+LeftUpVector; U+021BF
+LeftUpVectorBar; U+02958
+LeftVector; U+021BC
+LeftVectorBar; U+02952
+lEg; U+02A8B
+leg; U+022DA
+leq; U+02264
+leqq; U+02266
+leqslant; U+02A7D
+les; U+02A7D
+lescc; U+02AA8
+lesdot; U+02A7F
+lesdoto; U+02A81
+lesdotor; U+02A83
+lesg; U+022DA U+0FE00
+lesges; U+02A93
+lessapprox; U+02A85
+lessdot; U+022D6
+lesseqgtr; U+022DA
+lesseqqgtr; U+02A8B
+LessEqualGreater; U+022DA
+LessFullEqual; U+02266
+LessGreater; U+02276
+lessgtr; U+02276
+LessLess; U+02AA1
+lesssim; U+02272
+LessSlantEqual; U+02A7D
+LessTilde; U+02272
+lfisht; U+0297C
+lfloor; U+0230A
+Lfr; U+1D50F
+lfr; U+1D529
+lg; U+02276
+lgE; U+02A91
+lHar; U+02962
+lhard; U+021BD
+lharu; U+021BC
+lharul; U+0296A
+lhblk; U+02584
+LJcy; U+00409
+ljcy; U+00459
+Ll; U+022D8
+ll; U+0226A
+llarr; U+021C7
+llcorner; U+0231E
+Lleftarrow; U+021DA
+llhard; U+0296B
+lltri; U+025FA
+Lmidot; U+0013F
+lmidot; U+00140
+lmoust; U+023B0
+lmoustache; U+023B0
+lnap; U+02A89
+lnapprox; U+02A89
+lnE; U+02268
+lne; U+02A87
+lneq; U+02A87
+lneqq; U+02268
+lnsim; U+022E6
+loang; U+027EC
+loarr; U+021FD
+lobrk; U+027E6
+LongLeftArrow; U+027F5
+Longleftarrow; U+027F8
+longleftarrow; U+027F5
+LongLeftRightArrow; U+027F7
+Longleftrightarrow; U+027FA
+longleftrightarrow; U+027F7
+longmapsto; U+027FC
+LongRightArrow; U+027F6
+Longrightarrow; U+027F9
+longrightarrow; U+027F6
+looparrowleft; U+021AB
+looparrowright; U+021AC
+lopar; U+02985
+Lopf; U+1D543
+lopf; U+1D55D
+loplus; U+02A2D
+lotimes; U+02A34
+lowast; U+02217
+lowbar; U+0005F
+LowerLeftArrow; U+02199
+LowerRightArrow; U+02198
+loz; U+025CA
+lozenge; U+025CA
+lozf; U+029EB
+lpar; U+00028
+lparlt; U+02993
+lrarr; U+021C6
+lrcorner; U+0231F
+lrhar; U+021CB
+lrhard; U+0296D
+lrm; U+0200E
+lrtri; U+022BF
+lsaquo; U+02039
+Lscr; U+02112
+lscr; U+1D4C1
+Lsh; U+021B0
+lsh; U+021B0
+lsim; U+02272
+lsime; U+02A8D
+lsimg; U+02A8F
+lsqb; U+0005B
+lsquo; U+02018
+lsquor; U+0201A
+Lstrok; U+00141
+lstrok; U+00142
+LT; U+0003C
+LT U+0003C
+Lt; U+0226A
+lt; U+0003C
+lt U+0003C
+ltcc; U+02AA6
+ltcir; U+02A79
+ltdot; U+022D6
+lthree; U+022CB
+ltimes; U+022C9
+ltlarr; U+02976
+ltquest; U+02A7B
+ltri; U+025C3
+ltrie; U+022B4
+ltrif; U+025C2
+ltrPar; U+02996
+lurdshar; U+0294A
+luruhar; U+02966
+lvertneqq; U+02268 U+0FE00
+lvnE; U+02268 U+0FE00
+macr; U+000AF
+macr U+000AF
+male; U+02642
+malt; U+02720
+maltese; U+02720
+Map; U+02905
+map; U+021A6
+mapsto; U+021A6
+mapstodown; U+021A7
+mapstoleft; U+021A4
+mapstoup; U+021A5
+marker; U+025AE
+mcomma; U+02A29
+Mcy; U+0041C
+mcy; U+0043C
+mdash; U+02014
+mDDot; U+0223A
+measuredangle; U+02221
+MediumSpace; U+0205F
+Mellintrf; U+02133
+Mfr; U+1D510
+mfr; U+1D52A
+mho; U+02127
+micro; U+000B5
+micro U+000B5
+mid; U+02223
+midast; U+0002A
+midcir; U+02AF0
+middot; U+000B7
+middot U+000B7
+minus; U+02212
+minusb; U+0229F
+minusd; U+02238
+minusdu; U+02A2A
+MinusPlus; U+02213
+mlcp; U+02ADB
+mldr; U+02026
+mnplus; U+02213
+models; U+022A7
+Mopf; U+1D544
+mopf; U+1D55E
+mp; U+02213
+Mscr; U+02133
+mscr; U+1D4C2
+mstpos; U+0223E
+Mu; U+0039C
+mu; U+003BC
+multimap; U+022B8
+mumap; U+022B8
+nabla; U+02207
+Nacute; U+00143
+nacute; U+00144
+nang; U+02220 U+020D2
+nap; U+02249
+napE; U+02A70 U+00338
+napid; U+0224B U+00338
+napos; U+00149
+napprox; U+02249
+natur; U+0266E
+natural; U+0266E
+naturals; U+02115
+nbsp; U+000A0
+nbsp U+000A0
+nbump; U+0224E U+00338
+nbumpe; U+0224F U+00338
+ncap; U+02A43
+Ncaron; U+00147
+ncaron; U+00148
+Ncedil; U+00145
+ncedil; U+00146
+ncong; U+02247
+ncongdot; U+02A6D U+00338
+ncup; U+02A42
+Ncy; U+0041D
+ncy; U+0043D
+ndash; U+02013
+ne; U+02260
+nearhk; U+02924
+neArr; U+021D7
+nearr; U+02197
+nearrow; U+02197
+nedot; U+02250 U+00338
+NegativeMediumSpace; U+0200B
+NegativeThickSpace; U+0200B
+NegativeThinSpace; U+0200B
+NegativeVeryThinSpace; U+0200B
+nequiv; U+02262
+nesear; U+02928
+nesim; U+02242 U+00338
+NestedGreaterGreater; U+0226B
+NestedLessLess; U+0226A
+NewLine; U+0000A
+nexist; U+02204
+nexists; U+02204
+Nfr; U+1D511
+nfr; U+1D52B
+ngE; U+02267 U+00338
+nge; U+02271
+ngeq; U+02271
+ngeqq; U+02267 U+00338
+ngeqslant; U+02A7E U+00338
+nges; U+02A7E U+00338
+nGg; U+022D9 U+00338
+ngsim; U+02275
+nGt; U+0226B U+020D2
+ngt; U+0226F
+ngtr; U+0226F
+nGtv; U+0226B U+00338
+nhArr; U+021CE
+nharr; U+021AE
+nhpar; U+02AF2
+ni; U+0220B
+nis; U+022FC
+nisd; U+022FA
+niv; U+0220B
+NJcy; U+0040A
+njcy; U+0045A
+nlArr; U+021CD
+nlarr; U+0219A
+nldr; U+02025
+nlE; U+02266 U+00338
+nle; U+02270
+nLeftarrow; U+021CD
+nleftarrow; U+0219A
+nLeftrightarrow; U+021CE
+nleftrightarrow; U+021AE
+nleq; U+02270
+nleqq; U+02266 U+00338
+nleqslant; U+02A7D U+00338
+nles; U+02A7D U+00338
+nless; U+0226E
+nLl; U+022D8 U+00338
+nlsim; U+02274
+nLt; U+0226A U+020D2
+nlt; U+0226E
+nltri; U+022EA
+nltrie; U+022EC
+nLtv; U+0226A U+00338
+nmid; U+02224
+NoBreak; U+02060
+NonBreakingSpace; U+000A0
+Nopf; U+02115
+nopf; U+1D55F
+Not; U+02AEC
+not; U+000AC
+not U+000AC
+NotCongruent; U+02262
+NotCupCap; U+0226D
+NotDoubleVerticalBar; U+02226
+NotElement; U+02209
+NotEqual; U+02260
+NotEqualTilde; U+02242 U+00338
+NotExists; U+02204
+NotGreater; U+0226F
+NotGreaterEqual; U+02271
+NotGreaterFullEqual; U+02267 U+00338
+NotGreaterGreater; U+0226B U+00338
+NotGreaterLess; U+02279
+NotGreaterSlantEqual; U+02A7E U+00338
+NotGreaterTilde; U+02275
+NotHumpDownHump; U+0224E U+00338
+NotHumpEqual; U+0224F U+00338
+notin; U+02209
+notindot; U+022F5 U+00338
+notinE; U+022F9 U+00338
+notinva; U+02209
+notinvb; U+022F7
+notinvc; U+022F6
+NotLeftTriangle; U+022EA
+NotLeftTriangleBar; U+029CF U+00338
+NotLeftTriangleEqual; U+022EC
+NotLess; U+0226E
+NotLessEqual; U+02270
+NotLessGreater; U+02278
+NotLessLess; U+0226A U+00338
+NotLessSlantEqual; U+02A7D U+00338
+NotLessTilde; U+02274
+NotNestedGreaterGreater; U+02AA2 U+00338
+NotNestedLessLess; U+02AA1 U+00338
+notni; U+0220C
+notniva; U+0220C
+notnivb; U+022FE
+notnivc; U+022FD
+NotPrecedes; U+02280
+NotPrecedesEqual; U+02AAF U+00338
+NotPrecedesSlantEqual; U+022E0
+NotReverseElement; U+0220C
+NotRightTriangle; U+022EB
+NotRightTriangleBar; U+029D0 U+00338
+NotRightTriangleEqual; U+022ED
+NotSquareSubset; U+0228F U+00338
+NotSquareSubsetEqual; U+022E2
+NotSquareSuperset; U+02290 U+00338
+NotSquareSupersetEqual; U+022E3
+NotSubset; U+02282 U+020D2
+NotSubsetEqual; U+02288
+NotSucceeds; U+02281
+NotSucceedsEqual; U+02AB0 U+00338
+NotSucceedsSlantEqual; U+022E1
+NotSucceedsTilde; U+0227F U+00338
+NotSuperset; U+02283 U+020D2
+NotSupersetEqual; U+02289
+NotTilde; U+02241
+NotTildeEqual; U+02244
+NotTildeFullEqual; U+02247
+NotTildeTilde; U+02249
+NotVerticalBar; U+02224
+npar; U+02226
+nparallel; U+02226
+nparsl; U+02AFD U+020E5
+npart; U+02202 U+00338
+npolint; U+02A14
+npr; U+02280
+nprcue; U+022E0
+npre; U+02AAF U+00338
+nprec; U+02280
+npreceq; U+02AAF U+00338
+nrArr; U+021CF
+nrarr; U+0219B
+nrarrc; U+02933 U+00338
+nrarrw; U+0219D U+00338
+nRightarrow; U+021CF
+nrightarrow; U+0219B
+nrtri; U+022EB
+nrtrie; U+022ED
+nsc; U+02281
+nsccue; U+022E1
+nsce; U+02AB0 U+00338
+Nscr; U+1D4A9
+nscr; U+1D4C3
+nshortmid; U+02224
+nshortparallel; U+02226
+nsim; U+02241
+nsime; U+02244
+nsimeq; U+02244
+nsmid; U+02224
+nspar; U+02226
+nsqsube; U+022E2
+nsqsupe; U+022E3
+nsub; U+02284
+nsubE; U+02AC5 U+00338
+nsube; U+02288
+nsubset; U+02282 U+020D2
+nsubseteq; U+02288
+nsubseteqq; U+02AC5 U+00338
+nsucc; U+02281
+nsucceq; U+02AB0 U+00338
+nsup; U+02285
+nsupE; U+02AC6 U+00338
+nsupe; U+02289
+nsupset; U+02283 U+020D2
+nsupseteq; U+02289
+nsupseteqq; U+02AC6 U+00338
+ntgl; U+02279
+Ntilde; U+000D1
+Ntilde U+000D1
+ntilde; U+000F1
+ntilde U+000F1
+ntlg; U+02278
+ntriangleleft; U+022EA
+ntrianglelefteq; U+022EC
+ntriangleright; U+022EB
+ntrianglerighteq; U+022ED
+Nu; U+0039D
+nu; U+003BD
+num; U+00023
+numero; U+02116
+numsp; U+02007
+nvap; U+0224D U+020D2
+nVDash; U+022AF
+nVdash; U+022AE
+nvDash; U+022AD
+nvdash; U+022AC
+nvge; U+02265 U+020D2
+nvgt; U+0003E U+020D2
+nvHarr; U+02904
+nvinfin; U+029DE
+nvlArr; U+02902
+nvle; U+02264 U+020D2
+nvlt; U+0003C U+020D2
+nvltrie; U+022B4 U+020D2
+nvrArr; U+02903
+nvrtrie; U+022B5 U+020D2
+nvsim; U+0223C U+020D2
+nwarhk; U+02923
+nwArr; U+021D6
+nwarr; U+02196
+nwarrow; U+02196
+nwnear; U+02927
+Oacute; U+000D3
+Oacute U+000D3
+oacute; U+000F3
+oacute U+000F3
+oast; U+0229B
+ocir; U+0229A
+Ocirc; U+000D4
+Ocirc U+000D4
+ocirc; U+000F4
+ocirc U+000F4
+Ocy; U+0041E
+ocy; U+0043E
+odash; U+0229D
+Odblac; U+00150
+odblac; U+00151
+odiv; U+02A38
+odot; U+02299
+odsold; U+029BC
+OElig; U+00152
+oelig; U+00153
+ofcir; U+029BF
+Ofr; U+1D512
+ofr; U+1D52C
+ogon; U+002DB
+Ograve; U+000D2
+Ograve U+000D2
+ograve; U+000F2
+ograve U+000F2
+ogt; U+029C1
+ohbar; U+029B5
+ohm; U+003A9
+oint; U+0222E
+olarr; U+021BA
+olcir; U+029BE
+olcross; U+029BB
+oline; U+0203E
+olt; U+029C0
+Omacr; U+0014C
+omacr; U+0014D
+Omega; U+003A9
+omega; U+003C9
+Omicron; U+0039F
+omicron; U+003BF
+omid; U+029B6
+ominus; U+02296
+Oopf; U+1D546
+oopf; U+1D560
+opar; U+029B7
+OpenCurlyDoubleQuote; U+0201C
+OpenCurlyQuote; U+02018
+operp; U+029B9
+oplus; U+02295
+Or; U+02A54
+or; U+02228
+orarr; U+021BB
+ord; U+02A5D
+order; U+02134
+orderof; U+02134
+ordf; U+000AA
+ordf U+000AA
+ordm; U+000BA
+ordm U+000BA
+origof; U+022B6
+oror; U+02A56
+orslope; U+02A57
+orv; U+02A5B
+oS; U+024C8
+Oscr; U+1D4AA
+oscr; U+02134
+Oslash; U+000D8
+Oslash U+000D8
+oslash; U+000F8
+oslash U+000F8
+osol; U+02298
+Otilde; U+000D5
+Otilde U+000D5
+otilde; U+000F5
+otilde U+000F5
+Otimes; U+02A37
+otimes; U+02297
+otimesas; U+02A36
+Ouml; U+000D6
+Ouml U+000D6
+ouml; U+000F6
+ouml U+000F6
+ovbar; U+0233D
+OverBar; U+0203E
+OverBrace; U+023DE
+OverBracket; U+023B4
+OverParenthesis; U+023DC
+par; U+02225
+para; U+000B6
+para U+000B6
+parallel; U+02225
+parsim; U+02AF3
+parsl; U+02AFD
+part; U+02202
+PartialD; U+02202
+Pcy; U+0041F
+pcy; U+0043F
+percnt; U+00025
+period; U+0002E
+permil; U+02030
+perp; U+022A5
+pertenk; U+02031
+Pfr; U+1D513
+pfr; U+1D52D
+Phi; U+003A6
+phi; U+003C6
+phiv; U+003D5
+phmmat; U+02133
+phone; U+0260E
+Pi; U+003A0
+pi; U+003C0
+pitchfork; U+022D4
+piv; U+003D6
+planck; U+0210F
+planckh; U+0210E
+plankv; U+0210F
+plus; U+0002B
+plusacir; U+02A23
+plusb; U+0229E
+pluscir; U+02A22
+plusdo; U+02214
+plusdu; U+02A25
+pluse; U+02A72
+PlusMinus; U+000B1
+plusmn; U+000B1
+plusmn U+000B1
+plussim; U+02A26
+plustwo; U+02A27
+pm; U+000B1
+Poincareplane; U+0210C
+pointint; U+02A15
+Popf; U+02119
+popf; U+1D561
+pound; U+000A3
+pound U+000A3
+Pr; U+02ABB
+pr; U+0227A
+prap; U+02AB7
+prcue; U+0227C
+prE; U+02AB3
+pre; U+02AAF
+prec; U+0227A
+precapprox; U+02AB7
+preccurlyeq; U+0227C
+Precedes; U+0227A
+PrecedesEqual; U+02AAF
+PrecedesSlantEqual; U+0227C
+PrecedesTilde; U+0227E
+preceq; U+02AAF
+precnapprox; U+02AB9
+precneqq; U+02AB5
+precnsim; U+022E8
+precsim; U+0227E
+Prime; U+02033
+prime; U+02032
+primes; U+02119
+prnap; U+02AB9
+prnE; U+02AB5
+prnsim; U+022E8
+prod; U+0220F
+Product; U+0220F
+profalar; U+0232E
+profline; U+02312
+profsurf; U+02313
+prop; U+0221D
+Proportion; U+02237
+Proportional; U+0221D
+propto; U+0221D
+prsim; U+0227E
+prurel; U+022B0
+Pscr; U+1D4AB
+pscr; U+1D4C5
+Psi; U+003A8
+psi; U+003C8
+puncsp; U+02008
+Qfr; U+1D514
+qfr; U+1D52E
+qint; U+02A0C
+Qopf; U+0211A
+qopf; U+1D562
+qprime; U+02057
+Qscr; U+1D4AC
+qscr; U+1D4C6
+quaternions; U+0210D
+quatint; U+02A16
+quest; U+0003F
+questeq; U+0225F
+QUOT; U+00022
+QUOT U+00022
+quot; U+00022
+quot U+00022
+rAarr; U+021DB
+race; U+0223D U+00331
+Racute; U+00154
+racute; U+00155
+radic; U+0221A
+raemptyv; U+029B3
+Rang; U+027EB
+rang; U+027E9
+rangd; U+02992
+range; U+029A5
+rangle; U+027E9
+raquo; U+000BB
+raquo U+000BB
+Rarr; U+021A0
+rArr; U+021D2
+rarr; U+02192
+rarrap; U+02975
+rarrb; U+021E5
+rarrbfs; U+02920
+rarrc; U+02933
+rarrfs; U+0291E
+rarrhk; U+021AA
+rarrlp; U+021AC
+rarrpl; U+02945
+rarrsim; U+02974
+Rarrtl; U+02916
+rarrtl; U+021A3
+rarrw; U+0219D
+rAtail; U+0291C
+ratail; U+0291A
+ratio; U+02236
+rationals; U+0211A
+RBarr; U+02910
+rBarr; U+0290F
+rbarr; U+0290D
+rbbrk; U+02773
+rbrace; U+0007D
+rbrack; U+0005D
+rbrke; U+0298C
+rbrksld; U+0298E
+rbrkslu; U+02990
+Rcaron; U+00158
+rcaron; U+00159
+Rcedil; U+00156
+rcedil; U+00157
+rceil; U+02309
+rcub; U+0007D
+Rcy; U+00420
+rcy; U+00440
+rdca; U+02937
+rdldhar; U+02969
+rdquo; U+0201D
+rdquor; U+0201D
+rdsh; U+021B3
+Re; U+0211C
+real; U+0211C
+realine; U+0211B
+realpart; U+0211C
+reals; U+0211D
+rect; U+025AD
+REG; U+000AE
+REG U+000AE
+reg; U+000AE
+reg U+000AE
+ReverseElement; U+0220B
+ReverseEquilibrium; U+021CB
+ReverseUpEquilibrium; U+0296F
+rfisht; U+0297D
+rfloor; U+0230B
+Rfr; U+0211C
+rfr; U+1D52F
+rHar; U+02964
+rhard; U+021C1
+rharu; U+021C0
+rharul; U+0296C
+Rho; U+003A1
+rho; U+003C1
+rhov; U+003F1
+RightAngleBracket; U+027E9
+RightArrow; U+02192
+Rightarrow; U+021D2
+rightarrow; U+02192
+RightArrowBar; U+021E5
+RightArrowLeftArrow; U+021C4
+rightarrowtail; U+021A3
+RightCeiling; U+02309
+RightDoubleBracket; U+027E7
+RightDownTeeVector; U+0295D
+RightDownVector; U+021C2
+RightDownVectorBar; U+02955
+RightFloor; U+0230B
+rightharpoondown; U+021C1
+rightharpoonup; U+021C0
+rightleftarrows; U+021C4
+rightleftharpoons; U+021CC
+rightrightarrows; U+021C9
+rightsquigarrow; U+0219D
+RightTee; U+022A2
+RightTeeArrow; U+021A6
+RightTeeVector; U+0295B
+rightthreetimes; U+022CC
+RightTriangle; U+022B3
+RightTriangleBar; U+029D0
+RightTriangleEqual; U+022B5
+RightUpDownVector; U+0294F
+RightUpTeeVector; U+0295C
+RightUpVector; U+021BE
+RightUpVectorBar; U+02954
+RightVector; U+021C0
+RightVectorBar; U+02953
+ring; U+002DA
+risingdotseq; U+02253
+rlarr; U+021C4
+rlhar; U+021CC
+rlm; U+0200F
+rmoust; U+023B1
+rmoustache; U+023B1
+rnmid; U+02AEE
+roang; U+027ED
+roarr; U+021FE
+robrk; U+027E7
+ropar; U+02986
+Ropf; U+0211D
+ropf; U+1D563
+roplus; U+02A2E
+rotimes; U+02A35
+RoundImplies; U+02970
+rpar; U+00029
+rpargt; U+02994
+rppolint; U+02A12
+rrarr; U+021C9
+Rrightarrow; U+021DB
+rsaquo; U+0203A
+Rscr; U+0211B
+rscr; U+1D4C7
+Rsh; U+021B1
+rsh; U+021B1
+rsqb; U+0005D
+rsquo; U+02019
+rsquor; U+02019
+rthree; U+022CC
+rtimes; U+022CA
+rtri; U+025B9
+rtrie; U+022B5
+rtrif; U+025B8
+rtriltri; U+029CE
+RuleDelayed; U+029F4
+ruluhar; U+02968
+rx; U+0211E
+Sacute; U+0015A
+sacute; U+0015B
+sbquo; U+0201A
+Sc; U+02ABC
+sc; U+0227B
+scap; U+02AB8
+Scaron; U+00160
+scaron; U+00161
+sccue; U+0227D
+scE; U+02AB4
+sce; U+02AB0
+Scedil; U+0015E
+scedil; U+0015F
+Scirc; U+0015C
+scirc; U+0015D
+scnap; U+02ABA
+scnE; U+02AB6
+scnsim; U+022E9
+scpolint; U+02A13
+scsim; U+0227F
+Scy; U+00421
+scy; U+00441
+sdot; U+022C5
+sdotb; U+022A1
+sdote; U+02A66
+searhk; U+02925
+seArr; U+021D8
+searr; U+02198
+searrow; U+02198
+sect; U+000A7
+sect U+000A7
+semi; U+0003B
+seswar; U+02929
+setminus; U+02216
+setmn; U+02216
+sext; U+02736
+Sfr; U+1D516
+sfr; U+1D530
+sfrown; U+02322
+sharp; U+0266F
+SHCHcy; U+00429
+shchcy; U+00449
+SHcy; U+00428
+shcy; U+00448
+ShortDownArrow; U+02193
+ShortLeftArrow; U+02190
+shortmid; U+02223
+shortparallel; U+02225
+ShortRightArrow; U+02192
+ShortUpArrow; U+02191
+shy; U+000AD
+shy U+000AD
+Sigma; U+003A3
+sigma; U+003C3
+sigmaf; U+003C2
+sigmav; U+003C2
+sim; U+0223C
+simdot; U+02A6A
+sime; U+02243
+simeq; U+02243
+simg; U+02A9E
+simgE; U+02AA0
+siml; U+02A9D
+simlE; U+02A9F
+simne; U+02246
+simplus; U+02A24
+simrarr; U+02972
+slarr; U+02190
+SmallCircle; U+02218
+smallsetminus; U+02216
+smashp; U+02A33
+smeparsl; U+029E4
+smid; U+02223
+smile; U+02323
+smt; U+02AAA
+smte; U+02AAC
+smtes; U+02AAC U+0FE00
+SOFTcy; U+0042C
+softcy; U+0044C
+sol; U+0002F
+solb; U+029C4
+solbar; U+0233F
+Sopf; U+1D54A
+sopf; U+1D564
+spades; U+02660
+spadesuit; U+02660
+spar; U+02225
+sqcap; U+02293
+sqcaps; U+02293 U+0FE00
+sqcup; U+02294
+sqcups; U+02294 U+0FE00
+Sqrt; U+0221A
+sqsub; U+0228F
+sqsube; U+02291
+sqsubset; U+0228F
+sqsubseteq; U+02291
+sqsup; U+02290
+sqsupe; U+02292
+sqsupset; U+02290
+sqsupseteq; U+02292
+squ; U+025A1
+Square; U+025A1
+square; U+025A1
+SquareIntersection; U+02293
+SquareSubset; U+0228F
+SquareSubsetEqual; U+02291
+SquareSuperset; U+02290
+SquareSupersetEqual; U+02292
+SquareUnion; U+02294
+squarf; U+025AA
+squf; U+025AA
+srarr; U+02192
+Sscr; U+1D4AE
+sscr; U+1D4C8
+ssetmn; U+02216
+ssmile; U+02323
+sstarf; U+022C6
+Star; U+022C6
+star; U+02606
+starf; U+02605
+straightepsilon; U+003F5
+straightphi; U+003D5
+strns; U+000AF
+Sub; U+022D0
+sub; U+02282
+subdot; U+02ABD
+subE; U+02AC5
+sube; U+02286
+subedot; U+02AC3
+submult; U+02AC1
+subnE; U+02ACB
+subne; U+0228A
+subplus; U+02ABF
+subrarr; U+02979
+Subset; U+022D0
+subset; U+02282
+subseteq; U+02286
+subseteqq; U+02AC5
+SubsetEqual; U+02286
+subsetneq; U+0228A
+subsetneqq; U+02ACB
+subsim; U+02AC7
+subsub; U+02AD5
+subsup; U+02AD3
+succ; U+0227B
+succapprox; U+02AB8
+succcurlyeq; U+0227D
+Succeeds; U+0227B
+SucceedsEqual; U+02AB0
+SucceedsSlantEqual; U+0227D
+SucceedsTilde; U+0227F
+succeq; U+02AB0
+succnapprox; U+02ABA
+succneqq; U+02AB6
+succnsim; U+022E9
+succsim; U+0227F
+SuchThat; U+0220B
+Sum; U+02211
+sum; U+02211
+sung; U+0266A
+Sup; U+022D1
+sup; U+02283
+sup1; U+000B9
+sup1 U+000B9
+sup2; U+000B2
+sup2 U+000B2
+sup3; U+000B3
+sup3 U+000B3
+supdot; U+02ABE
+supdsub; U+02AD8
+supE; U+02AC6
+supe; U+02287
+supedot; U+02AC4
+Superset; U+02283
+SupersetEqual; U+02287
+suphsol; U+027C9
+suphsub; U+02AD7
+suplarr; U+0297B
+supmult; U+02AC2
+supnE; U+02ACC
+supne; U+0228B
+supplus; U+02AC0
+Supset; U+022D1
+supset; U+02283
+supseteq; U+02287
+supseteqq; U+02AC6
+supsetneq; U+0228B
+supsetneqq; U+02ACC
+supsim; U+02AC8
+supsub; U+02AD4
+supsup; U+02AD6
+swarhk; U+02926
+swArr; U+021D9
+swarr; U+02199
+swarrow; U+02199
+swnwar; U+0292A
+szlig; U+000DF
+szlig U+000DF
+Tab; U+00009
+target; U+02316
+Tau; U+003A4
+tau; U+003C4
+tbrk; U+023B4
+Tcaron; U+00164
+tcaron; U+00165
+Tcedil; U+00162
+tcedil; U+00163
+Tcy; U+00422
+tcy; U+00442
+tdot; U+020DB
+telrec; U+02315
+Tfr; U+1D517
+tfr; U+1D531
+there4; U+02234
+Therefore; U+02234
+therefore; U+02234
+Theta; U+00398
+theta; U+003B8
+thetasym; U+003D1
+thetav; U+003D1
+thickapprox; U+02248
+thicksim; U+0223C
+ThickSpace; U+0205F U+0200A
+thinsp; U+02009
+ThinSpace; U+02009
+thkap; U+02248
+thksim; U+0223C
+THORN; U+000DE
+THORN U+000DE
+thorn; U+000FE
+thorn U+000FE
+Tilde; U+0223C
+tilde; U+002DC
+TildeEqual; U+02243
+TildeFullEqual; U+02245
+TildeTilde; U+02248
+times; U+000D7
+times U+000D7
+timesb; U+022A0
+timesbar; U+02A31
+timesd; U+02A30
+tint; U+0222D
+toea; U+02928
+top; U+022A4
+topbot; U+02336
+topcir; U+02AF1
+Topf; U+1D54B
+topf; U+1D565
+topfork; U+02ADA
+tosa; U+02929
+tprime; U+02034
+TRADE; U+02122
+trade; U+02122
+triangle; U+025B5
+triangledown; U+025BF
+triangleleft; U+025C3
+trianglelefteq; U+022B4
+triangleq; U+0225C
+triangleright; U+025B9
+trianglerighteq; U+022B5
+tridot; U+025EC
+trie; U+0225C
+triminus; U+02A3A
+TripleDot; U+020DB
+triplus; U+02A39
+trisb; U+029CD
+tritime; U+02A3B
+trpezium; U+023E2
+Tscr; U+1D4AF
+tscr; U+1D4C9
+TScy; U+00426
+tscy; U+00446
+TSHcy; U+0040B
+tshcy; U+0045B
+Tstrok; U+00166
+tstrok; U+00167
+twixt; U+0226C
+twoheadleftarrow; U+0219E
+twoheadrightarrow; U+021A0
+Uacute; U+000DA
+Uacute U+000DA
+uacute; U+000FA
+uacute U+000FA
+Uarr; U+0219F
+uArr; U+021D1
+uarr; U+02191
+Uarrocir; U+02949
+Ubrcy; U+0040E
+ubrcy; U+0045E
+Ubreve; U+0016C
+ubreve; U+0016D
+Ucirc; U+000DB
+Ucirc U+000DB
+ucirc; U+000FB
+ucirc U+000FB
+Ucy; U+00423
+ucy; U+00443
+udarr; U+021C5
+Udblac; U+00170
+udblac; U+00171
+udhar; U+0296E
+ufisht; U+0297E
+Ufr; U+1D518
+ufr; U+1D532
+Ugrave; U+000D9
+Ugrave U+000D9
+ugrave; U+000F9
+ugrave U+000F9
+uHar; U+02963
+uharl; U+021BF
+uharr; U+021BE
+uhblk; U+02580
+ulcorn; U+0231C
+ulcorner; U+0231C
+ulcrop; U+0230F
+ultri; U+025F8
+Umacr; U+0016A
+umacr; U+0016B
+uml; U+000A8
+uml U+000A8
+UnderBar; U+0005F
+UnderBrace; U+023DF
+UnderBracket; U+023B5
+UnderParenthesis; U+023DD
+Union; U+022C3
+UnionPlus; U+0228E
+Uogon; U+00172
+uogon; U+00173
+Uopf; U+1D54C
+uopf; U+1D566
+UpArrow; U+02191
+Uparrow; U+021D1
+uparrow; U+02191
+UpArrowBar; U+02912
+UpArrowDownArrow; U+021C5
+UpDownArrow; U+02195
+Updownarrow; U+021D5
+updownarrow; U+02195
+UpEquilibrium; U+0296E
+upharpoonleft; U+021BF
+upharpoonright; U+021BE
+uplus; U+0228E
+UpperLeftArrow; U+02196
+UpperRightArrow; U+02197
+Upsi; U+003D2
+upsi; U+003C5
+upsih; U+003D2
+Upsilon; U+003A5
+upsilon; U+003C5
+UpTee; U+022A5
+UpTeeArrow; U+021A5
+upuparrows; U+021C8
+urcorn; U+0231D
+urcorner; U+0231D
+urcrop; U+0230E
+Uring; U+0016E
+uring; U+0016F
+urtri; U+025F9
+Uscr; U+1D4B0
+uscr; U+1D4CA
+utdot; U+022F0
+Utilde; U+00168
+utilde; U+00169
+utri; U+025B5
+utrif; U+025B4
+uuarr; U+021C8
+Uuml; U+000DC
+Uuml U+000DC
+uuml; U+000FC
+uuml U+000FC
+uwangle; U+029A7
+vangrt; U+0299C
+varepsilon; U+003F5
+varkappa; U+003F0
+varnothing; U+02205
+varphi; U+003D5
+varpi; U+003D6
+varpropto; U+0221D
+vArr; U+021D5
+varr; U+02195
+varrho; U+003F1
+varsigma; U+003C2
+varsubsetneq; U+0228A U+0FE00
+varsubsetneqq; U+02ACB U+0FE00
+varsupsetneq; U+0228B U+0FE00
+varsupsetneqq; U+02ACC U+0FE00
+vartheta; U+003D1
+vartriangleleft; U+022B2
+vartriangleright; U+022B3
+Vbar; U+02AEB
+vBar; U+02AE8
+vBarv; U+02AE9
+Vcy; U+00412
+vcy; U+00432
+VDash; U+022AB
+Vdash; U+022A9
+vDash; U+022A8
+vdash; U+022A2
+Vdashl; U+02AE6
+Vee; U+022C1
+vee; U+02228
+veebar; U+022BB
+veeeq; U+0225A
+vellip; U+022EE
+Verbar; U+02016
+verbar; U+0007C
+Vert; U+02016
+vert; U+0007C
+VerticalBar; U+02223
+VerticalLine; U+0007C
+VerticalSeparator; U+02758
+VerticalTilde; U+02240
+VeryThinSpace; U+0200A
+Vfr; U+1D519
+vfr; U+1D533
+vltri; U+022B2
+vnsub; U+02282 U+020D2
+vnsup; U+02283 U+020D2
+Vopf; U+1D54D
+vopf; U+1D567
+vprop; U+0221D
+vrtri; U+022B3
+Vscr; U+1D4B1
+vscr; U+1D4CB
+vsubnE; U+02ACB U+0FE00
+vsubne; U+0228A U+0FE00
+vsupnE; U+02ACC U+0FE00
+vsupne; U+0228B U+0FE00
+Vvdash; U+022AA
+vzigzag; U+0299A
+Wcirc; U+00174
+wcirc; U+00175
+wedbar; U+02A5F
+Wedge; U+022C0
+wedge; U+02227
+wedgeq; U+02259
+weierp; U+02118
+Wfr; U+1D51A
+wfr; U+1D534
+Wopf; U+1D54E
+wopf; U+1D568
+wp; U+02118
+wr; U+02240
+wreath; U+02240
+Wscr; U+1D4B2
+wscr; U+1D4CC
+xcap; U+022C2
+xcirc; U+025EF
+xcup; U+022C3
+xdtri; U+025BD
+Xfr; U+1D51B
+xfr; U+1D535
+xhArr; U+027FA
+xharr; U+027F7
+Xi; U+0039E
+xi; U+003BE
+xlArr; U+027F8
+xlarr; U+027F5
+xmap; U+027FC
+xnis; U+022FB
+xodot; U+02A00
+Xopf; U+1D54F
+xopf; U+1D569
+xoplus; U+02A01
+xotime; U+02A02
+xrArr; U+027F9
+xrarr; U+027F6
+Xscr; U+1D4B3
+xscr; U+1D4CD
+xsqcup; U+02A06
+xuplus; U+02A04
+xutri; U+025B3
+xvee; U+022C1
+xwedge; U+022C0
+Yacute; U+000DD
+Yacute U+000DD
+yacute; U+000FD
+yacute U+000FD
+YAcy; U+0042F
+yacy; U+0044F
+Ycirc; U+00176
+ycirc; U+00177
+Ycy; U+0042B
+ycy; U+0044B
+yen; U+000A5
+yen U+000A5
+Yfr; U+1D51C
+yfr; U+1D536
+YIcy; U+00407
+yicy; U+00457
+Yopf; U+1D550
+yopf; U+1D56A
+Yscr; U+1D4B4
+yscr; U+1D4CE
+YUcy; U+0042E
+yucy; U+0044E
+Yuml; U+00178
+yuml; U+000FF
+yuml U+000FF
+Zacute; U+00179
+zacute; U+0017A
+Zcaron; U+0017D
+zcaron; U+0017E
+Zcy; U+00417
+zcy; U+00437
+Zdot; U+0017B
+zdot; U+0017C
+zeetrf; U+02128
+ZeroWidthSpace; U+0200B
+Zeta; U+00396
+zeta; U+003B6
+Zfr; U+02128
+zfr; U+1D537
+ZHcy; U+00416
+zhcy; U+00436
+zigrarr; U+021DD
+Zopf; U+02124
+zopf; U+1D56B
+Zscr; U+1D4B5
+zscr; U+1D4CF
+zwj; U+0200D
+zwnj; U+0200C
@@ -1,2231 +0,0 @@
-Aacute; U+000C1
-Aacute U+000C1
-aacute; U+000E1
-aacute U+000E1
-Abreve; U+00102
-abreve; U+00103
-ac; U+0223E
-acd; U+0223F
-acE; U+0223E U+00333
-Acirc; U+000C2
-Acirc U+000C2
-acirc; U+000E2
-acirc U+000E2
-acute; U+000B4
-acute U+000B4
-Acy; U+00410
-acy; U+00430
-AElig; U+000C6
-AElig U+000C6
-aelig; U+000E6
-aelig U+000E6
-af; U+02061
-Afr; U+1D504
-afr; U+1D51E
-Agrave; U+000C0
-Agrave U+000C0
-agrave; U+000E0
-agrave U+000E0
-alefsym; U+02135
-aleph; U+02135
-Alpha; U+00391
-alpha; U+003B1
-Amacr; U+00100
-amacr; U+00101
-amalg; U+02A3F
-AMP; U+00026
-AMP U+00026
-amp; U+00026
-amp U+00026
-And; U+02A53
-and; U+02227
-andand; U+02A55
-andd; U+02A5C
-andslope; U+02A58
-andv; U+02A5A
-ang; U+02220
-ange; U+029A4
-angle; U+02220
-angmsd; U+02221
-angmsdaa; U+029A8
-angmsdab; U+029A9
-angmsdac; U+029AA
-angmsdad; U+029AB
-angmsdae; U+029AC
-angmsdaf; U+029AD
-angmsdag; U+029AE
-angmsdah; U+029AF
-angrt; U+0221F
-angrtvb; U+022BE
-angrtvbd; U+0299D
-angsph; U+02222
-angst; U+000C5
-angzarr; U+0237C
-Aogon; U+00104
-aogon; U+00105
-Aopf; U+1D538
-aopf; U+1D552
-ap; U+02248
-apacir; U+02A6F
-apE; U+02A70
-ape; U+0224A
-apid; U+0224B
-apos; U+00027
-ApplyFunction; U+02061
-approx; U+02248
-approxeq; U+0224A
-Aring; U+000C5
-Aring U+000C5
-aring; U+000E5
-aring U+000E5
-Ascr; U+1D49C
-ascr; U+1D4B6
-Assign; U+02254
-ast; U+0002A
-asymp; U+02248
-asympeq; U+0224D
-Atilde; U+000C3
-Atilde U+000C3
-atilde; U+000E3
-atilde U+000E3
-Auml; U+000C4
-Auml U+000C4
-auml; U+000E4
-auml U+000E4
-awconint; U+02233
-awint; U+02A11
-backcong; U+0224C
-backepsilon; U+003F6
-backprime; U+02035
-backsim; U+0223D
-backsimeq; U+022CD
-Backslash; U+02216
-Barv; U+02AE7
-barvee; U+022BD
-Barwed; U+02306
-barwed; U+02305
-barwedge; U+02305
-bbrk; U+023B5
-bbrktbrk; U+023B6
-bcong; U+0224C
-Bcy; U+00411
-bcy; U+00431
-bdquo; U+0201E
-becaus; U+02235
-Because; U+02235
-because; U+02235
-bemptyv; U+029B0
-bepsi; U+003F6
-bernou; U+0212C
-Bernoullis; U+0212C
-Beta; U+00392
-beta; U+003B2
-beth; U+02136
-between; U+0226C
-Bfr; U+1D505
-bfr; U+1D51F
-bigcap; U+022C2
-bigcirc; U+025EF
-bigcup; U+022C3
-bigodot; U+02A00
-bigoplus; U+02A01
-bigotimes; U+02A02
-bigsqcup; U+02A06
-bigstar; U+02605
-bigtriangledown; U+025BD
-bigtriangleup; U+025B3
-biguplus; U+02A04
-bigvee; U+022C1
-bigwedge; U+022C0
-bkarow; U+0290D
-blacklozenge; U+029EB
-blacksquare; U+025AA
-blacktriangle; U+025B4
-blacktriangledown; U+025BE
-blacktriangleleft; U+025C2
-blacktriangleright; U+025B8
-blank; U+02423
-blk12; U+02592
-blk14; U+02591
-blk34; U+02593
-block; U+02588
-bne; U+0003D U+020E5
-bnequiv; U+02261 U+020E5
-bNot; U+02AED
-bnot; U+02310
-Bopf; U+1D539
-bopf; U+1D553
-bot; U+022A5
-bottom; U+022A5
-bowtie; U+022C8
-boxbox; U+029C9
-boxDL; U+02557
-boxDl; U+02556
-boxdL; U+02555
-boxdl; U+02510
-boxDR; U+02554
-boxDr; U+02553
-boxdR; U+02552
-boxdr; U+0250C
-boxH; U+02550
-boxh; U+02500
-boxHD; U+02566
-boxHd; U+02564
-boxhD; U+02565
-boxhd; U+0252C
-boxHU; U+02569
-boxHu; U+02567
-boxhU; U+02568
-boxhu; U+02534
-boxminus; U+0229F
-boxplus; U+0229E
-boxtimes; U+022A0
-boxUL; U+0255D
-boxUl; U+0255C
-boxuL; U+0255B
-boxul; U+02518
-boxUR; U+0255A
-boxUr; U+02559
-boxuR; U+02558
-boxur; U+02514
-boxV; U+02551
-boxv; U+02502
-boxVH; U+0256C
-boxVh; U+0256B
-boxvH; U+0256A
-boxvh; U+0253C
-boxVL; U+02563
-boxVl; U+02562
-boxvL; U+02561
-boxvl; U+02524
-boxVR; U+02560
-boxVr; U+0255F
-boxvR; U+0255E
-boxvr; U+0251C
-bprime; U+02035
-Breve; U+002D8
-breve; U+002D8
-brvbar; U+000A6
-brvbar U+000A6
-Bscr; U+0212C
-bscr; U+1D4B7
-bsemi; U+0204F
-bsim; U+0223D
-bsime; U+022CD
-bsol; U+0005C
-bsolb; U+029C5
-bsolhsub; U+027C8
-bull; U+02022
-bullet; U+02022
-bump; U+0224E
-bumpE; U+02AAE
-bumpe; U+0224F
-Bumpeq; U+0224E
-bumpeq; U+0224F
-Cacute; U+00106
-cacute; U+00107
-Cap; U+022D2
-cap; U+02229
-capand; U+02A44
-capbrcup; U+02A49
-capcap; U+02A4B
-capcup; U+02A47
-capdot; U+02A40
-CapitalDifferentialD; U+02145
-caps; U+02229 U+0FE00
-caret; U+02041
-caron; U+002C7
-Cayleys; U+0212D
-ccaps; U+02A4D
-Ccaron; U+0010C
-ccaron; U+0010D
-Ccedil; U+000C7
-Ccedil U+000C7
-ccedil; U+000E7
-ccedil U+000E7
-Ccirc; U+00108
-ccirc; U+00109
-Cconint; U+02230
-ccups; U+02A4C
-ccupssm; U+02A50
-Cdot; U+0010A
-cdot; U+0010B
-cedil; U+000B8
-cedil U+000B8
-Cedilla; U+000B8
-cemptyv; U+029B2
-cent; U+000A2
-cent U+000A2
-CenterDot; U+000B7
-centerdot; U+000B7
-Cfr; U+0212D
-cfr; U+1D520
-CHcy; U+00427
-chcy; U+00447
-check; U+02713
-checkmark; U+02713
-Chi; U+003A7
-chi; U+003C7
-cir; U+025CB
-circ; U+002C6
-circeq; U+02257
-circlearrowleft; U+021BA
-circlearrowright; U+021BB
-circledast; U+0229B
-circledcirc; U+0229A
-circleddash; U+0229D
-CircleDot; U+02299
-circledR; U+000AE
-circledS; U+024C8
-CircleMinus; U+02296
-CirclePlus; U+02295
-CircleTimes; U+02297
-cirE; U+029C3
-cire; U+02257
-cirfnint; U+02A10
-cirmid; U+02AEF
-cirscir; U+029C2
-ClockwiseContourIntegral; U+02232
-CloseCurlyDoubleQuote; U+0201D
-CloseCurlyQuote; U+02019
-clubs; U+02663
-clubsuit; U+02663
-Colon; U+02237
-colon; U+0003A
-Colone; U+02A74
-colone; U+02254
-coloneq; U+02254
-comma; U+0002C
-commat; U+00040
-comp; U+02201
-compfn; U+02218
-complement; U+02201
-complexes; U+02102
-cong; U+02245
-congdot; U+02A6D
-Congruent; U+02261
-Conint; U+0222F
-conint; U+0222E
-ContourIntegral; U+0222E
-Copf; U+02102
-copf; U+1D554
-coprod; U+02210
-Coproduct; U+02210
-COPY; U+000A9
-COPY U+000A9
-copy; U+000A9
-copy U+000A9
-copysr; U+02117
-CounterClockwiseContourIntegral; U+02233
-crarr; U+021B5
-Cross; U+02A2F
-cross; U+02717
-Cscr; U+1D49E
-cscr; U+1D4B8
-csub; U+02ACF
-csube; U+02AD1
-csup; U+02AD0
-csupe; U+02AD2
-ctdot; U+022EF
-cudarrl; U+02938
-cudarrr; U+02935
-cuepr; U+022DE
-cuesc; U+022DF
-cularr; U+021B6
-cularrp; U+0293D
-Cup; U+022D3
-cup; U+0222A
-cupbrcap; U+02A48
-CupCap; U+0224D
-cupcap; U+02A46
-cupcup; U+02A4A
-cupdot; U+0228D
-cupor; U+02A45
-cups; U+0222A U+0FE00
-curarr; U+021B7
-curarrm; U+0293C
-curlyeqprec; U+022DE
-curlyeqsucc; U+022DF
-curlyvee; U+022CE
-curlywedge; U+022CF
-curren; U+000A4
-curren U+000A4
-curvearrowleft; U+021B6
-curvearrowright; U+021B7
-cuvee; U+022CE
-cuwed; U+022CF
-cwconint; U+02232
-cwint; U+02231
-cylcty; U+0232D
-Dagger; U+02021
-dagger; U+02020
-daleth; U+02138
-Darr; U+021A1
-dArr; U+021D3
-darr; U+02193
-dash; U+02010
-Dashv; U+02AE4
-dashv; U+022A3
-dbkarow; U+0290F
-dblac; U+002DD
-Dcaron; U+0010E
-dcaron; U+0010F
-Dcy; U+00414
-dcy; U+00434
-DD; U+02145
-dd; U+02146
-ddagger; U+02021
-ddarr; U+021CA
-DDotrahd; U+02911
-ddotseq; U+02A77
-deg; U+000B0
-deg U+000B0
-Del; U+02207
-Delta; U+00394
-delta; U+003B4
-demptyv; U+029B1
-dfisht; U+0297F
-Dfr; U+1D507
-dfr; U+1D521
-dHar; U+02965
-dharl; U+021C3
-dharr; U+021C2
-DiacriticalAcute; U+000B4
-DiacriticalDot; U+002D9
-DiacriticalDoubleAcute; U+002DD
-DiacriticalGrave; U+00060
-DiacriticalTilde; U+002DC
-diam; U+022C4
-Diamond; U+022C4
-diamond; U+022C4
-diamondsuit; U+02666
-diams; U+02666
-die; U+000A8
-DifferentialD; U+02146
-digamma; U+003DD
-disin; U+022F2
-div; U+000F7
-divide; U+000F7
-divide U+000F7
-divideontimes; U+022C7
-divonx; U+022C7
-DJcy; U+00402
-djcy; U+00452
-dlcorn; U+0231E
-dlcrop; U+0230D
-dollar; U+00024
-Dopf; U+1D53B
-dopf; U+1D555
-Dot; U+000A8
-dot; U+002D9
-DotDot; U+020DC
-doteq; U+02250
-doteqdot; U+02251
-DotEqual; U+02250
-dotminus; U+02238
-dotplus; U+02214
-dotsquare; U+022A1
-doublebarwedge; U+02306
-DoubleContourIntegral; U+0222F
-DoubleDot; U+000A8
-DoubleDownArrow; U+021D3
-DoubleLeftArrow; U+021D0
-DoubleLeftRightArrow; U+021D4
-DoubleLeftTee; U+02AE4
-DoubleLongLeftArrow; U+027F8
-DoubleLongLeftRightArrow; U+027FA
-DoubleLongRightArrow; U+027F9
-DoubleRightArrow; U+021D2
-DoubleRightTee; U+022A8
-DoubleUpArrow; U+021D1
-DoubleUpDownArrow; U+021D5
-DoubleVerticalBar; U+02225
-DownArrow; U+02193
-Downarrow; U+021D3
-downarrow; U+02193
-DownArrowBar; U+02913
-DownArrowUpArrow; U+021F5
-DownBreve; U+00311
-downdownarrows; U+021CA
-downharpoonleft; U+021C3
-downharpoonright; U+021C2
-DownLeftRightVector; U+02950
-DownLeftTeeVector; U+0295E
-DownLeftVector; U+021BD
-DownLeftVectorBar; U+02956
-DownRightTeeVector; U+0295F
-DownRightVector; U+021C1
-DownRightVectorBar; U+02957
-DownTee; U+022A4
-DownTeeArrow; U+021A7
-drbkarow; U+02910
-drcorn; U+0231F
-drcrop; U+0230C
-Dscr; U+1D49F
-dscr; U+1D4B9
-DScy; U+00405
-dscy; U+00455
-dsol; U+029F6
-Dstrok; U+00110
-dstrok; U+00111
-dtdot; U+022F1
-dtri; U+025BF
-dtrif; U+025BE
-duarr; U+021F5
-duhar; U+0296F
-dwangle; U+029A6
-DZcy; U+0040F
-dzcy; U+0045F
-dzigrarr; U+027FF
-Eacute; U+000C9
-Eacute U+000C9
-eacute; U+000E9
-eacute U+000E9
-easter; U+02A6E
-Ecaron; U+0011A
-ecaron; U+0011B
-ecir; U+02256
-Ecirc; U+000CA
-Ecirc U+000CA
-ecirc; U+000EA
-ecirc U+000EA
-ecolon; U+02255
-Ecy; U+0042D
-ecy; U+0044D
-eDDot; U+02A77
-Edot; U+00116
-eDot; U+02251
-edot; U+00117
-ee; U+02147
-efDot; U+02252
-Efr; U+1D508
-efr; U+1D522
-eg; U+02A9A
-Egrave; U+000C8
-Egrave U+000C8
-egrave; U+000E8
-egrave U+000E8
-egs; U+02A96
-egsdot; U+02A98
-el; U+02A99
-Element; U+02208
-elinters; U+023E7
-ell; U+02113
-els; U+02A95
-elsdot; U+02A97
-Emacr; U+00112
-emacr; U+00113
-empty; U+02205
-emptyset; U+02205
-EmptySmallSquare; U+025FB
-emptyv; U+02205
-EmptyVerySmallSquare; U+025AB
-emsp; U+02003
-emsp13; U+02004
-emsp14; U+02005
-ENG; U+0014A
-eng; U+0014B
-ensp; U+02002
-Eogon; U+00118
-eogon; U+00119
-Eopf; U+1D53C
-eopf; U+1D556
-epar; U+022D5
-eparsl; U+029E3
-eplus; U+02A71
-epsi; U+003B5
-Epsilon; U+00395
-epsilon; U+003B5
-epsiv; U+003F5
-eqcirc; U+02256
-eqcolon; U+02255
-eqsim; U+02242
-eqslantgtr; U+02A96
-eqslantless; U+02A95
-Equal; U+02A75
-equals; U+0003D
-EqualTilde; U+02242
-equest; U+0225F
-Equilibrium; U+021CC
-equiv; U+02261
-equivDD; U+02A78
-eqvparsl; U+029E5
-erarr; U+02971
-erDot; U+02253
-Escr; U+02130
-escr; U+0212F
-esdot; U+02250
-Esim; U+02A73
-esim; U+02242
-Eta; U+00397
-eta; U+003B7
-ETH; U+000D0
-ETH U+000D0
-eth; U+000F0
-eth U+000F0
-Euml; U+000CB
-Euml U+000CB
-euml; U+000EB
-euml U+000EB
-euro; U+020AC
-excl; U+00021
-exist; U+02203
-Exists; U+02203
-expectation; U+02130
-ExponentialE; U+02147
-exponentiale; U+02147
-fallingdotseq; U+02252
-Fcy; U+00424
-fcy; U+00444
-female; U+02640
-ffilig; U+0FB03
-fflig; U+0FB00
-ffllig; U+0FB04
-Ffr; U+1D509
-ffr; U+1D523
-filig; U+0FB01
-FilledSmallSquare; U+025FC
-FilledVerySmallSquare; U+025AA
-fjlig; U+00066 U+0006A
-flat; U+0266D
-fllig; U+0FB02
-fltns; U+025B1
-fnof; U+00192
-Fopf; U+1D53D
-fopf; U+1D557
-ForAll; U+02200
-forall; U+02200
-fork; U+022D4
-forkv; U+02AD9
-Fouriertrf; U+02131
-fpartint; U+02A0D
-frac12; U+000BD
-frac12 U+000BD
-frac13; U+02153
-frac14; U+000BC
-frac14 U+000BC
-frac15; U+02155
-frac16; U+02159
-frac18; U+0215B
-frac23; U+02154
-frac25; U+02156
-frac34; U+000BE
-frac34 U+000BE
-frac35; U+02157
-frac38; U+0215C
-frac45; U+02158
-frac56; U+0215A
-frac58; U+0215D
-frac78; U+0215E
-frasl; U+02044
-frown; U+02322
-Fscr; U+02131
-fscr; U+1D4BB
-gacute; U+001F5
-Gamma; U+00393
-gamma; U+003B3
-Gammad; U+003DC
-gammad; U+003DD
-gap; U+02A86
-Gbreve; U+0011E
-gbreve; U+0011F
-Gcedil; U+00122
-Gcirc; U+0011C
-gcirc; U+0011D
-Gcy; U+00413
-gcy; U+00433
-Gdot; U+00120
-gdot; U+00121
-gE; U+02267
-ge; U+02265
-gEl; U+02A8C
-gel; U+022DB
-geq; U+02265
-geqq; U+02267
-geqslant; U+02A7E
-ges; U+02A7E
-gescc; U+02AA9
-gesdot; U+02A80
-gesdoto; U+02A82
-gesdotol; U+02A84
-gesl; U+022DB U+0FE00
-gesles; U+02A94
-Gfr; U+1D50A
-gfr; U+1D524
-Gg; U+022D9
-gg; U+0226B
-ggg; U+022D9
-gimel; U+02137
-GJcy; U+00403
-gjcy; U+00453
-gl; U+02277
-gla; U+02AA5
-glE; U+02A92
-glj; U+02AA4
-gnap; U+02A8A
-gnapprox; U+02A8A
-gnE; U+02269
-gne; U+02A88
-gneq; U+02A88
-gneqq; U+02269
-gnsim; U+022E7
-Gopf; U+1D53E
-gopf; U+1D558
-grave; U+00060
-GreaterEqual; U+02265
-GreaterEqualLess; U+022DB
-GreaterFullEqual; U+02267
-GreaterGreater; U+02AA2
-GreaterLess; U+02277
-GreaterSlantEqual; U+02A7E
-GreaterTilde; U+02273
-Gscr; U+1D4A2
-gscr; U+0210A
-gsim; U+02273
-gsime; U+02A8E
-gsiml; U+02A90
-GT; U+0003E
-GT U+0003E
-Gt; U+0226B
-gt; U+0003E
-gt U+0003E
-gtcc; U+02AA7
-gtcir; U+02A7A
-gtdot; U+022D7
-gtlPar; U+02995
-gtquest; U+02A7C
-gtrapprox; U+02A86
-gtrarr; U+02978
-gtrdot; U+022D7
-gtreqless; U+022DB
-gtreqqless; U+02A8C
-gtrless; U+02277
-gtrsim; U+02273
-gvertneqq; U+02269 U+0FE00
-gvnE; U+02269 U+0FE00
-Hacek; U+002C7
-hairsp; U+0200A
-half; U+000BD
-hamilt; U+0210B
-HARDcy; U+0042A
-hardcy; U+0044A
-hArr; U+021D4
-harr; U+02194
-harrcir; U+02948
-harrw; U+021AD
-Hat; U+0005E
-hbar; U+0210F
-Hcirc; U+00124
-hcirc; U+00125
-hearts; U+02665
-heartsuit; U+02665
-hellip; U+02026
-hercon; U+022B9
-Hfr; U+0210C
-hfr; U+1D525
-HilbertSpace; U+0210B
-hksearow; U+02925
-hkswarow; U+02926
-hoarr; U+021FF
-homtht; U+0223B
-hookleftarrow; U+021A9
-hookrightarrow; U+021AA
-Hopf; U+0210D
-hopf; U+1D559
-horbar; U+02015
-HorizontalLine; U+02500
-Hscr; U+0210B
-hscr; U+1D4BD
-hslash; U+0210F
-Hstrok; U+00126
-hstrok; U+00127
-HumpDownHump; U+0224E
-HumpEqual; U+0224F
-hybull; U+02043
-hyphen; U+02010
-Iacute; U+000CD
-Iacute U+000CD
-iacute; U+000ED
-iacute U+000ED
-ic; U+02063
-Icirc; U+000CE
-Icirc U+000CE
-icirc; U+000EE
-icirc U+000EE
-Icy; U+00418
-icy; U+00438
-Idot; U+00130
-IEcy; U+00415
-iecy; U+00435
-iexcl; U+000A1
-iexcl U+000A1
-iff; U+021D4
-Ifr; U+02111
-ifr; U+1D526
-Igrave; U+000CC
-Igrave U+000CC
-igrave; U+000EC
-igrave U+000EC
-ii; U+02148
-iiiint; U+02A0C
-iiint; U+0222D
-iinfin; U+029DC
-iiota; U+02129
-IJlig; U+00132
-ijlig; U+00133
-Im; U+02111
-Imacr; U+0012A
-imacr; U+0012B
-image; U+02111
-ImaginaryI; U+02148
-imagline; U+02110
-imagpart; U+02111
-imath; U+00131
-imof; U+022B7
-imped; U+001B5
-Implies; U+021D2
-in; U+02208
-incare; U+02105
-infin; U+0221E
-infintie; U+029DD
-inodot; U+00131
-Int; U+0222C
-int; U+0222B
-intcal; U+022BA
-integers; U+02124
-Integral; U+0222B
-intercal; U+022BA
-Intersection; U+022C2
-intlarhk; U+02A17
-intprod; U+02A3C
-InvisibleComma; U+02063
-InvisibleTimes; U+02062
-IOcy; U+00401
-iocy; U+00451
-Iogon; U+0012E
-iogon; U+0012F
-Iopf; U+1D540
-iopf; U+1D55A
-Iota; U+00399
-iota; U+003B9
-iprod; U+02A3C
-iquest; U+000BF
-iquest U+000BF
-Iscr; U+02110
-iscr; U+1D4BE
-isin; U+02208
-isindot; U+022F5
-isinE; U+022F9
-isins; U+022F4
-isinsv; U+022F3
-isinv; U+02208
-it; U+02062
-Itilde; U+00128
-itilde; U+00129
-Iukcy; U+00406
-iukcy; U+00456
-Iuml; U+000CF
-Iuml U+000CF
-iuml; U+000EF
-iuml U+000EF
-Jcirc; U+00134
-jcirc; U+00135
-Jcy; U+00419
-jcy; U+00439
-Jfr; U+1D50D
-jfr; U+1D527
-jmath; U+00237
-Jopf; U+1D541
-jopf; U+1D55B
-Jscr; U+1D4A5
-jscr; U+1D4BF
-Jsercy; U+00408
-jsercy; U+00458
-Jukcy; U+00404
-jukcy; U+00454
-Kappa; U+0039A
-kappa; U+003BA
-kappav; U+003F0
-Kcedil; U+00136
-kcedil; U+00137
-Kcy; U+0041A
-kcy; U+0043A
-Kfr; U+1D50E
-kfr; U+1D528
-kgreen; U+00138
-KHcy; U+00425
-khcy; U+00445
-KJcy; U+0040C
-kjcy; U+0045C
-Kopf; U+1D542
-kopf; U+1D55C
-Kscr; U+1D4A6
-kscr; U+1D4C0
-lAarr; U+021DA
-Lacute; U+00139
-lacute; U+0013A
-laemptyv; U+029B4
-lagran; U+02112
-Lambda; U+0039B
-lambda; U+003BB
-Lang; U+027EA
-lang; U+027E8
-langd; U+02991
-langle; U+027E8
-lap; U+02A85
-Laplacetrf; U+02112
-laquo; U+000AB
-laquo U+000AB
-Larr; U+0219E
-lArr; U+021D0
-larr; U+02190
-larrb; U+021E4
-larrbfs; U+0291F
-larrfs; U+0291D
-larrhk; U+021A9
-larrlp; U+021AB
-larrpl; U+02939
-larrsim; U+02973
-larrtl; U+021A2
-lat; U+02AAB
-lAtail; U+0291B
-latail; U+02919
-late; U+02AAD
-lates; U+02AAD U+0FE00
-lBarr; U+0290E
-lbarr; U+0290C
-lbbrk; U+02772
-lbrace; U+0007B
-lbrack; U+0005B
-lbrke; U+0298B
-lbrksld; U+0298F
-lbrkslu; U+0298D
-Lcaron; U+0013D
-lcaron; U+0013E
-Lcedil; U+0013B
-lcedil; U+0013C
-lceil; U+02308
-lcub; U+0007B
-Lcy; U+0041B
-lcy; U+0043B
-ldca; U+02936
-ldquo; U+0201C
-ldquor; U+0201E
-ldrdhar; U+02967
-ldrushar; U+0294B
-ldsh; U+021B2
-lE; U+02266
-le; U+02264
-LeftAngleBracket; U+027E8
-LeftArrow; U+02190
-Leftarrow; U+021D0
-leftarrow; U+02190
-LeftArrowBar; U+021E4
-LeftArrowRightArrow; U+021C6
-leftarrowtail; U+021A2
-LeftCeiling; U+02308
-LeftDoubleBracket; U+027E6
-LeftDownTeeVector; U+02961
-LeftDownVector; U+021C3
-LeftDownVectorBar; U+02959
-LeftFloor; U+0230A
-leftharpoondown; U+021BD
-leftharpoonup; U+021BC
-leftleftarrows; U+021C7
-LeftRightArrow; U+02194
-Leftrightarrow; U+021D4
-leftrightarrow; U+02194
-leftrightarrows; U+021C6
-leftrightharpoons; U+021CB
-leftrightsquigarrow; U+021AD
-LeftRightVector; U+0294E
-LeftTee; U+022A3
-LeftTeeArrow; U+021A4
-LeftTeeVector; U+0295A
-leftthreetimes; U+022CB
-LeftTriangle; U+022B2
-LeftTriangleBar; U+029CF
-LeftTriangleEqual; U+022B4
-LeftUpDownVector; U+02951
-LeftUpTeeVector; U+02960
-LeftUpVector; U+021BF
-LeftUpVectorBar; U+02958
-LeftVector; U+021BC
-LeftVectorBar; U+02952
-lEg; U+02A8B
-leg; U+022DA
-leq; U+02264
-leqq; U+02266
-leqslant; U+02A7D
-les; U+02A7D
-lescc; U+02AA8
-lesdot; U+02A7F
-lesdoto; U+02A81
-lesdotor; U+02A83
-lesg; U+022DA U+0FE00
-lesges; U+02A93
-lessapprox; U+02A85
-lessdot; U+022D6
-lesseqgtr; U+022DA
-lesseqqgtr; U+02A8B
-LessEqualGreater; U+022DA
-LessFullEqual; U+02266
-LessGreater; U+02276
-lessgtr; U+02276
-LessLess; U+02AA1
-lesssim; U+02272
-LessSlantEqual; U+02A7D
-LessTilde; U+02272
-lfisht; U+0297C
-lfloor; U+0230A
-Lfr; U+1D50F
-lfr; U+1D529
-lg; U+02276
-lgE; U+02A91
-lHar; U+02962
-lhard; U+021BD
-lharu; U+021BC
-lharul; U+0296A
-lhblk; U+02584
-LJcy; U+00409
-ljcy; U+00459
-Ll; U+022D8
-ll; U+0226A
-llarr; U+021C7
-llcorner; U+0231E
-Lleftarrow; U+021DA
-llhard; U+0296B
-lltri; U+025FA
-Lmidot; U+0013F
-lmidot; U+00140
-lmoust; U+023B0
-lmoustache; U+023B0
-lnap; U+02A89
-lnapprox; U+02A89
-lnE; U+02268
-lne; U+02A87
-lneq; U+02A87
-lneqq; U+02268
-lnsim; U+022E6
-loang; U+027EC
-loarr; U+021FD
-lobrk; U+027E6
-LongLeftArrow; U+027F5
-Longleftarrow; U+027F8
-longleftarrow; U+027F5
-LongLeftRightArrow; U+027F7
-Longleftrightarrow; U+027FA
-longleftrightarrow; U+027F7
-longmapsto; U+027FC
-LongRightArrow; U+027F6
-Longrightarrow; U+027F9
-longrightarrow; U+027F6
-looparrowleft; U+021AB
-looparrowright; U+021AC
-lopar; U+02985
-Lopf; U+1D543
-lopf; U+1D55D
-loplus; U+02A2D
-lotimes; U+02A34
-lowast; U+02217
-lowbar; U+0005F
-LowerLeftArrow; U+02199
-LowerRightArrow; U+02198
-loz; U+025CA
-lozenge; U+025CA
-lozf; U+029EB
-lpar; U+00028
-lparlt; U+02993
-lrarr; U+021C6
-lrcorner; U+0231F
-lrhar; U+021CB
-lrhard; U+0296D
-lrm; U+0200E
-lrtri; U+022BF
-lsaquo; U+02039
-Lscr; U+02112
-lscr; U+1D4C1
-Lsh; U+021B0
-lsh; U+021B0
-lsim; U+02272
-lsime; U+02A8D
-lsimg; U+02A8F
-lsqb; U+0005B
-lsquo; U+02018
-lsquor; U+0201A
-Lstrok; U+00141
-lstrok; U+00142
-LT; U+0003C
-LT U+0003C
-Lt; U+0226A
-lt; U+0003C
-lt U+0003C
-ltcc; U+02AA6
-ltcir; U+02A79
-ltdot; U+022D6
-lthree; U+022CB
-ltimes; U+022C9
-ltlarr; U+02976
-ltquest; U+02A7B
-ltri; U+025C3
-ltrie; U+022B4
-ltrif; U+025C2
-ltrPar; U+02996
-lurdshar; U+0294A
-luruhar; U+02966
-lvertneqq; U+02268 U+0FE00
-lvnE; U+02268 U+0FE00
-macr; U+000AF
-macr U+000AF
-male; U+02642
-malt; U+02720
-maltese; U+02720
-Map; U+02905
-map; U+021A6
-mapsto; U+021A6
-mapstodown; U+021A7
-mapstoleft; U+021A4
-mapstoup; U+021A5
-marker; U+025AE
-mcomma; U+02A29
-Mcy; U+0041C
-mcy; U+0043C
-mdash; U+02014
-mDDot; U+0223A
-measuredangle; U+02221
-MediumSpace; U+0205F
-Mellintrf; U+02133
-Mfr; U+1D510
-mfr; U+1D52A
-mho; U+02127
-micro; U+000B5
-micro U+000B5
-mid; U+02223
-midast; U+0002A
-midcir; U+02AF0
-middot; U+000B7
-middot U+000B7
-minus; U+02212
-minusb; U+0229F
-minusd; U+02238
-minusdu; U+02A2A
-MinusPlus; U+02213
-mlcp; U+02ADB
-mldr; U+02026
-mnplus; U+02213
-models; U+022A7
-Mopf; U+1D544
-mopf; U+1D55E
-mp; U+02213
-Mscr; U+02133
-mscr; U+1D4C2
-mstpos; U+0223E
-Mu; U+0039C
-mu; U+003BC
-multimap; U+022B8
-mumap; U+022B8
-nabla; U+02207
-Nacute; U+00143
-nacute; U+00144
-nang; U+02220 U+020D2
-nap; U+02249
-napE; U+02A70 U+00338
-napid; U+0224B U+00338
-napos; U+00149
-napprox; U+02249
-natur; U+0266E
-natural; U+0266E
-naturals; U+02115
-nbsp; U+000A0
-nbsp U+000A0
-nbump; U+0224E U+00338
-nbumpe; U+0224F U+00338
-ncap; U+02A43
-Ncaron; U+00147
-ncaron; U+00148
-Ncedil; U+00145
-ncedil; U+00146
-ncong; U+02247
-ncongdot; U+02A6D U+00338
-ncup; U+02A42
-Ncy; U+0041D
-ncy; U+0043D
-ndash; U+02013
-ne; U+02260
-nearhk; U+02924
-neArr; U+021D7
-nearr; U+02197
-nearrow; U+02197
-nedot; U+02250 U+00338
-NegativeMediumSpace; U+0200B
-NegativeThickSpace; U+0200B
-NegativeThinSpace; U+0200B
-NegativeVeryThinSpace; U+0200B
-nequiv; U+02262
-nesear; U+02928
-nesim; U+02242 U+00338
-NestedGreaterGreater; U+0226B
-NestedLessLess; U+0226A
-NewLine; U+0000A
-nexist; U+02204
-nexists; U+02204
-Nfr; U+1D511
-nfr; U+1D52B
-ngE; U+02267 U+00338
-nge; U+02271
-ngeq; U+02271
-ngeqq; U+02267 U+00338
-ngeqslant; U+02A7E U+00338
-nges; U+02A7E U+00338
-nGg; U+022D9 U+00338
-ngsim; U+02275
-nGt; U+0226B U+020D2
-ngt; U+0226F
-ngtr; U+0226F
-nGtv; U+0226B U+00338
-nhArr; U+021CE
-nharr; U+021AE
-nhpar; U+02AF2
-ni; U+0220B
-nis; U+022FC
-nisd; U+022FA
-niv; U+0220B
-NJcy; U+0040A
-njcy; U+0045A
-nlArr; U+021CD
-nlarr; U+0219A
-nldr; U+02025
-nlE; U+02266 U+00338
-nle; U+02270
-nLeftarrow; U+021CD
-nleftarrow; U+0219A
-nLeftrightarrow; U+021CE
-nleftrightarrow; U+021AE
-nleq; U+02270
-nleqq; U+02266 U+00338
-nleqslant; U+02A7D U+00338
-nles; U+02A7D U+00338
-nless; U+0226E
-nLl; U+022D8 U+00338
-nlsim; U+02274
-nLt; U+0226A U+020D2
-nlt; U+0226E
-nltri; U+022EA
-nltrie; U+022EC
-nLtv; U+0226A U+00338
-nmid; U+02224
-NoBreak; U+02060
-NonBreakingSpace; U+000A0
-Nopf; U+02115
-nopf; U+1D55F
-Not; U+02AEC
-not; U+000AC
-not U+000AC
-NotCongruent; U+02262
-NotCupCap; U+0226D
-NotDoubleVerticalBar; U+02226
-NotElement; U+02209
-NotEqual; U+02260
-NotEqualTilde; U+02242 U+00338
-NotExists; U+02204
-NotGreater; U+0226F
-NotGreaterEqual; U+02271
-NotGreaterFullEqual; U+02267 U+00338
-NotGreaterGreater; U+0226B U+00338
-NotGreaterLess; U+02279
-NotGreaterSlantEqual; U+02A7E U+00338
-NotGreaterTilde; U+02275
-NotHumpDownHump; U+0224E U+00338
-NotHumpEqual; U+0224F U+00338
-notin; U+02209
-notindot; U+022F5 U+00338
-notinE; U+022F9 U+00338
-notinva; U+02209
-notinvb; U+022F7
-notinvc; U+022F6
-NotLeftTriangle; U+022EA
-NotLeftTriangleBar; U+029CF U+00338
-NotLeftTriangleEqual; U+022EC
-NotLess; U+0226E
-NotLessEqual; U+02270
-NotLessGreater; U+02278
-NotLessLess; U+0226A U+00338
-NotLessSlantEqual; U+02A7D U+00338
-NotLessTilde; U+02274
-NotNestedGreaterGreater; U+02AA2 U+00338
-NotNestedLessLess; U+02AA1 U+00338
-notni; U+0220C
-notniva; U+0220C
-notnivb; U+022FE
-notnivc; U+022FD
-NotPrecedes; U+02280
-NotPrecedesEqual; U+02AAF U+00338
-NotPrecedesSlantEqual; U+022E0
-NotReverseElement; U+0220C
-NotRightTriangle; U+022EB
-NotRightTriangleBar; U+029D0 U+00338
-NotRightTriangleEqual; U+022ED
-NotSquareSubset; U+0228F U+00338
-NotSquareSubsetEqual; U+022E2
-NotSquareSuperset; U+02290 U+00338
-NotSquareSupersetEqual; U+022E3
-NotSubset; U+02282 U+020D2
-NotSubsetEqual; U+02288
-NotSucceeds; U+02281
-NotSucceedsEqual; U+02AB0 U+00338
-NotSucceedsSlantEqual; U+022E1
-NotSucceedsTilde; U+0227F U+00338
-NotSuperset; U+02283 U+020D2
-NotSupersetEqual; U+02289
-NotTilde; U+02241
-NotTildeEqual; U+02244
-NotTildeFullEqual; U+02247
-NotTildeTilde; U+02249
-NotVerticalBar; U+02224
-npar; U+02226
-nparallel; U+02226
-nparsl; U+02AFD U+020E5
-npart; U+02202 U+00338
-npolint; U+02A14
-npr; U+02280
-nprcue; U+022E0
-npre; U+02AAF U+00338
-nprec; U+02280
-npreceq; U+02AAF U+00338
-nrArr; U+021CF
-nrarr; U+0219B
-nrarrc; U+02933 U+00338
-nrarrw; U+0219D U+00338
-nRightarrow; U+021CF
-nrightarrow; U+0219B
-nrtri; U+022EB
-nrtrie; U+022ED
-nsc; U+02281
-nsccue; U+022E1
-nsce; U+02AB0 U+00338
-Nscr; U+1D4A9
-nscr; U+1D4C3
-nshortmid; U+02224
-nshortparallel; U+02226
-nsim; U+02241
-nsime; U+02244
-nsimeq; U+02244
-nsmid; U+02224
-nspar; U+02226
-nsqsube; U+022E2
-nsqsupe; U+022E3
-nsub; U+02284
-nsubE; U+02AC5 U+00338
-nsube; U+02288
-nsubset; U+02282 U+020D2
-nsubseteq; U+02288
-nsubseteqq; U+02AC5 U+00338
-nsucc; U+02281
-nsucceq; U+02AB0 U+00338
-nsup; U+02285
-nsupE; U+02AC6 U+00338
-nsupe; U+02289
-nsupset; U+02283 U+020D2
-nsupseteq; U+02289
-nsupseteqq; U+02AC6 U+00338
-ntgl; U+02279
-Ntilde; U+000D1
-Ntilde U+000D1
-ntilde; U+000F1
-ntilde U+000F1
-ntlg; U+02278
-ntriangleleft; U+022EA
-ntrianglelefteq; U+022EC
-ntriangleright; U+022EB
-ntrianglerighteq; U+022ED
-Nu; U+0039D
-nu; U+003BD
-num; U+00023
-numero; U+02116
-numsp; U+02007
-nvap; U+0224D U+020D2
-nVDash; U+022AF
-nVdash; U+022AE
-nvDash; U+022AD
-nvdash; U+022AC
-nvge; U+02265 U+020D2
-nvgt; U+0003E U+020D2
-nvHarr; U+02904
-nvinfin; U+029DE
-nvlArr; U+02902
-nvle; U+02264 U+020D2
-nvlt; U+0003C U+020D2
-nvltrie; U+022B4 U+020D2
-nvrArr; U+02903
-nvrtrie; U+022B5 U+020D2
-nvsim; U+0223C U+020D2
-nwarhk; U+02923
-nwArr; U+021D6
-nwarr; U+02196
-nwarrow; U+02196
-nwnear; U+02927
-Oacute; U+000D3
-Oacute U+000D3
-oacute; U+000F3
-oacute U+000F3
-oast; U+0229B
-ocir; U+0229A
-Ocirc; U+000D4
-Ocirc U+000D4
-ocirc; U+000F4
-ocirc U+000F4
-Ocy; U+0041E
-ocy; U+0043E
-odash; U+0229D
-Odblac; U+00150
-odblac; U+00151
-odiv; U+02A38
-odot; U+02299
-odsold; U+029BC
-OElig; U+00152
-oelig; U+00153
-ofcir; U+029BF
-Ofr; U+1D512
-ofr; U+1D52C
-ogon; U+002DB
-Ograve; U+000D2
-Ograve U+000D2
-ograve; U+000F2
-ograve U+000F2
-ogt; U+029C1
-ohbar; U+029B5
-ohm; U+003A9
-oint; U+0222E
-olarr; U+021BA
-olcir; U+029BE
-olcross; U+029BB
-oline; U+0203E
-olt; U+029C0
-Omacr; U+0014C
-omacr; U+0014D
-Omega; U+003A9
-omega; U+003C9
-Omicron; U+0039F
-omicron; U+003BF
-omid; U+029B6
-ominus; U+02296
-Oopf; U+1D546
-oopf; U+1D560
-opar; U+029B7
-OpenCurlyDoubleQuote; U+0201C
-OpenCurlyQuote; U+02018
-operp; U+029B9
-oplus; U+02295
-Or; U+02A54
-or; U+02228
-orarr; U+021BB
-ord; U+02A5D
-order; U+02134
-orderof; U+02134
-ordf; U+000AA
-ordf U+000AA
-ordm; U+000BA
-ordm U+000BA
-origof; U+022B6
-oror; U+02A56
-orslope; U+02A57
-orv; U+02A5B
-oS; U+024C8
-Oscr; U+1D4AA
-oscr; U+02134
-Oslash; U+000D8
-Oslash U+000D8
-oslash; U+000F8
-oslash U+000F8
-osol; U+02298
-Otilde; U+000D5
-Otilde U+000D5
-otilde; U+000F5
-otilde U+000F5
-Otimes; U+02A37
-otimes; U+02297
-otimesas; U+02A36
-Ouml; U+000D6
-Ouml U+000D6
-ouml; U+000F6
-ouml U+000F6
-ovbar; U+0233D
-OverBar; U+0203E
-OverBrace; U+023DE
-OverBracket; U+023B4
-OverParenthesis; U+023DC
-par; U+02225
-para; U+000B6
-para U+000B6
-parallel; U+02225
-parsim; U+02AF3
-parsl; U+02AFD
-part; U+02202
-PartialD; U+02202
-Pcy; U+0041F
-pcy; U+0043F
-percnt; U+00025
-period; U+0002E
-permil; U+02030
-perp; U+022A5
-pertenk; U+02031
-Pfr; U+1D513
-pfr; U+1D52D
-Phi; U+003A6
-phi; U+003C6
-phiv; U+003D5
-phmmat; U+02133
-phone; U+0260E
-Pi; U+003A0
-pi; U+003C0
-pitchfork; U+022D4
-piv; U+003D6
-planck; U+0210F
-planckh; U+0210E
-plankv; U+0210F
-plus; U+0002B
-plusacir; U+02A23
-plusb; U+0229E
-pluscir; U+02A22
-plusdo; U+02214
-plusdu; U+02A25
-pluse; U+02A72
-PlusMinus; U+000B1
-plusmn; U+000B1
-plusmn U+000B1
-plussim; U+02A26
-plustwo; U+02A27
-pm; U+000B1
-Poincareplane; U+0210C
-pointint; U+02A15
-Popf; U+02119
-popf; U+1D561
-pound; U+000A3
-pound U+000A3
-Pr; U+02ABB
-pr; U+0227A
-prap; U+02AB7
-prcue; U+0227C
-prE; U+02AB3
-pre; U+02AAF
-prec; U+0227A
-precapprox; U+02AB7
-preccurlyeq; U+0227C
-Precedes; U+0227A
-PrecedesEqual; U+02AAF
-PrecedesSlantEqual; U+0227C
-PrecedesTilde; U+0227E
-preceq; U+02AAF
-precnapprox; U+02AB9
-precneqq; U+02AB5
-precnsim; U+022E8
-precsim; U+0227E
-Prime; U+02033
-prime; U+02032
-primes; U+02119
-prnap; U+02AB9
-prnE; U+02AB5
-prnsim; U+022E8
-prod; U+0220F
-Product; U+0220F
-profalar; U+0232E
-profline; U+02312
-profsurf; U+02313
-prop; U+0221D
-Proportion; U+02237
-Proportional; U+0221D
-propto; U+0221D
-prsim; U+0227E
-prurel; U+022B0
-Pscr; U+1D4AB
-pscr; U+1D4C5
-Psi; U+003A8
-psi; U+003C8
-puncsp; U+02008
-Qfr; U+1D514
-qfr; U+1D52E
-qint; U+02A0C
-Qopf; U+0211A
-qopf; U+1D562
-qprime; U+02057
-Qscr; U+1D4AC
-qscr; U+1D4C6
-quaternions; U+0210D
-quatint; U+02A16
-quest; U+0003F
-questeq; U+0225F
-QUOT; U+00022
-QUOT U+00022
-quot; U+00022
-quot U+00022
-rAarr; U+021DB
-race; U+0223D U+00331
-Racute; U+00154
-racute; U+00155
-radic; U+0221A
-raemptyv; U+029B3
-Rang; U+027EB
-rang; U+027E9
-rangd; U+02992
-range; U+029A5
-rangle; U+027E9
-raquo; U+000BB
-raquo U+000BB
-Rarr; U+021A0
-rArr; U+021D2
-rarr; U+02192
-rarrap; U+02975
-rarrb; U+021E5
-rarrbfs; U+02920
-rarrc; U+02933
-rarrfs; U+0291E
-rarrhk; U+021AA
-rarrlp; U+021AC
-rarrpl; U+02945
-rarrsim; U+02974
-Rarrtl; U+02916
-rarrtl; U+021A3
-rarrw; U+0219D
-rAtail; U+0291C
-ratail; U+0291A
-ratio; U+02236
-rationals; U+0211A
-RBarr; U+02910
-rBarr; U+0290F
-rbarr; U+0290D
-rbbrk; U+02773
-rbrace; U+0007D
-rbrack; U+0005D
-rbrke; U+0298C
-rbrksld; U+0298E
-rbrkslu; U+02990
-Rcaron; U+00158
-rcaron; U+00159
-Rcedil; U+00156
-rcedil; U+00157
-rceil; U+02309
-rcub; U+0007D
-Rcy; U+00420
-rcy; U+00440
-rdca; U+02937
-rdldhar; U+02969
-rdquo; U+0201D
-rdquor; U+0201D
-rdsh; U+021B3
-Re; U+0211C
-real; U+0211C
-realine; U+0211B
-realpart; U+0211C
-reals; U+0211D
-rect; U+025AD
-REG; U+000AE
-REG U+000AE
-reg; U+000AE
-reg U+000AE
-ReverseElement; U+0220B
-ReverseEquilibrium; U+021CB
-ReverseUpEquilibrium; U+0296F
-rfisht; U+0297D
-rfloor; U+0230B
-Rfr; U+0211C
-rfr; U+1D52F
-rHar; U+02964
-rhard; U+021C1
-rharu; U+021C0
-rharul; U+0296C
-Rho; U+003A1
-rho; U+003C1
-rhov; U+003F1
-RightAngleBracket; U+027E9
-RightArrow; U+02192
-Rightarrow; U+021D2
-rightarrow; U+02192
-RightArrowBar; U+021E5
-RightArrowLeftArrow; U+021C4
-rightarrowtail; U+021A3
-RightCeiling; U+02309
-RightDoubleBracket; U+027E7
-RightDownTeeVector; U+0295D
-RightDownVector; U+021C2
-RightDownVectorBar; U+02955
-RightFloor; U+0230B
-rightharpoondown; U+021C1
-rightharpoonup; U+021C0
-rightleftarrows; U+021C4
-rightleftharpoons; U+021CC
-rightrightarrows; U+021C9
-rightsquigarrow; U+0219D
-RightTee; U+022A2
-RightTeeArrow; U+021A6
-RightTeeVector; U+0295B
-rightthreetimes; U+022CC
-RightTriangle; U+022B3
-RightTriangleBar; U+029D0
-RightTriangleEqual; U+022B5
-RightUpDownVector; U+0294F
-RightUpTeeVector; U+0295C
-RightUpVector; U+021BE
-RightUpVectorBar; U+02954
-RightVector; U+021C0
-RightVectorBar; U+02953
-ring; U+002DA
-risingdotseq; U+02253
-rlarr; U+021C4
-rlhar; U+021CC
-rlm; U+0200F
-rmoust; U+023B1
-rmoustache; U+023B1
-rnmid; U+02AEE
-roang; U+027ED
-roarr; U+021FE
-robrk; U+027E7
-ropar; U+02986
-Ropf; U+0211D
-ropf; U+1D563
-roplus; U+02A2E
-rotimes; U+02A35
-RoundImplies; U+02970
-rpar; U+00029
-rpargt; U+02994
-rppolint; U+02A12
-rrarr; U+021C9
-Rrightarrow; U+021DB
-rsaquo; U+0203A
-Rscr; U+0211B
-rscr; U+1D4C7
-Rsh; U+021B1
-rsh; U+021B1
-rsqb; U+0005D
-rsquo; U+02019
-rsquor; U+02019
-rthree; U+022CC
-rtimes; U+022CA
-rtri; U+025B9
-rtrie; U+022B5
-rtrif; U+025B8
-rtriltri; U+029CE
-RuleDelayed; U+029F4
-ruluhar; U+02968
-rx; U+0211E
-Sacute; U+0015A
-sacute; U+0015B
-sbquo; U+0201A
-Sc; U+02ABC
-sc; U+0227B
-scap; U+02AB8
-Scaron; U+00160
-scaron; U+00161
-sccue; U+0227D
-scE; U+02AB4
-sce; U+02AB0
-Scedil; U+0015E
-scedil; U+0015F
-Scirc; U+0015C
-scirc; U+0015D
-scnap; U+02ABA
-scnE; U+02AB6
-scnsim; U+022E9
-scpolint; U+02A13
-scsim; U+0227F
-Scy; U+00421
-scy; U+00441
-sdot; U+022C5
-sdotb; U+022A1
-sdote; U+02A66
-searhk; U+02925
-seArr; U+021D8
-searr; U+02198
-searrow; U+02198
-sect; U+000A7
-sect U+000A7
-semi; U+0003B
-seswar; U+02929
-setminus; U+02216
-setmn; U+02216
-sext; U+02736
-Sfr; U+1D516
-sfr; U+1D530
-sfrown; U+02322
-sharp; U+0266F
-SHCHcy; U+00429
-shchcy; U+00449
-SHcy; U+00428
-shcy; U+00448
-ShortDownArrow; U+02193
-ShortLeftArrow; U+02190
-shortmid; U+02223
-shortparallel; U+02225
-ShortRightArrow; U+02192
-ShortUpArrow; U+02191
-shy; U+000AD
-shy U+000AD
-Sigma; U+003A3
-sigma; U+003C3
-sigmaf; U+003C2
-sigmav; U+003C2
-sim; U+0223C
-simdot; U+02A6A
-sime; U+02243
-simeq; U+02243
-simg; U+02A9E
-simgE; U+02AA0
-siml; U+02A9D
-simlE; U+02A9F
-simne; U+02246
-simplus; U+02A24
-simrarr; U+02972
-slarr; U+02190
-SmallCircle; U+02218
-smallsetminus; U+02216
-smashp; U+02A33
-smeparsl; U+029E4
-smid; U+02223
-smile; U+02323
-smt; U+02AAA
-smte; U+02AAC
-smtes; U+02AAC U+0FE00
-SOFTcy; U+0042C
-softcy; U+0044C
-sol; U+0002F
-solb; U+029C4
-solbar; U+0233F
-Sopf; U+1D54A
-sopf; U+1D564
-spades; U+02660
-spadesuit; U+02660
-spar; U+02225
-sqcap; U+02293
-sqcaps; U+02293 U+0FE00
-sqcup; U+02294
-sqcups; U+02294 U+0FE00
-Sqrt; U+0221A
-sqsub; U+0228F
-sqsube; U+02291
-sqsubset; U+0228F
-sqsubseteq; U+02291
-sqsup; U+02290
-sqsupe; U+02292
-sqsupset; U+02290
-sqsupseteq; U+02292
-squ; U+025A1
-Square; U+025A1
-square; U+025A1
-SquareIntersection; U+02293
-SquareSubset; U+0228F
-SquareSubsetEqual; U+02291
-SquareSuperset; U+02290
-SquareSupersetEqual; U+02292
-SquareUnion; U+02294
-squarf; U+025AA
-squf; U+025AA
-srarr; U+02192
-Sscr; U+1D4AE
-sscr; U+1D4C8
-ssetmn; U+02216
-ssmile; U+02323
-sstarf; U+022C6
-Star; U+022C6
-star; U+02606
-starf; U+02605
-straightepsilon; U+003F5
-straightphi; U+003D5
-strns; U+000AF
-Sub; U+022D0
-sub; U+02282
-subdot; U+02ABD
-subE; U+02AC5
-sube; U+02286
-subedot; U+02AC3
-submult; U+02AC1
-subnE; U+02ACB
-subne; U+0228A
-subplus; U+02ABF
-subrarr; U+02979
-Subset; U+022D0
-subset; U+02282
-subseteq; U+02286
-subseteqq; U+02AC5
-SubsetEqual; U+02286
-subsetneq; U+0228A
-subsetneqq; U+02ACB
-subsim; U+02AC7
-subsub; U+02AD5
-subsup; U+02AD3
-succ; U+0227B
-succapprox; U+02AB8
-succcurlyeq; U+0227D
-Succeeds; U+0227B
-SucceedsEqual; U+02AB0
-SucceedsSlantEqual; U+0227D
-SucceedsTilde; U+0227F
-succeq; U+02AB0
-succnapprox; U+02ABA
-succneqq; U+02AB6
-succnsim; U+022E9
-succsim; U+0227F
-SuchThat; U+0220B
-Sum; U+02211
-sum; U+02211
-sung; U+0266A
-Sup; U+022D1
-sup; U+02283
-sup1; U+000B9
-sup1 U+000B9
-sup2; U+000B2
-sup2 U+000B2
-sup3; U+000B3
-sup3 U+000B3
-supdot; U+02ABE
-supdsub; U+02AD8
-supE; U+02AC6
-supe; U+02287
-supedot; U+02AC4
-Superset; U+02283
-SupersetEqual; U+02287
-suphsol; U+027C9
-suphsub; U+02AD7
-suplarr; U+0297B
-supmult; U+02AC2
-supnE; U+02ACC
-supne; U+0228B
-supplus; U+02AC0
-Supset; U+022D1
-supset; U+02283
-supseteq; U+02287
-supseteqq; U+02AC6
-supsetneq; U+0228B
-supsetneqq; U+02ACC
-supsim; U+02AC8
-supsub; U+02AD4
-supsup; U+02AD6
-swarhk; U+02926
-swArr; U+021D9
-swarr; U+02199
-swarrow; U+02199
-swnwar; U+0292A
-szlig; U+000DF
-szlig U+000DF
-Tab; U+00009
-target; U+02316
-Tau; U+003A4
-tau; U+003C4
-tbrk; U+023B4
-Tcaron; U+00164
-tcaron; U+00165
-Tcedil; U+00162
-tcedil; U+00163
-Tcy; U+00422
-tcy; U+00442
-tdot; U+020DB
-telrec; U+02315
-Tfr; U+1D517
-tfr; U+1D531
-there4; U+02234
-Therefore; U+02234
-therefore; U+02234
-Theta; U+00398
-theta; U+003B8
-thetasym; U+003D1
-thetav; U+003D1
-thickapprox; U+02248
-thicksim; U+0223C
-ThickSpace; U+0205F U+0200A
-thinsp; U+02009
-ThinSpace; U+02009
-thkap; U+02248
-thksim; U+0223C
-THORN; U+000DE
-THORN U+000DE
-thorn; U+000FE
-thorn U+000FE
-Tilde; U+0223C
-tilde; U+002DC
-TildeEqual; U+02243
-TildeFullEqual; U+02245
-TildeTilde; U+02248
-times; U+000D7
-times U+000D7
-timesb; U+022A0
-timesbar; U+02A31
-timesd; U+02A30
-tint; U+0222D
-toea; U+02928
-top; U+022A4
-topbot; U+02336
-topcir; U+02AF1
-Topf; U+1D54B
-topf; U+1D565
-topfork; U+02ADA
-tosa; U+02929
-tprime; U+02034
-TRADE; U+02122
-trade; U+02122
-triangle; U+025B5
-triangledown; U+025BF
-triangleleft; U+025C3
-trianglelefteq; U+022B4
-triangleq; U+0225C
-triangleright; U+025B9
-trianglerighteq; U+022B5
-tridot; U+025EC
-trie; U+0225C
-triminus; U+02A3A
-TripleDot; U+020DB
-triplus; U+02A39
-trisb; U+029CD
-tritime; U+02A3B
-trpezium; U+023E2
-Tscr; U+1D4AF
-tscr; U+1D4C9
-TScy; U+00426
-tscy; U+00446
-TSHcy; U+0040B
-tshcy; U+0045B
-Tstrok; U+00166
-tstrok; U+00167
-twixt; U+0226C
-twoheadleftarrow; U+0219E
-twoheadrightarrow; U+021A0
-Uacute; U+000DA
-Uacute U+000DA
-uacute; U+000FA
-uacute U+000FA
-Uarr; U+0219F
-uArr; U+021D1
-uarr; U+02191
-Uarrocir; U+02949
-Ubrcy; U+0040E
-ubrcy; U+0045E
-Ubreve; U+0016C
-ubreve; U+0016D
-Ucirc; U+000DB
-Ucirc U+000DB
-ucirc; U+000FB
-ucirc U+000FB
-Ucy; U+00423
-ucy; U+00443
-udarr; U+021C5
-Udblac; U+00170
-udblac; U+00171
-udhar; U+0296E
-ufisht; U+0297E
-Ufr; U+1D518
-ufr; U+1D532
-Ugrave; U+000D9
-Ugrave U+000D9
-ugrave; U+000F9
-ugrave U+000F9
-uHar; U+02963
-uharl; U+021BF
-uharr; U+021BE
-uhblk; U+02580
-ulcorn; U+0231C
-ulcorner; U+0231C
-ulcrop; U+0230F
-ultri; U+025F8
-Umacr; U+0016A
-umacr; U+0016B
-uml; U+000A8
-uml U+000A8
-UnderBar; U+0005F
-UnderBrace; U+023DF
-UnderBracket; U+023B5
-UnderParenthesis; U+023DD
-Union; U+022C3
-UnionPlus; U+0228E
-Uogon; U+00172
-uogon; U+00173
-Uopf; U+1D54C
-uopf; U+1D566
-UpArrow; U+02191
-Uparrow; U+021D1
-uparrow; U+02191
-UpArrowBar; U+02912
-UpArrowDownArrow; U+021C5
-UpDownArrow; U+02195
-Updownarrow; U+021D5
-updownarrow; U+02195
-UpEquilibrium; U+0296E
-upharpoonleft; U+021BF
-upharpoonright; U+021BE
-uplus; U+0228E
-UpperLeftArrow; U+02196
-UpperRightArrow; U+02197
-Upsi; U+003D2
-upsi; U+003C5
-upsih; U+003D2
-Upsilon; U+003A5
-upsilon; U+003C5
-UpTee; U+022A5
-UpTeeArrow; U+021A5
-upuparrows; U+021C8
-urcorn; U+0231D
-urcorner; U+0231D
-urcrop; U+0230E
-Uring; U+0016E
-uring; U+0016F
-urtri; U+025F9
-Uscr; U+1D4B0
-uscr; U+1D4CA
-utdot; U+022F0
-Utilde; U+00168
-utilde; U+00169
-utri; U+025B5
-utrif; U+025B4
-uuarr; U+021C8
-Uuml; U+000DC
-Uuml U+000DC
-uuml; U+000FC
-uuml U+000FC
-uwangle; U+029A7
-vangrt; U+0299C
-varepsilon; U+003F5
-varkappa; U+003F0
-varnothing; U+02205
-varphi; U+003D5
-varpi; U+003D6
-varpropto; U+0221D
-vArr; U+021D5
-varr; U+02195
-varrho; U+003F1
-varsigma; U+003C2
-varsubsetneq; U+0228A U+0FE00
-varsubsetneqq; U+02ACB U+0FE00
-varsupsetneq; U+0228B U+0FE00
-varsupsetneqq; U+02ACC U+0FE00
-vartheta; U+003D1
-vartriangleleft; U+022B2
-vartriangleright; U+022B3
-Vbar; U+02AEB
-vBar; U+02AE8
-vBarv; U+02AE9
-Vcy; U+00412
-vcy; U+00432
-VDash; U+022AB
-Vdash; U+022A9
-vDash; U+022A8
-vdash; U+022A2
-Vdashl; U+02AE6
-Vee; U+022C1
-vee; U+02228
-veebar; U+022BB
-veeeq; U+0225A
-vellip; U+022EE
-Verbar; U+02016
-verbar; U+0007C
-Vert; U+02016
-vert; U+0007C
-VerticalBar; U+02223
-VerticalLine; U+0007C
-VerticalSeparator; U+02758
-VerticalTilde; U+02240
-VeryThinSpace; U+0200A
-Vfr; U+1D519
-vfr; U+1D533
-vltri; U+022B2
-vnsub; U+02282 U+020D2
-vnsup; U+02283 U+020D2
-Vopf; U+1D54D
-vopf; U+1D567
-vprop; U+0221D
-vrtri; U+022B3
-Vscr; U+1D4B1
-vscr; U+1D4CB
-vsubnE; U+02ACB U+0FE00
-vsubne; U+0228A U+0FE00
-vsupnE; U+02ACC U+0FE00
-vsupne; U+0228B U+0FE00
-Vvdash; U+022AA
-vzigzag; U+0299A
-Wcirc; U+00174
-wcirc; U+00175
-wedbar; U+02A5F
-Wedge; U+022C0
-wedge; U+02227
-wedgeq; U+02259
-weierp; U+02118
-Wfr; U+1D51A
-wfr; U+1D534
-Wopf; U+1D54E
-wopf; U+1D568
-wp; U+02118
-wr; U+02240
-wreath; U+02240
-Wscr; U+1D4B2
-wscr; U+1D4CC
-xcap; U+022C2
-xcirc; U+025EF
-xcup; U+022C3
-xdtri; U+025BD
-Xfr; U+1D51B
-xfr; U+1D535
-xhArr; U+027FA
-xharr; U+027F7
-Xi; U+0039E
-xi; U+003BE
-xlArr; U+027F8
-xlarr; U+027F5
-xmap; U+027FC
-xnis; U+022FB
-xodot; U+02A00
-Xopf; U+1D54F
-xopf; U+1D569
-xoplus; U+02A01
-xotime; U+02A02
-xrArr; U+027F9
-xrarr; U+027F6
-Xscr; U+1D4B3
-xscr; U+1D4CD
-xsqcup; U+02A06
-xuplus; U+02A04
-xutri; U+025B3
-xvee; U+022C1
-xwedge; U+022C0
-Yacute; U+000DD
-Yacute U+000DD
-yacute; U+000FD
-yacute U+000FD
-YAcy; U+0042F
-yacy; U+0044F
-Ycirc; U+00176
-ycirc; U+00177
-Ycy; U+0042B
-ycy; U+0044B
-yen; U+000A5
-yen U+000A5
-Yfr; U+1D51C
-yfr; U+1D536
-YIcy; U+00407
-yicy; U+00457
-Yopf; U+1D550
-yopf; U+1D56A
-Yscr; U+1D4B4
-yscr; U+1D4CE
-YUcy; U+0042E
-yucy; U+0044E
-Yuml; U+00178
-yuml; U+000FF
-yuml U+000FF
-Zacute; U+00179
-zacute; U+0017A
-Zcaron; U+0017D
-zcaron; U+0017E
-Zcy; U+00417
-zcy; U+00437
-Zdot; U+0017B
-zdot; U+0017C
-zeetrf; U+02128
-ZeroWidthSpace; U+0200B
-Zeta; U+00396
-zeta; U+003B6
-Zfr; U+02128
-zfr; U+1D537
-ZHcy; U+00416
-zhcy; U+00436
-zigrarr; U+021DD
-Zopf; U+02124
-zopf; U+1D56B
-Zscr; U+1D4B5
-zscr; U+1D4CF
-zwj; U+0200D
-zwnj; U+0200C
@@ -3,57 +3,27 @@ use Mojo::Base -base;
 
 # "Professor: These old Doomsday devices are dangerously unstable. I'll rest
 #             easier not knowing where they are."
-use Carp 'croak';
+use Carp ();
 use Mojo::Home;
 use Mojo::Log;
 use Mojo::Transaction::HTTP;
 use Mojo::UserAgent;
-use Scalar::Util 'weaken';
+use Mojo::Util;
+use Scalar::Util ();
 
-has home => sub { Mojo::Home->new };
+has home => sub { Mojo::Home->new->detect(ref shift) };
 has log  => sub { Mojo::Log->new };
 has ua   => sub {
-  my $self = shift;
-
   my $ua = Mojo::UserAgent->new;
-  weaken $ua->server->app($self)->{app};
-  weaken $self;
-  return $ua->catch(sub { $self->log->error($_[1]) });
+  Scalar::Util::weaken $ua->server->app(shift)->{app};
+  return $ua;
 };
 
 sub build_tx { Mojo::Transaction::HTTP->new }
 
-sub config { shift->_dict(config => @_) }
-
-sub handler { croak 'Method "handler" not implemented in subclass' }
-
-sub new {
-  my $self = shift->SUPER::new(@_);
-
-  # Check if we have a log directory
-  my $home = $self->home;
-  $home->detect(ref $self) unless @{$home->parts};
-  $self->log->path($home->rel_file('log/mojo.log'))
-    if -w $home->rel_file('log');
-
-  return $self;
-}
-
-sub _dict {
-  my ($self, $name) = (shift, shift);
+sub config { Mojo::Util::_stash(config => @_) }
 
-  # Hash
-  my $dict = $self->{$name} ||= {};
-  return $dict unless @_;
-
-  # Get
-  return $dict->{$_[0]} unless @_ > 1 || ref $_[0];
-
-  # Set
-  %$dict = (%$dict, %{ref $_[0] ? $_[0] : {@_}});
-
-  return $self;
-}
+sub handler { Carp::croak 'Method "handler" not implemented in subclass' }
 
 1;
 
@@ -168,14 +138,6 @@ be overloaded in a subclass.
     ...
   }
 
-=head2 new
-
-  my $app = Mojo->new;
-
-Construct a new L<Mojo> application. Will automatically detect your home
-directory if necessary and set up logging to C<log/mojo.log> if there's a
-C<log> directory.
-
 =head1 SEE ALSO
 
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
@@ -9,9 +9,8 @@ has usage => sub { shift->extract_usage };
 
 sub run {
   my ($self, @args) = @_;
-  my $cgi = Mojo::Server::CGI->new(app => $self->app);
-  GetOptionsFromArray \@args, nph => sub { $cgi->nph(1) };
-  $cgi->run;
+  GetOptionsFromArray \@args, nph => \(my $nph = 0);
+  Mojo::Server::CGI->new(app => $self->app, nph => $nph)->run;
 }
 
 1;
@@ -32,7 +32,7 @@ sub run {
     my $msg = $tx->error->{message};
     if    ($code == 401) { $msg = 'Wrong username or password.' }
     elsif ($code == 409) { $msg = 'File already exists on CPAN.' }
-    die qq{Problem uploading file "$file". ($msg)\n};
+    die qq{Problem uploading file "$file": $msg\n};
   }
 
   say 'Upload successful!';
@@ -39,6 +39,7 @@ Mojolicious::Command::daemon - Daemon command
 
     ./myapp.pl daemon -m production -l http://*:8080
     ./myapp.pl daemon -l http://127.0.0.1:8080 -l https://[::]:8081
+    ./myapp.pl daemon -l 'https://*:443?cert=./server.crt&key=./server.key'
 
   Options:
     -b, --backlog <size>         Listen backlog size, defaults to SOMAXCONN.
@@ -19,14 +19,14 @@ EOF
   # Script
   my $name = class_to_file $class;
   $self->render_to_rel_file('mojo', "$name/script/$name", $class);
-  $self->chmod_file("$name/script/$name", 0744);
+  $self->chmod_rel_file("$name/script/$name", 0744);
 
   # Application class
   my $app = class_to_path $class;
   $self->render_to_rel_file('appclass', "$name/lib/$app", $class);
 
   # Controller
-  my $controller = "${class}::Example";
+  my $controller = "${class}::Controller::Example";
   my $path       = class_to_path $controller;
   $self->render_to_rel_file('controller', "$name/lib/$path", $controller);
 
@@ -47,6 +47,64 @@ EOF
 }
 
 1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::generate::app - App generator command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION generate app [NAME]
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::generate::app> generates application directory
+structures for fully functional L<Mojolicious> applications.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::generate::app> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $app->description;
+  $app            = $app->description('Foo!');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $app->usage;
+  $app      = $app->usage('Foo!');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::generate::app> inherits all methods from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 run
+
+  $app->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
+
+=cut
+
 __DATA__
 
 @@ mojo
@@ -139,62 +197,3 @@ This page was generated from the template "templates/example/welcome.html.ep"
 and the layout "templates/layouts/default.html.ep",
 <a href="<%%== url_for %>">click here</a> to reload the page or
 <a href="/index.html">here</a> to move forward to a static page.
-
-__END__
-
-=encoding utf8
-
-=head1 NAME
-
-Mojolicious::Command::generate::app - App generator command
-
-=head1 SYNOPSIS
-
-  Usage: APPLICATION generate app [NAME]
-
-=head1 DESCRIPTION
-
-L<Mojolicious::Command::generate::app> generates application directory
-structures for fully functional L<Mojolicious> applications.
-
-This is a core command, that means it is always enabled and its code a good
-example for learning to build new commands, you're welcome to fork it.
-
-See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
-available by default.
-
-=head1 ATTRIBUTES
-
-L<Mojolicious::Command::generate::app> inherits all attributes from
-L<Mojolicious::Command> and implements the following new ones.
-
-=head2 description
-
-  my $description = $app->description;
-  $app            = $app->description('Foo!');
-
-Short description of this command, used for the command list.
-
-=head2 usage
-
-  my $usage = $app->usage;
-  $app      = $app->usage('Foo!');
-
-Usage information for this command, used for the help screen.
-
-=head1 METHODS
-
-L<Mojolicious::Command::generate::app> inherits all methods from
-L<Mojolicious::Command> and implements the following new ones.
-
-=head2 run
-
-  $app->run(@ARGV);
-
-Run this command.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
-
-=cut
@@ -8,40 +8,10 @@ sub run {
   my ($self, $name) = @_;
   $name ||= 'myapp.pl';
   $self->render_to_rel_file('liteapp', $name);
-  $self->chmod_file($name, 0744);
+  $self->chmod_rel_file($name, 0744);
 }
 
 1;
-__DATA__
-
-@@ liteapp
-#!/usr/bin/env perl
-use Mojolicious::Lite;
-
-# Documentation browser under "/perldoc"
-plugin 'PODRenderer';
-
-get '/' => sub {
-  my $c = shift;
-  $c->render('index');
-};
-
-app->start;
-<% %>__DATA__
-
-<% %>@@ index.html.ep
-%% layout 'default';
-%% title 'Welcome';
-Welcome to the Mojolicious real-time web framework!
-
-<% %>@@ layouts/default.html.ep
-<!DOCTYPE html>
-<html>
-  <head><title><%%= title %></title></head>
-  <body><%%= content %></body>
-</html>
-
-__END__
 
 =encoding utf8
 
@@ -99,3 +69,32 @@ Run this command.
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
 
 =cut
+
+__DATA__
+
+@@ liteapp
+#!/usr/bin/env perl
+use Mojolicious::Lite;
+
+# Documentation browser under "/perldoc"
+plugin 'PODRenderer';
+
+get '/' => sub {
+  my $c = shift;
+  $c->render('index');
+};
+
+app->start;
+<% %>__DATA__
+
+<% %>@@ index.html.ep
+%% layout 'default';
+%% title 'Welcome';
+Welcome to the Mojolicious real-time web framework!
+
+<% %>@@ layouts/default.html.ep
+<!DOCTYPE html>
+<html>
+  <head><title><%%= title %></title></head>
+  <body><%%= content %></body>
+</html>
@@ -9,21 +9,6 @@ has usage => sub { shift->extract_usage };
 sub run { shift->render_to_rel_file('makefile', 'Makefile.PL') }
 
 1;
-__DATA__
-
-@@ makefile
-use strict;
-use warnings;
-
-use ExtUtils::MakeMaker;
-
-WriteMakefile(
-  VERSION   => '0.01',
-  PREREQ_PM => {'Mojolicious' => '<%= $Mojolicious::VERSION %>'},
-  test      => {TESTS => 't/*.t'}
-);
-
-__END__
 
 =encoding utf8
 
@@ -81,3 +66,17 @@ Run this command.
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
 
 =cut
+
+__DATA__
+
+@@ makefile
+use strict;
+use warnings;
+
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+  VERSION   => '0.01',
+  PREREQ_PM => {'Mojolicious' => '<%= $Mojolicious::VERSION %>'},
+  test      => {TESTS => 't/*.t'}
+);
@@ -26,6 +26,64 @@ sub run {
 }
 
 1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojolicious::Command::generate::plugin - Plugin generator command
+
+=head1 SYNOPSIS
+
+  Usage: APPLICATION generate plugin [NAME]
+
+=head1 DESCRIPTION
+
+L<Mojolicious::Command::generate::plugin> generates directory structures for
+fully functional L<Mojolicious> plugins.
+
+This is a core command, that means it is always enabled and its code a good
+example for learning to build new commands, you're welcome to fork it.
+
+See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
+available by default.
+
+=head1 ATTRIBUTES
+
+L<Mojolicious::Command::generate::plugin> inherits all attributes from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 description
+
+  my $description = $plugin->description;
+  $plugin         = $plugin->description('Foo!');
+
+Short description of this command, used for the command list.
+
+=head2 usage
+
+  my $usage = $plugin->usage;
+  $plugin   = $plugin->usage('Foo!');
+
+Usage information for this command, used for the help screen.
+
+=head1 METHODS
+
+L<Mojolicious::Command::generate::plugin> inherits all methods from
+L<Mojolicious::Command> and implements the following new ones.
+
+=head2 run
+
+  $plugin->run(@ARGV);
+
+Run this command.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
+
+=cut
+
 __DATA__
 
 @@ class
@@ -111,62 +169,3 @@ WriteMakefile(
   PREREQ_PM    => {'Mojolicious' => '<%= $Mojolicious::VERSION %>'},
   test         => {TESTS => 't/*.t'}
 );
-
-__END__
-
-=encoding utf8
-
-=head1 NAME
-
-Mojolicious::Command::generate::plugin - Plugin generator command
-
-=head1 SYNOPSIS
-
-  Usage: APPLICATION generate plugin [NAME]
-
-=head1 DESCRIPTION
-
-L<Mojolicious::Command::generate::plugin> generates directory structures for
-fully functional L<Mojolicious> plugins.
-
-This is a core command, that means it is always enabled and its code a good
-example for learning to build new commands, you're welcome to fork it.
-
-See L<Mojolicious::Commands/"COMMANDS"> for a list of commands that are
-available by default.
-
-=head1 ATTRIBUTES
-
-L<Mojolicious::Command::generate::plugin> inherits all attributes from
-L<Mojolicious::Command> and implements the following new ones.
-
-=head2 description
-
-  my $description = $plugin->description;
-  $plugin         = $plugin->description('Foo!');
-
-Short description of this command, used for the command list.
-
-=head2 usage
-
-  my $usage = $plugin->usage;
-  $plugin   = $plugin->usage('Foo!');
-
-Usage information for this command, used for the help screen.
-
-=head1 METHODS
-
-L<Mojolicious::Command::generate::plugin> inherits all methods from
-L<Mojolicious::Command> and implements the following new ones.
-
-=head2 run
-
-  $plugin->run(@ARGV);
-
-Run this command.
-
-=head1 SEE ALSO
-
-L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
-
-=cut
@@ -29,8 +29,7 @@ sub run {
   my $selector = shift @args;
 
   # Parse header pairs
-  my %headers;
-  /^\s*([^:]+)\s*:\s*(.+)$/ and $headers{$1} = $2 for @headers;
+  my %headers = map { /^\s*([^:]+)\s*:\s*(.+)$/ ? ($1, $2) : () } @headers;
 
   # Detect proxy for absolute URLs
   my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
@@ -66,14 +65,12 @@ sub run {
   STDOUT->autoflush(1);
   my $tx = $ua->start($ua->build_tx($method, $url, \%headers, $content));
   my $err = $tx->error;
-  $url = encode 'UTF-8', $url;
-  warn qq{Problem loading URL "$url". ($err->{message})\n}
+  warn qq{Problem loading URL "@{[$tx->req->url]}": $err->{message}\n}
     if $err && !$err->{code};
 
   # JSON Pointer
   return unless defined $selector;
-  my $type = $tx->res->headers->content_type // '';
-  return _json($buffer, $selector) if $type =~ /json/i;
+  return _json($buffer, $selector) if $selector eq '' || $selector =~ m!^/!;
 
   # Selector
   _select($buffer, $selector, $charset // $tx->res->content->charset, @args);
@@ -132,7 +129,7 @@ Mojolicious::Command::get - Get command
     ./myapp.pl get /
     mojo get mojolicio.us
     mojo get -v -r google.com
-    mojo get -v -H 'Host: mojolicious.org' -H 'DNT: 1' mojolicio.us
+    mojo get -v -H 'Host: mojolicious.org' -H 'Accept: */*' mojolicio.us
     mojo get -M POST -c 'trololo' mojolicio.us
     mojo get mojolicio.us 'head > title' text
     mojo get mojolicio.us .footer all
@@ -23,7 +23,7 @@ sub run {
   }
 
   # Turn them into real files
-  for my $name (keys %all) {
+  for my $name (grep {/\.\w+$/} keys %all) {
     my $prefix = $name =~ /\.\w+\.\w+$/ ? 'templates' : 'public';
     $self->write_file($self->rel_file("$prefix/$name"), $all{$name});
   }
@@ -50,6 +50,7 @@ Mojolicious::Command::prefork - Prefork command
 
     ./myapp.pl prefork -m production -l http://*:8080
     ./myapp.pl prefork -l http://127.0.0.1:8080 -l https://[::]:8081
+    ./myapp.pl prefork -l 'https://*:443?cert=./server.crt&key=./server.key'
 
   Options:
     -A, --accepts <number>               Number of connections for workers to
@@ -28,9 +28,9 @@ sub _walk {
 
   # Flags
   my @flags;
-  push @flags, $route->inline ? 'B' : '.';
   push @flags, @{$route->over || []} ? 'C' : '.';
   push @flags, (my $partial = $route->partial) ? 'D' : '.';
+  push @flags, $route->inline       ? 'U' : '.';
   push @flags, $route->is_websocket ? 'W' : '.';
   push @$row, join('', @flags) if $verbose;
 
@@ -44,13 +44,10 @@ sub _walk {
 
   # Regex (verbose)
   my $pattern = $route->pattern;
-  $pattern->match('/', $route->is_endpoint);
-  my $regex = (regexp_pattern $pattern->regex)[0];
-  my $format = (regexp_pattern($pattern->format_regex || ''))[0];
-  my $optional
-    = !$pattern->constraints->{format} || $pattern->defaults->{format};
-  $regex .= $optional ? "(?:$format)?" : $format if $format && !$partial;
-  push @$row, $regex if $verbose;
+  $pattern->match('/', $route->is_endpoint && !$partial);
+  my $regex  = (regexp_pattern $pattern->regex)[0];
+  my $format = (regexp_pattern($pattern->format_regex))[0];
+  push @$row, $regex, $format ? $format : '' if $verbose;
 
   $depth++;
   _walk($_, $depth, $rows, $verbose) for @{$route->children};
@@ -71,7 +68,7 @@ Mojolicious::Command::routes - Routes command
 
   Options:
     -v, --verbose   Print additional details about routes, flags indicate
-                    B=Bridge, C=Conditions, D=Detour and W=WebSocket.
+                    C=Conditions, D=Detour, U=Under and W=WebSocket.
 
 =head1 DESCRIPTION
 
@@ -1,11 +1,7 @@
 package Mojolicious::Command::test;
 use Mojo::Base 'Mojolicious::Command';
 
-use Cwd 'realpath';
-use FindBin;
-use File::Spec::Functions qw(abs2rel catdir splitdir);
 use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case);
-use Mojo::Home;
 
 has description => 'Run tests.';
 has usage => sub { shift->extract_usage };
@@ -13,21 +9,13 @@ has usage => sub { shift->extract_usage };
 sub run {
   my ($self, @args) = @_;
 
-  GetOptionsFromArray \@args, 'v|verbose' => sub { $ENV{HARNESS_VERBOSE} = 1 };
+  GetOptionsFromArray \@args, 'v|verbose' => \$ENV{HARNESS_VERBOSE};
 
-  unless (@args) {
-    my @base = splitdir(abs2rel $FindBin::Bin);
-
-    # "./t"
-    my $path = catdir @base, 't';
-
-    # "../t"
-    $path = catdir @base, '..', 't' unless -d $path;
-    die "Can't find test directory.\n" unless -d $path;
-
-    my $home = Mojo::Home->new($path);
-    /\.t$/ and push @args, $home->rel_file($_) for @{$home->list_files};
-    say "Running tests from '", realpath($path), "'.";
+  if (!@args && (my $home = $self->app->home)) {
+    die "Can't find test directory.\n" unless -d $home->rel_dir('t');
+    my $files = $home->list_files('t');
+    /\.t$/ and push @args, $home->rel_file("t/$_") for @$files;
+    say qq{Running tests from "}, $home->rel_dir('t') . '".';
   }
 
   $ENV{HARNESS_OPTIONS} //= 'c';
@@ -1,7 +1,7 @@
 package Mojolicious::Command::version;
 use Mojo::Base 'Mojolicious::Command';
 
-use Mojo::IOLoop::Server;
+use Mojo::IOLoop::Client;
 use Mojo::UserAgent;
 use Mojolicious;
 
@@ -11,11 +11,11 @@ has usage => sub { shift->extract_usage };
 sub run {
   my $self = shift;
 
-  my $ev = eval 'use Mojo::Reactor::EV; 1' ? $EV::VERSION : 'not installed';
-  my $ipv6
-    = Mojo::IOLoop::Server::IPV6 ? $IO::Socket::IP::VERSION : 'not installed';
-  my $tls
-    = Mojo::IOLoop::Server::TLS ? $IO::Socket::SSL::VERSION : 'not installed';
+  my $ev    = eval 'use Mojo::Reactor::EV; 1' ? $EV::VERSION : 'not installed';
+  my $class = 'Mojo::IOLoop::Client';
+  my $socks = $class->SOCKS ? $IO::Socket::Socks::VERSION : 'not installed';
+  my $tls   = $class->TLS ? $IO::Socket::SSL::VERSION : 'not installed';
+  my $ndn   = $class->NDN ? $Net::DNS::Native::VERSION : 'not installed';
 
   print <<EOF;
 CORE
@@ -23,20 +23,19 @@ CORE
   Mojolicious ($Mojolicious::VERSION, $Mojolicious::CODENAME)
 
 OPTIONAL
-  EV 4.0+               ($ev)
-  IO::Socket::IP 0.20+  ($ipv6)
-  IO::Socket::SSL 1.84+ ($tls)
+  EV 4.0+                 ($ev)
+  IO::Socket::Socks 0.64+ ($socks)
+  IO::Socket::SSL 1.84+   ($tls)
+  Net::DNS::Native        ($ndn)
 
 EOF
 
   # Check latest version on CPAN
   my $latest = eval {
-    my $ua = Mojo::UserAgent->new(max_redirects => 10);
-    $ua->proxy->detect;
-    $ua->get('api.metacpan.org/v0/release/Mojolicious')->res->json->{version};
-  };
+    Mojo::UserAgent->new(max_redirects => 10)->tap(sub { $_->proxy->detect })
+      ->get('api.metacpan.org/v0/release/Mojolicious')->res->json->{version};
+  } or return;
 
-  return unless $latest;
   my $msg = 'This version is up to date, have fun!';
   $msg = 'Thanks for testing a development release, you are awesome!'
     if $latest < $Mojolicious::VERSION;
@@ -14,14 +14,13 @@ use Pod::Usage 'pod2usage';
 
 has app => sub { Mojo::Server->new->build_app('Mojo::HelloWorld') };
 has description => 'No description.';
-has quiet       => 0;
-has usage       => "Usage: APPLICATION\n";
+has 'quiet';
+has usage => "Usage: APPLICATION\n";
 
 sub chmod_file {
   my ($self, $path, $mod) = @_;
   chmod $mod, $path or croak qq{Can't chmod file "$path": $!};
-  $mod = sprintf '%lo', $mod;
-  say "  [chmod] $path $mod" unless $self->quiet;
+  say "  [chmod] $path " . sprintf('%lo', $mod) unless $self->quiet;
   return $self;
 }
 
@@ -131,7 +130,7 @@ L<Mojolicious::Command> implements the following attributes.
 =head2 app
 
   my $app  = $command->app;
-  $command = $command->app(MyApp->new);
+  $command = $command->app(Mojolicious->new);
 
 Application for command, defaults to a L<Mojo::HelloWorld> object.
 
@@ -1,7 +1,7 @@
 package Mojolicious::Commands;
 use Mojo::Base 'Mojolicious::Command';
 
-use Getopt::Long 'GetOptions';
+use Getopt::Long 'GetOptionsFromArray';
 use Mojo::Server;
 use Mojo::Util 'tablify';
 
@@ -26,16 +26,18 @@ sub detect {
 }
 
 # Command line options for MOJO_HELP, MOJO_HOME and MOJO_MODE
-BEGIN {
+sub _args {
+  return if __PACKAGE__->detect;
   Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case pass_through));
-  GetOptions(
-    'h|help'   => sub { $ENV{MOJO_HELP} = 1 },
-    'home=s'   => sub { $ENV{MOJO_HOME} = $_[1] },
-    'm|mode=s' => sub { $ENV{MOJO_MODE} = $_[1] }
-  ) unless __PACKAGE__->detect;
+  GetOptionsFromArray shift,
+    'h|help'   => \$ENV{MOJO_HELP},
+    'home=s'   => \$ENV{MOJO_HOME},
+    'm|mode=s' => \$ENV{MOJO_MODE};
   Getopt::Long::Configure('default');
 }
 
+BEGIN { _args([@ARGV]) }
+
 sub run {
   my ($self, $name, @args) = @_;
 
@@ -59,7 +61,8 @@ sub run {
     die qq{Unknown command "$name", maybe you need to install it?\n}
       unless $module;
 
-    # Run command
+    # Run command (remove options shared by all commands)
+    _args(\@args);
     my $command = $module->new(app => $self->app);
     return $help ? $command->help(@args) : $command->run(@args);
   }
@@ -83,10 +86,7 @@ sub run {
   return print $self->message, tablify(\@rows), $self->hint;
 }
 
-sub start_app {
-  my $self = shift;
-  return Mojo::Server->new->build_app(shift)->start(@_);
-}
+sub start_app { shift; Mojo::Server->new->build_app(shift)->start(@_) }
 
 sub _command {
   my ($module, $fatal) = @_;
@@ -313,7 +313,7 @@ disabled with the C<MOJO_NO_DETECT> environment variable.
   Mojolicious::Commands->start_app('MyApp');
   Mojolicious::Commands->start_app(MyApp => @ARGV);
 
-Load application and start the command line interface for it.
+Load application from class and start the command line interface for it.
 
   # Always start daemon for application and ignore @ARGV
   Mojolicious::Commands->start_app('MyApp', 'daemon', '-l', 'http://*:8080');
@@ -4,7 +4,6 @@ use Mojo::Base -base;
 # No imports, for security reasons!
 use Carp ();
 use Mojo::ByteStream;
-use Mojo::Exception;
 use Mojo::Transaction::HTTP;
 use Mojo::URL;
 use Mojo::Util;
@@ -27,13 +26,13 @@ my %RESERVED = map { $_ => 1 } (
 sub AUTOLOAD {
   my $self = shift;
 
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
   Carp::croak "Undefined subroutine &${package}::$method called"
     unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__);
 
   # Call helper with current controller
   Carp::croak qq{Can't locate object method "$method" via package "$package"}
-    unless my $helper = $self->app->renderer->helpers->{$method};
+    unless my $helper = $self->app->renderer->get_helper($method);
   return $self->$helper(@_);
 }
 
@@ -43,7 +42,7 @@ sub cookie {
   my ($self, $name) = (shift, shift);
 
   # Multiple names
-  return map { scalar $self->cookie($_) } @$name if ref $name eq 'ARRAY';
+  return map { $self->cookie($_) } @$name if ref $name eq 'ARRAY';
 
   # Response cookie
   if (@_) {
@@ -58,11 +57,18 @@ sub cookie {
   }
 
   # Request cookies
-  return map { $_->value } $self->req->cookie($name) if wantarray;
   return undef unless my $cookie = $self->req->cookie($name);
   return $cookie->value;
 }
 
+sub every_cookie {
+  [map { $_->value } @{shift->req->every_cookie(shift)}];
+}
+
+sub every_param { _param(@_) }
+
+sub every_signed_cookie { _signed_cookie(@_) }
+
 sub finish {
   my $self = shift;
 
@@ -87,12 +93,14 @@ sub flash {
     if @_ == 1 && !ref $_[0];
 
   # Initialize new flash and merge values
-  my $flash = $session->{new_flash} ||= {};
-  %$flash = (%$flash, %{@_ > 1 ? {@_} : $_[0]});
+  my $values = ref $_[0] ? $_[0] : {@_};
+  @{$session->{new_flash} ||= {}}{keys %$values} = values %$values;
 
   return $self;
 }
 
+sub helpers { $_[0]->app->renderer->get_helper('')->($_[0]) }
+
 sub on {
   my ($self, $name, $cb) = @_;
   my $tx = $self->tx;
@@ -104,7 +112,7 @@ sub param {
   my ($self, $name) = (shift, shift);
 
   # Multiple names
-  return map { scalar $self->param($_) } @$name if ref $name eq 'ARRAY';
+  return map { $self->param($_) } @$name if ref $name eq 'ARRAY';
 
   # List names
   my $captures = $self->stash->{'mojo.captures'} ||= {};
@@ -117,22 +125,12 @@ sub param {
     return sort @keys;
   }
 
-  # Override values
-  if (@_) {
-    $captures->{$name} = @_ > 1 ? [@_] : $_[0];
-    return $self;
-  }
+  # Value
+  return _param($self, $name)->[-1] unless @_;
 
-  # Captured unreserved values
-  if (!$RESERVED{$name} && defined(my $value = $captures->{$name})) {
-    return ref $value eq 'ARRAY' ? wantarray ? @$value : $$value[0] : $value;
-  }
-
-  # Uploads
-  return $req->upload($name) if $req->upload($name);
-
-  # Param values
-  return $req->param($name);
+  # Override values
+  $captures->{$name} = @_ > 1 ? [@_] : $_[0];
+  return $self;
 }
 
 sub redirect_to {
@@ -170,20 +168,19 @@ sub render {
   return !!$self->rendered($self->stash->{status});
 }
 
-sub render_exception { _development('exception', @_) }
+sub render_exception { shift->helpers->reply->exception(@_) }
 
 sub render_later { shift->stash('mojo.rendered' => 1) }
 
 sub render_maybe { shift->render(@_, 'mojo.maybe' => 1) }
 
-sub render_not_found { _development('not_found', @_) }
+sub render_not_found { shift->helpers->reply->not_found }
 
+# DEPRECATED in Tiger Face!
 sub render_static {
-  my ($self, $file) = @_;
-  my $app = $self->app;
-  return !!$self->rendered if $app->static->serve($self, $file);
-  $app->log->debug(qq{File "$file" not found, public directory missing?});
-  return !$self->render_not_found;
+  Mojo::Util::deprecated 'Mojolicious::Controller::render_static is DEPRECATED'
+    . ' in favor of the reply->static helper';
+  shift->helpers->reply->static(@_);
 }
 
 sub render_to_string { shift->render(@_, 'mojo.to_string' => 1) }
@@ -266,7 +263,8 @@ sub session {
   return $session->{$_[0]} unless @_ > 1 || ref $_[0];
 
   # Set
-  %$session = (%$session, %{ref $_[0] ? $_[0] : {@_}});
+  my $values = ref $_[0] ? $_[0] : {@_};
+  @$session{keys %$values} = values %$values;
 
   return $self;
 }
@@ -275,43 +273,18 @@ sub signed_cookie {
   my ($self, $name, $value, $options) = @_;
 
   # Multiple names
-  return map { scalar $self->signed_cookie($_) } @$name
-    if ref $name eq 'ARRAY';
-
-  # Response cookie
-  my $secrets = $self->stash->{'mojo.secrets'};
-  return $self->cookie($name,
-    "$value--" . Mojo::Util::hmac_sha1_sum($value, $secrets->[0]), $options)
-    if defined $value;
-
-  # Request cookies
-  my @results;
-  for my $value ($self->cookie($name)) {
-
-    # Check signature with rotating secrets
-    if ($value =~ s/--([^\-]+)$//) {
-      my $signature = $1;
-
-      my $valid;
-      for my $secret (@$secrets) {
-        my $check = Mojo::Util::hmac_sha1_sum($value, $secret);
-        ++$valid and last if Mojo::Util::secure_compare($signature, $check);
-      }
-      if ($valid) { push @results, $value }
+  return map { $self->signed_cookie($_) } @$name if ref $name eq 'ARRAY';
 
-      else {
-        $self->app->log->debug(
-          qq{Bad signed cookie "$name", possible hacking attempt.});
-      }
-    }
+  # Request cookie
+  return _signed_cookie($self, $name)->[-1] unless defined $value;
 
-    else { $self->app->log->debug(qq{Cookie "$name" not signed.}) }
-  }
-
-  return wantarray ? @results : $results[0];
+  # Response cookie
+  my $checksum
+    = Mojo::Util::hmac_sha1_sum($value, $self->stash->{'mojo.secrets'}[0]);
+  return $self->cookie($name, "$value--$checksum", $options);
 }
 
-sub stash { shift->Mojolicious::_dict(stash => @_) }
+sub stash { Mojo::Util::_stash(stash => @_) }
 
 sub url_for {
   my $self = shift;
@@ -383,48 +356,46 @@ sub write_chunk {
   return $self->rendered;
 }
 
-sub _development {
-  my ($page, $self, $e) = @_;
+sub _param {
+  my ($self, $name) = @_;
 
-  my $app = $self->app;
-  $app->log->error($e = Mojo::Exception->new($e)) if $page eq 'exception';
+  # Captured unreserved values
+  my $captures = $self->stash->{'mojo.captures'} ||= {};
+  if (!$RESERVED{$name} && defined(my $value = $captures->{$name})) {
+    return ref $value eq 'ARRAY' ? $value : [$value];
+  }
 
-  # Filtered stash snapshot
-  my $stash = $self->stash;
-  my %snapshot = map { $_ => $stash->{$_} }
-    grep { !/^mojo\./ and defined $stash->{$_} } keys %$stash;
-
-  # Render with fallbacks
-  my $mode     = $app->mode;
-  my $renderer = $app->renderer;
-  my $options  = {
-    exception => $page eq 'exception' ? $e : undef,
-    format => $stash->{format} || $renderer->default_format,
-    handler  => undef,
-    snapshot => \%snapshot,
-    status   => $page eq 'exception' ? 500 : 404,
-    template => "$page.$mode"
-  };
-  my $inline = $renderer->_bundled($mode eq 'development' ? $mode : $page);
-  return $self if _fallbacks($self, $options, $page, $inline);
-  _fallbacks($self, {%$options, format => 'html'}, $page, $inline);
-  return $self;
+  # Uploads or param values
+  my $req     = $self->req;
+  my $uploads = $req->every_upload($name);
+  return @$uploads ? $uploads : $req->every_param($name);
 }
 
-sub _fallbacks {
-  my ($self, $options, $template, $inline) = @_;
+sub _signed_cookie {
+  my ($self, $name) = @_;
+
+  my $secrets = $self->stash->{'mojo.secrets'};
+  my @results;
+  for my $value (@{$self->every_cookie($name)}) {
 
-  # Mode specific template
-  return 1 if $self->render_maybe(%$options);
+    # Check signature with rotating secrets
+    if ($value =~ s/--([^\-]+)$//) {
+      my $signature = $1;
 
-  # Normal template
-  return 1 if $self->render_maybe(%$options, template => $template);
+      my $valid;
+      for my $secret (@$secrets) {
+        my $check = Mojo::Util::hmac_sha1_sum($value, $secret);
+        ++$valid and last if Mojo::Util::secure_compare($signature, $check);
+      }
+      if ($valid) { push @results, $value }
 
-  # Inline template
-  my $stash = $self->stash;
-  return undef unless $stash->{format} eq 'html';
-  delete @$stash{qw(extends layout)};
-  return $self->render_maybe(%$options, inline => $inline, handler => 'ep');
+      else { $self->app->log->debug(qq{Cookie "$name" has bad signature.}) }
+    }
+
+    else { $self->app->log->debug(qq{Cookie "$name" not signed.}) }
+  }
+
+  return \@results;
 }
 
 1;
@@ -438,7 +409,7 @@ Mojolicious::Controller - Controller base class
 =head1 SYNOPSIS
 
   # Controller
-  package MyApp::Foo;
+  package MyApp::Controller::Foo;
   use Mojo::Base 'Mojolicious::Controller';
 
   # Action
@@ -483,8 +454,8 @@ Router results for the current request, defaults to a
 L<Mojolicious::Routes::Match> object.
 
   # Introspect
-  my $foo = $c->match->endpoint->pattern->defaults->{foo};
-  my $bar = $c->match->stack->[-1]{bar};
+  my $controller = $c->match->endpoint->pattern->defaults->{controller};
+  my $action     = $c->match->stack->[-1]{action};
 
 =head2 tx
 
@@ -520,17 +491,48 @@ Continue dispatch chain with L<Mojolicious::Routes/"continue">.
 
 =head2 cookie
 
-  my $foo         = $c->cookie('foo');
-  my @foo         = $c->cookie('foo');
+  my $value       = $c->cookie('foo');
   my ($foo, $bar) = $c->cookie(['foo', 'bar']);
   $c              = $c->cookie(foo => 'bar');
   $c              = $c->cookie(foo => 'bar', {path => '/'});
 
-Access request cookie values and create new response cookies.
+Access request cookie values and create new response cookies. If there are
+multiple values sharing the same name, and you want to access more than just
+the last one, you can use L</"every_cookie">.
 
   # Create response cookie with domain and expiration date
   $c->cookie(user => 'sri', {domain => 'example.com', expires => time + 60});
 
+=head2 every_cookie
+
+  my $values = $c->every_cookie('foo');
+
+Similar to L</"cookie">, but returns all request cookie values sharing the
+same name as an array reference.
+
+  $ Get first cookie value
+  my $first = $c->every_cookie('foo')->[0];
+
+=head2 every_param
+
+  my $values = $c->every_param('foo');
+
+Similar to L</"param">, but returns all values sharing the same name as an
+array reference.
+
+  # Get first value
+  my $first = $c->every_param('foo')->[0];
+
+=head2 every_signed_cookie
+
+  my $values = $c->every_signed_cookie('foo');
+
+Similar to L</"signed_cookie">, but returns all signed request cookie values
+sharing the same name as an array reference.
+
+  # Get first signed cookie value
+  my $first = $c->every_signed_cookie('foo')->[0];
+
 =head2 finish
 
   $c = $c->finish;
@@ -553,6 +555,17 @@ L</"session">.
   $c->flash(message => 'User created successfully!');
   $c->redirect_to('show_user', id => 23);
 
+=head2 helpers
+
+  my $helpers = $c->helpers;
+
+Return a proxy object containing the current controller object and on which
+helpers provided by L</"app"> can be called. This includes all helpers from
+L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
+
+  # Make sure to use the "title" helper and not the controller method
+  $c->helpers->title('Welcome!');
+
 =head2 on
 
   my $cb = $c->on(finish => sub {...});
@@ -590,30 +603,23 @@ status.
 =head2 param
 
   my @names       = $c->param;
-  my $foo         = $c->param('foo');
-  my @foo         = $c->param('foo');
+  my $value       = $c->param('foo');
   my ($foo, $bar) = $c->param(['foo', 'bar']);
   $c              = $c->param(foo => 'ba;r');
   $c              = $c->param(foo => qw(ba;r baz));
   $c              = $c->param(foo => ['ba;r', 'baz']);
 
 Access route placeholder values that are not reserved stash values, file
-uploads and C<GET>/C<POST> parameters, in that order. Note that this method is
-context sensitive in some cases and therefore needs to be used with care,
-there can always be multiple values, which might have unexpected consequences.
-Parts of the request body need to be loaded into memory to parse C<POST>
-parameters, so you have to make sure it is not excessively large, there's a
-10MB limit by default.
-
-  # List context is ambiguous and should be avoided, you can get multiple
-  # values returned for a query string like "?foo=bar&foo=baz&foo=yada"
-  my $hash = {foo => $c->param('foo')};
-
-  # Better enforce scalar context
-  my $hash = {foo => scalar $c->param('foo')};
+uploads as well as C<GET> and C<POST> parameters extracted from the query
+string and C<application/x-www-form-urlencoded> or C<multipart/form-data>
+message body, in that order. If there are multiple values sharing the same
+name, and you want to access more than just the last one, you can use
+L</"every_param">. Parts of the request body need to be loaded into memory to
+parse C<POST> parameters, so you have to make sure it is not excessively
+large, there's a 10MB limit by default.
 
-  # The multi-name form can also be used to enforce scalar context
-  my $hash = {foo => $c->param(['foo'])};
+  # Get first value
+  my $first = $c->every_param('foo')->[0];
 
 For more control you can also access request information directly.
 
@@ -654,7 +660,7 @@ Prepare a C<302> redirect response, takes the same arguments as L</"url_for">.
   my $bool = $c->render(handler => 'something');
   my $bool = $c->render('foo/index');
 
-Render content using L<Mojolicious::Renderer/"render"> and emit hooks
+Render content with L<Mojolicious::Renderer/"render"> and emit hooks
 L<Mojolicious/"before_render"> as well as L<Mojolicious/"after_render">. If no
 template is provided a default one based on controller and action or route
 name will be generated with L<Mojolicious::Renderer/"template_for">, all
@@ -684,10 +690,7 @@ additional pairs get merged into the L</"stash">.
   $c = $c->render_exception('Oops!');
   $c = $c->render_exception(Mojo::Exception->new('Oops!'));
 
-Render the exception template C<exception.$mode.$format.*> or
-C<exception.$format.*> and set the response status code to C<500>. Also sets
-the stash values C<exception> to a L<Mojo::Exception> object and C<snapshot>
-to a copy of the L</"stash"> for use in the templates.
+Alias for L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>exception">.
 
 =head2 render_later
 
@@ -708,7 +711,8 @@ automatic rendering would result in a response.
   my $bool = $c->render_maybe(controller => 'foo', action => 'bar');
   my $bool = $c->render_maybe('foo/index', format => 'html');
 
-Try to render content, but do not call L</"render_not_found"> if no response
+Try to render content, but do not call
+L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>not_found"> if no response
 could be generated, takes the same arguments as L</"render">.
 
   # Render template "index_local" only if it exists
@@ -718,19 +722,7 @@ could be generated, takes the same arguments as L</"render">.
 
   $c = $c->render_not_found;
 
-Render the not found template C<not_found.$mode.$format.*> or
-C<not_found.$format.*> and set the response status code to C<404>. Also sets
-the stash value C<snapshot> to a copy of the L</"stash"> for use in the
-templates.
-
-=head2 render_static
-
-  my $bool = $c->render_static('images/logo.png');
-  my $bool = $c->render_static('../lib/MyApp.pm');
-
-Render a static file using L<Mojolicious::Static/"serve">, usually from the
-C<public> directories or C<DATA> sections of your application. Note that this
-method does not protect from traversing to parent directories.
+Alias for L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>not_found">.
 
 =head2 render_to_string
 
@@ -761,7 +753,7 @@ using a C<200> response code.
 
   my $req = $c->req;
 
-Get L<Mojo::Message::Request> object from L<Mojo::Transaction/"req">.
+Get L<Mojo::Message::Request> object from L</"tx">.
 
   # Longer version
   my $req = $c->tx->req;
@@ -783,7 +775,7 @@ Get L<Mojo::Message::Request> object from L<Mojo::Transaction/"req">.
 
   my $res = $c->res;
 
-Get L<Mojo::Message::Response> object from L<Mojo::Transaction/"res">.
+Get L<Mojo::Message::Response> object from L</"tx">.
 
   # Longer version
   my $res = $c->tx->res;
@@ -883,15 +875,15 @@ on browser.
 
 =head2 signed_cookie
 
-  my $foo         = $c->signed_cookie('foo');
-  my @foo         = $c->signed_cookie('foo');
+  my $value       = $c->signed_cookie('foo');
   my ($foo, $bar) = $c->signed_cookie(['foo', 'bar']);
   $c              = $c->signed_cookie(foo => 'bar');
   $c              = $c->signed_cookie(foo => 'bar', {path => '/'});
 
-Access signed request cookie values and create new signed response cookies.
-Cookies failing HMAC-SHA1 signature verification will be automatically
-discarded.
+Access signed request cookie values and create new signed response cookies. If
+there are multiple values sharing the same name, and you want to access more
+than just the last one, you can use L</"every_signed_cookie">. Cookies failing
+HMAC-SHA1 signature verification will be automatically discarded.
 
 =head2 stash
 
@@ -945,9 +937,11 @@ to inherit query parameters from the current request.
   my $validation = $c->validation;
 
 Get L<Mojolicious::Validator::Validation> object for current request to
-validate C<GET>/C<POST> parameters. Parts of the request body need to be
-loaded into memory to parse C<POST> parameters, so you have to make sure it is
-not excessively large, there's a 10MB limit by default.
+validate C<GET> and C<POST> parameters extracted from the query string and
+C<application/x-www-form-urlencoded> or C<multipart/form-data> message body.
+Parts of the request body need to be loaded into memory to parse C<POST>
+parameters, so you have to make sure it is not excessively large, there's a
+10MB limit by default.
 
   my $validation = $c->validation;
   $validation->required('title')->size(3, 50);
@@ -1022,9 +1016,13 @@ helpers provided by L</"app"> on L<Mojolicious::Controller> objects. This
 includes all helpers from L<Mojolicious::Plugin::DefaultHelpers> and
 L<Mojolicious::Plugin::TagHelpers>.
 
+  # Call helpers
   $c->layout('green');
   $c->title('Welcome!');
 
+  # Longer version
+  $c->helpers->layout('green');
+
 =head1 SEE ALSO
 
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
@@ -38,16 +38,6 @@ Please report security issues directly to the CPAN email address of the
 pumpkin-holder, which is currently C<sri@cpan.org>, and give us a few days to
 develop and release a proper fix.
 
-=head2 Feature requests
-
-Please do not open GitHub issues for feature requests, if there's something
-you would like to see in a future version of L<Mojolicious>, you have to write
-the code yourself.
-
-If you're looking for feedback on your ideas, you're welcome to discuss them
-on the L<mailing-list|http://groups.google.com/group/mojolicious> or the
-official IRC channel C<#mojo> on C<irc.perl.org>.
-
 =head1 RESOLVING ISSUES
 
 There are many ways in which you can help us resolve existing issues on the
@@ -136,8 +126,8 @@ It's not a feature without a test and documentation.
 
 A feature is only needed when the majority of the user base benefits from it.
 
-Features may only be changed in a major release or after being deprecated for
-at least 3 months.
+Features may only be changed in a major release, to fix a serious security
+issue, or after being deprecated for at least 3 months.
 
 Refactoring and deprecations should be avoided if no important feature depends
 on it.
@@ -85,7 +85,7 @@ It is basically a restarter that forks a new L<Mojo::Server::Daemon> web
 server whenever a file in your project changes, and should therefore only be
 used during development.
 
-  $ morbo script/myapp
+  $ morbo ./script/myapp
   Server available at http://127.0.0.1:3000.
 
 =head2 Hypnotoad
@@ -105,12 +105,12 @@ It is based on the L<Mojo::Server::Prefork> web server, which adds preforking
 to L<Mojo::Server::Daemon>, but optimized specifically for production
 environments out of the box.
 
-  $ hypnotoad script/myapp
+  $ hypnotoad ./script/myapp
   Server available at http://127.0.0.1:8080.
 
-You can tweak many configuration settings right from within your application
-with L<Mojo/"config">, for a full list see
-L<Mojo::Server::Hypnotoad/"SETTINGS">.
+It automatically sets the operating mode to C<production> and you can tweak
+many configuration settings right from within your application with
+L<Mojo/"config">, for a full list see L<Mojo::Server::Hypnotoad/"SETTINGS">.
 
   use Mojolicious::Lite;
 
@@ -136,7 +136,7 @@ software upgrades (hot deployment). That means you can upgrade L<Mojolicious>,
 Perl or even system libraries at runtime without ever stopping the server or
 losing a single incoming connection, just by running the command above again.
 
-  $ hypnotoad script/myapp
+  $ hypnotoad ./script/myapp
   Starting hot deployment for Hypnotoad server 31841.
 
 You might also want to enable proxy support if you're using Hypnotoad behind a
@@ -238,8 +238,8 @@ C<X-Forwarded-For> and C<X-Forwarded-Proto> headers.
 
   $ MOJO_REVERSE_PROXY=1 plackup ./script/myapp
 
-If an older server adapter is not be able to correctly detect the application
-home directory, you can simply use the C<MOJO_HOME> environment variable.
+If an older server adapter is unable to correctly detect the application home
+directory, you can simply use the C<MOJO_HOME> environment variable.
 
   $ MOJO_HOME=/home/sri/myapp plackup ./script/myapp
 
@@ -257,7 +257,22 @@ application logic.
 
   builder {
     enable 'Deflater';
-    require 'myapp.pl';
+    require './script/myapp';
+  };
+
+L<Mojo::Server::PSGI> can be used directly to load and customize applications
+in the wrapper script.
+
+  #!/usr/bin/env plackup -s FCGI
+  use Mojo::Server::PSGI;
+  use Plack::Builder;
+
+  builder {
+    enable 'Deflater';
+    my $server = Mojo::Server::PSGI->new;
+    $server->load_app('./script/myapp');
+    $server->app->config(foo => 'bar');
+    $server->to_psgi_app;
   };
 
 But you could even use middleware right in your application.
@@ -284,7 +299,7 @@ you can use the hook L<Mojolicious/"before_dispatch"> to rewrite incoming
 requests.
 
   # Change scheme if "X-Forwarded-HTTPS" header is set
-  app->hook(before_dispatch => sub {
+  $app->hook(before_dispatch => sub {
     my $c = shift;
     $c->req->url->base->scheme('https')
       if $c->req->headers->header('X-Forwarded-HTTPS');
@@ -295,11 +310,11 @@ prefixes your application might be deployed under, rewriting the base path of
 incoming requests is also quite common.
 
   # Move first part and slash from path to base path in production mode
-  app->hook(before_dispatch => sub {
+  $app->hook(before_dispatch => sub {
     my $c = shift;
     push @{$c->req->url->base->path->trailing_slash(1)},
       shift @{$c->req->url->path->leading_slash(0)};
-  }) if app->mode eq 'production';
+  }) if $app->mode eq 'production';
 
 L<Mojo::URL> objects are very easy to manipulate, just make sure that the URL
 (C<foo/bar?baz=yada>), which represents the routing destination, is always
@@ -404,7 +419,7 @@ style.
   use Mojolicious::Lite;
   use Mojo::URL;
 
-  # Search MetaCPAN for "mojo" and "mango"
+  # Search MetaCPAN for "mojo" and "minion"
   get '/' => sub {
     my $c = shift;
 
@@ -416,16 +431,16 @@ style.
         my $delay = shift;
         my $url   = Mojo::URL->new('api.metacpan.org/v0/module/_search');
         $url->query({sort => 'date:desc'});
-        $c->ua->get($url->clone->query({q => 'mojo'})  => $delay->begin);
-        $c->ua->get($url->clone->query({q => 'mango'}) => $delay->begin);
+        $c->ua->get($url->clone->query({q => 'mojo'})   => $delay->begin);
+        $c->ua->get($url->clone->query({q => 'minion'}) => $delay->begin);
       },
 
       # Delayed rendering
       sub {
-        my ($delay, $mojo, $mango) = @_;
+        my ($delay, $mojo, $minion) = @_;
         $c->render(json => {
-          mojo  => $mojo->res->json('/hits/hits/0/_source/release'),
-          mango => $mango->res->json('/hits/hits/0/_source/release')
+          mojo   => $mojo->res->json('/hits/hits/0/_source/release'),
+          minion => $minion->res->json('/hits/hits/0/_source/release')
         });
       }
     );
@@ -500,9 +515,19 @@ created at startup time.
 
   app->start;
 
-Since timers and other low-level event watchers are also independent from
-applications, errors can't get logged automatically, you can change that by
-subscribing to the event L<Mojo::Reactor/"error">.
+Just remember that all events are processed cooperatively, so your callbacks
+shouldn't block for too long.
+
+=head2 Exceptions in events
+
+Since timers and other non-blocking operations are running solely in the event
+loop, outside of the application, exceptions that get thrown in callbacks
+can't get caught and handled automatically. But you can handle them manually
+by subscribing to the event L<Mojo::Reactor/"error"> or catching them inside
+the callback.
+
+  use Mojolicious::Lite;
+  use Mojo::IOLoop;
 
   # Forward error messages to the application log
   Mojo::IOLoop->singleton->reactor->on(error => sub {
@@ -510,8 +535,33 @@ subscribing to the event L<Mojo::Reactor/"error">.
     app->log->error($err);
   });
 
-Just remember that all events are processed cooperatively, so your callbacks
-shouldn't block for too long.
+  # Exception only gets logged (and connection times out)
+  get '/connection_times_out' => sub {
+    my $c = shift;
+    Mojo::IOLoop->timer(2 => sub {
+      die 'This request will not be getting a response';
+    });
+  };
+
+  # Exception gets caught and handled
+  get '/catch_exception' => sub {
+    my $c = shift;
+    Mojo::IOLoop->timer(2 => sub {
+      eval { die 'This request will be getting a response' };
+      $c->reply->exception($@) if $@;
+    });
+  };
+
+  app->start;
+
+A default subscriber that turns all errors into warnings will usually be added
+by L<Mojo::IOLoop> as a fallback.
+
+  Mojo::IOLoop->singleton->reactor->unsubscribe('error');
+
+During development or for applications where crashing is simply preferable,
+you can also make every exception that gets thrown in a callback fatal by
+removing all of its subscribers.
 
 =head2 WebSocket web service
 
@@ -566,9 +616,7 @@ L<Mojolicious::Controller/"send">.
         };
 
         // Outgoing messages
-        window.setInterval(function() {
-          ws.send('Hello Mojo!');
-        }, 1000);
+        window.setInterval(function () { ws.send('Hello Mojo!') }, 1000);
       </script>
     </body>
   </html>
@@ -899,7 +947,7 @@ content repeatedly for multiple requests.
 
   # Send multiple files streaming via PUT and POST
   $ua->put('http://example.com/upload'  => stream => '/home/sri/mojo.png');
-  $ua->post('http://example.com/upload' => stream => '/home/sri/mango.png');
+  $ua->post('http://example.com/upload' => stream => '/home/sri/minion.png');
 
 The C<json> and C<form> content generators are always available.
 
@@ -933,7 +981,7 @@ file with L<Mojo::Asset::File/"move_to">.
 
   # Lets fetch the latest Mojolicious tarball
   my $ua = Mojo::UserAgent->new(max_redirects => 5);
-  my $tx = $ua->get('latest.mojolicio.us');
+  my $tx = $ua->get('https://www.github.com/kraih/mojo/tarball/master');
   $tx->res->content->asset->move_to('mojo.tar.gz');
 
 To protect you from excessively large files there is also a limit of 10MB by
@@ -1029,11 +1077,11 @@ can keep many concurrent connections active at the same time.
   my $ua = Mojo::UserAgent->new;
   $ua->get('http://metacpan.org/search?q=mojo' => sub {
     my ($ua, $mojo) = @_;
-    ...
+    say $mojo->res->dom->at('title')->text;
   });
-  $ua->get('http://metacpan.org/search?q=mango' => sub {
-    my ($ua, $mango) = @_;
-    ...
+  $ua->get('http://metacpan.org/search?q=minion' => sub {
+    my ($ua, $minion) = @_;
+    say $minion->res->dom->at('title')->text;
   });
 
   # Start event loop if necessary
@@ -1052,11 +1100,12 @@ synchronize multiple non-blocking requests.
   # Synchronize non-blocking requests
   my $ua    = Mojo::UserAgent->new;
   my $delay = Mojo::IOLoop->delay(sub {
-    my ($delay, $mojo, $mango) = @_;
-    ...
+    my ($delay, $mojo, $minion) = @_;
+    say $mojo->res->dom->at('title')->text;
+    say $minion->res->dom->at('title')->text;
   });
-  $ua->get('http://metacpan.org/search?q=mojo'  => $delay->begin);
-  $ua->get('http://metacpan.org/search?q=mango' => $delay->begin);
+  $ua->get('http://metacpan.org/search?q=mojo'   => $delay->begin);
+  $ua->get('http://metacpan.org/search?q=minion' => $delay->begin);
   $delay->wait;
 
 The call to L<Mojo::IOLoop::Delay/"wait"> makes this code portable, it can now
@@ -1149,7 +1198,7 @@ This can be an invaluable tool for testing your applications.
 For quick hacks and especially testing, L<ojo> one-liners are also a great
 choice.
 
-  $ perl -Mojo -E 'say g("mojolicio.us")->dom->html->head->title->text'
+  $ perl -Mojo -E 'say g("mojolicio.us")->dom->at("title")->text'
 
 =head1 APPLICATIONS
 
@@ -1178,7 +1227,7 @@ C<Authorization> header.
 
 This can be combined with TLS for a secure authentication mechanism.
 
-  $ ./myapp.pl daemon -l https://*:3000?cert=./server.crt&key=./server.key
+  $ ./myapp.pl daemon -l 'https://*:3000?cert=./server.crt&key=./server.key'
 
 =head2 Adding a configuration file
 
@@ -1218,6 +1267,52 @@ the helper L<Mojolicious::Plugin::DefaultHelpers/"config">
 Alternatively you can also use configuration files in the JSON format with
 L<Mojolicious::Plugin::JSONConfig>.
 
+=head2 Adding a plugin to your application
+
+To organize your code better and to prevent helpers from cluttering your
+application, you can use application specific plugins.
+
+  $ mkdir -p lib/MyApp/Plugin
+  $ touch lib/MyApp/Plugin/MyHelpers.pm
+
+They work just like normal plugins and are also subclasses of
+L<Mojolicious::Plugin>. Nested helpers with a prefix based on the plugin name
+are an easy way to avoid conflicts.
+
+  package MyApp::Plugin::MyHelpers;
+  use Mojo::Base 'Mojolicious::Plugin';
+
+  sub register {
+    my ($self, $app) = @_;
+    $app->helper('my_helpers.render_with_header' => sub {
+      my ($c, @args) = @_;
+      $c->res->headers->header('X-Mojo' => 'I <3 Mojolicious!');
+      $c->render(@args);
+    });
+  }
+
+  1;
+
+You can have as many application specific plugins as you like, the only
+difference to normal plugins is that you load them using their full class
+name.
+
+  use Mojolicious::Lite;
+
+  use lib 'lib';
+
+  plugin 'MyApp::Plugin::MyHelpers';
+
+  get '/' => sub {
+    my $c = shift;
+    $c->my_helpers->render_with_header(text => 'I ♥ Mojolicious!');
+  };
+
+  app->start;
+
+Of course these plugins can contain more than just helpers, take a look at
+L<Mojolicious::Plugins/"PLUGINS"> for a few ideas.
+
 =head2 Adding commands to Mojolicious
 
 By now you've probably used many of the built-in commands described in
@@ -1235,6 +1330,9 @@ that they will be picked up automatically by the command line interface?
 
     # Leak secret passphrases
     say for @{$self->app->secrets} if $args[0] eq 'secrets';
+
+    # Leak mode
+    say $self->app->mode if $args[0] eq 'mode';
   }
 
   1;
@@ -1265,6 +1363,14 @@ L<Mojolicious::Commands/"namespaces">.
 
   1;
 
+Some options like C<-m> for the operating mode of your application are shared
+by all commands automatically.
+
+  $ ./myapp.pl spy -m production mode
+  production
+
+For a full list of shared options see L<Mojolicious::Commands/"SYNOPSIS">.
+
 =head2 Running code against your application
 
 Ever thought about running a quick one-liner against your L<Mojolicious>
@@ -16,7 +16,7 @@ We hope these answers are to your satisfaction.
 
 =head2 How does Mojolicious compare to other Perl web frameworks?
 
-The short answer is "it doesn't", because we interpret the words
+The short answer is "it doesn't", because we interpret the term
 "web framework" much more literally than others. With the emergence of the
 real-time web and new technologies such as WebSockets, we are facing new
 challenges that go way beyond what commonly used modules like L<LWP> were
@@ -24,8 +24,8 @@ designed for. Because of this, L<Mojolicious> contains a whole new HTTP
 client/server stack called L<Mojo>, which was heavily inspired by the original
 LWPng effort and carefully designed with these new requirements in mind. So
 while some of the higher abstraction layers might look similar to other web
-frameworks, it actually defines a whole new category and could even be the
-foundation for more advanced ones in the future.
+frameworks, it is more of a web toolkit and can even be used as the foundation
+for more advanced web frameworks.
 
 =head2 Why doesn't Mojolicious have any dependencies?
 
@@ -34,8 +34,9 @@ without compromises. While there are no rules in
 L<Mojolicious::Guides::Contributing> that forbid dependencies, we do currently
 discourage adding non-optional ones in favor of a faster and more painless
 installation process. And we do in fact already use several optional CPAN
-modules such as L<EV>, L<IO::Socket::IP>, L<IO::Socket::SSL> and L<Plack> to
-provide advanced functionality if they are installed.
+modules such as L<EV>, L<IO::Socket::Socks>, L<IO::Socket::SSL>,
+L<Net::DNS::Native> and L<Plack> to provide advanced functionality if they are
+installed.
 
 =head2 Why reinvent wheels?
 
@@ -172,11 +173,11 @@ one.
 =head2 What does "Your secret passphrase needs to be changed" mean?
 
 L<Mojolicious> uses secret passphrases for security features such as signed
-cookies. It defaults to using the moniker of your application, which is not
-very secure, so we added this log message as a reminder. You can change the
+cookies. It defaults to using L<Mojolicious/"moniker">, which is not very
+secure, so we added this log message as a reminder. You can change the
 passphrase with the attribute L<Mojolicious/"secrets">.
 
-  app->secrets(['My very secret passphrase.']);
+  $app->secrets(['My very secret passphrase.']);
 
 =head2 What does "Nothing has been rendered, expecting delayed response" mean?
 
@@ -8,7 +8,7 @@ Mojolicious::Guides::Growing - Growing
 =head1 OVERVIEW
 
 This document explains the process of starting a L<Mojolicious::Lite>
-prototype from scratch and growing it into a well structured L<Mojolicious>
+prototype from scratch and growing it into a well-structured L<Mojolicious>
 application.
 
 =head1 CONCEPTS
@@ -90,13 +90,13 @@ about previous requests, which makes user-friendly login systems very tricky.
 Sessions solve this problem by allowing web applications to keep stateful
 information across several HTTP requests.
 
-  GET /login?user=sri&pass=s3cret HTTP/1.1
+  GET /login?user=sebastian&pass=s3cret HTTP/1.1
   Host: mojolicio.us
 
   HTTP/1.1 200 OK
   Set-Cookie: sessionid=987654321
   Content-Length: 10
-  Hello sri.
+  Hello sebastian.
 
   GET /protected HTTP/1.1
   Host: mojolicio.us
@@ -105,7 +105,7 @@ information across several HTTP requests.
   HTTP/1.1 200 OK
   Set-Cookie: sessionid=987654321
   Content-Length: 16
-  Hello again sri.
+  Hello again sebastian.
 
 Traditionally all session data was stored on the server-side and only session
 ids were exchanged between browser and web server in the form of cookies.
@@ -149,7 +149,8 @@ organized CPAN distribution to maximize maintainability.
   |- lib                     # Library directory
   |  |- MyApp.pm             # Application class
   |  +- MyApp                # Application namespace
-  |     +- Example.pm        # Controller class
+  |     +- Controller        # Controller namespace
+  |        +- Example.pm     # Controller class
   |- t                       # Test directory
   |  +- basic.t              # Random test
   |- log                     # Log directory
@@ -169,6 +170,9 @@ L<Mojolicious::Command::generate::app>.
   $ mojo generate lite_app myapp.pl
   $ mojo generate app MyApp
 
+Feature-wise both are almost equal, the only real differences are
+organizational, so each one can be gradually transformed into the other.
+
 =head2 Foundation
 
 We start our new application with a single executable Perl script.
@@ -193,34 +197,62 @@ This will be the foundation for our login manager example application.
 The built-in development web server makes working on your application a lot of
 fun thanks to automatic reloading.
 
-  $ morbo myapp.pl
+  $ morbo ./myapp.pl
   Server available at http://127.0.0.1:3000.
 
 Just save your changes and they will be automatically in effect the next time
 you refresh your browser.
 
+=head2 A birds-eye view
+
+It all starts with an HTTP request like this, sent by your browser.
+
+  GET / HTTP/1.1
+  Host: localhost:3000
+
+Once the request has been received by the web server through the event loop,
+it will be passed on to L<Mojolicious>, where it will be handled in a few
+simple steps.
+
+  1. Check if a static file exists that would meet the requirements.
+  2. Try to find a route that would meet the requirements.
+  3. Dispatch the request to this route, usually reaching one or more actions.
+  4. Process the request, maybe generating a response with the renderer.
+  5. Return control to the web server, and if no response has been generated
+     yet, wait for a non-blocking operation to do so through the event loop.
+
+With our application the router would have found an action in step 2, and
+rendered some text in step 4, resulting in an HTTP response like this being
+sent back to the browser.
+
+  HTTP/1.1 200 OK
+  Content-Length: 12
+  Hello world!
+
 =head2 Model
 
 In L<Mojolicious> we consider web applications simple frontends for existing
-business logic, that means L<Mojolicious> is by design entirely L<model> layer
+business logic, that means L<Mojolicious> is by design entirely I<model> layer
 agnostic and you just use whatever Perl modules you like most.
 
-  $ mkdir lib
-  $ touch lib/MyUsers.pm
-  $ chmod 644 lib/MyUsers.pm
+  $ mkdir -p lib/MyApp/Model
+  $ touch lib/MyApp/Model/Users.pm
+  $ chmod 644 lib/MyApp/Model/Users.pm
 
 Our login manager will simply use a plain old Perl module abstracting away all
-logic related to matching usernames and passwords.
+logic related to matching usernames and passwords. The name
+C<MyApp::Model::Users> is an arbitrary choice, and is simply used to make the
+separation of concerns more visible.
 
-  package MyUsers;
+  package MyApp::Model::Users;
 
   use strict;
   use warnings;
 
   my $USERS = {
-    sri    => 'secr3t',
-    marcus => 'lulz',
-    yko    => 'zeecaptain'
+    joel      => 'las3rs',
+    marcus    => 'lulz',
+    sebastian => 'secr3t'
   };
 
   sub new { bless {}, shift }
@@ -245,12 +277,12 @@ templates.
   use Mojolicious::Lite;
 
   use lib 'lib';
-  use MyUsers;
+  use MyApp::Model::Users;
 
   # Helper to lazy initialize and store our model object
-  helper users => sub { state $users = MyUsers->new };
+  helper users => sub { state $users = MyApp::Model::Users->new };
 
-  # /?user=sri&pass=secr3t
+  # /?user=sebastian&pass=secr3t
   any '/' => sub {
     my $c = shift;
 
@@ -304,8 +336,8 @@ on L<Mojo::DOM>.
     ->element_exists('form input[type="submit"]');
 
   # Test login with valid credentials
-  $t->post_ok('/' => form => {user => 'sri', pass => 'secr3t'})
-    ->status_is(200)->text_like('html body' => qr/Welcome sri/);
+  $t->post_ok('/' => form => {user => 'sebastian', pass => 'secr3t'})
+    ->status_is(200)->text_like('html body' => qr/Welcome sebastian/);
 
   # Test accessing a protected page
   $t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/);
@@ -331,8 +363,8 @@ L<Mojolicious::Command::get>.
   $ ./myapp.pl get /
   Wrong username or password.
 
-  $ ./myapp.pl get -v '/?user=sri&pass=secr3t'
-  GET /?user=sri&pass=secr3t HTTP/1.1
+  $ ./myapp.pl get -v '/?user=sebastian&pass=secr3t'
+  GET /?user=sebastian&pass=secr3t HTTP/1.1
   User-Agent: Mojolicious (Perl)
   Connection: keep-alive
   Accept-Encoding: gzip
@@ -346,7 +378,7 @@ L<Mojolicious::Command::get>.
   Content-Length: 12
   Content-Type: text/plain
 
-  Welcome sri.
+  Welcome sebastian.
 
 =head2 State keeping
 
@@ -355,12 +387,12 @@ using the method L<Mojolicious::Controller/"session">, there is no setup
 required, but we suggest setting a more secure passphrase with
 L<Mojolicious/"secrets">.
 
-  app->secrets(['Mojolicious rocks']);
+  $app->secrets(['Mojolicious rocks']);
 
 This passphrase is used by the HMAC-SHA1 algorithm to make signed cookies
 secure and can be changed at any time to invalidate all existing sessions.
 
-  $c->session(user => 'sri');
+  $c->session(user => 'sebastian');
   my $user = $c->session('user');
 
 By default all sessions expire after one hour, for more control you can use
@@ -374,8 +406,9 @@ set an absolute expiration date in the past.
   $c->session(expires => 1);
 
 For data that should only be visible on the next request, like a confirmation
-message after a 302 redirect, you can use the flash, accessible through the
-method L<Mojolicious::Controller/"flash">.
+message after a C<302> redirect performed with
+L<Mojolicious::Controller/"redirect_to">, you can use the flash, accessible
+through the method L<Mojolicious::Controller/"flash">.
 
   $c->flash(message => 'Everything is fine.');
   $c->redirect_to('goodbye');
@@ -393,12 +426,12 @@ this.
   use Mojolicious::Lite;
 
   use lib 'lib';
-  use MyUsers;
+  use MyApp::Model::Users;
 
   # Make signed cookies secure
   app->secrets(['Mojolicious rocks']);
 
-  helper users => sub { state $users = MyUsers->new };
+  helper users => sub { state $users = MyApp::Model::Users->new };
 
   # Main login action
   any '/' => sub {
@@ -479,10 +512,21 @@ this.
     <body><%= content %></body>
   </html>
 
-A list of all built-in helpers can be found in
-L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
+And the directory structure should be looking like this now.
+
+  myapp
+  |- myapp.pl
+  |- lib
+  |  +- MyApp
+  |     +- Model
+  |        +- Users.pm
+  +- t
+     +- login.t
+
+Our templates are using quite a few features of the renderer,
+L<Mojolicious::Guides::Rendering> explains them all in great detail.
 
-=head1 WELL STRUCTURED APPLICATION
+=head1 WELL-STRUCTURED APPLICATION
 
 Due to the flexibility of L<Mojolicious> there are many variations of the
 actual growing process, but this should give you a good overview of the
@@ -496,8 +540,8 @@ directories with the command L<Mojolicious::Command::inflate>.
 
   $ ./myapp.pl inflate
 
-Those directories always get priority, so inflating can also be a great way to
-allow your users to customize their applications.
+Those directories have a higher precedence, so inflating can also be a great
+way to allow your users to customize their applications.
 
 =head2 Simplified application class
 
@@ -514,13 +558,13 @@ actual action code needs to be changed.
   package MyApp;
   use Mojo::Base 'Mojolicious';
 
-  use MyUsers;
+  use MyApp::Model::Users;
 
   sub startup {
     my $self = shift;
 
     $self->secrets(['Mojolicious rocks']);
-    $self->helper(users => sub { state $users = MyUsers->new });
+    $self->helper(users => sub { state $users = MyApp::Model::Users->new });
 
     my $r = $self->routes;
 
@@ -573,19 +617,37 @@ allow running tests again.
   # Start command line interface for application
   Mojolicious::Commands->start_app('MyApp');
 
+And the directory structure of our hybrid application should be looking like
+this.
+
+  myapp
+  |- myapp.pl
+  |- lib
+  |  |- MyApp.pm
+  |  +- MyApp
+  |     +- Model
+  |        +- Users.pm
+  |- t
+  |  +- login.t
+  +- templates
+     |- layouts
+     |  +- default.html.ep
+     |- index.html.ep
+     +- protected.html.ep
+
 =head2 Controller class
 
 Hybrid routes are a nice intermediate step, but to maximize maintainability it
 makes sense to split our action code from its routing information.
 
-  $ mkdir lib/MyApp
-  $ touch lib/MyApp/Login.pm
-  $ chmod 644 lib/MyApp/Login.pm
+  $ mkdir lib/MyApp/Controller
+  $ touch lib/MyApp/Controller/Login.pm
+  $ chmod 644 lib/MyApp/Controller/Login.pm
 
 Once again the actual action code does not need to change, we just rename
 C<$c> to C<$self> since the controller is now the invocant.
 
-  package MyApp::Login;
+  package MyApp::Controller::Login;
   use Mojo::Base 'Mojolicious::Controller';
 
   sub index {
@@ -626,30 +688,32 @@ information.
   package MyApp;
   use Mojo::Base 'Mojolicious';
 
-  use MyUsers;
+  use MyApp::Model::Users;
 
   sub startup {
     my $self = shift;
 
     $self->secrets(['Mojolicious rocks']);
-    $self->helper(users => sub { state $users = MyUsers->new });
+    $self->helper(users => sub { state $users = MyApp::Model::Users->new });
 
     my $r = $self->routes;
     $r->any('/')->to('login#index')->name('index');
-    my $logged_in = $r->under->to('login#logged_in');
+
+    my $logged_in = $r->under('/')->to('login#logged_in');
     $logged_in->get('/protected')->to('login#protected');
+
     $r->get('/logout')->to('login#logout');
   }
 
   1;
 
-L<Mojolicious::Routes> allows many route variations, choose whatever you like
-most.
+The router allows many different route variations,
+L<Mojolicious::Guides::Routing> explains them all in great detail.
 
 =head2 Templates
 
-Templates are usually bound to controllers, so they need to be moved into the
-appropriate directories.
+Templates are our views, and usually bound to controllers, so they need to be
+moved into the appropriate directories.
 
   $ mkdir templates/login
   $ mv templates/index.html.ep templates/login/index.html.ep
@@ -681,8 +745,8 @@ without breaking updated dual-life modules.
 
 =head2 Simplified tests
 
-Normal L<Mojolicious> applications are a little easier to test, so
-C<t/login.t> can be simplified.
+Full L<Mojolicious> applications are a little easier to test, so C<t/login.t>
+can be simplified.
 
   use Test::More;
   use Test::Mojo;
@@ -697,8 +761,8 @@ C<t/login.t> can be simplified.
     ->element_exists('form input[name="pass"]')
     ->element_exists('form input[type="submit"]');
 
-  $t->post_ok('/' => form => {user => 'sri', pass => 'secr3t'})
-    ->status_is(200)->text_like('html body' => qr/Welcome sri/);
+  $t->post_ok('/' => form => {user => 'sebastian', pass => 'secr3t'})
+    ->status_is(200)->text_like('html body' => qr/Welcome sebastian/);
 
   $t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/);
 
@@ -709,6 +773,27 @@ C<t/login.t> can be simplified.
 
   done_testing();
 
+And our final directory structure should be looking like this.
+
+  myapp
+  |- script
+  |  +- myapp
+  |- lib
+  |  |- MyApp.pm
+  |  +- MyApp
+  |     |- Controller
+  |     |  +- Login.pm
+  |     +- Model
+  |        +- Users.pm
+  |- t
+  |  +- login.t
+  +- templates
+     |- layouts
+     |  +- default.html.ep
+     +- login
+        |- index.html.ep
+        +- protected.html.ep
+
 Test-driven development takes a little getting used to, but can be a very
 powerful tool.
 
@@ -24,12 +24,19 @@ utilizing multiple template systems and data encoding modules.
 
 Templates can be automatically detected if enough information is provided by
 the developer or routes. Template names are expected to follow the
-C<name.format.handler> scheme, with C<name> defaulting to C<controller/action>
-or the route name, C<format> defaulting to C<html> and C<handler> to C<ep>.
+C<template.format.handler> scheme, with C<template> defaulting to
+C<controller/action> or the route name, C<format> defaulting to C<html> and
+C<handler> to C<ep>.
 
   {controller => 'users', action => 'list'} -> 'users/list.html.ep'
-  {name => 'foo', format => 'txt'}          -> 'foo.txt.ep'
-  {name => 'foo', handler => 'epl'}         -> 'foo.html.epl'
+  {template => 'foo', format => 'txt'}      -> 'foo.txt.ep'
+  {template => 'foo', handler => 'epl'}     -> 'foo.html.epl'
+
+The C<controller> value gets decamelized using L<Mojo::Util/"decamelize"> and
+C<-> characters replaced with C</>.
+
+  {controller => 'My::Users', action => 'add'} -> 'my/users/add.html.ep'
+  {controller => 'my-users', action => 'show'} -> 'my/users/show.html.ep'
 
 All templates should be in the C<templates> directories of the application,
 which can be customized with L<Mojolicious::Renderer/"paths">, or one of the
@@ -137,14 +144,19 @@ backslash.
   in multiple\\
   lines
 
-Stash values that don't have invalid characters in their name get
-automatically initialized as normal variables in the template, and the
-controller object as both C<$self> and C<$c>.
+At the beginning of the template, stash values that don't have invalid
+characters in their name get automatically initialized as normal variables,
+and the controller object as both C<$self> and C<$c>.
 
   $c->stash(name => 'tester');
 
   Hello <%= $name %> from <%= $c->tx->remote_address %>.
 
+A prefix like C<myapp.*> is commonly used for stash values that you don't want
+to expose in templates.
+
+  $c->stash('myapp.name' => 'tester');
+
 There are also many helper functions available, but more about that later.
 
   <%= dumper {foo => 'bar'} %>
@@ -243,7 +255,8 @@ too.
 =head2 Rendering text
 
 Characters can be rendered to bytes with the C<text> stash value, the given
-content will be automatically encoded to bytes.
+content will be automatically encoded with
+L<Mojolicious::Renderer/"encoding">.
 
   $c->render(text => 'I ♥ Mojolicious!');
 
@@ -295,16 +308,16 @@ These mappings can be easily extended or changed with L<Mojolicious/"types">.
 
 =head2 Stash data
 
-Any of the native Perl data types can be passed to templates through the
-L<Mojolicious::Controller/"stash">.
+Any of the native Perl data types can be passed to templates as references
+through the L<Mojolicious::Controller/"stash">.
 
   $c->stash(author     => 'Sebastian');
   $c->stash(frameworks => [qw(Catalyst Mojolicious)]);
-  $c->stash(examples   => {tweetylicious => 'a microblogging app'});
+  $c->stash(examples   => {convos => 'an IRC app'});
 
   %= $author
   %= $frameworks->[1]
-  %= $examples->{tweetylicious}
+  %= $examples->{convos}
 
 Since everything is just Perl normal control structures just work.
 
@@ -312,10 +325,51 @@ Since everything is just Perl normal control structures just work.
     <%= $framework %> was written by <%= $author %>.
   % }
 
-  % if (my $description = $examples->{tweetylicious}) {
-    Tweetylicious is a <%= $description %>.
+  % if (my $description = $examples->{convos}) {
+    Convos is a <%= $description %>.
   % }
 
+For templates that might get rendered in different ways and where you're not
+sure if a stash value will actually be set, you can just use the helper
+L<Mojolicious::Plugin::DefaultHelpers/"stash">.
+
+  % if (my $examples = stash 'examples') {
+    Convos is <%= $examples->{convos} %>.
+  % }
+
+=head2 Helpers
+
+Helpers are little functions you can use in templates as well as application
+and controller code.
+
+  # Template
+  %= dumper [1, 2, 3]
+
+  # Application
+  my $serialized = $app->dumper([1, 2, 3]);
+
+  # Controller
+  my $serialized = $c->dumper([1, 2, 3]);
+
+The helper L<Mojolicious::Plugin::DefaultHelpers/"dumper"> for example will
+use L<Data::Dumper> to serialize whatever data structure you pass it, this can
+be very useful for debugging. We differentiate between default helpers which
+are more general purpose like C<dumper> and tag helpers, which are template
+specific and mostly used to generate HTML tags.
+
+  %= link_to 'http://mojolicio.us' => begin
+    Mojolicious
+  % end
+
+In controllers you can also use the method
+L<Mojolicious::Controller/"helpers"> to fully qualify helper calls and ensure
+that they don't conflict with existing methods you may already have.
+
+  my $serialized = $c->helpers->dumper([1, 2, 3]);
+
+A list of all built-in helpers can be found in
+L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
+
 =head2 Content negotiation
 
 For resources with different representations and that require truly
@@ -392,8 +446,9 @@ By now you've probably already encountered the built-in 404 (Not Found) and
 500 (Server Error) pages, that get rendered automatically when you make a
 mistake. Those are fallbacks for when your own exception handling fails, but
 especially during development they can also be a great help, you can render
-them manually with the methods L<Mojolicious::Controller/"render_exception">
-and L<Mojolicious::Controller/"render_not_found">.
+them manually with the helpers
+L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>exception"> and
+L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>not_found">.
 
   use Mojolicious::Lite;
   use Scalar::Util 'looks_like_number';
@@ -403,11 +458,11 @@ and L<Mojolicious::Controller/"render_not_found">.
     my ($dividend, $divisor) = $c->param(['dividend', 'divisor']);
 
     # 404
-    return $c->render_not_found
+    return $c->reply->not_found
       unless looks_like_number $dividend && looks_like_number $divisor;
 
     # 500
-    return $c->render_exception('Division by zero!') if $divisor == 0;
+    return $c->reply->exception('Division by zero!') if $divisor == 0;
 
     # 200
     $c->render(text => $dividend / $divisor);
@@ -455,29 +510,6 @@ passed to the renderer.
 
   app->start;
 
-=head2 Helpers
-
-Helpers are little functions you can use in templates and controller code.
-
-  %= dumper [1, 2, 3]
-
-  my $serialized = $c->dumper([1, 2, 3]);
-
-The helper L<Mojolicious::Plugin::DefaultHelpers/"dumper"> for example will
-use L<Data::Dumper> to serialize whatever data structure you pass it, this can
-be very useful for debugging. We differentiate between default helpers which
-are more general purpose like C<dumper> and tag helpers, which are template
-specific and mostly used to generate HTML tags.
-
-  %= javascript '/script.js'
-
-  %= javascript begin
-    var a = 'b';
-  % end
-
-A list of all built-in helpers can be found in
-L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
-
 =head2 Layouts
 
 Most of the time when using C<ep> templates you will want to wrap your
@@ -609,6 +641,92 @@ A naive translation to Perl code could look like this.
   }
   return $output;
 
+=head2 Adding helpers
+
+You should always try to keep your actions small and reuse as much code as
+possible. Helpers make this very easy, you can use them to do pretty much
+anything an action could do.
+
+  use Mojolicious::Lite;
+
+  helper debug => sub {
+    my ($c, $str) = @_;
+    $c->app->log->debug($str);
+  };
+
+  get '/' => sub {
+    my $c = shift;
+    $c->debug('Hello from an action!');
+  } => 'index';
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  % debug 'Hello from a template!';
+
+Helpers can also accept template blocks as last argument, this for example
+allows very pleasant to use tag helpers and filters. Wrapping the helper
+result into a L<Mojo::ByteStream> object can prevent accidental double
+escaping.
+
+  use Mojolicious::Lite;
+  use Mojo::ByteStream;
+
+  helper trim_newline => sub {
+    my ($c, $block) = @_;
+    my $result = $block->();
+    $result =~ s/\n//g;
+    return Mojo::ByteStream->new($result);
+  };
+
+  get '/' => 'index';
+
+  app->start;
+  __DATA__
+
+  @@ index.html.ep
+  %= trim_newline begin
+    Some text.
+    %= 1 + 1
+    More text.
+  % end
+
+Similar to stash values you can use a prefix like C<myapp.*> to keep helpers
+from getting exposed in templates and to organize them into namespaces as your
+application grows. Every prefix automatically becomes a helper that returns a
+proxy object containing the current controller object and on which you can
+call the nested helpers.
+
+  use Mojolicious::Lite;
+
+  helper 'cache_control.no_caching' => sub {
+    my $c = shift;
+    $c->res->headers->cache_control('private, max-age=0, no-cache');
+  };
+
+  helper 'cache_control.five_minutes' => sub {
+    my $c = shift;
+    $c->res->headers->cache_control('public, max-age=300');
+  };
+
+  get '/news' => sub {
+    my $c = shift;
+    $c->cache_control->no_caching;
+    $c->render(text => 'Always up to date.');
+  };
+
+  get '/some_older_story' => sub {
+    my $c = shift;
+    $c->cache_control->five_minutes;
+    $c->render(text => 'This one can be cached for a bit.');
+  };
+
+  app->start;
+
+While helpers can also be redefined, this should only be done very carefully
+to avoid conflicts.
+
 =head2 Content blocks
 
 Blocks and the helper L<Mojolicious::Plugin::DefaultHelpers/"content_for">
@@ -669,12 +787,12 @@ This chain could go on and on to allow a very high level of template reuse.
 
 =head2 Form validation
 
-You can use L<Mojolicious::Controller/"validation"> to validate C<GET>/C<POST>
-parameters submitted to your application. All unknown fields will be ignored
-by default, so you have to decide which should be required or optional before
-you can perform checks on their values. Every check is performed right away,
-so you can use the results immediately to build more advanced validation logic
-with methods like L<Mojolicious::Validator::Validation/"is_valid">.
+You can use L<Mojolicious::Controller/"validation"> to validate C<GET> and
+C<POST> parameters submitted to your application. All unknown fields will be
+ignored by default, so you have to decide which should be required or optional
+before you can perform checks on their values. Every check is performed right
+away, so you can use the results immediately to build more advanced validation
+logic with methods like L<Mojolicious::Validator::Validation/"is_valid">.
 
   use Mojolicious::Lite;
 
@@ -796,6 +914,10 @@ L<Mojolicious::Validator::Validation/"error">.
     </body>
   </html>
 
+The methods L<Mojolicious::Controller/"flash"> and
+L<Mojolicious::Controller/"redirect_to"> are often used together to prevent
+double form submission.
+
 =head2 Cross-site request forgery
 
 CSRF is a very common attack on web applications that trick your logged in
@@ -839,56 +961,59 @@ with L<Mojolicious::Validator::Validation/"csrf_protect">.
 
 The token can also be submitted with the C<X-CSRF-Token> request header.
 
-=head2 Adding helpers
+=head1 ADVANCED
 
-Adding and redefining helpers is very easy, you can use them to do pretty much
-everything.
+Less commonly used and more powerful features.
 
-  use Mojolicious::Lite;
+=head2 Serving static files
 
-  helper debug => sub {
-    my ($c, $str) = @_;
-    $c->app->log->debug($str);
-  };
+Static files are automatically served from your C<DATA> sections and C<public>
+directories, and if that's not enough you can also serve them manually with
+L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>static">.
 
-  get '/' => sub {
+  use Mojolicious::Lite;
+
+  get '/some_static_file' => sub {
     my $c = shift;
-    $c->debug('Hello from an action!');
-  } => 'index';
+    $c->res->headers->content_disposition('attachment; filename=bar.png;');
+    $c->reply->static('foo/bar.png');
+  };
 
   app->start;
-  __DATA__
 
-  @@ index.html.ep
-  % debug 'Hello from a template!';
+=head2 Custom responses
 
-Helpers can also accept template blocks as last argument, this for example
-allows very pleasant to use tag helpers and filters.
+Most response content, static as well as dynamic, gets served through
+L<Mojo::Asset::File> and L<Mojo::Asset::Memory> objects. For somewhat static
+content, like cached JSON data or temporary file, you can create your own and
+use the helper L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>asset"> to
+serve them while allowing content negotiation to be performed with C<Range>,
+C<If-Modified-Since> and C<If-None-Match> headers.
 
   use Mojolicious::Lite;
-  use Mojo::ByteStream;
 
-  helper trim_newline => sub {
-    my ($c, $block) = @_;
-    my $result = $block->();
-    $result =~ s/\n//g;
-    return Mojo::ByteStream->new($result);
+  get '/leak' => sub {
+    my $c = shift;
+    $c->res->headers->content_type('text/plain');
+    $c->reply->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
   };
 
-  get '/' => 'index';
-
   app->start;
-  __DATA__
 
-  @@ index.html.ep
-  %= trim_newline begin
-    Some text.
-    %= 1 + 1
-    More text.
-  % end
+For even more control you can also just skip the helper and use
+L<Mojolicious::Controller/"rendered"> to tell the renderer when you're done
+generating a response.
 
-Wrapping the helper result into a L<Mojo::ByteStream> object can prevent
-accidental double escaping.
+  use Mojolicious::Lite;
+
+  get '/leak' => sub {
+    my $c = shift;
+    $c->res->headers->content_type('text/plain');
+    $c->res->content->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
+    $c->rendered(200);
+  };
+
+  app->start;
 
 =head2 Helper plugins
 
@@ -1016,29 +1141,6 @@ plugin.
   @@ alertassets.html.ep
   %= javascript "/alertassets.js"
 
-=head1 ADVANCED
-
-Less commonly used and more powerful features.
-
-=head2 Rendering static files
-
-If automatic rendering of static files is not enough, you can also render them
-manually from your C<DATA> sections and C<public> directories with
-L<Mojolicious::Controller/"render_static">.
-
-  $c->res->headers->content_disposition('attachment; filename=bar.png;');
-  $c->render_static('foo/bar.png');
-
-=head2 Custom responses
-
-For entirely custom responses to, for example, stream content directly from
-files, you can use L<Mojolicious::Controller/"rendered"> to tell the renderer
-that a response has been generated.
-
-  $c->res->headers->content_type('text/plain');
-  $c->res->content->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
-  $c->rendered(200);
-
 =head2 Post-processing dynamic content
 
 While post-processing tasks are generally very easy with the hook
@@ -1084,15 +1186,28 @@ L<Mojolicious::Controller/"write_chunk"> come in handy. A common use would be
 to send the C<head> section of an HTML document to the browser in advance and
 speed up preloading of referenced images and stylesheets.
 
-  $c->write_chunk('<html><head><title>Example</title></head>' => sub {
+  use Mojolicious::Lite;
+
+  get '/' => sub {
     my $c = shift;
-    $c->finish('<body>Example</body></html>');
-  });
+    $c->write_chunk('<html><head><title>Example</title></head>' => sub {
+      my $c = shift;
+      $c->finish('<body>Example</body></html>');
+    });
+  };
+
+  app->start;
 
 The optional drain callback ensures that all previous chunks have been
 written before processing continues. An empty chunk or call to
 L<Mojolicious::Controller/"finish"> marks the end of the stream.
 
+  HTTP/1.1 200 OK
+  Connection: keep-alive
+  Date: Sat, 13 Sep 2014 16:48:29 GMT
+  Transfer-Encoding: chunked
+  Server: Mojolicious (Perl)
+
   29
   <html><head><title>Example</title></head>
   1b
@@ -1241,22 +1356,22 @@ that need to be automatically encoded, but this can be easily disabled if
 you're generating bytes instead.
 
   use Mojolicious::Lite;
-  use Mango::BSON ':bson';
+  use Storable 'nfreeze';
 
-  # Add "bson" handler
-  app->renderer->add_handler(bson => sub {
+  # Add "storable" handler
+  app->renderer->add_handler(storable => sub {
     my ($renderer, $c, $output, $options) = @_;
 
     # Disable automatic encoding
     delete $options->{encoding};
 
-    # Encode BSON data from stash value
-    $$output = bson_encode delete $c->stash->{bson};
+    # Encode data from stash value
+    $$output = nfreeze delete $c->stash->{storable};
 
     return 1;
   });
 
-  get '/' => {bson => {i => '♥ mojolicious'}, handler => 'bson'};
+  get '/' => {storable => {i => '♥ mojolicious'}, handler => 'storable'};
 
   app->start;
 
@@ -25,10 +25,10 @@ This black box is usually called a dispatcher. There are many implementations
 using different strategies to establish these connections, but pretty much all
 are based around mapping the requests path to some kind of response generator.
 
-  /user/show/1 -> $c->render(text => 'Sebastian');
-  /user/show/2 -> $c->render(text => 'Sara');
-  /user/show/3 -> $c->render(text => 'Baerbel');
-  /user/show/4 -> $c->render(text => 'Wolfgang');
+  /user/show/2 -> $c->render(text => 'Daniel');
+  /user/show/3 -> $c->render(text => 'Sara');
+  /user/show/4 -> $c->render(text => 'Baerbel');
+  /user/show/5 -> $c->render(text => 'Wolfgang');
 
 While it is very well possible to make all these connections static, it is
 also rather inefficient. That's why regular expressions are commonly used to
@@ -164,16 +164,16 @@ defined.
     my $r = $self->routes;
 
     # Route
-    $r->route('/welcome')->to(controller => 'foo', action => 'welcome');
+    $r->get('/welcome')->to(controller => 'foo', action => 'welcome');
   }
 
   1;
 
-The minimal route above will load and instantiate the class C<MyApp::Foo> and
-call its C<welcome> method.
+The minimal route above will load and instantiate the class
+C<MyApp::Controller::Foo> and call its C<welcome> method.
 
   # Controller
-  package MyApp::Foo;
+  package MyApp::Controller::Foo;
   use Mojo::Base 'Mojolicious::Controller';
 
   # Action
@@ -191,17 +191,54 @@ class, but the router can be accessed from everywhere (even at runtime).
 
 =head2 Routing destination
 
-After you start a new route with the method
-L<Mojolicious::Routes::Route/"route">, you can also give it a destination in
-the form of a hash using the chained method
-L<Mojolicious::Routes::Route/"to">.
+After you start a new route with methods like
+L<Mojolicious::Routes::Route/"get">, you can also give it a destination in the
+form of a hash using the chained method L<Mojolicious::Routes::Route/"to">.
 
   # /welcome -> {controller => 'foo', action => 'welcome'}
-  $r->route('/welcome')->to(controller => 'foo', action => 'welcome');
+  $r->get('/welcome')->to(controller => 'foo', action => 'welcome');
 
 Now if the route matches an incoming request it will use the content of this
 hash to try and find appropriate code to generate a response.
 
+=head2 HTTP methods
+
+There are already shortcuts for the most common HTTP request methods like
+L<Mojolicious::Routes::Route/"post">, and for more control
+L<Mojolicious::Routes::Route/"any"> accepts an optional array reference with
+arbitrary request methods as first argument.
+
+  # PUT /hello  -> undef
+  # GET /hello  -> {controller => 'foo', action => 'hello'}
+  $r->get('/hello')->to(controller => 'foo', action => 'hello');
+
+  # PUT /hello -> {controller => 'foo', action => 'hello'}
+  $r->put('/hello')->to(controller => 'foo', action => 'hello');
+
+  # POST /hello -> {controller => 'foo', action => 'hello'}
+  $r->post('/hello')->to(controller => 'foo', action => 'hello');
+
+  # GET|POST /bye  -> {controller => 'foo', action => 'bye'}
+  $r->any([qw(GET POST)] => '/bye')->to(controller => 'foo', action => 'bye');
+
+  # * /whatever -> {controller => 'foo', action => 'whatever'}
+  $r->any('/whatever')->to(controller => 'foo', action => 'whatever');
+
+There is one small exception, C<HEAD> requests are considered equal to C<GET>,
+but content will not be sent with the response even if it is present.
+
+  # GET /test  -> {controller => 'bar', action => 'test'}
+  # HEAD /test -> {controller => 'bar', action => 'test'}
+  $r->get('/test')->to(controller => 'bar', action => 'test');
+
+=head2 IRIs
+
+IRIs are handled transparently, that means paths are guaranteed to be
+unescaped and decoded from bytes to characters.
+
+  # GET /☃ (unicode snowman) -> {controller => 'foo', action => 'snowman'}
+  $r->get('/☃')->to(controller => 'foo', action => 'snowman');
+
 =head2 Stash
 
 The generated hash of a matching route is actually the center of the whole
@@ -209,7 +246,7 @@ L<Mojolicious> request cycle. We call it the stash, and it persists until a
 response has been generated.
 
   # /bye -> {controller => 'foo', action => 'bye', mymessage => 'Bye'}
-  $r->route('/bye')
+  $r->get('/bye')
     ->to(controller => 'foo', action => 'bye', mymessage => 'Bye');
 
 There are a few stash values with special meaning, such as C<controller> and
@@ -238,30 +275,29 @@ endpoints of these nested routes can.
 
   # /foo     -> undef
   # /foo/bar -> {controller => 'foo', action => 'bar'}
-  my $foo = $r->route('/foo')->to(controller => 'foo');
-  $foo->route('/bar')->to(action => 'bar');
+  my $foo = $r->any('/foo')->to(controller => 'foo');
+  $foo->get('/bar')->to(action => 'bar');
 
 The stash is simply inherited from route to route and newer values override
 old ones.
 
-  # /foo     -> undef
-  # /foo/abc -> undef
   # /foo/bar -> {controller => 'foo', action => 'bar'}
   # /foo/baz -> {controller => 'foo', action => 'baz'}
   # /foo/cde -> {controller => 'foo', action => 'abc'}
-  my $foo = $r->route('/foo')->to(controller => 'foo', action => 'abc');
-  $foo->route('/bar')->to(action => 'bar');
-  $foo->route('/baz')->to(action => 'baz');
-  $foo->route('/cde');
+  my $foo = $r->any('/foo')->to(controller => 'foo', action => 'abc');
+  $foo->get('/bar')->to(action => 'bar');
+  $foo->get('/baz')->to(action => 'baz');
+  $foo->get('/cde');
 
 =head2 Special stash values
 
 When the dispatcher sees C<controller> and C<action> values in the stash it
 will always try to turn them into a class and method to dispatch to. The
 C<controller> value gets camelized using L<Mojo::Util/"camelize"> and
-prefixed with a C<namespace> (defaulting to the applications class). While the
-action value is not changed at all, because of this both values are case
-sensitive.
+appended to one or more namespaces, defaulting to a controller namespace based
+on the application class (C<MyApp::Controller>), as well as the bare
+application class (C<MyApp>), and these namespaces are searched in that order.
+The action value is not changed at all, so both values are case sensitive.
 
   # Application
   package MyApp;
@@ -270,14 +306,14 @@ sensitive.
   sub startup {
     my $self = shift;
 
-    # /bye -> {controller => 'foo', action => 'bye'} -> MyApp::Foo->bye
-    $self->routes->route('/bye')->to(controller => 'foo', action => 'bye');
+    # /bye -> MyApp::Controller::Foo->bye
+    $self->routes->get('/bye')->to(controller => 'foo', action => 'bye');
   }
 
   1;
 
   # Controller
-  package MyApp::Foo;
+  package MyApp::Controller::Foo;
   use Mojo::Base 'Mojolicious::Controller';
 
   # Action
@@ -296,52 +332,107 @@ used ones they also got a special shortcut in the form of
 C<controller#action>.
 
   # /bye -> {controller => 'foo', action => 'bye', mymessage => 'Bye'}
-  $r->route('/bye')->to('foo#bye', mymessage => 'Bye');
+  $r->get('/bye')->to('foo#bye', mymessage => 'Bye');
+
+During camelization C<-> characters get replaced with C<::>, this allows
+multi-level C<controller> hierarchies.
 
-During camelization C<-> gets replaced with C<::>, this allows multi-level
-C<controller> hierarchies.
+  # / -> MyApp::Controller::Foo::Bar->hi
+  $r->get('/')->to('foo-bar#hi');
 
-  # / -> {controller => 'foo-bar', action => 'hi'} -> MyApp::Foo::Bar->hi
-  $r->route('/')->to('foo-bar#hi');
+You can also just specify the C<controller> in CamelCase form instead of
+snake_case.
+
+  # / -> MyApp::Controller::Foo::Bar->hi
+  $r->get('/')->to('Foo::Bar#hi');
 
 For security reasons the dispatcher will always check if the C<controller> is
 actually a subclass of L<Mojolicious::Controller> or L<Mojo> before
 dispatching to it.
 
-=head2 Route to class
+=head2 Namespaces
 
 You can use the C<namespace> stash value to change the namespace of a whole
 route with all its children.
 
-  # /bye -> MyApp::Controller::Foo::Bar->bye
-  $r->route('/bye')
-    ->to(namespace => 'MyApp::Controller::Foo::Bar', action => 'bye');
+  # /bye -> MyApp::MyController::Foo::Bar->bye
+  $r->get('/bye')
+    ->to(namespace => 'MyApp::MyController::Foo::Bar', action => 'bye');
 
-The C<controller> is always appended to the C<namespace> if available.
+The C<controller> is always appended to this C<namespace> if available.
 
-  # /bye -> MyApp::Controller::Foo::Bar->bye
-  $r->route('/bye')->to('foo-bar#bye', namespace => 'MyApp::Controller');
+  # /bye -> MyApp::MyController::Foo::Bar->bye
+  $r->get('/bye')->to('foo-bar#bye', namespace => 'MyApp::MyController');
 
-  # /hey -> MyApp::Controller::Foo::Bar->hey
-  $r->route('/hey')->to('Foo::Bar#hey', namespace => 'MyApp::Controller');
+  # /hey -> MyApp::MyController::Foo::Bar->hey
+  $r->get('/hey')->to('Foo::Bar#hey', namespace => 'MyApp::MyController');
 
 You can also change the default namespaces for all routes in the application
 with the router attribute L<Mojolicious::Routes/"namespaces">.
 
-  $r->namespaces(['MyApp::Controller']);
+  $r->namespaces(['MyApp::MyController']);
 
 =head2 Route to callback
 
 The C<cb> stash value, which won't be inherited by nested routes, can be used
 to bypass controllers and execute a callback instead.
 
-  $r->route('/bye')->to(cb => sub {
+  $r->get('/bye')->to(cb => sub {
     my $c = shift;
     $c->render(text => 'Good bye.');
   });
 
-This technique is the foundation of L<Mojolicious::Lite>, you can learn more
-about it from the included tutorial.
+But just like in L<Mojolicious::Lite> you can also pass the callback directly,
+which usually looks much better.
+
+  $r->get('/bye' => sub {
+    my $c = shift;
+    $c->render(text => 'Good bye.');
+  });
+
+=head2 Named routes
+
+Naming your routes will allow backreferencing in many methods and helpers
+throughout the whole framework, most of them internally rely on
+L<Mojolicious::Controller/"url_for"> for this.
+
+  # /foo/marcus -> {controller => 'foo', action => 'bar', user => 'marcus'}
+  $r->get('/foo/:user')->to('foo#bar')->name('baz');
+
+  # Generate URL "/foo/marcus" for route "baz"
+  my $url = $c->url_for('baz');
+
+  # Generate URL "/foo/jan" for route "baz"
+  my $url = $c->url_for('baz', user => 'jan');
+
+  # Generate URL "http://127.0.0.1:3000/foo/jan" for route "baz"
+  my $url = $c->url_for('baz', user => 'jan')->to_abs;
+
+Nameless routes get an automatically generated one assigned that is simply
+equal to the route itself without non-word characters, custom names have a
+higher precedence though.
+
+  # /foo/bar ("foobar")
+  $r->get('/foo/bar')->to('test#stuff');
+
+  # Generate URL "/foo/bar"
+  my $url = $c->url_for('foobar');
+
+To refer to the current route you can use the reserved name C<current> or no
+name at all.
+
+  # Generate URL for current route
+  my $url = $c->url_for('current');
+  my $url = $c->url_for;
+
+To check or get the name of the current route you can use the helper
+L<Mojolicious::Plugin::DefaultHelpers/"current_route">.
+
+  # Name for current route
+  my $name = $c->current_route;
+
+  # Check route name in code shared by multiple routes
+  $c->stash(button => 'green') if $c->current_route('login');
 
 =head2 Optional placeholders
 
@@ -350,21 +441,18 @@ already exist.
 
   # /bye -> {controller => 'foo', action => 'bar', mymessage => 'bye'}
   # /hey -> {controller => 'foo', action => 'bar', mymessage => 'hey'}
-  $r->route('/:mymessage')
-    ->to(controller => 'foo', action => 'bar', mymessage => 'hi');
+  $r->get('/:mymessage')->to('foo#bar', mymessage => 'hi');
 
 One more interesting effect, a placeholder automatically becomes optional if
 there is already a stash value of the same name present, this works similar to
 the regular expression C<([^/.]+)?>.
 
   # / -> {controller => 'foo', action => 'bar', mymessage => 'hi'}
-  $r->route('/:mymessage')
-    ->to(controller => 'foo', action => 'bar', mymessage => 'hi');
+  $r->get('/:mymessage')->to('foo#bar', mymessage => 'hi');
 
   # /test/123     -> {controller => 'foo', action => 'bar', mymessage => 'hi'}
   # /test/bye/123 -> {controller => 'foo', action => 'bar', mymessage => 'bye'}
-  $r->route('/test/:mymessage/123')
-    ->to(controller => 'foo', action => 'bar', mymessage => 'hi');
+  $r->get('/test/:mymessage/123')->to('foo#bar', mymessage => 'hi');
 
 This is also the case if multiple placeholders are right after another and not
 separated by other characters than C</>.
@@ -372,8 +460,7 @@ separated by other characters than C</>.
   # /           -> {controller => 'foo',   action => 'bar'}
   # /users      -> {controller => 'users', action => 'bar'}
   # /users/list -> {controller => 'users', action => 'list'}
-  $r->route('/:controller/:action')
-    ->to(controller => 'foo', action => 'bar');
+  $r->get('/:controller/:action')->to('foo#bar');
 
 Special stash values like C<controller> and C<action> can also be
 placeholders, which is very convenient especially during development, but
@@ -394,11 +481,10 @@ A very easy way to make placeholders more restrictive are alternatives, you
 just make a list of possible values, which then work similar to the regular
 expression C<(bender|leela)>.
 
+  # /fry    -> undef
   # /bender -> {controller => 'foo', action => 'bar', name => 'bender'}
   # /leela  -> {controller => 'foo', action => 'bar', name => 'leela'}
-  # /fry    -> undef
-  $r->route('/:name', name => [qw(bender leela)])
-    ->to(controller => 'foo', action => 'bar');
+  $r->get('/:name' => [name => [qw(bender leela)]])->to('foo#bar');
 
 You can also adjust the regular expressions behind placeholders directly, just
 make sure not to use C<^> and C<$> or capturing groups C<(...)>, because
@@ -407,17 +493,75 @@ is fine though.
 
   # /23   -> {controller => 'foo', action => 'bar', number => 23}
   # /test -> undef
-  $r->route('/:number', number => qr/\d+/)
-    ->to(controller => 'foo', action => 'bar');
+  $r->get('/:number' => [number => qr/\d+/])->to('foo#bar');
 
   # /23   -> undef
   # /test -> {controller => 'foo', action => 'bar', name => 'test'}
-  $r->route('/:name', name => qr/[a-zA-Z]+/)
-    ->to(controller => 'foo', action => 'bar');
+  $r->get('/:name' => [name => qr/[a-zA-Z]+/])->to('foo#bar');
 
 This way you get easily readable routes and the raw power of regular
 expressions.
 
+=head2 Under
+
+To share code with multiple nested routes you can use
+L<Mojolicious::Routes::Route/"under">, because unlike normal nested routes,
+the routes generated with it have their own intermediate destination and
+result in additional dispatch cycles when they match.
+
+  # /foo     -> undef
+  # /foo/bar -> {controller => 'foo', action => 'baz'}
+  #             {controller => 'foo', action => 'bar'}
+  my $foo = $r->under('/foo')->to('foo#baz');
+  $foo->get('/bar')->to('#bar');
+
+The actual action code for this destination needs to return a true value or
+the dispatch chain will be broken, this can be a very powerful tool for
+authentication.
+
+  # /foo/bar -> {cb => sub {...}}
+  #             {controller => 'foo', action => 'bar'}
+  my $foo = $r->under('/foo' => sub {
+    my $c = shift;
+
+    # Authenticated
+    return 1 if $c->req->headers->header('X-Bender');
+
+    # Not authenticated
+    $c->render(text => "You're not Bender.");
+    return undef;
+  });
+  $foo->get('/bar')->to('foo#bar');
+
+Broken dispatch chains can be continued by calling the method
+L<Mojolicious::Controller/"continue">, this allows for example non-blocking
+operations to finish before reaching the next dispatch cycle.
+
+  my $foo = $r->under('/foo' => sub {
+    my $c = shift;
+
+    # Wait 3 seconds and then give visitors a 50% chance to continue
+    Mojo::IOLoop->timer(3 => sub {
+
+      # Loser
+      return $c->render(text => 'No luck.') unless int rand 2;
+
+      # Winner
+      $c->continue;
+    });
+
+    return undef;
+  });
+  $foo->get('/bar')->to('foo#bar');
+
+Every destination is just a snapshot of the stash at the time the route
+matched, and only the C<format> value is shared by all of them. For a little
+more power you can introspect the preceding and succeeding destinations with
+L<Mojolicious::Controller/"match">.
+
+  # Action of the fourth dispatch cycle
+  my $action = $c->match->stack->[3]{action};
+
 =head2 Formats
 
 File extensions like C<.html> and C<.txt> at the end of a route are
@@ -426,29 +570,23 @@ automatically detected and stored in the stash value C<format>.
   # /foo      -> {controller => 'foo', action => 'bar'}
   # /foo.html -> {controller => 'foo', action => 'bar', format => 'html'}
   # /foo.txt  -> {controller => 'foo', action => 'bar', format => 'txt'}
-  $r->route('/foo')->to(controller => 'foo', action => 'bar');
+  $r->get('/foo')->to('foo#bar');
 
 This for example allows multiple templates in different formats to share the
-same code.
-
-  # /foo      -> {controller => 'foo', action => 'bar'}
-  # /foo.html -> {controller => 'foo', action => 'bar', format => 'html'}
-  $r->route('/foo')->to(controller => 'foo', action => 'bar');
-
-Restrictive placeholders can also be used.
+same action code. Restrictive placeholders can also be used to limit the
+allowed formats.
 
+  # /foo.txt -> undef
   # /foo.rss -> {controller => 'foo', action => 'bar', format => 'rss'}
   # /foo.xml -> {controller => 'foo', action => 'bar', format => 'xml'}
-  # /foo.txt -> undef
-  $r->route('/foo', format => [qw(rss xml)])
-    ->to(controller => 'foo', action => 'bar');
+  $r->get('/foo' => [format => [qw(rss xml)]])->to('foo#bar');
 
 Or you can just disable format detection, which gets inherited by nested
 routes and allows selective re-enabling.
 
   # /foo      -> {controller => 'foo', action => 'bar'}
   # /foo.html -> undef
-  $r->route('/foo', format => 0)->to('foo#bar');
+  $r->get('/foo' => [format => 0])->to('foo#bar');
 
   # /foo      -> {controller => 'foo', action => 'bar'}
   # /foo.html -> undef
@@ -456,77 +594,9 @@ routes and allows selective re-enabling.
   # /baz.txt  -> {controller => 'baz', action => 'yada', format => 'txt'}
   # /baz.html -> {controller => 'baz', action => 'yada', format => 'html'}
   # /baz.xml  -> undef
-  my $inactive = $r->route(format => 0);
-  $inactive->route('/foo')->to('foo#bar');
-  $inactive->route('/baz', format => [qw(txt html)])->to('baz#yada');
-
-=head2 Named routes
-
-Naming your routes will allow backreferencing in many methods and helpers
-throughout the whole framework, most of them internally rely on
-L<Mojolicious::Controller/"url_for"> for this.
-
-  # /foo/abc -> {controller => 'foo', action => 'bar', name => 'abc'}
-  $r->route('/foo/:name')->name('test')
-    ->to(controller => 'foo', action => 'bar');
-
-  # Generate URL "/foo/abc" for route "test"
-  my $url = $c->url_for('test');
-
-  # Generate URL "/foo/sebastian" for route "test"
-  my $url = $c->url_for('test', name => 'sebastian');
-
-  # Generate URL "http://127.0.0.1:3000/foo/sebastian" for route "test"
-  my $url = $c->url_for('test', name => 'sebastian')->to_abs;
-
-Nameless routes get an automatically generated one assigned that is simply
-equal to the route itself without non-word characters.
-
-  # /foo/bar ("foobar")
-  $r->route('/foo/bar')->to('test#stuff');
-
-  # Generate URL "/foo/bar"
-  my $url = $c->url_for('foobar');
-
-To refer to the current route you can use the reserved name C<current> or no
-name at all.
-
-  # Generate URL for current route
-  my $url = $c->url_for('current');
-  my $url = $c->url_for;
-
-To check or get the name of the current route you can use the helper
-L<Mojolicious::Plugin::DefaultHelpers/"current_route">.
-
-  # Name for current route
-  my $name = $c->current_route;
-
-  # Check route name in code shared by multiple routes
-  $c->stash(button => 'green') if $c->current_route('login');
-
-=head2 HTTP methods
-
-The method L<Mojolicious::Routes::Route/"via"> allows only specific HTTP
-methods to pass.
-
-  # GET /bye    -> {controller => 'foo', action => 'bye'}
-  # POST /bye   -> undef
-  # DELETE /bye -> undef
-  $r->route('/bye')->via('GET')->to(controller => 'foo', action => 'bye');
-
-  # GET /bye    -> {controller => 'foo', action => 'bye'}
-  # POST /bye   -> {controller => 'foo', action => 'bye'}
-  # DELETE /bye -> undef
-  $r->route('/bye')->via('GET', 'POST')
-    ->to(controller => 'foo', action => 'bye');
-
-With one small exception, C<HEAD> requests are considered equal to C<GET> and
-content will not be sent with the response.
-
-  # GET /test  -> {controller => 'bar', action => 'test'}
-  # HEAD /test -> {controller => 'bar', action => 'test'}
-  # PUT /test  -> undef
-  $r->route('/test')->via('GET')->to(controller => 'bar', action => 'test');
+  my $inactive = $r->under([format => 0]);
+  $inactive->get('/foo')->to('foo#bar');
+  $inactive->get('/baz' => [format => [qw(txt html)]])->to('baz#yada');
 
 =head2 WebSockets
 
@@ -535,10 +605,10 @@ access to WebSocket handshakes, which are normal C<GET> requests with some
 additional information.
 
   # /echo (WebSocket handshake)
-  $r->websocket('/echo')->to(controller => 'foo', action => 'echo');
+  $r->websocket('/echo')->to('foo#echo');
 
   # Controller
-  package MyApp::Foo;
+  package MyApp::Controller::Foo;
   use Mojo::Base 'Mojolicious::Controller';
 
   # Action
@@ -557,110 +627,17 @@ request with a C<101> response status, which happens automatically if you
 subscribe to an event with L<Mojolicious::Controller/"on"> or send a message
 with L<Mojolicious::Controller/"send"> right away.
 
-=head2 Bridges
-
-Bridge routes created with the method L<Mojolicious::Routes::Route/"bridge">
-can be used to share code with multiple nested routes, because unlike normal
-nested routes, they result in additional dispatch cycles when they match.
-
-  # /foo     -> undef
-  # /foo/bar -> {controller => 'foo', action => 'baz'}
-  #             {controller => 'foo', action => 'bar'}
-  my $foo = $r->bridge('/foo')->to(controller => 'foo', action => 'baz');
-  $foo->route('/bar')->to(action => 'bar');
-
-The actual bridge code needs to return a true value or the dispatch chain will
-be broken, this makes bridges a very powerful tool for authentication.
-
-  # /foo     -> undef
-  # /foo/bar -> {cb => sub {...}}
-  #             {controller => 'foo', action => 'bar'}
-  my $foo = $r->bridge('/foo')->to(cb => sub {
-    my $c = shift;
-
-    # Authenticated
-    return 1 if $c->req->headers->header('X-Bender');
-
-    # Not authenticated
-    $c->render(text => "You're not Bender.");
-    return undef;
-  });
-  $foo->route('/bar')->to(controller => 'foo', action => 'bar');
-
-Broken dispatch chains can be continued by calling the method
-L<Mojolicious::Controller/"continue">, this allows for example non-blocking
-operations to finish before reaching the next dispatch cycle.
-
-  # /foo     -> undef
-  # /foo/bar -> {cb => sub {...}}
-  #          -> {controller => 'foo', action => 'bar'}
-  my $foo = $r->bridge('/foo')->to(cb => sub {
-    my $c = shift;
-
-    # Wait 3 seconds and then give visitors a 50% chance to continue
-    Mojo::IOLoop->timer(3 => sub {
-
-      # Loser
-      return $c->render(text => 'No luck.') unless int rand 2;
-
-      # Winner
-      $c->continue;
-    });
-
-    return undef;
-  });
-  $foo->route('/bar')->to(controller => 'foo', action => 'bar');
-
-=head2 More convenient routes
-
-From the tutorial you should already know L<Mojolicious::Lite> routes, which
-are in fact just a small convenience layer around everything described above
-and accessible through methods like L<Mojolicious::Routes::Route/"get"> and
-L<Mojolicious::Routes::Route/"any"> as part of the normal router.
-
-  # POST /foo -> {controller => 'foo', action => 'abc'}
-  $r->post('/foo')->to(controller => 'foo', action => 'abc');
+=head2 Catch-all route
 
-  # PATCH /bar -> {controller => 'foo', action => 'bar', test => 23}
-  $r->patch('/bar')->to('foo#bar', test => 23);
+Since routes match in the order in which they were defined, you can catch all
+requests that did not match in your last route with an optional wildcard
+placeholder.
 
-  # GET /baz -> {template => 'foo/bar'}
-  $r->get('/baz')->to(template => 'foo/bar');
-
-  # * /yada.txt  -> {controller => 'foo', action => 'yada', format => 'txt'}
-  # * /yada.json -> {controller => 'foo', action => 'yada', format => 'json'}
-  $r->any('/yada' => [format => [qw(txt json)]])->to('foo#yada');
-
-  # GET   /foo/bar  -> {controller => 'foo', action => 'bar'}
-  # PUT   /foo/baz  -> {controller => 'foo', action => 'baz'}
-  # PATCH /foo      -> {controller => 'foo', action => 'yada'}
-  my $foo = $r->any('/foo')->to('foo#');
-  $foo->get('/bar')->to('#bar');
-  $foo->put('/baz')->to('#baz');
-  $foo->patch->to('#yada');
-
-This makes the process of growing your L<Mojolicious::Lite> prototypes into
-full L<Mojolicious> applications very straightforward.
-
-  # POST /bar
-  $r->post('/bar' => sub {
-    my $c = shift;
-    $c->render(text => 'Just like a Mojolicious::Lite action.');
-  });
-
-Even the more abstract concepts are available with methods like
-L<Mojolicious::Routes::Route/"under">.
-
-  # GET  /yada
-  # POST /yada
-  my $yada = $r->under('/yada');
-  $yada->get(sub {
-    my $c = shift;
-    $c->render(text => 'Hello.');
-  });
-  $yada->post(sub {
-    my $c = shift;
-    $c->render(text => 'Go away.');
+  # * /*
+  $r->any('/*whatever' => {whatever => ''} => sub {
+    my $c        = shift;
+    my $whatever = $c->param('whatever');
+    $c->render(text => "/$whatever did not match.", status => 404);
   });
 
 =head2 Hooks
@@ -742,6 +719,22 @@ You can even extend much of the core functionality.
 
 For a full list of available hooks see L<Mojolicious/"HOOKS">.
 
+=head2 Introspection
+
+The command L<Mojolicious::Command::routes> can be used from the command line
+to list all available routes together with name and underlying regular
+expressions.
+
+  $ ./myapp.pl routes -v
+  /foo/:name  ....  POST  fooname  ^/foo/([^/\.]+)  ^/?(?:\.([^/]+))?$
+  /bar        ..U.  *     bar      ^/bar
+    +/baz     ...W  GET   baz      ^/baz            ^/?(?:\.([^/]+))?$
+  /yada       ....  *     yada     ^/yada           ^/?(?:\.([^/]+))?$
+
+=head1 ADVANCED
+
+Less commonly used and more powerful features.
+
 =head2 Shortcuts
 
 You can also add your own shortcuts with L<Mojolicious::Routes/"add_shortcut">
@@ -752,7 +745,7 @@ to make route generation more expressive.
     my ($r, $name) = @_;
 
     # Generate "/$name" route
-    my $resource = $r->route("/$name")->to("$name#");
+    my $resource = $r->any("/$name")->to("$name#");
 
     # Handle POST requests
     $resource->post->to('#create')->name("create_$name");
@@ -772,36 +765,9 @@ to make route generation more expressive.
 
   # POST    /user -> {controller => 'user', action => 'create'}
   # GET     /user -> {controller => 'user', action => 'show'}
-  # OPTIONS /user
+  # OPTIONS /user -> {cb => sub {...}}
   $r->resource('user');
 
-Shortcuts can lead to anything, routes, bridges or maybe even both. And watch
-out for quicksand!
-
-=head2 Introspection
-
-The command L<Mojolicious::Command::routes> can be used from the command line
-to list all available routes together with name and underlying regular
-expressions.
-
-  $ ./myapp.pl routes -v
-  /foo/:name  ....  POST  fooname  ^/foo/([^/\.]+))(?:\.([^/]+)$)?
-  /bar        B...  *     bar      ^/bar
-    +/baz     ...W  GET   baz      ^/baz(?:\.([^/]+)$)?
-  /yada       ....  *     yada     ^/yada(?:\.([^/]+)$)?
-
-=head1 ADVANCED
-
-Less commonly used and more powerful features.
-
-=head2 IRIs
-
-IRIs are handled transparently, that means paths are guaranteed to be
-unescaped and decoded from bytes to characters.
-
-  # GET /☃ (unicode snowman) -> {controller => 'foo', action => 'snowman'}
-  $r->get('/☃')->to('foo#snowman');
-
 =head2 Rearranging routes
 
 Until the first request has been handled, all routes can still be moved around
@@ -904,7 +870,7 @@ controller. This allows for example the use of the L<Mojolicious::Lite> domain
 specific language in normal L<Mojolicious> controllers.
 
   # Controller
-  package MyApp::Bar;
+  package MyApp::Controller::Bar;
   use Mojolicious::Lite;
 
   # /hello
@@ -927,7 +893,7 @@ path will be passed along in the C<path> stash value.
 A minimal embeddable application is nothing more than a subclass of L<Mojo>,
 containing a C<handler> method accepting L<Mojolicious::Controller> objects.
 
-  package MyApp::Bar;
+  package MyApp::Controller::Bar;
   use Mojo::Base 'Mojo';
 
   sub handler {
@@ -8,9 +8,8 @@ Mojolicious::Guides - Mojolicious guide to the galaxy
 =head1 DON'T PANIC!
 
 We are constantly working on new documentation, follow us on
-L<Twitter|http://twitter.com/kraih>, L<GitHub|http://github.com/kraih/mojo>
-or join the official IRC channel C<#mojo> on C<irc.perl.org> to get all the
-latest updates.
+L<GitHub|http://github.com/kraih/mojo> or join the official IRC channel
+C<#mojo> on C<irc.perl.org> to get all the latest updates.
 
 =head1 LEARNING PERL
 
@@ -22,6 +21,11 @@ available in many formats. Both are excellent introductions to the language.
 For more books and documentation, check out
 L<learn.perl.org|http://learn.perl.org/>.
 
+=head1 LEARNING HTML
+
+All web development starts with HTML, to learn the basics we recommend the
+L<Mozilla Developer Network|https://developer.mozilla.org/en-US/learn/html>.
+
 =head1 TUTORIAL
 
 =over 2
@@ -44,7 +48,7 @@ therefore considered a prerequisite, you should definitely take a look!
 =item L<Mojolicious::Guides::Growing>
 
 Starting a L<Mojolicious::Lite> prototype from scratch and growing it into a
-well structured L<Mojolicious> application.
+well-structured L<Mojolicious> application.
 
 =item L<Mojolicious::Guides::Routing>
 
@@ -129,13 +133,15 @@ designed to be used with it and are being developed under the same umbrella.
 
 =over 2
 
-=item L<Mango>
+=item L<Mojo::Pg>
 
-Pure-Perl non-blocking I/O MongoDB driver.
+A tiny wrapper around L<DBD::Pg> that makes
+L<PostgreSQL|http://www.postgresql.org> a lot of fun to use with
+L<Mojolicious>.
 
 =item L<Minion>
 
-Job queue.
+A job queue for L<Mojolicious> with support for multiple backends.
 
 =back
 
@@ -287,8 +293,6 @@ This is the class hierarchy of the L<Mojolicious> distribution.
 
 =item * L<Mojo::IOLoop>
 
-=item * L<Mojo::JSON>
-
 =item * L<Mojo::JSON::Pointer>
 
 =item * L<Mojo::Loader>
@@ -425,6 +429,8 @@ This is the class hierarchy of the L<Mojolicious> distribution.
 
 =item * L<Mojo::DOM>
 
+=item * L<Mojo::JSON>
+
 =item * L<Mojo::Util>
 
 =item * L<ojo>
@@ -87,15 +87,15 @@ L<Mojolicious>.
 =head1 TUTORIAL
 
 A quick example driven introduction to the wonders of L<Mojolicious::Lite>.
-Most of what you'll learn here also applies to normal L<Mojolicious>
+Most of what you'll learn here also applies to full L<Mojolicious>
 applications.
 
 =head2 Hello World
 
 A simple Hello World application can look like this, L<strict>, L<warnings>,
-L<utf8> and Perl 5.10 features are automatically enabled and a few functions
-imported when you use L<Mojolicious::Lite>, turning your script into a full
-featured web application.
+L<utf8> and Perl 5.10 features are automatically enabled and a few
+L</"FUNCTIONS"> imported when you use L<Mojolicious::Lite>, turning your
+script into a full featured web application.
 
   #!/usr/bin/env perl
   use Mojolicious::Lite;
@@ -144,7 +144,7 @@ Your application will automatically reload itself if you start it with the
 C<morbo> development web server, so you don't have to restart the server after
 every change.
 
-  $ morbo myapp.pl
+  $ morbo ./myapp.pl
   Server available at http://127.0.0.1:3000.
 
 For more information about how to deploy your application see also
@@ -262,11 +262,9 @@ only the information you're actually interested in.
 =head2 Route names
 
 All routes can have a name associated with them, this allows automatic
-template detection and back referencing with
-L<Mojolicious::Controller/"url_for"> as well as many helpers like
-L<Mojolicious::Plugin::TagHelpers/"link_to">. Nameless routes get an
-automatically generated one assigned that is simply equal to the route itself
-without non-word characters.
+template detection and backreferencing with
+L<Mojolicious::Controller/"url_for">, on which many methods and helpers like
+L<Mojolicious::Plugin::TagHelpers/"link_to"> rely.
 
   use Mojolicious::Lite;
 
@@ -289,6 +287,9 @@ without non-word characters.
   @@ hello.html.ep
   Hello World!
 
+Nameless routes get an automatically generated one assigned that is simply
+equal to the route itself without non-word characters.
+
 =head2 Layouts
 
 Templates can have layouts too, you just select one with the helper
@@ -347,9 +348,8 @@ many helpers.
 
 =head2 Helpers
 
-You can also extend L<Mojolicious> with your own helpers, a list of all
-built-in ones can be found in L<Mojolicious::Plugin::DefaultHelpers> and
-L<Mojolicious::Plugin::TagHelpers>.
+Helpers are little functions you can reuse throughout your whole application,
+from actions to templates.
 
   use Mojolicious::Lite;
 
@@ -374,6 +374,9 @@ L<Mojolicious::Plugin::TagHelpers>.
   @@ secret.html.ep
   We know who you are <%= whois %>.
 
+A list of all built-in ones can be found in
+L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
+
 =head2 Placeholders
 
 Route placeholders allow capturing parts of a request path until a C</> or
@@ -472,8 +475,7 @@ Routes can be restricted to specific request methods with different keywords.
 =head2 Optional placeholders
 
 All placeholders require a value, but by assigning them default values you can
-make capturing optional. Default values that don't belong to a placeholder
-simply get merged into the stash all the time.
+make capturing optional.
 
   use Mojolicious::Lite;
 
@@ -490,6 +492,9 @@ simply get merged into the stash all the time.
   @@ groovy.txt.ep
   My name is <%= $name %> and it is <%= $day %>.
 
+Default values that don't belong to a placeholder simply get merged into the
+stash all the time.
+
 =head2 Restrictive placeholders
 
 The easiest way to make placeholders more restrictive are alternatives, you
@@ -529,8 +534,8 @@ is fine though.
 =head2 Under
 
 Authentication and code shared between multiple routes can be realized easily
-with bridge routes generated by the L</"under"> statement. All following
-routes are only evaluated if the callback returned a true value.
+with routes generated by the L</"under"> statement. All following routes are
+only evaluated if the callback returned a true value.
 
   use Mojolicious::Lite;
 
@@ -615,10 +620,12 @@ L</"under"> statements.
 
 =head2 Formats
 
-Formats can be automatically detected by looking at file extensions.
+Formats can be automatically detected from file extensions, they are used to
+find the right template and generate the correct C<Content-Type> header.
 
   use Mojolicious::Lite;
 
+  # /detection
   # /detection.html
   # /detection.txt
   get '/detection' => sub {
@@ -639,7 +646,8 @@ Formats can be automatically detected by looking at file extensions.
   @@ detected.txt.ep
   TXT was detected.
 
-Restrictive placeholders can also be used.
+The default format is C<html>, restrictive placeholders can be used to limit
+possible values.
 
   use Mojolicious::Lite;
 
@@ -725,7 +733,12 @@ served automatically from a C<public> directory if it exists.
   $ mv something.js public/something.js
   $ mv mojolicious.tar.gz public/mojolicious.tar.gz
 
-Both have a higher precedence than routes.
+Both have a higher precedence than routes for C<GET> and C<HEAD> requests.
+Content negotiation with C<Range>, C<If-None-Match> and C<If-Modified-Since>
+headers is supported as well and can be tested very easily with
+L<Mojolicious::Command::get>.
+
+  $ ./myapp.pl get /something.js -v -H 'Range: bytes=2-4'
 
 =head2 External templates
 
@@ -861,7 +874,7 @@ L<Mojo::JSON> and L<Mojo::DOM> this can be a very powerful tool.
     my $c   = shift;
     my $url = $c->param('url') || 'http://mojolicio.us';
     my $dom = $c->ua->get($url)->res->dom;
-    $c->render(json => [$dom->find('h1, h2, h3')->text->each]);
+    $c->render(json => $dom->find('h1, h2, h3')->map('text')->to_array);
   };
 
   # Non-blocking
@@ -885,8 +898,8 @@ L<Mojo::JSON> and L<Mojo::DOM> this can be a very powerful tool.
       sub {
         my ($delay, $mojo, $cpan) = @_;
         $c->render(json => {
-          mojo => $mojo->res->dom->html->head->title->text,
-          cpan => $cpan->res->dom->html->head->title->text
+          mojo => $mojo->res->dom->at('title')->text,
+          cpan => $cpan->res->dom->at('title')->text
         });
       }
     );
@@ -1013,6 +1026,7 @@ automatically exported.
   my $route = any '/:foo' => {foo => 'bar'} => sub {...};
   my $route = any '/:foo' => [foo => qr/\w+/] => sub {...};
   my $route = any [qw(GET POST)] => '/:foo' => sub {...};
+  my $route = any [qw(GET POST)] => '/:foo' => [foo => qr/\w+/] => sub {...};
 
 Generate route with L<Mojolicious::Routes::Route/"any">, matching any of the
 listed HTTP request methods or all. See also the tutorial above for many more
@@ -1022,7 +1036,12 @@ argument variations.
 
   my $app = app;
 
-The L<Mojolicious::Lite> application.
+Returns the L<Mojolicious::Lite> application object, which is a subclass of
+L<Mojolicious>.
+
+  # Use all the available attributes and methods
+  app->log->level('error');
+  app->defaults(foo => 'bar');
 
 =head2 del
 
@@ -1108,12 +1127,13 @@ requests. See also the tutorial above for many more argument variations.
 
 =head2 under
 
-  my $bridge = under sub {...};
-  my $bridge = under '/:foo' => sub {...};
-  my $bridge = under '/:foo' => [foo => qr/\w+/];
-  my $bridge = under {format => 0};
+  my $route = under sub {...};
+  my $route = under '/:foo' => sub {...};
+  my $route = under '/:foo' => {foo => 'bar'};
+  my $route = under '/:foo' => [foo => qr/\w+/];
+  my $route = under [format => 0];
 
-Generate bridge route with L<Mojolicious::Routes::Route/"under">, to which all
+Generate nested route with L<Mojolicious::Routes::Route/"under">, to which all
 following routes are automatically appended. See also the tutorial above for
 more argument variations.
 
@@ -17,7 +17,7 @@ sub parse {
   my $config
     = eval 'package Mojolicious::Plugin::Config::Sandbox; no warnings;'
     . "sub app; local *app = sub { \$app }; use Mojo::Base -strict; $content";
-  die qq{Couldn't load configuration from file "$file": $@} if !$config && $@;
+  die qq{Can't load configuration from file "$file": $@} if !$config && $@;
   die qq{Configuration file "$file" did not return a hash reference.\n}
     unless ref $config eq 'HASH';
 
@@ -3,6 +3,7 @@ use Mojo::Base 'Mojolicious::Plugin';
 
 use Mojo::ByteStream;
 use Mojo::Collection;
+use Mojo::Exception;
 use Mojo::IOLoop;
 use Mojo::Util qw(dumper sha1_sum steady_time);
 
@@ -16,31 +17,32 @@ sub register {
 
   # Stash key shortcuts (should not generate log messages)
   for my $name (qw(extends layout title)) {
-    $app->helper(
-      $name => sub {
-        my $c     = shift;
-        my $stash = $c->stash;
-        $stash->{$name} = shift if @_;
-        $c->stash(@_) if @_;
-        return $stash->{$name};
-      }
-    );
+    $app->helper($name => sub { shift->stash($name, @_) });
   }
 
-  $app->helper($_ => $self->can("_$_"))
-    for qw(accepts content content_for csrf_token current_route delay),
-    qw(inactivity_timeout url_with);
-  $app->helper(b => sub { shift; Mojo::ByteStream->new(@_) });
-  $app->helper(c => sub { shift; Mojo::Collection->new(@_) });
+  $app->helper(accepts => sub { $_[0]->app->renderer->accepts(@_) });
+  $app->helper(b       => sub { shift; Mojo::ByteStream->new(@_) });
+  $app->helper(c       => sub { shift; Mojo::Collection->new(@_) });
   $app->helper(config  => sub { shift->app->config(@_) });
-  $app->helper(dumper  => sub { shift; dumper(@_) });
+
+  $app->helper($_ => $self->can("_$_"))
+    for qw(content content_for csrf_token current_route delay),
+    qw(inactivity_timeout is_fresh url_with);
+
+  $app->helper(dumper => sub { shift; dumper(@_) });
   $app->helper(include => sub { shift->render_to_string(@_) });
-  $app->helper(ua      => sub { shift->app->ua });
+
+  $app->helper("reply.$_" => $self->can("_$_")) for qw(asset static);
+
+  $app->helper('reply.exception' => sub { _development('exception', @_) });
+  $app->helper('reply.not_found' => sub { _development('not_found', @_) });
+  $app->helper(ua                => sub { shift->app->ua });
 }
 
-sub _accepts {
+sub _asset {
   my $c = shift;
-  return $c->app->renderer->accepts($c, @_);
+  $c->app->static->serve_asset($c, @_);
+  $c->rendered;
 }
 
 sub _content {
@@ -70,16 +72,59 @@ sub _csrf_token {
 }
 
 sub _current_route {
-  return '' unless my $endpoint = shift->match->endpoint;
-  return $endpoint->name unless @_;
-  return $endpoint->name eq shift;
+  return '' unless my $route = shift->match->endpoint;
+  return @_ ? $route->name eq shift : $route->name;
 }
 
 sub _delay {
-  my $self  = shift;
-  my $tx    = $self->render_later->tx;
+  my $c     = shift;
+  my $tx    = $c->render_later->tx;
   my $delay = Mojo::IOLoop->delay(@_);
-  $delay->catch(sub { $self->render_exception(pop) and undef $tx })->wait;
+  $delay->catch(sub { $c->render_exception(pop) and undef $tx })->wait;
+}
+
+sub _development {
+  my ($page, $c, $e) = @_;
+
+  my $app = $c->app;
+  $app->log->error($e = Mojo::Exception->new($e)) if $page eq 'exception';
+
+  # Filtered stash snapshot
+  my $stash = $c->stash;
+  my %snapshot = map { $_ => $stash->{$_} }
+    grep { !/^mojo\./ and defined $stash->{$_} } keys %$stash;
+
+  # Render with fallbacks
+  my $mode     = $app->mode;
+  my $renderer = $app->renderer;
+  my $options  = {
+    exception => $page eq 'exception' ? $e : undef,
+    format => $stash->{format} || $renderer->default_format,
+    handler  => undef,
+    snapshot => \%snapshot,
+    status   => $page eq 'exception' ? 500 : 404,
+    template => "$page.$mode"
+  };
+  my $inline = $renderer->_bundled($mode eq 'development' ? $mode : $page);
+  return $c if _fallbacks($c, $options, $page, $inline);
+  _fallbacks($c, {%$options, format => 'html'}, $page, $inline);
+  return $c;
+}
+
+sub _fallbacks {
+  my ($c, $options, $template, $inline) = @_;
+
+  # Mode specific template
+  return 1 if $c->render_maybe(%$options);
+
+  # Normal template
+  return 1 if $c->render_maybe(%$options, template => $template);
+
+  # Inline template
+  my $stash = $c->stash;
+  return undef unless $stash->{format} eq 'html';
+  delete @$stash{qw(extends layout)};
+  return $c->render_maybe(%$options, inline => $inline, handler => 'ep');
 }
 
 sub _inactivity_timeout {
@@ -87,6 +132,18 @@ sub _inactivity_timeout {
   $stream->timeout(shift);
 }
 
+sub _is_fresh {
+  my ($c, %options) = @_;
+  return $c->app->static->is_fresh($c, \%options);
+}
+
+sub _static {
+  my ($c, $file) = @_;
+  return !!$c->rendered if $c->app->static->serve($c, $file);
+  $c->app->log->debug(qq{File "$file" not found, public directory missing?});
+  return !$c->render_not_found;
+}
+
 sub _url_with {
   my $c = shift;
   return $c->url_for(@_)->query($c->req->url->query->clone);
@@ -110,7 +167,7 @@ Mojolicious::Plugin::DefaultHelpers - Default helpers plugin
 
 =head1 DESCRIPTION
 
-L<Mojolicious::Plugin::DefaultHelpers> is a collection of renderer helpers for
+L<Mojolicious::Plugin::DefaultHelpers> is a collection of helpers for
 L<Mojolicious>.
 
 This is a core plugin, that means it is always enabled and its code a good
@@ -225,13 +282,27 @@ Check or get name of current route.
 Disable automatic rendering and use L<Mojo::IOLoop/"delay"> to manage
 callbacks and control the flow of events, which can help you avoid deep nested
 closures and memory leaks that often result from continuation-passing style.
-Calls L<Mojolicious::Controller/"render_exception"> if an error occured in one
-of the steps, breaking the chain.
+Also keeps a reference to L<Mojolicious::Controller/"tx"> in case the
+underlying connection gets closed early, and calls L</"reply-E<gt>exception">
+if an exception gets thrown in one of the steps, breaking the chain.
 
   # Longer version
   $c->render_later;
+  my $tx    = $c->tx;
   my $delay = Mojo::IOLoop->delay(sub {...}, sub {...});
-  $delay->catch(sub { $c->render_exception(pop) })->wait;
+  $delay->catch(sub { $c->reply->exception(pop) and undef $tx })->wait;
+
+  # Non-blocking request
+  $c->delay(
+    sub {
+      my $delay = shift;
+      $c->ua->get('http://mojolicio.us' => $delay->begin);
+    },
+    sub {
+      my ($delay, $tx) = @_;
+      $c->render(json => {title => $tx->res->dom->at('title')->text});
+    }
+  );
 
 =head2 dumper
 
@@ -270,6 +341,21 @@ timeout if possible.
 
 Alias for C<Mojolicious::Controller/"render_to_string">.
 
+=head2 is_fresh
+
+  my $bool = $c->is_fresh;
+  my $bool = $c->is_fresh(etag => 'abc');
+  my $bool = $c->is_fresh(last_modified => $epoch);
+
+Check freshness of request by comparing the C<If-None-Match> and
+C<If-Modified-Since> request headers to the C<ETag> and C<Last-Modified>
+response headers with L<Mojolicious::Static/"is_fresh">.
+
+  # Add ETag header and check freshness before rendering
+  $c->is_fresh(etag => 'abc')
+    ? $c->rendered(304)
+    : $c->render(text => 'I ♥ Mojolicious!');
+
 =head2 layout
 
   % layout 'green';
@@ -284,6 +370,52 @@ L</"stash">.
 
 Alias for L<Mojolicious::Controller/"param">.
 
+=head2 reply->asset
+
+  $c->reply->asset(Mojo::Asset::File->new);
+
+Reply with a L<Mojo::Asset::File> or L<Mojo::Asset::Memory> object using
+L<Mojolicious::Static/"serve_asset">, and perform content negotiation with
+C<Range>, C<If-Modified-Since> and C<If-None-Match> headers.
+
+  # Serve asset with custom modification time
+  my $asset = Mojo::Asset::Memory->new;
+  $asset->add_chunk('Hello World!')->mtime(784111777);
+  $c->res->headers->content_type('text/plain');
+  $c->reply->asset($asset);
+
+=head2 reply->exception
+
+  $c = $c->reply->exception('Oops!');
+  $c = $c->reply->exception(Mojo::Exception->new('Oops!'));
+
+Render the exception template C<exception.$mode.$format.*> or
+C<exception.$format.*> and set the response status code to C<500>. Also sets
+the stash values C<exception> to a L<Mojo::Exception> object and C<snapshot>
+to a copy of the L</"stash"> for use in the templates.
+
+=head2 reply->not_found
+
+  $c = $c->reply->not_found;
+
+Render the not found template C<not_found.$mode.$format.*> or
+C<not_found.$format.*> and set the response status code to C<404>. Also sets
+the stash value C<snapshot> to a copy of the L</"stash"> for use in the
+templates.
+
+=head2 reply->static
+
+  my $bool = $c->reply->static('images/logo.png');
+  my $bool = $c->reply->static('../lib/MyApp.pm');
+
+Reply with a static file using L<Mojolicious::Static/"serve">, usually from
+the C<public> directories or C<DATA> sections of your application. Note that
+this helper does not protect from traversing to parent directories.
+
+  # Serve file with a custom content type
+  $c->res->headers->content_type('application/myapp');
+  $c->reply->static('foo.txt');
+
 =head2 session
 
   %= session 'foo'
@@ -301,11 +433,11 @@ Alias for L<Mojolicious::Controller/"stash">.
 
 =head2 title
 
+  %= title
   % title 'Welcome!';
   % title 'Welcome!', foo => 'bar';
-  %= title
 
-Set C<title> stash value, all additional pairs get merged into the
+Get or set C<title> stash value, all additional pairs get merged into the
 L</"stash">.
 
 =head2 ua
@@ -9,17 +9,8 @@ sub register { $_[1]->renderer->add_handler(epl => \&_epl) }
 sub _epl {
   my ($renderer, $c, $output, $options) = @_;
 
-  # Template
-  my $name   = $renderer->template_name($options);
-  my $inline = $options->{inline};
-  $name = md5_sum encode('UTF-8', $inline) if defined $inline;
-  return undef unless defined $name;
-
   # Cached
-  my $key   = delete $options->{cache} || $name;
-  my $cache = $renderer->cache;
-  my $mt    = $cache->get($key);
-  $mt ||= $cache->set($key => Mojo::Template->new)->get($key);
+  my $mt = delete $options->{'mojo.template'} || Mojo::Template->new;
   my $log = $c->app->log;
   if ($mt->compiled) {
     $log->debug("Rendering cached @{[$mt->name]}.");
@@ -28,6 +19,9 @@ sub _epl {
 
   # Not cached
   else {
+    my $inline = $options->{inline};
+    my $name = defined $inline ? md5_sum encode('UTF-8', $inline) : undef;
+    return undef unless defined($name //= $renderer->template_name($options));
 
     # Inline
     if (defined $inline) {
@@ -37,7 +31,7 @@ sub _epl {
 
     # File
     else {
-      $mt->encoding($renderer->encoding) if $renderer->encoding;
+      if (my $encoding = $renderer->encoding) { $mt->encoding($encoding) }
 
       # Try template
       if (defined(my $path = $renderer->template_path($options))) {
@@ -79,8 +73,8 @@ Mojolicious::Plugin::EPLRenderer - Embedded Perl Lite renderer plugin
 
 =head1 DESCRIPTION
 
-L<Mojolicious::Plugin::EPLRenderer> is a renderer for C<epl> templates. C<epl>
-templates are pretty much just raw L<Mojo::Template>.
+L<Mojolicious::Plugin::EPLRenderer> is a renderer for C<epl> templates, which
+are pretty much just raw L<Mojo::Template>.
 
 This is a core plugin, that means it is always enabled and its code a good
 example for learning to build new plugins, you're welcome to fork it.
@@ -4,45 +4,47 @@ use Mojo::Base 'Mojolicious::Plugin';
 use Mojo::Template;
 use Mojo::Util qw(encode md5_sum monkey_patch);
 
+sub DESTROY { Mojo::Util::_teardown(shift->{namespace}) }
+
 sub register {
   my ($self, $app, $conf) = @_;
 
   # Auto escape by default to prevent XSS attacks
   my $template = {auto_escape => 1, %{$conf->{template} || {}}};
+  my $ns = $self->{namespace} = $template->{namespace}
+    //= 'Mojo::Template::Sandbox::' . md5_sum "$self";
 
   # Add "ep" handler and make it the default
   $app->renderer->default_handler('ep')->add_handler(
     $conf->{name} || 'ep' => sub {
       my ($renderer, $c, $output, $options) = @_;
 
-      # Generate name
-      my $name = $options->{inline} || $renderer->template_name($options);
+      my $name = $options->{inline} // $renderer->template_name($options);
       return undef unless defined $name;
       my @keys = sort grep {/^\w+$/} keys %{$c->stash};
-      my $id = encode 'UTF-8', join(',', $name, @keys);
-      my $key = $options->{cache} = md5_sum $id;
+      my $key = md5_sum encode 'UTF-8', join(',', $name, @keys);
 
-      # Cache template for "epl" handler
+      # Prepare template for "epl" handler
       my $cache = $renderer->cache;
-      my $mt    = $cache->get($key);
-      unless ($mt) {
-        $mt = Mojo::Template->new($template);
+      unless ($options->{'mojo.template'} = $cache->get($key)) {
+        my $mt = $options->{'mojo.template'} = Mojo::Template->new($template);
 
         # Helpers (only once)
-        ++$self->{helpers} and _helpers($mt->namespace, $renderer->helpers)
+        ++$self->{helpers} and _helpers($ns, $renderer->helpers)
           unless $self->{helpers};
 
         # Stash values (every time)
-        my $prepend = 'my $self = my $c = shift; my $_S = $c->stash;';
-        $prepend .= " my \$$_ = \$_S->{'$_'};" for @keys;
+        my $prepend = 'my $self = my $c = shift; my $_S = $c->stash; {';
+        $prepend .= join '', map {" my \$$_ = \$_S->{'$_'};"} @keys;
+        $mt->prepend($prepend . $mt->prepend)->append('}' . $mt->append);
 
-        $cache->set($key => $mt->prepend($prepend . $mt->prepend));
+        $cache->set($key => $mt);
       }
 
       # Make current controller available
       no strict 'refs';
       no warnings 'redefine';
-      local *{"@{[$mt->namespace]}::_C"} = sub {$c};
+      local *{"${ns}::_C"} = sub {$c};
 
       # Render with "epl" handler
       return $renderer->handlers->{epl}->($renderer, $c, $output, $options);
@@ -51,10 +53,10 @@ sub register {
 }
 
 sub _helpers {
-  my ($ns, $helpers) = @_;
-  for my $name (grep {/^\w+$/} keys %$helpers) {
-    monkey_patch $ns, $name,
-      sub { $ns->_C->app->renderer->helpers->{$name}->($ns->_C, @_) };
+  my ($class, $helpers) = @_;
+  for my $method (grep {/^\w+$/} keys %$helpers) {
+    my $sub = $helpers->{$method};
+    monkey_patch $class, $method, sub { $class->_C->$sub(@_) };
   }
 }
 
@@ -80,13 +82,14 @@ Mojolicious::Plugin::EPRenderer - Embedded Perl renderer plugin
 
 =head1 DESCRIPTION
 
-L<Mojolicious::Plugin::EPRenderer> is a renderer for C<ep> templates.
+L<Mojolicious::Plugin::EPRenderer> is a renderer for C<ep> or C<Embedded Perl>
+templates.
 
-C<ep> or C<Embedded Perl> is a simple template format where you embed perl
-code into documents. It is based on L<Mojo::Template>, but extends it with
-some convenient syntax sugar designed specifically for L<Mojolicious>. It
-supports L<Mojolicious> template helpers and exposes the stash directly as
-Perl variables.
+C<Embedded Perl> is a simple template format where you embed perl code into
+documents. It is based on L<Mojo::Template>, but extends it with some
+convenient syntax sugar designed specifically for L<Mojolicious>. It supports
+L<Mojolicious> template helpers and exposes the stash directly as Perl
+variables.
 
 This is a core plugin, that means it is always enabled and its code a good
 example for learning to build new plugins, you're welcome to fork it.
@@ -1,18 +1,15 @@
 package Mojolicious::Plugin::JSONConfig;
 use Mojo::Base 'Mojolicious::Plugin::Config';
 
-use Mojo::JSON;
+use Mojo::JSON 'from_json';
 use Mojo::Template;
-use Mojo::Util 'encode';
 
 sub parse {
   my ($self, $content, $file, $conf, $app) = @_;
 
-  my $json   = Mojo::JSON->new;
-  my $config = $json->decode($self->render($content, $file, $conf, $app));
-  my $err    = $json->error;
-  die qq{Couldn't parse config "$file": $err} if !$config && $err;
-  die qq{Invalid config "$file"} if !$config || ref $config ne 'HASH';
+  my $config = eval { from_json $self->render($content, $file, $conf, $app) };
+  die qq{Can't parse config "$file": $@} if !$config && $@;
+  die qq{Invalid config "$file"} unless ref $config eq 'HASH';
 
   return $config;
 }
@@ -26,10 +23,9 @@ sub render {
   my $prepend = q[my $app = shift; no strict 'refs'; no warnings 'redefine';];
   $prepend .= q[sub app; local *app = sub { $app }; use Mojo::Base -strict;];
 
-  # Render and encode for JSON decoding
   my $mt = Mojo::Template->new($conf->{template} || {})->name($file);
-  my $json = $mt->prepend($prepend . $mt->prepend)->render($content, $app);
-  return ref $json ? die $json : encode 'UTF-8', $json;
+  my $output = $mt->prepend($prepend . $mt->prepend)->render($content, $app);
+  return ref $output ? die $output : $output;
 }
 
 1;
@@ -17,9 +17,7 @@ sub register {
   }
 
   my $route = $app->routes->route($path)->detour(app => $embed);
-  $route->over(host => $host) if $host;
-
-  return $route;
+  return $host ? $route->over(host => $host) : $route;
 }
 
 1;
@@ -6,7 +6,7 @@ use Mojo::ByteStream 'b';
 use Mojo::DOM;
 use Mojo::URL;
 use Mojo::Util qw(slurp unindent url_escape);
-use Pod::Simple::HTML;
+use Pod::Simple::XHTML 3.09;
 use Pod::Simple::Search;
 
 sub register {
@@ -35,19 +35,17 @@ sub register {
 }
 
 sub _html {
-  my ($self, $src) = @_;
+  my ($c, $src) = @_;
 
   # Rewrite links
   my $dom     = Mojo::DOM->new(_pod_to_html($src));
-  my $perldoc = $self->url_for('/perldoc/');
-  for my $e ($dom->find('a[href]')->each) {
-    my $attrs = $e->attr;
-    $attrs->{href} =~ s!%3A%3A!/!gi
-      if $attrs->{href} =~ s!^http://search\.cpan\.org/perldoc\?!$perldoc!;
-  }
+  my $perldoc = $c->url_for('/perldoc/');
+  $_->{href} =~ s!^https://metacpan\.org/pod/!$perldoc!
+    and $_->{href} =~ s!::!/!gi
+    for $dom->find('a[href]')->map('attr')->each;
 
   # Rewrite code blocks for syntax highlighting and correct indentation
-  for my $e ($dom->find('pre')->each) {
+  for my $e ($dom->find('pre > code')->each) {
     $e->content(my $str = unindent $e->content);
     next if $str =~ /^\s*(?:\$|Usage:)\s+/m || $str !~ /[\$\@\%]\w|-&gt;\w/m;
     my $attrs = $e->attr;
@@ -57,23 +55,15 @@ sub _html {
 
   # Rewrite headers
   my $toc = Mojo::URL->new->fragment('toc');
-  my (%anchors, @parts);
+  my @parts;
   for my $e ($dom->find('h1, h2, h3')->each) {
 
-    # Anchor and text
-    my $name = my $text = $e->all_text;
-    $name =~ s/\s+/_/g;
-    $name =~ s/[^\w\-]//g;
-    my $anchor = $name;
-    my $i      = 1;
-    $anchor = $name . $i++ while $anchors{$anchor}++;
-
-    # Rewrite
     push @parts, [] if $e->type eq 'h1' || !@parts;
-    my $link = Mojo::URL->new->fragment($anchor);
-    push @{$parts[-1]}, $text, $link;
-    my $permalink = $self->link_to('#' => $link, class => 'permalink');
-    $e->content($permalink . $self->link_to($text => $toc, id => $anchor));
+    my $anchor = $e->{id};
+    my $link   = Mojo::URL->new->fragment($anchor);
+    push @{$parts[-1]}, my $text = $e->all_text, $link;
+    my $permalink = $c->link_to('#' => $link, class => 'permalink');
+    $e->content($permalink . $c->link_to($text => $toc, id => $anchor));
   }
 
   # Try to find a title
@@ -81,39 +71,34 @@ sub _html {
   $dom->find('h1 + p')->first(sub { $title = shift->text });
 
   # Combine everything to a proper response
-  $self->content_for(perldoc => "$dom");
-  my $template = $self->app->renderer->_bundled('perldoc');
-  $self->render(inline => $template, title => $title, parts => \@parts);
+  $c->content_for(perldoc => "$dom");
+  my $template = $c->app->renderer->_bundled('perldoc');
+  $c->render(inline => $template, title => $title, parts => \@parts);
 }
 
 sub _perldoc {
-  my $self = shift;
+  my $c = shift;
 
   # Find module or redirect to CPAN
-  my $module = $self->param('module');
-  $module =~ s!/!::!g;
+  my $module = join '::', split '/', scalar $c->param('module');
   my $path
     = Pod::Simple::Search->new->find($module, map { $_, "$_/pods" } @INC);
-  return $self->redirect_to("http://metacpan.org/module/$module")
+  return $c->redirect_to("https://metacpan.org/pod/$module")
     unless $path && -r $path;
 
   my $src = slurp $path;
-  $self->respond_to(txt => {data => $src}, html => sub { _html($self, $src) });
+  $c->respond_to(txt => {data => $src}, html => sub { _html($c, $src) });
 }
 
 sub _pod_to_html {
   return '' unless defined(my $pod = ref $_[0] eq 'CODE' ? shift->() : shift);
 
-  my $parser = Pod::Simple::HTML->new;
-  $parser->$_('') for qw(force_title html_header_before_title);
-  $parser->$_('') for qw(html_header_after_title html_footer);
+  my $parser = Pod::Simple::XHTML->new;
+  $parser->perldoc_url_prefix('https://metacpan.org/pod/');
+  $parser->$_('') for qw(html_header html_footer);
   $parser->output_string(\(my $output));
   return $@ unless eval { $parser->parse_string_document("$pod"); 1 };
 
-  # Filter
-  $output =~ s!<a name='___top' class='dummyTopAnchor'\s*?></a>\n!!g;
-  $output =~ s!<a class='u'.*?name=".*?"\s*>(.*?)</a>!$1!sg;
-
   return $output;
 }
 
@@ -2,7 +2,7 @@ package Mojolicious::Plugin::TagHelpers;
 use Mojo::Base 'Mojolicious::Plugin';
 
 use Mojo::ByteStream;
-use Mojo::Util 'xml_escape';
+use Mojo::Util 'xss_escape';
 use Scalar::Util 'blessed';
 
 sub register {
@@ -45,7 +45,7 @@ sub register {
 
 sub _csrf_field {
   my $c = shift;
-  return _hidden_field($c, csrf_token => $c->csrf_token, @_);
+  return _hidden_field($c, csrf_token => $c->helpers->csrf_token, @_);
 }
 
 sub _form_for {
@@ -76,7 +76,7 @@ sub _input {
   my %attrs = @_ % 2 ? (value => shift, @_) : @_;
 
   # Special selection value
-  my @values = $c->param($name);
+  my @values = @{$c->every_param($name)};
   my $type = $attrs{type} || '';
   if (@values && $type ne 'submit') {
 
@@ -140,7 +140,7 @@ sub _option {
   $attrs{selected} = 'selected' if exists $values->{$pair->[1]};
   %attrs = (%attrs, @$pair[2 .. $#$pair]);
 
-  return _tag('option', %attrs, sub { xml_escape $pair->[0] });
+  return _tag('option', %attrs, $pair->[0]);
 }
 
 sub _password_field {
@@ -152,7 +152,7 @@ sub _password_field {
 sub _select_field {
   my ($c, $name, $options, %attrs) = (shift, shift, shift, @_);
 
-  my %values = map { $_ => 1 } $c->param($name);
+  my %values = map { $_ => 1 } @{$c->every_param($name)};
 
   my $groups = '';
   for my $group (@$options) {
@@ -212,13 +212,13 @@ sub _tag {
     }
     delete $attrs{data};
   }
-  $tag .= qq{ $_="} . xml_escape($attrs{$_} // '') . '"' for sort keys %attrs;
+  $tag .= qq{ $_="} . xss_escape($attrs{$_} // '') . '"' for sort keys %attrs;
 
   # Empty element
   unless ($cb || defined $content) { $tag .= ' />' }
 
   # End tag
-  else { $tag .= '>' . ($cb ? $cb->() : xml_escape($content)) . "</$name>" }
+  else { $tag .= '>' . ($cb ? $cb->() : xss_escape $content) . "</$name>" }
 
   # Prevent escaping
   return Mojo::ByteStream->new($tag);
@@ -234,19 +234,17 @@ sub _tag_with_error {
 sub _text_area {
   my ($c, $name) = (shift, shift);
 
-  # Make sure content is wrapped
-  my $cb = ref $_[-1] eq 'CODE' ? pop : sub {''};
+  my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
   my $content = @_ % 2 ? shift : undef;
-  $cb = sub { xml_escape $content }
-    if defined($content = $c->param($name) // $content);
+  $content = $c->param($name) // $content // $cb // '';
 
-  return _validation($c, $name, 'textarea', @_, name => $name, $cb);
+  return _validation($c, $name, 'textarea', @_, name => $name, $content);
 }
 
 sub _validation {
   my ($c, $name) = (shift, shift);
   return _tag(@_) unless $c->validation->has_error($name);
-  return $c->tag_with_error(@_);
+  return $c->helpers->tag_with_error(@_);
 }
 
 1;
@@ -686,12 +684,13 @@ HTML/XML tag generator.
 
 Very useful for reuse in more specific tag helpers.
 
-  $c->tag('div');
-  $c->tag('div', id => 'foo');
-  $c->tag(div => sub { 'Content' });
+  my $output = $c->tag('div');
+  my $output = $c->tag('div', id => 'foo');
+  my $output = $c->tag(div => '<p>This will be escaped</p>');
+  my $output = $c->tag(div => sub { '<p>This will not be escaped</p>' });
 
 Results are automatically wrapped in L<Mojo::ByteStream> objects to prevent
-accidental double escaping.
+accidental double escaping in C<ep> templates.
 
 =head2 tag_with_error
 
@@ -7,7 +7,7 @@ has namespaces => sub { ['Mojolicious::Plugin'] };
 
 sub emit_hook {
   my $self = shift;
-  $_->(@_) for @{$self->subscribers(shift)};
+  for my $cb (@{$self->subscribers(shift)}) { $cb->(@_) }
   return $self;
 }
 
@@ -25,7 +25,7 @@ sub emit_chain {
 
 sub emit_hook_reverse {
   my $self = shift;
-  $_->(@_) for reverse @{$self->subscribers(shift)};
+  for my $cb (reverse @{$self->subscribers(shift)}) { $cb->(@_) }
   return $self;
 }
 
@@ -34,10 +34,7 @@ sub load_plugin {
 
   # Try all namespaces
   my $class = $name =~ /^[a-z]/ ? camelize($name) : $name;
-  for my $ns (@{$self->namespaces}) {
-    my $module = "${ns}::$class";
-    return $module->new if _load($module);
-  }
+  _load($_) and return $_->new for map {"${_}::$class"} @{$self->namespaces};
 
   # Full module name
   return $name->new if _load($name);
@@ -52,10 +49,9 @@ sub register_plugin {
 
 sub _load {
   my $module = shift;
-  if (my $e = Mojo::Loader->new->load($module)) {
-    ref $e ? die $e : return undef;
-  }
-  return $module->isa('Mojolicious::Plugin');
+  return $module->isa('Mojolicious::Plugin')
+    unless my $e = Mojo::Loader->new->load($module);
+  ref $e ? die $e : return undef;
 }
 
 1;
@@ -70,7 +66,7 @@ Mojolicious::Plugins - Plugin manager
 
   use Mojolicious::Plugins;
 
-  my $plugins = Mojolicious::Plugin->new;
+  my $plugins = Mojolicious::Plugins->new;
   push @{$plugins->namespaces}, 'MyApp::Plugin';
 
 =head1 DESCRIPTION
@@ -6,7 +6,7 @@ use Mojo::Cache;
 use Mojo::JSON 'encode_json';
 use Mojo::Home;
 use Mojo::Loader;
-use Mojo::Util qw(decamelize encode slurp);
+use Mojo::Util qw(decamelize encode md5_sum monkey_patch slurp);
 
 has cache   => sub { Mojo::Cache->new };
 has classes => sub { ['main'] };
@@ -29,6 +29,10 @@ $HOME->parse(
   $HOME->parse($HOME->mojo_lib_dir)->rel_dir('Mojolicious/templates'));
 my %TEMPLATES = map { $_ => slurp $HOME->rel_file($_) } @{$HOME->list_files};
 
+my $LOADER = Mojo::Loader->new;
+
+sub DESTROY { Mojo::Util::_teardown($_) for @{shift->{namespaces}} }
+
 sub accepts {
   my ($self, $c) = (shift, shift);
 
@@ -53,23 +57,31 @@ sub add_helper  { shift->_add(helpers  => @_) }
 sub get_data_template {
   my ($self, $options) = @_;
 
-  # Index DATA templates
-  my $loader = Mojo::Loader->new;
-  unless ($self->{index}) {
-    my $index = $self->{index} = {};
-    for my $class (reverse @{$self->classes}) {
-      $index->{$_} = $class for keys %{$loader->data($class)};
-    }
+  # Find template
+  return undef unless my $template = $self->template_name($options);
+  return $LOADER->data($self->{index}{$template}, $template);
+}
+
+sub get_helper {
+  my ($self, $name) = @_;
+
+  if (my $h = $self->{proxy}{$name} || $self->helpers->{$name}) { return $h }
+
+  my $found;
+  my $class = 'Mojolicious::Renderer::Helpers::' . md5_sum "$name:$self";
+  my $re = $name eq '' ? qr/^(([^.]+))/ : qr/^(\Q$name\E\.([^.]+))/;
+  for my $key (keys %{$self->helpers}) {
+    $key =~ $re ? ($found, my $method) = (1, $2) : next;
+    my $sub = $self->get_helper($1);
+    monkey_patch $class, $method => sub { ${shift()}->$sub(@_) };
   }
 
-  # Find template
-  my $template = $self->template_name($options);
-  return $loader->data($self->{index}{$template}, $template);
+  $found ? push @{$self->{namespaces}}, $class : return undef;
+  return $self->{proxy}{$name} = sub { bless \(my $dummy = shift), $class };
 }
 
 sub render {
   my ($self, $c, $args) = @_;
-  $args ||= {};
 
   # Localize "extends" and "layout" to allow argument overrides
   my $stash = $c->stash;
@@ -81,7 +93,7 @@ sub render {
   delete @{$stash}{qw(layout extends)} if $ts;
 
   # Merge stash and arguments
-  %$stash = (%$stash, %$args);
+  @$stash{keys %$args} = values %$args;
 
   my $options = {
     encoding => $self->encoding,
@@ -146,14 +158,15 @@ sub template_for {
     if $controller && $action;
 
   # Try the route name if we don't have controller and action
-  return undef unless my $endpoint = $c->match->endpoint;
-  return $endpoint->name;
+  return undef unless my $route = $c->match->endpoint;
+  return $route->name;
 }
 
 sub template_handler {
   my ($self, $options) = @_;
   return undef unless my $file = $self->template_name($options);
-  return $self->default_handler unless my $handlers = $self->_handlers($file);
+  return $self->default_handler
+    unless my $handlers = $self->{templates}{$file};
   return $handlers->[0];
 }
 
@@ -164,11 +177,13 @@ sub template_name {
   return undef unless my $format   = $options->{format};
   $template .= ".$format";
 
+  $self->_warmup unless $self->{templates};
+
   # Variants
   my $handler = $options->{handler};
   if (defined(my $variant = $options->{variant})) {
     $variant = "$template+$variant";
-    my $handlers = $self->_handlers($variant) // [];
+    my $handlers = $self->{templates}{$variant} // [];
     $template = $variant
       if @$handlers && !defined $handler || grep { $_ eq $handler } @$handlers;
   }
@@ -194,6 +209,7 @@ sub template_path {
 sub _add {
   my ($self, $attr, $name, $cb) = @_;
   $self->$attr->{$name} = $cb;
+  delete $self->{proxy};
   return $self;
 }
 
@@ -206,24 +222,6 @@ sub _extends {
   return delete $stash->{extends};
 }
 
-sub _handlers {
-  my ($self, $file) = @_;
-
-  unless ($self->{templates}) {
-
-    # Templates
-    s/\.(\w+)$// and push @{$self->{templates}{$_}}, $1
-      for map { sort @{Mojo::Home->new($_)->list_files} } @{$self->paths};
-
-    # DATA templates
-    my $loader = Mojo::Loader->new;
-    s/\.(\w+)$// and push @{$self->{templates}{$_}}, $1
-      for map { sort keys %{$loader->data($_)} } @{$self->classes};
-  }
-
-  return $self->{templates}{$file};
-}
-
 sub _render_template {
   my ($self, $c, $output, $options) = @_;
 
@@ -239,6 +237,22 @@ sub _render_template {
   return undef;
 }
 
+sub _warmup {
+  my $self = shift;
+
+  my ($index, $templates) = @$self{qw(index templates)} = ({}, {});
+
+  # Handlers for templates
+  s/\.(\w+)$// and push @{$templates->{$_}}, $1
+    for map { sort @{Mojo::Home->new($_)->list_files} } @{$self->paths};
+
+  # Handlers and classes for DATA templates
+  for my $class (reverse @{$self->classes}) {
+    $index->{$_} = $class for my @keys = sort keys %{$LOADER->data($class)};
+    s/\.(\w+)$// and unshift @{$templates->{$_}}, $1 for reverse @keys;
+  }
+}
+
 1;
 
 =encoding utf8
@@ -252,7 +266,7 @@ Mojolicious::Renderer - Generate dynamic content
   use Mojolicious::Renderer;
 
   my $renderer = Mojolicious::Renderer->new;
-  push @{$renderer->classes}, 'MyApp::Foo';
+  push @{$renderer->classes}, 'MyApp::Controller::Foo';
   push @{$renderer->paths}, '/home/sri/templates';
 
 =head1 DESCRIPTION
@@ -372,9 +386,17 @@ Register a new helper.
 
 Get a C<DATA> section template by name, usually used by handlers.
 
+=head2 get_helper
+
+  my $helper = $renderer->get_helper('url_for');
+
+Get a helper by full name, generate a helper dynamically for a prefix or
+return C<undef> if no helper or prefix could be found. Generated helpers
+return a proxy object containing the current controller object and on which
+nested helpers can be called.
+
 =head2 render
 
-  my ($output, $format) = $renderer->render(Mojolicious::Controller->new);
   my ($output, $format) = $renderer->render(Mojolicious::Controller->new, {
     template => 'foo/bar',
     foo      => 'bar'
@@ -1,7 +1,7 @@
 package Mojolicious::Routes::Match;
 use Mojo::Base -base;
 
-use Mojolicious::Routes::Route;
+use Mojo::Util;
 
 has current => 0;
 has [qw(endpoint root)];
@@ -10,89 +10,84 @@ has stack => sub { [] };
 sub match { $_[0]->_match($_[0]->root, $_[1], $_[2]) }
 
 sub path_for {
-  my ($self, $name, %values)
-    = (shift, Mojolicious::Routes::Route::_values(@_));
+  my ($self, $name, %values) = (shift, Mojo::Util::_options(@_));
 
   # Current route
-  my $endpoint;
-  if ($name && $name eq 'current' || !$name) {
-    return {} unless $endpoint = $self->endpoint;
+  my $route;
+  if (!$name || $name eq 'current') {
+    return {} unless $route = $self->endpoint;
   }
 
   # Find endpoint
-  else { return {path => $name} unless $endpoint = $self->root->lookup($name) }
+  else { return {path => $name} unless $route = $self->root->lookup($name) }
 
   # Merge values (clear format)
   my $captures = $self->stack->[-1] || {};
   %values = (%$captures, format => undef, %values);
-  my $pattern = $endpoint->pattern;
+  my $pattern = $route->pattern;
   $values{format}
     //= defined $captures->{format}
     ? $captures->{format}
     : $pattern->defaults->{format}
     if $pattern->constraints->{format};
 
-  my $path = $endpoint->render('', \%values);
-  return {path => $path, websocket => $endpoint->has_websocket};
+  my $path = $route->render(\%values);
+  return {path => $path, websocket => $route->has_websocket};
 }
 
 sub _match {
   my ($self, $r, $c, $options) = @_;
 
   # Pattern
-  my $path = $options->{path};
-  return
-    unless my $captures = $r->pattern->match_partial(\$path, $r->is_endpoint);
+  my $path    = $options->{path};
+  my $partial = $r->partial;
+  my $detect  = (my $endpoint = $r->is_endpoint) && !$partial;
+  return undef
+    unless my $captures = $r->pattern->match_partial(\$path, $detect);
   local $options->{path} = $path;
-  $captures = $self->{captures} = {%{$self->{captures} || {}}, %$captures};
+  local @{$self->{captures} ||= {}}{keys %$captures} = values %$captures;
+  $captures = $self->{captures};
 
   # Method
   my $methods = $r->via;
-  return if $methods && !grep { $_ eq $options->{method} } @$methods;
+  return undef if $methods && !grep { $_ eq $options->{method} } @$methods;
 
   # Conditions
   if (my $over = $r->over) {
     my $conditions = $self->{conditions} ||= $self->root->conditions;
     for (my $i = 0; $i < @$over; $i += 2) {
-      return unless my $condition = $conditions->{$over->[$i]};
-      return if !$condition->($r, $c, $captures, $over->[$i + 1]);
+      return undef unless my $condition = $conditions->{$over->[$i]};
+      return undef if !$condition->($r, $c, $captures, $over->[$i + 1]);
     }
   }
 
   # WebSocket
-  return if $r->is_websocket && !$options->{websocket};
+  return undef if $r->is_websocket && !$options->{websocket};
 
   # Partial
   my $empty = !length $path || $path eq '/';
-  if ($r->partial) {
+  if ($partial) {
     $captures->{path} = $path;
     $self->endpoint($r);
     $empty = 1;
   }
 
-  # Endpoint (or bridge)
-  my $endpoint = $r->is_endpoint;
+  # Endpoint (or intermediate destination)
   if (($endpoint && $empty) || $r->inline) {
     push @{$self->stack}, {%$captures};
     if ($endpoint && $empty) {
       my $format = $captures->{format};
       if ($format) { $_->{format} = $format for @{$self->stack} }
-      return $self->endpoint($r);
+      return !!$self->endpoint($r);
     }
     delete @$captures{qw(app cb)};
   }
 
   # Match children
-  my $snapshot = [@{$self->stack}];
+  my @snapshot = $r->parent ? ([@{$self->stack}], $captures) : ([], {});
   for my $child (@{$r->children}) {
-    $self->_match($child, $c, $options);
-
-    # Endpoint found
-    return if $self->endpoint;
-
-    # Reset
-    if   ($r->parent) { $self->stack([@$snapshot])->{captures} = $captures }
-    else              { $self->stack([])->{captures}           = {} }
+    return 1 if $self->_match($child, $c, $options);
+    $self->stack([@{$snapshot[0]}])->{captures} = $snapshot[1];
   }
 }
 
@@ -144,11 +139,11 @@ Current position on the L</"stack">, defaults to C<0>.
 
 =head2 endpoint
 
-  my $endpoint = $match->endpoint;
-  $match       = $match->endpoint(Mojolicious::Routes::Route->new);
+  my $route = $match->endpoint;
+  $match    = $match->endpoint(Mojolicious::Routes::Route->new);
 
 The route endpoint that matched, usually a L<Mojolicious::Routes::Route>
-objects.
+object.
 
 =head2 root
 
@@ -160,7 +155,7 @@ The root of the route structure, usually a L<Mojolicious::Routes> object.
 =head2 stack
 
   my $stack = $match->stack;
-  $match    = $match->stack([{foo => 'bar'}]);
+  $match    = $match->stack([{action => 'foo'}, {action => 'bar'}]);
 
 Captured parameters with nesting history.
 
@@ -20,12 +20,11 @@ sub match_partial {
   my ($self, $pathref, $detect) = @_;
 
   # Compile on demand
-  my $regex = $self->regex || $self->_compile;
-  my $format
-    = $detect ? ($self->format_regex || $self->_compile_format) : undef;
+  $self->_compile unless $self->{regex};
+  $self->_compile_format if $detect && !$self->{format_regex};
 
   # Match
-  return undef unless my @captures = $$pathref =~ $regex;
+  return undef unless my @captures = $$pathref =~ $self->regex;
   $$pathref = ${^POSTMATCH};
 
   # Merge captures
@@ -37,11 +36,10 @@ sub match_partial {
   }
 
   # Format
-  my $constraint = $self->constraints->{format};
-  return $captures if !$detect || defined $constraint && !$constraint;
-  if ($$pathref =~ s!^/?$format!!) { $captures->{format} = $1 }
-  elsif ($constraint) { return undef unless $captures->{format} }
-
+  return $captures unless $detect && (my $regex = $self->format_regex);
+  return undef unless $$pathref =~ $regex;
+  $captures->{format} = $1 if defined $1;
+  $$pathref = '';
   return $captures;
 }
 
@@ -59,31 +57,26 @@ sub parse {
 }
 
 sub render {
-  my ($self, $values, $render) = @_;
-
-  # Merge values with defaults
-  my $format = ($values ||= {})->{format};
-  my $defaults = $self->defaults;
-  $values = {%$defaults, %$values};
+  my ($self, $values, $endpoint) = @_;
 
   # Placeholders can only be optional without a format
-  my $optional = !$format;
+  my $optional = !(my $format = $values->{format});
 
   my $str = '';
   for my $token (reverse @{$self->tree}) {
-    my ($op, $value) = @$token[0, 1];
+    my ($op, $value) = @$token;
     my $fragment = '';
 
-    # Slash
-    if ($op eq 'slash') { $fragment = '/' unless $optional }
-
     # Text
-    elsif ($op eq 'text') { ($fragment, $optional) = ($value, 0) }
+    if ($op eq 'text') { ($fragment, $optional) = ($value, 0) }
+
+    # Slash
+    elsif ($op eq 'slash') { $fragment = '/' unless $optional }
 
     # Placeholder
     else {
-      $fragment = $values->{$value} // '';
-      my $default = $defaults->{$value};
+      my $default = $self->defaults->{$value};
+      $fragment = $values->{$value} // $default // '';
       if (!defined $default || ($default ne $fragment)) { $optional = 0 }
       elsif ($optional) { $fragment = '' }
     }
@@ -92,8 +85,7 @@ sub render {
   }
 
   # Format can be optional
-  $str ||= '/';
-  return $render && $format ? "$str.$format" : $str;
+  return $endpoint && $format ? "$str.$format" : $str;
 }
 
 sub _compile {
@@ -106,35 +98,34 @@ sub _compile {
   my $block = my $regex = '';
   my $optional = 1;
   for my $token (reverse @{$self->tree}) {
-    my ($op, $value) = @$token[0, 1];
+    my ($op, $value) = @$token;
     my $fragment = '';
 
+    # Text
+    if ($op eq 'text') { ($fragment, $optional) = (quotemeta $value, 0) }
+
     # Slash
-    if ($op eq 'slash') {
+    elsif ($op eq 'slash') {
       $regex = ($optional ? "(?:/$block)?" : "/$block") . $regex;
       ($block, $optional) = ('', 1);
       next;
     }
 
-    # Text
-    elsif ($op eq 'text') { ($fragment, $optional) = (quotemeta $value, 0) }
-
     # Placeholder
-    elsif ($op eq 'placeholder' || $op eq 'relaxed' || $op eq 'wildcard') {
+    else {
       unshift @$placeholders, $value;
 
       # Placeholder
-      if ($op eq 'placeholder') { $fragment = '([^\/\.]+)' }
+      if ($op eq 'placeholder') { $fragment = '([^/\.]+)' }
 
       # Relaxed
-      elsif ($op eq 'relaxed') { $fragment = '([^\/]+)' }
+      elsif ($op eq 'relaxed') { $fragment = '([^/]+)' }
 
       # Wildcard
-      elsif ($op eq 'wildcard') { $fragment = '(.+)' }
+      else { $fragment = '(.+)' }
 
       # Custom regex
-      my $constraint = $constraints->{$value};
-      $fragment = _compile_req($constraint) if $constraint;
+      if (my $c = $constraints->{$value}) { $fragment = _compile_req($c) }
 
       # Optional placeholder
       exists $defaults->{$value} ? ($fragment .= '?') : ($optional = 0);
@@ -146,23 +137,23 @@ sub _compile {
   # Not rooted with a slash
   $regex = "$block$regex" if $block;
 
-  return $self->regex(qr/^$regex/ps)->regex;
+  $self->regex(qr/^$regex/ps);
 }
 
 sub _compile_format {
   my $self = shift;
 
   # Default regex
-  my $c = $self->constraints;
-  return $self->format_regex(qr!\.([^/]+)$!)->format_regex
-    unless defined $c->{format};
+  my $format = $self->constraints->{format};
+  return $self->format_regex(qr!^/?(?:\.([^/]+))?$!) unless defined $format;
 
   # No regex
-  return undef unless $c->{format};
+  return undef unless $format;
 
   # Compile custom regex
-  my $regex = _compile_req($c->{format});
-  return $self->format_regex(qr!\.$regex$!)->format_regex;
+  my $regex = '\.' . _compile_req($format);
+  $regex = "(?:$regex)?" if $self->defaults->{format};
+  $self->format_regex(qr!^/?$regex$!);
 }
 
 sub _compile_req {
@@ -213,9 +204,15 @@ sub _tokenize {
     # Placeholder, relaxed or wildcard
     elsif ($inside) { $tree[-1][-1] .= $char }
 
-    # Text
+    # Text (optimize slash+text and *+text+slash+text)
     elsif ($tree[-1][0] eq 'text') { $tree[-1][-1] .= $char }
-    else                           { push @tree, ['text', $char] }
+    elsif (!$tree[-2] && $tree[-1][0] eq 'slash') {
+      @tree = (['text', "/$char"]);
+    }
+    elsif ($tree[-2] && $tree[-2][0] eq 'text' && $tree[-1][0] eq 'slash') {
+      pop @tree && ($tree[-1][-1] .= "/$char");
+    }
+    else { push @tree, ['text', $char] }
   }
 
   return $self->pattern($pattern)->tree(\@tree);
@@ -271,8 +268,8 @@ Compiled regular expression for format matching.
 
 =head2 pattern
 
-  my $pattern = $pattern->pattern;
-  $pattern    = $pattern->pattern('/(foo)/(bar)');
+  my $raw  = $pattern->pattern;
+  $pattern = $pattern->pattern('/(foo)/(bar)');
 
 Raw unparsed pattern.
 
@@ -321,7 +318,7 @@ Character indicating a relaxed placeholder, defaults to C<#>.
 =head2 tree
 
   my $tree = $pattern->tree;
-  $pattern = $pattern->tree([['slash'], ['text', 'foo']]);
+  $pattern = $pattern->tree([['text', '/foo']]);
 
 Pattern in parsed form. Note that this structure should only be used very
 carefully since it is very dynamic.
@@ -1,9 +1,10 @@
 package Mojolicious::Routes::Route;
 use Mojo::Base -base;
 
-use Carp 'croak';
+use Carp ();
+use Mojo::Util;
 use Mojolicious::Routes::Pattern;
-use Scalar::Util qw(blessed weaken);
+use Scalar::Util ();
 
 has [qw(inline parent partial)];
 has 'children' => sub { [] };
@@ -12,19 +13,19 @@ has pattern    => sub { Mojolicious::Routes::Pattern->new };
 sub AUTOLOAD {
   my $self = shift;
 
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
-  croak "Undefined subroutine &${package}::$method called"
-    unless blessed $self && $self->isa(__PACKAGE__);
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
+  Carp::croak "Undefined subroutine &${package}::$method called"
+    unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__);
 
   # Call shortcut with current route
-  croak qq{Can't locate object method "$method" via package "$package"}
+  Carp::croak qq{Can't locate object method "$method" via package "$package"}
     unless my $shortcut = $self->root->shortcuts->{$method};
   return $self->$shortcut(@_);
 }
 
 sub add_child {
   my ($self, $route) = @_;
-  weaken $route->remove->parent($self)->{parent};
+  Scalar::Util::weaken $route->remove->parent($self)->{parent};
   push @{$self->children}, $route;
   return $self;
 }
@@ -57,7 +58,10 @@ sub find {
 
 sub get { shift->_generate_route(GET => @_) }
 
+# DEPRECATED in Tiger Face!
 sub has_conditions {
+  Mojo::Util::deprecated
+    'Mojolicious::Routes::Route::has_conditions is DEPRECATED';
   my $self = shift;
   return 1 if @{$self->over || []};
   return undef unless my $parent = $self->parent;
@@ -68,9 +72,8 @@ sub has_custom_name { !!shift->{custom} }
 
 sub has_websocket {
   my $self = shift;
-  return 1 if $self->is_websocket;
-  return undef unless my $parent = $self->parent;
-  return $parent->is_websocket;
+  return $self->{has_websocket} if exists $self->{has_websocket};
+  return $self->{has_websocket} = grep { $_->is_websocket } @{$self->_chain};
 }
 
 sub is_endpoint { $_[0]->inline ? undef : !@{$_[0]->children} }
@@ -96,7 +99,7 @@ sub over {
   my $conditions = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
   return $self unless @$conditions;
   $self->{over} = $conditions;
-  $self->root->cache(0);
+  $self->root->cache->max_keys(0);
 
   return $self;
 }
@@ -120,22 +123,14 @@ sub remove {
 }
 
 sub render {
-  my ($self, $path, $values) = @_;
-
-  # Render pattern
-  my $prefix = $self->pattern->render($values, !$path);
-  $path = "$prefix$path" unless $prefix eq '/';
-  $path ||= '/' unless my $parent = $self->parent;
-
-  # Let parent render
-  return $parent ? $parent->render($path, $values) : $path;
+  my ($self, $values) = @_;
+  my $path = join '',
+    map { $_->pattern->render($values, !@{$_->children} && !$_->partial) }
+    @{$self->_chain};
+  return $path || '/';
 }
 
-sub root {
-  my $root = my $parent = shift;
-  $root = $parent while $parent = $parent->parent;
-  return $root;
-}
+sub root { shift->_chain->[0] }
 
 sub route {
   my $self   = shift;
@@ -150,7 +145,7 @@ sub to {
 
   my $pattern = $self->pattern;
   return $pattern->defaults unless @_;
-  my ($shortcut, %defaults) = _values(@_);
+  my ($shortcut, %defaults) = Mojo::Util::_options(@_);
 
   if ($shortcut) {
 
@@ -166,16 +161,13 @@ sub to {
     }
   }
 
-  $pattern->defaults({%{$pattern->defaults}, %defaults});
+  @{$pattern->defaults}{keys %defaults} = values %defaults;
 
   return $self;
 }
 
 sub to_string {
-  my $self = shift;
-  my $pattern = $self->parent ? $self->parent->to_string : '';
-  $pattern .= $self->pattern->pattern if $self->pattern->pattern;
-  return $pattern;
+  join '', map { $_->pattern->pattern // '' } @{shift->_chain};
 }
 
 sub under { shift->_generate_route(under => @_) }
@@ -194,6 +186,12 @@ sub websocket {
   return $route;
 }
 
+sub _chain {
+  my @chain = (my $parent = shift);
+  unshift @chain, $parent while $parent = $parent->parent;
+  return \@chain;
+}
+
 sub _generate_route {
   my ($self, $methods, @args) = @_;
 
@@ -219,28 +217,13 @@ sub _generate_route {
     elsif (ref $arg eq 'HASH') { %defaults = (%defaults, %$arg) }
   }
 
-  # Create bridge or route
   my $route
-    = $methods eq 'under'
-    ? $self->bridge($pattern, @constraints)
-    : $self->route($pattern, @constraints)->via($methods);
-  $route->over(\@conditions)->to(\%defaults);
+    = $self->route($pattern, @constraints)->over(\@conditions)->to(\%defaults);
+  $methods eq 'under' ? $route->inline(1) : $route->via($methods);
 
   return defined $name ? $route->name($name) : $route;
 }
 
-sub _values {
-
-  # Hash or name (one)
-  return ref $_[0] eq 'HASH' ? (undef, %{shift()}) : @_ if @_ == 1;
-
-  # Name and values (odd)
-  return shift, @_ if @_ % 2;
-
-  # Name and hash or just values (even)
-  return ref $_[1] eq 'HASH' ? (shift, %{shift()}) : (undef, @_);
-}
-
 1;
 
 =encoding utf8
@@ -276,7 +259,7 @@ The children of this route, used for nesting routes.
   my $bool = $r->inline;
   $r       = $r->inline($bool);
 
-Allow L</"bridge"> semantics for this route.
+Allow L</"under"> semantics for this route.
 
 =head2 parent
 
@@ -321,6 +304,7 @@ current parent if necessary.
   my $route = $r->any('/:foo' => {foo => 'bar'} => sub {...});
   my $route = $r->any('/:foo' => [foo => qr/\w+/] => sub {...});
   my $route = $r->any([qw(GET POST)] => '/:foo' => sub {...});
+  my $route = $r->any([qw(GET POST)] => '/:foo' => [foo => qr/\w+/]);
 
 Generate L<Mojolicious::Routes::Route> object matching any of the listed HTTP
 request methods or all. See also the L<Mojolicious::Lite> tutorial for many
@@ -330,13 +314,13 @@ more argument variations.
 
 =head2 bridge
 
-  my $bridge = $r->bridge;
-  my $bridge = $r->bridge('/:action');
-  my $bridge = $r->bridge('/:action', action => qr/\w+/);
-  my $bridge = $r->bridge(format => 0);
+  my $route = $r->bridge;
+  my $route = $r->bridge('/:action');
+  my $route = $r->bridge('/:action', action => qr/\w+/);
+  my $route = $r->bridge(format => 0);
 
-Low-level generator for bridge routes, returns a L<Mojolicious::Routes::Route>
-object.
+Low-level generator for nested routes with their own intermediate destination,
+returns a L<Mojolicious::Routes::Route> object.
 
   my $auth = $r->bridge('/user')->to('user#auth');
   $auth->get('/show')->to('#show');
@@ -386,12 +370,6 @@ See also the L<Mojolicious::Lite> tutorial for many more argument variations.
 
   $r->get('/user')->to('user#show');
 
-=head2 has_conditions
-
-  my $bool = $r->has_conditions;
-
-Check if this route has active conditions.
-
 =head2 has_custom_name
 
   my $bool = $r->has_custom_name;
@@ -402,7 +380,8 @@ Check if this route has a custom name.
 
   my $bool = $r->has_websocket;
 
-Check if this route has a WebSocket ancestor.
+Check if this route has a WebSocket ancestor and cache the result for future
+checks.
 
 =head2 is_endpoint
 
@@ -518,8 +497,7 @@ Remove route from parent.
 
 =head2 render
 
-  my $path = $r->render($suffix);
-  my $path = $r->render($suffix, {foo => 'bar'});
+  my $path = $r->render({foo => 'bar'});
 
 Render route with parameters into a path.
 
@@ -527,9 +505,7 @@ Render route with parameters into a path.
 
   my $root = $r->root;
 
-The L<Mojolicious::Routes> object this route is an descendent of.
-
-  $r->root->cache(0);
+The L<Mojolicious::Routes> object this route is a descendant of.
 
 =head2 route
 
@@ -566,13 +542,15 @@ Stringify the whole route.
 
 =head2 under
 
-  my $bridge = $r->under(sub {...});
-  my $bridge = $r->under('/:foo' => sub {...});
-  my $bridge = $r->under('/:foo' => [foo => qr/\w+/]);
-  my $bridge = $r->under({format => 0});
+  my $route = $r->under(sub {...});
+  my $route = $r->under('/:foo' => sub {...});
+  my $route = $r->under('/:foo' => {foo => 'bar'});
+  my $route = $r->under('/:foo' => [foo => qr/\w+/]);
+  my $route = $r->under([format => 0]);
 
-Generate L<Mojolicious::Routes::Route> object for bridge route. See also the
-L<Mojolicious::Lite> tutorial for many more argument variations.
+Generate L<Mojolicious::Routes::Route> object for a nested route with its own
+intermediate destination. See also the L<Mojolicious::Lite> tutorial for many
+more argument variations.
 
   my $auth = $r->under('/user')->to('user#auth');
   $auth->get('/show')->to('#show');
@@ -608,11 +586,13 @@ variations.
 In addition to the L</"ATTRIBUTES"> and L</"METHODS"> above you can also call
 shortcuts provided by L</"root"> on L<Mojolicious::Routes::Route> objects.
 
+  # Add a "firefox" shortcut
   $r->root->add_shortcut(firefox => sub {
     my ($r, $path) = @_;
     $r->get($path, agent => qr/Firefox/);
   });
 
+  # Use "firefox" shortcut to generate routes
   $r->firefox('/welcome')->to('firefox#welcome');
   $r->firefox('/bye')->to('firefox#bye');
 
@@ -34,9 +34,8 @@ sub continue {
 
   # Merge captures into stash
   my $stash = $c->stash;
-  my $captures = $stash->{'mojo.captures'} //= {};
-  %$captures = (%$captures, %$field);
-  %$stash    = (%$stash,    %$field);
+  @{$stash->{'mojo.captures'} //= {}}{keys %$field} = values %$field;
+  @$stash{keys %$field} = values %$field;
 
   my $continue;
   my $last = !$stack->[++$current];
@@ -87,17 +86,15 @@ sub match {
   my $match = Mojolicious::Routes::Match->new(root => $self);
   $c->match($match);
   my $cache = $self->cache;
-  my $cached = $cache ? $cache->get("$method:$path:$ws") : undef;
-  return $match->endpoint($cached->{endpoint})->stack($cached->{stack})
-    if $cached;
+  if (my $result = $cache->get("$method:$path:$ws")) {
+    return $match->endpoint($result->{endpoint})->stack($result->{stack});
+  }
 
   # Check routes
   $match->match($c => {method => $method, path => $path, websocket => $ws});
-
-  # Cache routes without conditions
-  return unless $cache && (my $endpoint = $match->endpoint);
-  my $result = {endpoint => $endpoint, stack => $match->stack};
-  $cache->set("$method:$path:$ws" => $result) unless $endpoint->has_conditions;
+  return unless my $route = $match->endpoint;
+  $cache->set(
+    "$method:$path:$ws" => {endpoint => $route, stack => $match->stack});
 }
 
 sub route {
@@ -145,11 +142,8 @@ sub _class {
   for my $class (@classes) {
 
     # Failed
-    unless (my $found = $self->_load($class)) {
-      next unless defined $found;
-      $log->debug(qq{Class "$class" is not a controller.});
-      return undef;
-    }
+    next unless defined(my $found = $self->_load($class));
+    return !$log->debug(qq{Class "$class" is not a controller.}) unless $found;
 
     # Success
     my $new = $class->new(%$c);
@@ -173,7 +167,7 @@ sub _controller {
   my $class = ref $new;
   my $app   = $old->app;
   my $log   = $app->log;
-  if (my $sub = $new->can('handler')) {
+  if ($new->isa('Mojo')) {
     $log->debug(qq{Routing to application "$class".});
 
     # Try to connect routes
@@ -181,7 +175,7 @@ sub _controller {
       my $r = $new->$sub;
       weaken $r->parent($old->match->endpoint)->{parent} unless $r->parent;
     }
-    $new->$sub($old);
+    $new->handler($old);
     $old->stash->{'mojo.routed'}++;
   }
 
@@ -263,9 +257,6 @@ L<Mojolicious::Controller> and L<Mojo>.
 
 Routing cache, defaults to a L<Mojo::Cache> object.
 
-  # Disable caching
-  $r->cache(0);
-
 =head2 conditions
 
   my $conditions = $r->conditions;
@@ -289,7 +280,7 @@ C<attr>, C<has>, C<new> and C<tap>.
 Namespaces to load controllers from.
 
   # Add another namespace to load controllers from
-  push @{$r->namespaces}, 'MyApp::Controller';
+  push @{$r->namespaces}, 'MyApp::MyController';
 
 =head2 shortcuts
 
@@ -14,6 +14,7 @@ has serialize          => sub { \&Mojo::JSON::encode_json };
 sub load {
   my ($self, $c) = @_;
 
+  return unless $c->req->headers->cookie;
   return unless my $value = $c->signed_cookie($self->cookie_name);
   $value =~ y/-/=/;
   return unless my $session = $self->deserialize->(b64_decode $value);
@@ -7,23 +7,28 @@ use Mojo::Asset::Memory;
 use Mojo::Date;
 use Mojo::Home;
 use Mojo::Loader;
+use Mojo::Util 'md5_sum';
 
 has classes => sub { ['main'] };
 has paths   => sub { [] };
 
-# Last modified default
-my $MTIME = time;
-
 # Bundled files
 my $HOME   = Mojo::Home->new;
 my $PUBLIC = $HOME->parse($HOME->mojo_lib_dir)->rel_dir('Mojolicious/public');
 
+my $LOADER = Mojo::Loader->new;
+
 sub dispatch {
   my ($self, $c) = @_;
 
+  # Method (GET or HEAD)
+  my $req    = $c->req;
+  my $method = $req->method;
+  return undef unless $method eq 'GET' || $method eq 'HEAD';
+
   # Canonical path
   my $stash = $c->stash;
-  my $path  = $c->req->url->path;
+  my $path  = $req->url->path;
   $path = $stash->{path} ? $path->new($stash->{path}) : $path->clone;
   return undef unless my @parts = @{$path->canonicalize->parts};
 
@@ -49,69 +54,80 @@ sub file {
   return $self->_get_file(catfile($PUBLIC, split('/', $rel)));
 }
 
+sub is_fresh {
+  my ($self, $c, $options) = @_;
+
+  my $res_headers = $c->res->headers;
+  my ($last, $etag) = @$options{qw(last_modified etag)};
+  $res_headers->last_modified(Mojo::Date->new($last)) if $last;
+  $res_headers->etag($etag = qq{"$etag"}) if $etag;
+
+  # Unconditional
+  my $req_headers = $c->req->headers;
+  my $match       = $req_headers->if_none_match;
+  return undef unless (my $since = $req_headers->if_modified_since) || $match;
+
+  # If-None-Match
+  return undef if $match && ($etag // $res_headers->etag // '') ne $match;
+
+  # If-Modified-Since
+  return !!$match unless ($last //= $res_headers->last_modified) && $since;
+  return _epoch($last) <= (_epoch($since) // 0);
+}
+
 sub serve {
   my ($self, $c, $rel) = @_;
+
   return undef unless my $asset = $self->file($rel);
+  my $headers = $c->res->headers;
+  return !!$self->serve_asset($c, $asset) if $headers->content_type;
+
+  # Content-Type
   my $types = $c->app->types;
   my $type = $rel =~ /\.(\w+)$/ ? $types->type($1) : undef;
-  $c->res->headers->content_type($type || $types->type('txt'));
+  $headers->content_type($type || $types->type('txt'));
   return !!$self->serve_asset($c, $asset);
 }
 
 sub serve_asset {
   my ($self, $c, $asset) = @_;
 
-  # Last modified
-  my $mtime = $asset->is_file ? (stat $asset->path)[9] : $MTIME;
+  # Last-Modified and ETag
   my $res = $c->res;
-  $res->code(200)->headers->last_modified(Mojo::Date->new($mtime))
-    ->accept_ranges('bytes');
-
-  # If modified since
-  my $headers = $c->req->headers;
-  if (my $date = $headers->if_modified_since) {
-    my $since = Mojo::Date->new($date)->epoch;
-    return $res->code(304) if defined $since && $since == $mtime;
-  }
+  $res->code(200)->headers->accept_ranges('bytes');
+  my $mtime = $asset->mtime;
+  my $options = {etag => md5_sum($mtime), last_modified => $mtime};
+  return $res->code(304) if $self->is_fresh($c, $options);
 
   # Range
-  my $size  = $asset->size;
-  my $start = 0;
-  my $end   = $size - 1;
-  if (my $range = $headers->range) {
-
-    # Not satisfiable
-    return $res->code(416) unless $size && $range =~ m/^bytes=(\d+)?-(\d+)?/;
-    $start = $1 if defined $1;
-    $end = $2 if defined $2 && $2 <= $end;
-    return $res->code(416) if $start > $end || $end > ($size - 1);
-
-    # Satisfiable
-    $res->code(206)->headers->content_length($end - $start + 1)
-      ->content_range("bytes $start-$end/$size");
-  }
-
+  return $res->content->asset($asset)
+    unless my $range = $c->req->headers->range;
+
+  # Not satisfiable
+  return $res->code(416) unless my $size = $asset->size;
+  return $res->code(416) unless $range =~ m/^bytes=(\d+)?-(\d+)?/;
+  my ($start, $end) = ($1 // 0, defined $2 && $2 < $size ? $2 : $size - 1);
+  return $res->code(416) if $start > $end;
+
+  # Satisfiable
+  $res->code(206)->headers->content_length($end - $start + 1)
+    ->content_range("bytes $start-$end/$size");
   return $res->content->asset($asset->start_range($start)->end_range($end));
 }
 
+sub _epoch { Mojo::Date->new(shift)->epoch }
+
 sub _get_data_file {
   my ($self, $rel) = @_;
 
-  # Protect templates
-  return undef if $rel =~ /\.\w+\.\w+$/;
+  # Protect files without extensions and templates with two extensions
+  return undef if $rel !~ /\.\w+$/ || $rel =~ /\.\w+\.\w+$/;
 
-  # Index DATA files
-  my $loader = Mojo::Loader->new;
-  unless ($self->{index}) {
-    my $index = $self->{index} = {};
-    for my $class (reverse @{$self->classes}) {
-      $index->{$_} = $class for keys %{$loader->data($class)};
-    }
-  }
+  $self->_warmup unless $self->{index};
 
   # Find file
   return undef
-    unless defined(my $data = $loader->data($self->{index}{$rel}, $rel));
+    unless defined(my $data = $LOADER->data($self->{index}{$rel}, $rel));
   return Mojo::Asset::Memory->new->add_chunk($data);
 }
 
@@ -121,6 +137,14 @@ sub _get_file {
   return -f $path && -r $path ? Mojo::Asset::File->new(path => $path) : undef;
 }
 
+sub _warmup {
+  my $self = shift;
+  my $index = $self->{index} = {};
+  for my $class (reverse @{$self->classes}) {
+    $index->{$_} = $class for keys %{$LOADER->data($class)};
+  }
+}
+
 1;
 
 =encoding utf8
@@ -134,13 +158,13 @@ Mojolicious::Static - Serve static files
   use Mojolicious::Static;
 
   my $static = Mojolicious::Static->new;
-  push @{$static->classes}, 'MyApp::Foo';
+  push @{$static->classes}, 'MyApp::Controller::Foo';
   push @{$static->paths}, '/home/sri/public';
 
 =head1 DESCRIPTION
 
-L<Mojolicious::Static> is a static file server with C<Range> and
-C<If-Modified-Since> support based on
+L<Mojolicious::Static> is a static file server with C<Range>,
+C<If-Modified-Since> and C<If-None-Match> support based on
 L<RFC 7232|http://tools.ietf.org/html/rfc7232> and
 L<RFC 7233|http://tools.ietf.org/html/rfc7233>.
 
@@ -191,6 +215,32 @@ protect from traversing to parent directories.
 
   my $content = $static->file('foo/bar.html')->slurp;
 
+=head2 is_fresh
+
+  my $bool = $static->is_fresh(Mojolicious::Controller->new, {etag => 'abc'});
+
+Check freshness of request by comparing the C<If-None-Match> and
+C<If-Modified-Since> request headers to the C<ETag> and C<Last-Modified>
+response headers.
+
+These options are currently available:
+
+=over 2
+
+=item etag
+
+  etag => 'abc'
+
+Add C<ETag> header before comparing.
+
+=item last_modified
+
+  last_modified => $epoch
+
+Add C<Last-Modified> header before comparing.
+
+=back
+
 =head2 serve
 
   my $bool = $static->serve(Mojolicious::Controller->new, 'images/logo.png');
@@ -203,8 +253,8 @@ that this method does not protect from traversing to parent directories.
 
   $static->serve_asset(Mojolicious::Controller->new, Mojo::Asset::File->new);
 
-Serve a L<Mojo::Asset::File> or L<Mojo::Asset::Memory> object with C<Range>
-and C<If-Modified-Since> support.
+Serve a L<Mojo::Asset::File> or L<Mojo::Asset::Memory> object with C<Range>,
+C<If-Modified-Since> and C<If-None-Match> support.
 
 =head1 SEE ALSO
 
@@ -1,8 +1,8 @@
 package Mojolicious::Validator::Validation;
 use Mojo::Base -base;
 
-use Carp 'croak';
-use Scalar::Util 'blessed';
+use Carp         ();
+use Scalar::Util ();
 
 has [qw(csrf_token topic validator)];
 has [qw(input output)] => sub { {} };
@@ -10,11 +10,11 @@ has [qw(input output)] => sub { {} };
 sub AUTOLOAD {
   my $self = shift;
 
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
-  croak "Undefined subroutine &${package}::$method called"
-    unless blessed $self && $self->isa(__PACKAGE__);
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
+  Carp::croak "Undefined subroutine &${package}::$method called"
+    unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__);
 
-  croak qq{Can't locate object method "$method" via package "$package"}
+  Carp::croak qq{Can't locate object method "$method" via package "$package"}
     unless $self->validator->checks->{$method};
   return $self->check($method => @_);
 }
@@ -54,6 +54,8 @@ sub error {
   return $self;
 }
 
+sub every_param { shift->_param(@_) }
+
 sub has_data { !!keys %{shift->input} }
 
 sub has_error { $_[1] ? exists $_[0]{error}{$_[1]} : !!keys %{$_[0]{error}} }
@@ -75,14 +77,12 @@ sub param {
   my ($self, $name) = @_;
 
   # Multiple names
-  return map { scalar $self->param($_) } @$name if ref $name eq 'ARRAY';
+  return map { $self->param($_) } @$name if ref $name eq 'ARRAY';
 
   # List names
   return sort keys %{$self->output} unless defined $name;
 
-  my $value = $self->output->{$name};
-  my @values = ref $value eq 'ARRAY' ? @$value : ($value);
-  return wantarray ? @values : $values[0];
+  return $self->_param($name)->[-1];
 }
 
 sub required {
@@ -91,6 +91,11 @@ sub required {
   return $self->error($name => ['required']);
 }
 
+sub _param {
+  return [] unless defined(my $value = shift->output->{shift()});
+  return [ref $value eq 'ARRAY' ? @$value : $value];
+}
+
 1;
 
 =encoding utf8
@@ -122,8 +127,8 @@ L<Mojolicious::Validator::Validation> implements the following attributes.
 
 =head2 csrf_token
 
-  my $token   = $validation->token;
-  $validation = $validation->token('fa6a08...');
+  my $token   = $validation->csrf_token;
+  $validation = $validation->csrf_token('fa6a08...');
 
 CSRF token.
 
@@ -184,6 +189,16 @@ only be one per field.
 
   my ($check, $result, @args) = @{$validation->error('foo')};
 
+=head2 every_param
+
+  my $values = $validation->every_param('foo');
+
+Similar to L</"param">, but returns all values sharing the same name as an
+array reference.
+
+  # Get first value
+  my $first = $validation->every_param('foo')->[0];
+
 =head2 has_data
 
   my $bool = $validation->has_data;
@@ -214,11 +229,12 @@ Change validation L</"topic">.
 =head2 param
 
   my @names       = $validation->param;
-  my $foo         = $validation->param('foo');
-  my @foo         = $validation->param('foo');
+  my $value       = $validation->param('foo');
   my ($foo, $bar) = $validation->param(['foo', 'bar']);
 
-Access validated parameters, similar to L<Mojolicious::Controller/"param">.
+Access validated parameters. If there are multiple values sharing the same
+name, and you want to access more than just the last one, you can use
+L</"every_param">.
 
 =head2 required
 
@@ -233,10 +249,14 @@ In addition to the L</"ATTRIBUTES"> and L</"METHODS"> above, you can also call
 validation checks provided by L</"validator"> on
 L<Mojolicious::Validator::Validation> objects, similar to L</"check">.
 
+  # Call validation checks
   $validation->required('foo')->size(2, 5)->like(qr/^[A-Z]/);
   $validation->optional('bar')->equal_to('foo');
   $validation->optional('baz')->in(qw(test 123));
 
+  # Longer version
+  $validation->required('foo')->check('size', 2,5)->check('like', qr/^[A-Z]/);
+
 =head1 SEE ALSO
 
 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
@@ -117,7 +117,7 @@
     %= include inline => app->renderer->_bundled('mojobar')
     <script>
       function mojoDrawer (handle, drawer) {
-        $(handle).click(function() {
+        $(handle).click(function () {
           $(drawer).slideToggle('slow');
           var text = $(handle + ' div.tap').text();
           text = text == "tap for more" ? "tap for less" : "tap for more";
@@ -125,7 +125,7 @@
         });
         $(drawer).toggle();
       }
-      $(document).ready(function() {
+      $(document).ready(function () {
         mojoDrawer('#trace', '#frames');
         mojoDrawer('#more', '#infos');
       });
@@ -179,8 +179,8 @@
             <div class="tap">tap for more</div>
             <script>
               var current = '#context';
-              $('#showcase').click(function() {
-                $(current).slideToggle('slow', function() {
+              $('#showcase').click(function () {
+                $(current).slideToggle('slow', function () {
                   if (current == '#context') {
                     current = '#insight';
                   }
@@ -216,8 +216,8 @@
           <h1>Page not found... yet!</h1>
           <p>
             None of these routes could generate a response for your
-            <code><%= $self->req->method %></code> request for
-            <code><%= $self->req->url->path->to_route %></code>, maybe you need
+            <code><%= $c->req->method %></code> request for
+            <code><%= $c->req->url->path->to_route %></code>, maybe you need
             to add a new one?
           </p>
           % my $walk = begin
@@ -254,7 +254,7 @@
       % }
       <div id="request" class="box infobox spaced">
         <table>
-          % my $req = $self->req;
+          % my $req = $c->req;
           %= $kv->(Method => $req->method)
           % my $url = $req->url;
           %= $kv->(URL => $url->to_string)
@@ -263,8 +263,8 @@
           %= $kv->(Stash => dumper $snapshot)
           %= $kv->(Session => dumper session)
           %= $kv->(Version => $req->version)
-          % for my $name (sort @{$self->req->headers->names}) {
-            % my $value = $self->req->headers->header($name);
+          % for my $name (sort @{$c->req->headers->names}) {
+            % my $value = $c->req->headers->header($name);
             %= $kv->($name, $value)
           % }
         </table>
@@ -56,11 +56,11 @@
     %= link_to Documentation => 'http://mojolicio.us/perldoc'
     %= link_to Wiki => 'https://github.com/kraih/mojo/wiki'
     %= link_to GitHub => 'https://github.com/kraih/mojo'
-    %= link_to CPAN => 'http://metacpan.org/release/Mojolicious/'
-    %= link_to MailingList => 'http://groups.google.com/group/mojolicious'
+    %= link_to CPAN => 'https://metacpan.org/release/Mojolicious/'
+    %= link_to MailingList => 'https://groups.google.com/group/mojolicious'
     %= link_to Blog => 'http://blog.kraih.com'
-    %= link_to Twitter => 'http://twitter.com/kraih'
-    %= form_for 'http://google.com/cse' => (target => '_blank') => begin
+    %= link_to Twitter => 'https://twitter.com/kraih'
+    %= form_for 'https://www.google.com/cse' => (target => '_blank') => begin
       %= hidden_field cx => '014527573091551588235:pwfplkjpgbi'
       %= hidden_field ie => 'UTF-8'
       %= search_field 'q', placeholder => 'Search'
@@ -106,7 +106,7 @@
       }
     });
   });
-  $(document).ready(function() {
+  $(document).ready(function () {
     $('a[href^="#"]').addClass('mojoscroll');
     $(".mojoscroll").click(function(e) {
       e.preventDefault();
@@ -16,7 +16,7 @@
         line-height: 1.5em;
         margin: 0;
       }
-      code {
+      :not(pre) > code {
         background-color: rgba(0, 0, 0, 0.04);
         border-radius: 3px;
         font: 0.9em Consolas, Menlo, Monaco, Courier, monospace;
@@ -30,17 +30,24 @@
         position: relative;
       }
       h1 a, h2 a, h3 a { text-decoration: none }
+      li > p {
+        margin-bottom: 0;
+        margin-top: 0;
+      }
       pre {
         background: url(<%= url_for '/mojo/stripes.png' %>);
         border: 1px solid #d1d1d1;
         border-radius: 3px;
         box-shadow: 0 1px #fff, inset -1px 1px 4px rgba(0, 0, 0, 0.1);
-        color: #4d4d4c;
-        font: 0.9em Consolas, Menlo, Monaco, Courier, monospace;
-        line-height: 1.5em;
+        font: 100% Consolas, Menlo, Monaco, Courier, monospace;
         padding: 1em;
         padding-bottom: 1.5em;
         padding-top: 1.5em;
+      }
+      pre > code {
+        color: #4d4d4c;
+        font: 0.9em Consolas, Menlo, Monaco, Courier, monospace;
+        line-height: 1.5em;
         text-align: left;
         text-shadow: #eee 0 1px 0;
         white-space: pre-wrap;
@@ -2,9 +2,9 @@ package Mojolicious;
 use Mojo::Base 'Mojo';
 
 # "Fry: Shut up and take my money!"
-use Carp 'croak';
+use Carp ();
 use Mojo::Exception;
-use Mojo::Util 'decamelize';
+use Mojo::Util;
 use Mojolicious::Commands;
 use Mojolicious::Controller;
 use Mojolicious::Plugins;
@@ -14,17 +14,17 @@ use Mojolicious::Sessions;
 use Mojolicious::Static;
 use Mojolicious::Types;
 use Mojolicious::Validator;
-use Scalar::Util qw(blessed weaken);
-use Time::HiRes 'gettimeofday';
+use Scalar::Util ();
+use Time::HiRes  ();
 
 has commands => sub {
   my $commands = Mojolicious::Commands->new(app => shift);
-  weaken $commands->{app};
+  Scalar::Util::weaken $commands->{app};
   return $commands;
 };
 has controller_class => 'Mojolicious::Controller';
 has mode => sub { $ENV{MOJO_MODE} || $ENV{PLACK_ENV} || 'development' };
-has moniker  => sub { decamelize ref shift };
+has moniker  => sub { Mojo::Util::decamelize ref shift };
 has plugins  => sub { Mojolicious::Plugins->new };
 has renderer => sub { Mojolicious::Renderer->new };
 has routes   => sub { Mojolicious::Routes->new };
@@ -43,18 +43,18 @@ has types     => sub { Mojolicious::Types->new };
 has validator => sub { Mojolicious::Validator->new };
 
 our $CODENAME = 'Tiger Face';
-our $VERSION  = '5.16';
+our $VERSION  = '5.62';
 
 sub AUTOLOAD {
   my $self = shift;
 
-  my ($package, $method) = split /::(\w+)$/, our $AUTOLOAD;
-  croak "Undefined subroutine &${package}::$method called"
-    unless blessed $self && $self->isa(__PACKAGE__);
+  my ($package, $method) = our $AUTOLOAD =~ /^(.+)::(.+)$/;
+  Carp::croak "Undefined subroutine &${package}::$method called"
+    unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__);
 
   # Call helper with fresh controller
-  croak qq{Can't locate object method "$method" via package "$package"}
-    unless my $helper = $self->renderer->helpers->{$method};
+  Carp::croak qq{Can't locate object method "$method" via package "$package"}
+    unless my $helper = $self->renderer->get_helper($method);
   return $self->build_controller->$helper(@_);
 }
 
@@ -68,10 +68,11 @@ sub build_controller {
   $stash->{'mojo.secrets'} //= $self->secrets;
 
   # Build default controller
-  %$stash = (%$stash, %{$self->defaults});
+  my $defaults = $self->defaults;
+  @$stash{keys %$defaults} = values %$defaults;
   my $c
     = $self->controller_class->new(app => $self, stash => $stash, tx => $tx);
-  weaken $c->{app};
+  Scalar::Util::weaken $c->{app};
 
   return $c;
 }
@@ -83,7 +84,7 @@ sub build_tx {
   return $tx;
 }
 
-sub defaults { shift->_dict(defaults => @_) }
+sub defaults { Mojo::Util::_stash(defaults => @_) }
 
 sub dispatch {
   my ($self, $c) = @_;
@@ -105,7 +106,7 @@ sub dispatch {
     my $method = $req->method;
     my $path   = $req->url->path->to_abs_string;
     $self->log->debug(qq{$method "$path".});
-    $stash->{'mojo.started'} = [gettimeofday];
+    $stash->{'mojo.started'} = [Time::HiRes::gettimeofday];
   }
 
   # Routes
@@ -128,7 +129,7 @@ sub handler {
 
   # Process with chain
   my $c = $self->build_controller(@_);
-  weaken $c->{tx};
+  Scalar::Util::weaken $c->{tx};
   $self->plugins->emit_chain(around_dispatch => $c);
 
   # Delayed response
@@ -153,20 +154,24 @@ sub new {
   push @{$self->renderer->paths}, $home->rel_dir('templates');
   push @{$self->static->paths},   $home->rel_dir('public');
 
-  # Default to application namespace
-  my $r = $self->routes->namespaces([ref $self]);
+  # Default to controller and application namespace
+  my $r = $self->routes->namespaces(["@{[ref $self]}::Controller", ref $self]);
 
-  # Hide controller attributes/methods and "handler"
-  $r->hide(qw(app continue cookie finish flash handler match on param));
+  # Hide controller attributes/methods
+  $r->hide(qw(app continue cookie every_cookie every_param));
+  $r->hide(qw(every_signed_cookie finish flash helpers match on param));
   $r->hide(qw(redirect_to render render_exception render_later render_maybe));
-  $r->hide(qw(render_not_found render_static render_to_string rendered req));
-  $r->hide(qw(res respond_to send session signed_cookie stash tx url_for));
-  $r->hide(qw(validation write write_chunk));
+  $r->hide(qw(render_not_found render_to_string rendered req res respond_to));
+  $r->hide(qw(send session signed_cookie stash tx url_for validation write));
+  $r->hide(qw(write_chunk));
 
-  # Check if we have a log directory
+  # DEPRECATED in Tiger Face!
+  $r->hide('render_static');
+
+  # Check if we have a log directory that is writable
   my $mode = $self->mode;
   $self->log->path($home->rel_file("log/$mode.log"))
-    if -w $home->rel_file('log');
+    if -d $home->rel_file('log') && -w _;
 
   $self->plugin($_)
     for qw(HeaderCondition DefaultHelpers TagHelpers EPLRenderer EPRenderer);
@@ -187,7 +192,11 @@ sub plugin {
   $self->plugins->register_plugin(shift, $self, @_);
 }
 
-sub start { shift->commands->run(@_ ? @_ : @ARGV) }
+sub start {
+  my $self = shift;
+  $_->_warmup for $self->static, $self->renderer;
+  return $self->commands->run(@_ ? @_ : @ARGV);
+}
 
 sub startup { }
 
@@ -219,7 +228,7 @@ Mojolicious - Real-time web framework
   }
 
   # Controller
-  package MyApp::Foo;
+  package MyApp::Controller::Foo;
   use Mojo::Base 'Mojolicious::Controller';
 
   # Action
@@ -356,8 +365,9 @@ Useful for rewriting outgoing responses and other post-processing tasks.
 Emitted right before the L</"before_dispatch"> hook and wraps around the whole
 dispatch process, so you have to manually forward to the next hook if you want
 to continue the chain. Default exception handling with
-L<Mojolicious::Controller/"render_exception"> is the first hook in the chain
-and a call to L</"dispatch"> the last, yours will be in between.
+L<Mojolicious::Plugin::DefaultHelpers/"reply-E<gt>exception"> is the first
+hook in the chain and a call to L</"dispatch"> the last, yours will be in
+between.
 
   $app->hook(around_dispatch => sub {
     my ($next, $c) = @_;
@@ -456,7 +466,7 @@ startup method to define the url endpoints for your application.
   $r->post('/baz')->to('test#baz');
 
   # Add another namespace to load controllers from
-  push @{$app->routes->namespaces}, 'MyApp::Controller';
+  push @{$app->routes->namespaces}, 'MyApp::MyController';
 
 =head2 secrets
 
@@ -664,7 +674,11 @@ Note that application helpers are always called with a new default controller
 object, so they can't depend on or change controller state, which includes
 request, response and stash.
 
-  $app->log->debug($app->dumper({foo => 'bar'}));
+  # Call helper
+  say $app->dumper({foo => 'bar'});
+
+  # Longer version
+  say $app->build_controller->helpers->dumper({foo => 'bar'});
 
 =head1 BUNDLED FILES
 
@@ -737,6 +751,8 @@ Abhijit Menon-Sen, C<ams@cpan.org>
 
 Glen Hinkle, C<tempire@cpan.org>
 
+Jan Henning Thorsen, C<jhthorsen@cpan.org>
+
 Joel Berger, C<jberger@cpan.org>
 
 Marcus Ramberg, C<mramberg@cpan.org>
@@ -853,8 +869,6 @@ Ilya Chesnokov
 
 James Duncan
 
-Jan Henning Thorsen
-
 Jan Jona Javorsek
 
 Jan Schmidt
@@ -877,6 +891,8 @@ Kevin Old
 
 Kitamura Akatsuki
 
+Klaus S. Madsen
+
 Lars Balker Rasmussen
 
 Leon Brocard
@@ -885,6 +901,10 @@ Magnus Holm
 
 Maik Fischer
 
+Mark Fowler
+
+Mark Grimes
+
 Mark Stosberg
 
 Marty Tennison
@@ -929,6 +949,8 @@ Peter Edwards
 
 Pierre-Yves Ritschard
 
+Piotr Roszatycki
+
 Quentin Carbonneaux
 
 Rafal Pocztarski
@@ -969,6 +991,8 @@ Tatsuhiko Miyagawa
 
 Terrence Brannon
 
+Tianon Gravi
+
 Tomas Znamenacek
 
 Ulrich Habel
@@ -265,14 +265,13 @@ sub send_ok {
 
 sub status_is {
   my ($self, $status, $desc) = @_;
-  $desc ||= "$status " . $self->tx->res->new(code => $status)->default_message;
+  $desc ||= "$status " . $self->tx->res->default_message($status);
   return $self->_test('is', $self->tx->res->code, $status, $desc);
 }
 
 sub status_isnt {
   my ($self, $status, $desc) = @_;
-  $desc
-    ||= "not $status " . $self->tx->res->new(code => $status)->default_message;
+  $desc ||= "not $status " . $self->tx->res->default_message($status);
   return $self->_test('isnt', $self->tx->res->code, $status, $desc);
 }
 
@@ -421,8 +420,8 @@ Test::Mojo - Testing Mojo!
 
 =head1 DESCRIPTION
 
-L<Test::Mojo> is a collection of testing helpers for everyone developing
-L<Mojo> and L<Mojolicious> applications.
+L<Test::Mojo> is a test user agent based on L<Mojo::UserAgent>, it is usually
+used together with L<Test::More> to test L<Mojolicious> applications.
 
 =head1 ATTRIBUTES
 
@@ -433,7 +432,14 @@ L<Test::Mojo> implements the following attributes.
   my $msg = $t->message;
   $t      = $t->message([text => $bytes]);
 
-Current WebSocket message.
+Current WebSocket message represented as an array reference containing the
+frame type and payload.
+
+  # More specific tests
+  use Mojo::JSON 'decode_json';
+  my $hash = decode_json $t->message->[1];
+  is ref $hash, 'HASH', 'right reference';
+  is $hash->{foo}, 'bar', 'right value';
 
   # Test custom message
   $t->message([binary => $bytes])
@@ -504,7 +510,7 @@ following new ones.
 =head2 app
 
   my $app = $t->app;
-  $t      = $t->app(MyApp->new);
+  $t      = $t->app(Mojolicious->new);
 
 Access application with L<Mojo::UserAgent::Server/"app">.
 
@@ -591,9 +597,9 @@ Opposite of L</"content_type_like">.
 =head2 delete_ok
 
   $t = $t->delete_ok('/foo');
-  $t = $t->delete_ok('/foo' => {DNT => 1} => 'Hi!');
-  $t = $t->delete_ok('/foo' => {DNT => 1} => form => {a => 'b'});
-  $t = $t->delete_ok('/foo' => {DNT => 1} => json => {a => 'b'});
+  $t = $t->delete_ok('/foo' => {Accept => '*/*'} => 'Hi!');
+  $t = $t->delete_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
+  $t = $t->delete_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform a C<DELETE> request and check for transport errors, takes the same
 arguments as L<Mojo::UserAgent/"delete">, except for the callback.
@@ -630,9 +636,9 @@ Wait for WebSocket connection to be closed gracefully and check status.
 =head2 get_ok
 
   $t = $t->get_ok('/foo');
-  $t = $t->get_ok('/foo' => {DNT => 1} => 'Hi!');
-  $t = $t->get_ok('/foo' => {DNT => 1} => form => {a => 'b'});
-  $t = $t->get_ok('/foo' => {DNT => 1} => json => {a => 'b'});
+  $t = $t->get_ok('/foo' => {Accept => '*/*'} => 'Hi!');
+  $t = $t->get_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
+  $t = $t->get_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform a C<GET> request and check for transport errors, takes the same
 arguments as L<Mojo::UserAgent/"get">, except for the callback.
@@ -640,41 +646,45 @@ arguments as L<Mojo::UserAgent/"get">, except for the callback.
   # Run tests against remote host
   $t->get_ok('http://mojolicio.us/perldoc')->status_is(200);
 
+  # Run additional tests on the transaction
+  $t->get_ok('/foo')->status_is(200);
+  is $t->tx->res->dom->at('input')->{value}, 'whatever', 'right value';
+
 =head2 head_ok
 
   $t = $t->head_ok('/foo');
-  $t = $t->head_ok('/foo' => {DNT => 1} => 'Hi!');
-  $t = $t->head_ok('/foo' => {DNT => 1} => form => {a => 'b'});
-  $t = $t->head_ok('/foo' => {DNT => 1} => json => {a => 'b'});
+  $t = $t->head_ok('/foo' => {Accept => '*/*'} => 'Hi!');
+  $t = $t->head_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
+  $t = $t->head_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform a C<HEAD> request and check for transport errors, takes the same
 arguments as L<Mojo::UserAgent/"head">, except for the callback.
 
 =head2 header_is
 
-  $t = $t->header_is(Expect => 'fun');
-  $t = $t->header_is(Expect => 'fun', 'right header');
+  $t = $t->header_is(ETag => '"abc321"');
+  $t = $t->header_is(ETag => '"abc321"', 'right header');
 
 Check response header for exact match.
 
 =head2 header_isnt
 
-  $t = $t->header_isnt(Expect => 'fun');
-  $t = $t->header_isnt(Expect => 'fun', 'different header');
+  $t = $t->header_isnt(Etag => '"abc321"');
+  $t = $t->header_isnt(ETag => '"abc321"', 'different header');
 
 Opposite of L</"header_is">.
 
 =head2 header_like
 
-  $t = $t->header_like(Expect => qr/fun/);
-  $t = $t->header_like(Expect => qr/fun/, 'right header');
+  $t = $t->header_like(ETag => qr/abc/);
+  $t = $t->header_like(ETag => qr/abc/, 'right header');
 
 Check response header for similar match.
 
 =head2 header_unlike
 
-  $t = $t->header_like(Expect => qr/fun/);
-  $t = $t->header_like(Expect => qr/fun/, 'different header');
+  $t = $t->header_unlike(ETag => qr/abc/);
+  $t = $t->header_unlike(ETag => qr/abc/, 'different header');
 
 Opposite of L</"header_like">.
 
@@ -818,9 +828,9 @@ Construct a new L<Test::Mojo> object.
 =head2 options_ok
 
   $t = $t->options_ok('/foo');
-  $t = $t->options_ok('/foo' => {DNT => 1} => 'Hi!');
-  $t = $t->options_ok('/foo' => {DNT => 1} => form => {a => 'b'});
-  $t = $t->options_ok('/foo' => {DNT => 1} => json => {a => 'b'});
+  $t = $t->options_ok('/foo' => {Accept => '*/*'} => 'Hi!');
+  $t = $t->options_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
+  $t = $t->options_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform a C<OPTIONS> request and check for transport errors, takes the same
 arguments as L<Mojo::UserAgent/"options">, except for the callback.
@@ -838,9 +848,9 @@ Invoke callback if the value of L</"success"> is false.
 =head2 patch_ok
 
   $t = $t->patch_ok('/foo');
-  $t = $t->patch_ok('/foo' => {DNT => 1} => 'Hi!');
-  $t = $t->patch_ok('/foo' => {DNT => 1} => form => {a => 'b'});
-  $t = $t->patch_ok('/foo' => {DNT => 1} => json => {a => 'b'});
+  $t = $t->patch_ok('/foo' => {Accept => '*/*'} => 'Hi!');
+  $t = $t->patch_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
+  $t = $t->patch_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform a C<PATCH> request and check for transport errors, takes the same
 arguments as L<Mojo::UserAgent/"patch">, except for the callback.
@@ -848,9 +858,9 @@ arguments as L<Mojo::UserAgent/"patch">, except for the callback.
 =head2 post_ok
 
   $t = $t->post_ok('/foo');
-  $t = $t->post_ok('/foo' => {DNT => 1} => 'Hi!');
-  $t = $t->post_ok('/foo' => {DNT => 1} => form => {a => 'b'});
-  $t = $t->post_ok('/foo' => {DNT => 1} => json => {a => 'b'});
+  $t = $t->post_ok('/foo' => {Accept => '*/*'} => 'Hi!');
+  $t = $t->post_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
+  $t = $t->post_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform a C<POST> request and check for transport errors, takes the same
 arguments as L<Mojo::UserAgent/"post">, except for the callback.
@@ -867,9 +877,9 @@ arguments as L<Mojo::UserAgent/"post">, except for the callback.
 =head2 put_ok
 
   $t = $t->put_ok('/foo');
-  $t = $t->put_ok('/foo' => {DNT => 1} => 'Hi!');
-  $t = $t->put_ok('/foo' => {DNT => 1} => form => {a => 'b'});
-  $t = $t->put_ok('/foo' => {DNT => 1} => json => {a => 'b'});
+  $t = $t->put_ok('/foo' => {Accept => '*/*'} => 'Hi!');
+  $t = $t->put_ok('/foo' => {Accept => '*/*'} => form => {a => 'b'});
+  $t = $t->put_ok('/foo' => {Accept => '*/*'} => json => {a => 'b'});
 
 Perform a C<PUT> request and check for transport errors, takes the same
 arguments as L<Mojo::UserAgent/"put">, except for the callback.
@@ -1,6 +1,7 @@
 package ojo;
 use Mojo::Base -strict;
 
+use Benchmark qw(timeit timestr :hireswallclock);
 use Mojo::ByteStream 'b';
 use Mojo::Collection 'c';
 use Mojo::DOM;
@@ -14,7 +15,7 @@ sub import {
 
   # Mojolicious::Lite
   my $caller = caller;
-  eval "package $caller; use Mojolicious::Lite;";
+  eval "package $caller; use Mojolicious::Lite; 1" or die $@;
   my $ua = $caller->app->ua;
   $ua->server->app->hook(around_action => sub { local $_ = $_[1]; $_[0]->() });
 
@@ -26,10 +27,11 @@ sub import {
     a => sub { $caller->can('any')->(@_) and return $ua->server->app },
     b => \&b,
     c => \&c,
-    d => sub { _request($ua, 'DELETE',  @_) },
-    g => sub { _request($ua, 'GET',     @_) },
-    h => sub { _request($ua, 'HEAD',    @_) },
+    d => sub { _request($ua, 'DELETE', @_) },
+    g => sub { _request($ua, 'GET',    @_) },
+    h => sub { _request($ua, 'HEAD',   @_) },
     j => \&j,
+    n => sub (&@) { say STDERR timestr timeit($_[1] // 1, $_[0]) },
     o => sub { _request($ua, 'OPTIONS', @_) },
     p => sub { _request($ua, 'POST',    @_) },
     r => \&dumper,
@@ -43,8 +45,7 @@ sub _request {
 
   my $tx  = $ua->start($ua->build_tx(@_));
   my $err = $tx->error;
-  warn
-    qq/Problem loading URL "@{[$tx->req->url->to_abs]}". ($err->{message})\n/
+  warn qq/Problem loading URL "@{[$tx->req->url]}": $err->{message}\n/
     if $err && !$err->{code};
 
   return $tx->res;
@@ -111,7 +112,7 @@ Turn list into a L<Mojo::Collection> object.
 =head2 d
 
   my $res = d('example.com');
-  my $res = d('http://example.com' => {DNT => 1} => 'Hi!');
+  my $res = d('http://example.com' => {Accept => '*/*'} => 'Hi!');
 
 Perform C<DELETE> request with L<Mojo::UserAgent/"delete"> and return
 resulting L<Mojo::Message::Response> object.
@@ -119,17 +120,17 @@ resulting L<Mojo::Message::Response> object.
 =head2 g
 
   my $res = g('example.com');
-  my $res = g('http://example.com' => {DNT => 1} => 'Hi!');
+  my $res = g('http://example.com' => {Accept => '*/*'} => 'Hi!');
 
 Perform C<GET> request with L<Mojo::UserAgent/"get"> and return resulting
 L<Mojo::Message::Response> object.
 
-  $ perl -Mojo -E 'say g("mojolicio.us")->dom("h1, h2, h3")->text'
+  $ perl -Mojo -E 'say g("mojolicio.us")->dom("h1")->map("text")->join("\n")'
 
 =head2 h
 
   my $res = h('example.com');
-  my $res = h('http://example.com' => {DNT => 1} => 'Hi!');
+  my $res = h('http://example.com' => {Accept => '*/*'} => 'Hi!');
 
 Perform C<HEAD> request with L<Mojo::UserAgent/"head"> and return resulting
 L<Mojo::Message::Response> object.
@@ -144,10 +145,20 @@ Encode Perl data structure or decode JSON with L<Mojo::JSON/"j">.
 
   $ perl -Mojo -E 'b(j({hello => "world!"}))->spurt("hello.json")'
 
+=head2 n
+
+  n {...};
+  n {...} 100;
+
+Benchmark block and print the results to C<STDERR>, with an optional number of
+iterations, which defaults to C<1>.
+
+  $ perl -Mojo -E 'n { say g("mojolicio.us")->code }'
+
 =head2 o
 
   my $res = o('example.com');
-  my $res = o('http://example.com' => {DNT => 1} => 'Hi!');
+  my $res = o('http://example.com' => {Accept => '*/*'} => 'Hi!');
 
 Perform C<OPTIONS> request with L<Mojo::UserAgent/"options"> and return
 resulting L<Mojo::Message::Response> object.
@@ -155,7 +166,7 @@ resulting L<Mojo::Message::Response> object.
 =head2 p
 
   my $res = p('example.com');
-  my $res = p('http://example.com' => {DNT => 1} => 'Hi!');
+  my $res = p('http://example.com' => {Accept => '*/*'} => 'Hi!');
 
 Perform C<POST> request with L<Mojo::UserAgent/"post"> and return resulting
 L<Mojo::Message::Response> object.
@@ -171,7 +182,7 @@ Dump a Perl data structure with L<Mojo::Util/"dumper">.
 =head2 t
 
   my $res = t('example.com');
-  my $res = t('http://example.com' => {DNT => 1} => 'Hi!');
+  my $res = t('http://example.com' => {Accept => '*/*'} => 'Hi!');
 
 Perform C<PATCH> request with L<Mojo::UserAgent/"patch"> and return resulting
 L<Mojo::Message::Response> object.
@@ -179,7 +190,7 @@ L<Mojo::Message::Response> object.
 =head2 u
 
   my $res = u('example.com');
-  my $res = u('http://example.com' => {DNT => 1} => 'Hi!');
+  my $res = u('http://example.com' => {Accept => '*/*'} => 'Hi!');
 
 Perform C<PUT> request with L<Mojo::UserAgent/"put"> and return resulting
 L<Mojo::Message::Response> object.
@@ -3,16 +3,13 @@
 use strict;
 use warnings;
 
-use FindBin;
-BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
-
 use Getopt::Long qw(GetOptions :config no_auto_abbrev no_ignore_case);
 
 GetOptions
-  'f|foreground' => sub { $ENV{HYPNOTOAD_FOREGROUND} = 1 },
+  'f|foreground' => \$ENV{HYPNOTOAD_FOREGROUND},
   'h|help'       => \my $help,
-  's|stop'       => sub { $ENV{HYPNOTOAD_STOP}       = 1 },
-  't|test'       => sub { $ENV{HYPNOTOAD_TEST}       = 1 };
+  's|stop'       => \$ENV{HYPNOTOAD_STOP},
+  't|test'       => \$ENV{HYPNOTOAD_TEST};
 
 my $app = shift || $ENV{HYPNOTOAD_APP};
 if ($help || !$app) {
@@ -33,9 +30,9 @@ hypnotoad - Hypnotoad HTTP and WebSocket server
 
   Usage: hypnotoad [OPTIONS] [APPLICATION]
 
-    hypnotoad script/myapp
-    hypnotoad myapp.pl
-    hypnotoad -f myapp.pl
+    hypnotoad ./script/myapp
+    hypnotoad ./myapp.pl
+    hypnotoad -f ./myapp.pl
 
   Options:
     -f, --foreground   Keep manager process in foreground.
@@ -3,9 +3,6 @@
 use strict;
 use warnings;
 
-use FindBin;
-BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
-
 require Mojolicious::Commands;
 Mojolicious::Commands->start_app('Mojo::HelloWorld');
 
@@ -3,16 +3,13 @@
 use strict;
 use warnings;
 
-use FindBin;
-BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
-
 use Getopt::Long qw(GetOptions :config no_auto_abbrev no_ignore_case);
 
 GetOptions
   'h|help'     => \my $help,
   'l|listen=s' => \my @listen,
-  'm|mode=s'   => sub { $ENV{MOJO_MODE} = $_[1] },
-  'v|verbose'  => sub { $ENV{MORBO_VERBOSE} = 1 },
+  'm|mode=s'   => \$ENV{MOJO_MODE},
+  'v|verbose'  => \$ENV{MORBO_VERBOSE},
   'w|watch=s'  => \my @watch;
 
 my $app = shift;
@@ -37,10 +34,11 @@ morbo - Morbo HTTP and WebSocket development server
 
   Usage: morbo [OPTIONS] [APPLICATION]
 
-    morbo script/myapp
-    morbo myapp.pl
-    morbo -m production -l https://*:443 -l http://[::]:3000 myapp.pl
-    morbo -w /usr/local/lib -w public myapp.pl
+    morbo ./script/myapp
+    morbo ./myapp.pl
+    morbo -m production -l https://*:443 -l http://[::]:3000 ./myapp.pl
+    morbo -l 'https://*:443?cert=./server.crt&key=./server.key' ./myapp.pl
+    morbo -w /usr/local/lib -w public ./myapp.pl
 
   Options:
     -h, --help                     Show this message.
@@ -9,10 +9,15 @@ use Mojo::Asset::Memory;
 
 # File asset
 my $file = Mojo::Asset::File->new;
+is $file->size, 0, 'file is empty';
+is $file->mtime, (stat $file->handle)[9], 'right mtime';
+is $file->slurp, '', 'file is empty';
 $file->add_chunk('abc');
 is $file->contains('abc'), 0,  '"abc" at position 0';
 is $file->contains('bc'),  1,  '"bc" at position 1';
 is $file->contains('db'),  -1, 'does not contain "db"';
+is $file->size, 3, 'right size';
+is $file->mtime, (stat $file->handle)[9], 'right mtime';
 
 # Cleanup
 my $path = $file->path;
@@ -26,6 +31,11 @@ $mem->add_chunk('abc');
 is $mem->contains('abc'), 0,  '"abc" at position 0';
 is $mem->contains('bc'),  1,  '"bc" at position 1';
 is $mem->contains('db'),  -1, 'does not contain "db"';
+is $mem->size, 3, 'right size';
+ok $mem->mtime > (time - 100), 'right mtime';
+is $mem->mtime, Mojo::Asset::Memory->new->mtime, 'same mtime';
+my $mtime = $mem->mtime;
+is $mem->mtime($mtime + 23)->mtime, $mtime + 23, 'right mtime';
 
 # Empty file asset
 $file = Mojo::Asset::File->new;
@@ -183,18 +193,6 @@ ok !$asset->is_file, 'stored in memory';
 $asset = $asset->add_chunk('lala');
 ok !$asset->is_file, 'stored in memory';
 
-# Append to file asset
-$file = Mojo::Asset::File->new(cleanup => 0);
-is $file->add_chunk('hello')->slurp, 'hello', 'right content';
-$path = $file->path;
-undef $file;
-ok -e $path, 'file still exists';
-$file = Mojo::Asset::File->new(path => $path, cleanup => 1);
-is $file->add_chunk(' world')->slurp, 'hello world',  'right content';
-is $file->add_chunk('!')->slurp,      'hello world!', 'right content';
-undef $file;
-ok !-e $path, 'file has been cleaned up';
-
 # Temporary directory
 {
   my $tmpdir = tempdir CLEANUP => 1;
@@ -226,6 +224,7 @@ $file = Mojo::Asset::File->new(cleanup => 0)->add_chunk('test');
 ok $file->is_file, 'stored in file';
 is $file->slurp,   'test', 'right content';
 is $file->size,    4, 'right size';
+is $file->mtime, (stat $file->handle)[9], 'right mtime';
 is $file->contains('es'), 1, '"es" at position 1';
 $path = $file->path;
 undef $file;
@@ -13,6 +13,8 @@ use base 'Mojo::BaseTest::Base2';
 __PACKAGE__->attr(heads => 1);
 __PACKAGE__->attr('name');
 
+sub more_heads { shift->{heads} += shift // 1 }
+
 package Mojo::BaseTestTest;
 use Mojo::Base 'Mojo::BaseTest';
 
@@ -66,6 +68,10 @@ $monkey = Mojo::BaseTest->new;
 is $monkey->tap(sub { $_->name('foo') })->name, 'foo', 'right attribute value';
 is $monkey->tap(sub { shift->name('bar')->name })->name, 'bar',
   'right attribute value';
+is $monkey->tap('heads')->heads, 1, 'right attribute value';
+is $monkey->more_heads, 2, 'right attribute value';
+is $monkey->tap('more_heads')->heads, 3, 'right attribute value';
+is $monkey->tap(more_heads => 3)->heads, 6, 'right attribute value';
 
 # Inherit -base flag
 $monkey = Mojo::BaseTest::Base3->new(evil => 1);
@@ -101,18 +101,17 @@ ok !ref $stream->to_string, 'nested bytestream stringified';
 
 # split
 $stream = b('1,2,3,4,5');
-is_deeply [$stream->split(',')->each],   [1, 2, 3, 4, 5], 'right elements';
-is_deeply [$stream->split(qr/,/)->each], [1, 2, 3, 4, 5], 'right elements';
-is_deeply [b('54321')->split('')->each], [5, 4, 3, 2, 1], 'right elements';
-is_deeply [b('')->split('')->each],    [], 'no elements';
-is_deeply [b('')->split(',')->each],   [], 'no elements';
-is_deeply [b('')->split(qr/,/)->each], [], 'no elements';
+is_deeply $stream->split(',')->to_array,   [1, 2, 3, 4, 5], 'right elements';
+is_deeply $stream->split(qr/,/)->to_array, [1, 2, 3, 4, 5], 'right elements';
+is_deeply b('54321')->split('')->to_array, [5, 4, 3, 2, 1], 'right elements';
+is_deeply b('')->split('')->to_array,    [], 'no elements';
+is_deeply b('')->split(',')->to_array,   [], 'no elements';
+is_deeply b('')->split(qr/,/)->to_array, [], 'no elements';
 $stream = b('1/2/3');
 is $stream->split('/')->map(sub { $_->quote })->join(', '), '"1", "2", "3"',
   'right result';
 is $stream->split('/')->map(sub { shift->quote })->join(', '),
   '"1", "2", "3"', 'right result';
-is $stream->split('/')->quote->join(', '), '"1", "2", "3"', 'right result';
 
 # length
 is b('foo bar baz')->size, 11, 'size is 11';
@@ -151,8 +150,4 @@ $file = catfile $dir, 'test.txt';
 is b("just\nworks!")->spurt($file)->quote, qq{"just\nworks!"}, 'right result';
 is b($file)->slurp, "just\nworks!", 'successful roundtrip';
 
-# Boolean context
-ok !Mojo::ByteStream->new(0),  '"0" is falsy';
-ok !!Mojo::ByteStream->new(1), '"1" is truthy';
-
 done_testing();
@@ -25,8 +25,7 @@ is $cache->get('two'), 2,     'right result';
 
 $cache = Mojo::Cache->new(max_keys => 3);
 is $cache->get('foo'), undef, 'no result';
-$cache->set(foo => 'bar');
-is $cache->get('foo'), 'bar', 'right result';
+is $cache->set(foo => 'bar')->get('foo'), 'bar', 'right result';
 $cache->set(bar => 'baz');
 is $cache->get('foo'), 'bar', 'right result';
 is $cache->get('bar'), 'baz', 'right result';
@@ -40,4 +39,12 @@ is $cache->get('bar'),  'baz',  'right result';
 is $cache->get('baz'),  'yada', 'right result';
 is $cache->get('yada'), 23,     'right result';
 
+$cache = Mojo::Cache->new(max_keys => 0);
+is $cache->get('foo'), undef, 'no result';
+is $cache->set(foo => 'bar')->get('foo'), undef, 'no result';
+$cache = Mojo::Cache->new(max_keys => -1);
+is $cache->get('foo'), undef, 'no result';
+$cache->set(foo => 'bar');
+is $cache->get('foo'), undef, 'no result';
+
 done_testing();
@@ -12,19 +12,19 @@ push @$collection, 3, 4, 5;
 is_deeply [@$collection], [1, 2, 3, 4, 5], 'right result';
 
 # Tap into method chain
-is_deeply [c(1, 2, 3)->tap(sub { $_->[1] += 2 })->each], [1, 4, 3],
+is_deeply c(1, 2, 3)->tap(sub { $_->[1] += 2 })->to_array, [1, 4, 3],
   'right result';
 
 # compact
-is_deeply [c(undef, 0, 1, '', 2, 3)->compact->each], [0, 1, 2, 3],
+is_deeply c(undef, 0, 1, '', 2, 3)->compact->to_array, [0, 1, 2, 3],
   'right result';
-is_deeply [c(3, 2, 1)->compact->each], [3, 2, 1], 'right result';
-is_deeply [c()->compact->each], [], 'right result';
+is_deeply c(3, 2, 1)->compact->to_array, [3, 2, 1], 'right result';
+is_deeply c()->compact->to_array, [], 'right result';
 
 # flatten
-is_deeply [c(1, 2, [3, 4], 5, c(6, 7))->flatten->each], [1, 2, 3, 4, 5, 6, 7],
-  'right result';
-is_deeply [c(undef, 1, [2, {}, [3, c(4, 5)]], undef, 6)->flatten->each],
+is_deeply c(1, 2, [3, 4], 5, c(6, 7))->flatten->to_array,
+  [1, 2, 3, 4, 5, 6, 7], 'right result';
+is_deeply c(undef, 1, [2, {}, [3, c(4, 5)]], undef, 6)->flatten->to_array,
   [undef, 1, 2, {}, 3, 4, 5, undef, 6], 'right result';
 
 # each
@@ -48,7 +48,7 @@ is $collection->first(qr/[1-4]/), 4, 'right result';
 is $collection->first(sub { ref $_ eq 'CODE' }), undef, 'no result';
 $collection = c();
 is $collection->first, undef, 'no result';
-is $collection->first(sub { defined $_ }), undef, 'no result';
+is $collection->first(sub {defined}), undef, 'no result';
 
 # last
 is c(5, 4, 3)->last, 3, 'right result';
@@ -57,16 +57,18 @@ is c()->last, undef, 'no result';
 
 # grep
 $collection = c(1, 2, 3, 4, 5, 6, 7, 8, 9);
-is_deeply [$collection->grep(qr/[6-9]/)->each], [6, 7, 8, 9], 'right elements';
-is_deeply [$collection->grep(sub {/[6-9]/})->each], [6, 7, 8, 9],
+is_deeply $collection->grep(qr/[6-9]/)->to_array, [6, 7, 8, 9],
+  'right elements';
+is_deeply $collection->grep(sub {/[6-9]/})->to_array, [6, 7, 8, 9],
+  'right elements';
+is_deeply $collection->grep(sub { $_ > 5 })->to_array, [6, 7, 8, 9],
   'right elements';
-is_deeply [$collection->grep(sub { $_ > 5 })->each], [6, 7, 8, 9],
+is_deeply $collection->grep(sub { $_ < 5 })->to_array, [1, 2, 3, 4],
   'right elements';
-is_deeply [$collection->grep(sub { $_ < 5 })->each], [1, 2, 3, 4],
+is_deeply $collection->grep(sub { shift == 5 })->to_array, [5],
   'right elements';
-is_deeply [$collection->grep(sub { shift == 5 })->each], [5], 'right elements';
-is_deeply [$collection->grep(sub { $_ < 1 })->each], [], 'no elements';
-is_deeply [$collection->grep(sub { $_ > 9 })->each], [], 'no elements';
+is_deeply $collection->grep(sub { $_ < 1 })->to_array, [], 'no elements';
+is_deeply $collection->grep(sub { $_ > 9 })->to_array, [], 'no elements';
 
 # join
 $collection = c(1, 2, 3);
@@ -75,8 +77,6 @@ is $collection->join(''),    '123',       'right result';
 is $collection->join('---'), '1---2---3', 'right result';
 is $collection->join("\n"),  "1\n2\n3",   'right result';
 is $collection->join('/')->url_escape, '1%2F2%2F3', 'right result';
-$collection = c(c(1, 2, 3), c(3, 2, 1));
-is $collection->join(''), "1\n2\n33\n2\n1", 'right result';
 
 # map
 $collection = c(1, 2, 3);
@@ -84,21 +84,26 @@ is $collection->map(sub { $_ + 1 })->join(''), '234', 'right result';
 is_deeply [@$collection], [1, 2, 3], 'right elements';
 is $collection->map(sub { shift() + 2 })->join(''), '345', 'right result';
 is_deeply [@$collection], [1, 2, 3], 'right elements';
+$collection = c(c(1, 2, 3), c(4, 5, 6), c(7, 8, 9));
+is $collection->map('reverse')->map(join => "\n")->join("\n"),
+  "3\n2\n1\n6\n5\n4\n9\n8\n7", 'right result';
+is $collection->map(join => '-')->join("\n"), "1-2-3\n4-5-6\n7-8-9",
+  'right result';
 
 # reverse
 $collection = c(3, 2, 1);
-is_deeply [$collection->reverse->each], [1, 2, 3], 'right order';
+is_deeply $collection->reverse->to_array, [1, 2, 3], 'right order';
 $collection = c(3);
-is_deeply [$collection->reverse->each], [3], 'right order';
+is_deeply $collection->reverse->to_array, [3], 'right order';
 $collection = c();
-is_deeply [$collection->reverse->each], [], 'no elements';
+is_deeply $collection->reverse->to_array, [], 'no elements';
 
 # shuffle
 $collection = c(0 .. 10000);
 my $random = $collection->shuffle;
 is $collection->size, $random->size, 'same number of elements';
 isnt "@$collection", "@$random", 'different order';
-is_deeply [c()->shuffle->each], [], 'no elements';
+is_deeply c()->shuffle->to_array, [], 'no elements';
 
 # size
 $collection = c();
@@ -112,60 +117,41 @@ is $collection->size, 1, 'right size';
 $collection = c(5, 4, 3, 2, 1);
 is $collection->size, 5, 'right size';
 
+# reduce
+$collection = c(2, 5, 4, 1);
+is $collection->reduce(sub { $a + $b }), 12, 'right result';
+is $collection->reduce(sub { $a + $b }, 5), 17, 'right result';
+is c()->reduce(sub { $a + $b }), undef, 'no result';
+
 # sort
 $collection = c(2, 5, 4, 1);
-is_deeply [$collection->sort->each], [1, 2, 4, 5], 'right order';
-is_deeply [$collection->sort(sub { $_[1] cmp $_[0] })->each], [5, 4, 2, 1],
+is_deeply $collection->sort->to_array, [1, 2, 4, 5], 'right order';
+is_deeply $collection->sort(sub { $b cmp $a })->to_array, [5, 4, 2, 1],
+  'right order';
+is_deeply $collection->sort(sub { $_[1] cmp $_[0] })->to_array, [5, 4, 2, 1],
   'right order';
 $collection = c(qw(Test perl Mojo));
-is_deeply [$collection->sort(sub { uc(shift) cmp uc(shift) })->each],
+is_deeply $collection->sort(sub { uc(shift) cmp uc(shift) })->to_array,
   [qw(Mojo perl Test)], 'right order';
 $collection = c();
-is_deeply [$collection->sort->each], [], 'no elements';
-is_deeply [$collection->sort(sub { $_[1] cmp $_[0] })->each], [],
-  'no elements';
+is_deeply $collection->sort->to_array, [], 'no elements';
+is_deeply $collection->sort(sub { $a cmp $b })->to_array, [], 'no elements';
 
 # slice
 $collection = c(1, 2, 3, 4, 5, 6, 7, 10, 9, 8);
-is_deeply [$collection->slice(0)->each],  [1], 'right result';
-is_deeply [$collection->slice(1)->each],  [2], 'right result';
-is_deeply [$collection->slice(2)->each],  [3], 'right result';
-is_deeply [$collection->slice(-1)->each], [8], 'right result';
-is_deeply [$collection->slice(-3, -5)->each], [10, 6], 'right result';
-is_deeply [$collection->slice(1, 2, 3)->each], [2, 3, 4], 'right result';
-is_deeply [$collection->slice(6, 1, 4)->each], [7, 2, 5], 'right result';
-is_deeply [$collection->slice(6 .. 9)->each], [7, 10, 9, 8], 'right result';
-
-# pluck
-$collection = c(c(1, 2, 3), c(4, 5, 6), c(7, 8, 9));
-is $collection->pluck('reverse'), "3\n2\n1\n6\n5\n4\n9\n8\n7", 'right result';
-is $collection->pluck(join => '-'), "1-2-3\n4-5-6\n7-8-9", 'right result';
-$collection = c(b('one'), b('two'), b('three'));
-is $collection->camelize, "One\nTwo\nThree", 'right result';
-is $collection->url_escape('^netwhr')->reverse, "%54hree\n%54w%6F\n%4Fne",
-  'right result';
+is_deeply $collection->slice(0)->to_array,  [1], 'right result';
+is_deeply $collection->slice(1)->to_array,  [2], 'right result';
+is_deeply $collection->slice(2)->to_array,  [3], 'right result';
+is_deeply $collection->slice(-1)->to_array, [8], 'right result';
+is_deeply $collection->slice(-3, -5)->to_array, [10, 6], 'right result';
+is_deeply $collection->slice(1, 2, 3)->to_array, [2, 3, 4], 'right result';
+is_deeply $collection->slice(6, 1, 4)->to_array, [7, 2, 5], 'right result';
+is_deeply $collection->slice(6 .. 9)->to_array, [7, 10, 9, 8], 'right result';
 
 # uniq
 $collection = c(1, 2, 3, 2, 3, 4, 5, 4);
-is_deeply [$collection->uniq->each], [1, 2, 3, 4, 5], 'right result';
-is_deeply [$collection->uniq->reverse->uniq->each], [5, 4, 3, 2, 1],
+is_deeply $collection->uniq->to_array, [1, 2, 3, 4, 5], 'right result';
+is_deeply $collection->uniq->reverse->uniq->to_array, [5, 4, 3, 2, 1],
   'right result';
 
-# Missing method and function (AUTOLOAD)
-eval { Mojo::Collection->new(b('whatever'))->missing };
-like $@,
-  qr/^Can't locate object method "missing" via package "Mojo::ByteStream"/,
-  'right error';
-eval { Mojo::Collection->new(undef)->missing };
-like $@, qr/^Can't call method "missing" on an undefined value/, 'right error';
-eval { Mojo::Collection::missing() };
-like $@, qr/^Undefined subroutine &Mojo::Collection::missing called/,
-  'right error';
-
-# Boolean context
-$collection = Mojo::Collection->new;
-ok !$collection, 'empty collection is falsy';
-$collection = Mojo::Collection->new('wat');
-ok $collection, ' non-empty collection is truthy ';
-
 done_testing();
@@ -82,7 +82,7 @@ is $content->charset, 'UTF-8', 'right charset';
 $content->headers->content_type('text/plain; charset  =  "UTF-8"');
 is $content->charset, 'UTF-8', 'right charset';
 
-# Partial content with 128bit content length
+# Partial content with 128-bit content length
 $content = Mojo::Content::Single->new;
 $content->parse(
   "Content-Length: 18446744073709551616\x0d\x0a\x0d\x0aHello World!");
@@ -344,7 +344,7 @@ $jar->extract($tx);
 $tx = Mojo::Transaction::HTTP->new;
 $tx->req->url->parse('http://example.com/test');
 $jar->inject($tx);
-@cookies = $tx->req->cookie('foo');
+@cookies = @{$tx->req->every_cookie('foo')};
 is $cookies[0]->name,  'foo',     'right name';
 is $cookies[0]->value, 'without', 'right value';
 is $cookies[1]->name,  'foo',     'right name';
@@ -353,7 +353,7 @@ is $cookies[2], undef, 'no third cookie';
 $tx = Mojo::Transaction::HTTP->new;
 $tx->req->url->parse('http://www.example.com/test');
 $jar->inject($tx);
-@cookies = $tx->req->cookie('foo');
+@cookies = @{$tx->req->every_cookie('foo')};
 is $cookies[0]->name,  'foo',  'right name';
 is $cookies[0]->value, 'with', 'right value';
 is $cookies[1], undef, 'no second cookie';
@@ -1,12 +1,11 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
+use Cwd 'abs_path';
 use File::Spec::Functions 'catdir';
+use FindBin;
 use Mojo;
 use Mojo::IOLoop;
 use Mojo::Log;
@@ -14,6 +13,25 @@ use Mojo::Server::Daemon;
 use Mojo::UserAgent;
 use Mojolicious;
 
+package TestApp;
+use Mojo::Base 'Mojo';
+
+sub handler {
+  my ($self, $tx) = @_;
+  $tx->res->code(200);
+  $tx->res->body('Hello TestApp!');
+  $tx->resume;
+}
+
+package main;
+
+# Minimal application
+my $ua = Mojo::UserAgent->new;
+$ua->server->app(TestApp->new);
+my $tx = $ua->get('/');
+is $tx->res->code, 200, 'right status';
+is $tx->res->body, 'Hello TestApp!', 'right content';
+
 # Timeout
 {
   is(Mojo::Server::Daemon->new->inactivity_timeout, 15, 'right value');
@@ -61,12 +79,29 @@ is $app->config({test => 23})->config->{test}, 23, 'right value';
 is_deeply $app->config, {foo => 'bar', baz => 'yada', test => 23},
   'right value';
 
+# Script name
+my $path = "$FindBin::Bin/lib/../lib/myapp.pl";
+is(Mojo::Server::Daemon->new->load_app($path)->config('script'),
+  abs_path($path), 'right script name');
+
+# Load broken app
+eval {
+  Mojo::Server::Daemon->new->load_app(
+    "$FindBin::Bin/lib/Mojo/LoaderException.pm");
+};
+like $@, qr/^Can't load application/, 'right error';
+
+# Load missing application class
+eval { Mojo::Server::Daemon->new->build_app('Mojo::DoesNotExist') };
+like $@, qr/^Can't find application class "Mojo::DoesNotExist" in \@INC/,
+  'right error';
+
 # Transaction
 isa_ok $app->build_tx, 'Mojo::Transaction::HTTP', 'right class';
 
 # Fresh application
 $app = Mojolicious->new;
-my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
+$ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
 is $ua->server->app($app)->app->moniker, 'mojolicious', 'right moniker';
 
 # Silence
@@ -105,7 +140,7 @@ $app->routes->post(
 $app->routes->any('/*whatever' => {text => 'Whatever!'});
 
 # Normal request
-my $tx = $ua->get('/normal/');
+$tx = $ua->get('/normal/');
 ok $tx->keep_alive, 'will be kept alive';
 is $tx->res->code, 200,         'right status';
 is $tx->res->body, 'Whatever!', 'right content';
@@ -172,32 +207,29 @@ my %params;
 for my $i (1 .. 10) { $params{"test$i"} = $i }
 my $result = '';
 for my $key (sort keys %params) { $result .= $params{$key} }
-my ($code, $body);
-my $port = $ua->server->url->port;
-$tx = $ua->post("http://127.0.0.1:$port/chunked" => form => \%params);
+$tx = $ua->post('/chunked' => form => \%params);
 is $tx->res->code, 200, 'right status';
 is $tx->res->body, $result, 'right content';
 
 # Upload
-($code, $body) = ();
-$tx = $ua->post(
-  "http://127.0.0.1:$port/upload" => form => {file => {content => $result}});
+$tx = $ua->post('/upload' => form => {file => {content => $result}});
 is $tx->res->code, 200, 'right status';
 is $tx->res->body, $result, 'right content';
 ok $tx->local_address, 'has local address';
 ok $tx->local_port > 0, 'has local port';
-ok $tx->remote_address, 'has local address';
-ok $tx->remote_port > 0, 'has local port';
+ok $tx->original_remote_address, 'has original remote address';
+ok $tx->remote_address,          'has remote address';
+ok $tx->remote_port > 0, 'has remote port';
 ok $local_address, 'has local address';
 ok $local_port > 0, 'has local port';
-ok $remote_address, 'has local address';
-ok $remote_port > 0, 'has local port';
+ok $remote_address, 'has remote address';
+ok $remote_port > 0, 'has remote port';
 
 # Pipelined
 my $daemon
   = Mojo::Server::Daemon->new(listen => ['http://127.0.0.1'], silent => 1);
 $daemon->start;
-$port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->handle->sockport;
+my $port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->handle->sockport;
 is $daemon->app->moniker, 'HelloWorld', 'right moniker';
 my $buffer = '';
 my $id;
@@ -9,6 +9,32 @@ is $date->epoch, 784111777, 'right epoch value';
 $date = Mojo::Date->new('Fri, 13 May 2011 10:00:24 GMT');
 is $date->epoch, 1305280824, 'right epoch value';
 
+# RFC 3339
+is(Mojo::Date->new('2014-08-20T20:45:00')->epoch,
+  1408567500, 'right epoch value');
+is(Mojo::Date->new(1408567500)->to_datetime,
+  '2014-08-20T20:45:00Z', 'right format');
+is(Mojo::Date->new('2014-08-20T20:45:00.01')->epoch,
+  1408567500.01, 'right epoch value');
+is(Mojo::Date->new('2014-08-20T20:45:00-00:46')->epoch,
+  1408570260, 'right epoch value');
+is(Mojo::Date->new(1408570260)->to_datetime,
+  '2014-08-20T21:31:00Z', 'right format');
+is(Mojo::Date->new('2014-08-20t20:45:00-01:46')->epoch,
+  1408573860, 'right epoch value');
+is(Mojo::Date->new('2014-08-20t20:45:00+01:46')->epoch,
+  1408561140, 'right epoch value');
+is(Mojo::Date->new(1408561140)->to_datetime,
+  '2014-08-20T18:59:00Z', 'right format');
+is(Mojo::Date->new('1994-11-06T08:49:37Z')->epoch,
+  784111777, 'right epoch value');
+is(Mojo::Date->new('1994-11-06t08:49:37.33z')->epoch,
+  784111777.33, 'right epoch value');
+is(Mojo::Date->new('2014-08-20  20:45:00')->epoch,
+  1408567500, 'right epoch value');
+is(Mojo::Date->new(784111777.33)->to_datetime,
+  '1994-11-06T08:49:37.33Z', 'right format');
+
 # RFC 850/1036
 is(Mojo::Date->new('Sunday, 06-Nov-94 08:49:37 GMT')->epoch,
   784111777, 'right epoch value');
@@ -35,6 +61,8 @@ is(Mojo::Date->new('Sun Nov  6 08:49:37 1994 GARBAGE')->epoch,
   undef, 'no epoch value');
 is(Mojo::Date->new('Fri, 75 May 2011 99:99:99 GMT')->epoch,
   undef, 'no epoch value');
+is(Mojo::Date->new('0000-00-00T00:00:00+01:00')->epoch,
+  undef, 'no epoch value');
 
 # to_string
 $date = Mojo::Date->new(784111777);
@@ -42,6 +70,13 @@ is "$date", 'Sun, 06 Nov 1994 08:49:37 GMT', 'right format';
 $date = Mojo::Date->new(1305280824);
 is $date->to_string, 'Fri, 13 May 2011 10:00:24 GMT', 'right format';
 
+# Current time roundtrips
+my $before = time;
+ok(Mojo::Date->new(Mojo::Date->new->to_string)->epoch >= $before,
+  'successful roundtrip');
+ok(Mojo::Date->new(Mojo::Date->new->to_datetime)->epoch >= $before,
+  'successful roundtrip');
+
 # Zero time checks
 $date = Mojo::Date->new(0);
 is $date->epoch, 0, 'right epoch value';
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop;
@@ -8,7 +8,7 @@ my $dom = Mojo::DOM->new->parse(
   '<div><div FOO="0" id="a">A</div><div id="b">B</div></div>');
 is $dom->at('#b')->text, 'B', 'right text';
 my @div;
-push @div, $dom->find('div[id]')->text->each;
+push @div, $dom->find('div[id]')->map('text')->each;
 is_deeply \@div, [qw(A B)], 'found all div elements with id';
 @div = ();
 $dom->find('div[id]')->each(sub { push @div, $_->text });
@@ -20,7 +20,8 @@ is "$dom", '<div><div foo="0" id="a">A</div><div id="b">B</div></div>',
 
 # Tap into method chain
 $dom = Mojo::DOM->new->parse('<div id="a">A</div><div id="b">B</div>');
-is_deeply [$dom->find('[id]')->attr('id')->each], [qw(a b)], 'right result';
+is_deeply [$dom->find('[id]')->map(attr => 'id')->each], [qw(a b)],
+  'right result';
 is $dom->tap(sub { $_->at('#b')->remove }), '<div id="a">A</div>',
   'right result';
 
@@ -142,7 +143,7 @@ is $dom->next,     undef, 'no siblings';
 is $dom->previous, undef, 'no siblings';
 is $dom->at('foo > a')->next,          undef, 'no next sibling';
 is $dom->at('foo > simple')->previous, undef, 'no previous sibling';
-is_deeply [$dom->at('simple')->ancestors->type->each], ['foo'],
+is_deeply [$dom->at('simple')->ancestors->map('type')->each], ['foo'],
   'right results';
 ok !$dom->at('simple')->ancestors->first->xml, 'XML mode not active';
 
@@ -185,7 +186,7 @@ is $dom->contents->[2]->node,    'pi',    'right node';
 is $dom->contents->[2]->content, 'after', 'right content';
 is $dom->contents->first->content(' again')->content, ' again',
   'right content';
-is $dom->contents->grep(sub { $_->node eq 'pi' })->remove->first->node,
+is $dom->contents->grep(sub { $_->node eq 'pi' })->map('remove')->first->node,
   'root', 'right node';
 is "$dom", '<!DOCTYPE again><p><![CDATA[123]]><!-- 456 --></p>',
   'right result';
@@ -213,8 +214,8 @@ is $dom->at('script')->contents->first->wrap('<i>:)</i>')->root,
   '<script><i>:)a</i><b>fce</b>1<b>d</b></script>', 'right result';
 is $dom->at('i')->contents->first->wrap_content('<b></b>')->root,
   '<script><i><b>:)</b>a</i><b>fce</b>1<b>d</b></script>', 'right result';
-is $dom->at('b')->contents->first->ancestors->type->join(','), 'b,i,script',
-  'right result';
+is $dom->at('b')->contents->first->ancestors->map('type')->join(','),
+  'b,i,script', 'right result';
 is $dom->at('b')->contents->first->append_content('g')->content, ':)g',
   'right content';
 is $dom->at('b')->contents->first->prepend_content('h')->content, 'h:)g',
@@ -283,8 +284,8 @@ $dom->find('p')->each(sub { push @p, $_->attr('id') });
 is_deeply \@p, [qw(foo bar)], 'found all p elements';
 my $ids = [qw(container header logo buttons buttons content)];
 is_deeply \@div, $ids, 'found all div elements';
-is_deeply [$dom->at('p')->ancestors->type->each], [qw(div div div body html)],
-  'right results';
+is_deeply [$dom->at('p')->ancestors->map('type')->each],
+  [qw(div div div body html)], 'right results';
 is_deeply [$dom->at('html')->ancestors->each], [], 'no results';
 is_deeply [$dom->ancestors->each],             [], 'no results';
 is_deeply [$dom->siblings->each],              [], 'no results';
@@ -293,7 +294,7 @@ ok $dom->at('form')->siblings->[1]->match('#content'), 'right sibling';
 is $dom->at('form')->siblings('#content')->first->text, 'More stuff',
   'right text';
 is_deeply [$dom->at('form')->siblings('#nothing')->each], [], 'no results';
-is_deeply [$dom->at('#header')->siblings->type->each], [qw(form div)],
+is_deeply [$dom->at('#header')->siblings->map('type')->each], [qw(form div)],
   'right results';
 
 # Script tag
@@ -436,7 +437,7 @@ is $dom->replace(''), '', 'no result';
 is "$dom", '', 'no result';
 $dom->replace('<div>foo<p>lalala</p>bar</div>');
 is "$dom", '<div>foo<p>lalala</p>bar</div>', 'right result';
-$dom->find('p')->replace('');
+$dom->find('p')->map(replace => '');
 is "$dom", '<div>foobar</div>', 'right result';
 $dom = Mojo::DOM->new->parse('<div>♥</div>');
 $dom->at('div')->content('☃');
@@ -450,14 +451,14 @@ is $dom->replace('<b>whatever</b>')->root, '<b>whatever</b>', 'right result';
 is $dom->to_string, '<b>whatever</b>', 'right result';
 $dom->at('b')->prepend('<p>foo</p>')->append('<p>bar</p>');
 is "$dom", '<p>foo</p><b>whatever</b><p>bar</p>', 'right result';
-is $dom->find('p')->remove->first->root->at('b')->text, 'whatever',
+is $dom->find('p')->map('remove')->first->root->at('b')->text, 'whatever',
   'right result';
 is "$dom", '<b>whatever</b>', 'right result';
 is $dom->at('b')->strip, 'whatever', 'right result';
 is $dom->strip,  'whatever', 'right result';
 is $dom->remove, '',         'right result';
 $dom->replace('A<div>B<p>C<b>D<i><u>E</u></i>F</b>G</p><div>H</div></div>I');
-is $dom->find(':not(div):not(i):not(u)')->strip->first->root,
+is $dom->find(':not(div):not(i):not(u)')->map('strip')->first->root,
   'A<div>BCD<i><u>E</u></i>FG<div>H</div></div>I', 'right result';
 is $dom->at('i')->to_string, '<i><u>E</u></i>', 'right result';
 
@@ -536,7 +537,7 @@ $dom = Mojo::DOM->new->parse(<<EOF);
 EOF
 ok $dom->xml, 'XML mode detected';
 is $dom->find('rss')->[0]->attr('version'), '2.0', 'right version';
-is_deeply [$dom->at('title')->ancestors->pluck('type')->each],
+is_deeply [$dom->at('title')->ancestors->map('type')->each],
   [qw(channel rss)], 'right results';
 is $dom->at('extension')->attr('foo:id'), 'works', 'right id';
 like $dom->at('#works')->text,       qr/\[awesome\]\]/, 'right text';
@@ -1425,8 +1426,9 @@ is $dom->find('table > colgroup > col')->[2]->attr->{id}, 'bar',
   'right attribute';
 is $dom->at('table > thead > tr > th')->text, 'A', 'right text';
 is $dom->find('table > thead > tr > th')->[1]->text, 'D', 'right text';
-is $dom->at('table > tbody > tr > td')->text,   'B',    'right text';
-is $dom->find('table > tbody > tr > td')->text, "B\nE", 'right text';
+is $dom->at('table > tbody > tr > td')->text, 'B', 'right text';
+is $dom->find('table > tbody > tr > td')->map('text')->join("\n"), "B\nE",
+  'right text';
 
 # Optional "colgroup", "tbody", "tr", "th" and "td" tags
 $dom = Mojo::DOM->new->parse(<<EOF);
@@ -1505,8 +1507,9 @@ is $dom->find('tbody > tr > .gamma')->[0]->text, '',            'no text';
 is $dom->find('tbody > tr > .gamma > a')->[0]->text, 'Gamma',     'right text';
 is $dom->find('tbody > tr > .alpha')->[1]->text,     'Alpha Two', 'right text';
 is $dom->find('tbody > tr > .gamma > a')->[1]->text, 'Gamma Two', 'right text';
-my @siblings = $dom->find('tr > td:nth-child(1)')->siblings(':nth-child(even)')
-  ->flatten->all_text->each;
+my @siblings
+  = $dom->find('tr > td:nth-child(1)')->map(siblings => ':nth-child(even)')
+  ->flatten->map('all_text')->each;
 is_deeply \@siblings, ['Beta', 'Delta', 'Beta Two', 'Delta Two'],
   'right results';
 
@@ -1964,47 +1967,6 @@ $dom->find('b')->each(
 );
 is_deeply \@results, [qw(baz yada)], 'right results';
 
-# AUTOLOAD children in XML mode
-$dom = Mojo::DOM->new->xml(1)->parse(<<EOF);
-<a id="one">
-  <B class="two" test>
-    foo
-    <c id="three">bar</c>
-    <c ID="four">baz</c>
-  </B>
-</a>
-EOF
-ok $dom->xml, 'XML mode active';
-is $dom->a->B->text, 'foo', 'right text';
-is $dom->a->B->c->[0]->text, 'bar', 'right text';
-is $dom->a->B->c->[1]->text, 'baz', 'right text';
-is $dom->a->B->c->[2], undef, 'no result';
-is $dom->a->B->c->size, 2, 'right number of elements';
-
-# AUTOLOAD children in HTML mode
-$dom = Mojo::DOM->new(<<EOF);
-<a id="one">
-  <B class="two" test>
-    foo
-    <c id="three">bar</c>
-    <c ID="four">baz</c>
-  </B>
-</a>
-EOF
-ok !$dom->xml, 'XML mode not active';
-is $dom->a->b->text, 'foo', 'right text';
-is $dom->a->b->c->[0]->text, 'bar', 'right text';
-is $dom->a->b->c->[1]->text, 'baz', 'right text';
-is $dom->a->b->c->[2], undef, 'no result';
-is $dom->a->b->c->size, 2, 'right number of elements';
-
-# Missing method and function (AUTOLOAD)
-eval { Mojo::DOM->new->missing };
-like $@, qr/^Can't locate object method "missing" via package "Mojo::DOM"/,
-  'right error';
-eval { Mojo::DOM::missing() };
-like $@, qr/^Undefined subroutine &Mojo::DOM::missing called/, 'right error';
-
 # Direct hash access to attributes in XML mode
 $dom = Mojo::DOM->new->xml(1)->parse(<<EOF);
 <a id="one">
@@ -2016,30 +1978,26 @@ $dom = Mojo::DOM->new->xml(1)->parse(<<EOF);
 </a>
 EOF
 ok $dom->xml, 'XML mode active';
-is $dom->a->{id}, 'one', 'right attribute';
-is_deeply [sort keys %{$dom->a}], ['id'], 'right attributes';
-is $dom->a->B->text, 'foo', 'right text';
-is $dom->at('a')->B->text, 'foo', 'right text';
-is $dom->find('a')->[0]->B->text, 'foo', 'right text';
-is $dom->a->B->{class}, 'two', 'right attribute';
-is $dom->at('a')->B->{class}, 'two', 'right attribute';
-is $dom->find('a')->[0]->B->{class}, 'two', 'right attribute';
-is_deeply [sort keys %{$dom->a->B}], [qw(class test)], 'right attributes';
-is $dom->a->B->c->[0]->text, 'bar', 'right text';
-is $dom->a->B->c->[0]{id}, 'three', 'right attribute';
-is_deeply [sort keys %{$dom->a->B->c->[0]}], ['id'], 'right attributes';
-is $dom->a->B->c->[1]->text, 'baz', 'right text';
-is $dom->a->B->c->[1]{ID}, 'four', 'right attribute';
-is_deeply [sort keys %{$dom->a->B->c->[1]}], ['ID'], 'right attributes';
-is $dom->a->B->c->[2], undef, 'no result';
-is $dom->a->B->c->size, 2, 'right number of elements';
+is $dom->at('a')->{id}, 'one', 'right attribute';
+is_deeply [sort keys %{$dom->at('a')}], ['id'], 'right attributes';
+is $dom->at('a')->at('B')->text, 'foo', 'right text';
+is $dom->at('B')->{class}, 'two', 'right attribute';
+is_deeply [sort keys %{$dom->at('a B')}], [qw(class test)], 'right attributes';
+is $dom->find('a B c')->[0]->text, 'bar', 'right text';
+is $dom->find('a B c')->[0]{id}, 'three', 'right attribute';
+is_deeply [sort keys %{$dom->find('a B c')->[0]}], ['id'], 'right attributes';
+is $dom->find('a B c')->[1]->text, 'baz', 'right text';
+is $dom->find('a B c')->[1]{ID}, 'four', 'right attribute';
+is_deeply [sort keys %{$dom->find('a B c')->[1]}], ['ID'], 'right attributes';
+is $dom->find('a B c')->[2], undef, 'no result';
+is $dom->find('a B c')->size, 2, 'right number of elements';
 @results = ();
-$dom->a->B->c->each(sub { push @results, $_->text });
+$dom->find('a B c')->each(sub { push @results, $_->text });
 is_deeply \@results, [qw(bar baz)], 'right results';
-is $dom->a->B->c, qq{<c id="three">bar</c>\n<c ID="four">baz</c>},
-  'right result';
+is $dom->find('a B c')->join("\n"),
+  qq{<c id="three">bar</c>\n<c ID="four">baz</c>}, 'right result';
 is_deeply [keys %$dom], [], 'root has no attributes';
-is $dom->find('#nothing'), '', 'no result';
+is $dom->find('#nothing')->join, '', 'no result';
 
 # Direct hash access to attributes in HTML mode
 $dom = Mojo::DOM->new(<<EOF);
@@ -2052,30 +2010,26 @@ $dom = Mojo::DOM->new(<<EOF);
 </a>
 EOF
 ok !$dom->xml, 'XML mode not active';
-is $dom->a->{id}, 'one', 'right attribute';
-is_deeply [sort keys %{$dom->a}], ['id'], 'right attributes';
-is $dom->a->b->text, 'foo', 'right text';
-is $dom->at('a')->b->text, 'foo', 'right text';
-is $dom->find('a')->[0]->b->text, 'foo', 'right text';
-is $dom->a->b->{class}, 'two', 'right attribute';
-is $dom->at('a')->b->{class}, 'two', 'right attribute';
-is $dom->find('a')->[0]->b->{class}, 'two', 'right attribute';
-is_deeply [sort keys %{$dom->a->b}], [qw(class test)], 'right attributes';
-is $dom->a->b->c->[0]->text, 'bar', 'right text';
-is $dom->a->b->c->[0]{id}, 'three', 'right attribute';
-is_deeply [sort keys %{$dom->a->b->c->[0]}], ['id'], 'right attributes';
-is $dom->a->b->c->[1]->text, 'baz', 'right text';
-is $dom->a->b->c->[1]{id}, 'four', 'right attribute';
-is_deeply [sort keys %{$dom->a->b->c->[1]}], ['id'], 'right attributes';
-is $dom->a->b->c->[2], undef, 'no result';
-is $dom->a->b->c->size, 2, 'right number of elements';
+is $dom->at('a')->{id}, 'one', 'right attribute';
+is_deeply [sort keys %{$dom->at('a')}], ['id'], 'right attributes';
+is $dom->at('a')->at('b')->text, 'foo', 'right text';
+is $dom->at('b')->{class}, 'two', 'right attribute';
+is_deeply [sort keys %{$dom->at('a b')}], [qw(class test)], 'right attributes';
+is $dom->find('a b c')->[0]->text, 'bar', 'right text';
+is $dom->find('a b c')->[0]{id}, 'three', 'right attribute';
+is_deeply [sort keys %{$dom->find('a b c')->[0]}], ['id'], 'right attributes';
+is $dom->find('a b c')->[1]->text, 'baz', 'right text';
+is $dom->find('a b c')->[1]{id}, 'four', 'right attribute';
+is_deeply [sort keys %{$dom->find('a b c')->[1]}], ['id'], 'right attributes';
+is $dom->find('a b c')->[2], undef, 'no result';
+is $dom->find('a b c')->size, 2, 'right number of elements';
 @results = ();
-$dom->a->b->c->each(sub { push @results, $_->text });
+$dom->find('a b c')->each(sub { push @results, $_->text });
 is_deeply \@results, [qw(bar baz)], 'right results';
-is $dom->a->b->c, qq{<c id="three">bar</c>\n<c id="four">baz</c>},
-  'right result';
+is $dom->find('a b c')->join("\n"),
+  qq{<c id="three">bar</c>\n<c id="four">baz</c>}, 'right result';
 is_deeply [keys %$dom], [], 'root has no attributes';
-is $dom->find('#nothing'), '', 'no result';
+is $dom->find('#nothing')->join, '', 'no result';
 
 # Append and prepend content
 $dom = Mojo::DOM->new('<a><b>Test<c /></b></a>');
@@ -2123,10 +2077,10 @@ $dom = Mojo::DOM->new(<<EOF);
   </tr>
 </table>
 EOF
-is $dom->table->tr->td->[0]->div->{id}, 'A', 'right attribute';
-is $dom->table->tr->td->[1]->div->{id}, 'B', 'right attribute';
-is $dom->table->tr->td->[2], undef, 'no result';
-is $dom->table->tr->td->size, 2, 'right number of elements';
+is $dom->find('table tr td')->[0]->at('div')->{id}, 'A', 'right attribute';
+is $dom->find('table tr td')->[1]->at('div')->{id}, 'B', 'right attribute';
+is $dom->find('table tr td')->[2], undef, 'no result';
+is $dom->find('table tr td')->size, 2, 'right number of elements';
 is "$dom", <<EOF, 'right result';
 <table>
   <tr>
@@ -2152,19 +2106,22 @@ is $dom->text(0), "\n", 'right text';
 is $dom->all_text, "looks like\n  it\n    really\n  works", 'right text';
 is $dom->all_text(0), "\n  looks\n  like\n  it\n    really\n  \n  works\n\n",
   'right text';
-is $dom->div->text, 'looks works', 'right text';
-is $dom->div->text(0), "\n  looks\n  \n  works\n", 'right text';
-is $dom->div->all_text, "looks like\n  it\n    really\n  works", 'right text';
-is $dom->div->all_text(0),
+is $dom->at('div')->text, 'looks works', 'right text';
+is $dom->at('div')->text(0), "\n  looks\n  \n  works\n", 'right text';
+is $dom->at('div')->all_text, "looks like\n  it\n    really\n  works",
+  'right text';
+is $dom->at('div')->all_text(0),
   "\n  looks\n  like\n  it\n    really\n  \n  works\n", 'right text';
-is $dom->div->pre->text, "\n  ", 'right text';
-is $dom->div->pre->text(0), "\n  ", 'right text';
-is $dom->div->pre->all_text, "like\n  it\n    really\n  ", 'right text';
-is $dom->div->pre->all_text(0), "like\n  it\n    really\n  ", 'right text';
-is $dom->div->pre->code->text, "like\n  it\n    really", 'right text';
-is $dom->div->pre->code->text(0), "like\n  it\n    really", 'right text';
-is $dom->div->pre->code->all_text, "like\n  it\n    really", 'right text';
-is $dom->div->pre->code->all_text(0), "like\n  it\n    really", 'right text';
+is $dom->at('div pre')->text, "\n  ", 'right text';
+is $dom->at('div pre')->text(0), "\n  ", 'right text';
+is $dom->at('div pre')->all_text, "like\n  it\n    really\n  ", 'right text';
+is $dom->at('div pre')->all_text(0), "like\n  it\n    really\n  ",
+  'right text';
+is $dom->at('div pre code')->text, "like\n  it\n    really", 'right text';
+is $dom->at('div pre code')->text(0), "like\n  it\n    really", 'right text';
+is $dom->at('div pre code')->all_text, "like\n  it\n    really", 'right text';
+is $dom->at('div pre code')->all_text(0), "like\n  it\n    really",
+  'right text';
 
 # PoCo example with whitespace sensitive text
 $dom = Mojo::DOM->new(<<EOF);
@@ -2190,21 +2147,23 @@ Springfield, VT 12345 USA</formatted>
   </entry>
 </response>
 EOF
-is $dom->find('entry')->[0]->displayName->text, 'Homer Simpson', 'right text';
-is $dom->find('entry')->[0]->id->text,          '1286823',       'right text';
-is $dom->find('entry')->[0]->addresses->children('type')->[0]->text, 'home',
+is $dom->find('entry')->[0]->at('displayName')->text, 'Homer Simpson',
   'right text';
-is $dom->find('entry')->[0]->addresses->formatted->text,
+is $dom->find('entry')->[0]->at('id')->text, '1286823', 'right text';
+is $dom->find('entry')->[0]->at('addresses')->children('type')->[0]->text,
+  'home', 'right text';
+is $dom->find('entry')->[0]->at('addresses formatted')->text,
   "742 Evergreen Terrace\nSpringfield, VT 12345 USA", 'right text';
-is $dom->find('entry')->[0]->addresses->formatted->text(0),
+is $dom->find('entry')->[0]->at('addresses formatted')->text(0),
   "742 Evergreen Terrace\nSpringfield, VT 12345 USA", 'right text';
-is $dom->find('entry')->[1]->displayName->text, 'Marge Simpson', 'right text';
-is $dom->find('entry')->[1]->id->text,          '1286822',       'right text';
-is $dom->find('entry')->[1]->addresses->children('type')->[0]->text, 'home',
+is $dom->find('entry')->[1]->at('displayName')->text, 'Marge Simpson',
   'right text';
-is $dom->find('entry')->[1]->addresses->formatted->text,
+is $dom->find('entry')->[1]->at('id')->text, '1286822', 'right text';
+is $dom->find('entry')->[1]->at('addresses')->children('type')->[0]->text,
+  'home', 'right text';
+is $dom->find('entry')->[1]->at('addresses formatted')->text,
   '742 Evergreen Terrace Springfield, VT 12345 USA', 'right text';
-is $dom->find('entry')->[1]->addresses->formatted->text(0),
+is $dom->find('entry')->[1]->at('addresses formatted')->text(0),
   "742 Evergreen Terrace\nSpringfield, VT 12345 USA", 'right text';
 is $dom->find('entry')->[2], undef, 'no result';
 is $dom->find('entry')->size, 2, 'right number of elements';
@@ -2281,6 +2240,22 @@ is "$dom", <<EOF, 'right result';
 <bar>after</bar>
 EOF
 
+# Nested description lists
+$dom = Mojo::DOM->new->parse(<<EOF);
+<dl>
+  <dt>A</dt>
+  <DD>
+    <dl>
+      <dt>B
+      <dd>C
+    </dl>
+  </dd>
+</dl>
+EOF
+is $dom->find('dl > dd > dl > dt')->[0]->text, 'B', 'right text';
+is $dom->find('dl > dd > dl > dd')->[0]->text, 'C', 'right text';
+is $dom->find('dl > dt')->[0]->text,           'A', 'right text';
+
 # Nested lists
 $dom = Mojo::DOM->new(<<EOF);
 <div>
@@ -2309,6 +2284,13 @@ is_deeply $dom->at('input')->attr,
   {type => 'checkbox', value => '/a/', checked => undef}, 'right attributes';
 is "$dom", '<input checked type="checkbox" value="/a/"><br>', 'right result';
 
+# Dot and hash in class and id attributes
+$dom = Mojo::DOM->new('<p class="a#b.c">A</p><p id="a#b.c">B</p>');
+is $dom->at('p.a\#b\.c')->text,       'A', 'right text';
+is $dom->at(':not(p.a\#b\.c)')->text, 'B', 'right text';
+is $dom->at('p#a\#b\.c')->text,       'B', 'right text';
+is $dom->at(':not(p#a\#b\.c)')->text, 'A', 'right text';
+
 # Extra whitespace
 $dom = Mojo::DOM->new('< span>a< /span><b >b</b><span >c</ span>');
 is $dom->at('span')->text,     'a', 'right text';
@@ -16,41 +16,21 @@ eval { $e->emit('die') };
 is $@, "works!\n", 'right error';
 
 # Unhandled error event
-eval { $e->emit(error => 'just') };
-like $@, qr/^Mojo::EventEmitter: just/, 'right error';
-eval { $e->emit_safe(error => 'works') };
+eval { $e->emit(error => 'works') };
 like $@, qr/^Mojo::EventEmitter: works/, 'right error';
 
+# Catch
+my $err;
+ok !$e->has_subscribers('foo'), 'no subscribers';
+$e->catch(sub { $err = pop });
+ok $e->has_subscribers('error'), 'has subscribers';
+$e->emit(error => 'just works!');
+is $err, 'just works!', 'right error';
+
 # Exception in error event
 $e->once(error => sub { die "$_[1]entional" });
 eval { $e->emit(error => 'int') };
 like $@, qr/^intentional/, 'right error';
-$e->once(error => sub { die "$_[1]entional" });
-eval { $e->emit_safe(error => 'int') };
-like $@, qr/^Mojo::EventEmitter: Event "error" failed: intentional/,
-  'right error';
-
-# Error fallback
-my ($echo, $err);
-$e->catch(sub { $err = pop })->on(test2 => sub { $echo .= 'echo: ' . pop });
-$e->on(
-  test2 => sub {
-    my ($e, $msg) = @_;
-    die "test2: $msg\n";
-  }
-);
-my $cb = sub { $echo .= 'echo2: ' . pop };
-$e->on(test2 => $cb);
-$e->emit_safe('test2', 'works!');
-is $echo, 'echo: works!echo2: works!', 'right echo';
-is $err, qq{Event "test2" failed: test2: works!\n}, 'right error';
-($echo, $err) = ();
-is scalar @{$e->subscribers('test2')}, 3, 'three subscribers';
-$e->unsubscribe(test2 => $cb);
-is scalar @{$e->subscribers('test2')}, 2, 'two subscribers';
-$e->emit_safe('test2', 'works!');
-is $echo, 'echo: works!', 'right echo';
-is $err, qq{Event "test2" failed: test2: works!\n}, 'right error';
 
 # Normal event again
 $e->emit('test1');
@@ -126,7 +106,7 @@ is $once, 1, 'event was not emitted again';
 # Unsubscribe
 $e = Mojo::EventEmitter->new;
 my $counter;
-$cb = $e->on(foo => sub { $counter++ });
+my $cb = $e->on(foo => sub { $counter++ });
 $e->on(foo => sub { $counter++ });
 $e->on(foo => sub { $counter++ });
 $e->unsubscribe(foo => $e->once(foo => sub { $counter++ }));
@@ -151,7 +131,7 @@ is $buffer, '', 'no result';
 $e->emit(one => $buffer => 'two');
 is $buffer, 'abctwo123two', 'right result';
 $e->once(one => sub { $_[1] .= 'def' });
-$e->emit_safe(one => $buffer => 'three');
+$e->emit(one => $buffer => 'three');
 is $buffer, 'abctwo123twoabcthree123threedef', 'right result';
 $e->emit(one => $buffer => 'x');
 is $buffer, 'abctwo123twoabcthree123threedefabcx123x', 'right result';
@@ -50,14 +50,18 @@ is $headers->accept_charset('foo')->accept_charset,   'foo', 'right value';
 is $headers->accept_encoding('foo')->accept_encoding, 'foo', 'right value';
 is $headers->accept_language('foo')->accept_language, 'foo', 'right value';
 is $headers->accept_ranges('foo')->accept_ranges,     'foo', 'right value';
-is $headers->allow('foo')->allow,                     'foo', 'right value';
-is $headers->authorization('foo')->authorization,     'foo', 'right value';
-is $headers->connection('foo')->connection,           'foo', 'right value';
-is $headers->cache_control('foo')->cache_control,     'foo', 'right value';
+is $headers->access_control_allow_origin('foo')->access_control_allow_origin,
+  'foo', 'right value';
+is $headers->allow('foo')->allow,                 'foo', 'right value';
+is $headers->authorization('foo')->authorization, 'foo', 'right value';
+is $headers->connection('foo')->connection,       'foo', 'right value';
+is $headers->cache_control('foo')->cache_control, 'foo', 'right value';
 is $headers->content_disposition('foo')->content_disposition, 'foo',
   'right value';
 is $headers->content_encoding('foo')->content_encoding,   'foo', 'right value';
+is $headers->content_language('foo')->content_language,   'foo', 'right value';
 is $headers->content_length('foo')->content_length,       'foo', 'right value';
+is $headers->content_location('foo')->content_location,   'foo', 'right value';
 is $headers->content_range('foo')->content_range,         'foo', 'right value';
 is $headers->content_type('foo')->content_type,           'foo', 'right value';
 is $headers->cookie('foo')->cookie,                       'foo', 'right value';
@@ -86,9 +90,11 @@ is $headers->sec_websocket_protocol('foo')->sec_websocket_protocol, 'foo',
   'right value';
 is $headers->sec_websocket_version('foo')->sec_websocket_version, 'foo',
   'right value';
-is $headers->server('foo')->server,                       'foo', 'right value';
-is $headers->set_cookie('foo')->set_cookie,               'foo', 'right value';
-is $headers->status('foo')->status,                       'foo', 'right value';
+is $headers->server('foo')->server,         'foo', 'right value';
+is $headers->set_cookie('foo')->set_cookie, 'foo', 'right value';
+is $headers->status('foo')->status,         'foo', 'right value';
+is $headers->strict_transport_security('foo')->strict_transport_security,
+  'foo', 'right value';
 is $headers->te('foo')->te,                               'foo', 'right value';
 is $headers->trailer('foo')->trailer,                     'foo', 'right value';
 is $headers->transfer_encoding('foo')->transfer_encoding, 'foo', 'right value';
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop;
@@ -7,8 +7,6 @@ use Mojo::IOLoop::Server;
 
 plan skip_all => 'set TEST_IPV6 to enable this test (developer only!)'
   unless $ENV{TEST_IPV6};
-plan skip_all => 'IO::Socket::IP 0.20 required for this test!'
-  unless Mojo::IOLoop::Server::IPV6;
 
 use Mojo::IOLoop;
 
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop::Server;
@@ -10,7 +10,7 @@ use Mojo::Base -strict;
 
 use Test::More;
 use Mojo::ByteStream 'b';
-use Mojo::JSON qw(decode_json encode_json j);
+use Mojo::JSON qw(decode_json encode_json false from_json j to_json true);
 use Mojo::Util 'encode';
 use Scalar::Util 'dualvar';
 
@@ -54,8 +54,7 @@ is_deeply $array, [Mojo::JSON->true], 'decode [true]';
 $array = decode_json '[null]';
 is_deeply $array, [undef], 'decode [null]';
 $array = decode_json '[true, false]';
-is_deeply $array, [Mojo::JSON->true, Mojo::JSON->false],
-  'decode [true, false]';
+is_deeply $array, [true, false], 'decode [true, false]';
 $value = decode_json 'true';
 is $value, Mojo::JSON->true, 'decode true';
 $value = decode_json 'false';
@@ -147,7 +146,7 @@ is b($bytes)->decode('UTF-8'), "[\"hello\\u0003\x{0152}world\x{0152}!\"]",
 $bytes = encode_json ["123abc"];
 is $bytes, '["123abc"]', 'encode ["123abc"]';
 $bytes = encode_json ["\x00\x1f \a\b/\f\r"];
-is $bytes, '["\\u0000\\u001F \\u0007\\b\/\f\r"]',
+is $bytes, '["\\u0000\\u001F \\u0007\\b/\f\r"]',
   'encode ["\x00\x1f \a\b/\f\r"]';
 $bytes = encode_json '';
 is $bytes, '""', 'encode ""';
@@ -250,6 +249,11 @@ is index($bytes, b("\x{2029}")->encode), -1, 'properly escaped';
 is_deeply decode_json($bytes), ["\x{2028}test\x{2029}123"],
   'successful roundtrip';
 
+# JSON without UTF-8 encoding
+is_deeply from_json('["♥"]'), ['♥'], 'characters decoded';
+is to_json(['♥']), '["♥"]', 'characters encoded';
+is_deeply from_json(to_json(["\xe9"])), ["\xe9"], 'successful roundtrip';
+
 # Blessed reference
 $bytes = encode_json [b('test')];
 is_deeply decode_json($bytes), ['test'], 'successful roundtrip';
@@ -317,67 +321,74 @@ like encode_json({test => -sin(9**9**9)}), qr/^{"test":".*"}$/,
   'encode "nan" as string';
 
 # "null"
-my $json = Mojo::JSON->new;
-is $json->decode('null'), undef, 'decode null';
-ok !$json->error, 'no error';
 is j('null'), undef, 'decode null';
 
 # Errors
-is $json->decode('test'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Expected string, array, object, number,'
-  . ' boolean or null at line 0, offset 0', 'right error';
-is $json->decode('["♥"]'), undef, 'wide character in input';
-is $json->error, 'Wide character in input', 'right error';
-is $json->decode(b('["\\ud800"]')->encode), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Missing low-surrogate at line 1, offset 8',
+eval { decode_json 'test' };
+like $@, qr/Malformed JSON: Expected string, array, object/, 'right error';
+like $@, qr/object, number, boolean or null at line 0, offset 0/,
+  'right error';
+eval { decode_json b('["\\ud800"]')->encode };
+like $@, qr/Malformed JSON: Missing low-surrogate at line 1, offset 8/,
+  'right error';
+eval { decode_json b('["\\udf46"]')->encode };
+like $@, qr/Malformed JSON: Missing high-surrogate at line 1, offset 8/,
+  'right error';
+eval { decode_json '[[]' };
+like $@, qr/Malformed JSON: Expected comma or right square bracket/,
+  'right error';
+like $@, qr/bracket while parsing array at line 1, offset 3/, 'right error';
+eval { decode_json '{{}' };
+like $@,
+  qr/Malformed JSON: Expected string while parsing object at line 1, offset 1/,
+  'right error';
+eval { decode_json "[\"foo\x00]" };
+like $@, qr/Malformed JSON: Unexpected character or invalid escape/,
   'right error';
-is $json->decode(b('["\\udf46"]')->encode), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Missing high-surrogate at line 1, offset 8',
+like $@, qr/escape while parsing string at line 1, offset 5/, 'right error';
+eval { decode_json '{"foo":"bar"{' };
+like $@, qr/Malformed JSON: Expected comma or right curly bracket/,
   'right error';
-is $json->decode('[[]'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Expected comma or right square bracket while'
-  . ' parsing array at line 1, offset 3', 'right error';
-is $json->decode('{{}'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Expected string while'
-  . ' parsing object at line 1, offset 1', 'right error';
-is $json->decode("[\"foo\x00]"), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Unexpected character or invalid escape while'
-  . ' parsing string at line 1, offset 5', 'right error';
-is $json->decode('{"foo":"bar"{'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Expected comma or right curly bracket while'
-  . ' parsing object at line 1, offset 12', 'right error';
-is $json->decode('{"foo""bar"}'), undef, 'syntax error';
-is $json->error,
-  'Malformed JSON: Expected colon while parsing object at line 1, offset 6',
+like $@, qr/bracket while parsing object at line 1, offset 12/, 'right error';
+eval { decode_json '{"foo""bar"}' };
+like $@,
+  qr/Malformed JSON: Expected colon while parsing object at line 1, offset 6/,
   'right error';
-is $json->decode('[[]...'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Expected comma or right square bracket while'
-  . ' parsing array at line 1, offset 3', 'right error';
-is $json->decode('{{}...'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Expected string while'
-  . ' parsing object at line 1, offset 1', 'right error';
-is $json->decode('[nan]'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Expected string, array, object, number,'
-  . ' boolean or null at line 1, offset 1', 'right error';
-is $json->decode('["foo]'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Unterminated string at line 1, offset 6',
+eval { decode_json '[[]...' };
+like $@, qr/Malformed JSON: Expected comma or right square bracket/,
   'right error';
-is $json->decode('{"foo":"bar"}lala'), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Unexpected data at line 1, offset 13',
+like $@, qr/bracket while parsing array at line 1, offset 3/, 'right error';
+eval { decode_json '{{}...' };
+like $@,
+  qr/Malformed JSON: Expected string while parsing object at line 1, offset 1/,
   'right error';
-is $json->decode(''), undef, 'missing input';
-is $json->error, 'Missing or empty input', 'right error';
-is $json->decode("[\"foo\",\n\"bar\"]lala"), undef, 'syntax error';
-is $json->error, 'Malformed JSON: Unexpected data at line 2, offset 6',
+eval { decode_json '[nan]' };
+like $@, qr/Malformed JSON: Expected string, array, object, number/,
   'right error';
-is $json->decode("[\"foo\",\n\"bar\",\n\"bazra\"]lalala"), undef,
-  'syntax error';
-is $json->error, 'Malformed JSON: Unexpected data at line 3, offset 8',
+like $@, qr/number, boolean or null at line 1, offset 1/, 'right error';
+eval { decode_json '["foo]' };
+like $@, qr/Malformed JSON: Unterminated string at line 1, offset 6/,
   'right error';
-is $json->decode(encode('Shift_JIS', 'やった')), undef, 'invalid encoding';
-is $json->error, 'Input is not UTF-8 encoded', 'right error';
+eval { decode_json '{"foo":"bar"}lala' };
+like $@, qr/Malformed JSON: Unexpected data at line 1, offset 13/,
+  'right error';
+eval { decode_json '' };
+like $@, qr/Missing or empty input/, 'right error';
+eval { decode_json "[\"foo\",\n\"bar\"]lala" };
+like $@, qr/Malformed JSON: Unexpected data at line 2, offset 6/,
+  'right error';
+eval { decode_json "[\"foo\",\n\"bar\",\n\"bazra\"]lalala" };
+like $@, qr/Malformed JSON: Unexpected data at line 3, offset 8/,
+  'right error';
+eval { decode_json '["♥"]' };
+like $@, qr/Input is not UTF-8 encoded/, 'right error';
+eval { decode_json encode('Shift_JIS', 'やった') };
+like $@, qr/Input is not UTF-8 encoded/, 'right error';
 is j('{'), undef, 'syntax error';
-eval { decode_json("[\"foo\",\n\"bar\",\n\"bazra\"]lalala") };
+eval { decode_json "[\"foo\",\n\"bar\",\n\"bazra\"]lalala" };
+like $@, qr/JSON: Unexpected data at line 3, offset 8 at.*json\.t/,
+  'right error';
+eval { from_json "[\"foo\",\n\"bar\",\n\"bazra\"]lalala" };
 like $@, qr/JSON: Unexpected data at line 3, offset 8 at.*json\.t/,
   'right error';
 
@@ -4,11 +4,11 @@ use Test::More;
 use Mojo::JSON::Pointer;
 
 # "contains" (hash)
-my $pointer = Mojo::JSON::Pointer->new;
-ok $pointer->contains({foo => 23}, ''),     'contains ""';
-ok $pointer->contains({foo => 23}, '/foo'), 'contains "/foo"';
-ok !$pointer->contains({foo => 23}, '/bar'), 'does not contains "/bar"';
-ok $pointer->contains({foo => {bar => undef}}, '/foo/bar'),
+my $pointer = Mojo::JSON::Pointer->new({foo => 23});
+ok $pointer->contains(''),     'contains ""';
+ok $pointer->contains('/foo'), 'contains "/foo"';
+ok !$pointer->contains('/bar'), 'does not contains "/bar"';
+ok $pointer->new({foo => {bar => undef}})->contains('/foo/bar'),
   'contains "/foo/bar"';
 
 # "contains" (mixed)
@@ -20,16 +20,17 @@ ok !$pointer->contains('/foo/bar'), 'does not contain "/foo/bar"';
 ok !$pointer->contains('/0'),       'does not contain "/0"';
 
 # "get" (hash)
-is_deeply $pointer->get({foo => 'bar'}, ''), {foo => 'bar'},
-  '"" is "{foo => "bar"}"';
-is $pointer->get({foo => 'bar'}, '/foo'), 'bar', '"/foo" is "bar"';
-is $pointer->get({foo => {bar => 42}}, '/foo/bar'), 42, '"/foo/bar" is "42"';
-is_deeply $pointer->get({foo => {23 => {baz => 0}}}, '/foo/23'), {baz => 0},
-  '"/foo/23" is "{baz => 0}"';
+$pointer = Mojo::JSON::Pointer->new({foo => 'bar'});
+is_deeply $pointer->get(''), {foo => 'bar'}, '"" is "{foo => "bar"}"';
+is $pointer->get('/foo'), 'bar', '"/foo" is "bar"';
+is $pointer->new({foo => {bar => 42}})->get('/foo/bar'), 42,
+  '"/foo/bar" is "42"';
+is_deeply $pointer->new({foo => {23 => {baz => 0}}})->get('/foo/23'),
+  {baz => 0}, '"/foo/23" is "{baz => 0}"';
 
 # "get" (mixed)
-is_deeply $pointer->get({foo => {bar => [1, 2, 3]}}, '/foo/bar'), [1, 2, 3],
-  '"/foo/bar" is "[1, 2, 3]"';
+is_deeply $pointer->new({foo => {bar => [1, 2, 3]}})->get('/foo/bar'),
+  [1, 2, 3], '"/foo/bar" is "[1, 2, 3]"';
 $pointer = Mojo::JSON::Pointer->new({foo => {bar => [0, undef, 3]}});
 is $pointer->get('/foo/bar/0'), 0,     '"/foo/bar/0" is "0"';
 is $pointer->get('/foo/bar/1'), undef, '"/foo/bar/1" is "undef"';
@@ -37,19 +38,20 @@ is $pointer->get('/foo/bar/2'), 3,     '"/foo/bar/2" is "3"';
 is $pointer->get('/foo/bar/6'), undef, '"/foo/bar/6" is "undef"';
 
 # "get" (encoded)
-is $pointer->get([{'foo/bar' => 'bar'}], '/0/foo~1bar'), 'bar',
+is $pointer->new([{'foo/bar' => 'bar'}])->get('/0/foo~1bar'), 'bar',
   '"/0/foo~1bar" is "bar"';
-is $pointer->get([{'foo/bar/baz' => 'yada'}], '/0/foo~1bar~1baz'), 'yada',
-  '"/0/foo~1bar~1baz" is "yada"';
-is $pointer->get([{'foo~/bar' => 'bar'}], '/0/foo~0~1bar'), 'bar',
+is $pointer->new([{'foo/bar/baz' => 'yada'}])->get('/0/foo~1bar~1baz'),
+  'yada', '"/0/foo~1bar~1baz" is "yada"';
+is $pointer->new([{'foo~/bar' => 'bar'}])->get('/0/foo~0~1bar'), 'bar',
   '"/0/foo~0~1bar" is "bar"';
-is $pointer->get(
-  [{'f~o~o~/b~' => {'a~' => {'r' => 'baz'}}}] => '/0/f~0o~0o~0~1b~0/a~0/r'),
-  'baz', '"/0/f~0o~0o~0~1b~0/a~0/r" is "baz"';
+is $pointer->new([{'f~o~o~/b~' => {'a~' => {'r' => 'baz'}}}])
+  ->get('/0/f~0o~0o~0~1b~0/a~0/r'), 'baz',
+  '"/0/f~0o~0o~0~1b~0/a~0/r" is "baz"';
 
 # Unicode
-is $pointer->get({'☃' => 'snowman'}, '/☃'), 'snowman', 'found the snowman';
-is $pointer->get({'☃' => ['snowman']}, '/☃/0'), 'snowman',
+is $pointer->new({'☃' => 'snowman'})->get('/☃'), 'snowman',
+  'found the snowman';
+is $pointer->new->data({'☃' => ['snowman']})->get('/☃/0'), 'snowman',
   'found the snowman';
 
 # RFC 6901
@@ -0,0 +1,5 @@
+use Mojolicious::Lite;
+
+app->config(script => $0);
+
+app->start;
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 
@@ -33,13 +33,14 @@ is $params2->to_string, 'x=1&y=2', 'right format';
 
 # Param
 is_deeply $params->param('foo'), 'b;ar', 'right structure';
-is_deeply [$params->param('a')], [4, 5], 'right structure';
-is_deeply [$params->param(['a'])], [4], 'right structure';
-is_deeply [$params->param([qw(a foo)])], [4, 'b;ar'], 'right structure';
+is_deeply $params->every_param('foo'), ['b;ar'], 'right structure';
+is_deeply $params->every_param('a'), [4, 5], 'right structure';
+is_deeply [$params->param(['a'])], [5], 'right structure';
+is_deeply [$params->param([qw(a foo)])], [5, 'b;ar'], 'right structure';
 $params->param(foo => 'bar');
 is_deeply [$params->param('foo')], ['bar'], 'right structure';
-is_deeply [$params->param(foo => qw(baz yada))->param('foo')], [qw(baz yada)],
-  'right structure';
+is_deeply $params->param(foo => qw(baz yada))->every_param('foo'),
+  [qw(baz yada)], 'right structure';
 
 # Remove
 $params->parse('q=1&w=2&e=3&e=4&r=6&t=7');
@@ -65,9 +66,12 @@ is $params->to_string, 'bar=bar&foo=', 'right format';
 # "0"
 $params = Mojo::Parameters->new(foo => 0);
 is $params->param('foo'), 0, 'right value';
+is_deeply $params->every_param('foo'), [0], 'right value';
+is_deeply $params->every_param('bar'), [], 'no values';
 is $params->to_string, 'foo=0', 'right format';
 $params = Mojo::Parameters->new($params->to_string);
 is $params->param('foo'), 0, 'right value';
+is_deeply $params->every_param('foo'), [0], 'right value';
 is $params->to_hash->{foo}, 0, 'right value';
 is_deeply $params->to_hash, {foo => 0}, 'right structure';
 is $params->to_string, 'foo=0', 'right format';
@@ -123,9 +127,9 @@ is "$params", 'a=works+too', 'right format';
 # Array values
 $params = Mojo::Parameters->new;
 $params->append(foo => [qw(bar baz)], bar => [qw(bas test)], a => 'b');
-is_deeply [$params->param('foo')], [qw(bar baz)], 'right values';
+is_deeply $params->every_param('foo'), [qw(bar baz)], 'right values';
 is $params->param('a'), 'b', 'right value';
-is_deeply [$params->param('bar')], [qw(bas test)], 'right values';
+is_deeply $params->every_param('bar'), [qw(bas test)], 'right values';
 is_deeply $params->to_hash,
   {foo => ['bar', 'baz'], a => 'b', bar => ['bas', 'test']}, 'right structure';
 $params = Mojo::Parameters->new(foo => ['ba;r', 'b;az']);
@@ -133,14 +137,14 @@ is_deeply $params->to_hash, {foo => ['ba;r', 'b;az']}, 'right structure';
 $params->append(foo => ['bar'], foo => ['baz', 'yada']);
 is_deeply $params->to_hash, {foo => ['ba;r', 'b;az', 'bar', 'baz', 'yada']},
   'right structure';
-is $params->param('foo'), 'ba;r', 'right value';
-is_deeply [$params->param('foo')], [qw(ba;r b;az bar baz yada)],
+is $params->param('foo'), 'yada', 'right value';
+is_deeply $params->every_param('foo'), [qw(ba;r b;az bar baz yada)],
   'right values';
 $params = Mojo::Parameters->new(foo => ['ba;r', 'b;az'], bar => 23);
 is_deeply $params->to_hash, {foo => ['ba;r', 'b;az'], bar => 23},
   'right structure';
-is $params->param('foo'), 'ba;r', 'right value';
-is_deeply [$params->param('foo')], [qw(ba;r b;az)], 'right values';
+is $params->param('foo'), 'b;az', 'right value';
+is_deeply $params->every_param('foo'), [qw(ba;r b;az)], 'right values';
 $params = Mojo::Parameters->new;
 is $params->param(foo => ['ba;r', 'baz'])->to_string, 'foo=ba%3Br&foo=baz',
   'right format';
@@ -19,6 +19,7 @@ ok $path->trailing_slash, 'has trailing slash';
 $path = Mojo::Path->new;
 is $path->to_string,     '',  'no path';
 is $path->to_abs_string, '/', 'right absolute path';
+is $path->to_route,      '/', 'right route';
 
 # Advanced
 $path = Mojo::Path->new('/AZaz09-._~!$&\'()*+,;=:@');
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 
@@ -1,7 +1,5 @@
 use Mojo::Base -strict;
 
-BEGIN { $ENV{MOJO_NO_IPV6} = 1 }
-
 use Test::More;
 
 plan skip_all => 'set TEST_EV to enable this test (developer only!)'
@@ -240,13 +238,11 @@ is(Mojo::Reactor->detect, 'Mojo::Reactor::Test', 'right class');
 $ENV{MOJO_REACTOR} = 'Mojo::Reactor::EV';
 is ref Mojo::IOLoop->singleton->reactor, 'Mojo::Reactor::EV', 'right object';
 ok !Mojo::IOLoop->is_running, 'loop is not running';
-my ($server_err, $server_running, $client_err, $client_running);
-$server = $client = '';
+my ($buffer, $server_err, $server_running, $client_err, $client_running);
 $id = Mojo::IOLoop->server(
   {address => '127.0.0.1'} => sub {
     my ($loop, $stream) = @_;
     $stream->write('test' => sub { shift->write('321') });
-    $stream->on(read => sub { $server .= pop });
     $server_running = Mojo::IOLoop->is_running;
     eval { Mojo::IOLoop->start };
     $server_err = $@;
@@ -256,21 +252,24 @@ $port = Mojo::IOLoop->acceptor($id)->handle->sockport;
 Mojo::IOLoop->client(
   {port => $port} => sub {
     my ($loop, $err, $stream) = @_;
-    $stream->write('tset' => sub { shift->write('123') });
-    $stream->on(read => sub { $client .= pop });
+    $stream->on(
+      read => sub {
+        my ($stream, $chunk) = @_;
+        $buffer .= $chunk;
+        return unless $buffer eq 'test321';
+        EV::break(EV::BREAK_ALL());
+      }
+    );
     $client_running = Mojo::IOLoop->is_running;
     eval { Mojo::IOLoop->start };
     $client_err = $@;
   }
 );
-Mojo::IOLoop->timer(1 => sub { EV::break(EV::BREAK_ALL()) });
 EV::run();
 ok !Mojo::IOLoop->is_running, 'loop is not running';
 like $server_err, qr/^Mojo::IOLoop already running/, 'right error';
 like $client_err, qr/^Mojo::IOLoop already running/, 'right error';
 ok $server_running, 'loop is running';
 ok $client_running, 'loop is running';
-is $server,         'tset123', 'right content';
-is $client,         'test321', 'right content';
 
 done_testing();
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use IO::Socket::INET;
@@ -238,13 +235,11 @@ is(Mojo::Reactor->detect, 'Mojo::Reactor::Test', 'right class');
 $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 is ref Mojo::IOLoop->singleton->reactor, 'Mojo::Reactor::Poll', 'right object';
 ok !Mojo::IOLoop->is_running, 'loop is not running';
-my ($server_err, $server_running, $client_err, $client_running);
-$server = $client = '';
+my ($buffer, $server_err, $server_running, $client_err, $client_running);
 $id = Mojo::IOLoop->server(
   {address => '127.0.0.1'} => sub {
     my ($loop, $stream) = @_;
     $stream->write('test' => sub { shift->write('321') });
-    $stream->on(read => sub { $server .= pop });
     $server_running = Mojo::IOLoop->is_running;
     eval { Mojo::IOLoop->start };
     $server_err = $@;
@@ -254,21 +249,24 @@ $port = Mojo::IOLoop->acceptor($id)->handle->sockport;
 Mojo::IOLoop->client(
   {port => $port} => sub {
     my ($loop, $err, $stream) = @_;
-    $stream->write('tset' => sub { shift->write('123') });
-    $stream->on(read => sub { $client .= pop });
+    $stream->on(
+      read => sub {
+        my ($stream, $chunk) = @_;
+        $buffer .= $chunk;
+        return unless $buffer eq 'test321';
+        Mojo::IOLoop->singleton->reactor->stop;
+      }
+    );
     $client_running = Mojo::IOLoop->is_running;
     eval { Mojo::IOLoop->start };
     $client_err = $@;
   }
 );
-Mojo::IOLoop->timer(1 => sub { Mojo::IOLoop->singleton->reactor->stop });
 Mojo::IOLoop->singleton->reactor->start;
 ok !Mojo::IOLoop->is_running, 'loop is not running';
 like $server_err, qr/^Mojo::IOLoop already running/, 'right error';
 like $client_err, qr/^Mojo::IOLoop already running/, 'right error';
 ok $server_running, 'loop is running';
 ok $client_running, 'loop is running';
-is $server,         'tset123', 'right content';
-is $client,         'test321', 'right content';
 
 done_testing();
@@ -587,12 +587,12 @@ is_deeply $req->body_params->to_hash->{foo}, [qw(bar bar)], 'right values';
 is $req->body_params->to_hash->{' tset'}, '23 ', 'right value';
 is $req->body_params, 'foo=bar&+tset=23+&foo=bar', 'right parameters';
 is_deeply $req->params->to_hash->{foo}, [qw(bar bar 13)], 'right values';
-is_deeply [$req->param('foo')], [qw(bar bar 13)], 'right values';
+is_deeply $req->every_param('foo'),     [qw(bar bar 13)], 'right values';
 is $req->param(' tset'), '23 ', 'right value';
 $req->param('set', 'single');
 is $req->param('set'), 'single', 'setting single param works';
 $req->param('multi', 1, 2, 3);
-is_deeply [$req->param('multi')], [qw(1 2 3)],
+is_deeply $req->every_param('multi'), [qw(1 2 3)],
   'setting multiple value param works';
 is $req->param('test23'), undef, 'no value';
 
@@ -1587,10 +1587,11 @@ is $req2->headers->cookie, 'foo=bar; foo=baz; foo=yada; bar=foo',
   'right "Cookie" value';
 is $req2->url, '/foo/bar', 'right URL';
 is $req2->url->to_abs, 'http://127.0.0.1/foo/bar', 'right absolute URL';
-is_deeply [map { $_->value } $req2->cookie('foo')], [qw(bar baz yada)],
+is_deeply [map { $_->value } @{$req2->every_cookie('foo')}],
+  [qw(bar baz yada)], 'right values';
+is_deeply [map { $_->value } @{$req2->every_cookie('bar')}], ['foo'],
   'right values';
-is_deeply [map { $_->value } $req2->cookie('bar')], ['foo'], 'right values';
-is_deeply [map { $_->value } $req2->cookie([qw(foo bar)])], [qw(bar foo)],
+is_deeply [map { $_->value } $req2->cookie([qw(foo bar)])], [qw(yada foo)],
   'right values';
 
 # Parse full HTTP 1.0 request with cookies and progress callback
@@ -64,8 +64,6 @@ is $res->code(428)->default_message, 'Precondition Required', 'right message';
 is $res->code(429)->default_message, 'Too Many Requests',     'right message';
 is $res->code(431)->default_message, 'Request Header Fields Too Large',
   'right message';
-is $res->code(451)->default_message, 'Unavailable For Legal Reasons',
-  'right message';
 is $res->code(500)->default_message, 'Internal Server Error', 'right message';
 is $res->code(501)->default_message, 'Not Implemented',       'right message';
 is $res->code(502)->default_message, 'Bad Gateway',           'right message';
@@ -82,6 +80,7 @@ is $res->code(509)->default_message, 'Bandwidth Limit Exceeded',
 is $res->code(510)->default_message, 'Not Extended', 'right message';
 is $res->code(511)->default_message, 'Network Authentication Required',
   'right message';
+is $res->default_message(100), 'Continue', 'right message';
 
 # Status code ranges
 ok $res->code(200)->is_status_class(200), 'is in range';
@@ -272,6 +271,7 @@ $res->parse("Hello World!\n1234\nlalalala\n");
 ok !$res->is_finished, 'response is not finished';
 ok !$res->is_empty,    'response is not empty';
 ok !$res->content->skip_body, 'body has not been skipped';
+ok $res->content->relaxed, 'relaxed response';
 is $res->code,    500,                     'right status';
 is $res->message, 'Internal Server Error', 'right message';
 is $res->version, '1.1',                   'right version';
@@ -279,6 +279,19 @@ is $res->headers->content_type,   'text/plain', 'right "Content-Type" value';
 is $res->headers->content_length, undef,        'no "Content-Length" value';
 is $res->body, "Hello World!\n1234\nlalalala\n", 'right content';
 
+# Parse full HTTP 1.1 response (broken Content-Length)
+$res = Mojo::Message::Response->new;
+$res->parse("HTTP/1.1 200 OK\x0d\x0a");
+$res->parse("Content-Length: 123test\x0d\x0a\x0d\x0a");
+$res->parse('Hello World!');
+ok $res->is_finished, 'response is finished';
+is $res->code,        200, 'right status';
+is $res->message,     'OK', 'right message';
+is $res->version,     '1.1', 'right version';
+is $res->headers->content_length, '123test', 'right "Content-Length" value';
+is $res->body, '', 'no content';
+is $res->content->leftovers, 'Hello World!', 'content in leftovers';
+
 # Parse full HTTP 1.1 response (100 Continue)
 $res = Mojo::Message::Response->new;
 $res->content->on(body => sub { shift->headers->header('X-Body' => 'one') });
@@ -464,7 +477,7 @@ isa_ok $res->content->parts->[2], 'Mojo::Content::Single', 'right part';
 is $res->content->parts->[0]->asset->slurp, "hallo welt test123\n",
   'right content';
 
-# Parse HTTP 1.1 chunked multipart response (at once)
+# Parse HTTP 1.1 chunked multipart response with leftovers (at once)
 $res = Mojo::Message::Response->new;
 my $multipart
   = "HTTP/1.1 200 OK\x0d\x0a"
@@ -486,7 +499,8 @@ my $multipart
   . "print \"Hello World :)\\n\"\n"
   . "\x0d\x0a------------0xKhTmLbOuNdA"
   . "r\x0d\x0a3\x0d\x0aY--\x0d\x0a"
-  . "0\x0d\x0a\x0d\x0a";
+  . "0\x0d\x0a\x0d\x0a"
+  . "HTTP/1.0 200 OK\x0d\x0a\x0d\x0a";
 $res->parse($multipart);
 ok $res->is_finished, 'response is finished';
 is $res->code,        200, 'right status';
@@ -508,6 +522,8 @@ isa_ok $res->upload('upload')->asset, 'Mojo::Asset::Memory', 'right file';
 is $res->upload('upload')->asset->size, 69, 'right size';
 is $res->content->parts->[2]->headers->content_type,
   'application/octet-stream', 'right "Content-Type" value';
+is $res->content->leftovers, "HTTP/1.0 200 OK\x0d\x0a\x0d\x0a",
+  'next response in leftovers';
 
 # Parse HTTP 1.1 chunked multipart response (in multiple small chunks)
 $res = Mojo::Message::Response->new;
@@ -1022,18 +1038,18 @@ is $res->version,     '1.1', 'right version';
 is $res->dom->at('p')->text,     'foo', 'right value';
 is $res->dom->at('p > a')->text, 'bar', 'right value';
 is $res->dom('p')->first->text, 'foo', 'right value';
-is_deeply [$res->dom('p > a')->pluck('text')->each], [qw(bar baz)],
+is_deeply $res->dom('p > a')->map('text')->to_array, [qw(bar baz)],
   'right values';
-my @text = $res->dom('a')->pluck(content => 'yada')->first->root->find('p > a')
-  ->pluck('text')->each;
+my @text = $res->dom('a')->map(content => 'yada')->first->root->find('p > a')
+  ->map('text')->each;
 is_deeply \@text, [qw(yada yada)], 'right values';
-is_deeply [$res->dom('p > a')->pluck('text')->each], [qw(yada yada)],
+is_deeply $res->dom('p > a')->map('text')->to_array, [qw(yada yada)],
   'right values';
 @text
-  = $res->dom->find('a')->pluck(content => 'test')->first->root->find('p > a')
-  ->pluck('text')->each;
+  = $res->dom->find('a')->map(content => 'test')->first->root->find('p > a')
+  ->map('text')->each;
 is_deeply \@text, [qw(test test)], 'right values';
-is_deeply [$res->dom->find('p > a')->pluck('text')->each], [qw(test test)],
+is_deeply $res->dom->find('p > a')->map('text')->to_array, [qw(test test)],
   'right values';
 
 # Build DOM from response with charset
@@ -79,6 +79,11 @@ $mt     = Mojo::Template->new;
 $output = $mt->render('    <%= "one" =%><%= "two" %>  three');
 is $output, "onetwo  three\n", 'expression tags trimmed';
 
+# Nothing to trim
+$mt     = Mojo::Template->new;
+$output = $mt->render('<% =%>');
+is $output, '', 'nothing trimmed';
+
 # Replace tag
 $mt     = Mojo::Template->new;
 $output = $mt->render('<%% 1 + 1 %>');
@@ -652,7 +657,8 @@ like "$output", qr/ohoh/, 'right result';
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
 test
-123
+123\
+456
  %# This dies
 % die 'oops!';
 %= 1 + 1
@@ -663,21 +669,23 @@ like $output->message, qr/oops!/, 'right message';
 is $output->lines_before->[0][0], 1,               'right number';
 is $output->lines_before->[0][1], 'test',          'right line';
 is $output->lines_before->[1][0], 2,               'right number';
-is $output->lines_before->[1][1], '123',           'right line';
+is $output->lines_before->[1][1], '123\\',         'right line';
 is $output->lines_before->[2][0], 3,               'right number';
-is $output->lines_before->[2][1], ' %# This dies', 'right line';
-is $output->line->[0], 4, 'right number';
+is $output->lines_before->[2][1], '456',           'right line';
+is $output->lines_before->[3][0], 4,               'right number';
+is $output->lines_before->[3][1], ' %# This dies', 'right line';
+is $output->line->[0], 5, 'right number';
 is $output->line->[1], "% die 'oops!';", 'right line';
-is $output->lines_after->[0][0], 5,          'right number';
+is $output->lines_after->[0][0], 6,          'right number';
 is $output->lines_after->[0][1], '%= 1 + 1', 'right line';
-is $output->lines_after->[1][0], 6,          'right number';
+is $output->lines_after->[1][0], 7,          'right number';
 is $output->lines_after->[1][1], 'test',     'right line';
-like "$output", qr/oops! at template line 4/, 'right result';
+like "$output", qr/oops! at template line 5/, 'right result';
 
 # Exception in template (empty perl lines)
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
-test
+test\\
 123
 %
 % die 'oops!';
@@ -689,15 +697,15 @@ test
 EOF
 isa_ok $output, 'Mojo::Exception', 'right exception';
 like $output->message, qr/oops!/, 'right message';
-is $output->lines_before->[0][0], 1,      'right number';
-is $output->lines_before->[0][1], 'test', 'right line';
+is $output->lines_before->[0][0], 1,          'right number';
+is $output->lines_before->[0][1], 'test\\\\', 'right line';
 ok $output->lines_before->[0][2], 'contains code';
-is $output->lines_before->[1][0], 2,      'right number';
-is $output->lines_before->[1][1], '123',  'right line';
+is $output->lines_before->[1][0], 2,          'right number';
+is $output->lines_before->[1][1], '123',      'right line';
 ok $output->lines_before->[1][2], 'contains code';
-is $output->lines_before->[2][0], 3,      'right number';
-is $output->lines_before->[2][1], '%',    'right line';
-is $output->lines_before->[2][2], ' ',    'right code';
+is $output->lines_before->[2][0], 3,          'right number';
+is $output->lines_before->[2][1], '%',        'right line';
+is $output->lines_before->[2][2], ' ',        'right code';
 is $output->line->[0], 4, 'right number';
 is $output->line->[1], "% die 'oops!';", 'right line';
 is $output->lines_after->[0][0], 5,     'right number';
@@ -999,6 +1007,26 @@ $output = $mt->render(<<'EOF');
 EOF
 is $output, "hello world\n", 'escaped multiline expression';
 
+# Empty statement
+$mt     = Mojo::Template->new;
+$output = $mt->render("test\n\n123\n\n<% %>456\n789");
+is $output, "test\n\n123\n\n456\n789\n", 'empty statement';
+
+# Optimize successive text lines ending with newlines
+$mt = Mojo::Template->new;
+$mt->parse(<<'EOF');
+test
+123
+456\
+789\\
+987
+654
+321
+EOF
+is $mt->tree->[0][1], "test\n123\n456", 'optimized text lines';
+$output = $mt->build->compile || $mt->interpret;
+is $output, "test\n123\n456789\\\n987\n654\n321\n", 'just text';
+
 # Scoped scalar
 $mt     = Mojo::Template->new;
 $output = $mt->render(<<'EOF');
@@ -151,12 +151,14 @@ is $tx->req->headers->content_type, 'application/x-www-form-urlencoded',
   'right "Content-Type" value';
 is $tx->req->body, 'test=12345678912', 'right content';
 
-# UTF-8 form with header
-$tx = $t->tx(POST => 'http://example.com/foo' => {Accept => '*/*'} => form =>
+# UTF-8 form with header and custom content type
+$tx
+  = $t->tx(POST => 'http://example.com/foo' =>
+    {Accept => '*/*', 'Content-Type' => 'application/mojo-form'} => form =>
     {test => 123} => charset => 'UTF-8');
 is $tx->req->url->to_abs, 'http://example.com/foo', 'right URL';
 is $tx->req->method, 'POST', 'right method';
-is $tx->req->headers->content_type, 'application/x-www-form-urlencoded',
+is $tx->req->headers->content_type, 'application/mojo-form',
   'right "Content-Type" value';
 is $tx->req->headers->accept, '*/*', 'right "Accept" value';
 is $tx->req->body, 'test=123', 'right content';
@@ -197,7 +199,7 @@ like $tx->req->content->parts->[3]->headers->content_disposition, qr/"b"/,
   'right "Content-Disposition" value';
 is $tx->req->content->parts->[3]->asset->slurp, 4, 'right part';
 is $tx->req->content->parts->[4], undef, 'no more parts';
-is_deeply [$tx->req->param('a')], [1, 2, 3], 'right values';
+is_deeply $tx->req->every_param('a'), [1, 2, 3], 'right values';
 is_deeply [$tx->req->param('b')], [4], 'right values';
 
 # Multipart form with real file and custom header
@@ -218,12 +220,14 @@ ok !$tx->req->content->parts->[0]->headers->header('file'), 'no "file" header';
 is $tx->req->content->parts->[0]->headers->dnt, 1, 'right "DNT" header';
 is $tx->req->content->parts->[1], undef, 'no more parts';
 
-# Multipart form with asset
-$tx = $t->tx(POST => 'http://example.com/foo' => form =>
+# Multipart form with asset and custom content type
+$tx
+  = $t->tx(POST => 'http://example.com/foo' =>
+    {'Content-Type' => 'multipart/mojo-form'} => form =>
     {mytext => {file => Mojo::Asset::File->new(path => $path)}});
 is $tx->req->url->to_abs, 'http://example.com/foo', 'right URL';
 is $tx->req->method, 'POST', 'right method';
-is $tx->req->headers->content_type, 'multipart/form-data',
+is $tx->req->headers->content_type, 'multipart/mojo-form',
   'right "Content-Type" value';
 like $tx->req->content->parts->[0]->headers->content_disposition,
   qr/"mytext"/, 'right "Content-Disposition" value';
@@ -326,6 +330,13 @@ is(($t->endpoint($tx))[0], 'http',      'right scheme');
 is(($t->endpoint($tx))[1], '127.0.0.1', 'right host');
 is(($t->endpoint($tx))[2], 3000,        'right port');
 
+# Simple endpoint with SOCKS proxy
+$tx = $t->tx(GET => 'http://mojolicio.us');
+$tx->req->proxy('socks://127.0.0.1:3000');
+is(($t->endpoint($tx))[0], 'http',         'right scheme');
+is(($t->endpoint($tx))[1], 'mojolicio.us', 'right host');
+is(($t->endpoint($tx))[2], 80,             'right port');
+
 # Simple WebSocket endpoint with proxy
 $tx = $t->websocket('ws://mojolicio.us');
 $tx->req->proxy('http://127.0.0.1:3000');
@@ -346,6 +357,13 @@ is(($t->endpoint($tx))[0], 'https',        'right scheme');
 is(($t->endpoint($tx))[1], 'mojolicio.us', 'right host');
 is(($t->endpoint($tx))[2], 443,            'right port');
 
+# HTTPS endpoint with SOCKS proxy
+$tx = $t->tx(GET => 'https://mojolicio.us');
+$tx->req->proxy('socks://127.0.0.1:3000');
+is(($t->endpoint($tx))[0], 'https',        'right scheme');
+is(($t->endpoint($tx))[1], 'mojolicio.us', 'right host');
+is(($t->endpoint($tx))[2], 443,            'right port');
+
 # TLS WebSocket endpoint with proxy
 $tx = $t->websocket('WSS://mojolicio.us');
 $tx->req->proxy('http://127.0.0.1:3000');
@@ -366,6 +384,13 @@ is(($t->peer($tx))[0], 'http',      'right scheme');
 is(($t->peer($tx))[1], '127.0.0.1', 'right host');
 is(($t->peer($tx))[2], 3000,        'right port');
 
+# Simple peer with SOCKS proxy
+$tx = $t->tx(GET => 'http://mojolicio.us');
+$tx->req->proxy('socks://127.0.0.1:3000');
+is(($t->peer($tx))[0], 'socks',     'right scheme');
+is(($t->peer($tx))[1], '127.0.0.1', 'right host');
+is(($t->peer($tx))[2], 3000,        'right port');
+
 # Simple peer with proxy (no port)
 $tx = $t->tx(GET => 'http://mojolicio.us');
 $tx->req->proxy('http://127.0.0.1');
@@ -400,6 +425,13 @@ is(($t->peer($tx))[0], 'http',      'right scheme');
 is(($t->peer($tx))[1], '127.0.0.1', 'right host');
 is(($t->peer($tx))[2], 3000,        'right port');
 
+# HTTPS peer with SOCKS proxy
+$tx = $t->tx(GET => 'https://mojolicio.us');
+$tx->req->proxy('socks://127.0.0.1:3000');
+is(($t->peer($tx))[0], 'socks',     'right scheme');
+is(($t->peer($tx))[1], '127.0.0.1', 'right host');
+is(($t->peer($tx))[2], 3000,        'right port');
+
 # TLS WebSocket peer with proxy
 $tx = $t->websocket('wss://mojolicio.us');
 $tx->req->proxy('http://127.0.0.1:3000');
@@ -494,6 +526,9 @@ is $tx->req->headers->host, 'mojolicio.us', 'right "Host" header';
 is $t->proxy_connect($tx), undef, 'already a CONNECT request';
 $tx->req->method('Connect');
 is $t->proxy_connect($tx), undef, 'already a CONNECT request';
+$tx = $t->tx(GET => 'https://mojolicio.us');
+$tx->req->proxy('socks://127.0.0.1:3000');
+is $t->proxy_connect($tx), undef, 'using a SOCKS proxy';
 
 # Simple 301 redirect
 $tx = $t->tx(
@@ -758,13 +793,13 @@ is $tx->res->headers->location, undef, 'no "Location" value';
 $tx = $t->tx(
   POST => 'http://mojolicio.us/foo' => {Accept => '*/*'} => 'whatever');
 $tx->res->code(308);
-$tx->res->headers->location('http://example.com/bar');
+$tx->res->headers->location('https://example.com/bar');
 is $tx->req->headers->accept, '*/*', 'right "Accept" value';
 is $tx->req->body, 'whatever', 'right content';
 $tx = $t->redirect($tx);
 is $tx->req->method, 'POST', 'right method';
-is $tx->req->url->to_abs, 'http://example.com/bar', 'right URL';
-is $tx->req->headers->accept, '*/*', 'right "Accept" value';
+is $tx->req->url->to_abs, 'https://example.com/bar', 'right URL';
+is $tx->req->headers->accept,   '*/*', 'right "Accept" value';
 is $tx->req->headers->location, undef, 'no "Location" value';
 is $tx->req->body, 'whatever', 'right content';
 is $tx->res->code, undef,      'no status';
@@ -786,6 +821,12 @@ is $tx->req->headers->accept, 'application/json', 'right "Accept" value';
 is $tx->req->body, '', 'no content';
 is $t->redirect($tx), undef, 'unsupported redirect';
 
+# 302 redirect with bad location
+$tx = $t->tx(GET => 'http://mojolicio.us/foo');
+$tx->res->code(302);
+$tx->res->headers->location('data:image/png;base64,helloworld123');
+is $t->redirect($tx), undef, 'unsupported redirect';
+
 # 302 redirect (relative path and query)
 $tx = $t->tx(
   POST => 'http://mojolicio.us/foo/bar?a=b' => {Accept => 'application/json'});
@@ -1,7 +1,7 @@
 use Mojo::Base -strict;
 
 BEGIN {
-  $ENV{MOJO_NO_IPV6} = $ENV{MOJO_NO_TLS} = 1;
+  $ENV{MOJO_NO_SOCKS} = $ENV{MOJO_NO_TLS} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -125,10 +125,17 @@ ok $success, 'successful';
 is $code,    200, 'right status';
 is $body,    'works!', 'right content';
 
+# SOCKS proxy request without SOCKS support
+$ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
+my $tx = $ua->build_tx(GET => '/');
+$tx->req->proxy($ua->server->url->scheme('socks'));
+$tx = $ua->start($tx);
+like $tx->error->{message}, qr/IO::Socket::Socks/, 'right error';
+
 # HTTPS request without TLS support
 $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
-my $tx = $ua->get($ua->server->url->scheme('https'));
-ok $tx->error, 'has error';
+$tx = $ua->get($ua->server->url->scheme('https'));
+like $tx->error->{message}, qr/IO::Socket::SSL/, 'right error';
 
 # Blocking
 $tx = $ua->get('/');
@@ -172,14 +179,13 @@ is $tx->res->code, 200, 'right status';
 is $tx->res->headers->connection, 'test', 'right "Connection" value';
 is $tx->res->body, 'One!', 'right content';
 
-# Error in callback is logged
-app->ua->once(error => sub { Mojo::IOLoop->stop });
-ok app->ua->has_subscribers('error'), 'has subscribers';
+# Error in callback
+Mojo::IOLoop->singleton->reactor->unsubscribe('error');
 my $err;
-my $msg = app->log->on(message => sub { $err .= pop });
+Mojo::IOLoop->singleton->reactor->once(
+  error => sub { $err .= pop; Mojo::IOLoop->stop });
 app->ua->get('/' => sub { die 'error event works' });
 Mojo::IOLoop->start;
-app->log->unsubscribe(message => $msg);
 like $err, qr/error event works/, 'right error';
 
 # Events
@@ -282,7 +288,7 @@ is $body,    '{"hello":"world"}', 'right content';
 
 # Built-in web server times out
 my $log = '';
-$msg = app->log->on(message => sub { $log .= pop });
+my $msg = app->log->on(message => sub { $log .= pop });
 $tx = $ua->get('/timeout?timeout=0.25');
 app->log->unsubscribe(message => $msg);
 ok !$tx->success, 'not successful';
@@ -307,9 +313,7 @@ ok !$tx->success, 'not successful';
 is $tx->error->{message}, 'Inactivity timeout', 'right error';
 
 # Keep alive connection times out
-my ($fail, $id);
-my $error = $ua->on(error => sub { $fail++ });
-ok $ua->has_subscribers('error'), 'has subscribers';
+my $id;
 $ua->get(
   '/' => sub {
     my ($ua, $tx) = @_;
@@ -319,10 +323,7 @@ $ua->get(
   }
 );
 Mojo::IOLoop->start;
-ok !$fail, 'error event has not been emitted';
 ok !Mojo::IOLoop->stream($id), 'connection timed out';
-$ua->unsubscribe(error => $error);
-ok !$ua->has_subscribers('error'), 'unsubscribed successfully';
 
 # Response exceeding message size limit
 $ua->once(
@@ -344,6 +345,22 @@ ok !$tx->success, 'not successful';
 is $tx->error->{message}, 'Not Found', 'right error';
 is $tx->error->{code},    404,         'right status';
 
+# Compressed response
+$tx = $ua->build_tx(GET => '/echo' => 'Hello GZip!');
+$tx = $ua->start($ua->build_tx(GET => '/echo' => 'Hello GZip!'));
+ok $tx->success, 'successful';
+is $tx->res->code, 200, 'right status';
+is $tx->res->headers->content_encoding, undef, 'no "Content-Encoding" value';
+is $tx->res->body, 'Hello GZip!', 'right content';
+$tx = $ua->build_tx(GET => '/echo' => 'Hello GZip!');
+$tx->res->content->auto_decompress(0);
+$tx = $ua->start($tx);
+ok $tx->success, 'successful';
+is $tx->res->code, 200, 'right status';
+is $tx->res->headers->content_encoding, 'gzip',
+  'right "Content-Encoding" value';
+isnt $tx->res->body, 'Hello GZip!', 'different content';
+
 # Fork safety
 $tx = $ua->get('/');
 is $tx->res->body, 'works!', 'right content';
@@ -10,8 +10,6 @@ use Mojo::IOLoop::Server;
 
 plan skip_all => 'set TEST_ONLINE to enable this test (developer only!)'
   unless $ENV{TEST_ONLINE};
-plan skip_all => 'IO::Socket::IP 0.20 required for this test!'
-  unless Mojo::IOLoop::Server::IPV6;
 plan skip_all => 'IO::Socket::SSL 1.84 required for this test!'
   unless Mojo::IOLoop::Server::TLS;
 
@@ -87,7 +85,7 @@ ok $tx->error,       'has error';
 $tx = $ua->build_tx(GET => 'http://cdeabcdeffoobarnonexisting.com');
 $ua->start($tx);
 ok $tx->is_finished, 'transaction is finished';
-like $tx->error->{message}, qr/^Couldn't connect/, 'right error';
+ok $tx->error,       'has error';
 
 # Fresh user agent again
 $ua = Mojo::UserAgent->new;
@@ -234,8 +232,9 @@ is $tx->res->code,   200,             'right status';
 ok $tx->kept_alive,    'connection was kept alive';
 ok $tx->local_address, 'has local address';
 ok $tx->local_port > 0, 'has local port';
-ok $tx->remote_address, 'has local address';
-ok $tx->remote_port > 0, 'has local port';
+ok $tx->original_remote_address, 'has original remote address';
+ok $tx->remote_address,          'has remote address';
+ok $tx->remote_port > 0, 'has remote port';
 
 # Simple request with redirect
 $ua->max_redirects(3);
@@ -0,0 +1,169 @@
+use Mojo::Base -strict;
+
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
+
+use Test::More;
+use Mojo::IOLoop::Client;
+
+plan skip_all => 'set TEST_SOCKS to enable this test (developer only!)'
+  unless $ENV{TEST_SOCKS};
+plan skip_all => 'IO::Socket::Socks 0.64 required for this test!'
+  unless Mojo::IOLoop::Client::SOCKS;
+plan skip_all => 'IO::Socket::SSL 1.84 required for this test!'
+  unless Mojo::IOLoop::Server::TLS;
+
+use Mojo::IOLoop;
+use Mojo::IOLoop::Server;
+use Mojo::IOLoop::Stream;
+use Mojo::UserAgent;
+use Mojolicious::Lite;
+use Scalar::Util 'weaken';
+
+# Silence
+app->log->level('fatal');
+
+get '/' => sub {
+  my $c = shift;
+  $c->render(text => $c->tx->remote_port);
+};
+
+websocket '/echo' => sub {
+  my $c = shift;
+  $c->on(
+    message => sub {
+      my $c = shift;
+      $c->send($c->tx->remote_port);
+    }
+  );
+};
+
+get '/secure' => sub {
+  my $c = shift;
+  $c->render(
+    text => $c->req->url->to_abs->protocol . ':' . $c->tx->remote_port);
+};
+
+my $port   = Mojo::IOLoop::Server->generate_port;
+my $server = IO::Socket::Socks->new(
+  Blocking    => 0,
+  Listen      => 10,
+  ProxyAddr   => '127.0.0.1',
+  ProxyPort   => $port,
+  RequireAuth => 1,
+  UserAuth    => sub { $_[0] eq 'foo' && $_[1] eq 'bar' }
+);
+
+# SOCKS proxy server for testing
+my $last;
+Mojo::IOLoop->singleton->reactor->io(
+  $server => sub {
+    my $reactor = shift;
+
+    my $client = $server->accept;
+    $client->blocking(0);
+    my ($address, $port);
+    $reactor->io(
+      $client => sub {
+        my $reactor = shift;
+
+        my $err = $IO::Socket::Socks::SOCKS_ERROR;
+        if ($client->ready) {
+
+          if ($address) {
+            $reactor->remove($client);
+            Mojo::IOLoop->client(
+              {address => $address, port => $port} => sub {
+                my ($loop, $err, $server) = @_;
+                $last = $server->handle->sockport;
+                weaken $server;
+                $client = Mojo::IOLoop::Stream->new($client);
+                Mojo::IOLoop->stream($client);
+                $client->on(read  => sub { $server->write(pop) });
+                $client->on(close => sub { $server && $server->close });
+                $server->on(read  => sub { $client->write(pop) });
+                $server->on(close => sub { $client && $client->close });
+              }
+            );
+          }
+
+          else {
+            ($address, $port) = @{$client->command}[1, 2];
+            $client->command_reply(IO::Socket::Socks::REPLY_SUCCESS(),
+              $address, $port);
+          }
+        }
+        elsif ($err == IO::Socket::Socks::SOCKS_WANT_WRITE()) {
+          $reactor->watch($client, 1, 1);
+        }
+        elsif ($err == IO::Socket::Socks::SOCKS_WANT_READ()) {
+          $reactor->watch($client, 1, 0);
+        }
+      }
+    );
+  }
+);
+
+# Failed authentication with SOCKS proxy
+my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
+$ua->proxy->http("socks://foo:baz\@127.0.0.1:$port");
+my $tx = $ua->get('/');
+ok !$tx->success, 'not successful';
+ok $tx->error, 'has error';
+
+# Simple request with SOCKS proxy
+$ua->proxy->http("socks://foo:bar\@127.0.0.1:$port");
+$tx = $ua->get('/');
+ok $tx->success, 'successful';
+ok !$tx->kept_alive, 'kept connection not alive';
+ok $tx->keep_alive, 'keep connection alive';
+is $tx->res->code, 200, 'right status';
+is $tx->req->headers->proxy_authorization, undef,
+  'no "Proxy-Authorization" value';
+is $tx->res->body, $last, 'right content';
+isnt(Mojo::IOLoop->stream($tx->connection)->handle->sockport,
+  $last, 'different ports');
+
+# Keep alive request with SOCKS proxy
+my $before = $last;
+$tx = $ua->get('/');
+ok $tx->success,    'successful';
+ok $tx->kept_alive, 'kept connection alive';
+ok $tx->keep_alive, 'keep connection alive';
+is $tx->res->code, 200, 'right status';
+is $tx->res->body, $last, 'right content';
+is $before, $last, 'same port';
+isnt(Mojo::IOLoop->stream($tx->connection)->handle->sockport,
+  $last, 'different ports');
+
+# WebSocket with SOCKS proxy
+my ($result, $id);
+$ua->websocket(
+  '/echo' => sub {
+    my ($ua, $tx) = @_;
+    $id = $tx->connection;
+    $tx->on(
+      message => sub {
+        $result = pop;
+        Mojo::IOLoop->stop;
+      }
+    );
+    $tx->send('test');
+  }
+);
+Mojo::IOLoop->start;
+is $result, $last, 'right result';
+isnt(Mojo::IOLoop->stream($id)->handle->sockport, $last, 'different ports');
+
+# HTTPS request with SOCKS proxy
+$ua->proxy->https("socks://foo:bar\@127.0.0.1:$port");
+$ua->server->url('https');
+$tx = $ua->get('/secure');
+ok $tx->success, 'successful';
+ok !$tx->kept_alive, 'kept connection not alive';
+ok $tx->keep_alive, 'keep connection alive';
+is $tx->res->code, 200,           'right status';
+is $tx->res->body, "https:$last", 'right content';
+isnt(Mojo::IOLoop->stream($tx->connection)->handle->sockport,
+  $last, 'different ports');
+
+done_testing();
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop::Server;
@@ -6,6 +6,7 @@ use lib "$FindBin::Bin/lib";
 use Test::More;
 use File::Spec::Functions qw(catfile splitdir);
 use File::Temp 'tempdir';
+use Mojo::ByteStream 'b';
 use Mojo::DeprecationTest;
 
 use Mojo::Util
@@ -14,7 +15,7 @@ use Mojo::Util
   qw(monkey_patch punycode_decode punycode_encode quote secure_compare),
   qw(secure_compare sha1_bytes sha1_sum slurp split_header spurt squish),
   qw(steady_time tablify trim unindent unquote url_escape url_unescape),
-  qw(xml_escape xor_encode);
+  qw(xml_escape xor_encode xss_escape);
 
 # camelize
 is camelize('foo_bar_baz'), 'FooBarBaz', 'right camelized result';
@@ -67,8 +68,8 @@ is_deeply split_header('f "o" o , ba  r'),
   'right result';
 is_deeply split_header('foo="b,; a\" r\"\\\\"'), [['foo', 'b,; a" r"\\']],
   'right result';
-is_deeply split_header('foo = "b a\" r\"\\\\"'), [['foo', 'b a" r"\\']],
-  'right result';
+is_deeply split_header('foo = "b a\" r\"\\\\"; bar="ba z"'),
+  [['foo', 'b a" r"\\', 'bar', 'ba z']], 'right result';
 my $header = q{</foo/bar>; rel="x"; t*=UTF-8'de'a%20b};
 my $tree = [['</foo/bar>', undef, 'rel', 'x', 't*', 'UTF-8\'de\'a%20b']];
 is_deeply split_header($header), $tree, 'right result';
@@ -193,6 +194,10 @@ is xml_escape('привет'), 'привет', 'right XML escaped result';
 is xml_escape('привет<foo>'), 'привет&lt;foo&gt;',
   'right XML escaped result';
 
+# xss_escape
+is xss_escape('<p>'), '&lt;p&gt;', 'right XSS escaped result';
+is xss_escape(b('<p>')), '<p>', 'right XSS escaped result';
+
 # punycode_encode
 is punycode_encode('bücher'), 'bcher-kva', 'right punycode encoded result';
 
@@ -408,6 +413,15 @@ is MojoMonkeyTest::yin(), 'yin', 'right result';
 ok !!MojoMonkeyTest->can('yang'), 'function "yang" exists';
 is MojoMonkeyTest::yang(), 'yang', 'right result';
 
+# monkey_patch (with name)
+SKIP: {
+  skip 'Sub::Util required!', 2 unless eval 'use Sub::Util; 1';
+  is Sub::Util::subname(MojoMonkeyTest->can('foo')), 'MojoMonkeyTest::foo',
+    'right name';
+  is Sub::Util::subname(MojoMonkeyTest->can('bar')), 'MojoMonkeyTest::bar',
+    'right name';
+}
+
 # tablify
 is tablify([["f\r\no o\r\n", 'bar']]),     "fo o  bar\n",      'right result';
 is tablify([["  foo",        '  b a r']]), "  foo    b a r\n", 'right result';
@@ -416,6 +430,8 @@ is tablify([['foo', 'yada'], ['yada', 'yada']]), "foo   yada\nyada  yada\n",
   'right result';
 is tablify([['foo', 'bar', 'baz'], ['yada', 'yada', 'yada']]),
   "foo   bar   baz\nyada  yada  yada\n", 'right result';
+is tablify([['a', '', 'b'], ['c', '', 'd']]), "a    b\nc    d\n",
+  'right result';
 
 # deprecated
 {
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use IO::Socket::INET;
@@ -382,7 +379,7 @@ $ua->websocket(
 );
 Mojo::IOLoop->start;
 ok $finished, 'transaction is finished';
-ok !$ws, 'no websocket';
+ok !$ws, 'not a websocket';
 is $code, 500, 'right status';
 is $msg, 'Internal Server Error', 'right message';
 
@@ -398,7 +395,7 @@ $ua->websocket(
   }
 );
 Mojo::IOLoop->start;
-ok !$ws, 'no websocket';
+ok !$ws, 'not a websocket';
 is $code, 403,            'right status';
 is $msg,  "i'm a teapot", 'right message';
 
@@ -420,7 +417,7 @@ $ua->websocket(
 Mojo::IOLoop->start;
 is $status, 1006, 'right status';
 
-# 16bit length
+# 16-bit length
 $result = undef;
 $ua->websocket(
   '/echo' => sub {
@@ -29,6 +29,19 @@ is $frame->[4], 1,          'text frame';
 is $frame->[5], 'whatever', 'right payload';
 is $ws->build_frame(1, 1, 1, 1, 1, 'whatever'), $bytes, 'frames are equal';
 
+# Simple text frame roundtrip without FIN bit
+$ws = Mojo::Transaction::WebSocket->new;
+$bytes = $ws->build_frame(0, 0, 0, 0, 1, 'whatever');
+is $bytes, "\x01\x08\x77\x68\x61\x74\x65\x76\x65\x72", 'right frame';
+$frame = $ws->parse_frame(\($dummy = $bytes));
+is $frame->[0], 0,          'fin flag is not set';
+is $frame->[1], 0,          'rsv1 flag is not set';
+is $frame->[2], 0,          'rsv2 flag is not set';
+is $frame->[3], 0,          'rsv3 flag is not set';
+is $frame->[4], 1,          'text frame';
+is $frame->[5], 'whatever', 'right payload';
+is $ws->build_frame(0, 0, 0, 0, 1, 'whatever'), $bytes, 'frames are equal';
+
 # Simple text frame roundtrip with RSV1 flags set
 $ws = Mojo::Transaction::WebSocket->new;
 $bytes = $ws->build_frame(1, 1, 0, 0, 1, 'whatever');
@@ -140,7 +153,7 @@ is $frame->[4], 2,   'binary frame';
 is $frame->[5], 'a', 'right payload';
 is $bytes = $ws->build_frame(1, 0, 0, 0, 2, 'a'), $bytes, 'frames are equal';
 
-# 16bit text frame roundtrip
+# 16-bit text frame roundtrip
 $ws = Mojo::Transaction::WebSocket->new;
 $bytes = $ws->build_frame(1, 0, 0, 0, 1, 'hi' x 10000);
 is $bytes, "\x81\x7e\x4e\x20" . ("\x68\x69" x 10000), 'right frame';
@@ -153,7 +166,7 @@ is $frame->[4], 1, 'text frame';
 is $frame->[5], 'hi' x 10000, 'right payload';
 is $ws->build_frame(1, 0, 0, 0, 1, 'hi' x 10000), $bytes, 'frames are equal';
 
-# 64bit text frame roundtrip
+# 64-bit text frame roundtrip
 $ws = Mojo::Transaction::WebSocket->new(max_websocket_size => 500000);
 $bytes = $ws->build_frame(1, 0, 0, 0, 1, 'hi' x 200000);
 is $bytes, "\x81\x7f\x00\x00\x00\x00\x00\x06\x1a\x80" . ("\x68\x69" x 200000),
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop;
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop::Server;
@@ -139,6 +136,19 @@ my $id = Mojo::IOLoop->server(
 );
 my $proxy = Mojo::IOLoop->acceptor($id)->handle->sockport;
 
+# Fake server to test failed TLS handshake
+$id = Mojo::IOLoop->server(
+  sub {
+    my ($loop, $stream) = @_;
+    $stream->on(read => sub { shift->close });
+  }
+);
+my $close = Mojo::IOLoop->acceptor($id)->handle->sockport;
+
+# Fake server to test idle connection
+$id = Mojo::IOLoop->server(sub { });
+my $idle = Mojo::IOLoop->acceptor($id)->handle->sockport;
+
 # User agent with valid certificates
 my $ua = Mojo::UserAgent->new(
   ioloop => Mojo::IOLoop->singleton,
@@ -272,7 +282,7 @@ $ua->websocket(
   "wss://localhost:$port2/test" => sub {
     my ($ua, $tx) = @_;
     $success = $tx->success;
-    $err     = $tx->error;
+    $err     = $tx->res->error;
     Mojo::IOLoop->stop;
   }
 );
@@ -280,8 +290,18 @@ Mojo::IOLoop->start;
 ok !$success, 'no success';
 is $err->{message}, 'Proxy connection failed', 'right error';
 
+# Failed TLS handshake through proxy
+$tx = $ua->get("https://localhost:$close");
+is $err->{message}, 'Proxy connection failed', 'right error';
+
+# Idle connection through proxy
+$ua->on(start =>
+    sub { shift->connect_timeout(0.25) if pop->req->method eq 'CONNECT' });
+$tx = $ua->get("https://localhost:$idle");
+is $err->{message}, 'Proxy connection failed', 'right error';
+$ua->connect_timeout(10);
+
 # Blocking proxy request again
-$ua->proxy->https("http://localhost:$proxy");
 $tx = $ua->get("https://localhost:$port/proxy");
 is $tx->res->code, 200, 'right status';
 is $tx->res->body, "https://localhost:$port/proxy", 'right content';
@@ -3,7 +3,6 @@ use Mojo::Base -strict;
 BEGIN {
   $ENV{PLACK_ENV}    = undef;
   $ENV{MOJO_MODE}    = 'development';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -65,6 +64,8 @@ is $t->app->renderer->template_handler(
 is $t->app->build_controller->req->url, '', 'no URL';
 is $t->app->build_controller->render_to_string('does_not_exist'), undef,
   'no result';
+is $t->app->build_controller->render_to_string(inline => '%= $c', c => 'foo'),
+  "foo\n", 'right result';
 
 # Missing methods and functions (AUTOLOAD)
 eval { $t->app->missing };
@@ -91,45 +92,47 @@ like $@, qr/^Undefined subroutine &Mojolicious::Route::missing called/,
 # Hidden controller attributes and methods
 $t->app->routes->hide('bar');
 ok !$t->app->routes->is_hidden('foo'), 'not hidden';
-ok $t->app->routes->is_hidden('bar'),              'is hidden';
-ok $t->app->routes->is_hidden('_foo'),             'is hidden';
-ok $t->app->routes->is_hidden('AUTOLOAD'),         'is hidden';
-ok $t->app->routes->is_hidden('DESTROY'),          'is hidden';
-ok $t->app->routes->is_hidden('FOO_BAR'),          'is hidden';
-ok $t->app->routes->is_hidden('app'),              'is hidden';
-ok $t->app->routes->is_hidden('attr'),             'is hidden';
-ok $t->app->routes->is_hidden('continue'),         'is hidden';
-ok $t->app->routes->is_hidden('cookie'),           'is hidden';
-ok $t->app->routes->is_hidden('finish'),           'is hidden';
-ok $t->app->routes->is_hidden('flash'),            'is hidden';
-ok $t->app->routes->is_hidden('handler'),          'is hidden';
-ok $t->app->routes->is_hidden('has'),              'is hidden';
-ok $t->app->routes->is_hidden('match'),            'is hidden';
-ok $t->app->routes->is_hidden('new'),              'is hidden';
-ok $t->app->routes->is_hidden('on'),               'is hidden';
-ok $t->app->routes->is_hidden('param'),            'is hidden';
-ok $t->app->routes->is_hidden('redirect_to'),      'is hidden';
-ok $t->app->routes->is_hidden('render'),           'is hidden';
-ok $t->app->routes->is_hidden('render_exception'), 'is hidden';
-ok $t->app->routes->is_hidden('render_later'),     'is hidden';
-ok $t->app->routes->is_hidden('render_maybe'),     'is hidden';
-ok $t->app->routes->is_hidden('render_not_found'), 'is hidden';
-ok $t->app->routes->is_hidden('render_static'),    'is hidden';
-ok $t->app->routes->is_hidden('render_to_string'), 'is hidden';
-ok $t->app->routes->is_hidden('rendered'),         'is hidden';
-ok $t->app->routes->is_hidden('req'),              'is hidden';
-ok $t->app->routes->is_hidden('res'),              'is hidden';
-ok $t->app->routes->is_hidden('respond_to'),       'is hidden';
-ok $t->app->routes->is_hidden('send'),             'is hidden';
-ok $t->app->routes->is_hidden('session'),          'is hidden';
-ok $t->app->routes->is_hidden('signed_cookie'),    'is hidden';
-ok $t->app->routes->is_hidden('stash'),            'is hidden';
-ok $t->app->routes->is_hidden('tap'),              'is hidden';
-ok $t->app->routes->is_hidden('tx'),               'is hidden';
-ok $t->app->routes->is_hidden('url_for'),          'is hidden';
-ok $t->app->routes->is_hidden('validation'),       'is hidden';
-ok $t->app->routes->is_hidden('write'),            'is hidden';
-ok $t->app->routes->is_hidden('write_chunk'),      'is hidden';
+ok $t->app->routes->is_hidden('bar'),                 'is hidden';
+ok $t->app->routes->is_hidden('_foo'),                'is hidden';
+ok $t->app->routes->is_hidden('AUTOLOAD'),            'is hidden';
+ok $t->app->routes->is_hidden('DESTROY'),             'is hidden';
+ok $t->app->routes->is_hidden('FOO_BAR'),             'is hidden';
+ok $t->app->routes->is_hidden('app'),                 'is hidden';
+ok $t->app->routes->is_hidden('attr'),                'is hidden';
+ok $t->app->routes->is_hidden('continue'),            'is hidden';
+ok $t->app->routes->is_hidden('cookie'),              'is hidden';
+ok $t->app->routes->is_hidden('every_cookie'),        'is hidden';
+ok $t->app->routes->is_hidden('every_param'),         'is hidden';
+ok $t->app->routes->is_hidden('every_signed_cookie'), 'is hidden';
+ok $t->app->routes->is_hidden('finish'),              'is hidden';
+ok $t->app->routes->is_hidden('flash'),               'is hidden';
+ok $t->app->routes->is_hidden('has'),                 'is hidden';
+ok $t->app->routes->is_hidden('helpers'),             'is hidden';
+ok $t->app->routes->is_hidden('match'),               'is hidden';
+ok $t->app->routes->is_hidden('new'),                 'is hidden';
+ok $t->app->routes->is_hidden('on'),                  'is hidden';
+ok $t->app->routes->is_hidden('param'),               'is hidden';
+ok $t->app->routes->is_hidden('redirect_to'),         'is hidden';
+ok $t->app->routes->is_hidden('render'),              'is hidden';
+ok $t->app->routes->is_hidden('render_exception'),    'is hidden';
+ok $t->app->routes->is_hidden('render_later'),        'is hidden';
+ok $t->app->routes->is_hidden('render_maybe'),        'is hidden';
+ok $t->app->routes->is_hidden('render_not_found'),    'is hidden';
+ok $t->app->routes->is_hidden('render_to_string'),    'is hidden';
+ok $t->app->routes->is_hidden('rendered'),            'is hidden';
+ok $t->app->routes->is_hidden('req'),                 'is hidden';
+ok $t->app->routes->is_hidden('res'),                 'is hidden';
+ok $t->app->routes->is_hidden('respond_to'),          'is hidden';
+ok $t->app->routes->is_hidden('send'),                'is hidden';
+ok $t->app->routes->is_hidden('session'),             'is hidden';
+ok $t->app->routes->is_hidden('signed_cookie'),       'is hidden';
+ok $t->app->routes->is_hidden('stash'),               'is hidden';
+ok $t->app->routes->is_hidden('tap'),                 'is hidden';
+ok $t->app->routes->is_hidden('tx'),                  'is hidden';
+ok $t->app->routes->is_hidden('url_for'),             'is hidden';
+ok $t->app->routes->is_hidden('validation'),          'is hidden';
+ok $t->app->routes->is_hidden('write'),               'is hidden';
+ok $t->app->routes->is_hidden('write_chunk'),         'is hidden';
 
 # Unknown hooks
 ok !$t->app->plugins->emit_chain('does_not_exist'), 'hook has been emitted';
@@ -224,8 +227,7 @@ like $log, qr/Rendering template "syntaxerror.html.epl"\./, 'right message';
 like $log, qr/Missing right curly/, 'right message';
 like $log, qr/Template "exception.development.html.ep" not found\./,
   'right message';
-like $log, qr/Rendering cached template "exception.html.epl"\./,
-  'right message';
+like $log, qr/Rendering template "exception.html.epl"\./, 'right message';
 like $log, qr/500 Internal Server Error/, 'right message';
 $t->app->log->unsubscribe(message => $cb);
 
@@ -259,10 +261,16 @@ $t->get_ok('/fun/time' => {'X-Test' => 'Hi there!'})->status_is(200)
 
 # Foo::fun
 $url = $t->ua->server->url;
+$log = '';
+$cb  = $t->app->log->on(message => sub { $log .= pop });
 $url->path('/fun/time');
 $t->get_ok($url => {'X-Test' => 'Hi there!'})->status_is(200)
   ->header_is('X-Bender' => undef)->header_is(Server => 'Mojolicious (Perl)')
   ->content_is('Have fun!');
+like $log,
+  qr!Rendering cached template "foo/fun\.html\.ep" from DATA section\.!,
+  'right message';
+$t->app->log->unsubscribe(message => $cb);
 
 # Foo::fun
 $t->get_ok('/happy/fun/time' => {'X-Test' => 'Hi there!'})->status_is(200)
@@ -374,8 +382,9 @@ my $mtime = Mojo::Date->new((stat $path)[9])->to_string;
 
 # Static file /hello.txt
 $t->get_ok('/hello.txt')->status_is(200)
-  ->header_is(Server => 'Mojolicious (Perl)')
-  ->header_is('Last-Modified' => $mtime)->header_is('Content-Length' => $size)
+  ->header_is(Server           => 'Mojolicious (Perl)')
+  ->header_is('Last-Modified'  => $mtime)->header_like('ETag' => qr/^"\w+"$/)
+  ->header_is('Content-Length' => $size)
   ->content_type_is('text/plain;charset=UTF-8')
   ->content_like(qr/Hello Mojo from a development static file!/);
 
@@ -389,6 +398,28 @@ $t->get_ok('/../../mojolicious/secret.txt')->status_is(404)
 $t->get_ok('/hello.txt' => {'If-Modified-Since' => $mtime})->status_is(304)
   ->header_is(Server => 'Mojolicious (Perl)')->content_is('');
 
+# Check If-None-Match
+my $etag = $t->tx->res->headers->etag;
+$t->get_ok('/hello.txt' => {'If-None-Match' => $etag})->status_is(304)
+  ->header_is(Server => 'Mojolicious (Perl)')->content_is('');
+
+# Check If-None-Match and If-Last-Modified
+$t->get_ok(
+  '/hello.txt' => {'If-None-Match' => $etag, 'If-Last-Modified' => $mtime})
+  ->status_is(304)->header_is(Server => 'Mojolicious (Perl)')->content_is('');
+
+# Bad If-None-Match with correct If-Modified-Since
+$t->get_ok(
+  '/hello.txt' => {'If-None-Match' => '"123"', 'If-Modified-Since' => $mtime})
+  ->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
+  ->content_like(qr/Hello Mojo from a development static file!/);
+
+# Bad If-Modified-Since with correct If-None-Match
+$t->get_ok('/hello.txt' =>
+    {'If-Modified-Since' => Mojo::Date->new(23), 'If-None-Match' => $etag})
+  ->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
+  ->content_like(qr/Hello Mojo from a development static file!/);
+
 # Embedded development static file
 $t->get_ok('/some/static/file.txt')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::ByteStream 'b';
@@ -10,7 +10,14 @@ use Test::More;
 use FindBin;
 use lib "$FindBin::Bin/lib";
 
-use Mojolicious::Commands;
+# Make sure @ARGV is not changed
+{
+  local $ENV{MOJO_MODE};
+  local @ARGV = qw(-m production -x whatever);
+  require Mojolicious::Commands;
+  is $ENV{MOJO_MODE}, 'production', 'right mode';
+  is_deeply \@ARGV, [qw(-m production -x whatever)], 'unchanged';
+}
 
 # Environment detection
 my $commands = Mojolicious::Commands->new;
@@ -55,6 +62,16 @@ is $app->start('test_command'), 'works!', 'right result';
     'works!', 'right result');
 }
 
+# Do not pick up options for detected environments
+{
+  local $ENV{MOJO_MODE};
+  local $ENV{PLACK_ENV} = 'testing';
+  local @ARGV = qw(psgi -m production);
+  is ref Mojolicious::Commands->start_app('MojoliciousTest'), 'CODE',
+    'right reference';
+  is $ENV{MOJO_MODE}, undef, 'no mode';
+}
+
 # mojo
 ok $commands->description, 'has a description';
 like $commands->message,   qr/COMMAND/, 'has a message';
@@ -70,14 +70,15 @@ is_deeply $stash, {a => 1, b => 2}, 'set via hash reference';
 is $c->param('foo'), undef, 'no value';
 is $c->param(foo => 'works')->param('foo'), 'works', 'right value';
 is $c->param(foo => 'too')->param('foo'),   'too',   'right value';
-is $c->param(foo => qw(just works))->param('foo'), 'just', 'right value';
-is_deeply [$c->param('foo')], [qw(just works)], 'right values';
+is $c->param(foo => qw(just works))->param('foo'), 'works', 'right value';
+is_deeply $c->every_param('foo'), [qw(just works)], 'right values';
+is_deeply $c->every_param('bar'), [], 'no values';
 is $c->param(foo => undef)->param('foo'), undef, 'no value';
 is $c->param(foo => Mojo::Upload->new(name => 'bar'))->param('foo')->name,
   'bar', 'right value';
-is scalar $c->param(foo => ['ba;r', 'baz'])->param('foo'), 'ba;r',
+is scalar $c->param(foo => ['ba;r', 'baz'])->param('foo'), 'baz',
   'right value';
-is_deeply [$c->param('foo')], ['ba;r', 'baz'], 'right values';
+is_deeply $c->every_param('foo'), ['ba;r', 'baz'], 'right values';
 
 # Reserved stash values are hidden
 $c = Mojolicious::Controller->new;
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'testing';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'testing';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -194,7 +193,7 @@ $t->get_ok('/x/1/stream')->status_is(200)->content_is('hello!');
 
 # URL from myapp.pl
 $t->get_ok('/x/1/url/☃')->status_is(200)
-  ->content_is('/x/1/url/%E2%98%83 -> /x/1/%E2%98%83/stream!');
+  ->content_is('/x/1/url/%E2%98%83.json -> /x/1/%E2%98%83/stream!');
 
 # Route to template from myapp.pl
 $t->get_ok('/x/1/template/menubar')->status_is(200)
@@ -227,7 +226,8 @@ $t->get_ok('/x/♥/stream')->status_is(200)->content_is('hello!');
 
 # URL from myapp.pl with unicode prefix
 $t->get_ok('/x/♥/url/☃')->status_is(200)
-  ->content_is('/x/%E2%99%A5/url/%E2%98%83 -> /x/%E2%99%A5/%E2%98%83/stream!');
+  ->content_is(
+  '/x/%E2%99%A5/url/%E2%98%83.json -> /x/%E2%99%A5/%E2%98%83/stream!');
 
 # Route to template from myapp.pl with unicode prefix
 $t->get_ok('/x/♥/template/menubar')->status_is(200)
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'development';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -37,7 +37,7 @@ get '/stream' => sub {
 
 get '/url/☃' => sub {
   my $c     = shift;
-  my $route = $c->url_for;
+  my $route = $c->url_for({format => 'json'});
   my $rel   = $c->url_for('/☃/stream');
   $c->render(text => "$route -> $rel!");
 };
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'testing';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'testing';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -39,6 +38,6 @@ $t->get_ok('/stream')->status_is(200)->content_is('hello!');
 
 # URL generated by myapp.pl
 $t->get_ok('/url/☃')->status_is(200)
-  ->content_is('/url/%E2%98%83 -> /%E2%98%83/stream!');
+  ->content_is('/url/%E2%98%83.json -> /%E2%98%83/stream!');
 
 done_testing();
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::ByteStream 'b';
@@ -206,7 +203,7 @@ $t->get_ok('/multi')->status_is(200)
 # Multiple cookies with same name (again)
 $t->get_ok('/multi')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')
-  ->content_is("one\nthree\none\ntwo\nfour\nsix\nfour\nfive\n");
+  ->content_is("two\nthree\none\ntwo\nfive\nsix\nfour\nfive\n");
 
 # Missing action behind bridge
 $t->get_ok('/missing')->status_is(404)->content_is("Oops!\n");
@@ -274,9 +271,8 @@ $cb = $t->app->log->on(message => sub { $log .= pop });
 $t->get_ok('/bridge2stash')->status_is(200)
   ->content_is(
   "stash too!cookie!!signed_cookie!!bad_cookie--12345678!session!flash!\n");
-like $log, qr/Cookie "foo" not signed\./, 'right message';
-like $log, qr/Bad signed cookie "bad", possible hacking attempt\./,
-  'right message';
+like $log, qr/Cookie "foo" not signed\./,        'right message';
+like $log, qr/Cookie "bad" has bad signature\./, 'right message';
 ok $t->tx->res->cookie('mojolicious')->httponly,
   'session cookie has HttpOnly flag';
 $t->app->log->unsubscribe(message => $cb);
@@ -288,15 +284,15 @@ my $hmac    = $session->clone->hmac_sha1_sum($t->app->secrets->[0]);
 $t->get_ok('/bridge2stash' => {Cookie => "mojolicious=$session--$hmac"})
   ->status_is(200)->content_is("stash too!!!!!!!!\n");
 
-# Without cookie jar
-$t->ua->cookie_jar(0);
+# Not extracting cookies
+$t->reset_session->ua->cookie_jar->extracting(0);
 $t->get_ok('/bridge2stash' => {'X-Flash' => 1})->status_is(200)
   ->content_is("stash too!!!!!!!!\n");
 
-# Again without cookie jar
+# Still not extracting cookies
 $t->get_ok('/bridge2stash' => {'X-Flash' => 1})->status_is(200)
   ->content_is("stash too!!!!!!!!\n");
-$t->reset_session->ua->cookie_jar(Mojo::UserAgent::CookieJar->new);
+$t->ua->cookie_jar->extracting(1);
 
 # Fresh start without cookies, session or flash
 $t->get_ok('/bridge2stash' => {'X-Flash' => 1})->status_is(200)
@@ -474,15 +470,15 @@ Oops!
 % my ($one, $three) = $c->cookie([qw(unsigned1 unsigned2)]);
 %= $one // ''
 %= $three // '';
-% my @unsigned1 = $c->cookie('unsigned1');
-%= $unsigned1[0] // ''
-%= $unsigned1[1] // ''
+% my $unsigned1 = $c->every_cookie('unsigned1');
+%= $unsigned1->[0] // ''
+%= $unsigned1->[1] // ''
 % my ($four, $six) = $c->signed_cookie([qw(signed1 signed2)]);
 %= $four // ''
 %= $six // '';
-% my @signed1 = $c->signed_cookie('signed1');
-%= $signed1[0] // ''
-%= $signed1[1] // ''
+% my $signed1 = $c->every_signed_cookie('signed1');
+%= $signed1->[0] // ''
+%= $signed1->[1] // ''
 
 @@ param_auth.html.epl
 Bender!
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'development';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -17,13 +16,17 @@ use Test::Mojo;
 app->config(it => 'works');
 is_deeply app->config, {it => 'works'}, 'right value';
 
+# Invalid config file
+my $path = abs_path catfile(dirname(__FILE__), 'public', 'hello.txt');
+eval { plugin JSONConfig => {file => $path}; };
+like $@, qr/Malformed JSON/, 'right error';
+
 # Load plugins
 my $config
   = plugin j_s_o_n_config => {default => {foo => 'baz', hello => 'there'}};
 my $log = '';
 my $cb = app->log->on(message => sub { $log .= pop });
-my $path
-  = abs_path(catfile(dirname(__FILE__), 'json_config_lite_app_abs.json'));
+$path = abs_path catfile(dirname(__FILE__), 'json_config_lite_app_abs.json');
 plugin JSONConfig => {file => $path};
 like $log, qr/Reading configuration file "\Q$path\E"\./, 'right message';
 app->log->unsubscribe(message => $cb);
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'testing';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 
@@ -16,9 +16,6 @@ sub startup {
   push @{$self->renderer->classes}, 'SingleFileTestApp::Foo';
   push @{$self->static->classes},   'SingleFileTestApp::Foo';
 
-  # Allow redispatching controller
-  push @{$self->routes->base_classes}, 'Mojo::Base';
-
   # Helper route
   $self->routes->route('/helper')->to(
     cb => sub {
@@ -32,7 +29,7 @@ sub startup {
 }
 
 package SingleFileTestApp::Redispatch;
-use Mojo::Base -base;
+use Mojo::Base 'Mojo';
 
 sub handler {
   my ($self, $c) = @_;
@@ -63,7 +60,7 @@ sub data_template { shift->render('index') }
 
 sub data_template2 { shift->stash(template => 'too') }
 
-sub data_static { shift->render_static('singlefiletestapp/foo.txt') }
+sub data_static { shift->reply->static('singlefiletestapp/foo.txt') }
 
 sub index {
   shift->stash(template => 'WithGreenLayout', msg => 'works great!');
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'development';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -38,6 +37,12 @@ is app->test_helper2, 'Mojolicious::Controller', 'right value';
 app->test_helper3->{foo} = 'bar';
 is app->test_helper3->{foo}, 'bar', 'right result';
 
+# Nested helpers
+helper 'test.helper' => sub { shift->app->controller_class };
+is app->test->helper, 'Mojolicious::Controller', 'right value';
+is app->build_controller->test->helper, 'Mojolicious::Controller',
+  'right value';
+
 # Test renderer
 app->renderer->add_handler(dead => sub { die 'renderer works!' });
 
@@ -91,8 +96,8 @@ any sub { shift->render(text => 'Bye!') };
 
 post '/multipart/form' => sub {
   my $c    = shift;
-  my @test = $c->param('test');
-  $c->render(text => join "\n", @test);
+  my $test = $c->every_param('test');
+  $c->render(text => join "\n", @$test);
 };
 
 get '/auto_name' => sub {
@@ -197,8 +202,9 @@ get '/root' => sub { shift->render(text => 'root fallback!') };
 get '/template.txt' => {template => 'template', format => 'txt'};
 
 get ':number' => [number => qr/0/] => sub {
-  my $c       = shift;
-  my $url     = $c->req->url->to_abs;
+  my $c   = shift;
+  my $url = $c->req->url->to_abs;
+  $c->res->headers->header('X-Original' => $c->tx->original_remote_address);
   my $address = $c->tx->remote_address;
   my $num     = $c->param('number');
   $c->render(text => "$url-$address-$num");
@@ -231,7 +237,7 @@ get '/source' => sub {
   my $c = shift;
   my $file = $c->param('fail') ? 'does_not_exist.txt' : '../lite_app.t';
   $c->render_maybe('this_does_not_ever_exist')
-    or $c->render_static($file)
+    or $c->reply->static($file)
     or $c->res->headers->header('X-Missing' => 1);
 };
 
@@ -369,24 +375,14 @@ get '/subrequest_non_blocking' => sub {
   $c->stash->{nb} = 'success!';
 };
 
-get '/redirect_url' => sub {
-  shift->redirect_to('http://127.0.0.1/foo')->render(text => 'Redirecting!');
-};
+get '/redirect_url' => sub { shift->redirect_to('http://127.0.0.1/foo') };
 
-get '/redirect_path' => sub {
-  shift->redirect_to('/foo/bar?foo=bar')->render(text => 'Redirecting!');
-};
+get '/redirect_path' => sub { shift->redirect_to('/foo/bar?foo=bar') };
 
-get '/redirect_named' => sub {
-  shift->redirect_to('index', format => 'txt')->render(text => 'Redirecting!');
-};
+get '/redirect_named' => sub { shift->redirect_to('index', format => 'txt') };
 
 get '/redirect_twice' => sub { shift->redirect_to('/redirect_named') };
 
-get '/redirect_no_render' => sub {
-  shift->redirect_to('index', {format => 'txt'});
-};
-
 get '/redirect_callback' => sub {
   my $c = shift;
   Mojo::IOLoop->next_tick(
@@ -398,7 +394,7 @@ get '/redirect_callback' => sub {
   );
 };
 
-get '/static_render' => sub { shift->render_static('hello.txt') };
+get '/static' => sub { shift->reply->static('hello.txt') };
 
 app->types->type('koi8-r' => 'text/html; charset=koi8-r');
 get '/koi8-r' => sub {
@@ -474,6 +470,8 @@ is $t->app->build_controller($t->app->ua->build_tx(GET => '/foo'))->req->url,
   '/foo', 'right URL';
 is $t->app->build_controller->render_to_string('index', handler => 'epl'),
   'Just works!', 'right result';
+is $t->app->build_controller->render_to_string(inline => '0'), "0\n",
+  'right result';
 
 # Unicode snowman
 $t->get_ok('/☃')->status_is(200)
@@ -600,7 +598,7 @@ $t->get_ok('/multi/B?foo=A&baz=C')->status_is(200)
 # Injection attack
 $t->get_ok('/multi/B?foo=A&foo=E&baz=C&yada=D&yada=text&yada=fail')
   ->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
-  ->content_is('ABC');
+  ->content_is('EBC');
 
 # Missing parameter
 $t->get_ok('/multi/B?baz=C')->status_is(200)
@@ -733,7 +731,8 @@ $t->get_ok('/.html')->status_is(200)
   local $ENV{MOJO_REVERSE_PROXY} = 1;
   $t->ua->server->restart;
   $t->get_ok('/0' => {'X-Forwarded-For' => '192.0.2.2, 192.0.2.1'})
-    ->status_is(200)->content_like(qr!http://localhost:\d+/0-192\.0\.2\.1-0$!);
+    ->status_is(200)->header_unlike('X-Original' => qr/192\.0\.2\.1/)
+    ->content_like(qr!http://localhost:\d+/0-192\.0\.2\.1-0$!);
 }
 
 # Reverse proxy with "X-Forwarded-Proto"
@@ -965,22 +964,18 @@ is $nb, 'broken!', 'right text';
 
 # Redirect to URL
 $t->get_ok('/redirect_url')->status_is(302)
-  ->header_is(Server           => 'Mojolicious (Perl)')
-  ->header_is('Content-Length' => 12)
-  ->header_is(Location => 'http://127.0.0.1/foo')->content_is('Redirecting!');
+  ->header_is(Server   => 'Mojolicious (Perl)')
+  ->header_is(Location => 'http://127.0.0.1/foo')->content_is('');
 
 # Redirect to path
 $t->get_ok('/redirect_path')->status_is(302)
-  ->header_is(Server           => 'Mojolicious (Perl)')
-  ->header_is('Content-Length' => 12)
-  ->header_like(Location => qr!/foo/bar\?foo=bar$!)
-  ->content_is('Redirecting!');
+  ->header_is(Server => 'Mojolicious (Perl)')
+  ->header_like(Location => qr!/foo/bar\?foo=bar$!)->content_is('');
 
 # Redirect to named route
 $t->get_ok('/redirect_named')->status_is(302)
-  ->header_is(Server           => 'Mojolicious (Perl)')
-  ->header_is('Content-Length' => 12)
-  ->header_like(Location => qr!/template.txt$!)->content_is('Redirecting!');
+  ->header_is(Server => 'Mojolicious (Perl)')
+  ->header_like(Location => qr!/template.txt$!)->content_is('');
 
 # Redirect twice
 $t->ua->max_redirects(3);
@@ -993,12 +988,6 @@ is $redirects->[0]->req->url->path, '/redirect_twice', 'right path';
 is $redirects->[1]->req->url->path, '/redirect_named', 'right path';
 $t->ua->max_redirects(0);
 
-# Redirect without rendering
-$t->get_ok('/redirect_no_render')->status_is(302)
-  ->header_is(Server           => 'Mojolicious (Perl)')
-  ->header_is('Content-Length' => 0)
-  ->header_like(Location => qr!/template.txt$!)->content_is('');
-
 # Non-blocking redirect
 $t->get_ok('/redirect_callback')->status_is(301)
   ->header_is(Server           => 'Mojolicious (Perl)')
@@ -1006,7 +995,7 @@ $t->get_ok('/redirect_callback')->status_is(301)
   ->header_is(Location => 'http://127.0.0.1/foo')->content_is('Whatever!');
 
 # Static file
-$t->get_ok('/static_render')->status_is(200)
+$t->get_ok('/static')->status_is(200)
   ->header_is(Server           => 'Mojolicious (Perl)')
   ->header_is('Content-Length' => 31)
   ->content_is("Hello Mojo from a static file!\n");
@@ -1022,7 +1011,7 @@ $t->get_ok('/redirect_named')->status_is(200)
 $t->ua->max_redirects(0);
 Test::Mojo->new->tx($t->tx->previous)->status_is(302)
   ->header_is(Server => 'Mojolicious (Perl)')
-  ->header_like(Location => qr!/template.txt$!)->content_is('Redirecting!');
+  ->header_like(Location => qr!/template.txt$!)->content_is('');
 
 # Request with koi8-r content
 my $koi8
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop;
@@ -175,7 +172,7 @@ get '/longpoll/static/delayed' => sub {
   $c->on(finish => sub { shift->stash->{finished}++ });
   $c->cookie(bar => 'baz');
   $c->session(foo => 'bar');
-  Mojo::IOLoop->timer(0.25 => sub { $c->render_static('hello.txt') });
+  Mojo::IOLoop->timer(0.25 => sub { $c->reply->static('hello.txt') });
 };
 
 get '/longpoll/dynamic/delayed' => sub {
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojolicious::Lite;
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'development';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_PROXY}   = 0;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
@@ -49,4 +48,18 @@ is c(1, 2, 3)->join('-'), '1-2-3', 'right result';
 # Dumper
 is r([1, 2]), "[\n  1,\n  2\n]\n", 'right result';
 
+# Benchmark
+{
+  my $buffer = '';
+  open my $handle, '>', \$buffer;
+  local *STDERR = $handle;
+  my $i = 0;
+  n { ++$i };
+  is $i,        1,             'block has been invoked once';
+  like $buffer, qr/wallclock/, 'right output';
+  n { $i++ } 10;
+  is $i, 11, 'block has been invoked ten times';
+  like $buffer, qr/wallclock.*wallclock/s, 'right output';
+}
+
 done_testing();
@@ -4,8 +4,14 @@ use Test::More;
 use Mojo::ByteStream 'b';
 use Mojolicious::Routes::Pattern;
 
+# Text pattern (optimized)
+my $pattern = Mojolicious::Routes::Pattern->new('/test/123');
+is_deeply $pattern->match('/test/123'), {}, 'right structure';
+is_deeply $pattern->match('/test'), undef, 'no result';
+is $pattern->tree->[0][1], '/test/123', 'optimized pattern';
+
 # Normal pattern with text, placeholders and a default value
-my $pattern = Mojolicious::Routes::Pattern->new('/test/(controller)/:action');
+$pattern = Mojolicious::Routes::Pattern->new('/test/(controller)/:action');
 $pattern->defaults({action => 'index'});
 is_deeply $pattern->match('/test/foo/bar', 1),
   {controller => 'foo', action => 'bar'}, 'right structure';
@@ -55,10 +61,12 @@ is $pattern->render({a => 'c', b => 'd'}), '/test/c/123/d/456', 'right result';
 
 # Root
 $pattern = Mojolicious::Routes::Pattern->new('/');
+is $pattern->pattern, undef, 'slash has been optimized away';
 $pattern->defaults({action => 'index'});
 ok !$pattern->match('/test/foo/bar'), 'no result';
 is_deeply $pattern->match('/'), {action => 'index'}, 'right structure';
-is $pattern->render, '/', 'right result';
+is $pattern->render, '', 'right result';
+is $pattern->render({format => 'txt'}, 1), '.txt', 'right result';
 
 # Regex in pattern
 $pattern = Mojolicious::Routes::Pattern->new('/test/(controller)/:action/(id)',
@@ -246,12 +254,19 @@ is $pattern->render($result, 1), '/foo/bar', 'right result';
 $pattern = Mojolicious::Routes::Pattern->new('//');
 $result = $pattern->match('/', 1);
 is_deeply $result, {}, 'right structure';
-is $pattern->render($result, 1), '/', 'right result';
+is $pattern->render($result, 1), '', 'right result';
 $pattern = Mojolicious::Routes::Pattern->new('0');
 $result = $pattern->match('/0', 1);
 is_deeply $result, {}, 'right structure';
 is $pattern->render($result, 1), '/0', 'right result';
 
+# Optional format with constraint
+$pattern                        = Mojolicious::Routes::Pattern->new('/');
+$pattern->defaults->{format}    = 'txt';
+$pattern->constraints->{format} = ['txt'];
+$result                         = $pattern->match('/', 1);
+is_deeply $result, {format => 'txt'}, 'right structure';
+
 # Unicode
 $pattern = Mojolicious::Routes::Pattern->new('/(one)♥(two)');
 $result  = $pattern->match('/i♥mojolicious');
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojolicious::Lite;
@@ -31,17 +28,18 @@ my $t = Test::Mojo->new;
 
 # Simple POD template
 $t->get_ok('/')->status_is(200)
-  ->content_like(qr|<h1>Test123</h1>\s+<p>It <code>works</code>!</p>|);
+  ->content_like(qr!<h1 id="Test123">Test123</h1>!)
+  ->content_like(qr|<p>It <code>works</code>!</p>|);
 
 # POD helper
-$t->post_ok('/')->status_is(200)
-  ->content_like(qr!test123\s+<h1>A</h1>\s+<h1>B</h1>!)
+$t->post_ok('/')->status_is(200)->content_like(qr!test123<h1 id="A">A</h1>!)
+  ->content_like(qr!<h1 id="B">B</h1>!)
   ->content_like(qr!\s+<p><code>test</code></p>!)->content_like(qr/Gray/);
 
 # POD filter
 $t->post_ok('/block')->status_is(200)
-  ->content_like(qr!test321\s+<h2>lalala</h2>\s+<p><code>test</code></p>!)
-  ->content_like(qr/Gray/);
+  ->content_like(qr!test321<h2 id="lalala">lalala</h2>!)
+  ->content_like(qr!<p><code>test</code></p>!)->content_like(qr/Gray/);
 
 # Empty
 $t->get_ok('/empty')->status_is(200)->content_is('');
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'production';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -35,6 +34,10 @@ is $t->app->static->file('hello.txt')->slurp,
   "Hello Mojo from a static file!\n", 'right content';
 is $t->app->moniker, 'mojolicious_test', 'right moniker';
 
+# Default namespaces
+is_deeply $t->app->routes->namespaces,
+  ['MojoliciousTest::Controller', 'MojoliciousTest'], 'right namespaces';
+
 # Plugin::Test::SomePlugin2::register (security violation)
 $t->get_ok('/plugin-test-some_plugin2/register')->status_isnt(500)
   ->status_is(404)->header_is(Server => 'Mojolicious (Perl)')
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::URL;
@@ -1,6 +1,7 @@
 use Mojo::Base -strict;
 
 use Test::More;
+use Mojo::Util 'decode';
 use Mojolicious::Controller;
 
 # Partial rendering
@@ -61,4 +62,60 @@ $c->cookie(foo => 'x' x 4097);
 like $log, qr/Cookie "foo" is bigger than 4096 bytes\./, 'right message';
 $c->app->log->unsubscribe(message => $cb);
 
+# Nested helpers
+my $first = Mojolicious::Controller->new;
+$first->helpers->app->log->level('fatal');
+$first->app->helper('myapp.multi_level.test' => sub {'works!'});
+ok $first->app->renderer->get_helper('myapp'),                  'found helper';
+ok $first->app->renderer->get_helper('myapp.multi_level'),      'found helper';
+ok $first->app->renderer->get_helper('myapp.multi_level.test'), 'found helper';
+is $first->myapp->multi_level->test, 'works!', 'right result';
+is $first->helpers->myapp->multi_level->test, 'works!', 'right result';
+$first->app->helper('myapp.defaults' => sub { shift->app->defaults(@_) });
+ok $first->app->renderer->get_helper('myapp.defaults'), 'found helper';
+is $first->app->renderer->get_helper('myap.'),          undef, 'no helper';
+is $first->app->renderer->get_helper('yapp'),           undef, 'no helper';
+$first->myapp->defaults(foo => 'bar');
+is $first->myapp->defaults('foo'), 'bar', 'right result';
+is $first->helpers->myapp->defaults('foo'), 'bar', 'right result';
+is $first->app->myapp->defaults('foo'),     'bar', 'right result';
+my $second = Mojolicious::Controller->new;
+$second->app->log->level('fatal');
+is $second->app->renderer->get_helper('myapp'),          undef, 'no helper';
+is $second->app->renderer->get_helper('myapp.defaults'), undef, 'no helper';
+$second->app->helper('myapp.defaults' => sub {'nothing'});
+my $myapp = $first->myapp;
+is $first->myapp->defaults('foo'),  'bar',     'right result';
+is $second->myapp->defaults('foo'), 'nothing', 'right result';
+is $second->helpers->myapp->defaults('foo'), 'nothing', 'right result';
+is $first->myapp->defaults('foo'), 'bar', 'right result';
+is $first->helpers->myapp->defaults('foo'), 'bar', 'right result';
+
+# Reuse proxy objects
+my $helpers = $first->helpers;
+is $helpers->myapp->multi_level->test, $helpers->myapp->multi_level->test,
+  'same result';
+
+# Missing method (AUTOLOAD)
+my $class = ref $first->myapp;
+eval { $first->myapp->missing };
+like $@, qr/^Can't locate object method "missing" via package "$class"/,
+  'right error';
+eval { $first->app->myapp->missing };
+like $@, qr/^Can't locate object method "missing" via package "$class"/,
+  'right error';
+
+# No leaky namespaces
+my $helper_class = ref $second->myapp;
+is ref $second->myapp, $helper_class, 'same class';
+ok $helper_class->can('defaults'), 'helpers are active';
+my $template_class = decode 'UTF-8',
+  $second->render_to_string(inline => "<%= __PACKAGE__ =%>");
+is decode('UTF-8', $second->render_to_string(inline => "<%= __PACKAGE__ =%>")),
+  $template_class, 'same class';
+ok $template_class->can('stash'), 'helpers are active';
+undef $second;
+ok !$helper_class->can('defaults'), 'helpers have been cleaned up';
+ok !$template_class->can('stash'),  'helpers have been cleaned up';
+
 done_testing();
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojolicious::Lite;
@@ -38,6 +38,12 @@ $r->route('/alternatives4/:foo', foo => [qw(foo foo.bar)]);
 # /optional/*/*
 $r->route('/optional/:foo/:bar')->to(bar => 'test');
 
+# /optional2
+# /optional2/*
+# /optional2/*/*
+$r->route('/optional2/:foo')->to(foo => 'one')->route('/:bar')
+  ->to(bar => 'two');
+
 # /*/test
 my $test = $r->route('/:controller/test')->to(action => 'test');
 
@@ -180,11 +186,19 @@ $source->route('/second')->to('#second');
 my $third  = $source->route('/third')->to('#third');
 my $target = $r->remove->route('/target')->to('target#');
 my $second = $r->find('second');
-is $second->render('', {}), '/source/second', 'right result';
+is $second->render({}), '/source/second', 'right result';
 $second->remove;
-is $second->render('', {}), '/second', 'right result';
+is $second->render({}), '/second', 'right result';
 $target->add_child($first)->add_child($second);
-is $second->render('', {}), '/target/second', 'right result';
+is $second->render({}), '/target/second', 'right result';
+
+# /websocket
+$r->websocket('/websocket' => {controller => 'ws'})->route('/')
+  ->to(action => 'just')->route->to(works => 1);
+
+# /slash
+$r->route('/slash')->to(controller => 'just')->route('/')
+  ->to(action => 'slash');
 
 # /missing/*/name
 # /missing/too
@@ -194,6 +208,15 @@ $r->route('/missing/*/name')->to('missing#wildcard');
 $r->route('/missing/too/*', '' => ['test'])
   ->to('missing#too', '' => 'missing');
 
+# /partial/*
+$r->route('/partial')->detour('foo#bar');
+
+# GET  /similar/*
+# POST /similar/too
+my $similar = $r->bridge('/similar');
+$similar->route('/:something')->via('GET')->to('similar#get');
+$similar->route('/too')->via('POST')->to('similar#post');
+
 # Cached lookup
 my $fast = $r->route('/fast');
 is $r->find('fast'),   $fast, 'fast route found';
@@ -355,6 +378,30 @@ is $m->path_for('optionalfoobar')->{path}, '/optional/23/24', 'right path';
 is $m->path_for('optionalfoobar', foo => 0)->{path}, '/optional/0/24',
   'right path';
 
+# Optional placeholders in nested routes
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/optional2'});
+is_deeply $m->stack, [{foo => 'one', bar => 'two'}], 'right structure';
+is $m->path_for->{path}, '/optional2', 'right path';
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/optional2.txt'});
+is_deeply $m->stack, [{foo => 'one', bar => 'two', format => 'txt'}],
+  'right structure';
+is $m->path_for->{path}, '/optional2', 'right path';
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/optional2/three'});
+is_deeply $m->stack, [{foo => 'three', bar => 'two'}], 'right structure';
+is $m->path_for->{path}, '/optional2/three', 'right path';
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/optional2/three/four'});
+is_deeply $m->stack, [{foo => 'three', bar => 'four'}], 'right structure';
+is $m->path_for->{path}, '/optional2/three/four', 'right path';
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/optional2/three/four.txt'});
+is_deeply $m->stack, [{foo => 'three', bar => 'four', format => 'txt'}],
+  'right structure';
+is $m->path_for->{path}, '/optional2/three/four', 'right path';
+
 # Real world example using most features at once
 $m = Mojolicious::Routes::Match->new(root => $r);
 $m->match($c => {method => 'GET', path => '/articles/1/edit'});
@@ -815,6 +862,26 @@ $m = Mojolicious::Routes::Match->new(root => $r);
 $m->match($c => {method => 'GET', path => '/target/third'});
 is_deeply $m->stack, [], 'empty stack';
 
+# WebSocket
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/websocket'});
+is_deeply $m->stack, [], 'empty stack';
+$m->match($c => {method => 'GET', path => '/websocket', websocket => 1});
+is_deeply $m->stack, [{controller => 'ws', action => 'just', works => 1}],
+  'right structure';
+is $m->path_for->{path}, '/websocket', 'right path';
+ok $m->path_for->{websocket}, 'is a websocket';
+
+# Just a slash with a format after a path
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/slash.txt'});
+is_deeply $m->stack,
+  [{controller => 'just', action => 'slash', format => 'txt'}],
+  'right structure';
+is $m->path_for->{path}, '/slash', 'right path';
+ok !$m->path_for->{websocket}, 'not a websocket';
+is $m->path_for(format => 'html')->{path}, '/slash.html', 'right path';
+
 # Nameless placeholder
 $m = Mojolicious::Routes::Match->new(root => $r);
 $m->match($c => {method => 'GET', path => '/missing/foo/name'});
@@ -848,4 +915,27 @@ is_deeply $m->stack,
   'right structure';
 is $m->path_for->{path}, '/missing/too', 'right path';
 
+# Partial route
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/partial/test'});
+is_deeply $m->stack,
+  [{controller => 'foo', action => 'bar', 'path' => '/test'}],
+  'right structure';
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/partial.test'});
+is_deeply $m->stack,
+  [{controller => 'foo', action => 'bar', 'path' => '.test'}],
+  'right structure';
+
+# Similar routes with placeholders
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'GET', path => '/similar/too'});
+is_deeply $m->stack,
+  [{}, {controller => 'similar', action => 'get', 'something' => 'too'}],
+  'right structure';
+$m = Mojolicious::Routes::Match->new(root => $r);
+$m->match($c => {method => 'POST', path => '/similar/too'});
+is_deeply $m->stack, [{}, {controller => 'similar', action => 'post'}],
+  'right structure';
+
 done_testing();
@@ -1,25 +1,82 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
+use Mojo::Asset::Memory;
 use Mojo::Date;
 use Mojolicious::Lite;
 use Test::Mojo;
 
-get '/hello3.txt' => sub { shift->render_static('hello2.txt') };
+get '/hello3.txt' => sub { shift->reply->static('hello2.txt') };
+
+post '/hello4.txt' => sub {
+  my $c = shift;
+  $c->res->headers->content_type('text/html');
+  $c->reply->static('hello2.txt');
+};
+
+options '/hello.txt' => sub { shift->render(text => 'Options!') };
+
+get '/etag' => sub {
+  my $c = shift;
+  $c->is_fresh(etag => 'abc')
+    ? $c->rendered(304)
+    : $c->render(text => 'I ♥ Mojolicious!');
+};
+
+get '/asset' => sub {
+  my $c   = shift;
+  my $mem = Mojo::Asset::Memory->new->add_chunk('I <3 Assets!');
+  $c->reply->asset($mem);
+};
 
 my $t = Test::Mojo->new;
 
+# Freshness
+my $c = $t->app->build_controller;
+ok !$c->is_fresh, 'content is stale';
+$c->res->headers->etag('"abc"');
+$c->req->headers->if_none_match('"abc"');
+ok $c->is_fresh, 'content is fresh';
+$c = $t->app->build_controller;
+my $date = Mojo::Date->new(23);
+$c->res->headers->last_modified($date);
+$c->req->headers->if_modified_since($date);
+ok $c->is_fresh, 'content is fresh';
+$c = $t->app->build_controller;
+$c->req->headers->if_none_match('"abc"');
+$c->req->headers->if_modified_since($date);
+ok $c->is_fresh(etag => 'abc', last_modified => $date->epoch),
+  'content is fresh';
+is $c->res->headers->etag,          '"abc"', 'right "ETag" value';
+is $c->res->headers->last_modified, "$date", 'right "Last-Modified" value';
+$c = $t->app->build_controller;
+ok !$c->is_fresh(last_modified => $date->epoch), 'content is stale';
+is $c->res->headers->etag,          undef,   'no "ETag" value';
+is $c->res->headers->last_modified, "$date", 'right "Last-Modified" value';
+
 # Static file
 $t->get_ok('/hello.txt')->status_is(200)
   ->header_is(Server => 'Mojolicious (Perl)')
   ->header_is('Accept-Ranges' => 'bytes')->header_is('Content-Length' => 31)
   ->content_is("Hello Mojo from a static file!\n");
 
+# Static file (HEAD)
+$t->head_ok('/hello.txt')->status_is(200)
+  ->header_is(Server => 'Mojolicious (Perl)')
+  ->header_is('Accept-Ranges' => 'bytes')->header_is('Content-Length' => 31)
+  ->content_is('');
+
+# Route for method other than GET and HEAD
+$t->options_ok('/hello.txt')->status_is(200)
+  ->header_is(Server           => 'Mojolicious (Perl)')
+  ->header_is('Content-Length' => 8)->content_is('Options!');
+
+# Unknown method
+$t->put_ok('/hello.txt')->status_is(404)
+  ->header_is(Server => 'Mojolicious (Perl)');
+
 # Partial static file
 $t->get_ok('/hello.txt' => {Range => 'bytes=2-8'})->status_is(206)
   ->header_is(Server          => 'Mojolicious (Perl)')
@@ -57,7 +114,14 @@ $t->get_ok('/hello.txt' => {Range => 'bytes=0-0'})->status_is(206)
   ->header_is('Content-Range' => 'bytes 0-0/31')->content_is('H');
 
 # Partial static file, end outside of range
-$t->get_ok('/hello.txt' => {Range => 'bytes=25-35'})->status_is(206)
+$t->get_ok('/hello.txt' => {Range => 'bytes=25-31'})->status_is(206)
+  ->header_is(Server           => 'Mojolicious (Perl)')
+  ->header_is('Content-Length' => 6)
+  ->header_is('Content-Range'  => 'bytes 25-30/31')
+  ->header_is('Accept-Ranges'  => 'bytes')->content_is("file!\n");
+
+# Partial static file, end way outside of range
+$t->get_ok('/hello.txt' => {Range => 'bytes=25-300'})->status_is(206)
   ->header_is(Server           => 'Mojolicious (Perl)')
   ->header_is('Content-Length' => 6)
   ->header_is('Content-Range'  => 'bytes 25-30/31')
@@ -80,6 +144,33 @@ $t->get_ok('/hello3.txt' => {Range => 'bytes=0-0'})->status_is(206)
   ->header_is('Accept-Ranges' => 'bytes')->header_is('Content-Length' => 1)
   ->header_is('Content-Range' => 'bytes 0-0/1')->content_is('X');
 
+# Render static file with custom content type
+$t->post_ok('/hello4.txt')->status_is(200)
+  ->header_is(Server => 'Mojolicious (Perl)')->content_type_is('text/html')
+  ->header_is('Content-Length' => 1)->content_is('X');
+
+# Fresh content
+$t->get_ok('/etag')->status_is(200)->header_is(Server => 'Mojolicious (Perl)')
+  ->header_is(ETag => '"abc"')->content_is('I ♥ Mojolicious!');
+
+# Stale content
+$t->get_ok('/etag' => {'If-None-Match' => '"abc"'})->status_is(304)
+  ->header_is(Server => 'Mojolicious (Perl)')->header_is(ETag => '"abc"')
+  ->content_is('');
+
+# Fresh asset
+$t->get_ok('/asset')->status_is(200)
+  ->header_is(Server => 'Mojolicious (Perl)')->content_is('I <3 Assets!');
+my $etag = $t->tx->res->headers->etag;
+
+# Stale asset
+$t->get_ok('/asset' => {'If-None-Match' => $etag})->status_is(304)
+  ->header_is(Server => 'Mojolicious (Perl)')->content_is('');
+
+# Partial asset
+$t->get_ok('/asset' => {'Range' => 'bytes=3-5'})->status_is(206)
+  ->header_is(Server => 'Mojolicious (Perl)')->content_is('3 A');
+
 # Empty file
 $t->get_ok('/hello4.txt')->status_is(200)
   ->header_is(Server           => 'Mojolicious (Perl)')
@@ -90,6 +181,9 @@ $t->get_ok('/hello4.txt' => {Range => 'bytes=0-0'})->status_is(416)
   ->header_is(Server          => 'Mojolicious (Perl)')
   ->header_is('Accept-Ranges' => 'bytes')->content_is('');
 
+# Hidden inline file
+$t->get_ok('/hidden')->status_is(404)->content_unlike(qr/Unreachable file/);
+
 # Base64 static inline file, If-Modified-Since
 my $modified = Mojo::Date->new->epoch(time - 3600);
 $t->get_ok('/static.txt' => {'If-Modified-Since' => $modified})
@@ -128,5 +222,8 @@ $t->get_ok('/static.txt' => {Range => 'bytes=45-50'})->status_is(416)
 done_testing();
 
 __DATA__
+@@ hidden
+Unreachable file.
+
 @@ static.txt (base64)
 dGVzdCAxMjMKbGFsYWxh
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojolicious::Lite;
@@ -71,7 +68,8 @@ EOF
 
 # Shortcut
 $t->get_ok('/small_tags')->status_is(200)->content_is(<<EOF);
-<div>test &amp; 123</div>
+<div id="&amp;lt;">test &amp; 123</div>
+<div id="&lt;">test&nbsp;321</div>
 <div>
   <p id="0">just</p>
   <p>0</p>
@@ -497,7 +495,8 @@ __DATA__
 %= tag 'bar', class => 'test', ''
 
 @@ small_tags.html.ep
-%=t div => 'test & 123'
+%=t div => (id => '&lt;') => 'test & 123'
+%=t div => (id => b('&lt;')) => b('test&nbsp;321')
 %=t div => begin
   %=t p => (id => 0) => 'just'
   %=t p => 0
@@ -2,7 +2,6 @@ use Mojo::Base -strict;
 
 BEGIN {
   $ENV{MOJO_MODE}    = 'testing';
-  $ENV{MOJO_NO_IPV6} = 1;
   $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
 }
 
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::IOLoop::Server;
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojolicious::Lite;
@@ -87,13 +84,13 @@ $t->get_ok('/advanced')->status_is(200)->header_is('X-Append' => 'bar')
   ->content_is("&LT;escape me>\n123423");
 
 # Normal "pod" template
-$t->get_ok('/docs')->status_is(200)->content_like(qr!<h3>snowman</h3>!);
+$t->get_ok('/docs')->status_is(200)->content_like(qr!<h3.*>snowman</h3>!);
 
 # Template in "teapod" format
-$t->get_ok('/docs2')->status_is(200)->content_like(qr!<h2>snowman</h2>!);
+$t->get_ok('/docs2')->status_is(200)->content_like(qr!<h2.*>snowman</h2>!);
 
 # Empty stash value
-$t->get_ok('/docs3')->status_is(200)->content_like(qr!<h3></h3>!);
+$t->get_ok('/docs3')->status_is(200)->content_like(qr!<h3.*></h3>!);
 
 # REST request for "foo" format
 $t->get_ok('/rest')->status_is(200)->header_is('X-Rest' => 1)
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::Asset::File;
@@ -26,7 +23,7 @@ post '/upload' => sub {
 
 post '/multi' => sub {
   my $c = shift;
-  my @uploads = map { $c->param($_) } $c->param('name');
+  my @uploads = map { @{$c->every_param($_)} } @{$c->every_param('name')};
   $c->render(text => join '', map { $_->filename, $_->asset->slurp } @uploads);
 };
 
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojolicious::Lite;
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojolicious::Lite;
@@ -36,9 +33,12 @@ my $t = Test::Mojo->new;
 # Required and optional values
 my $validation = $t->app->validation->input({foo => 'bar', baz => 'yada'});
 is_deeply [$validation->error], [], 'no names';
+is $validation->param('foo'), undef, 'no value';
+is_deeply $validation->every_param('foo'), [], 'no values';
 ok $validation->required('foo')->is_valid, 'valid';
 is_deeply $validation->output, {foo => 'bar'}, 'right result';
 is $validation->param('foo'), 'bar', 'right value';
+is_deeply $validation->every_param('foo'), ['bar'], 'right values';
 is_deeply [$validation->param], ['foo'], 'right names';
 ok !$validation->has_error, 'no error';
 ok $validation->optional('baz')->is_valid, 'valid';
@@ -77,6 +77,8 @@ is_deeply [$validation->error], [qw(baz yada)], 'right names';
 $validation = $t->app->validation->input(
   {foo => [qw(bar whatever)], baz => [qw(yada ohoh)]});
 ok $validation->required('foo')->in(qw(23 bar whatever))->is_valid, 'valid';
+is_deeply $validation->every_param('foo'), [qw(bar whatever)], 'right results';
+is $validation->param('foo'), 'whatever', 'right result';
 is_deeply $validation->output, {foo => [qw(bar whatever)]}, 'right result';
 ok !$validation->has_error, 'no error';
 ok !$validation->required('baz')->in(qw(yada whatever))->is_valid, 'not valid';
@@ -1,9 +1,6 @@
 use Mojo::Base -strict;
 
-BEGIN {
-  $ENV{MOJO_NO_IPV6} = 1;
-  $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
-}
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
 
 use Test::More;
 use Mojo::ByteStream 'b';
@@ -132,12 +129,17 @@ $t->websocket_ok('/echo')->send_ok({binary => 'bytes!'})
   ->send_ok({binary => 'bytes!'})
   ->message_ok->message_isnt({text => 'bytes!'})->finish_ok;
 
+# Bytes in multiple frames
+$t->websocket_ok('/echo')->send_ok([0, 0, 0, 0, 2, 'a'])
+  ->send_ok([0, 0, 0, 0, 0, 'b'])->send_ok([1, 0, 0, 0, 0, 'c'])
+  ->message_ok->message_is({binary => 'abc'})->finish_ok;
+
 # Zero
 $t->websocket_ok('/echo')->send_ok(0)->message_ok->message_is('echo: 0')
   ->send_ok(0)->message_ok->message_like({text => qr/0/})->finish_ok(1000)
   ->finished_ok(1000);
 
-# 64bit binary message (extended limit)
+# 64-bit binary message (extended limit)
 $t->request_ok($t->ua->build_websocket_tx('/echo'));
 is $t->tx->max_websocket_size, 262144, 'right size';
 $t->tx->max_websocket_size(262145);
@@ -145,11 +147,11 @@ $t->send_ok({binary => 'a' x 262145})
   ->message_ok->message_is({binary => 'a' x 262145})
   ->finish_ok->finished_ok(1005);
 
-# 64bit binary message (too large)
+# 64-bit binary message (too large)
 $t->websocket_ok('/echo')->send_ok({binary => 'b' x 262145})
   ->finished_ok(1009);
 
-# Binary message in two 64bit frames without FIN bit (too large)
+# Binary message in two 64-bit frames without FIN bit (too large)
 $t->websocket_ok('/echo')->send_ok([0, 0, 0, 0, 2, 'c' x 100000])
   ->send_ok([0, 0, 0, 0, 0, 'c' x 162146])->finished_ok(1009);
 
@@ -184,7 +186,7 @@ $t->message_ok->message_is({binary => 'a' x 50000});
 ok length $payload < 262145, 'message has been compressed';
 $t->finish_ok->finished_ok(1005);
 
-# Compressed message exceeding the limit when uncompressed
+# Compressed message exceeding the limit when decompressed
 $t->websocket_ok(
   '/echo' => {'Sec-WebSocket-Extensions' => 'permessage-deflate'})
   ->header_is('Sec-WebSocket-Extensions' => 'permessage-deflate')
@@ -7,5 +7,10 @@ plan skip_all => 'set TEST_POD to enable this test (developer only!)'
 plan skip_all => 'Test::Pod::Coverage 1.04 required for this test!'
   unless eval 'use Test::Pod::Coverage 1.04; 1';
 
-# False positive constants
-all_pod_coverage_ok({also_private => [qw(IPV6 TLS)]});
+# DEPRECATED in Tiger Face!
+my @tiger = (
+  qw(decode emit_safe encode error has_conditions new pluck render_static),
+  qw(val)
+);
+
+all_pod_coverage_ok({also_private => [@tiger]});