... | ... |
@@ -0,0 +1,2 @@ |
1 |
+mojo-legacy 4.57 |
|
2 |
+https://github.com/jamadam/mojo-legacy |
... | ... |
@@ -15,10 +15,10 @@ has log => sub { Mojo::Log->new }; |
15 | 15 |
has ua => sub { |
16 | 16 |
my $self = shift; |
17 | 17 |
|
18 |
- my $ua = Mojo::UserAgent->new->app($self); |
|
18 |
+ my $ua = Mojo::UserAgent->new; |
|
19 |
+ weaken $ua->server->app($self)->{app}; |
|
19 | 20 |
weaken $self; |
20 | 21 |
$ua->on(error => sub { $self->log->error($_[1]) }); |
21 |
- weaken $ua->{app}; |
|
22 | 22 |
|
23 | 23 |
return $ua; |
24 | 24 |
}; |
... | ... |
@@ -27,7 +27,8 @@ sub new { |
27 | 27 |
my $self = shift->SUPER::new(@_); |
28 | 28 |
|
29 | 29 |
# Check if we have a log directory |
30 |
- my $home = $self->home->detect(ref $self); |
|
30 |
+ my $home = $self->home; |
|
31 |
+ $home->detect(ref $self) unless @{$home->parts}; |
|
31 | 32 |
$self->log->path($home->rel_file('log/mojo.log')) |
32 | 33 |
if -w $home->rel_file('log'); |
33 | 34 |
|
... | ... |
@@ -58,6 +59,8 @@ sub _dict { |
58 | 59 |
|
59 | 60 |
1; |
60 | 61 |
|
62 |
+=encoding utf8 |
|
63 |
+ |
|
61 | 64 |
=head1 NAME |
62 | 65 |
|
63 | 66 |
Mojo - Duct tape for the HTML5 web! |
... | ... |
@@ -92,7 +95,7 @@ frameworks. It provides all the basic tools and helpers needed to write |
92 | 95 |
simple web applications and higher level web frameworks, such as |
93 | 96 |
L<Mojolicious>. |
94 | 97 |
|
95 |
-See L<Mojolicious> for more! |
|
98 |
+See L<Mojolicious::Guides> for more! |
|
96 | 99 |
|
97 | 100 |
=head1 ATTRIBUTES |
98 | 101 |
|
... | ... |
@@ -130,7 +133,7 @@ plugins, since non-blocking requests that are already in progress will |
130 | 133 |
interfere with new blocking ones. |
131 | 134 |
|
132 | 135 |
# Perform blocking request |
133 |
- my $body = $app->ua->get('example.com')->res->body; |
|
136 |
+ say $app->ua->get('example.com')->res->body; |
|
134 | 137 |
|
135 | 138 |
=head1 METHODS |
136 | 139 |
|
... | ... |
@@ -142,7 +145,8 @@ new ones. |
142 | 145 |
my $app = Mojo->new; |
143 | 146 |
|
144 | 147 |
Construct a new L<Mojo> application. Will automatically detect your home |
145 |
-directory and set up logging to C<log/mojo.log> if there's a C<log> directory. |
|
148 |
+directory if necessary and set up logging to C<log/mojo.log> if there's a |
|
149 |
+C<log> directory. |
|
146 | 150 |
|
147 | 151 |
=head2 build_tx |
148 | 152 |
|
... | ... |
@@ -20,6 +20,8 @@ sub slurp { croak 'Method "slurp" not implemented by subclass' } |
20 | 20 |
|
21 | 21 |
1; |
22 | 22 |
|
23 |
+=encoding utf8 |
|
24 |
+ |
|
23 | 25 |
=head1 NAME |
24 | 26 |
|
25 | 27 |
Mojo::Asset - HTTP content storage base class |
... | ... |
@@ -58,7 +60,7 @@ Pretend file ends earlier. |
58 | 60 |
=head2 start_range |
59 | 61 |
|
60 | 62 |
my $start = $asset->start_range; |
61 |
- $asset = $asset->start_range(0); |
|
63 |
+ $asset = $asset->start_range(3); |
|
62 | 64 |
|
63 | 65 |
Pretend file starts later. |
64 | 66 |
|
... | ... |
@@ -96,9 +98,9 @@ False. |
96 | 98 |
|
97 | 99 |
=head2 is_range |
98 | 100 |
|
99 |
- my $success = $asset->is_range; |
|
101 |
+ my $bool = $asset->is_range; |
|
100 | 102 |
|
101 |
-Check if asset has a C<start_range> or C<end_range>. |
|
103 |
+Check if asset has a L</"start_range"> or L</"end_range">. |
|
102 | 104 |
|
103 | 105 |
=head2 move_to |
104 | 106 |
|
... | ... |
@@ -26,7 +26,7 @@ has handle => sub { |
26 | 26 |
my $name = defined $path ? $path : $base; |
27 | 27 |
until ($handle->open($name, O_CREAT | O_EXCL | O_RDWR)) { |
28 | 28 |
croak qq{Can't open file "$name": $!} if defined $path || $! != $!{EEXIST}; |
29 |
- $name = "$base." . md5_sum(time . $$ . rand 9999999); |
|
29 |
+ $name = "$base." . md5_sum(time . $$ . rand 9 x 7); |
|
30 | 30 |
} |
31 | 31 |
$self->path($name); |
32 | 32 |
|
... | ... |
@@ -140,6 +140,8 @@ sub slurp { |
140 | 140 |
|
141 | 141 |
1; |
142 | 142 |
|
143 |
+=encoding utf8 |
|
144 |
+ |
|
143 | 145 |
=head1 NAME |
144 | 146 |
|
145 | 147 |
Mojo::Asset::File - File storage for HTTP content |
... | ... |
@@ -174,8 +176,8 @@ implements the following new ones. |
174 | 176 |
|
175 | 177 |
=head2 cleanup |
176 | 178 |
|
177 |
- my $cleanup = $file->cleanup; |
|
178 |
- $file = $file->cleanup(1); |
|
179 |
+ my $bool = $file->cleanup; |
|
180 |
+ $file = $file->cleanup($bool); |
|
179 | 181 |
|
180 | 182 |
Delete file automatically once it's not used anymore. |
181 | 183 |
|
... | ... |
@@ -184,14 +186,14 @@ Delete file automatically once it's not used anymore. |
184 | 186 |
my $handle = $file->handle; |
185 | 187 |
$file = $file->handle(IO::File->new); |
186 | 188 |
|
187 |
-File handle, created on demand. |
|
189 |
+Filehandle, created on demand. |
|
188 | 190 |
|
189 | 191 |
=head2 path |
190 | 192 |
|
191 | 193 |
my $path = $file->path; |
192 | 194 |
$file = $file->path('/home/sri/foo.txt'); |
193 | 195 |
|
194 |
-File path used to create C<handle>, can also be automatically generated if |
|
196 |
+File path used to create L</"handle">, can also be automatically generated if |
|
195 | 197 |
necessary. |
196 | 198 |
|
197 | 199 |
=head2 tmpdir |
... | ... |
@@ -199,7 +201,7 @@ necessary. |
199 | 201 |
my $tmpdir = $file->tmpdir; |
200 | 202 |
$file = $file->tmpdir('/tmp'); |
201 | 203 |
|
202 |
-Temporary directory used to generate C<path>, defaults to the value of the |
|
204 |
+Temporary directory used to generate L</"path">, defaults to the value of the |
|
203 | 205 |
MOJO_TMPDIR environment variable or auto detection. |
204 | 206 |
|
205 | 207 |
=head1 METHODS |
... | ... |
@@ -237,7 +239,7 @@ True. |
237 | 239 |
|
238 | 240 |
$file = $file->move_to('/home/sri/bar.txt'); |
239 | 241 |
|
240 |
-Move asset data into a specific file and disable C<cleanup>. |
|
242 |
+Move asset data into a specific file and disable L</"cleanup">. |
|
241 | 243 |
|
242 | 244 |
=head2 size |
243 | 245 |
|
... | ... |
@@ -55,6 +55,8 @@ sub slurp { shift->{content} } |
55 | 55 |
|
56 | 56 |
1; |
57 | 57 |
|
58 |
+=encoding utf8 |
|
59 |
+ |
|
58 | 60 |
=head1 NAME |
59 | 61 |
|
60 | 62 |
Mojo::Asset::Memory - In-memory storage for HTTP content |
... | ... |
@@ -97,10 +99,10 @@ implements the following new ones. |
97 | 99 |
|
98 | 100 |
=head2 auto_upgrade |
99 | 101 |
|
100 |
- my $upgrade = $mem->auto_upgrade; |
|
101 |
- $mem = $mem->auto_upgrade(1); |
|
102 |
+ my $bool = $mem->auto_upgrade; |
|
103 |
+ $mem = $mem->auto_upgrade($bool); |
|
102 | 104 |
|
103 |
-Try to detect if content size exceeds C<max_memory_size> limit and |
|
105 |
+Try to detect if content size exceeds L</"max_memory_size"> limit and |
|
104 | 106 |
automatically upgrade to a L<Mojo::Asset::File> object. |
105 | 107 |
|
106 | 108 |
=head2 max_memory_size |
... | ... |
@@ -14,7 +14,6 @@ use IO::Handle (); |
14 | 14 |
sub import { |
15 | 15 |
my $class = shift; |
16 | 16 |
return unless my $flag = shift; |
17 |
- no strict 'refs'; |
|
18 | 17 |
|
19 | 18 |
# Base |
20 | 19 |
if ($flag eq '-base') { $flag = $class } |
... | ... |
@@ -23,21 +22,24 @@ sub import { |
23 | 22 |
elsif ($flag eq '-strict') { $flag = undef } |
24 | 23 |
|
25 | 24 |
# Module |
26 |
- else { |
|
27 |
- my $file = $flag; |
|
28 |
- $file =~ s/::|'/\//g; |
|
29 |
- require "$file.pm" unless $flag->can('new'); |
|
25 |
+ elsif ((my $file = $flag) && !$flag->can('new')) { |
|
26 |
+ $file =~ s!::|'!/!g; |
|
27 |
+ require "$file.pm"; |
|
30 | 28 |
} |
31 | 29 |
|
32 | 30 |
# ISA |
33 | 31 |
if ($flag) { |
34 | 32 |
my $caller = caller; |
33 |
+ no strict 'refs'; |
|
35 | 34 |
push @{"${caller}::ISA"}, $flag; |
36 | 35 |
*{"${caller}::has"} = sub { attr($caller, @_) }; |
37 | 36 |
} |
38 | 37 |
|
39 | 38 |
my $caller = caller; |
40 |
- *{"${caller}::say"} = sub { say(@_) }; |
|
39 |
+ { |
|
40 |
+ no strict 'refs'; |
|
41 |
+ *{"${caller}::say"} = sub { say(@_) }; |
|
42 |
+ } |
|
41 | 43 |
|
42 | 44 |
# Mojo modules are strict! |
43 | 45 |
strict->import; |
... | ... |
@@ -51,9 +53,6 @@ sub new { |
51 | 53 |
bless @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}, ref $class || $class; |
52 | 54 |
} |
53 | 55 |
|
54 |
-# Performance is very important for something as often used as accessors, |
|
55 |
-# so we optimize them by compiling our own code, don't be scared, we have |
|
56 |
-# tests for every single case |
|
57 | 56 |
sub attr { |
58 | 57 |
my ($class, $attrs, $default) = @_; |
59 | 58 |
return unless ($class = ref $class || $class) && $attrs; |
... | ... |
@@ -61,7 +60,6 @@ sub attr { |
61 | 60 |
Carp::croak 'Default has to be a code reference or constant value' |
62 | 61 |
if ref $default && ref $default ne 'CODE'; |
63 | 62 |
|
64 |
- # Compile attributes |
|
65 | 63 |
for my $attr (@{ref $attrs eq 'ARRAY' ? $attrs : [$attrs]}) { |
66 | 64 |
Carp::croak qq{Attribute "$attr" invalid} unless $attr =~ /^[a-zA-Z_]\w*$/; |
67 | 65 |
|
... | ... |
@@ -89,8 +87,6 @@ sub attr { |
89 | 87 |
# Footer (return invocant) |
90 | 88 |
$code .= " \$_[0];\n}"; |
91 | 89 |
|
92 |
- # We compile custom attribute code for speed |
|
93 |
- no strict 'refs'; |
|
94 | 90 |
warn "-- Attribute $attr in $class\n$code\n\n" if $ENV{MOJO_BASE_DEBUG}; |
95 | 91 |
Carp::croak "Mojo::Base error: $@" unless eval "$code;1"; |
96 | 92 |
} |
... | ... |
@@ -104,6 +100,8 @@ sub tap { |
104 | 100 |
|
105 | 101 |
1; |
106 | 102 |
|
103 |
+=encoding utf8 |
|
104 |
+ |
|
107 | 105 |
=head1 NAME |
108 | 106 |
|
109 | 107 |
Mojo::Base - Minimal base class for Mojo projects |
... | ... |
@@ -185,7 +183,7 @@ flag or a base class. |
185 | 183 |
has [qw(name1 name2 name3)] => 'foo'; |
186 | 184 |
has [qw(name1 name2 name3)] => sub {...}; |
187 | 185 |
|
188 |
-Create attributes for hash-based objects, just like the C<attr> method. |
|
186 |
+Create attributes for hash-based objects, just like the L</"attr"> method. |
|
189 | 187 |
|
190 | 188 |
=head1 METHODS |
191 | 189 |
|
... | ... |
@@ -222,8 +220,8 @@ argument. |
222 | 220 |
$object = $object->tap(sub {...}); |
223 | 221 |
|
224 | 222 |
K combinator, tap into a method chain to perform operations on an object |
225 |
-within the chain. The object will be the first argument passed to the closure |
|
226 |
-and is also available via C<$_>. |
|
223 |
+within the chain. The object will be the first argument passed to the callback |
|
224 |
+and is also available as C<$_>. |
|
227 | 225 |
|
228 | 226 |
=head2 C<say> |
229 | 227 |
|
... | ... |
@@ -1,5 +1,5 @@ |
1 | 1 |
package Mojo::ByteStream; |
2 |
-use Mojo::Base -base; |
|
2 |
+use Mojo::Base -strict; |
|
3 | 3 |
use overload '""' => sub { shift->to_string }, fallback => 1; |
4 | 4 |
|
5 | 5 |
use Exporter 'import'; |
... | ... |
@@ -63,10 +63,14 @@ sub split { |
63 | 63 |
return Mojo::Collection->new(map { $self->new($_) } split $pattern, $$self); |
64 | 64 |
} |
65 | 65 |
|
66 |
+sub tap { shift->Mojo::Base::tap(@_) } |
|
67 |
+ |
|
66 | 68 |
sub to_string { ${$_[0]} } |
67 | 69 |
|
68 | 70 |
1; |
69 | 71 |
|
72 |
+=encoding utf8 |
|
73 |
+ |
|
70 | 74 |
=head1 NAME |
71 | 75 |
|
72 | 76 |
Mojo::ByteStream - ByteStream |
... | ... |
@@ -104,8 +108,7 @@ Construct a new scalar-based L<Mojo::ByteStream> object. |
104 | 108 |
|
105 | 109 |
=head1 METHODS |
106 | 110 |
|
107 |
-L<Mojo::ByteStream> inherits all methods from L<Mojo::Base> and implements the |
|
108 |
-following new ones. |
|
111 |
+L<Mojo::ByteStream> implements the following methods. |
|
109 | 112 |
|
110 | 113 |
=head2 new |
111 | 114 |
|
... | ... |
@@ -219,7 +222,7 @@ Print bytestream to handle and append a newline, defaults to C<STDOUT>. |
219 | 222 |
|
220 | 223 |
=head2 secure_compare |
221 | 224 |
|
222 |
- my $success = $stream->secure_compare($str); |
|
225 |
+ my $bool = $stream->secure_compare($str); |
|
223 | 226 |
|
224 | 227 |
Compare bytestream with L<Mojo::Util/"secure_compare">. |
225 | 228 |
|
... | ... |
@@ -263,9 +266,10 @@ Write all data from bytestream at once to file with L<Mojo::Util/"spurt">. |
263 | 266 |
|
264 | 267 |
my $collection = $stream->split(','); |
265 | 268 |
|
266 |
-Turn bytestream into L<Mojo::Collection>. |
|
269 |
+Turn bytestream into L<Mojo::Collection> object containing L<Mojo::ByteStream> |
|
270 |
+objects. |
|
267 | 271 |
|
268 |
- b('a,b,c')->split(',')->pluck('quote')->join(',')->say; |
|
272 |
+ b('a,b,c')->split(',')->quote->join(',')->say; |
|
269 | 273 |
|
270 | 274 |
=head2 squish |
271 | 275 |
|
... | ... |
@@ -275,6 +279,12 @@ Trim whitespace characters from both ends of bytestream and then change all |
275 | 279 |
consecutive groups of whitespace into one space each with |
276 | 280 |
L<Mojo::Util/"squish">. |
277 | 281 |
|
282 |
+=head2 tap |
|
283 |
+ |
|
284 |
+ $stream = $stream->tap(sub {...}); |
|
285 |
+ |
|
286 |
+Alias for L<Mojo::Base/"tap">. |
|
287 |
+ |
|
278 | 288 |
=head2 to_string |
279 | 289 |
|
280 | 290 |
my $str = $stream->to_string; |
... | ... |
@@ -327,6 +337,12 @@ bytestream with L<Mojo::Util/"xml_escape">. |
327 | 337 |
|
328 | 338 |
XOR encode bytestream with L<Mojo::Util/"xor_encode">. |
329 | 339 |
|
340 |
+=head1 BYTESTREAM |
|
341 |
+ |
|
342 |
+Direct scalar reference access to the bytestream is also possible. |
|
343 |
+ |
|
344 |
+ $$stream .= 'foo'; |
|
345 |
+ |
|
330 | 346 |
=head1 SEE ALSO |
331 | 347 |
|
332 | 348 |
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
... | ... |
@@ -19,6 +19,8 @@ sub set { |
19 | 19 |
|
20 | 20 |
1; |
21 | 21 |
|
22 |
+=encoding utf8 |
|
23 |
+ |
|
22 | 24 |
=head1 NAME |
23 | 25 |
|
24 | 26 |
Mojo::Cache - Naive in-memory cache |
... | ... |
@@ -1,16 +1,29 @@ |
1 | 1 |
package Mojo::Collection; |
2 |
-use Mojo::Base -base; |
|
3 |
-use overload |
|
4 |
- 'bool' => sub {1}, |
|
5 |
- '""' => sub { shift->join("\n") }, |
|
6 |
- fallback => 1; |
|
2 |
+use Mojo::Base -strict; |
|
3 |
+use overload bool => sub {1}, '""' => sub { shift->join("\n") }, fallback => 1; |
|
7 | 4 |
|
5 |
+use Carp 'croak'; |
|
8 | 6 |
use Exporter 'import'; |
9 | 7 |
use List::Util; |
10 | 8 |
use Mojo::ByteStream; |
9 |
+use Scalar::Util 'blessed'; |
|
11 | 10 |
|
12 | 11 |
our @EXPORT_OK = ('c'); |
13 | 12 |
|
13 |
+sub AUTOLOAD { |
|
14 |
+ my $self = shift; |
|
15 |
+ |
|
16 |
+ my ($package, $method) = our $AUTOLOAD =~ /^([\w:]+)::(\w+)$/; |
|
17 |
+ croak "Undefined subroutine &${package}::$method called" |
|
18 |
+ unless blessed $self && $self->isa(__PACKAGE__); |
|
19 |
+ |
|
20 |
+ croak qq{Can't locate object method "$method" via package "$package"} |
|
21 |
+ unless @$self; |
|
22 |
+ return $self->pluck($method, @_); |
|
23 |
+} |
|
24 |
+ |
|
25 |
+sub DESTROY { } |
|
26 |
+ |
|
14 | 27 |
sub new { |
15 | 28 |
my $class = shift; |
16 | 29 |
return bless [@_], ref $class || $class; |
... | ... |
@@ -18,6 +31,10 @@ sub new { |
18 | 31 |
|
19 | 32 |
sub c { __PACKAGE__->new(@_) } |
20 | 33 |
|
34 |
+sub compact { |
|
35 |
+ shift->grep(sub { length(defined $_ ? $_ : '') }); |
|
36 |
+} |
|
37 |
+ |
|
21 | 38 |
sub each { |
22 | 39 |
my ($self, $cb) = @_; |
23 | 40 |
return @$self unless $cb; |
... | ... |
@@ -33,16 +50,15 @@ sub first { |
33 | 50 |
return List::Util::first { $_ =~ $cb } @$self; |
34 | 51 |
} |
35 | 52 |
|
53 |
+sub flatten { $_[0]->new(_flatten(@{$_[0]})) } |
|
54 |
+ |
|
36 | 55 |
sub grep { |
37 | 56 |
my ($self, $cb) = @_; |
38 | 57 |
return $self->new(grep { $cb->($_) } @$self) if ref $cb eq 'CODE'; |
39 | 58 |
return $self->new(grep { $_ =~ $cb } @$self); |
40 | 59 |
} |
41 | 60 |
|
42 |
-sub join { |
|
43 |
- my ($self, $expr) = @_; |
|
44 |
- return Mojo::ByteStream->new(join $expr, map({"$_"} @$self)); |
|
45 |
-} |
|
61 |
+sub join { Mojo::ByteStream->new(join $_[1], map({"$_"} @{$_[0]})) } |
|
46 | 62 |
|
47 | 63 |
sub map { |
48 | 64 |
my ($self, $cb) = @_; |
... | ... |
@@ -54,15 +70,9 @@ sub pluck { |
54 | 70 |
return $self->map(sub { $_->$method(@args) }); |
55 | 71 |
} |
56 | 72 |
|
57 |
-sub reverse { |
|
58 |
- my $self = shift; |
|
59 |
- return $self->new(reverse @$self); |
|
60 |
-} |
|
73 |
+sub reverse { $_[0]->new(reverse @{$_[0]}) } |
|
61 | 74 |
|
62 |
-sub shuffle { |
|
63 |
- my $self = shift; |
|
64 |
- return $self->new(List::Util::shuffle @$self); |
|
65 |
-} |
|
75 |
+sub shuffle { $_[0]->new(List::Util::shuffle @{$_[0]}) } |
|
66 | 76 |
|
67 | 77 |
sub size { scalar @{$_[0]} } |
68 | 78 |
|
... | ... |
@@ -76,14 +86,24 @@ sub sort { |
76 | 86 |
return $self->new($cb ? sort { $a->$cb($b) } @$self : sort @$self); |
77 | 87 |
} |
78 | 88 |
|
89 |
+sub tap { shift->Mojo::Base::tap(@_) } |
|
90 |
+ |
|
79 | 91 |
sub uniq { |
80 | 92 |
my $self = shift; |
81 | 93 |
my %seen; |
82 | 94 |
return $self->grep(sub { !$seen{$_}++ }); |
83 | 95 |
} |
84 | 96 |
|
97 |
+sub _flatten { |
|
98 |
+ map { _ref($_) ? _flatten(@$_) : $_ } @_; |
|
99 |
+} |
|
100 |
+ |
|
101 |
+sub _ref { ref $_[0] && (ref $_[0] eq 'ARRAY' || $_[0]->isa(__PACKAGE__)) } |
|
102 |
+ |
|
85 | 103 |
1; |
86 | 104 |
|
105 |
+=encoding utf8 |
|
106 |
+ |
|
87 | 107 |
=head1 NAME |
88 | 108 |
|
89 | 109 |
Mojo::Collection - Collection |
... | ... |
@@ -119,8 +139,7 @@ Construct a new array-based L<Mojo::Collection> object. |
119 | 139 |
|
120 | 140 |
=head1 METHODS |
121 | 141 |
|
122 |
-L<Mojo::Collection> inherits all methods from L<Mojo::Base> and implements the |
|
123 |
-following new ones. |
|
142 |
+L<Mojo::Collection> implements the following methods. |
|
124 | 143 |
|
125 | 144 |
=head2 new |
126 | 145 |
|
... | ... |
@@ -128,12 +147,21 @@ following new ones. |
128 | 147 |
|
129 | 148 |
Construct a new array-based L<Mojo::Collection> object. |
130 | 149 |
|
150 |
+=head2 compact |
|
151 |
+ |
|
152 |
+ my $new = $collection->compact; |
|
153 |
+ |
|
154 |
+Create a new collection with all elements that are defined and not an empty |
|
155 |
+string. |
|
156 |
+ |
|
131 | 157 |
=head2 each |
132 | 158 |
|
133 | 159 |
my @elements = $collection->each; |
134 | 160 |
$collection = $collection->each(sub {...}); |
135 | 161 |
|
136 |
-Evaluate callback for each element in collection. |
|
162 |
+Evaluate callback for each element in collection or return all elements as a |
|
163 |
+list if none has been provided. The element will be the first argument passed |
|
164 |
+to the callback and is also available as C<$_>. |
|
137 | 165 |
|
138 | 166 |
$collection->each(sub { |
139 | 167 |
my ($e, $count) = @_; |
... | ... |
@@ -148,10 +176,18 @@ Evaluate callback for each element in collection. |
148 | 176 |
|
149 | 177 |
Evaluate regular expression or callback for each element in collection and |
150 | 178 |
return the first one that matched the regular expression, or for which the |
151 |
-callback returned true. |
|
179 |
+callback returned true. The element will be the first argument passed to the |
|
180 |
+callback and is also available as C<$_>. |
|
152 | 181 |
|
153 | 182 |
my $five = $collection->first(sub { $_ == 5 }); |
154 | 183 |
|
184 |
+=head2 flatten |
|
185 |
+ |
|
186 |
+ my $new = $collection->flatten; |
|
187 |
+ |
|
188 |
+Flatten nested collections/arrays recursively and create a new collection with |
|
189 |
+all elements. |
|
190 |
+ |
|
155 | 191 |
=head2 grep |
156 | 192 |
|
157 | 193 |
my $new = $collection->grep(qr/foo/); |
... | ... |
@@ -159,7 +195,8 @@ callback returned true. |
159 | 195 |
|
160 | 196 |
Evaluate regular expression or callback for each element in collection and |
161 | 197 |
create a new collection with all elements that matched the regular expression, |
162 |
-or for which the callback returned true. |
|
198 |
+or for which the callback returned true. The element will be the first |
|
199 |
+argument passed to the callback and is also available as C<$_>. |
|
163 | 200 |
|
164 | 201 |
my $interesting = $collection->grep(qr/mojo/i); |
165 | 202 |
|
... | ... |
@@ -176,7 +213,8 @@ Turn collection into L<Mojo::ByteStream>. |
176 | 213 |
my $new = $collection->map(sub {...}); |
177 | 214 |
|
178 | 215 |
Evaluate callback for each element in collection and create a new collection |
179 |
-from the results. |
|
216 |
+from the results. The element will be the first argument passed to the |
|
217 |
+callback and is also available as C<$_>. |
|
180 | 218 |
|
181 | 219 |
my $doubled = $collection->map(sub { $_ * 2 }); |
182 | 220 |
|
... | ... |
@@ -225,12 +263,27 @@ from the results. |
225 | 263 |
|
226 | 264 |
my $insensitive = $collection->sort(sub { uc(shift) cmp uc(shift) }); |
227 | 265 |
|
266 |
+=head2 tap |
|
267 |
+ |
|
268 |
+ $collection = $collection->tap(sub {...}); |
|
269 |
+ |
|
270 |
+Alias for L<Mojo::Base/"tap">. |
|
271 |
+ |
|
228 | 272 |
=head2 uniq |
229 | 273 |
|
230 | 274 |
my $new = $collection->uniq; |
231 | 275 |
|
232 | 276 |
Create a new collection without duplicate elements. |
233 | 277 |
|
278 |
+=head1 ELEMENT METHODS |
|
279 |
+ |
|
280 |
+In addition to the methods above, you can also call methods provided by all |
|
281 |
+elements in the collection directly and create a new collection from the |
|
282 |
+results, similar to L</"pluck">. |
|
283 |
+ |
|
284 |
+ push @$collection, Mojo::DOM->new("<div><h1>$_</h1></div>") for 1 .. 9; |
|
285 |
+ say $collection->at('h1')->type('h2')->prepend_content('Test ')->root; |
|
286 |
+ |
|
234 | 287 |
=head1 ELEMENTS |
235 | 288 |
|
236 | 289 |
Direct array reference access to elements is also possible. |
... | ... |
@@ -18,7 +18,7 @@ sub body_size { croak 'Method "body_size" not implemented by subclass' } |
18 | 18 |
|
19 | 19 |
sub boundary { |
20 | 20 |
return undef unless my $type = shift->headers->content_type; |
21 |
- $type =~ m!multipart.*boundary=(?:"([^"]+)"|([\w'(),.:?\-+/]+))!i |
|
21 |
+ $type =~ m!multipart.*boundary\s*=\s*(?:"([^"]+)"|([\w'(),.:?\-+/]+))!i |
|
22 | 22 |
and return defined $1 ? $1 : $2; |
23 | 23 |
return undef; |
24 | 24 |
} |
... | ... |
@@ -27,8 +27,8 @@ sub build_body { shift->_build('get_body_chunk') } |
27 | 27 |
sub build_headers { shift->_build('get_header_chunk') } |
28 | 28 |
|
29 | 29 |
sub charset { |
30 |
- my $type = do {my $tmp = shift->headers->content_type; defined $tmp ? $tmp : ''}; |
|
31 |
- return $type =~ /charset="?([^"\s;]+)"?/i ? $1 : undef; |
|
30 |
+ my $type = do { my $type = shift->headers->content_type; defined $type ? $type : ''}; |
|
31 |
+ return $type =~ /charset\s*=\s*"?([^"\s;]+)"?/i ? $1 : undef; |
|
32 | 32 |
} |
33 | 33 |
|
34 | 34 |
sub clone { |
... | ... |
@@ -220,7 +220,7 @@ sub _parse_chunked { |
220 | 220 |
# Start new chunk (ignore the chunk extension) |
221 | 221 |
unless ($self->{chunk_len}) { |
222 | 222 |
last |
223 |
- unless $self->{pre_buffer} =~ s/^(?:\x0d?\x0a)?([[:xdigit:]]+).*\x0a//; |
|
223 |
+ unless $self->{pre_buffer} =~ s/^(?:\x0d?\x0a)?([0-9a-fA-F]+).*\x0a//; |
|
224 | 224 |
next if $self->{chunk_len} = hex $1; |
225 | 225 |
|
226 | 226 |
# Last chunk |
... | ... |
@@ -306,6 +306,8 @@ sub _uncompress { |
306 | 306 |
|
307 | 307 |
1; |
308 | 308 |
|
309 |
+=encoding utf8 |
|
310 |
+ |
|
309 | 311 |
=head1 NAME |
310 | 312 |
|
311 | 313 |
Mojo::Content - HTTP content base class |
... | ... |
@@ -378,8 +380,8 @@ L<Mojo::Content> implements the following attributes. |
378 | 380 |
|
379 | 381 |
=head2 auto_relax |
380 | 382 |
|
381 |
- my $relax = $content->auto_relax; |
|
382 |
- $content = $content->auto_relax(1); |
|
383 |
+ my $bool = $content->auto_relax; |
|
384 |
+ $content = $content->auto_relax($bool); |
|
383 | 385 |
|
384 | 386 |
Try to detect when relaxed parsing is necessary. |
385 | 387 |
|
... | ... |
@@ -408,16 +410,16 @@ value of the MOJO_MAX_LEFTOVER_SIZE environment variable or C<262144>. |
408 | 410 |
|
409 | 411 |
=head2 relaxed |
410 | 412 |
|
411 |
- my $relaxed = $content->relaxed; |
|
412 |
- $content = $content->relaxed(1); |
|
413 |
+ my $bool = $content->relaxed; |
|
414 |
+ $content = $content->relaxed($bool); |
|
413 | 415 |
|
414 | 416 |
Activate relaxed parsing for responses that are terminated with a connection |
415 | 417 |
close. |
416 | 418 |
|
417 | 419 |
=head2 skip_body |
418 | 420 |
|
419 |
- my $skip = $content->skip_body; |
|
420 |
- $content = $content->skip_body(1); |
|
421 |
+ my $bool = $content->skip_body; |
|
422 |
+ $content = $content->skip_body($bool); |
|
421 | 423 |
|
422 | 424 |
Skip body parsing and finish after headers. |
423 | 425 |
|
... | ... |
@@ -428,7 +430,7 @@ implements the following new ones. |
428 | 430 |
|
429 | 431 |
=head2 body_contains |
430 | 432 |
|
431 |
- my $success = $content->body_contains('foo bar baz'); |
|
433 |
+ my $bool = $content->body_contains('foo bar baz'); |
|
432 | 434 |
|
433 | 435 |
Check if content contains a specific string. Meant to be overloaded in a |
434 | 436 |
subclass. |
... | ... |
@@ -496,34 +498,34 @@ Size of headers in bytes. |
496 | 498 |
|
497 | 499 |
=head2 is_chunked |
498 | 500 |
|
499 |
- my $success = $content->is_chunked; |
|
501 |
+ my $bool = $content->is_chunked; |
|
500 | 502 |
|
501 | 503 |
Check if content is chunked. |
502 | 504 |
|
503 | 505 |
=head2 is_compressed |
504 | 506 |
|
505 |
- my $success = $content->is_compressed; |
|
507 |
+ my $bool = $content->is_compressed; |
|
506 | 508 |
|
507 | 509 |
Check if content is C<gzip> compressed. |
508 | 510 |
|
509 | 511 |
=head2 is_dynamic |
510 | 512 |
|
511 |
- my $success = $content->is_dynamic; |
|
513 |
+ my $bool = $content->is_dynamic; |
|
512 | 514 |
|
513 |
-Check if content will be dynamically generated, which prevents C<clone> from |
|
514 |
-working. |
|
515 |
+Check if content will be dynamically generated, which prevents L</"clone"> |
|
516 |
+from working. |
|
515 | 517 |
|
516 | 518 |
=head2 is_finished |
517 | 519 |
|
518 |
- my $success = $content->is_finished; |
|
520 |
+ my $bool = $content->is_finished; |
|
519 | 521 |
|
520 | 522 |
Check if parser is finished. |
521 | 523 |
|
522 | 524 |
=head2 is_limit_exceeded |
523 | 525 |
|
524 |
- my $success = $content->is_limit_exceeded; |
|
526 |
+ my $bool = $content->is_limit_exceeded; |
|
525 | 527 |
|
526 |
-Check if buffer has exceeded C<max_buffer_size>. |
|
528 |
+Check if buffer has exceeded L</"max_buffer_size">. |
|
527 | 529 |
|
528 | 530 |
=head2 is_multipart |
529 | 531 |
|
... | ... |
@@ -533,7 +535,7 @@ False. |
533 | 535 |
|
534 | 536 |
=head2 is_parsing_body |
535 | 537 |
|
536 |
- my $success = $content->is_parsing_body; |
|
538 |
+ my $bool = $content->is_parsing_body; |
|
537 | 539 |
|
538 | 540 |
Check if body parsing started yet. |
539 | 541 |
|
... | ... |
@@ -45,7 +45,7 @@ sub build_boundary { |
45 | 45 |
my $boundary; |
46 | 46 |
my $size = 1; |
47 | 47 |
while (1) { |
48 |
- $boundary = b64_encode join('', map chr(rand(256)), 1 .. $size++ * 3); |
|
48 |
+ $boundary = b64_encode join('', map chr(rand 256), 1 .. $size++ * 3); |
|
49 | 49 |
$boundary =~ s/\W/X/g; |
50 | 50 |
last unless $self->body_contains($boundary); |
51 | 51 |
} |
... | ... |
@@ -199,6 +199,8 @@ sub _read { |
199 | 199 |
|
200 | 200 |
1; |
201 | 201 |
|
202 |
+=encoding utf8 |
|
203 |
+ |
|
202 | 204 |
=head1 NAME |
203 | 205 |
|
204 | 206 |
Mojo::Content::MultiPart - HTTP multipart content |
... | ... |
@@ -258,12 +260,12 @@ implements the following new ones. |
258 | 260 |
|
259 | 261 |
my $multi = Mojo::Content::MultiPart->new; |
260 | 262 |
|
261 |
-Construct a new L<Mojo::Content::MultiPart> object and subscribe to C<read> |
|
263 |
+Construct a new L<Mojo::Content::MultiPart> object and subscribe to L</"read"> |
|
262 | 264 |
event with default content parser. |
263 | 265 |
|
264 | 266 |
=head2 body_contains |
265 | 267 |
|
266 |
- my $success = $multi->body_contains('foobarbaz'); |
|
268 |
+ my $bool = $multi->body_contains('foobarbaz'); |
|
267 | 269 |
|
268 | 270 |
Check if content parts contain a specific string. |
269 | 271 |
|
... | ... |
@@ -46,13 +46,15 @@ sub parse { |
46 | 46 |
|
47 | 47 |
# Content needs to be upgraded to multipart |
48 | 48 |
$self->unsubscribe(read => $self->{read}); |
49 |
- my $multi = Mojo::Content::MultiPart->new($self); |
|
49 |
+ my $multi = Mojo::Content::MultiPart->new(%$self); |
|
50 | 50 |
$self->emit(upgrade => $multi); |
51 | 51 |
return $multi->parse; |
52 | 52 |
} |
53 | 53 |
|
54 | 54 |
1; |
55 | 55 |
|
56 |
+=encoding utf8 |
|
57 |
+ |
|
56 | 58 |
=head1 NAME |
57 | 59 |
|
58 | 60 |
Mojo::Content::Single - HTTP content |
... | ... |
@@ -120,12 +122,12 @@ implements the following new ones. |
120 | 122 |
|
121 | 123 |
my $single = Mojo::Content::Single->new; |
122 | 124 |
|
123 |
-Construct a new L<Mojo::Content::Single> object and subscribe to C<read> event |
|
124 |
-with default content parser. |
|
125 |
+Construct a new L<Mojo::Content::Single> object and subscribe to L</"read"> |
|
126 |
+event with default content parser. |
|
125 | 127 |
|
126 | 128 |
=head2 body_contains |
127 | 129 |
|
128 |
- my $success = $single->body_contains('1234567'); |
|
130 |
+ my $bool = $single->body_contains('1234567'); |
|
129 | 131 |
|
130 | 132 |
Check if content contains a specific string. |
131 | 133 |
|
... | ... |
@@ -1,47 +1,18 @@ |
1 | 1 |
package Mojo::Cookie; |
2 | 2 |
use Mojo::Base -base; |
3 |
-use overload |
|
4 |
- 'bool' => sub {1}, |
|
5 |
- '""' => sub { shift->to_string }, |
|
6 |
- fallback => 1; |
|
3 |
+use overload bool => sub {1}, '""' => sub { shift->to_string }, fallback => 1; |
|
7 | 4 |
|
8 | 5 |
use Carp 'croak'; |
9 |
-use Mojo::Util 'unquote'; |
|
10 | 6 |
|
11 | 7 |
has [qw(name value)]; |
12 | 8 |
|
13 | 9 |
sub parse { croak 'Method "parse" not implemented by subclass' } |
14 | 10 |
sub to_string { croak 'Method "to_string" not implemented by subclass' } |
15 | 11 |
|
16 |
-sub _tokenize { |
|
17 |
- my ($self, $str) = @_; |
|
18 |
- |
|
19 |
- # Nibbling parser |
|
20 |
- my (@tree, @token); |
|
21 |
- while ($str =~ s/^\s*([^=;,]+)\s*=?\s*//) { |
|
22 |
- my $name = $1; |
|
23 |
- |
|
24 |
- # "expires" is a special case, thank you Netscape... |
|
25 |
- $str =~ s/^([^;,]+,?[^;,]+)/"$1"/ if $name =~ /^expires$/i; |
|
26 |
- |
|
27 |
- # Value |
|
28 |
- my $value; |
|
29 |
- $value = unquote $1 if $str =~ s/^("(?:\\\\|\\"|[^"])+"|[^;,]+)\s*//; |
|
30 |
- push @token, [$name, $value]; |
|
31 |
- |
|
32 |
- # Separator |
|
33 |
- $str =~ s/^\s*;\s*//; |
|
34 |
- next unless $str =~ s/^\s*,\s*//; |
|
35 |
- push @tree, [@token]; |
|
36 |
- @token = (); |
|
37 |
- } |
|
38 |
- |
|
39 |
- # Take care of final token |
|
40 |
- return @token ? (@tree, \@token) : @tree; |
|
41 |
-} |
|
42 |
- |
|
43 | 12 |
1; |
44 | 13 |
|
14 |
+=encoding utf8 |
|
15 |
+ |
|
45 | 16 |
=head1 NAME |
46 | 17 |
|
47 | 18 |
Mojo::Cookie - HTTP cookie base class |
... | ... |
@@ -1,14 +1,15 @@ |
1 | 1 |
package Mojo::Cookie::Request; |
2 | 2 |
use Mojo::Base 'Mojo::Cookie'; |
3 | 3 |
|
4 |
-use Mojo::Util 'quote'; |
|
4 |
+use Mojo::Util qw(quote split_header); |
|
5 | 5 |
|
6 | 6 |
sub parse { |
7 | 7 |
my ($self, $str) = @_; |
8 | 8 |
|
9 | 9 |
my @cookies; |
10 |
- for my $token (map {@$_} $self->_tokenize(defined $str ? $str : '')) { |
|
11 |
- my ($name, $value) = @$token; |
|
10 |
+ my @pairs = map {@$_} @{split_header(defined $str? $str: '')}; |
|
11 |
+ while (@pairs) { |
|
12 |
+ my ($name, $value) = (shift @pairs, shift @pairs); |
|
12 | 13 |
next if $name =~ /^\$/; |
13 | 14 |
push @cookies, $self->new(name => $name, value => defined $value ? $value : ''); |
14 | 15 |
} |
... | ... |
@@ -18,14 +19,15 @@ sub parse { |
18 | 19 |
|
19 | 20 |
sub to_string { |
20 | 21 |
my $self = shift; |
21 |
- return '' unless my $name = $self->name; |
|
22 |
+ return '' unless length(my $name = defined $self->name ? $self->name : ''); |
|
22 | 23 |
my $value = defined $self->value ? $self->value : ''; |
23 |
- $value = $value =~ /[,;"]/ ? quote($value) : $value; |
|
24 |
- return "$name=$value"; |
|
24 |
+ return join '=', $name, $value =~ /[,;" ]/ ? quote($value) : $value; |
|
25 | 25 |
} |
26 | 26 |
|
27 | 27 |
1; |
28 | 28 |
|
29 |
+=encoding utf8 |
|
30 |
+ |
|
29 | 31 |
=head1 NAME |
30 | 32 |
|
31 | 33 |
Mojo::Cookie::Request - HTTP request cookie |
... | ... |
@@ -2,9 +2,9 @@ package Mojo::Cookie::Response; |
2 | 2 |
use Mojo::Base 'Mojo::Cookie'; |
3 | 3 |
|
4 | 4 |
use Mojo::Date; |
5 |
-use Mojo::Util 'quote'; |
|
5 |
+use Mojo::Util qw(quote split_header); |
|
6 | 6 |
|
7 |
-has [qw(domain httponly max_age path secure)]; |
|
7 |
+has [qw(domain httponly max_age origin path secure)]; |
|
8 | 8 |
|
9 | 9 |
sub expires { |
10 | 10 |
my $self = shift; |
... | ... |
@@ -22,21 +22,29 @@ sub parse { |
22 | 22 |
my ($self, $str) = @_; |
23 | 23 |
|
24 | 24 |
my @cookies; |
25 |
- for my $token ($self->_tokenize(defined $str ? $str : '')) { |
|
26 |
- for my $i (0 .. $#$token) { |
|
27 |
- my ($name, $value) = @{$token->[$i]}; |
|
25 |
+ my $tree = split_header(defined $str ? $str : ''); |
|
26 |
+ while (my $pairs = shift @$tree) { |
|
27 |
+ my $i = 0; |
|
28 |
+ while (@$pairs) { |
|
29 |
+ my ($name, $value) = (shift @$pairs, shift @$pairs); |
|
30 |
+ |
|
31 |
+ # "expires" is a special case, thank you Netscape... |
|
32 |
+ if ($name =~ /^expires$/i) { |
|
33 |
+ push @$pairs, @{my $elem = shift @$tree; defined $elem ? $elem : []}; |
|
34 |
+ my $len = (defined $pairs->[0] ? $pairs->[0] : '') =~ /-/ ? 6 : 10; |
|
35 |
+ $value .= join ' ', ',', grep {defined} splice @$pairs, 0, $len; |
|
36 |
+ } |
|
28 | 37 |
|
29 | 38 |
# This will only run once |
30 | 39 |
push @cookies, $self->new(name => $name, value => defined $value ? $value : '') and next |
31 |
- unless $i; |
|
40 |
+ unless $i++; |
|
32 | 41 |
|
33 | 42 |
# Attributes (Netscape and RFC 6265) |
34 |
- my @match |
|
35 |
- = $name =~ /^(expires|domain|path|secure|Max-Age|HttpOnly)$/msi; |
|
36 |
- next unless @match; |
|
37 |
- my $attr = lc $match[0]; |
|
38 |
- $attr =~ tr/-/_/; |
|
39 |
- $cookies[-1]->$attr($attr =~ /(?:secure|HttpOnly)/i ? 1 : $value); |
|
43 |
+ next unless $name =~ /^(expires|domain|path|secure|max-age|httponly)$/i; |
|
44 |
+ my $attr = lc $1; |
|
45 |
+ $attr = 'max_age' if $attr eq 'max-age'; |
|
46 |
+ $cookies[-1] |
|
47 |
+ ->$attr($attr eq 'secure' || $attr eq 'httponly' ? 1 : $value); |
|
40 | 48 |
} |
41 | 49 |
} |
42 | 50 |
|
... | ... |
@@ -47,10 +55,9 @@ sub to_string { |
47 | 55 |
my $self = shift; |
48 | 56 |
|
49 | 57 |
# Name and value (Netscape) |
50 |
- return '' unless my $name = $self->name; |
|
58 |
+ return '' unless length(my $name = defined $self->name ? $self->name : ''); |
|
51 | 59 |
my $value = defined $self->value ? $self->value : ''; |
52 |
- $value = $value =~ /[,;"]/ ? quote($value) : $value; |
|
53 |
- my $cookie = "$name=$value"; |
|
60 |
+ my $cookie = join '=', $name, $value =~ /[,;" ]/ ? quote($value) : $value; |
|
54 | 61 |
|
55 | 62 |
# "expires" (Netscape) |
56 | 63 |
if (defined(my $e = $self->expires)) { $cookie .= "; expires=$e" } |
... | ... |
@@ -62,19 +69,21 @@ sub to_string { |
62 | 69 |
if (my $path = $self->path) { $cookie .= "; path=$path" } |
63 | 70 |
|
64 | 71 |
# "secure" (Netscape) |
65 |
- if (my $secure = $self->secure) { $cookie .= "; secure" } |
|
72 |
+ $cookie .= "; secure" if $self->secure; |
|
66 | 73 |
|
67 | 74 |
# "Max-Age" (RFC 6265) |
68 |
- if (defined(my $m = $self->max_age)) { $cookie .= "; Max-Age=$m" } |
|
75 |
+ if (defined(my $max = $self->max_age)) { $cookie .= "; Max-Age=$max" } |
|
69 | 76 |
|
70 | 77 |
# "HttpOnly" (RFC 6265) |
71 |
- if (my $httponly = $self->httponly) { $cookie .= "; HttpOnly" } |
|
78 |
+ $cookie .= "; HttpOnly" if $self->httponly; |
|
72 | 79 |
|
73 | 80 |
return $cookie; |
74 | 81 |
} |
75 | 82 |
|
76 | 83 |
1; |
77 | 84 |
|
85 |
+=encoding utf8 |
|
86 |
+ |
|
78 | 87 |
=head1 NAME |
79 | 88 |
|
80 | 89 |
Mojo::Cookie::Response - HTTP response cookie |
... | ... |
@@ -107,8 +116,8 @@ Cookie domain. |
107 | 116 |
|
108 | 117 |
=head2 httponly |
109 | 118 |
|
110 |
- my $httponly = $cookie->httponly; |
|
111 |
- $cookie = $cookie->httponly(1); |
|
119 |
+ my $bool = $cookie->httponly; |
|
120 |
+ $cookie = $cookie->httponly($bool); |
|
112 | 121 |
|
113 | 122 |
HttpOnly flag, which can prevent client-side scripts from accessing this |
114 | 123 |
cookie. |
... | ... |
@@ -120,6 +129,13 @@ cookie. |
120 | 129 |
|
121 | 130 |
Max age for cookie. |
122 | 131 |
|
132 |
+=head2 origin |
|
133 |
+ |
|
134 |
+ my $origin = $cookie->origin; |
|
135 |
+ $cookie = $cookie->origin('mojolicio.us'); |
|
136 |
+ |
|
137 |
+Origin of the cookie. |
|
138 |
+ |
|
123 | 139 |
=head2 path |
124 | 140 |
|
125 | 141 |
my $path = $cookie->path; |
... | ... |
@@ -129,8 +145,8 @@ Cookie path. |
129 | 145 |
|
130 | 146 |
=head2 secure |
131 | 147 |
|
132 |
- my $secure = $cookie->secure; |
|
133 |
- $cookie = $cookie->secure(1); |
|
148 |
+ my $bool = $cookie->secure; |
|
149 |
+ $cookie = $cookie->secure($bool); |
|
134 | 150 |
|
135 | 151 |
Secure flag, which instructs browsers to only send this cookie over HTTPS |
136 | 152 |
connections. |
... | ... |
@@ -1,8 +1,8 @@ |
1 | 1 |
package Mojo::DOM; |
2 |
-use Mojo::Base -base; |
|
2 |
+use Mojo::Base -strict; |
|
3 | 3 |
use overload |
4 |
- '%{}' => sub { shift->attrs }, |
|
5 |
- 'bool' => sub {1}, |
|
4 |
+ '%{}' => sub { shift->attr }, |
|
5 |
+ bool => sub {1}, |
|
6 | 6 |
'""' => sub { shift->to_xml }, |
7 | 7 |
fallback => 1; |
8 | 8 |
|
... | ... |
@@ -36,23 +36,22 @@ sub new { |
36 | 36 |
return @_ ? $self->parse(@_) : $self; |
37 | 37 |
} |
38 | 38 |
|
39 |
-sub all_text { |
|
40 |
- my $tree = shift->tree; |
|
41 |
- return _text(_elements($tree), 1, _trim($tree, @_)); |
|
42 |
-} |
|
39 |
+sub all_text { shift->_content(1, @_) } |
|
40 |
+ |
|
41 |
+sub ancestors { _select($_[0]->_collect(_ancestors($_[0]->tree)), $_[1]) } |
|
43 | 42 |
|
44 | 43 |
sub append { shift->_add(1, @_) } |
45 | 44 |
|
46 | 45 |
sub append_content { |
47 | 46 |
my ($self, $new) = @_; |
48 | 47 |
my $tree = $self->tree; |
49 |
- push @$tree, @{_parent($self->_parse("$new"), $tree)}; |
|
48 |
+ push @$tree, _link($self->_parse("$new"), $tree); |
|
50 | 49 |
return $self; |
51 | 50 |
} |
52 | 51 |
|
53 | 52 |
sub at { shift->find(@_)->[0] } |
54 | 53 |
|
55 |
-sub attrs { |
|
54 |
+sub attr { |
|
56 | 55 |
my $self = shift; |
57 | 56 |
|
58 | 57 |
# Hash |
... | ... |
@@ -70,45 +69,35 @@ sub attrs { |
70 | 69 |
} |
71 | 70 |
|
72 | 71 |
sub children { |
73 |
- my ($self, $type) = @_; |
|
74 |
- |
|
75 |
- my @children; |
|
76 |
- my $xml = $self->xml; |
|
77 |
- my $tree = $self->tree; |
|
78 |
- for my $e (@$tree[($tree->[0] eq 'root' ? 1 : 4) .. $#$tree]) { |
|
79 |
- |
|
80 |
- # Make sure child is the right type |
|
81 |
- next if $e->[0] ne 'tag' || (defined $type && $e->[1] ne $type); |
|
82 |
- push @children, $self->new->tree($e)->xml($xml); |
|
83 |
- } |
|
84 |
- |
|
85 |
- return Mojo::Collection->new(@children); |
|
72 |
+ my $self = shift; |
|
73 |
+ return _select( |
|
74 |
+ $self->_collect(grep { $_->[0] eq 'tag' } _nodes($self->tree)), @_); |
|
86 | 75 |
} |
87 | 76 |
|
88 | 77 |
sub content_xml { |
89 | 78 |
my $self = shift; |
90 |
- |
|
91 |
- # Render children individually |
|
92 |
- my $tree = $self->tree; |
|
93 | 79 |
my $xml = $self->xml; |
94 |
- return join '', |
|
95 |
- map { Mojo::DOM::HTML->new(tree => $_, xml => $xml)->render } |
|
96 |
- @$tree[($tree->[0] eq 'root' ? 1 : 4) .. $#$tree]; |
|
80 |
+ return join '', map { _render($_, $xml) } _nodes($self->tree); |
|
97 | 81 |
} |
98 | 82 |
|
99 | 83 |
sub find { |
100 |
- my ($self, $selector) = @_; |
|
101 |
- my $xml = $self->xml; |
|
102 |
- my $results = Mojo::DOM::CSS->new(tree => $self->tree)->select($selector); |
|
103 |
- return Mojo::Collection->new(map { $self->new->tree($_)->xml($xml) } |
|
104 |
- @$results); |
|
84 |
+ my $self = shift; |
|
85 |
+ my $results = Mojo::DOM::CSS->new(tree => $self->tree)->select(@_); |
|
86 |
+ return $self->_collect(@$results); |
|
87 |
+} |
|
88 |
+ |
|
89 |
+sub match { |
|
90 |
+ my $self = shift; |
|
91 |
+ return undef unless Mojo::DOM::CSS->new(tree => $self->tree)->match(@_); |
|
92 |
+ return $self; |
|
105 | 93 |
} |
106 | 94 |
|
107 | 95 |
sub namespace { |
108 | 96 |
my $self = shift; |
109 | 97 |
|
110 |
- # Extract namespace prefix and search parents |
|
111 | 98 |
return '' if (my $current = $self->tree)->[0] eq 'root'; |
99 |
+ |
|
100 |
+ # Extract namespace prefix and search parents |
|
112 | 101 |
my $ns = $current->[1] =~ /^(.*?):/ ? "xmlns:$1" : undef; |
113 | 102 |
while ($current->[0] ne 'root') { |
114 | 103 |
|
... | ... |
@@ -119,14 +108,13 @@ sub namespace { |
119 | 108 |
# Namespace attribute |
120 | 109 |
elsif (defined $attrs->{xmlns}) { return $attrs->{xmlns} } |
121 | 110 |
|
122 |
- # Parent |
|
123 | 111 |
$current = $current->[3]; |
124 | 112 |
} |
125 | 113 |
|
126 | 114 |
return ''; |
127 | 115 |
} |
128 | 116 |
|
129 |
-sub next { shift->_sibling(1) } |
|
117 |
+sub next { shift->_siblings->[1][0] } |
|
130 | 118 |
|
131 | 119 |
sub parent { |
132 | 120 |
my $self = shift; |
... | ... |
@@ -134,201 +122,228 @@ sub parent { |
134 | 122 |
return $self->new->tree($tree->[3])->xml($self->xml); |
135 | 123 |
} |
136 | 124 |
|
137 |
-sub parse { shift->_html(parse => shift) } |
|
125 |
+sub parse { shift->_delegate(parse => shift) } |
|
138 | 126 |
|
139 | 127 |
sub prepend { shift->_add(0, @_) } |
140 | 128 |
|
141 | 129 |
sub prepend_content { |
142 | 130 |
my ($self, $new) = @_; |
143 | 131 |
my $tree = $self->tree; |
144 |
- splice @$tree, $tree->[0] eq 'root' ? 1 : 4, 0, |
|
145 |
- @{_parent($self->_parse("$new"), $tree)}; |
|
132 |
+ splice @$tree, _offset($tree), 0, _link($self->_parse("$new"), $tree); |
|
146 | 133 |
return $self; |
147 | 134 |
} |
148 | 135 |
|
149 |
-sub previous { shift->_sibling(0) } |
|
136 |
+sub previous { shift->_siblings->[0][-1] } |
|
150 | 137 |
|
151 | 138 |
sub remove { shift->replace('') } |
152 | 139 |
|
153 | 140 |
sub replace { |
154 | 141 |
my ($self, $new) = @_; |
155 |
- |
|
156 | 142 |
my $tree = $self->tree; |
157 |
- if ($tree->[0] eq 'root') { return $self->xml(undef)->parse($new) } |
|
158 |
- else { $new = $self->_parse("$new") } |
|
159 |
- |
|
160 |
- my $parent = $tree->[3]; |
|
161 |
- my $i = $parent->[0] eq 'root' ? 1 : 4; |
|
162 |
- for my $e (@$parent[$i .. $#$parent]) { |
|
163 |
- last if $e == $tree; |
|
164 |
- $i++; |
|
165 |
- } |
|
166 |
- splice @$parent, $i, 1, @{_parent($new, $parent)}; |
|
167 |
- |
|
168 |
- return $self; |
|
143 |
+ return $self->xml(undef)->parse($new) if $tree->[0] eq 'root'; |
|
144 |
+ return $self->_replace($tree, $self->_parse("$new")); |
|
169 | 145 |
} |
170 | 146 |
|
171 | 147 |
sub replace_content { |
172 | 148 |
my ($self, $new) = @_; |
173 | 149 |
my $tree = $self->tree; |
174 |
- splice @$tree, $tree->[0] eq 'root' ? 1 : 4, $#$tree, |
|
175 |
- @{_parent($self->_parse("$new"), $tree)}; |
|
150 |
+ splice @$tree, _offset($tree), $#$tree, _link($self->_parse("$new"), $tree); |
|
176 | 151 |
return $self; |
177 | 152 |
} |
178 | 153 |
|
179 | 154 |
sub root { |
180 | 155 |
my $self = shift; |
156 |
+ return $self unless my $tree = _ancestors($self->tree, 1); |
|
157 |
+ return $self->new->tree($tree)->xml($self->xml); |
|
158 |
+} |
|
181 | 159 |
|
182 |
- my $root = $self->tree; |
|
183 |
- while ($root->[0] eq 'tag') { |
|
184 |
- last unless my $parent = $root->[3]; |
|
185 |
- $root = $parent; |
|
186 |
- } |
|
160 |
+sub siblings { _select(Mojo::Collection->new(@{_siblings($_[0], 1)}), $_[1]) } |
|
187 | 161 |
|
188 |
- return $self->new->tree($root)->xml($self->xml); |
|
162 |
+sub strip { |
|
163 |
+ my $self = shift; |
|
164 |
+ my $tree = $self->tree; |
|
165 |
+ return $self if $tree->[0] eq 'root'; |
|
166 |
+ return $self->_replace($tree, ['root', _nodes($tree)]); |
|
189 | 167 |
} |
190 | 168 |
|
191 |
-sub text { |
|
192 |
- my $tree = shift->tree; |
|
193 |
- return _text(_elements($tree), 0, _trim($tree, @_)); |
|
194 |
-} |
|
169 |
+sub tap { shift->Mojo::Base::tap(@_) } |
|
170 |
+ |
|
171 |
+sub text { shift->_content(0, @_) } |
|
195 | 172 |
|
196 | 173 |
sub text_after { |
197 | 174 |
my ($self, $trim) = @_; |
198 | 175 |
|
199 |
- # Find following text elements |
|
200 | 176 |
return '' if (my $tree = $self->tree)->[0] eq 'root'; |
201 |
- my (@elements, $started); |
|
202 |
- for my $e (@{_elements($tree->[3])}) { |
|
203 |
- ++$started and next if $e eq $tree; |
|
177 |
+ |
|
178 |
+ my (@nodes, $started); |
|
179 |
+ for my $n (_nodes($tree->[3])) { |
|
180 |
+ ++$started and next if $n eq $tree; |
|
204 | 181 |
next unless $started; |
205 |
- last if $e->[0] eq 'tag'; |
|
206 |
- push @elements, $e; |
|
182 |
+ last if $n->[0] eq 'tag'; |
|
183 |
+ push @nodes, $n; |
|
207 | 184 |
} |
208 | 185 |
|
209 |
- return _text(\@elements, 0, _trim($tree->[3], $trim)); |
|
186 |
+ return _text(\@nodes, 0, _trim($tree->[3], $trim)); |
|
210 | 187 |
} |
211 | 188 |
|
212 | 189 |
sub text_before { |
213 | 190 |
my ($self, $trim) = @_; |
214 | 191 |
|
215 |
- # Find preceding text elements |
|
216 | 192 |
return '' if (my $tree = $self->tree)->[0] eq 'root'; |
217 |
- my @elements; |
|
218 |
- for my $e (@{_elements($tree->[3])}) { |
|
219 |
- last if $e eq $tree; |
|
220 |
- push @elements, $e; |
|
221 |
- @elements = () if $e->[0] eq 'tag'; |
|
193 |
+ |
|
194 |
+ my @nodes; |
|
195 |
+ for my $n (_nodes($tree->[3])) { |
|
196 |
+ last if $n eq $tree; |
|
197 |
+ push @nodes, $n; |
|
198 |
+ @nodes = () if $n->[0] eq 'tag'; |
|
222 | 199 |
} |
223 | 200 |
|
224 |
- return _text(\@elements, 0, _trim($tree->[3], $trim)); |
|
201 |
+ return _text(\@nodes, 0, _trim($tree->[3], $trim)); |
|
225 | 202 |
} |
226 | 203 |
|
227 | 204 |
sub to_xml { shift->[0]->render } |
228 | 205 |
|
229 |
-sub tree { shift->_html(tree => @_) } |
|
206 |
+sub tree { shift->_delegate(tree => @_) } |
|
230 | 207 |
|
231 | 208 |
sub type { |
232 | 209 |
my ($self, $type) = @_; |
233 |
- |
|
234 |
- # Get |
|
235 | 210 |
return '' if (my $tree = $self->tree)->[0] eq 'root'; |
236 | 211 |
return $tree->[1] unless $type; |
237 |
- |
|
238 |
- # Set |
|
239 | 212 |
$tree->[1] = $type; |
240 |
- |
|
241 | 213 |
return $self; |
242 | 214 |
} |
243 | 215 |
|
244 |
-sub xml { shift->_html(xml => @_) } |
|
216 |
+sub xml { shift->_delegate(xml => @_) } |
|
245 | 217 |
|
246 | 218 |
sub _add { |
247 | 219 |
my ($self, $offset, $new) = @_; |
248 | 220 |
|
249 |
- # Not a tag |
|
250 | 221 |
return $self if (my $tree = $self->tree)->[0] eq 'root'; |
251 | 222 |
|
252 |
- # Find parent |
|
253 | 223 |
my $parent = $tree->[3]; |
254 |
- my $i = $parent->[0] eq 'root' ? 1 : 4; |
|
255 |
- for my $e (@$parent[$i .. $#$parent]) { |
|
256 |
- last if $e == $tree; |
|
257 |
- $i++; |
|
258 |
- } |
|
259 |
- |
|
260 |
- # Add children |
|
261 |
- splice @$parent, $i + $offset, 0, @{_parent($self->_parse("$new"), $parent)}; |
|
224 |
+ splice @$parent, _parent($parent, $tree) + $offset, 0, |
|
225 |
+ _link($self->_parse("$new"), $parent); |
|
262 | 226 |
|
263 | 227 |
return $self; |
264 | 228 |
} |
265 | 229 |
|
266 |
-sub _elements { |
|
267 |
- return [] unless my $e = shift; |
|
268 |
- return [@$e[($e->[0] eq 'root' ? 1 : 4) .. $#$e]]; |
|
230 |
+sub _ancestors { |
|
231 |
+ my ($tree, $root) = @_; |
|
232 |
+ my @ancestors; |
|
233 |
+ push @ancestors, $tree while ($tree->[0] eq 'tag') && ($tree = $tree->[3]); |
|
234 |
+ return $root ? $ancestors[-1] : @ancestors[0 .. $#ancestors - 1]; |
|
235 |
+} |
|
236 |
+ |
|
237 |
+sub _collect { |
|
238 |
+ my $self = shift; |
|
239 |
+ my $xml = $self->xml; |
|
240 |
+ return Mojo::Collection->new(@_) |
|
241 |
+ ->map(sub { $self->new->tree($_)->xml($xml) }); |
|
242 |
+} |
|
243 |
+ |
|
244 |
+sub _content { |
|
245 |
+ my $tree = shift->tree; |
|
246 |
+ return _text([_nodes($tree)], shift, _trim($tree, @_)); |
|
269 | 247 |
} |
270 | 248 |
|
271 |
-sub _html { |
|
249 |
+sub _delegate { |
|
272 | 250 |
my ($self, $method) = (shift, shift); |
273 | 251 |
return $self->[0]->$method unless @_; |
274 | 252 |
$self->[0]->$method(@_); |
275 | 253 |
return $self; |
276 | 254 |
} |
277 | 255 |
|
278 |
-sub _parent { |
|
256 |
+sub _link { |
|
279 | 257 |
my ($children, $parent) = @_; |
280 | 258 |
|
281 | 259 |
# Link parent to children |
282 | 260 |
my @new; |
283 |
- for my $e (@$children[1 .. $#$children]) { |
|
284 |
- if ($e->[0] eq 'tag') { |
|
285 |
- $e->[3] = $parent; |
|
286 |
- weaken $e->[3]; |
|
287 |
- } |
|
288 |
- push @new, $e; |
|
261 |
+ for my $n (@$children[1 .. $#$children]) { |
|
262 |
+ push @new, $n; |
|
263 |
+ next unless $n->[0] eq 'tag'; |
|
264 |
+ $n->[3] = $parent; |
|
265 |
+ weaken $n->[3]; |
|
266 |
+ } |
|
267 |
+ |
|
268 |
+ return @new; |
|
269 |
+} |
|
270 |
+ |
|
271 |
+sub _nodes { |
|
272 |
+ return unless my $n = shift; |
|
273 |
+ return @$n[_offset($n) .. $#$n]; |
|
274 |
+} |
|
275 |
+ |
|
276 |
+sub _offset { $_[0][0] eq 'root' ? 1 : 4 } |
|
277 |
+ |
|
278 |
+sub _parent { |
|
279 |
+ my ($parent, $child) = @_; |
|
280 |
+ |
|
281 |
+ # Find parent offset for child |
|
282 |
+ my $i = _offset($parent); |
|
283 |
+ for my $n (@$parent[$i .. $#$parent]) { |
|
284 |
+ last if $n == $child; |
|
285 |
+ $i++; |
|
289 | 286 |
} |
290 | 287 |
|
291 |
- return \@new; |
|
288 |
+ return $i; |
|
292 | 289 |
} |
293 | 290 |
|
294 | 291 |
sub _parse { Mojo::DOM::HTML->new(xml => shift->xml)->parse(shift)->tree } |
295 | 292 |
|
296 |
-sub _sibling { |
|
297 |
- my ($self, $next) = @_; |
|
293 |
+sub _render { Mojo::DOM::HTML->new(tree => shift, xml => shift)->render } |
|
294 |
+ |
|
295 |
+sub _replace { |
|
296 |
+ my ($self, $tree, $new) = @_; |
|
297 |
+ my $parent = $tree->[3]; |
|
298 |
+ splice @$parent, _parent($parent, $tree), 1, _link($new, $parent); |
|
299 |
+ return $self->parent; |
|
300 |
+} |
|
301 |
+ |
|
302 |
+sub _select { |
|
303 |
+ my ($self, $selector) = @_; |
|
304 |
+ return defined $selector ? $self->grep(sub { $_->match($selector) }) : $self; |
|
305 |
+} |
|
306 |
+ |
|
307 |
+sub _siblings { |
|
308 |
+ my ($self, $merge) = @_; |
|
298 | 309 |
|
299 |
- # Make sure we have a parent |
|
300 |
- return undef unless my $parent = $self->parent; |
|
310 |
+ return $merge ? [] : [[], []] unless my $parent = $self->parent; |
|
301 | 311 |
|
302 |
- # Find previous or next sibling |
|
303 |
- my ($previous, $current); |
|
312 |
+ my $tree = $self->tree; |
|
313 |
+ my (@before, @after, $match); |
|
304 | 314 |
for my $child ($parent->children->each) { |
305 |
- ++$current and next if $child->tree eq $self->tree; |
|
306 |
- return $next ? $child : $previous if $current; |
|
307 |
- $previous = $child; |
|
315 |
+ ++$match and next if $child->tree eq $tree; |
|
316 |
+ $match ? push @after, $child : push @before, $child; |
|
308 | 317 |
} |
309 | 318 |
|
310 |
- # No siblings |
|
311 |
- return undef; |
|
319 |
+ return $merge ? [@before, @after] : [\@before, \@after]; |
|
312 | 320 |
} |
313 | 321 |
|
314 | 322 |
sub _text { |
315 |
- my ($elements, $recurse, $trim) = @_; |
|
323 |
+ my ($nodes, $recurse, $trim) = @_; |
|
324 |
+ |
|
325 |
+ # Merge successive text nodes |
|
326 |
+ my $i = 0; |
|
327 |
+ while (my $next = $nodes->[$i + 1]) { |
|
328 |
+ ++$i and next unless $nodes->[$i][0] eq 'text' && $next->[0] eq 'text'; |
|
329 |
+ splice @$nodes, $i, 2, ['text', $nodes->[$i][1] . $next->[1]]; |
|
330 |
+ } |
|
316 | 331 |
|
317 | 332 |
my $text = ''; |
318 |
- for my $e (@$elements) { |
|
319 |
- my $type = $e->[0]; |
|
333 |
+ for my $n (@$nodes) { |
|
334 |
+ my $type = $n->[0]; |
|
320 | 335 |
|
321 | 336 |
# Nested tag |
322 | 337 |
my $content = ''; |
323 | 338 |
if ($type eq 'tag' && $recurse) { |
324 |
- $content = _text(_elements($e), 1, _trim($e, $trim)); |
|
339 |
+ $content = _text([_nodes($n)], 1, _trim($n, $trim)); |
|
325 | 340 |
} |
326 | 341 |
|
327 | 342 |
# Text |
328 |
- elsif ($type eq 'text') { $content = $trim ? squish($e->[1]) : $e->[1] } |
|
343 |
+ elsif ($type eq 'text') { $content = $trim ? squish($n->[1]) : $n->[1] } |
|
329 | 344 |
|
330 | 345 |
# CDATA or raw text |
331 |
- elsif ($type eq 'cdata' || $type eq 'raw') { $content = $e->[1] } |
|
346 |
+ elsif ($type eq 'cdata' || $type eq 'raw') { $content = $n->[1] } |
|
332 | 347 |
|
333 | 348 |
# Add leading whitespace if punctuation allows it |
334 | 349 |
$content = " $content" if $text =~ /\S\z/ && $content =~ /^[^.!?,;:\s]+/; |
... | ... |
@@ -357,6 +372,8 @@ sub _trim { |
357 | 372 |
|
358 | 373 |
1; |
359 | 374 |
|
375 |
+=encoding utf8 |
|
376 |
+ |
|
360 | 377 |
=head1 NAME |
361 | 378 |
|
362 | 379 |
Mojo::DOM - Minimalistic HTML/XML DOM parser with CSS selectors |
... | ... |
@@ -370,7 +387,8 @@ Mojo::DOM - Minimalistic HTML/XML DOM parser with CSS selectors |
370 | 387 |
|
371 | 388 |
# Find |
372 | 389 |
say $dom->at('#b')->text; |
373 |
- say $dom->find('p')->pluck('text'); |
|
390 |
+ say $dom->find('p')->text; |
|
391 |
+ say $dom->find('[id]')->attr('id'); |
|
374 | 392 |
|
375 | 393 |
# Walk |
376 | 394 |
say $dom->div->p->[0]->text; |
... | ... |
@@ -386,6 +404,7 @@ Mojo::DOM - Minimalistic HTML/XML DOM parser with CSS selectors |
386 | 404 |
|
387 | 405 |
# Modify |
388 | 406 |
$dom->div->p->[1]->append('<p id="c">C</p>'); |
407 |
+ $dom->find(':not(p)')->strip; |
|
389 | 408 |
|
390 | 409 |
# Render |
391 | 410 |
say "$dom"; |
... | ... |
@@ -399,7 +418,7 @@ use it for validation. |
399 | 418 |
=head1 CASE SENSITIVITY |
400 | 419 |
|
401 | 420 |
L<Mojo::DOM> defaults to HTML semantics, that means all tags and attributes |
402 |
-are lowercased and selectors need to be lower case as well. |
|
421 |
+are lowercased and selectors need to be lowercase as well. |
|
403 | 422 |
|
404 | 423 |
my $dom = Mojo::DOM->new('<P ID="greeting">Hi!</P>'); |
405 | 424 |
say $dom->at('p')->text; |
... | ... |
@@ -412,7 +431,7 @@ into XML mode and everything becomes case sensitive. |
412 | 431 |
say $dom->at('P')->text; |
413 | 432 |
say $dom->P->{ID}; |
414 | 433 |
|
415 |
-XML detection can also be disabled with the C<xml> method. |
|
434 |
+XML detection can also be disabled with the L</"xml"> method. |
|
416 | 435 |
|
417 | 436 |
# Force XML semantics |
418 | 437 |
$dom->xml(1); |
... | ... |
@@ -422,16 +441,15 @@ XML detection can also be disabled with the C<xml> method. |
422 | 441 |
|
423 | 442 |
=head1 METHODS |
424 | 443 |
|
425 |
-L<Mojo::DOM> inherits all methods from L<Mojo::Base> and implements the |
|
426 |
-following new ones. |
|
444 |
+L<Mojo::DOM> implements the following methods. |
|
427 | 445 |
|
428 | 446 |
=head2 new |
429 | 447 |
|
430 | 448 |
my $dom = Mojo::DOM->new; |
431 | 449 |
my $dom = Mojo::DOM->new('<foo bar="baz">test</foo>'); |
432 | 450 |
|
433 |
-Construct a new array-based L<Mojo::DOM> object and C<parse> HTML/XML document |
|
434 |
-if necessary. |
|
451 |
+Construct a new array-based L<Mojo::DOM> object and L</"parse"> HTML/XML |
|
452 |
+fragment if necessary. |
|
435 | 453 |
|
436 | 454 |
=head2 all_text |
437 | 455 |
|
... | ... |
@@ -447,11 +465,23 @@ enabled by default. |
447 | 465 |
# "foo\nbarbaz\n" |
448 | 466 |
$dom->parse("<div>foo\n<p>bar</p>baz\n</div>")->div->all_text(0); |
449 | 467 |
|
468 |
+=head2 ancestors |
|
469 |
+ |
|
470 |
+ my $collection = $dom->ancestors; |
|
471 |
+ my $collection = $dom->ancestors('div'); |
|
472 |
+ |
|
473 |
+Find all ancestors of this element matching the CSS selector and return a |
|
474 |
+L<Mojo::Collection> object containing these elements as L<Mojo::DOM> objects. |
|
475 |
+All selectors from L<Mojo::DOM::CSS> are supported. |
|
476 |
+ |
|
477 |
+ # List types of ancestor elements |
|
478 |
+ say $dom->ancestors->type; |
|
479 |
+ |
|
450 | 480 |
=head2 append |
451 | 481 |
|
452 | 482 |
$dom = $dom->append('<p>Hi!</p>'); |
453 | 483 |
|
454 |
-Append HTML/XML to element. |
|
484 |
+Append HTML/XML fragment to element. |
|
455 | 485 |
|
456 | 486 |
# "<div><h1>A</h1><h2>B</h2></div>" |
457 | 487 |
$dom->parse('<div><h1>A</h1></div>')->at('h1')->append('<h2>B</h2>')->root; |
... | ... |
@@ -460,7 +490,7 @@ Append HTML/XML to element. |
460 | 490 |
|
461 | 491 |
$dom = $dom->append_content('<p>Hi!</p>'); |
462 | 492 |
|
463 |
-Append HTML/XML to element content. |
|
493 |
+Append HTML/XML fragment to element content. |
|
464 | 494 |
|
465 | 495 |
# "<div><h1>AB</h1></div>" |
466 | 496 |
$dom->parse('<div><h1>A</h1></div>')->at('h1')->append_content('B')->root; |
... | ... |
@@ -476,22 +506,26 @@ L<Mojo::DOM::CSS> are supported. |
476 | 506 |
# Find first element with "svg" namespace definition |
477 | 507 |
my $namespace = $dom->at('[xmlns\:svg]')->{'xmlns:svg'}; |
478 | 508 |
|
479 |
-=head2 attrs |
|
509 |
+=head2 attr |
|
480 | 510 |
|
481 |
- my $attrs = $dom->attrs; |
|
482 |
- my $foo = $dom->attrs('foo'); |
|
483 |
- $dom = $dom->attrs({foo => 'bar'}); |
|
484 |
- $dom = $dom->attrs(foo => 'bar'); |
|
511 |
+ my $attrs = $dom->attr; |
|
512 |
+ my $foo = $dom->attr('foo'); |
|
513 |
+ $dom = $dom->attr({foo => 'bar'}); |
|
514 |
+ $dom = $dom->attr(foo => 'bar'); |
|
485 | 515 |
|
486 | 516 |
Element attributes. |
487 | 517 |
|
518 |
+ # List id attributes |
|
519 |
+ say $dom->find('*')->attr('id')->compact; |
|
520 |
+ |
|
488 | 521 |
=head2 children |
489 | 522 |
|
490 | 523 |
my $collection = $dom->children; |
491 | 524 |
my $collection = $dom->children('div'); |
492 | 525 |
|
493 |
-Return a L<Mojo::Collection> object containing the children of this element as |
|
494 |
-L<Mojo::DOM> objects, similar to C<find>. |
|
526 |
+Find all children of this element matching the CSS selector and return a |
|
527 |
+L<Mojo::Collection> object containing these elements as L<Mojo::DOM> objects. |
|
528 |
+All selectors from L<Mojo::DOM::CSS> are supported. |
|
495 | 529 |
|
496 | 530 |
# Show type of random child element |
497 | 531 |
say $dom->children->shuffle->first->type; |
... | ... |
@@ -517,7 +551,16 @@ L<Mojo::DOM::CSS> are supported. |
517 | 551 |
my $id = $dom->find('div')->[23]{id}; |
518 | 552 |
|
519 | 553 |
# Extract information from multiple elements |
520 |
- my @headers = $dom->find('h1, h2, h3')->pluck('text')->each; |
|
554 |
+ my @headers = $dom->find('h1, h2, h3')->text->each; |
|
555 |
+ my @links = $dom->find('a[href]')->attr('href')->each; |
|
556 |
+ |
|
557 |
+=head2 match |
|
558 |
+ |
|
559 |
+ my $result = $dom->match('html title'); |
|
560 |
+ |
|
561 |
+Match the CSS selector against this element and return it as a L<Mojo::DOM> |
|
562 |
+object or return C<undef> if it didn't match. All selectors from |
|
563 |
+L<Mojo::DOM::CSS> are supported. |
|
521 | 564 |
|
522 | 565 |
=head2 namespace |
523 | 566 |
|
... | ... |
@@ -552,7 +595,7 @@ has no parent. |
552 | 595 |
|
553 | 596 |
$dom = $dom->parse('<foo bar="baz">test</foo>'); |
554 | 597 |
|
555 |
-Parse HTML/XML document with L<Mojo::DOM::HTML>. |
|
598 |
+Parse HTML/XML fragment with L<Mojo::DOM::HTML>. |
|
556 | 599 |
|
557 | 600 |
# Parse XML |
558 | 601 |
my $dom = Mojo::DOM->new->xml(1)->parse($xml); |
... | ... |
@@ -561,7 +604,7 @@ Parse HTML/XML document with L<Mojo::DOM::HTML>. |
561 | 604 |
|
562 | 605 |
$dom = $dom->prepend('<p>Hi!</p>'); |
563 | 606 |
|
564 |
-Prepend HTML/XML to element. |
|
607 |
+Prepend HTML/XML fragment to element. |
|
565 | 608 |
|
566 | 609 |
# "<div><h1>A</h1><h2>B</h2></div>" |
567 | 610 |
$dom->parse('<div><h2>B</h2></div>')->at('h2')->prepend('<h1>A</h1>')->root; |
... | ... |
@@ -570,7 +613,7 @@ Prepend HTML/XML to element. |
570 | 613 |
|
571 | 614 |
$dom = $dom->prepend_content('<p>Hi!</p>'); |
572 | 615 |
|
573 |
-Prepend HTML/XML to element content. |
|
616 |
+Prepend HTML/XML fragment to element content. |
|
574 | 617 |
|
575 | 618 |
# "<div><h2>AB</h2></div>" |
576 | 619 |
$dom->parse('<div><h2>B</h2></div>')->at('h2')->prepend_content('A')->root; |
... | ... |
@@ -587,31 +630,31 @@ there are no more siblings. |
587 | 630 |
|
588 | 631 |
=head2 remove |
589 | 632 |
|
590 |
- my $old = $dom->remove; |
|
633 |
+ my $parent = $dom->remove; |
|
591 | 634 |
|
592 |
-Remove element and return it as a L<Mojo::DOM> object. |
|
635 |
+Remove element and return L<Mojo::DOM> object for parent of element. |
|
593 | 636 |
|
594 | 637 |
# "<div></div>" |
595 |
- $dom->parse('<div><h1>A</h1></div>')->at('h1')->remove->root; |
|
638 |
+ $dom->parse('<div><h1>A</h1></div>')->at('h1')->remove; |
|
596 | 639 |
|
597 | 640 |
=head2 replace |
598 | 641 |
|
599 |
- my $old = $dom->replace('<div>test</div>'); |
|
642 |
+ my $parent = $dom->replace('<div>test</div>'); |
|
600 | 643 |
|
601 |
-Replace element with HTML/XML and return the replaced element as a |
|
602 |
-L<Mojo::DOM> object. |
|
644 |
+Replace element with HTML/XML fragment and return L<Mojo::DOM> object for |
|
645 |
+parent of element. |
|
603 | 646 |
|
604 | 647 |
# "<div><h2>B</h2></div>" |
605 |
- $dom->parse('<div><h1>A</h1></div>')->at('h1')->replace('<h2>B</h2>')->root; |
|
648 |
+ $dom->parse('<div><h1>A</h1></div>')->at('h1')->replace('<h2>B</h2>'); |
|
606 | 649 |
|
607 | 650 |
# "<div></div>" |
608 |
- $dom->parse('<div><h1>A</h1></div>')->at('h1')->replace('')->root; |
|
651 |
+ $dom->parse('<div><h1>A</h1></div>')->at('h1')->replace(''); |
|
609 | 652 |
|
610 | 653 |
=head2 replace_content |
611 | 654 |
|
612 | 655 |
$dom = $dom->replace_content('<p>test</p>'); |
613 | 656 |
|
614 |
-Replace element content with HTML/XML. |
|
657 |
+Replace element content with HTML/XML fragment. |
|
615 | 658 |
|
616 | 659 |
# "<div><h1>B</h1></div>" |
617 | 660 |
$dom->parse('<div><h1>A</h1></div>')->at('h1')->replace_content('B')->root; |
... | ... |
@@ -625,6 +668,34 @@ Replace element content with HTML/XML. |
625 | 668 |
|
626 | 669 |
Return L<Mojo::DOM> object for root node. |
627 | 670 |
|
671 |
+=head2 siblings |
|
672 |
+ |
|
673 |
+ my $collection = $dom->siblings; |
|
674 |
+ my $collection = $dom->siblings('div'); |
|
675 |
+ |
|
676 |
+Find all siblings of this element matching the CSS selector and return a |
|
677 |
+L<Mojo::Collection> object containing these elements as L<Mojo::DOM> objects. |
|
678 |
+All selectors from L<Mojo::DOM::CSS> are supported. |
|
679 |
+ |
|
680 |
+ # List types of sibling elements |
|
681 |
+ say $dom->siblings->type; |
|
682 |
+ |
|
683 |
+=head2 strip |
|
684 |
+ |
|
685 |
+ my $parent = $dom->strip; |
|
686 |
+ |
|
687 |
+Remove element while preserving its content and return L<Mojo::DOM> object for |
|
688 |
+parent of element. |
|
689 |
+ |
|
690 |
+ # "<div>A</div>" |
|
691 |
+ $dom->parse('<div><h1>A</h1></div>')->at('h1')->strip; |
|
692 |
+ |
|
693 |
+=head2 tap |
|
694 |
+ |
|
695 |
+ $dom = $dom->tap(sub {...}); |
|
696 |
+ |
|
697 |
+Alias for L<Mojo::Base/"tap">. |
|
698 |
+ |
|
628 | 699 |
=head2 text |
629 | 700 |
|
630 | 701 |
my $trimmed = $dom->text; |
... | ... |
@@ -693,12 +764,12 @@ carefully since it is very dynamic. |
693 | 764 |
Element type. |
694 | 765 |
|
695 | 766 |
# List types of child elements |
696 |
- say $dom->children->pluck('type'); |
|
767 |
+ say $dom->children->type; |
|
697 | 768 |
|
698 | 769 |
=head2 xml |
699 | 770 |
|
700 |
- my $xml = $dom->xml; |
|
701 |
- $dom = $dom->xml(1); |
|
771 |
+ my $bool = $dom->xml; |
|
772 |
+ $dom = $dom->xml($bool); |
|
702 | 773 |
|
703 | 774 |
Disable HTML semantics in parser and activate case sensitivity, defaults to |
704 | 775 |
auto detection based on processing instructions. |
... | ... |
@@ -711,7 +782,7 @@ L<Mojo::Collection> object, depending on number of children. |
711 | 782 |
|
712 | 783 |
say $dom->p->text; |
713 | 784 |
say $dom->div->[23]->text; |
714 |
- say $dom->div->pluck('text'); |
|
785 |
+ say $dom->div->text; |
|
715 | 786 |
|
716 | 787 |
=head1 ELEMENT ATTRIBUTES |
717 | 788 |
|
... | ... |
@@ -3,14 +3,14 @@ use Mojo::Base -base; |
3 | 3 |
|
4 | 4 |
has 'tree'; |
5 | 5 |
|
6 |
-my $ESCAPE_RE = qr/\\[^[:xdigit:]]|\\[[:xdigit:]]{1,6}/; |
|
6 |
+my $ESCAPE_RE = qr/\\[^0-9a-fA-F]|\\[0-9a-fA-F]{1,6}/; |
|
7 | 7 |
my $ATTR_RE = qr/ |
8 | 8 |
\[ |
9 |
- ((?:$ESCAPE_RE|[\w\-])+) # Key |
|
9 |
+ ((?:$ESCAPE_RE|[\w\-])+) # Key |
|
10 | 10 |
(?: |
11 |
- (\W)? # Operator |
|
11 |
+ (\W)? # Operator |
|
12 | 12 |
= |
13 |
- (?:"((?:\\"|[^"])+)"|(\S+)) # Value |
|
13 |
+ (?:"((?:\\"|[^"])*)"|([^\]]+)) # Value |
|
14 | 14 |
)? |
15 | 15 |
\] |
16 | 16 |
/x; |
... | ... |
@@ -33,6 +33,13 @@ my $TOKEN_RE = qr/ |
33 | 33 |
)? |
34 | 34 |
/x; |
35 | 35 |
|
36 |
+sub match { |
|
37 |
+ my $self = shift; |
|
38 |
+ my $tree = $self->tree; |
|
39 |
+ return undef if $tree->[0] eq 'root'; |
|
40 |
+ return $self->_match($self->_compile(shift), $tree, $tree); |
|
41 |
+} |
|
42 |
+ |
|
36 | 43 |
sub select { |
37 | 44 |
my $self = shift; |
38 | 45 |
|
... | ... |
@@ -43,19 +50,14 @@ sub select { |
43 | 50 |
while (my $current = shift @queue) { |
44 | 51 |
my $type = $current->[0]; |
45 | 52 |
|
46 |
- # Root |
|
47 |
- if ($type eq 'root') { unshift @queue, @$current[1 .. $#$current] } |
|
48 |
- |
|
49 | 53 |
# Tag |
50 |
- elsif ($type eq 'tag') { |
|
54 |
+ if ($type eq 'tag') { |
|
51 | 55 |
unshift @queue, @$current[4 .. $#$current]; |
52 |
- |
|
53 |
- # Try all selectors with element |
|
54 |
- for my $part (@$pattern) { |
|
55 |
- push @results, $current and last |
|
56 |
- if $self->_combinator([reverse @$part], $current, $tree); |
|
57 |
- } |
|
56 |
+ push @results, $current if $self->_match($pattern, $current, $tree); |
|
58 | 57 |
} |
58 |
+ |
|
59 |
+ # Root |
|
60 |
+ elsif ($type eq 'root') { unshift @queue, @$current[1 .. $#$current] } |
|
59 | 61 |
} |
60 | 62 |
|
61 | 63 |
return \@results; |
... | ... |
@@ -125,7 +127,6 @@ sub _compile { |
125 | 127 |
my ($separator, $element, $pc, $attrs, $combinator) |
126 | 128 |
= ($1, defined $2 ? $2 : '', $3, $6, $11); |
127 | 129 |
|
128 |
- # Trash |
|
129 | 130 |
next unless $separator || $element || $pc || $attrs || $combinator; |
130 | 131 |
|
131 | 132 |
# New selector |
... | ... |
@@ -149,12 +150,8 @@ sub _compile { |
149 | 150 |
|
150 | 151 |
# Class or ID |
151 | 152 |
while ($element =~ /$CLASS_ID_RE/g) { |
152 |
- |
|
153 |
- # Class |
|
154 | 153 |
push @$selector, ['attr', 'class', $self->_regex('~', $1)] if defined $1; |
155 |
- |
|
156 |
- # ID |
|
157 |
- push @$selector, ['attr', 'id', $self->_regex('', $2)] if defined $2; |
|
154 |
+ push @$selector, ['attr', 'id', $self->_regex('', $2)] if defined $2; |
|
158 | 155 |
} |
159 | 156 |
|
160 | 157 |
# Pseudo classes |
... | ... |
@@ -187,28 +184,33 @@ sub _equation { |
187 | 184 |
my ($self, $equation) = @_; |
188 | 185 |
|
189 | 186 |
# "even" |
190 |
- my $num = [1, 1]; |
|
191 |
- if ($equation =~ /^even$/i) { $num = [2, 2] } |
|
187 |
+ return [2, 2] if $equation =~ /^even$/i; |
|
192 | 188 |
|
193 | 189 |
# "odd" |
194 |
- elsif ($equation =~ /^odd$/i) { $num = [2, 1] } |
|
190 |
+ return [2, 1] if $equation =~ /^odd$/i; |
|
195 | 191 |
|
196 | 192 |
# Equation |
197 |
- elsif ($equation =~ /(?:(-?(?:\d+)?)?(n))?\s*\+?\s*(-?\s*\d+)?\s*$/i) { |
|
198 |
- $num->[0] = defined($1) && length($1) ? $1 : $2 ? 1 : 0; |
|
199 |
- $num->[0] = -1 if $num->[0] eq '-'; |
|
200 |
- $num->[1] = defined $3 ? $3 : 0; |
|
201 |
- $num->[1] =~ s/\s+//g; |
|
202 |
- } |
|
203 |
- |
|
193 |
+ my $num = [1, 1]; |
|
194 |
+ return $num if $equation !~ /(?:(-?(?:\d+)?)?(n))?\s*\+?\s*(-?\s*\d+)?\s*$/i; |
|
195 |
+ $num->[0] = defined($1) && length($1) ? $1 : $2 ? 1 : 0; |
|
196 |
+ $num->[0] = -1 if $num->[0] eq '-'; |
|
197 |
+ $num->[1] = defined $3 ? $3 : 0; |
|
198 |
+ $num->[1] =~ s/\s+//g; |
|
204 | 199 |
return $num; |
205 | 200 |
} |
206 | 201 |
|
202 |
+sub _match { |
|
203 |
+ my ($self, $pattern, $current, $tree) = @_; |
|
204 |
+ $self->_combinator([reverse @$_], $current, $tree) and return 1 |
|
205 |
+ for @$pattern; |
|
206 |
+ return undef; |
|
207 |
+} |
|
208 |
+ |
|
207 | 209 |
sub _parent { |
208 | 210 |
my ($self, $selectors, $current, $tree) = @_; |
209 | 211 |
return undef unless my $parent = $current->[3]; |
210 | 212 |
return undef if $parent->[0] eq 'root'; |
211 |
- return $self->_combinator($selectors, $parent, $tree) ? 1 : undef; |
|
213 |
+ return $self->_combinator($selectors, $parent, $tree); |
|
212 | 214 |
} |
213 | 215 |
|
214 | 216 |
sub _pc { |
... | ... |
@@ -251,10 +253,9 @@ sub _pc { |
251 | 253 |
|
252 | 254 |
# Siblings |
253 | 255 |
my $parent = $current->[3]; |
254 |
- my $start = $parent->[0] eq 'root' ? 1 : 4; |
|
255 | 256 |
my @siblings; |
256 | 257 |
my $type = $class =~ /of-type$/ ? $current->[1] : undef; |
257 |
- for my $i ($start .. $#$parent) { |
|
258 |
+ for my $i (($parent->[0] eq 'root' ? 1 : 4) .. $#$parent) { |
|
258 | 259 |
my $sibling = $parent->[$i]; |
259 | 260 |
next unless $sibling->[0] eq 'tag'; |
260 | 261 |
next if defined $type && $type ne $sibling->[1]; |
... | ... |
@@ -279,8 +280,7 @@ sub _pc { |
279 | 280 |
|
280 | 281 |
# Siblings |
281 | 282 |
my $parent = $current->[3]; |
282 |
- my $start = $parent->[0] eq 'root' ? 1 : 4; |
|
283 |
- for my $i ($start .. $#$parent) { |
|
283 |
+ for my $i (($parent->[0] eq 'root' ? 1 : 4) .. $#$parent) { |
|
284 | 284 |
my $sibling = $parent->[$i]; |
285 | 285 |
next if $sibling->[0] ne 'tag' || $sibling eq $current; |
286 | 286 |
return undef unless defined $type && $sibling->[1] ne $type; |
... | ... |
@@ -345,8 +345,7 @@ sub _sibling { |
345 | 345 |
|
346 | 346 |
my $parent = $current->[3]; |
347 | 347 |
my $found; |
348 |
- my $start = $parent->[0] eq 'root' ? 1 : 4; |
|
349 |
- for my $e (@$parent[$start .. $#$parent]) { |
|
348 |
+ for my $e (@$parent[($parent->[0] eq 'root' ? 1 : 4) .. $#$parent]) { |
|
350 | 349 |
return $found if $e eq $current; |
351 | 350 |
next unless $e->[0] eq 'tag'; |
352 | 351 |
|
... | ... |
@@ -367,7 +366,7 @@ sub _unescape { |
367 | 366 |
$value =~ s/\\\n//g; |
368 | 367 |
|
369 | 368 |
# Unescape Unicode characters |
370 |
- $value =~ s/\\([[:xdigit:]]{1,6})\s?/pack('U', hex $1)/ge; |
|
369 |
+ $value =~ s/\\([0-9a-fA-F]{1,6})\s?/pack('U', hex $1)/ge; |
|
371 | 370 |
|
372 | 371 |
# Remove backslash |
373 | 372 |
$value =~ s/\\//g; |
... | ... |
@@ -377,6 +376,8 @@ sub _unescape { |
377 | 376 |
|
378 | 377 |
1; |
379 | 378 |
|
379 |
+=encoding utf8 |
|
380 |
+ |
|
380 | 381 |
=head1 NAME |
381 | 382 |
|
382 | 383 |
Mojo::DOM::CSS - CSS selector engine |
... | ... |
@@ -591,7 +592,7 @@ Elements of type C<E>, C<F> and C<G>. |
591 | 592 |
|
592 | 593 |
An C<E> element whose attributes match all following attribute selectors. |
593 | 594 |
|
594 |
- my $links = $css->select('a[foo^="b"][foo$="ar"]'); |
|
595 |
+ my $links = $css->select('a[foo^=b][foo$=ar]'); |
|
595 | 596 |
|
596 | 597 |
=head1 ATTRIBUTES |
597 | 598 |
|
... | ... |
@@ -610,11 +611,17 @@ carefully since it is very dynamic. |
610 | 611 |
L<Mojo::DOM::CSS> inherits all methods from L<Mojo::Base> and implements the |
611 | 612 |
following new ones. |
612 | 613 |
|
614 |
+=head2 match |
|
615 |
+ |
|
616 |
+ my $bool = $css->match('head > title'); |
|
617 |
+ |
|
618 |
+Match CSS selector against first node in L</"tree">. |
|
619 |
+ |
|
613 | 620 |
=head2 select |
614 | 621 |
|
615 | 622 |
my $results = $css->select('head > title'); |
616 | 623 |
|
617 |
-Run CSS selector against C<tree>. |
|
624 |
+Run CSS selector against L</"tree">. |
|
618 | 625 |
|
619 | 626 |
=head1 SEE ALSO |
620 | 627 |
|
... | ... |
@@ -8,7 +8,7 @@ has 'xml'; |
8 | 8 |
has tree => sub { ['root'] }; |
9 | 9 |
|
10 | 10 |
my $ATTR_RE = qr/ |
11 |
- ([^=\s>]+) # Key |
|
11 |
+ ([^<>=\s]+) # Key |
|
12 | 12 |
(?: |
13 | 13 |
\s*=\s* |
14 | 14 |
(?: |
... | ... |
@@ -40,50 +40,52 @@ my $TOKEN_RE = qr/ |
40 | 40 |
| |
41 | 41 |
<( |
42 | 42 |
\s* |
43 |
- [^>\s]+ # Tag |
|
43 |
+ [^<>\s]+ # Tag |
|
44 | 44 |
\s* |
45 | 45 |
(?:$ATTR_RE)* # Attributes |
46 | 46 |
)> |
47 |
+ | |
|
48 |
+ (<) # Runaway "<" |
|
47 | 49 |
)?? |
48 | 50 |
/xis; |
49 | 51 |
|
50 |
-# Optional HTML elements |
|
51 |
-my %OPTIONAL = map { $_ => 1 } |
|
52 |
- qw(body colgroup dd head li optgroup option p rt rp tbody td tfoot th); |
|
53 |
- |
|
54 |
-# Elements that break HTML paragraphs |
|
52 |
+# HTML elements that break paragraphs |
|
55 | 53 |
my %PARAGRAPH = map { $_ => 1 } ( |
56 | 54 |
qw(address article aside blockquote dir div dl fieldset footer form h1 h2), |
57 |
- qw(h3 h4 h5 h6 header hgroup hr menu nav ol p pre section table ul) |
|
55 |
+ qw(h3 h4 h5 h6 header hr main menu nav ol p pre section table ul) |
|
58 | 56 |
); |
59 | 57 |
|
60 |
-# HTML table elements |
|
61 |
-my %TABLE = map { $_ => 1 } qw(col colgroup tbody td th thead tr); |
|
58 |
+# HTML table elements with optional end tags |
|
59 |
+my %TABLE = map { $_ => 1 } qw(colgroup tbody td tfoot th thead tr); |
|
62 | 60 |
|
63 |
-# HTML void elements |
|
61 |
+# HTML elements without end tags |
|
64 | 62 |
my %VOID = map { $_ => 1 } ( |
65 |
- qw(area base br col command embed hr img input keygen link meta param), |
|
63 |
+ qw(area base br col embed hr img input keygen link menuitem meta param), |
|
66 | 64 |
qw(source track wbr) |
67 | 65 |
); |
68 | 66 |
|
69 |
-# HTML inline elements |
|
70 |
-my %INLINE = map { $_ => 1 } ( |
|
71 |
- qw(a abbr acronym applet b basefont bdo big br button cite code del dfn em), |
|
72 |
- qw(font i iframe img ins input kbd label map object q s samp script select), |
|
73 |
- qw(small span strike strong sub sup textarea tt u var) |
|
67 |
+# HTML elements categorized as phrasing content (and obsolete inline elements) |
|
68 |
+my @PHRASING = ( |
|
69 |
+ qw(a abbr area audio b bdi bdo br button canvas cite code data datalist), |
|
70 |
+ qw(del dfn em embed i iframe img input ins kbd keygen label link map mark), |
|
71 |
+ qw(math meta meter noscript object output progress q ruby s samp script), |
|
72 |
+ qw(select small span strong sub sup svg template textarea time u var video), |
|
73 |
+ qw(wbr) |
|
74 | 74 |
); |
75 |
+my @OBSOLETE = qw(acronym applet basefont big font strike tt); |
|
76 |
+my %PHRASING = map { $_ => 1 } @OBSOLETE, @PHRASING; |
|
75 | 77 |
|
76 | 78 |
sub parse { |
77 | 79 |
my ($self, $html) = @_; |
78 | 80 |
|
79 |
- my $tree = ['root']; |
|
80 |
- my $current = $tree; |
|
81 |
+ my $current = my $tree = ['root']; |
|
81 | 82 |
while ($html =~ m/\G$TOKEN_RE/gcs) { |
82 |
- my ($text, $pi, $comment, $cdata, $doctype, $tag) |
|
83 |
- = ($1, $2, $3, $4, $5, $6); |
|
83 |
+ my ($text, $pi, $comment, $cdata, $doctype, $tag, $runaway) |
|
84 |
+ = ($1, $2, $3, $4, $5, $6, $11); |
|
84 | 85 |
|
85 |
- # Text |
|
86 |
- if (length $text) { push @$current, ['text', html_unescape($text)] } |
|
86 |
+ # Text (and runaway "<") |
|
87 |
+ $text .= '<' if defined $runaway; |
|
88 |
+ push @$current, ['text', html_unescape $text] if length $text; |
|
87 | 89 |
|
88 | 90 |
# DOCTYPE |
89 | 91 |
if ($doctype) { push @$current, ['doctype', $doctype] } |
... | ... |
@@ -124,7 +126,7 @@ sub parse { |
124 | 126 |
# Tag |
125 | 127 |
$self->_start($start, \%attrs, \$current); |
126 | 128 |
|
127 |
- # Empty element |
|
129 |
+ # Element without end tag |
|
128 | 130 |
$self->_end($start, \$current) |
129 | 131 |
if (!$self->xml && $VOID{$start}) || $attr =~ m!/\s*$!; |
130 | 132 |
|
... | ... |
@@ -144,18 +146,12 @@ sub parse { |
144 | 146 |
sub render { $_[0]->_render($_[0]->tree) } |
145 | 147 |
|
146 | 148 |
sub _close { |
147 |
- my ($self, $current, $tags, $stop) = @_; |
|
148 |
- $tags ||= \%TABLE; |
|
149 |
- $stop ||= 'table'; |
|
149 |
+ my ($self, $current, $allowed, $scope) = @_; |
|
150 | 150 |
|
151 |
- # Check if parents need to be closed |
|
151 |
+ # Close allowed parent elements in scope |
|
152 | 152 |
my $parent = $$current; |
153 |
- while ($parent->[0] ne 'root' && $parent->[1] ne $stop) { |
|
154 |
- |
|
155 |
- # Close |
|
156 |
- $tags->{$parent->[1]} and $self->_end($parent->[1], $current); |
|
157 |
- |
|
158 |
- # Try next |
|
153 |
+ while ($parent->[0] ne 'root' && $parent->[1] ne $scope) { |
|
154 |
+ $self->_end($parent->[1], $current) if $allowed->{$parent->[1]}; |
|
159 | 155 |
$parent = $parent->[3]; |
160 | 156 |
} |
161 | 157 |
} |
... | ... |
@@ -171,10 +167,9 @@ sub _end { |
171 | 167 |
# Right tag |
172 | 168 |
++$found and last if $next->[1] eq $end; |
173 | 169 |
|
174 |
- # Inline elements can only cross other inline elements |
|
175 |
- return if !$self->xml && $INLINE{$end} && !$INLINE{$next->[1]}; |
|
170 |
+ # Phrasing content can only cross phrasing content |
|
171 |
+ return if !$self->xml && $PHRASING{$end} && !$PHRASING{$next->[1]}; |
|
176 | 172 |
|
177 |
- # Parent |
|
178 | 173 |
$next = $next->[3]; |
179 | 174 |
} |
180 | 175 |
|
... | ... |
@@ -189,11 +184,8 @@ sub _end { |
189 | 184 |
# Match |
190 | 185 |
if ($end eq $$current->[1]) { return $$current = $$current->[3] } |
191 | 186 |
|
192 |
- # Optional elements |
|
193 |
- elsif ($OPTIONAL{$$current->[1]}) { $self->_end($$current->[1], $current) } |
|
194 |
- |
|
195 | 187 |
# Table |
196 |
- elsif ($end eq 'table') { $self->_close($current) } |
|
188 |
+ elsif ($end eq 'table') { $self->_close($current, \%TABLE, $end) } |
|
197 | 189 |
|
198 | 190 |
# Missing end tag |
199 | 191 |
$self->_end($$current->[1], $current); |
... | ... |
@@ -211,19 +203,19 @@ sub _render { |
211 | 203 |
return $tree->[1] if $e eq 'raw'; |
212 | 204 |
|
213 | 205 |
# DOCTYPE |
214 |
- return "<!DOCTYPE" . $tree->[1] . ">" if $e eq 'doctype'; |
|
206 |
+ return '<!DOCTYPE' . $tree->[1] . '>' if $e eq 'doctype'; |
|
215 | 207 |
|
216 | 208 |
# Comment |
217 |
- return "<!--" . $tree->[1] . "-->" if $e eq 'comment'; |
|
209 |
+ return '<!--' . $tree->[1] . '-->' if $e eq 'comment'; |
|
218 | 210 |
|
219 | 211 |
# CDATA |
220 |
- return "<![CDATA[" . $tree->[1] . "]]>" if $e eq 'cdata'; |
|
212 |
+ return '<![CDATA[' . $tree->[1] . ']]>' if $e eq 'cdata'; |
|
221 | 213 |
|
222 | 214 |
# Processing instruction |
223 |
- return "<?" . $tree->[1] . "?>" if $e eq 'pi'; |
|
215 |
+ return '<?' . $tree->[1] . '?>' if $e eq 'pi'; |
|
224 | 216 |
|
225 | 217 |
# Start tag |
226 |
- my $start = $e eq 'root' ? 1 : 2; |
|
218 |
+ my $start = 1; |
|
227 | 219 |
my $content = ''; |
228 | 220 |
if ($e eq 'tag') { |
229 | 221 |
$start = 4; |
... | ... |
@@ -246,7 +238,7 @@ sub _render { |
246 | 238 |
my $attrs = join ' ', @attrs; |
247 | 239 |
$content .= " $attrs" if $attrs; |
248 | 240 |
|
249 |
- # Empty tag |
|
241 |
+ # Element without end tag |
|
250 | 242 |
return $self->xml || $VOID{$tag} ? "$content />" : "$content></$tag>" |
251 | 243 |
unless $tree->[4]; |
252 | 244 |
|
... | ... |
@@ -269,40 +261,40 @@ sub _start { |
269 | 261 |
# Autoclose optional HTML elements |
270 | 262 |
if (!$self->xml && $$current->[0] ne 'root') { |
271 | 263 |
|
272 |
- # "<li>" |
|
264 |
+ # "li" |
|
273 | 265 |
if ($start eq 'li') { $self->_close($current, {li => 1}, 'ul') } |
274 | 266 |
|
275 |
- # "<p>" |
|
267 |
+ # "p" |
|
276 | 268 |
elsif ($PARAGRAPH{$start}) { $self->_end('p', $current) } |
277 | 269 |
|
278 |
- # "<head>" |
|
270 |
+ # "head" |
|
279 | 271 |
elsif ($start eq 'body') { $self->_end('head', $current) } |
280 | 272 |
|
281 |
- # "<optgroup>" |
|
273 |
+ # "optgroup" |
|
282 | 274 |
elsif ($start eq 'optgroup') { $self->_end('optgroup', $current) } |
283 | 275 |
|
284 |
- # "<option>" |
|
276 |
+ # "option" |
|
285 | 277 |
elsif ($start eq 'option') { $self->_end('option', $current) } |
286 | 278 |
|
287 |
- # "<colgroup>", "<thead>", "tbody" and "tfoot" |
|
279 |
+ # "colgroup", "thead", "tbody" and "tfoot" |
|
288 | 280 |
elsif (grep { $_ eq $start } qw(colgroup thead tbody tfoot)) { |
289 |
- $self->_close($current); |
|
281 |
+ $self->_close($current, \%TABLE, 'table'); |
|
290 | 282 |
} |
291 | 283 |
|
292 |
- # "<tr>" |
|
293 |
- elsif ($start eq 'tr') { $self->_close($current, {tr => 1}) } |
|
284 |
+ # "tr" |
|
285 |
+ elsif ($start eq 'tr') { $self->_close($current, {tr => 1}, 'table') } |
|
294 | 286 |
|
295 |
- # "<th>" and "<td>" |
|
287 |
+ # "th" and "td" |
|
296 | 288 |
elsif ($start eq 'th' || $start eq 'td') { |
297 |
- $self->_close($current, {$_ => 1}) for qw(th td); |
|
289 |
+ $self->_close($current, {$_ => 1}, 'table') for qw(th td); |
|
298 | 290 |
} |
299 | 291 |
|
300 |
- # "<dt>" and "<dd>" |
|
292 |
+ # "dt" and "dd" |
|
301 | 293 |
elsif ($start eq 'dt' || $start eq 'dd') { |
302 | 294 |
$self->_end($_, $current) for qw(dt dd); |
303 | 295 |
} |
304 | 296 |
|
305 |
- # "<rt>" and "<rp>" |
|
297 |
+ # "rt" and "rp" |
|
306 | 298 |
elsif ($start eq 'rt' || $start eq 'rp') { |
307 | 299 |
$self->_end($_, $current) for qw(rt rp); |
308 | 300 |
} |
... | ... |
@@ -317,6 +309,8 @@ sub _start { |
317 | 309 |
|
318 | 310 |
1; |
319 | 311 |
|
312 |
+=encoding utf8 |
|
313 |
+ |
|
320 | 314 |
=head1 NAME |
321 | 315 |
|
322 | 316 |
Mojo::DOM::HTML - HTML/XML engine |
... | ... |
@@ -348,8 +342,8 @@ carefully since it is very dynamic. |
348 | 342 |
|
349 | 343 |
=head2 xml |
350 | 344 |
|
351 |
- my $xml = $html->xml; |
|
352 |
- $html = $html->xml(1); |
|
345 |
+ my $bool = $html->xml; |
|
346 |
+ $html = $html->xml($bool); |
|
353 | 347 |
|
354 | 348 |
Disable HTML semantics in parser and activate case sensitivity, defaults to |
355 | 349 |
auto detection based on processing instructions. |
... | ... |
@@ -363,7 +357,7 @@ following new ones. |
363 | 357 |
|
364 | 358 |
$html = $html->parse('<foo bar="baz">test</foo>'); |
365 | 359 |
|
366 |
-Parse HTML/XML document. |
|
360 |
+Parse HTML/XML fragment. |
|
367 | 361 |
|
368 | 362 |
=head2 render |
369 | 363 |
|
... | ... |
@@ -1,9 +1,6 @@ |
1 | 1 |
package Mojo::Date; |
2 | 2 |
use Mojo::Base -base; |
3 |
-use overload |
|
4 |
- 'bool' => sub {1}, |
|
5 |
- '""' => sub { shift->to_string }, |
|
6 |
- fallback => 1; |
|
3 |
+use overload bool => sub {1}, '""' => sub { shift->to_string }, fallback => 1; |
|
7 | 4 |
|
8 | 5 |
use Time::Local 'timegm'; |
9 | 6 |
|
... | ... |
@@ -63,6 +60,8 @@ sub to_string { |
63 | 60 |
|
64 | 61 |
1; |
65 | 62 |
|
63 |
+=encoding utf8 |
|
64 |
+ |
|
66 | 65 |
=head1 NAME |
67 | 66 |
|
68 | 67 |
Mojo::Date - HTTP date |
... | ... |
@@ -109,7 +108,7 @@ following new ones. |
109 | 108 |
my $date = Mojo::Date->new; |
110 | 109 |
my $date = Mojo::Date->new('Sun Nov 6 08:49:37 1994'); |
111 | 110 |
|
112 |
-Construct a new L<Mojo::Date> object and C<parse> date if necessary. |
|
111 |
+Construct a new L<Mojo::Date> object and L</"parse"> date if necessary. |
|
113 | 112 |
|
114 | 113 |
=head2 parse |
115 | 114 |
|
... | ... |
@@ -9,12 +9,12 @@ sub emit { |
9 | 9 |
my ($self, $name) = (shift, shift); |
10 | 10 |
|
11 | 11 |
if (my $s = $self->{events}{$name}) { |
12 |
- warn "-- Emit $name in @{[blessed($self)]} (@{[scalar(@$s)]})\n" if DEBUG; |
|
12 |
+ warn "-- Emit $name in @{[blessed $self]} (@{[scalar @$s]})\n" if DEBUG; |
|
13 | 13 |
for my $cb (@$s) { $self->$cb(@_) } |
14 | 14 |
} |
15 | 15 |
else { |
16 |
- warn "-- Emit $name in @{[blessed($self)]} (0)\n" if DEBUG; |
|
17 |
- warn $_[0] if $name eq 'error'; |
|
16 |
+ warn "-- Emit $name in @{[blessed $self]} (0)\n" if DEBUG; |
|
17 |
+ die "@{[blessed $self]}: $_[0]" if $name eq 'error'; |
|
18 | 18 |
} |
19 | 19 |
|
20 | 20 |
return $self; |
... | ... |
@@ -24,22 +24,16 @@ sub emit_safe { |
24 | 24 |
my ($self, $name) = (shift, shift); |
25 | 25 |
|
26 | 26 |
if (my $s = $self->{events}{$name}) { |
27 |
- warn "-- Emit $name in @{[blessed($self)]} safely (@{[scalar(@$s)]})\n" |
|
27 |
+ warn "-- Emit $name in @{[blessed $self]} safely (@{[scalar @$s]})\n" |
|
28 | 28 |
if DEBUG; |
29 | 29 |
for my $cb (@$s) { |
30 |
- unless (eval { $self->$cb(@_); 1 }) { |
|
31 |
- |
|
32 |
- # Error event failed |
|
33 |
- if ($name eq 'error') { warn qq{Event "error" failed: $@} } |
|
34 |
- |
|
35 |
- # Normal event failed |
|
36 |
- else { $self->emit_safe('error', qq{Event "$name" failed: $@}) } |
|
37 |
- } |
|
30 |
+ $self->emit(error => qq{Event "$name" failed: $@}) |
|
31 |
+ unless eval { $self->$cb(@_); 1 }; |
|
38 | 32 |
} |
39 | 33 |
} |
40 | 34 |
else { |
41 |
- warn "-- Emit $name in @{[blessed($self)]} safely (0)\n" if DEBUG; |
|
42 |
- warn $_[0] if $name eq 'error'; |
|
35 |
+ warn "-- Emit $name in @{[blessed $self]} safely (0)\n" if DEBUG; |
|
36 |
+ die "@{[blessed $self]}: $_[0]" if $name eq 'error'; |
|
43 | 37 |
} |
44 | 38 |
|
45 | 39 |
return $self; |
... | ... |
@@ -76,6 +70,7 @@ sub unsubscribe { |
76 | 70 |
# One |
77 | 71 |
if ($cb) { |
78 | 72 |
$self->{events}{$name} = [grep { $cb ne $_ } @{$self->{events}{$name}}]; |
73 |
+ delete $self->{events}{$name} unless @{$self->{events}{$name}}; |
|
79 | 74 |
} |
80 | 75 |
|
81 | 76 |
# All |
... | ... |
@@ -86,6 +81,8 @@ sub unsubscribe { |
86 | 81 |
|
87 | 82 |
1; |
88 | 83 |
|
84 |
+=encoding utf8 |
|
85 |
+ |
|
89 | 86 |
=head1 NAME |
90 | 87 |
|
91 | 88 |
Mojo::EventEmitter - Event emitter base class |
... | ... |
@@ -126,7 +123,7 @@ L<Mojo::EventEmitter> can emit the following events. |
126 | 123 |
... |
127 | 124 |
}); |
128 | 125 |
|
129 |
-Emitted safely for event errors. |
|
126 |
+Emitted for event errors, fatal if unhandled. |
|
130 | 127 |
|
131 | 128 |
$e->on(error => sub { |
132 | 129 |
my ($e, $err) = @_; |
... | ... |
@@ -150,11 +147,11 @@ Emit event. |
150 | 147 |
$e = $e->emit_safe('foo'); |
151 | 148 |
$e = $e->emit_safe('foo', 123); |
152 | 149 |
|
153 |
-Emit event safely and emit C<error> event on failure. |
|
150 |
+Emit event safely and emit L</"error"> event on failure. |
|
154 | 151 |
|
155 | 152 |
=head2 has_subscribers |
156 | 153 |
|
157 |
- my $success = $e->has_subscribers('foo'); |
|
154 |
+ my $bool = $e->has_subscribers('foo'); |
|
158 | 155 |
|
159 | 156 |
Check if event has subscribers. |
160 | 157 |
|
... | ... |
@@ -1,9 +1,6 @@ |
1 | 1 |
package Mojo::Exception; |
2 | 2 |
use Mojo::Base -base; |
3 |
-use overload |
|
4 |
- 'bool' => sub {1}, |
|
5 |
- '""' => sub { shift->to_string }, |
|
6 |
- fallback => 1; |
|
3 |
+use overload bool => sub {1}, '""' => sub { shift->to_string }, fallback => 1; |
|
7 | 4 |
|
8 | 5 |
use Scalar::Util 'blessed'; |
9 | 6 |
|
... | ... |
@@ -46,40 +43,36 @@ sub trace { |
46 | 43 |
} |
47 | 44 |
|
48 | 45 |
sub _context { |
49 |
- my ($self, $line, $lines) = @_; |
|
50 |
- |
|
51 |
- # Wrong file |
|
52 |
- return unless defined $lines->[0][$line - 1]; |
|
46 |
+ my ($self, $num, $lines) = @_; |
|
53 | 47 |
|
54 | 48 |
# Line |
55 |
- $self->line([$line]); |
|
56 |
- for my $l (@$lines) { |
|
57 |
- chomp(my $code = $l->[$line - 1]); |
|
49 |
+ return unless defined $lines->[0][$num - 1]; |
|
50 |
+ $self->line([$num]); |
|
51 |
+ for my $line (@$lines) { |
|
52 |
+ chomp(my $code = $line->[$num - 1]); |
|
58 | 53 |
push @{$self->line}, $code; |
59 | 54 |
} |
60 | 55 |
|
61 | 56 |
# Before |
62 | 57 |
for my $i (2 .. 6) { |
63 |
- last if ((my $previous = $line - $i) < 0); |
|
64 |
- if (defined $lines->[0][$previous]) { |
|
65 |
- unshift @{$self->lines_before}, [$previous + 1]; |
|
66 |
- for my $l (@$lines) { |
|
67 |
- chomp(my $code = $l->[$previous]); |
|
68 |
- push @{$self->lines_before->[0]}, $code; |
|
69 |
- } |
|
58 |
+ last if ((my $previous = $num - $i) < 0); |
|
59 |
+ next unless defined $lines->[0][$previous]; |
|
60 |
+ unshift @{$self->lines_before}, [$previous + 1]; |
|
61 |
+ for my $line (@$lines) { |
|
62 |
+ chomp(my $code = $line->[$previous]); |
|
63 |
+ push @{$self->lines_before->[0]}, $code; |
|
70 | 64 |
} |
71 | 65 |
} |
72 | 66 |
|
73 | 67 |
# After |
74 | 68 |
for my $i (0 .. 4) { |
75 |
- next if ((my $next = $line + $i) < 0); |
|
76 |
- if (defined $lines->[0][$next]) { |
|
77 |
- push @{$self->lines_after}, [$next + 1]; |
|
78 |
- for my $l (@$lines) { |
|
79 |
- next unless defined(my $code = $l->[$next]); |
|
80 |
- chomp $code; |
|
81 |
- push @{$self->lines_after->[-1]}, $code; |
|
82 |
- } |
|
69 |
+ next if ((my $next = $num + $i) < 0); |
|
70 |
+ next unless defined $lines->[0][$next]; |
|
71 |
+ push @{$self->lines_after}, [$next + 1]; |
|
72 |
+ for my $line (@$lines) { |
|
73 |
+ last unless defined(my $code = $line->[$next]); |
|
74 |
+ chomp $code; |
|
75 |
+ push @{$self->lines_after->[-1]}, $code; |
|
83 | 76 |
} |
84 | 77 |
} |
85 | 78 |
} |
... | ... |
@@ -113,6 +106,8 @@ sub _detect { |
113 | 106 |
|
114 | 107 |
1; |
115 | 108 |
|
109 |
+=encoding utf8 |
|
110 |
+ |
|
116 | 111 |
=head1 NAME |
117 | 112 |
|
118 | 113 |
Mojo::Exception - Exceptions with context |
... | ... |
@@ -172,8 +167,8 @@ Exception message. |
172 | 167 |
|
173 | 168 |
=head2 verbose |
174 | 169 |
|
175 |
- my $verbose = $e->verbose; |
|
176 |
- $e = $e->verbose(1); |
|
170 |
+ my $bool = $e->verbose; |
|
171 |
+ $e = $e->verbose($bool); |
|
177 | 172 |
|
178 | 173 |
Render exception with context. |
179 | 174 |
|
... | ... |
@@ -8,13 +8,13 @@ has max_line_size => sub { $ENV{MOJO_MAX_LINE_SIZE} || 10240 }; |
8 | 8 |
# Common headers |
9 | 9 |
my @HEADERS = ( |
10 | 10 |
qw(Accept Accept-Charset Accept-Encoding Accept-Language Accept-Ranges), |
11 |
- qw(Authorization Cache-Control Connection Content-Disposition), |
|
11 |
+ qw(Allow Authorization Cache-Control Connection Content-Disposition), |
|
12 | 12 |
qw(Content-Encoding Content-Length Content-Range Content-Type Cookie DNT), |
13 |
- qw(Date ETag Expect Expires Host If-Modified-Since Last-Modified Location), |
|
14 |
- qw(Origin Proxy-Authenticate Proxy-Authorization Range), |
|
13 |
+ qw(Date ETag Expect Expires Host If-Modified-Since Last-Modified Link), |
|
14 |
+ qw(Location Origin Proxy-Authenticate Proxy-Authorization Range), |
|
15 | 15 |
qw(Sec-WebSocket-Accept Sec-WebSocket-Extensions Sec-WebSocket-Key), |
16 | 16 |
qw(Sec-WebSocket-Protocol Sec-WebSocket-Version Server Set-Cookie Status), |
17 |
- qw(TE Trailer Transfer-Encoding Upgrade User-Agent WWW-Authenticate) |
|
17 |
+ qw(TE Trailer Transfer-Encoding Upgrade User-Agent Vary WWW-Authenticate) |
|
18 | 18 |
); |
19 | 19 |
for my $header (@HEADERS) { |
20 | 20 |
my $name = lc $header; |
... | ... |
@@ -22,7 +22,7 @@ for my $header (@HEADERS) { |
22 | 22 |
monkey_patch __PACKAGE__, $name, sub { scalar shift->header($header => @_) }; |
23 | 23 |
} |
24 | 24 |
|
25 |
-# Lower case headers |
|
25 |
+# Lowercase headers |
|
26 | 26 |
my %NORMALCASE = map { lc($_) => $_ } @HEADERS; |
27 | 27 |
|
28 | 28 |
sub add { |
... | ... |
@@ -38,6 +38,12 @@ sub add { |
38 | 38 |
return $self; |
39 | 39 |
} |
40 | 40 |
|
41 |
+sub append { |
|
42 |
+ my ($self, $name, $value) = @_; |
|
43 |
+ my $old = $self->header($name); |
|
44 |
+ return $self->header($name => defined $old ? "$old, $value" : $value); |
|
45 |
+} |
|
46 |
+ |
|
41 | 47 |
sub clone { |
42 | 48 |
my $self = shift; |
43 | 49 |
return $self->new->from_hash($self->to_hash(1)); |
... | ... |
@@ -149,6 +155,8 @@ sub to_string { |
149 | 155 |
|
150 | 156 |
1; |
151 | 157 |
|
158 |
+=encoding utf8 |
|
159 |
+ |
|
152 | 160 |
=head1 NAME |
153 | 161 |
|
154 | 162 |
Mojo::Headers - Headers |
... | ... |
@@ -234,6 +242,29 @@ Shortcut for the C<Accept-Ranges> header. |
234 | 242 |
|
235 | 243 |
Add one or more header values with one or more lines. |
236 | 244 |
|
245 |
+ # "Vary: Accept" |
|
246 |
+ # "Vary: Accept-Encoding" |
|
247 |
+ $headers->vary('Accept')->add(Vary => 'Accept-Encoding')->to_string; |
|
248 |
+ |
|
249 |
+=head2 allow |
|
250 |
+ |
|
251 |
+ my $allow = $headers->allow; |
|
252 |
+ $headers = $headers->allow('GET, POST'); |
|
253 |
+ |
|
254 |
+Shortcut for the C<Allow> header. |
|
255 |
+ |
|
256 |
+=head2 append |
|
257 |
+ |
|
258 |
+ $headers = $headers->append(Vary => 'Accept-Encoding'); |
|
259 |
+ |
|
260 |
+Append value to header and flatten it if necessary. |
|
261 |
+ |
|
262 |
+ # "Vary: Accept" |
|
263 |
+ $headers->append(Vary => 'Accept')->to_string; |
|
264 |
+ |
|
265 |
+ # "Vary: Accept, Accept-Encoding" |
|
266 |
+ $headers->vary('Accept')->append(Vary => 'Accept-Encoding')->to_string; |
|
267 |
+ |
|
237 | 268 |
=head2 authorization |
238 | 269 |
|
239 | 270 |
my $authorization = $headers->authorization; |
... | ... |
@@ -380,13 +411,13 @@ Shortcut for the C<If-Modified-Since> header. |
380 | 411 |
|
381 | 412 |
=head2 is_finished |
382 | 413 |
|
383 |
- my $success = $headers->is_finished; |
|
414 |
+ my $bool = $headers->is_finished; |
|
384 | 415 |
|
385 | 416 |
Check if header parser is finished. |
386 | 417 |
|
387 | 418 |
=head2 is_limit_exceeded |
388 | 419 |
|
389 |
- my $success = $headers->is_limit_exceeded; |
|
420 |
+ my $bool = $headers->is_limit_exceeded; |
|
390 | 421 |
|
391 | 422 |
Check if a header has exceeded C<max_line_size>. |
392 | 423 |
|
... | ... |
@@ -403,6 +434,13 @@ Shortcut for the C<Last-Modified> header. |
403 | 434 |
|
404 | 435 |
Get leftover data from header parser. |
405 | 436 |
|
437 |
+=head2 link |
|
438 |
+ |
|
439 |
+ my $link = $headers->link; |
|
440 |
+ $headers = $headers->link('<http://127.0.0.1/foo/3>; rel="next"'); |
|
441 |
+ |
|
442 |
+Shortcut for the C<Link> header from RFC 5988. |
|
443 |
+ |
|
406 | 444 |
=head2 location |
407 | 445 |
|
408 | 446 |
my $location = $headers->location; |
... | ... |
@@ -414,7 +452,10 @@ Shortcut for the C<Location> header. |
414 | 452 |
|
415 | 453 |
my $names = $headers->names; |
416 | 454 |
|
417 |
-Generate a list of all currently defined headers. |
|
455 |
+Return a list of all currently defined headers. |
|
456 |
+ |
|
457 |
+ # Names of all headers |
|
458 |
+ say for @{$headers->names}; |
|
418 | 459 |
|
419 | 460 |
=head2 origin |
420 | 461 |
|
... | ... |
@@ -571,6 +612,13 @@ Shortcut for the C<Upgrade> header. |
571 | 612 |
|
572 | 613 |
Shortcut for the C<User-Agent> header. |
573 | 614 |
|
615 |
+=head2 vary |
|
616 |
+ |
|
617 |
+ my $vary = $headers->vary; |
|
618 |
+ $headers = $headers->vary('*'); |
|
619 |
+ |
|
620 |
+Shortcut for the C<Vary> header. |
|
621 |
+ |
|
574 | 622 |
=head2 www_authenticate |
575 | 623 |
|
576 | 624 |
my $authenticate = $headers->www_authenticate; |
... | ... |
@@ -7,6 +7,8 @@ any '/*whatever' => {whatever => '', text => 'Your Mojo is working!'}; |
7 | 7 |
|
8 | 8 |
1; |
9 | 9 |
|
10 |
+=encoding utf8 |
|
11 |
+ |
|
10 | 12 |
=head1 NAME |
11 | 13 |
|
12 | 14 |
Mojo::HelloWorld - Hello World! |
... | ... |
@@ -1,9 +1,6 @@ |
1 | 1 |
package Mojo::Home; |
2 | 2 |
use Mojo::Base -base; |
3 |
-use overload |
|
4 |
- 'bool' => sub {1}, |
|
5 |
- '""' => sub { shift->to_string }, |
|
6 |
- fallback => 1; |
|
3 |
+use overload bool => sub {1}, '""' => sub { shift->to_string }, fallback => 1; |
|
7 | 4 |
|
8 | 5 |
use Cwd 'abs_path'; |
9 | 6 |
use File::Basename 'dirname'; |
... | ... |
@@ -12,16 +9,15 @@ use File::Spec::Functions qw(abs2rel catdir catfile splitdir); |
12 | 9 |
use FindBin; |
13 | 10 |
use Mojo::Util qw(class_to_path slurp); |
14 | 11 |
|
12 |
+has parts => sub { [] }; |
|
13 |
+ |
|
15 | 14 |
sub new { shift->SUPER::new->parse(@_) } |
16 | 15 |
|
17 | 16 |
sub detect { |
18 | 17 |
my $self = shift; |
19 | 18 |
|
20 | 19 |
# Environment variable |
21 |
- if ($ENV{MOJO_HOME}) { |
|
22 |
- $self->{parts} = [splitdir(abs_path $ENV{MOJO_HOME})]; |
|
23 |
- return $self; |
|
24 |
- } |
|
20 |
+ return $self->parts([splitdir(abs_path $ENV{MOJO_HOME})]) if $ENV{MOJO_HOME}; |
|
25 | 21 |
|
26 | 22 |
# Try to find home from lib directory |
27 | 23 |
if (my $class = @_ ? shift : 'Mojo::HelloWorld') { |
... | ... |
@@ -34,28 +30,23 @@ sub detect { |
34 | 30 |
pop @home while @home && ($home[-1] =~ /^b?lib$/ || $home[-1] eq ''); |
35 | 31 |
|
36 | 32 |
# Turn into absolute path |
37 |
- $self->{parts} = [splitdir(abs_path(catdir(@home) || '.'))]; |
|
33 |
+ return $self->parts([splitdir(abs_path(catdir(@home) || '.'))]); |
|
38 | 34 |
} |
39 | 35 |
} |
40 | 36 |
|
41 | 37 |
# FindBin fallback |
42 |
- $self->{parts} = [split /\//, $FindBin::Bin] unless $self->{parts}; |
|
43 |
- |
|
44 |
- return $self; |
|
38 |
+ return $self->parts([split /\//, $FindBin::Bin]); |
|
45 | 39 |
} |
46 | 40 |
|
47 | 41 |
sub lib_dir { |
48 |
- my $path = catdir @{shift->{parts} || []}, 'lib'; |
|
42 |
+ my $path = catdir @{shift->parts}, 'lib'; |
|
49 | 43 |
return -d $path ? $path : undef; |
50 | 44 |
} |
51 | 45 |
|
52 | 46 |
sub list_files { |
53 | 47 |
my ($self, $dir) = @_; |
54 | 48 |
|
55 |
- # Files relative to directory |
|
56 |
- my $parts = $self->{parts} || []; |
|
57 |
- my $root = catdir @$parts; |
|
58 |
- $dir = catdir $root, split '/', ($dir || ''); |
|
49 |
+ $dir = catdir @{$self->parts}, split '/', (defined $dir ? $dir : ''); |
|
59 | 50 |
return [] unless -d $dir; |
60 | 51 |
my @files; |
61 | 52 |
find { |
... | ... |
@@ -73,17 +64,18 @@ sub mojo_lib_dir { catdir(dirname(__FILE__), '..') } |
73 | 64 |
|
74 | 65 |
sub parse { |
75 | 66 |
my ($self, $path) = @_; |
76 |
- $self->{parts} = [splitdir $path] if defined $path; |
|
77 |
- return $self; |
|
67 |
+ return defined $path ? $self->parts([splitdir $path]) : $self; |
|
78 | 68 |
} |
79 | 69 |
|
80 |
-sub rel_dir { catdir(@{shift->{parts} || []}, split '/', shift) } |
|
81 |
-sub rel_file { catfile(@{shift->{parts} || []}, split '/', shift) } |
|
70 |
+sub rel_dir { catdir(@{shift->parts}, split '/', shift) } |
|
71 |
+sub rel_file { catfile(@{shift->parts}, split '/', shift) } |
|
82 | 72 |
|
83 |
-sub to_string { catdir(@{shift->{parts} || []}) } |
|
73 |
+sub to_string { catdir(@{shift->parts}) } |
|
84 | 74 |
|
85 | 75 |
1; |
86 | 76 |
|
77 |
+=encoding utf8 |
|
78 |
+ |
|
87 | 79 |
=head1 NAME |
88 | 80 |
|
89 | 81 |
Mojo::Home - Home sweet home! |
... | ... |
@@ -103,6 +95,17 @@ Mojo::Home - Home sweet home! |
103 | 95 |
|
104 | 96 |
L<Mojo::Home> is a container for home directories. |
105 | 97 |
|
98 |
+=head1 ATTRIBUTES |
|
99 |
+ |
|
100 |
+L<Mojo::Home> implements the following attributes. |
|
101 |
+ |
|
102 |
+=head2 parts |
|
103 |
+ |
|
104 |
+ my $parts = $home->parts; |
|
105 |
+ $home = $home->parts([]); |
|
106 |
+ |
|
107 |
+Home directory parts. |
|
108 |
+ |
|
106 | 109 |
=head1 METHODS |
107 | 110 |
|
108 | 111 |
L<Mojo::Home> inherits all methods from L<Mojo::Base> and implements the |
... | ... |
@@ -113,7 +116,8 @@ following new ones. |
113 | 116 |
my $home = Mojo::Home->new; |
114 | 117 |
my $home = Mojo::Home->new('/home/sri/myapp'); |
115 | 118 |
|
116 |
-Construct a new L<Mojo::Home> object and C<parse> home directory if necessary. |
|
119 |
+Construct a new L<Mojo::Home> object and L</"parse"> home directory if |
|
120 |
+necessary. |
|
117 | 121 |
|
118 | 122 |
=head2 detect |
119 | 123 |
|
... | ... |
@@ -10,7 +10,7 @@ use Mojo::IOLoop::Server; |
10 | 10 |
use Mojo::IOLoop::Stream; |
11 | 11 |
use Mojo::Reactor::Poll; |
12 | 12 |
use Mojo::Util qw(md5_sum steady_time); |
13 |
-use Scalar::Util 'weaken'; |
|
13 |
+use Scalar::Util qw(blessed weaken); |
|
14 | 14 |
|
15 | 15 |
use constant DEBUG => $ENV{MOJO_IOLOOP_DEBUG} || 0; |
16 | 16 |
|
... | ... |
@@ -22,7 +22,9 @@ has multi_accept => 50; |
22 | 22 |
has reactor => sub { |
23 | 23 |
my $class = Mojo::Reactor::Poll->detect; |
24 | 24 |
warn "-- Reactor initialized ($class)\n" if DEBUG; |
25 |
- return $class->new; |
|
25 |
+ my $reactor = $class->new; |
|
26 |
+ $reactor->on(error => sub { warn "@{[blessed $_[0]]}: $_[1]" }); |
|
27 |
+ return $reactor; |
|
26 | 28 |
}; |
27 | 29 |
|
28 | 30 |
# Ignore PIPE signal |
... | ... |
@@ -32,8 +34,7 @@ $SIG{PIPE} = 'IGNORE'; |
32 | 34 |
__PACKAGE__->singleton->reactor; |
33 | 35 |
|
34 | 36 |
sub acceptor { |
35 |
- my ($self, $acceptor) = @_; |
|
36 |
- $self = $self->singleton unless ref $self; |
|
37 |
+ my ($self, $acceptor) = (_instance(shift), @_); |
|
37 | 38 |
|
38 | 39 |
# Find acceptor for id |
39 | 40 |
return $self->{acceptors}{$acceptor} unless ref $acceptor; |
... | ... |
@@ -44,35 +45,28 @@ sub acceptor { |
44 | 45 |
weaken $acceptor->reactor($self->reactor)->{reactor}; |
45 | 46 |
$self->{accepts} = $self->max_accepts if $self->max_accepts; |
46 | 47 |
|
47 |
- # Stop accepting so new acceptor can get picked up |
|
48 |
+ # Allow new acceptor to get picked up |
|
48 | 49 |
$self->_not_accepting; |
49 | 50 |
|
50 | 51 |
return $id; |
51 | 52 |
} |
52 | 53 |
|
53 | 54 |
sub client { |
54 |
- my ($self, $cb) = (shift, pop); |
|
55 |
- $self = $self->singleton unless ref $self; |
|
55 |
+ my ($self, $cb) = (_instance(shift), pop); |
|
56 | 56 |
|
57 | 57 |
# Make sure timers are running |
58 |
- $self->_timers; |
|
58 |
+ $self->_recurring; |
|
59 | 59 |
|
60 |
- my $id = $self->_id; |
|
61 |
- my $c = $self->{connections}{$id} ||= {}; |
|
62 |
- my $client = $c->{client} = Mojo::IOLoop::Client->new; |
|
60 |
+ my $id = $self->_id; |
|
61 |
+ my $client = $self->{connections}{$id}{client} = Mojo::IOLoop::Client->new; |
|
63 | 62 |
weaken $client->reactor($self->reactor)->{reactor}; |
64 | 63 |
|
65 | 64 |
weaken $self; |
66 | 65 |
$client->on( |
67 | 66 |
connect => sub { |
68 |
- my $handle = pop; |
|
69 |
- |
|
70 |
- # Turn handle into stream |
|
71 |
- my $c = $self->{connections}{$id}; |
|
72 |
- delete $c->{client}; |
|
73 |
- my $stream = $c->{stream} = Mojo::IOLoop::Stream->new($handle); |
|
67 |
+ delete $self->{connections}{$id}{client}; |
|
68 |
+ my $stream = Mojo::IOLoop::Stream->new(pop); |
|
74 | 69 |
$self->_stream($stream => $id); |
75 |
- |
|
76 | 70 |
$self->$cb(undef, $stream); |
77 | 71 |
} |
78 | 72 |
); |
... | ... |
@@ -88,8 +82,7 @@ sub client { |
88 | 82 |
} |
89 | 83 |
|
90 | 84 |
sub delay { |
91 |
- my $self = shift; |
|
92 |
- $self = $self->singleton unless ref $self; |
|
85 |
+ my $self = _instance(shift); |
|
93 | 86 |
|
94 | 87 |
my $delay = Mojo::IOLoop::Delay->new; |
95 | 88 |
weaken $delay->ioloop($self)->{ioloop}; |
... | ... |
@@ -103,42 +96,24 @@ sub generate_port { Mojo::IOLoop::Server->generate_port } |
103 | 96 |
sub is_running { (ref $_[0] ? $_[0] : $_[0]->singleton)->reactor->is_running } |
104 | 97 |
sub one_tick { (ref $_[0] ? $_[0] : $_[0]->singleton)->reactor->one_tick } |
105 | 98 |
|
106 |
-sub recurring { |
|
107 |
- my ($self, $after, $cb) = @_; |
|
108 |
- $self = $self->singleton unless ref $self; |
|
109 |
- weaken $self; |
|
110 |
- return $self->reactor->recurring($after => sub { $self->$cb }); |
|
111 |
-} |
|
99 |
+sub recurring { shift->_timer(recurring => @_) } |
|
112 | 100 |
|
113 | 101 |
sub remove { |
114 |
- my ($self, $id) = @_; |
|
115 |
- $self = $self->singleton unless ref $self; |
|
102 |
+ my ($self, $id) = (_instance(shift), @_); |
|
116 | 103 |
my $c = $self->{connections}{$id}; |
117 | 104 |
if ($c && (my $stream = $c->{stream})) { return $stream->close_gracefully } |
118 | 105 |
$self->_remove($id); |
119 | 106 |
} |
120 | 107 |
|
121 | 108 |
sub server { |
122 |
- my ($self, $cb) = (shift, pop); |
|
123 |
- $self = $self->singleton unless ref $self; |
|
109 |
+ my ($self, $cb) = (_instance(shift), pop); |
|
124 | 110 |
|
125 | 111 |
my $server = Mojo::IOLoop::Server->new; |
126 | 112 |
weaken $self; |
127 | 113 |
$server->on( |
128 | 114 |
accept => sub { |
129 |
- my $handle = pop; |
|
130 |
- |
|
131 |
- # Turn handle into stream |
|
132 |
- my $stream = Mojo::IOLoop::Stream->new($handle); |
|
115 |
+ my $stream = Mojo::IOLoop::Stream->new(pop); |
|
133 | 116 |
$self->$cb($stream, $self->stream($stream)); |
134 |
- |
|
135 |
- # Enforce connection limit (randomize to improve load balancing) |
|
136 |
- $self->max_connections(0) |
|
137 |
- if defined $self->{accepts} |
|
138 |
- && ($self->{accepts} -= int(rand 2) + 1) <= 0; |
|
139 |
- |
|
140 |
- # Stop accepting to release accept mutex |
|
141 |
- $self->_not_accepting; |
|
142 | 117 |
} |
143 | 118 |
); |
144 | 119 |
$server->listen(@_); |
... | ... |
@@ -158,24 +133,23 @@ sub start { |
158 | 133 |
sub stop { (ref $_[0] ? $_[0] : $_[0]->singleton)->reactor->stop } |
159 | 134 |
|
160 | 135 |
sub stream { |
161 |
- my ($self, $stream) = @_; |
|
162 |
- $self = $self->singleton unless ref $self; |
|
163 |
- |
|
164 |
- # Connect stream with reactor |
|
165 |
- return $self->_stream($stream, $self->_id) if ref $stream; |
|
136 |
+ my ($self, $stream) = (_instance(shift), @_); |
|
166 | 137 |
|
167 | 138 |
# Find stream for id |
168 |
- return undef unless my $c = $self->{connections}{$stream}; |
|
169 |
- return $c->{stream}; |
|
170 |
-} |
|
139 |
+ return ($self->{connections}{$stream} || {})->{stream} unless ref $stream; |
|
171 | 140 |
|
172 |
-sub timer { |
|
173 |
- my ($self, $after, $cb) = @_; |
|
174 |
- $self = $self->singleton unless ref $self; |
|
175 |
- weaken $self; |
|
176 |
- return $self->reactor->timer($after => sub { $self->$cb }); |
|
141 |
+ # Release accept mutex |
|
142 |
+ $self->_not_accepting; |
|
143 |
+ |
|
144 |
+ # Enforce connection limit (randomize to improve load balancing) |
|
145 |
+ $self->max_connections(0) |
|
146 |
+ if defined $self->{accepts} && ($self->{accepts} -= int(rand 2) + 1) <= 0; |
|
147 |
+ |
|
148 |
+ return $self->_stream($stream, $self->_id); |
|
177 | 149 |
} |
178 | 150 |
|
151 |
+sub timer { shift->_timer(timer => @_) } |
|
152 |
+ |
|
179 | 153 |
sub _accepting { |
180 | 154 |
my $self = shift; |
181 | 155 |
|
... | ... |
@@ -206,11 +180,13 @@ sub _id { |
206 | 180 |
return $id; |
207 | 181 |
} |
208 | 182 |
|
183 |
+sub _instance { ref $_[0] ? $_[0] : $_[0]->singleton } |
|
184 |
+ |
|
209 | 185 |
sub _not_accepting { |
210 | 186 |
my $self = shift; |
211 | 187 |
|
212 | 188 |
# Make sure timers are running |
213 |
- $self->_timers; |
|
189 |
+ $self->_recurring; |
|
214 | 190 |
|
215 | 191 |
# Release accept mutex |
216 | 192 |
return unless delete $self->{accepting}; |
... | ... |
@@ -220,6 +196,12 @@ sub _not_accepting { |
220 | 196 |
$_->stop for values %{$self->{acceptors} || {}}; |
221 | 197 |
} |
222 | 198 |
|
199 |
+sub _recurring { |
|
200 |
+ my $self = shift; |
|
201 |
+ $self->{accept} ||= $self->recurring($self->accept_interval => \&_accepting); |
|
202 |
+ $self->{stop} ||= $self->recurring(1 => \&_stop); |
|
203 |
+} |
|
204 |
+ |
|
223 | 205 |
sub _remove { |
224 | 206 |
my ($self, $id) = @_; |
225 | 207 |
|
... | ... |
@@ -246,7 +228,7 @@ sub _stream { |
246 | 228 |
my ($self, $stream, $id) = @_; |
247 | 229 |
|
248 | 230 |
# Make sure timers are running |
249 |
- $self->_timers; |
|
231 |
+ $self->_recurring; |
|
250 | 232 |
|
251 | 233 |
# Connect stream with reactor |
252 | 234 |
$self->{connections}{$id}{stream} = $stream; |
... | ... |
@@ -258,14 +240,16 @@ sub _stream { |
258 | 240 |
return $id; |
259 | 241 |
} |
260 | 242 |
|
261 |
-sub _timers { |
|
262 |
- my $self = shift; |
|
263 |
- $self->{accept} ||= $self->recurring($self->accept_interval => \&_accepting); |
|
264 |
- $self->{stop} ||= $self->recurring(1 => \&_stop); |
|
243 |
+sub _timer { |
|
244 |
+ my ($self, $method, $after, $cb) = (_instance(shift), @_); |
|
245 |
+ weaken $self; |
|
246 |
+ return $self->reactor->$method($after => sub { $self->$cb }); |
|
265 | 247 |
} |
266 | 248 |
|
267 | 249 |
1; |
268 | 250 |
|
251 |
+=encoding utf8 |
|
252 |
+ |
|
269 | 253 |
=head1 NAME |
270 | 254 |
|
271 | 255 |
Mojo::IOLoop - Minimalistic event loop |
... | ... |
@@ -319,17 +303,18 @@ L<Mojo::IOLoop> is a very minimalistic event loop based on L<Mojo::Reactor>, |
319 | 303 |
it has been reduced to the absolute minimal feature set required to build |
320 | 304 |
solid and scalable non-blocking TCP clients and servers. |
321 | 305 |
|
322 |
-Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
|
323 |
-L<IO::Socket::SSL> (1.75+) are supported transparently, and used if installed. |
|
324 |
-Individual features can also be disabled with the MOJO_NO_IPV6 and MOJO_NO_TLS |
|
325 |
-environment variables. |
|
326 |
- |
|
327 | 306 |
The event loop will be resilient to time jumps if a monotonic clock is |
328 | 307 |
available through L<Time::HiRes>. A TLS certificate and key are also built |
329 | 308 |
right in, to make writing test servers as easy as possible. Also note that for |
330 | 309 |
convenience the C<PIPE> signal will be set to C<IGNORE> when L<Mojo::IOLoop> |
331 | 310 |
is loaded. |
332 | 311 |
|
312 |
+For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS |
|
313 |
+support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
|
314 |
+L<IO::Socket::SSL> (1.75+) will be used automatically if they are installed. |
|
315 |
+Individual features can also be disabled with the MOJO_NO_IPV6 and MOJO_NO_TLS |
|
316 |
+environment variables. |
|
317 |
+ |
|
333 | 318 |
See L<Mojolicious::Guides::Cookbook> for more. |
334 | 319 |
|
335 | 320 |
=head1 ATTRIBUTES |
... | ... |
@@ -396,7 +381,7 @@ Number of connections to accept at once, defaults to C<50>. |
396 | 381 |
$loop = $loop->reactor(Mojo::Reactor->new); |
397 | 382 |
|
398 | 383 |
Low level event reactor, usually a L<Mojo::Reactor::Poll> or |
399 |
-L<Mojo::Reactor::EV> object. |
|
384 |
+L<Mojo::Reactor::EV> object with a default C<error> event. |
|
400 | 385 |
|
401 | 386 |
# Watch if handle becomes readable or writable |
402 | 387 |
$loop->reactor->io($handle => sub { |
... | ... |
@@ -464,6 +449,7 @@ event, and multiple ones as a chain of steps. |
464 | 449 |
$end->(); |
465 | 450 |
}); |
466 | 451 |
} |
452 |
+ $delay->wait unless Mojo::IOLoop->is_running; |
|
467 | 453 |
|
468 | 454 |
# Sequentialize multiple events |
469 | 455 |
my $delay = Mojo::IOLoop->delay( |
... | ... |
@@ -486,8 +472,6 @@ event, and multiple ones as a chain of steps. |
486 | 472 |
# Third step (the end) |
487 | 473 |
sub { say 'And done after 5 seconds total.' } |
488 | 474 |
); |
489 |
- |
|
490 |
- # Wait for events if necessary |
|
491 | 475 |
$delay->wait unless Mojo::IOLoop->is_running; |
492 | 476 |
|
493 | 477 |
=head2 generate_port |
... | ... |
@@ -499,8 +483,8 @@ Find a free TCP port, this is a utility function primarily used for tests. |
499 | 483 |
|
500 | 484 |
=head2 is_running |
501 | 485 |
|
502 |
- my $success = Mojo::IOLoop->is_running; |
|
503 |
- my $success = $loop->is_running; |
|
486 |
+ my $bool = Mojo::IOLoop->is_running; |
|
487 |
+ my $bool = $loop->is_running; |
|
504 | 488 |
|
505 | 489 |
Check if event loop is running. |
506 | 490 |
|
... | ... |
@@ -514,6 +498,11 @@ Check if event loop is running. |
514 | 498 |
Run event loop until an event occurs. Note that this method can recurse back |
515 | 499 |
into the reactor, so you need to be careful. |
516 | 500 |
|
501 |
+ # Don't block longer than 0.5 seconds |
|
502 |
+ my $id = Mojo::IOLoop->timer(0.5 => sub {}); |
|
503 |
+ Mojo::IOLoop->one_tick; |
|
504 |
+ Mojo::IOLoop->remove($id); |
|
505 |
+ |
|
517 | 506 |
=head2 recurring |
518 | 507 |
|
519 | 508 |
my $id = Mojo::IOLoop->recurring(0.5 => sub {...}); |
... | ... |
@@ -559,13 +548,17 @@ loop object from everywhere inside the process. |
559 | 548 |
Mojo::IOLoop->timer(2 => sub { Mojo::IOLoop->stop }); |
560 | 549 |
Mojo::IOLoop->start; |
561 | 550 |
|
551 |
+ # Restart active timer |
|
552 |
+ my $id = Mojo::IOLoop->timer(3 => sub { say 'Timeout!' }); |
|
553 |
+ Mojo::IOLoop->singleton->reactor->again($id); |
|
554 |
+ |
|
562 | 555 |
=head2 start |
563 | 556 |
|
564 | 557 |
Mojo::IOLoop->start; |
565 | 558 |
$loop->start; |
566 | 559 |
|
567 |
-Start the event loop, this will block until C<stop> is called. Note that some |
|
568 |
-reactors stop automatically if there are no events being watched anymore. |
|
560 |
+Start the event loop, this will block until L</"stop"> is called. Note that |
|
561 |
+some reactors stop automatically if there are no events being watched anymore. |
|
569 | 562 |
|
570 | 563 |
# Start event loop only if it is not running already |
571 | 564 |
Mojo::IOLoop->start unless Mojo::IOLoop->is_running; |
... | ... |
@@ -576,7 +569,7 @@ reactors stop automatically if there are no events being watched anymore. |
576 | 569 |
$loop->stop; |
577 | 570 |
|
578 | 571 |
Stop the event loop, this will not interrupt any existing connections and the |
579 |
-event loop can be restarted by running C<start> again. |
|
572 |
+event loop can be restarted by running L</"start"> again. |
|
580 | 573 |
|
581 | 574 |
=head2 stream |
582 | 575 |
|
... | ... |
@@ -34,7 +34,7 @@ sub connect { |
34 | 34 |
|
35 | 35 |
sub _cleanup { |
36 | 36 |
my $self = shift; |
37 |
- return $self unless my $reactor = $self->{reactor}; |
|
37 |
+ return $self unless my $reactor = $self->reactor; |
|
38 | 38 |
$self->{$_} && $reactor->remove(delete $self->{$_}) |
39 | 39 |
for qw(delay timer handle); |
40 | 40 |
return $self; |
... | ... |
@@ -50,18 +50,17 @@ sub _connect { |
50 | 50 |
my %options = ( |
51 | 51 |
Blocking => 0, |
52 | 52 |
PeerAddr => $address eq 'localhost' ? '127.0.0.1' : $address, |
53 |
- PeerPort => $args->{port} || ($args->{tls} ? 443 : 80), |
|
54 |
- Proto => 'tcp' |
|
53 |
+ PeerPort => $args->{port} || ($args->{tls} ? 443 : 80) |
|
55 | 54 |
); |
56 | 55 |
$options{LocalAddr} = $args->{local_address} if $args->{local_address}; |
57 | 56 |
$options{PeerAddr} =~ s/[\[\]]//g if $options{PeerAddr}; |
58 | 57 |
my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET'; |
59 |
- return $self->emit_safe(error => "Couldn't connect") |
|
58 |
+ return $self->emit(error => "Couldn't connect: $@") |
|
60 | 59 |
unless $self->{handle} = $handle = $class->new(%options); |
61 | 60 |
|
62 | 61 |
# Timeout |
63 | 62 |
$self->{timer} = $reactor->timer($args->{timeout} || 10, |
64 |
- sub { $self->emit_safe(error => 'Connect timeout') }); |
|
63 |
+ sub { $self->emit(error => 'Connect timeout') }); |
|
65 | 64 |
} |
66 | 65 |
$handle->blocking(0); |
67 | 66 |
|
... | ... |
@@ -73,16 +72,15 @@ sub _connect { |
73 | 72 |
sub _tls { |
74 | 73 |
my $self = shift; |
75 | 74 |
|
76 |
- # Switch between reading and writing |
|
75 |
+ # Connected |
|
77 | 76 |
my $handle = $self->{handle}; |
78 |
- if ($self->{tls} && !$handle->connect_SSL) { |
|
79 |
- my $err = $IO::Socket::SSL::SSL_ERROR; |
|
80 |
- if ($err == TLS_READ) { $self->reactor->watch($handle, 1, 0) } |
|
81 |
- elsif ($err == TLS_WRITE) { $self->reactor->watch($handle, 1, 1) } |
|
82 |
- return; |
|
83 |
- } |
|
77 |
+ return $self->_cleanup->emit_safe(connect => $handle) |
|
78 |
+ if $handle->connect_SSL; |
|
84 | 79 |
|
85 |
- $self->_cleanup->emit_safe(connect => $handle); |
|
80 |
+ # Switch between reading and writing |
|
81 |
+ my $err = $IO::Socket::SSL::SSL_ERROR; |
|
82 |
+ if ($err == TLS_READ) { $self->reactor->watch($handle, 1, 0) } |
|
83 |
+ elsif ($err == TLS_WRITE) { $self->reactor->watch($handle, 1, 1) } |
|
86 | 84 |
} |
87 | 85 |
|
88 | 86 |
sub _try { |
... | ... |
@@ -90,47 +88,44 @@ sub _try { |
90 | 88 |
|
91 | 89 |
# Retry or handle exceptions |
92 | 90 |
my $handle = $self->{handle}; |
93 |
- return $! == EINPROGRESS ? undef : $self->emit_safe(error => $!) |
|
91 |
+ return $! == EINPROGRESS ? undef : $self->emit(error => $!) |
|
94 | 92 |
if IPV6 && !$handle->connect; |
95 |
- return $self->emit_safe(error => $! = $handle->sockopt(SO_ERROR)) |
|
93 |
+ return $self->emit(error => $! = $handle->sockopt(SO_ERROR)) |
|
96 | 94 |
if !IPV6 && !$handle->connected; |
97 | 95 |
|
98 | 96 |
# Disable Nagle's algorithm |
99 | 97 |
setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1; |
100 | 98 |
|
101 |
- # TLS |
|
102 |
- if ($args->{tls} && !$handle->isa('IO::Socket::SSL')) { |
|
103 |
- return $self->emit_safe( |
|
104 |
- error => 'IO::Socket::SSL 1.75 required for TLS support') |
|
105 |
- unless TLS; |
|
106 |
- |
|
107 |
- # Upgrade |
|
108 |
- weaken $self; |
|
109 |
- my %options = ( |
|
110 |
- SSL_ca_file => $args->{tls_ca} |
|
111 |
- && -T $args->{tls_ca} ? $args->{tls_ca} : undef, |
|
112 |
- SSL_cert_file => $args->{tls_cert}, |
|
113 |
- SSL_error_trap => sub { $self->_cleanup->emit_safe(error => $_[1]) }, |
|
114 |
- SSL_hostname => $args->{address}, |
|
115 |
- SSL_key_file => $args->{tls_key}, |
|
116 |
- SSL_startHandshake => 0, |
|
117 |
- SSL_verify_mode => $args->{tls_ca} ? 0x01 : 0x00, |
|
118 |
- SSL_verifycn_name => $args->{address}, |
|
119 |
- SSL_verifycn_scheme => $args->{tls_ca} ? 'http' : undef |
|
120 |
- ); |
|
121 |
- $self->{tls} = 1; |
|
122 |
- my $reactor = $self->reactor; |
|
123 |
- $reactor->remove($handle); |
|
124 |
- return $self->emit_safe(error => 'TLS upgrade failed') |
|
125 |
- unless $handle = IO::Socket::SSL->start_SSL($handle, %options); |
|
126 |
- return $reactor->io($handle => sub { $self->_tls })->watch($handle, 0, 1); |
|
127 |
- } |
|
99 |
+ return $self->_cleanup->emit_safe(connect => $handle) |
|
100 |
+ if !$args->{tls} || $handle->isa('IO::Socket::SSL'); |
|
101 |
+ return $self->emit(error => 'IO::Socket::SSL 1.75 required for TLS support') |
|
102 |
+ unless TLS; |
|
128 | 103 |
|
129 |
- $self->_cleanup->emit_safe(connect => $handle); |
|
104 |
+ # Upgrade |
|
105 |
+ weaken $self; |
|
106 |
+ my %options = ( |
|
107 |
+ SSL_ca_file => $args->{tls_ca} |
|
108 |
+ && -T $args->{tls_ca} ? $args->{tls_ca} : undef, |
|
109 |
+ SSL_cert_file => $args->{tls_cert}, |
|
110 |
+ SSL_error_trap => sub { $self->_cleanup->emit(error => $_[1]) }, |
|
111 |
+ SSL_hostname => $args->{address}, |
|
112 |
+ SSL_key_file => $args->{tls_key}, |
|
113 |
+ SSL_startHandshake => 0, |
|
114 |
+ SSL_verify_mode => $args->{tls_ca} ? 0x01 : 0x00, |
|
115 |
+ SSL_verifycn_name => $args->{address}, |
|
116 |
+ SSL_verifycn_scheme => $args->{tls_ca} ? 'http' : undef |
|
117 |
+ ); |
|
118 |
+ my $reactor = $self->reactor; |
|
119 |
+ $reactor->remove($handle); |
|
120 |
+ return $self->emit(error => 'TLS upgrade failed') |
|
121 |
+ unless $handle = IO::Socket::SSL->start_SSL($handle, %options); |
|
122 |
+ $reactor->io($handle => sub { $self->_tls })->watch($handle, 0, 1); |
|
130 | 123 |
} |
131 | 124 |
|
132 | 125 |
1; |
133 | 126 |
|
127 |
+=encoding utf8 |
|
128 |
+ |
|
134 | 129 |
=head1 NAME |
135 | 130 |
|
136 | 131 |
Mojo::IOLoop::Client - Non-blocking TCP client |
... | ... |
@@ -179,7 +174,7 @@ Emitted safely once the connection is established. |
179 | 174 |
... |
180 | 175 |
}); |
181 | 176 |
|
182 |
-Emitted safely if an error occurs on the connection. |
|
177 |
+Emitted if an error occurs on the connection, fatal if unhandled. |
|
183 | 178 |
|
184 | 179 |
=head1 ATTRIBUTES |
185 | 180 |
|
... | ... |
@@ -211,39 +206,57 @@ These options are currently available: |
211 | 206 |
|
212 | 207 |
=item address |
213 | 208 |
|
209 |
+ address => 'mojolicio.us' |
|
210 |
+ |
|
214 | 211 |
Address or host name of the peer to connect to, defaults to C<localhost>. |
215 | 212 |
|
216 | 213 |
=item handle |
217 | 214 |
|
215 |
+ handle => $handle |
|
216 |
+ |
|
218 | 217 |
Use an already prepared handle. |
219 | 218 |
|
220 | 219 |
=item local_address |
221 | 220 |
|
221 |
+ local_address => '127.0.0.1' |
|
222 |
+ |
|
222 | 223 |
Local address to bind to. |
223 | 224 |
|
224 | 225 |
=item port |
225 | 226 |
|
226 |
-Port to connect to. |
|
227 |
+ port => 80 |
|
228 |
+ |
|
229 |
+Port to connect to, defaults to C<80> or C<443> with C<tls> option. |
|
227 | 230 |
|
228 | 231 |
=item timeout |
229 | 232 |
|
233 |
+ timeout => 15 |
|
234 |
+ |
|
230 | 235 |
Maximum amount of time in seconds establishing connection may take before |
231 | 236 |
getting canceled, defaults to C<10>. |
232 | 237 |
|
233 | 238 |
=item tls |
234 | 239 |
|
240 |
+ tls => 1 |
|
241 |
+ |
|
235 | 242 |
Enable TLS. |
236 | 243 |
|
237 | 244 |
=item tls_ca |
238 | 245 |
|
246 |
+ tls_ca => '/etc/tls/ca.crt' |
|
247 |
+ |
|
239 | 248 |
Path to TLS certificate authority file. Also activates hostname verification. |
240 | 249 |
|
241 | 250 |
=item tls_cert |
242 | 251 |
|
252 |
+ tls_cert => '/etc/tls/client.crt' |
|
253 |
+ |
|
243 | 254 |
Path to the TLS certificate file. |
244 | 255 |
|
245 | 256 |
=item tls_key |
246 | 257 |
|
258 |
+ tls_key => '/etc/tls/client.key' |
|
259 |
+ |
|
247 | 260 |
Path to the TLS key file. |
248 | 261 |
|
249 | 262 |
=back |
... | ... |
@@ -15,36 +15,44 @@ sub begin { |
15 | 15 |
sub steps { |
16 | 16 |
my $self = shift; |
17 | 17 |
$self->{steps} = [@_]; |
18 |
- $self->begin->(); |
|
18 |
+ $self->ioloop->timer(0 => $self->begin); |
|
19 | 19 |
return $self; |
20 | 20 |
} |
21 | 21 |
|
22 | 22 |
sub wait { |
23 | 23 |
my $self = shift; |
24 |
+ |
|
24 | 25 |
my @args; |
26 |
+ $self->once(error => \&_die); |
|
25 | 27 |
$self->once(finish => sub { shift->ioloop->stop; @args = @_ }); |
26 | 28 |
$self->ioloop->start; |
29 |
+ |
|
27 | 30 |
return wantarray ? @args : $args[0]; |
28 | 31 |
} |
29 | 32 |
|
33 |
+sub _die { $_[0]->has_subscribers('error') ? $_[0]->ioloop->stop : die $_[1] } |
|
34 |
+ |
|
30 | 35 |
sub _step { |
31 | 36 |
my ($self, $id) = (shift, shift); |
32 | 37 |
|
33 | 38 |
$self->{args}[$id] = [@_]; |
34 |
- return $self->{pending} if --$self->{pending} || $self->{lock}; |
|
39 |
+ return if $self->{fail} || --$self->{pending} || $self->{lock}; |
|
35 | 40 |
local $self->{lock} = 1; |
36 | 41 |
my @args = map {@$_} @{delete $self->{args}}; |
37 | 42 |
|
38 | 43 |
$self->{counter} = 0; |
39 |
- if (my $cb = shift @{$self->{steps} ||= []}) { $self->$cb(@args) } |
|
44 |
+ if (my $cb = shift @{$self->{steps} ||= []}) { |
|
45 |
+ eval { $self->$cb(@args); 1 } or return $self->emit(error => $@)->{fail}++; |
|
46 |
+ } |
|
40 | 47 |
|
41 |
- if (!$self->{counter}) { $self->emit(finish => @args) } |
|
42 |
- elsif (!$self->{pending}) { $self->ioloop->timer(0 => $self->begin) } |
|
43 |
- return 0; |
|
48 |
+ return $self->emit(finish => @args) unless $self->{counter}; |
|
49 |
+ $self->ioloop->timer(0 => $self->begin) unless $self->{pending}; |
|
44 | 50 |
} |
45 | 51 |
|
46 | 52 |
1; |
47 | 53 |
|
54 |
+=encoding utf8 |
|
55 |
+ |
|
48 | 56 |
=head1 NAME |
49 | 57 |
|
50 | 58 |
Mojo::IOLoop::Delay - Manage callbacks and control the flow of events |
... | ... |
@@ -63,6 +71,7 @@ Mojo::IOLoop::Delay - Manage callbacks and control the flow of events |
63 | 71 |
$end->(); |
64 | 72 |
}); |
65 | 73 |
} |
74 |
+ $delay->wait unless Mojo::IOLoop->is_running; |
|
66 | 75 |
|
67 | 76 |
# Sequentialize multiple events |
68 | 77 |
my $delay = Mojo::IOLoop::Delay->new; |
... | ... |
@@ -89,8 +98,6 @@ Mojo::IOLoop::Delay - Manage callbacks and control the flow of events |
89 | 98 |
say 'And done after 5 seconds total.'; |
90 | 99 |
} |
91 | 100 |
); |
92 |
- |
|
93 |
- # Wait for events if necessary |
|
94 | 101 |
$delay->wait unless Mojo::IOLoop->is_running; |
95 | 102 |
|
96 | 103 |
=head1 DESCRIPTION |
... | ... |
@@ -103,6 +110,16 @@ L<Mojo::IOLoop>. |
103 | 110 |
L<Mojo::IOLoop::Delay> inherits all events from L<Mojo::EventEmitter> and can |
104 | 111 |
emit the following new ones. |
105 | 112 |
|
113 |
+=head2 error |
|
114 |
+ |
|
115 |
+ $delay->on(error => sub { |
|
116 |
+ my ($delay, $err) = @_; |
|
117 |
+ ... |
|
118 |
+ }); |
|
119 |
+ |
|
120 |
+Emitted if an error occurs in one of the steps, breaking the chain, fatal if |
|
121 |
+unhandled. |
|
122 |
+ |
|
106 | 123 |
=head2 finish |
107 | 124 |
|
108 | 125 |
$delay->on(finish => sub { |
... | ... |
@@ -132,13 +149,13 @@ implements the following new ones. |
132 | 149 |
|
133 | 150 |
=head2 begin |
134 | 151 |
|
135 |
- my $cb = $delay->begin; |
|
136 |
- my $cb = $delay->begin(0); |
|
152 |
+ my $without_first_arg = $delay->begin; |
|
153 |
+ my $with_first_arg = $delay->begin(0); |
|
137 | 154 |
|
138 | 155 |
Increment active event counter, the returned callback can be used to decrement |
139 | 156 |
the active event counter again. Arguments passed to the callback are queued in |
140 |
-the right order for the next step or C<finish> event and C<wait> method, the |
|
141 |
-first argument will be ignored by default. |
|
157 |
+the right order for the next step or L</"finish"> event and L</"wait"> method, |
|
158 |
+the first argument will be ignored by default. |
|
142 | 159 |
|
143 | 160 |
# Capture all arguments |
144 | 161 |
my $delay = Mojo::IOLoop->delay; |
... | ... |
@@ -150,16 +167,17 @@ first argument will be ignored by default. |
150 | 167 |
$delay = $delay->steps(sub {...}, sub {...}); |
151 | 168 |
|
152 | 169 |
Sequentialize multiple events, the first callback will run right away, and the |
153 |
-next one once the active event counter reaches zero, this chain will continue |
|
154 |
-until there are no more callbacks or a callback does not increment the active |
|
155 |
-event counter. |
|
170 |
+next one once the active event counter reaches zero. This chain will continue |
|
171 |
+until there are no more callbacks, a callback does not increment the active |
|
172 |
+event counter or an error occurs in a callback. |
|
156 | 173 |
|
157 | 174 |
=head2 wait |
158 | 175 |
|
176 |
+ my $arg = $delay->wait; |
|
159 | 177 |
my @args = $delay->wait; |
160 | 178 |
|
161 |
-Start C<ioloop> and stop it again once the C<finish> event gets emitted, only |
|
162 |
-works when C<ioloop> is not running already. |
|
179 |
+Start L</"ioloop"> and stop it again once an L</"error"> or L</"finish"> event |
|
180 |
+gets emitted, only works when L</"ioloop"> is not running already. |
|
163 | 181 |
|
164 | 182 |
# Use the "finish" event to synchronize portably |
165 | 183 |
$delay->on(finish => sub { |
... | ... |
@@ -34,11 +34,17 @@ has reactor => sub { |
34 | 34 |
sub DESTROY { |
35 | 35 |
my $self = shift; |
36 | 36 |
if (my $port = $self->{port}) { $ENV{MOJO_REUSE} =~ s/(?:^|\,)${port}:\d+// } |
37 |
- return unless my $reactor = $self->{reactor}; |
|
37 |
+ return unless my $reactor = $self->reactor; |
|
38 | 38 |
$self->stop if $self->{handle}; |
39 | 39 |
$reactor->remove($_) for values %{$self->{handles}}; |
40 | 40 |
} |
41 | 41 |
|
42 |
+sub generate_port { |
|
43 |
+ IO::Socket::INET->new(Listen => 5, LocalAddr => '127.0.0.1')->sockport; |
|
44 |
+} |
|
45 |
+ |
|
46 |
+sub handle { shift->{handle} } |
|
47 |
+ |
|
42 | 48 |
sub listen { |
43 | 49 |
my $self = shift; |
44 | 50 |
my $args = ref $_[0] ? $_[0] : {@_}; |
... | ... |
@@ -56,8 +62,8 @@ sub listen { |
56 | 62 |
my $handle; |
57 | 63 |
my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET'; |
58 | 64 |
if (defined $fd) { |
59 |
- $handle = $class->new; |
|
60 |
- $handle->fdopen($fd, 'r') or croak "Can't open file descriptor $fd: $!"; |
|
65 |
+ $handle = $class->new_from_fd($fd, 'r') |
|
66 |
+ or croak "Can't open file descriptor $fd: $!"; |
|
61 | 67 |
} |
62 | 68 |
|
63 | 69 |
# New socket |
... | ... |
@@ -66,12 +72,12 @@ sub listen { |
66 | 72 |
Listen => defined $args->{backlog} ? $args->{backlog} : SOMAXCONN, |
67 | 73 |
LocalAddr => $args->{address} || '0.0.0.0', |
68 | 74 |
LocalPort => $port, |
69 |
- Proto => 'tcp', |
|
70 | 75 |
ReuseAddr => 1, |
76 |
+ ReusePort => $args->{reuse}, |
|
71 | 77 |
Type => SOCK_STREAM |
72 | 78 |
); |
73 | 79 |
$options{LocalAddr} =~ s/[\[\]]//g; |
74 |
- $handle = $class->new(%options) or croak "Can't create listen socket: $!"; |
|
80 |
+ $handle = $class->new(%options) or croak "Can't create listen socket: $@"; |
|
75 | 81 |
$fd = fileno $handle; |
76 | 82 |
$ENV{MOJO_REUSE} .= length $ENV{MOJO_REUSE} ? ",$reuse:$fd" : "$reuse:$fd"; |
77 | 83 |
} |
... | ... |
@@ -81,25 +87,18 @@ sub listen { |
81 | 87 |
return unless $args->{tls}; |
82 | 88 |
croak "IO::Socket::SSL 1.75 required for TLS support" unless TLS; |
83 | 89 |
|
84 |
- # Options (Prioritize RC4 to mitigate BEAST attack) |
|
85 |
- my $options = $self->{tls} = { |
|
90 |
+ # Prioritize RC4 to mitigate BEAST attack |
|
91 |
+ $self->{tls} = { |
|
92 |
+ SSL_ca_file => $args->{tls_ca} |
|
93 |
+ && -T $args->{tls_ca} ? $args->{tls_ca} : undef, |
|
86 | 94 |
SSL_cert_file => $args->{tls_cert} || $CERT, |
87 |
- SSL_cipher_list => |
|
88 |
- '!aNULL:!eNULL:!EXPORT:!DSS:!DES:!SSLv2:!LOW:RC4-SHA:RC4-MD5:ALL', |
|
95 |
+ SSL_cipher_list => defined $args->{tls_ciphers} ? $args->{tls_ciphers} |
|
96 |
+ : 'ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH', |
|
89 | 97 |
SSL_honor_cipher_order => 1, |
90 | 98 |
SSL_key_file => $args->{tls_key} || $KEY, |
91 | 99 |
SSL_startHandshake => 0, |
92 |
- SSL_verify_mode => 0x00 |
|
100 |
+ SSL_verify_mode => defined $args->{tls_verify} ? $args->{tls_verify} : $args->{tls_ca} ? 0x03 : 0x00 |
|
93 | 101 |
}; |
94 |
- return unless $args->{tls_ca}; |
|
95 |
- $options->{SSL_ca_file} = -T $args->{tls_ca} ? $args->{tls_ca} : undef; |
|
96 |
- $options->{SSL_verify_mode} |
|
97 |
- = defined $args->{tls_verify} ? $args->{tls_verify} : 0x03; |
|
98 |
-} |
|
99 |
- |
|
100 |
-sub generate_port { |
|
101 |
- IO::Socket::INET->new(Listen => 5, LocalAddr => '127.0.0.1', Proto => 'tcp') |
|
102 |
- ->sockport; |
|
103 | 102 |
} |
104 | 103 |
|
105 | 104 |
sub start { |
... | ... |
@@ -139,8 +138,7 @@ sub _tls { |
139 | 138 |
# Accepted |
140 | 139 |
if ($handle->accept_SSL) { |
141 | 140 |
$self->reactor->remove($handle); |
142 |
- delete $self->{handles}{$handle}; |
|
143 |
- return $self->emit_safe(accept => $handle); |
|
141 |
+ return $self->emit_safe(accept => delete $self->{handles}{$handle}); |
|
144 | 142 |
} |
145 | 143 |
|
146 | 144 |
# Switch between reading and writing |
... | ... |
@@ -151,6 +149,8 @@ sub _tls { |
151 | 149 |
|
152 | 150 |
1; |
153 | 151 |
|
152 |
+=encoding utf8 |
|
153 |
+ |
|
154 | 154 |
=head1 NAME |
155 | 155 |
|
156 | 156 |
Mojo::IOLoop::Server - Non-blocking TCP server |
... | ... |
@@ -216,6 +216,18 @@ global L<Mojo::IOLoop> singleton. |
216 | 216 |
L<Mojo::IOLoop::Server> inherits all methods from L<Mojo::EventEmitter> and |
217 | 217 |
implements the following new ones. |
218 | 218 |
|
219 |
+=head2 generate_port |
|
220 |
+ |
|
221 |
+ my $port = $server->generate_port; |
|
222 |
+ |
|
223 |
+Find a free TCP port, this is a utility function primarily used for tests. |
|
224 |
+ |
|
225 |
+=head2 handle |
|
226 |
+ |
|
227 |
+ my $handle = $server->handle; |
|
228 |
+ |
|
229 |
+Get handle for server. |
|
230 |
+ |
|
219 | 231 |
=head2 listen |
220 | 232 |
|
221 | 233 |
$server->listen(port => 3000); |
... | ... |
@@ -229,44 +241,67 @@ These options are currently available: |
229 | 241 |
|
230 | 242 |
=item address |
231 | 243 |
|
244 |
+ address => '127.0.0.1' |
|
245 |
+ |
|
232 | 246 |
Local address to listen on, defaults to all. |
233 | 247 |
|
234 | 248 |
=item backlog |
235 | 249 |
|
250 |
+ backlog => 128 |
|
251 |
+ |
|
236 | 252 |
Maximum backlog size, defaults to C<SOMAXCONN>. |
237 | 253 |
|
238 | 254 |
=item port |
239 | 255 |
|
256 |
+ port => 80 |
|
257 |
+ |
|
240 | 258 |
Port to listen on. |
241 | 259 |
|
260 |
+=item reuse |
|
261 |
+ |
|
262 |
+ reuse => 1 |
|
263 |
+ |
|
264 |
+Allow multiple servers to use the same port with the C<SO_REUSEPORT> socket |
|
265 |
+option. |
|
266 |
+ |
|
242 | 267 |
=item tls |
243 | 268 |
|
269 |
+ tls => 1 |
|
270 |
+ |
|
244 | 271 |
Enable TLS. |
245 | 272 |
|
246 | 273 |
=item tls_ca |
247 | 274 |
|
275 |
+ tls_ca => '/etc/tls/ca.crt' |
|
276 |
+ |
|
248 | 277 |
Path to TLS certificate authority file. |
249 | 278 |
|
250 | 279 |
=item tls_cert |
251 | 280 |
|
281 |
+ tls_cert => '/etc/tls/server.crt' |
|
282 |
+ |
|
252 | 283 |
Path to the TLS cert file, defaults to a built-in test certificate. |
253 | 284 |
|
285 |
+=item tls_ciphers |
|
286 |
+ |
|
287 |
+ tls_ciphers => 'AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH' |
|
288 |
+ |
|
289 |
+Cipher specification string. |
|
290 |
+ |
|
254 | 291 |
=item tls_key |
255 | 292 |
|
293 |
+ tls_key => '/etc/tls/server.key' |
|
294 |
+ |
|
256 | 295 |
Path to the TLS key file, defaults to a built-in test key. |
257 | 296 |
|
258 | 297 |
=item tls_verify |
259 | 298 |
|
299 |
+ tls_verify => 0x00 |
|
300 |
+ |
|
260 | 301 |
TLS verification mode, defaults to C<0x03>. |
261 | 302 |
|
262 | 303 |
=back |
263 | 304 |
|
264 |
-=head2 generate_port |
|
265 |
- |
|
266 |
- my $port = $server->generate_port; |
|
267 |
- |
|
268 |
-Find a free TCP port, this is a utility function primarily used for tests. |
|
269 |
- |
|
270 | 305 |
=head2 start |
271 | 306 |
|
272 | 307 |
$server->start; |
... | ... |
@@ -11,16 +11,14 @@ has reactor => sub { |
11 | 11 |
|
12 | 12 |
sub DESTROY { shift->close } |
13 | 13 |
|
14 |
-sub new { shift->SUPER::new(handle => shift, buffer => '') } |
|
14 |
+sub new { shift->SUPER::new(handle => shift, buffer => '', timeout => 15) } |
|
15 | 15 |
|
16 | 16 |
sub close { |
17 | 17 |
my $self = shift; |
18 | 18 |
|
19 |
- # Cleanup |
|
20 |
- return unless my $reactor = $self->{reactor}; |
|
19 |
+ return unless my $reactor = $self->reactor; |
|
21 | 20 |
return unless my $handle = delete $self->timeout(0)->{handle}; |
22 | 21 |
$reactor->remove($handle); |
23 |
- |
|
24 | 22 |
close $handle; |
25 | 23 |
$self->emit_safe('close'); |
26 | 24 |
} |
... | ... |
@@ -48,14 +46,14 @@ sub is_writing { |
48 | 46 |
sub start { |
49 | 47 |
my $self = shift; |
50 | 48 |
|
51 |
- my $reactor = $self->reactor; |
|
52 |
- $reactor->io($self->timeout(15)->{handle}, |
|
53 |
- sub { pop() ? $self->_write : $self->_read }) |
|
54 |
- unless $self->{timer}; |
|
55 |
- |
|
56 | 49 |
# Resume |
57 |
- $reactor->watch($self->{handle}, 1, $self->is_writing) |
|
50 |
+ my $reactor = $self->reactor; |
|
51 |
+ return $reactor->watch($self->{handle}, 1, $self->is_writing) |
|
58 | 52 |
if delete $self->{paused}; |
53 |
+ |
|
54 |
+ weaken $self; |
|
55 |
+ my $cb = sub { pop() ? $self->_write : $self->_read }; |
|
56 |
+ $reactor->io($self->timeout($self->{timeout})->{handle} => $cb); |
|
59 | 57 |
} |
60 | 58 |
|
61 | 59 |
sub stop { |
... | ... |
@@ -109,7 +107,7 @@ sub _error { |
109 | 107 |
return $self->close if $! == ECONNRESET || $! == EPIPE; |
110 | 108 |
|
111 | 109 |
# Error |
112 |
- $self->emit_safe(error => $!)->close; |
|
110 |
+ $self->emit(error => $!)->close; |
|
113 | 111 |
} |
114 | 112 |
|
115 | 113 |
sub _read { |
... | ... |
@@ -139,6 +137,8 @@ sub _write { |
139 | 137 |
|
140 | 138 |
1; |
141 | 139 |
|
140 |
+=encoding utf8 |
|
141 |
+ |
|
142 | 142 |
=head1 NAME |
143 | 143 |
|
144 | 144 |
Mojo::IOLoop::Stream - Non-blocking I/O stream |
... | ... |
@@ -204,7 +204,7 @@ Emitted safely once all data has been written. |
204 | 204 |
... |
205 | 205 |
}); |
206 | 206 |
|
207 |
-Emitted safely if an error occurs on the stream. |
|
207 |
+Emitted if an error occurs on the stream, fatal if unhandled. |
|
208 | 208 |
|
209 | 209 |
=head2 read |
210 | 210 |
|
... | ... |
@@ -277,14 +277,14 @@ Get handle for stream. |
277 | 277 |
|
278 | 278 |
=head2 is_readable |
279 | 279 |
|
280 |
- my $success = $stream->is_readable; |
|
280 |
+ my $bool = $stream->is_readable; |
|
281 | 281 |
|
282 | 282 |
Quick non-blocking check if stream is readable, useful for identifying tainted |
283 | 283 |
sockets. |
284 | 284 |
|
285 | 285 |
=head2 is_writing |
286 | 286 |
|
287 |
- my $success = $stream->is_writing; |
|
287 |
+ my $bool = $stream->is_writing; |
|
288 | 288 |
|
289 | 289 |
Check if stream is writing. |
290 | 290 |
|
... | ... |
@@ -19,23 +19,23 @@ my %ESCAPE = ( |
19 | 19 |
'"' => '"', |
20 | 20 |
'\\' => '\\', |
21 | 21 |
'/' => '/', |
22 |
- 'b' => "\x07", |
|
23 |
- 'f' => "\x0C", |
|
24 |
- 'n' => "\x0A", |
|
25 |
- 'r' => "\x0D", |
|
22 |
+ 'b' => "\x08", |
|
23 |
+ 'f' => "\x0c", |
|
24 |
+ 'n' => "\x0a", |
|
25 |
+ 'r' => "\x0d", |
|
26 | 26 |
't' => "\x09", |
27 | 27 |
'u2028' => "\x{2028}", |
28 | 28 |
'u2029' => "\x{2029}" |
29 | 29 |
); |
30 | 30 |
my %REVERSE = map { $ESCAPE{$_} => "\\$_" } keys %ESCAPE; |
31 |
-for (0x00 .. 0x1F, 0x7F) { $REVERSE{pack 'C', $_} = do {my $tmp = $REVERSE{pack 'C', $_}; defined $tmp ? $tmp : sprintf '\u%.4X', $_} } |
|
31 |
+for (0x00 .. 0x1f, 0x7f) {$REVERSE{pack 'C', $_} = defined $REVERSE{pack 'C', $_} ? $REVERSE{pack 'C', $_} : sprintf '\u%.4X', $_} |
|
32 | 32 |
|
33 | 33 |
# Unicode encoding detection |
34 | 34 |
my $UTF_PATTERNS = { |
35 |
- 'UTF-32BE' => qr/^\0\0\0[^\0]/, |
|
36 |
- 'UTF-16BE' => qr/^\0[^\0]\0[^\0]/, |
|
37 |
- 'UTF-32LE' => qr/^[^\0]\0\0\0/, |
|
38 |
- 'UTF-16LE' => qr/^[^\0]\0[^\0]\0/ |
|
35 |
+ 'UTF-32BE' => qr/^\x00{3}[^\x00]/, |
|
36 |
+ 'UTF-32LE' => qr/^[^\x00]\x00{3}/, |
|
37 |
+ 'UTF-16BE' => qr/^(?:\x00[^\x00]){2}/, |
|
38 |
+ 'UTF-16LE' => qr/^(?:[^\x00]\x00){2}/ |
|
39 | 39 |
}; |
40 | 40 |
|
41 | 41 |
my $WHITESPACE_RE = qr/[\x20\x09\x0a\x0d]*/; |
... | ... |
@@ -75,7 +75,7 @@ sub decode { |
75 | 75 |
# Object |
76 | 76 |
elsif (m/\G\{/gc) { $ref = _decode_object() } |
77 | 77 |
|
78 |
- # Unexpected |
|
78 |
+ # Invalid character |
|
79 | 79 |
else { _exception('Expected array or object') } |
80 | 80 |
|
81 | 81 |
# Leftover data |
... | ... |
@@ -166,13 +166,13 @@ sub _decode_string { |
166 | 166 |
my $pos = pos; |
167 | 167 |
|
168 | 168 |
# Extract string with escaped characters |
169 |
- m#\G(((?:[^\x00-\x1F\\"]|\\(?:["\\/bfnrt]|u[[:xdigit:]]{4})){0,32766})*)#gc; |
|
169 |
+ m!\G((?:(?:[^\x00-\x1f\\"]|\\(?:["\\/bfnrt]|u[0-9a-fA-F]{4})){0,32766})*)!gc; |
|
170 | 170 |
my $str = $1; |
171 | 171 |
|
172 |
- # Missing quote |
|
172 |
+ # Invalid character |
|
173 | 173 |
unless (m/\G"/gc) { |
174 | 174 |
_exception('Unexpected character or invalid escape while parsing string') |
175 |
- if m/\G[\x00-\x1F\\]/; |
|
175 |
+ if m/\G[\x00-\x1f\\]/; |
|
176 | 176 |
_exception('Unterminated string'); |
177 | 177 |
} |
178 | 178 |
|
... | ... |
@@ -195,18 +195,17 @@ sub _decode_string { |
195 | 195 |
my $ord = hex $3; |
196 | 196 |
|
197 | 197 |
# Surrogate pair |
198 |
- if (($ord & 0xF800) == 0xD800) { |
|
198 |
+ if (($ord & 0xf800) == 0xd800) { |
|
199 | 199 |
|
200 | 200 |
# High surrogate |
201 |
- ($ord & 0xFC00) == 0xD800 |
|
201 |
+ ($ord & 0xfc00) == 0xd800 |
|
202 | 202 |
or pos($_) = $pos + pos($str), _exception('Missing high-surrogate'); |
203 | 203 |
|
204 | 204 |
# Low surrogate |
205 | 205 |
$str =~ m/\G\\u([Dd][C-Fc-f]..)/gc |
206 | 206 |
or pos($_) = $pos + pos($str), _exception('Missing low-surrogate'); |
207 | 207 |
|
208 |
- # Pair |
|
209 |
- $ord = 0x10000 + ($ord - 0xD800) * 0x400 + (hex($1) - 0xDC00); |
|
208 |
+ $ord = 0x10000 + ($ord - 0xd800) * 0x400 + (hex($1) - 0xdc00); |
|
210 | 209 |
} |
211 | 210 |
|
212 | 211 |
# Character |
... | ... |
@@ -245,7 +244,7 @@ sub _decode_value { |
245 | 244 |
# Null |
246 | 245 |
return undef if m/\Gnull/gc; |
247 | 246 |
|
248 |
- # Invalid data |
|
247 |
+ # Invalid character |
|
249 | 248 |
_exception('Expected string, array, object, number, boolean or null'); |
250 | 249 |
} |
251 | 250 |
|
... | ... |
@@ -263,7 +262,7 @@ sub _encode_object { |
263 | 262 |
|
264 | 263 |
sub _encode_string { |
265 | 264 |
my $str = shift; |
266 |
- $str =~ s!([\x00-\x1F\x7F\x{2028}\x{2029}\\"/\b\f\n\r\t])!$REVERSE{$1}!gs; |
|
265 |
+ $str =~ s!([\x00-\x1f\x7f\x{2028}\x{2029}\\"/\b\f\n\r\t])!$REVERSE{$1}!gs; |
|
267 | 266 |
return "\"$str\""; |
268 | 267 |
} |
269 | 268 |
|
... | ... |
@@ -322,6 +321,8 @@ use overload '0+' => sub { ${$_[0]} }, '""' => sub { ${$_[0]} }, fallback => 1; |
322 | 321 |
|
323 | 322 |
1; |
324 | 323 |
|
324 |
+=encoding utf8 |
|
325 |
+ |
|
325 | 326 |
=head1 NAME |
326 | 327 |
|
327 | 328 |
Mojo::JSON - Minimalistic JSON |
... | ... |
@@ -352,19 +353,28 @@ it for validation. |
352 | 353 |
|
353 | 354 |
It supports normal Perl data types like C<Scalar>, C<Array> reference, C<Hash> |
354 | 355 |
reference and will try to call the C<TO_JSON> method on blessed references, or |
355 |
-stringify them if it doesn't exist. |
|
356 |
+stringify them if it doesn't exist. Differentiating between strings and |
|
357 |
+numbers in Perl is hard, depending on how it has been used, a C<Scalar> can be |
|
358 |
+both at the same time. Since numeric comparisons on strings are very unlikely |
|
359 |
+to happen intentionally, the numeric value always gets priority, so any |
|
360 |
+C<Scalar> that has been used in numeric context is considered a number. |
|
356 | 361 |
|
357 | 362 |
[1, -2, 3] -> [1, -2, 3] |
358 | 363 |
{"foo": "bar"} -> {foo => 'bar'} |
359 | 364 |
|
360 | 365 |
Literal names will be translated to and from L<Mojo::JSON> constants or a |
361 |
-similar native Perl value. In addition C<Scalar> references will be used to |
|
362 |
-generate booleans, based on if their values are true or false. |
|
366 |
+similar native Perl value. |
|
363 | 367 |
|
364 | 368 |
true -> Mojo::JSON->true |
365 | 369 |
false -> Mojo::JSON->false |
366 | 370 |
null -> undef |
367 | 371 |
|
372 |
+In addition C<Scalar> references will be used to generate booleans, based on |
|
373 |
+if their values are true or false. |
|
374 |
+ |
|
375 |
+ \1 -> true |
|
376 |
+ \0 -> false |
|
377 |
+ |
|
368 | 378 |
Decoding UTF-16 (LE/BE) and UTF-32 (LE/BE) will be handled transparently, |
369 | 379 |
encoding will only generate UTF-8. The two Unicode whitespace characters |
370 | 380 |
C<u2028> and C<u2029> will always be escaped to make JSONP easier. |
... | ... |
@@ -53,7 +53,7 @@ L<Mojo::JSON::Pointer> is a relaxed implementation of RFC 6901. |
53 | 53 |
|
54 | 54 |
=head2 contains |
55 | 55 |
|
56 |
- my $success = $pointer->contains($data, '/foo/1'); |
|
56 |
+ my $bool = $pointer->contains($data, '/foo/1'); |
|
57 | 57 |
|
58 | 58 |
Check if data structure contains a value that can be identified with the given |
59 | 59 |
JSON Pointer. |
... | ... |
@@ -6,12 +6,11 @@ use File::Spec::Functions qw(catdir catfile splitdir); |
6 | 6 |
use Mojo::Exception; |
7 | 7 |
use Mojo::Util qw(b64_decode class_to_path); |
8 | 8 |
|
9 |
-my %CACHE; |
|
9 |
+my (%BIN, %CACHE); |
|
10 | 10 |
|
11 |
-sub data { |
|
12 |
- my ($self, $class, $data) = @_; |
|
13 |
- return $class ? $data ? _all($class)->{$data} : _all($class) : undef; |
|
14 |
-} |
|
11 |
+sub data { $_[1] ? $_[2] ? _all($_[1])->{$_[2]} : _all($_[1]) : undef } |
|
12 |
+ |
|
13 |
+sub is_binary { keys %{_all($_[1])} ? !!$BIN{$_[1]}{$_[2]} : undef } |
|
15 | 14 |
|
16 | 15 |
sub load { |
17 | 16 |
my ($self, $module) = @_; |
... | ... |
@@ -23,8 +22,7 @@ sub load { |
23 | 22 |
return undef if $module->can('new') || eval "require $module; 1"; |
24 | 23 |
|
25 | 24 |
# Exists |
26 |
- my $path = class_to_path $module; |
|
27 |
- return 1 if $@ =~ /^Can't locate $path in \@INC/; |
|
25 |
+ return 1 if $@ =~ /^Can't locate \Q@{[class_to_path $module]}\E in \@INC/; |
|
28 | 26 |
|
29 | 27 |
# Real error |
30 | 28 |
return Mojo::Exception->new($@); |
... | ... |
@@ -52,29 +50,26 @@ sub search { |
52 | 50 |
sub _all { |
53 | 51 |
my $class = shift; |
54 | 52 |
|
55 |
- # Refresh or use cached data |
|
56 | 53 |
my $handle = do { no strict 'refs'; \*{"${class}::DATA"} }; |
57 |
- return $CACHE{$class} || {} unless fileno $handle; |
|
54 |
+ return $CACHE{$class} || {} if $CACHE{$class} || !fileno $handle; |
|
58 | 55 |
seek $handle, 0, 0; |
59 |
- my $content = join '', <$handle>; |
|
60 |
- close $handle; |
|
56 |
+ my $data = join '', <$handle>; |
|
61 | 57 |
|
62 | 58 |
# Ignore everything before __DATA__ (Windows will seek to start of file) |
63 |
- $content =~ s/^.*\n__DATA__\r?\n/\n/s; |
|
59 |
+ $data =~ s/^.*\n__DATA__\r?\n/\n/s; |
|
64 | 60 |
|
65 | 61 |
# Ignore everything after __END__ |
66 |
- $content =~ s/\n__END__\r?\n.*$/\n/s; |
|
62 |
+ $data =~ s/\n__END__\r?\n.*$/\n/s; |
|
67 | 63 |
|
68 | 64 |
# Split files |
69 |
- my @data = split /^@@\s*(.+?)\s*\r?\n/m, $content; |
|
70 |
- shift @data; |
|
65 |
+ (undef, my @files) = split /^@@\s*(.+?)\s*\r?\n/m, $data; |
|
71 | 66 |
|
72 | 67 |
# Find data |
73 | 68 |
my $all = $CACHE{$class} = {}; |
74 |
- while (@data) { |
|
75 |
- my ($name, $content) = splice @data, 0, 2; |
|
76 |
- $content = b64_decode $content if $name =~ s/\s*\(\s*base64\s*\)$//; |
|
77 |
- $all->{$name} = $content; |
|
69 |
+ while (@files) { |
|
70 |
+ my ($name, $data) = splice @files, 0, 2; |
|
71 |
+ $all->{$name} = $name =~ s/\s*\(\s*base64\s*\)$// |
|
72 |
+ && ++$BIN{$class}{$name} ? b64_decode($data) : $data; |
|
78 | 73 |
} |
79 | 74 |
|
80 | 75 |
return $all; |
... | ... |
@@ -82,6 +77,8 @@ sub _all { |
82 | 77 |
|
83 | 78 |
1; |
84 | 79 |
|
80 |
+=encoding utf8 |
|
81 |
+ |
|
85 | 82 |
=head1 NAME |
86 | 83 |
|
87 | 84 |
Mojo::Loader - Loader |
... | ... |
@@ -120,6 +117,12 @@ Extract embedded file from the C<DATA> section of a class. |
120 | 117 |
|
121 | 118 |
say for keys %{$loader->data('Foo::Bar')}; |
122 | 119 |
|
120 |
+=head2 is_binary |
|
121 |
+ |
|
122 |
+ my $bool = $loader->is_binary('Foo::Bar', 'test.png'); |
|
123 |
+ |
|
124 |
+Check if embedded file from the C<DATA> section of a class was Base64 encoded. |
|
125 |
+ |
|
123 | 126 |
=head2 load |
124 | 127 |
|
125 | 128 |
my $e = $loader->load('Foo::Bar'); |
... | ... |
@@ -128,7 +131,7 @@ Load a class and catch exceptions. Note that classes are checked for a C<new> |
128 | 131 |
method to see if they are already loaded. |
129 | 132 |
|
130 | 133 |
if (my $e = $loader->load('Foo::Bar')) { |
131 |
- die ref $e ? "Exception: $e" : 'Already loaded!'; |
|
134 |
+ die ref $e ? "Exception: $e" : 'Not found!'; |
|
132 | 135 |
} |
133 | 136 |
|
134 | 137 |
=head2 search |
... | ... |
@@ -35,7 +35,8 @@ sub fatal { shift->log(fatal => @_) } |
35 | 35 |
|
36 | 36 |
sub format { |
37 | 37 |
my ($self, $level, @lines) = @_; |
38 |
- return '[' . localtime(time) . "] [$level] " . join("\n", @lines) . "\n"; |
|
38 |
+ return encode 'UTF-8', |
|
39 |
+ '[' . localtime(time) . "] [$level] " . join("\n", @lines, ''); |
|
39 | 40 |
} |
40 | 41 |
|
41 | 42 |
sub info { shift->log(info => @_) } |
... | ... |
@@ -57,18 +58,19 @@ sub log { shift->emit('message', lc(shift), @_) } |
57 | 58 |
sub warn { shift->log(warn => @_) } |
58 | 59 |
|
59 | 60 |
sub _message { |
60 |
- my ($self, $level, @lines) = @_; |
|
61 |
+ my ($self, $level) = (shift, shift); |
|
61 | 62 |
|
62 | 63 |
return unless $self->is_level($level) && (my $handle = $self->handle); |
63 | 64 |
|
64 | 65 |
flock $handle, LOCK_EX; |
65 |
- croak "Can't write to log: $!" |
|
66 |
- unless $handle->print(encode 'UTF-8', $self->format($level, @lines)); |
|
66 |
+ $handle->print($self->format($level, @_)) or croak "Can't write to log: $!"; |
|
67 | 67 |
flock $handle, LOCK_UN; |
68 | 68 |
} |
69 | 69 |
|
70 | 70 |
1; |
71 | 71 |
|
72 |
+=encoding utf8 |
|
73 |
+ |
|
72 | 74 |
=head1 NAME |
73 | 75 |
|
74 | 76 |
Mojo::Log - Simple logger |
... | ... |
@@ -123,39 +125,24 @@ L<Mojo::Log> implements the following attributes. |
123 | 125 |
my $handle = $log->handle; |
124 | 126 |
$log = $log->handle(IO::Handle->new); |
125 | 127 |
|
126 |
-Log file handle used by default C<message> event, defaults to opening C<path> |
|
127 |
-or C<STDERR>. |
|
128 |
+Log filehandle used by default L</"message"> event, defaults to opening |
|
129 |
+L</"path"> or C<STDERR>. |
|
128 | 130 |
|
129 | 131 |
=head2 level |
130 | 132 |
|
131 | 133 |
my $level = $log->level; |
132 | 134 |
$log = $log->level('debug'); |
133 | 135 |
|
134 |
-Active log level, defaults to C<debug>. Note that the MOJO_LOG_LEVEL |
|
135 |
-environment variable can override this value. |
|
136 |
- |
|
137 |
-These levels are currently available: |
|
138 |
- |
|
139 |
-=over 2 |
|
140 |
- |
|
141 |
-=item debug |
|
142 |
- |
|
143 |
-=item info |
|
144 |
- |
|
145 |
-=item warn |
|
146 |
- |
|
147 |
-=item error |
|
148 |
- |
|
149 |
-=item fatal |
|
150 |
- |
|
151 |
-=back |
|
136 |
+Active log level, defaults to C<debug>. Available log levels are C<debug>, |
|
137 |
+C<info>, C<warn>, C<error> and C<fatal>, in that order. Note that the |
|
138 |
+MOJO_LOG_LEVEL environment variable can override this value. |
|
152 | 139 |
|
153 | 140 |
=head2 path |
154 | 141 |
|
155 | 142 |
my $path = $log->path |
156 | 143 |
$log = $log->path('/var/log/mojo.log'); |
157 | 144 |
|
158 |
-Log file path used by C<handle>. |
|
145 |
+Log file path used by L</"handle">. |
|
159 | 146 |
|
160 | 147 |
=head1 METHODS |
161 | 148 |
|
... | ... |
@@ -166,85 +153,91 @@ the following new ones. |
166 | 153 |
|
167 | 154 |
my $log = Mojo::Log->new; |
168 | 155 |
|
169 |
-Construct a new L<Mojo::Log> object and subscribe to C<message> event with |
|
156 |
+Construct a new L<Mojo::Log> object and subscribe to L</"message"> event with |
|
170 | 157 |
default logger. |
171 | 158 |
|
172 | 159 |
=head2 debug |
173 | 160 |
|
174 |
- $log = $log->debug('You screwed up, but that is ok'); |
|
161 |
+ $log = $log->debug('You screwed up, but that is ok.'); |
|
162 |
+ $log = $log->debug('All', 'cool!'); |
|
175 | 163 |
|
176 | 164 |
Log debug message. |
177 | 165 |
|
178 | 166 |
=head2 error |
179 | 167 |
|
180 |
- $log = $log->error('You really screwed up this time'); |
|
168 |
+ $log = $log->error('You really screwed up this time.'); |
|
169 |
+ $log = $log->error('Wow', 'seriously!'); |
|
181 | 170 |
|
182 | 171 |
Log error message. |
183 | 172 |
|
184 | 173 |
=head2 fatal |
185 | 174 |
|
186 | 175 |
$log = $log->fatal('Its over...'); |
176 |
+ $log = $log->fatal('Bye', 'bye!'); |
|
187 | 177 |
|
188 | 178 |
Log fatal message. |
189 | 179 |
|
190 | 180 |
=head2 format |
191 | 181 |
|
192 |
- my $msg = $log->format('debug', 'Hi there!'); |
|
193 |
- my $msg = $log->format('debug', 'Hi', 'there!'); |
|
182 |
+ my $msg = $log->format(debug => 'Hi there!'); |
|
183 |
+ my $msg = $log->format(debug => 'Hi', 'there!'); |
|
194 | 184 |
|
195 | 185 |
Format log message. |
196 | 186 |
|
197 | 187 |
=head2 info |
198 | 188 |
|
199 |
- $log = $log->info('You are bad, but you prolly know already'); |
|
189 |
+ $log = $log->info('You are bad, but you prolly know already.'); |
|
190 |
+ $log = $log->info('Ok', 'then!'); |
|
200 | 191 |
|
201 | 192 |
Log info message. |
202 | 193 |
|
203 | 194 |
=head2 is_level |
204 | 195 |
|
205 |
- my $success = $log->is_level('debug'); |
|
196 |
+ my $bool = $log->is_level('debug'); |
|
206 | 197 |
|
207 | 198 |
Check log level. |
208 | 199 |
|
209 | 200 |
=head2 is_debug |
210 | 201 |
|
211 |
- my $success = $log->is_debug; |
|
202 |
+ my $bool = $log->is_debug; |
|
212 | 203 |
|
213 | 204 |
Check for debug log level. |
214 | 205 |
|
215 | 206 |
=head2 is_error |
216 | 207 |
|
217 |
- my $success = $log->is_error; |
|
208 |
+ my $bool = $log->is_error; |
|
218 | 209 |
|
219 | 210 |
Check for error log level. |
220 | 211 |
|
221 | 212 |
=head2 is_fatal |
222 | 213 |
|
223 |
- my $success = $log->is_fatal; |
|
214 |
+ my $bool = $log->is_fatal; |
|
224 | 215 |
|
225 | 216 |
Check for fatal log level. |
226 | 217 |
|
227 | 218 |
=head2 is_info |
228 | 219 |
|
229 |
- my $success = $log->is_info; |
|
220 |
+ my $bool = $log->is_info; |
|
230 | 221 |
|
231 | 222 |
Check for info log level. |
232 | 223 |
|
233 | 224 |
=head2 is_warn |
234 | 225 |
|
235 |
- my $success = $log->is_warn; |
|
226 |
+ my $bool = $log->is_warn; |
|
236 | 227 |
|
237 | 228 |
Check for warn log level. |
238 | 229 |
|
239 | 230 |
=head2 log |
240 | 231 |
|
241 |
- $log = $log->log(debug => 'This should work'); |
|
232 |
+ $log = $log->log(debug => 'This should work.'); |
|
233 |
+ $log = $log->log(debug => 'This', 'too!'); |
|
242 | 234 |
|
243 |
-Emit C<message> event. |
|
235 |
+Emit L</"message"> event. |
|
244 | 236 |
|
245 | 237 |
=head2 warn |
246 | 238 |
|
247 | 239 |
$log = $log->warn('Dont do that Dave...'); |
240 |
+ $log = $log->warn('No', 'really!'); |
|
248 | 241 |
|
249 | 242 |
Log warn message. |
250 | 243 |
|
... | ... |
@@ -14,7 +14,7 @@ use Mojo::Util 'decode'; |
14 | 14 |
has content => sub { Mojo::Content::Single->new }; |
15 | 15 |
has default_charset => 'UTF-8'; |
16 | 16 |
has max_line_size => sub { $ENV{MOJO_MAX_LINE_SIZE} || 10240 }; |
17 |
-has max_message_size => sub { $ENV{MOJO_MAX_MESSAGE_SIZE} || 5242880 }; |
|
17 |
+has max_message_size => sub { defined $ENV{MOJO_MAX_MESSAGE_SIZE} ? $ENV{MOJO_MAX_MESSAGE_SIZE} : 10485760 }; |
|
18 | 18 |
has version => '1.1'; |
19 | 19 |
|
20 | 20 |
sub body { |
... | ... |
@@ -28,7 +28,7 @@ sub body { |
28 | 28 |
# Get |
29 | 29 |
return $content->asset->slurp unless @_; |
30 | 30 |
|
31 |
- # Set raw content |
|
31 |
+ # Set |
|
32 | 32 |
$content->asset(Mojo::Asset::Memory->new->add_chunk(@_)); |
33 | 33 |
|
34 | 34 |
return $self; |
... | ... |
@@ -41,17 +41,15 @@ sub body_params { |
41 | 41 |
my $params = $self->{body_params} = Mojo::Parameters->new; |
42 | 42 |
$params->charset($self->content->charset || $self->default_charset); |
43 | 43 |
|
44 |
- # "x-application-urlencoded" and "application/x-www-form-urlencoded" |
|
44 |
+ # "application/x-www-form-urlencoded" |
|
45 | 45 |
my $type = defined $self->headers->content_type ? $self->headers->content_type : ''; |
46 |
- if ($type =~ m!(?:x-application|application/x-www-form)-urlencoded!i) { |
|
46 |
+ if ($type =~ m!application/x-www-form-urlencoded!i) { |
|
47 | 47 |
$params->parse($self->content->asset->slurp); |
48 | 48 |
} |
49 | 49 |
|
50 |
- # "multipart/formdata" |
|
50 |
+ # "multipart/form-data" |
|
51 | 51 |
elsif ($type =~ m!multipart/form-data!i) { |
52 |
- for my $data (@{$self->_parse_formdata}) { |
|
53 |
- $params->append($data->[0], $data->[2]) unless defined $data->[1]; |
|
54 |
- } |
|
52 |
+ $params->append(@$_[0, 1]) for @{$self->_parse_formdata}; |
|
55 | 53 |
} |
56 | 54 |
|
57 | 55 |
return $params; |
... | ... |
@@ -69,13 +67,8 @@ sub cookies { croak 'Method "cookies" not implemented by subclass' } |
69 | 67 |
|
70 | 68 |
sub dom { |
71 | 69 |
my $self = shift; |
72 |
- |
|
73 | 70 |
return undef if $self->content->is_multipart; |
74 |
- my $html = $self->body; |
|
75 |
- my $charset = $self->content->charset; |
|
76 |
- $html = do { my $tmp = decode($charset, $html); defined $tmp ? $tmp : $html } if $charset; |
|
77 |
- my $dom = $self->{dom} ||= Mojo::DOM->new($html); |
|
78 |
- |
|
71 |
+ my $dom = $self->{dom} ||= Mojo::DOM->new($self->text); |
|
79 | 72 |
return @_ ? $dom->find(@_) : $dom; |
80 | 73 |
} |
81 | 74 |
|
... | ... |
@@ -160,8 +153,9 @@ sub parse { |
160 | 153 |
my ($self, $chunk) = @_; |
161 | 154 |
|
162 | 155 |
# Check message size |
156 |
+ my $max = $self->max_message_size; |
|
163 | 157 |
return $self->_limit('Maximum message size exceeded', 413) |
164 |
- if ($self->{raw_size} += length($chunk = defined $chunk ? $chunk : '')) > $self->max_message_size; |
|
158 |
+ if $max && ($self->{raw_size} += length($chunk = defined $chunk ? $chunk : '')) > $max; |
|
165 | 159 |
|
166 | 160 |
$self->{buffer} .= $chunk; |
167 | 161 |
|
... | ... |
@@ -195,6 +189,13 @@ sub parse { |
195 | 189 |
|
196 | 190 |
sub start_line_size { length shift->build_start_line } |
197 | 191 |
|
192 |
+sub text { |
|
193 |
+ my $self = shift; |
|
194 |
+ my $body = $self->body; |
|
195 |
+ my $charset = $self->content->charset; |
|
196 |
+ return $charset ? do {my $decoded = decode($charset, $body); defined $decoded ? $decoded : $body} : $body; |
|
197 |
+} |
|
198 |
+ |
|
198 | 199 |
sub to_string { |
199 | 200 |
my $self = shift; |
200 | 201 |
return $self->build_start_line . $self->build_headers . $self->build_body; |
... | ... |
@@ -206,17 +207,12 @@ sub uploads { |
206 | 207 |
my $self = shift; |
207 | 208 |
|
208 | 209 |
my @uploads; |
209 |
- for my $data (@{$self->_parse_formdata}) { |
|
210 |
- |
|
211 |
- # Just a form value |
|
212 |
- next unless defined $data->[1]; |
|
213 |
- |
|
214 |
- # Uploaded file |
|
210 |
+ for my $data (@{$self->_parse_formdata(1)}) { |
|
215 | 211 |
my $upload = Mojo::Upload->new( |
216 | 212 |
name => $data->[0], |
217 |
- filename => $data->[1], |
|
218 |
- asset => $data->[2]->asset, |
|
219 |
- headers => $data->[2]->headers |
|
213 |
+ filename => $data->[2], |
|
214 |
+ asset => $data->[1]->asset, |
|
215 |
+ headers => $data->[1]->headers |
|
220 | 216 |
); |
221 | 217 |
push @uploads, $upload; |
222 | 218 |
} |
... | ... |
@@ -264,41 +260,37 @@ sub _limit { |
264 | 260 |
} |
265 | 261 |
|
266 | 262 |
sub _parse_formdata { |
267 |
- my $self = shift; |
|
263 |
+ my ($self, $upload) = @_; |
|
268 | 264 |
|
269 |
- # Check for multipart content |
|
270 | 265 |
my @formdata; |
271 | 266 |
my $content = $self->content; |
272 | 267 |
return \@formdata unless $content->is_multipart; |
273 | 268 |
my $charset = $content->charset || $self->default_charset; |
274 | 269 |
|
275 |
- # Check all parts for form data |
|
270 |
+ # Check all parts recursively |
|
276 | 271 |
my @parts = ($content); |
277 | 272 |
while (my $part = shift @parts) { |
278 | 273 |
|
279 |
- # Nested multipart content |
|
280 | 274 |
if ($part->is_multipart) { |
281 | 275 |
unshift @parts, @{$part->parts}; |
282 | 276 |
next; |
283 | 277 |
} |
284 | 278 |
|
285 |
- # Extract information from Content-Disposition header |
|
286 | 279 |
next unless my $disposition = $part->headers->content_disposition; |
287 |
- my ($name) = $disposition =~ /[; ]name="?([^";]+)"?/; |
|
288 |
- my ($filename) = $disposition =~ /[; ]filename="?([^"]*)"?/; |
|
280 |
+ my ($filename) = $disposition =~ /[; ]filename\s*=\s*"?((?:\\"|[^"])*)"?/i; |
|
281 |
+ next if ($upload && !defined $filename) || (!$upload && defined $filename); |
|
282 |
+ my ($name) = $disposition =~ /[; ]name\s*=\s*"?((?:\\"|[^";])+)"?/i; |
|
289 | 283 |
if ($charset) { |
290 | 284 |
$name = do {my $tmp = decode($charset, $name); defined $tmp ? $tmp : $name} if $name; |
291 | 285 |
$filename = do {my $tmp = decode($charset, $filename); defined $tmp ? $tmp : $filename} if $filename; |
292 | 286 |
} |
293 | 287 |
|
294 |
- # Check for file upload |
|
295 |
- my $value = $part; |
|
296 |
- unless (defined $filename) { |
|
297 |
- $value = $part->asset->slurp; |
|
298 |
- $value = do {my $tmp = decode($charset, $value); defined $tmp ? $tmp : $value} if $charset; |
|
288 |
+ unless ($upload) { |
|
289 |
+ $part = $part->asset->slurp; |
|
290 |
+ $part = do {my $decoded = decode($charset, $part); defined $decoded ? $decoded : $part} if $charset; |
|
299 | 291 |
} |
300 | 292 |
|
301 |
- push @formdata, [$name, $filename, $value]; |
|
293 |
+ push @formdata, [$name, $part, $filename]; |
|
302 | 294 |
} |
303 | 295 |
|
304 | 296 |
return \@formdata; |
... | ... |
@@ -306,6 +298,8 @@ sub _parse_formdata { |
306 | 298 |
|
307 | 299 |
1; |
308 | 300 |
|
301 |
+=encoding utf8 |
|
302 |
+ |
|
309 | 303 |
=head1 NAME |
310 | 304 |
|
311 | 305 |
Mojo::Message - HTTP message base class |
... | ... |
@@ -383,7 +377,7 @@ Message content, defaults to a L<Mojo::Content::Single> object. |
383 | 377 |
my $charset = $msg->default_charset; |
384 | 378 |
$msg = $msg->default_charset('UTF-8'); |
385 | 379 |
|
386 |
-Default charset used for form data parsing, defaults to C<UTF-8>. |
|
380 |
+Default charset used for form-data parsing, defaults to C<UTF-8>. |
|
387 | 381 |
|
388 | 382 |
=head2 max_line_size |
389 | 383 |
|
... | ... |
@@ -399,10 +393,11 @@ MOJO_MAX_LINE_SIZE environment variable or C<10240>. |
399 | 393 |
$msg = $msg->max_message_size(1024); |
400 | 394 |
|
401 | 395 |
Maximum message size in bytes, defaults to the value of the |
402 |
-MOJO_MAX_MESSAGE_SIZE environment variable or C<5242880>. Note that increasing |
|
403 |
-this value can also drastically increase memory usage, should you for example |
|
404 |
-attempt to parse an excessively large message body with the C<body_params>, |
|
405 |
-C<dom> or C<json> methods. |
|
396 |
+MOJO_MAX_MESSAGE_SIZE environment variable or C<10485760>. Setting the value |
|
397 |
+to C<0> will allow messages of indefinite size. Note that increasing this |
|
398 |
+value can also drastically increase memory usage, should you for example |
|
399 |
+attempt to parse an excessively large message body with the L</"body_params">, |
|
400 |
+L</"dom"> or L</"json"> methods. |
|
406 | 401 |
|
407 | 402 |
=head2 version |
408 | 403 |
|
... | ... |
@@ -421,16 +416,19 @@ implements the following new ones. |
421 | 416 |
my $bytes = $msg->body; |
422 | 417 |
$msg = $msg->body('Hello!'); |
423 | 418 |
|
424 |
-Slurp or replace C<content>. |
|
419 |
+Slurp or replace L</"content">, L<Mojo::Content::MultiPart> will be |
|
420 |
+automatically downgraded to L<Mojo::Content::Single>. |
|
425 | 421 |
|
426 | 422 |
=head2 body_params |
427 | 423 |
|
428 | 424 |
my $params = $msg->body_params; |
429 | 425 |
|
430 |
-C<POST> parameters extracted from C<x-application-urlencoded>, |
|
431 |
-C<application/x-www-form-urlencoded> or C<multipart/form-data> message body, |
|
432 |
-usually a L<Mojo::Parameters> object. Note that this method caches all data, |
|
433 |
-so it should not be called before the entire message body has been received. |
|
426 |
+POST parameters extracted from C<application/x-www-form-urlencoded> or |
|
427 |
+C<multipart/form-data> message body, usually a L<Mojo::Parameters> object. |
|
428 |
+Note that this method caches all data, so it should not be called before the |
|
429 |
+entire message body has been received. Parts of the message body need to be |
|
430 |
+loaded into memory to parse POST parameters, so you have to make sure it is |
|
431 |
+not excessively large. |
|
434 | 432 |
|
435 | 433 |
# Get POST parameter value |
436 | 434 |
say $msg->body_params->param('foo'); |
... | ... |
@@ -485,14 +483,16 @@ Access message cookies. Meant to be overloaded in a subclass. |
485 | 483 |
Turns message body into a L<Mojo::DOM> object and takes an optional selector |
486 | 484 |
to perform a C<find> on it right away, which returns a L<Mojo::Collection> |
487 | 485 |
object. Note that this method caches all data, so it should not be called |
488 |
-before the entire message body has been received. |
|
486 |
+before the entire message body has been received. The whole message body needs |
|
487 |
+to be loaded into memory to parse it, so you have to make sure it is not |
|
488 |
+excessively large. |
|
489 | 489 |
|
490 | 490 |
# Perform "find" right away |
491 |
- say $msg->dom('h1, h2, h3')->pluck('text'); |
|
491 |
+ say $msg->dom('h1, h2, h3')->text; |
|
492 | 492 |
|
493 | 493 |
# Use everything else Mojo::DOM has to offer |
494 | 494 |
say $msg->dom->at('title')->text; |
495 |
- say $msg->dom->html->body->children->pluck('type')->uniq; |
|
495 |
+ say $msg->dom->html->body->children->type->uniq; |
|
496 | 496 |
|
497 | 497 |
=head2 error |
498 | 498 |
|
... | ... |
@@ -505,7 +505,7 @@ Error and code. |
505 | 505 |
|
506 | 506 |
=head2 extract_start_line |
507 | 507 |
|
508 |
- my $success = $msg->extract_start_line(\$str); |
|
508 |
+ my $bool = $msg->extract_start_line(\$str); |
|
509 | 509 |
|
510 | 510 |
Extract start line from string. Meant to be overloaded in a subclass. |
511 | 511 |
|
... | ... |
@@ -554,15 +554,15 @@ Message headers, usually a L<Mojo::Headers> object. |
554 | 554 |
|
555 | 555 |
=head2 is_finished |
556 | 556 |
|
557 |
- my $success = $msg->is_finished; |
|
557 |
+ my $bool = $msg->is_finished; |
|
558 | 558 |
|
559 | 559 |
Check if message parser/generator is finished. |
560 | 560 |
|
561 | 561 |
=head2 is_limit_exceeded |
562 | 562 |
|
563 |
- my $success = $msg->is_limit_exceeded; |
|
563 |
+ my $bool = $msg->is_limit_exceeded; |
|
564 | 564 |
|
565 |
-Check if message has exceeded C<max_line_size> or C<max_message_size>. |
|
565 |
+Check if message has exceeded L</"max_line_size"> or L</"max_message_size">. |
|
566 | 566 |
|
567 | 567 |
=head2 json |
568 | 568 |
|
... | ... |
@@ -574,6 +574,8 @@ Decode JSON message body directly using L<Mojo::JSON> if possible, returns |
574 | 574 |
C<undef> otherwise. An optional JSON Pointer can be used to extract a specific |
575 | 575 |
value with L<Mojo::JSON::Pointer>. Note that this method caches all data, so |
576 | 576 |
it should not be called before the entire message body has been received. |
577 |
+The whole message body needs to be loaded into memory to parse it, so you have |
|
578 |
+to make sure it is not excessively large. |
|
577 | 579 |
|
578 | 580 |
# Extract JSON values |
579 | 581 |
say $msg->json->{foo}{bar}[23]; |
... | ... |
@@ -585,8 +587,10 @@ it should not be called before the entire message body has been received. |
585 | 587 |
my $foo = $msg->param('foo'); |
586 | 588 |
my @foo = $msg->param('foo'); |
587 | 589 |
|
588 |
-Access C<POST> parameters. Note that this method caches all data, so it should |
|
589 |
-not be called before the entire message body has been received. |
|
590 |
+Access POST parameters. Note that this method caches all data, so it should |
|
591 |
+not be called before the entire message body has been received. Parts of the |
|
592 |
+message body need to be loaded into memory to parse POST parameters, so you |
|
593 |
+have to make sure it is not excessively large. |
|
590 | 594 |
|
591 | 595 |
=head2 parse |
592 | 596 |
|
... | ... |
@@ -600,6 +604,13 @@ Parse message chunk. |
600 | 604 |
|
601 | 605 |
Size of the start line in bytes. |
602 | 606 |
|
607 |
+=head2 text |
|
608 |
+ |
|
609 |
+ my $str = $msg->text; |
|
610 |
+ |
|
611 |
+Retrieve L</"body"> and try to decode it if a charset could be extracted with |
|
612 |
+L<Mojo::Content/"charset">. |
|
613 |
+ |
|
603 | 614 |
=head2 to_string |
604 | 615 |
|
605 | 616 |
my $str = $msg->to_string; |
... | ... |
@@ -9,14 +9,14 @@ has env => sub { {} }; |
9 | 9 |
has method => 'GET'; |
10 | 10 |
has url => sub { Mojo::URL->new }; |
11 | 11 |
|
12 |
-my $START_LINE_RE = qr| |
|
12 |
+my $START_LINE_RE = qr/ |
|
13 | 13 |
^ |
14 |
- ([a-zA-Z]+) # Method |
|
14 |
+ ([a-zA-Z]+) # Method |
|
15 | 15 |
\s+ |
16 |
- ([0-9a-zA-Z\-._~:/?#[\]\@!\$&'()*+,;=\%]+) # Path |
|
17 |
- (?:\s+HTTP/(\d\.\d))? # Version |
|
16 |
+ ([0-9a-zA-Z!#\$\%&'()*+,\-.\/:;=?\@[\\\]^_`\{|\}~]+) # URL |
|
17 |
+ (?:\s+HTTP\/(\d\.\d))? # Version |
|
18 | 18 |
$ |
19 |
-|x; |
|
19 |
+/x; |
|
20 | 20 |
|
21 | 21 |
sub clone { |
22 | 22 |
my $self = shift; |
... | ... |
@@ -260,6 +260,8 @@ sub _parse_env { |
260 | 260 |
|
261 | 261 |
1; |
262 | 262 |
|
263 |
+=encoding utf8 |
|
264 |
+ |
|
263 | 265 |
=head1 NAME |
264 | 266 |
|
265 | 267 |
Mojo::Message::Request - HTTP request |
... | ... |
@@ -270,9 +272,9 @@ Mojo::Message::Request - HTTP request |
270 | 272 |
|
271 | 273 |
# Parse |
272 | 274 |
my $req = Mojo::Message::Request->new; |
273 |
- $req->parse("GET /foo HTTP/1.0\x0a\x0d"); |
|
274 |
- $req->parse("Content-Length: 12\x0a\x0d\x0a\x0d"); |
|
275 |
- $req->parse("Content-Type: text/plain\x0a\x0d\x0a\x0d"); |
|
275 |
+ $req->parse("GET /foo HTTP/1.0\x0d\x0a"); |
|
276 |
+ $req->parse("Content-Length: 12\x0d\x0a"); |
|
277 |
+ $req->parse("Content-Type: text/plain\x0d\x0a\x0d\x0a"); |
|
276 | 278 |
$req->parse('Hello World!'); |
277 | 279 |
say $req->method; |
278 | 280 |
say $req->headers->content_type; |
... | ... |
@@ -351,7 +353,7 @@ Access request cookies, usually L<Mojo::Cookie::Request> objects. |
351 | 353 |
|
352 | 354 |
=head2 extract_start_line |
353 | 355 |
|
354 |
- my $success = $req->extract_start_line(\$str); |
|
356 |
+ my $bool = $req->extract_start_line(\$str); |
|
355 | 357 |
|
356 | 358 |
Extract request line from string. |
357 | 359 |
|
... | ... |
@@ -369,13 +371,13 @@ Get a chunk of request line data starting from a specific position. |
369 | 371 |
|
370 | 372 |
=head2 is_secure |
371 | 373 |
|
372 |
- my $success = $req->is_secure; |
|
374 |
+ my $bool = $req->is_secure; |
|
373 | 375 |
|
374 | 376 |
Check if connection is secure. |
375 | 377 |
|
376 | 378 |
=head2 is_xhr |
377 | 379 |
|
378 |
- my $success = $req->is_xhr; |
|
380 |
+ my $bool = $req->is_xhr; |
|
379 | 381 |
|
380 | 382 |
Check C<X-Requested-With> header for C<XMLHttpRequest> value. |
381 | 383 |
|
... | ... |
@@ -385,16 +387,20 @@ Check C<X-Requested-With> header for C<XMLHttpRequest> value. |
385 | 387 |
my $foo = $req->param('foo'); |
386 | 388 |
my @foo = $req->param('foo'); |
387 | 389 |
|
388 |
-Access C<GET> and C<POST> parameters. Note that this method caches all data, |
|
389 |
-so it should not be called before the entire request body has been received. |
|
390 |
+Access GET and POST parameters. Note that this method caches all data, so it |
|
391 |
+should not be called before the entire request body has been received. Parts |
|
392 |
+of the request body need to be loaded into memory to parse POST parameters, so |
|
393 |
+you have to make sure it is not excessively large. |
|
390 | 394 |
|
391 | 395 |
=head2 params |
392 | 396 |
|
393 | 397 |
my $params = $req->params; |
394 | 398 |
|
395 |
-All C<GET> and C<POST> parameters, usually a L<Mojo::Parameters> object. Note |
|
396 |
-that this method caches all data, so it should not be called before the entire |
|
397 |
-request body has been received. |
|
399 |
+All GET and POST parameters, usually a L<Mojo::Parameters> object. Note that |
|
400 |
+this method caches all data, so it should not be called before the entire |
|
401 |
+request body has been received. Parts of the request body need to be loaded |
|
402 |
+into memory to parse POST parameters, so you have to make sure it is not |
|
403 |
+excessively large. |
|
398 | 404 |
|
399 | 405 |
# Get parameter value |
400 | 406 |
say $req->params->param('foo'); |
... | ... |
@@ -422,7 +428,7 @@ Proxy URL for request. |
422 | 428 |
|
423 | 429 |
my $params = $req->query_params; |
424 | 430 |
|
425 |
-All C<GET> parameters, usually a L<Mojo::Parameters> object. |
|
431 |
+All GET parameters, usually a L<Mojo::Parameters> object. |
|
426 | 432 |
|
427 | 433 |
# Turn GET parameters to hash and extract value |
428 | 434 |
say $req->query_params->to_hash->{foo}; |
... | ... |
@@ -140,6 +140,8 @@ sub is_status_class { |
140 | 140 |
|
141 | 141 |
1; |
142 | 142 |
|
143 |
+=encoding utf8 |
|
144 |
+ |
|
143 | 145 |
=head1 NAME |
144 | 146 |
|
145 | 147 |
Mojo::Message::Response - HTTP response |
... | ... |
@@ -149,10 +151,10 @@ Mojo::Message::Response - HTTP response |
149 | 151 |
use Mojo::Message::Response; |
150 | 152 |
|
151 | 153 |
# Parse |
152 |
- my $res = Mojo::Message::Reponse->new; |
|
153 |
- $res->parse("HTTP/1.0 200 OK\x0a\x0d"); |
|
154 |
- $res->parse("Content-Length: 12\x0a\x0d\x0a\x0d"); |
|
155 |
- $res->parse("Content-Type: text/plain\x0a\x0d\x0a\x0d"); |
|
154 |
+ my $res = Mojo::Message::Response->new; |
|
155 |
+ $res->parse("HTTP/1.0 200 OK\x0d\x0a"); |
|
156 |
+ $res->parse("Content-Length: 12\x0d\x0a"); |
|
157 |
+ $res->parse("Content-Type: text/plain\x0d\x0a\x0d\x0a"); |
|
156 | 158 |
$res->parse('Hello World!'); |
157 | 159 |
say $res->code; |
158 | 160 |
say $res->headers->content_type; |
... | ... |
@@ -214,7 +216,7 @@ Generate default response message for code. |
214 | 216 |
|
215 | 217 |
=head2 extract_start_line |
216 | 218 |
|
217 |
- my $success = $res->extract_start_line(\$str); |
|
219 |
+ my $bool = $res->extract_start_line(\$str); |
|
218 | 220 |
|
219 | 221 |
Extract status line from string. |
220 | 222 |
|
... | ... |
@@ -232,13 +234,13 @@ Get a chunk of status line data starting from a specific position. |
232 | 234 |
|
233 | 235 |
=head2 is_empty |
234 | 236 |
|
235 |
- my $success = $res->is_empty; |
|
237 |
+ my $bool = $res->is_empty; |
|
236 | 238 |
|
237 | 239 |
Check if this is a C<1xx>, C<204> or C<304> response. |
238 | 240 |
|
239 | 241 |
=head2 is_status_class |
240 | 242 |
|
241 |
- my $success = $res->is_status_class(200); |
|
243 |
+ my $bool = $res->is_status_class(200); |
|
242 | 244 |
|
243 | 245 |
Check response status class. |
244 | 246 |
|
... | ... |
@@ -2,7 +2,7 @@ package Mojo::Parameters; |
2 | 2 |
use Mojo::Base -base; |
3 | 3 |
use overload |
4 | 4 |
'@{}' => sub { shift->params }, |
5 |
- 'bool' => sub {1}, |
|
5 |
+ bool => sub {1}, |
|
6 | 6 |
'""' => sub { shift->to_string }, |
7 | 7 |
fallback => 1; |
8 | 8 |
|
... | ... |
@@ -230,7 +230,7 @@ following new ones. |
230 | 230 |
my $params = Mojo::Parameters->new(foo => ['ba;r', 'b;az']); |
231 | 231 |
my $params = Mojo::Parameters->new(foo => ['ba;r', 'b;az'], bar => 23); |
232 | 232 |
|
233 |
-Construct a new L<Mojo::Parameters> object and C<parse> parameters if |
|
233 |
+Construct a new L<Mojo::Parameters> object and L</"parse"> parameters if |
|
234 | 234 |
necessary. |
235 | 235 |
|
236 | 236 |
=head2 append |
... | ... |
@@ -2,7 +2,7 @@ package Mojo::Path; |
2 | 2 |
use Mojo::Base -base; |
3 | 3 |
use overload |
4 | 4 |
'@{}' => sub { shift->parts }, |
5 |
- 'bool' => sub {1}, |
|
5 |
+ bool => sub {1}, |
|
6 | 6 |
'""' => sub { shift->to_string }, |
7 | 7 |
fallback => 1; |
8 | 8 |
|
... | ... |
@@ -67,7 +67,7 @@ sub merge { |
67 | 67 |
sub parse { |
68 | 68 |
my $self = shift; |
69 | 69 |
$self->{path} = shift; |
70 |
- delete $self->{$_} for qw(leading_slash parts trailing_slash); |
|
70 |
+ delete @$self{qw(leading_slash parts trailing_slash)}; |
|
71 | 71 |
return $self; |
72 | 72 |
} |
73 | 73 |
|
... | ... |
@@ -81,7 +81,7 @@ sub to_abs_string { |
81 | 81 |
sub to_dir { |
82 | 82 |
my $clone = shift->clone; |
83 | 83 |
pop @{$clone->parts} unless $clone->trailing_slash; |
84 |
- return $clone->trailing_slash(@{$clone->parts} ? 1 : 0); |
|
84 |
+ return $clone->trailing_slash(!!@{$clone->parts}); |
|
85 | 85 |
} |
86 | 86 |
|
87 | 87 |
sub to_route { |
... | ... |
@@ -118,10 +118,10 @@ sub _parse { |
118 | 118 |
unless ($self->{parts}) { |
119 | 119 |
my $path = url_unescape do { my $tmp = delete($self->{path}); defined $tmp ? $tmp : '' }; |
120 | 120 |
my $charset = $self->charset; |
121 |
- $path = do { my $tmp = decode($charset, $path); defined $tmp ? $tmp : $path } if $charset; |
|
122 |
- $self->{leading_slash} = $path =~ s!^/!! ? 1 : undef; |
|
123 |
- $self->{trailing_slash} = $path =~ s!/$!! ? 1 : undef; |
|
124 |
- $self->{parts} = [split '/', $path, -1]; |
|
121 |
+ $path = do {my $decoded = decode($charset, $path); defined $decoded ? $decoded : $path} if $charset; |
|
122 |
+ $self->{leading_slash} = $path =~ s!^/!!; |
|
123 |
+ $self->{trailing_slash} = $path =~ s!/$!!; |
|
124 |
+ $self->{parts} = [split '/', $path, -1]; |
|
125 | 125 |
} |
126 | 126 |
|
127 | 127 |
return $self->{$name} unless @_; |
... | ... |
@@ -178,7 +178,7 @@ following new ones. |
178 | 178 |
my $path = Mojo::Path->new; |
179 | 179 |
my $path = Mojo::Path->new('/foo%2Fbar%3B/baz.html'); |
180 | 180 |
|
181 |
-Construct a new L<Mojo::Path> object and C<parse> path if necessary. |
|
181 |
+Construct a new L<Mojo::Path> object and L</"parse"> path if necessary. |
|
182 | 182 |
|
183 | 183 |
=head2 canonicalize |
184 | 184 |
|
... | ... |
@@ -189,6 +189,9 @@ Canonicalize path. |
189 | 189 |
# "/foo/baz" |
190 | 190 |
Mojo::Path->new('/foo/./bar/../baz')->canonicalize; |
191 | 191 |
|
192 |
+ # "/../baz" |
|
193 |
+ Mojo::Path->new('/foo/../bar/../../baz')->canonicalize; |
|
194 |
+ |
|
192 | 195 |
=head2 clone |
193 | 196 |
|
194 | 197 |
my $clone = $path->clone; |
... | ... |
@@ -197,7 +200,7 @@ Clone path. |
197 | 200 |
|
198 | 201 |
=head2 contains |
199 | 202 |
|
200 |
- my $success = $path->contains('/i/♥/mojolicious'); |
|
203 |
+ my $bool = $path->contains('/i/♥/mojolicious'); |
|
201 | 204 |
|
202 | 205 |
Check if path contains given prefix. |
203 | 206 |
|
... | ... |
@@ -213,8 +216,8 @@ Check if path contains given prefix. |
213 | 216 |
|
214 | 217 |
=head2 leading_slash |
215 | 218 |
|
216 |
- my $slash = $path->leading_slash; |
|
217 |
- $path = $path->leading_slash(1); |
|
219 |
+ my $bool = $path->leading_slash; |
|
220 |
+ $path = $path->leading_slash($bool); |
|
218 | 221 |
|
219 | 222 |
Path has a leading slash. Note that this method will normalize the path and |
220 | 223 |
that C<%2F> will be treated as C</> for security reasons. |
... | ... |
@@ -301,8 +304,8 @@ Turn path into a string. |
301 | 304 |
|
302 | 305 |
=head2 trailing_slash |
303 | 306 |
|
304 |
- my $slash = $path->trailing_slash; |
|
305 |
- $path = $path->trailing_slash(1); |
|
307 |
+ my $bool = $path->trailing_slash; |
|
308 |
+ $path = $path->trailing_slash($bool); |
|
306 | 309 |
|
307 | 310 |
Path has a trailing slash. Note that this method will normalize the path and |
308 | 311 |
that C<%2F> will be treated as C</> for security reasons. |
... | ... |
@@ -37,6 +37,8 @@ sub watch { croak 'Method "watch" not implemented by subclass' } |
37 | 37 |
|
38 | 38 |
1; |
39 | 39 |
|
40 |
+=encoding utf8 |
|
41 |
+ |
|
40 | 42 |
=head1 NAME |
41 | 43 |
|
42 | 44 |
Mojo::Reactor - Low level event reactor base class |
... | ... |
@@ -75,7 +77,9 @@ the following new ones. |
75 | 77 |
... |
76 | 78 |
}); |
77 | 79 |
|
78 |
-Emitted safely for exceptions caught in callbacks. |
|
80 |
+Emitted for exceptions caught in callbacks, fatal if unhandled. Note that if |
|
81 |
+this event is unhandled or fails it might kill your program, so you need to be |
|
82 |
+careful. |
|
79 | 83 |
|
80 | 84 |
$reactor->on(error => sub { |
81 | 85 |
my ($reactor, $err) = @_; |
... | ... |
@@ -119,14 +123,14 @@ readable or writable. Meant to be overloaded in a subclass. |
119 | 123 |
|
120 | 124 |
=head2 is_readable |
121 | 125 |
|
122 |
- my $success = $reactor->is_readable($handle); |
|
126 |
+ my $bool = $reactor->is_readable($handle); |
|
123 | 127 |
|
124 | 128 |
Quick non-blocking check if a handle is readable, useful for identifying |
125 | 129 |
tainted sockets. |
126 | 130 |
|
127 | 131 |
=head2 is_running |
128 | 132 |
|
129 |
- my $success = $reactor->is_running; |
|
133 |
+ my $bool = $reactor->is_running; |
|
130 | 134 |
|
131 | 135 |
Check if reactor is running. Meant to be overloaded in a subclass. |
132 | 136 |
|
... | ... |
@@ -154,8 +158,8 @@ amount of time in seconds. Meant to be overloaded in a subclass. |
154 | 158 |
|
155 | 159 |
=head2 remove |
156 | 160 |
|
157 |
- my $success = $reactor->remove($handle); |
|
158 |
- my $success = $reactor->remove($id); |
|
161 |
+ my $bool = $reactor->remove($handle); |
|
162 |
+ my $bool = $reactor->remove($id); |
|
159 | 163 |
|
160 | 164 |
Remove handle or timer. Meant to be overloaded in a subclass. |
161 | 165 |
|
... | ... |
@@ -163,7 +167,7 @@ Remove handle or timer. Meant to be overloaded in a subclass. |
163 | 167 |
|
164 | 168 |
$reactor->start; |
165 | 169 |
|
166 |
-Start watching for I/O and timer events, this will block until C<stop> is |
|
170 |
+Start watching for I/O and timer events, this will block until L</"stop"> is |
|
167 | 171 |
called. Note that some reactors stop automatically if there are no events |
168 | 172 |
being watched anymore. Meant to be overloaded in a subclass. |
169 | 173 |
|
... | ... |
@@ -72,6 +72,8 @@ sub _timer { |
72 | 72 |
|
73 | 73 |
1; |
74 | 74 |
|
75 |
+=encoding utf8 |
|
76 |
+ |
|
75 | 77 |
=head1 NAME |
76 | 78 |
|
77 | 79 |
Mojo::Reactor::EV - Low level event reactor with libev support |
... | ... |
@@ -127,7 +129,7 @@ Restart active timer. |
127 | 129 |
|
128 | 130 |
=head2 is_running |
129 | 131 |
|
130 |
- my $success = $reactor->is_running; |
|
132 |
+ my $bool = $reactor->is_running; |
|
131 | 133 |
|
132 | 134 |
Check if reactor is running. |
133 | 135 |
|
... | ... |
@@ -149,7 +151,7 @@ amount of time in seconds. |
149 | 151 |
|
150 | 152 |
$reactor->start; |
151 | 153 |
|
152 |
-Start watching for I/O and timer events, this will block until C<stop> is |
|
154 |
+Start watching for I/O and timer events, this will block until L</"stop"> is |
|
153 | 155 |
called or no events are being watched anymore. |
154 | 156 |
|
155 | 157 |
=head2 stop |
... | ... |
@@ -105,8 +105,8 @@ sub watch { |
105 | 105 |
sub _poll { shift->{poll} ||= IO::Poll->new } |
106 | 106 |
|
107 | 107 |
sub _sandbox { |
108 |
- my ($self, $desc, $cb) = (shift, shift, shift); |
|
109 |
- eval { $self->$cb(@_); 1 } or $self->emit_safe(error => "$desc failed: $@"); |
|
108 |
+ my ($self, $event, $cb) = (shift, shift, shift); |
|
109 |
+ eval { $self->$cb(@_); 1 } or $self->emit(error => "$event failed: $@"); |
|
110 | 110 |
} |
111 | 111 |
|
112 | 112 |
sub _timer { |
... | ... |
@@ -124,6 +124,8 @@ sub _timer { |
124 | 124 |
|
125 | 125 |
1; |
126 | 126 |
|
127 |
+=encoding utf8 |
|
128 |
+ |
|
127 | 129 |
=head1 NAME |
128 | 130 |
|
129 | 131 |
Mojo::Reactor::Poll - Low level event reactor with poll support |
... | ... |
@@ -180,7 +182,7 @@ readable or writable. |
180 | 182 |
|
181 | 183 |
=head2 is_running |
182 | 184 |
|
183 |
- my $success = $reactor->is_running; |
|
185 |
+ my $bool = $reactor->is_running; |
|
184 | 186 |
|
185 | 187 |
Check if reactor is running. |
186 | 188 |
|
... | ... |
@@ -200,8 +202,8 @@ amount of time in seconds. |
200 | 202 |
|
201 | 203 |
=head2 remove |
202 | 204 |
|
203 |
- my $success = $reactor->remove($handle); |
|
204 |
- my $success = $reactor->remove($id); |
|
205 |
+ my $bool = $reactor->remove($handle); |
|
206 |
+ my $bool = $reactor->remove($id); |
|
205 | 207 |
|
206 | 208 |
Remove handle or timer. |
207 | 209 |
|
... | ... |
@@ -209,7 +211,7 @@ Remove handle or timer. |
209 | 211 |
|
210 | 212 |
$reactor->start; |
211 | 213 |
|
212 |
-Start watching for I/O and timer events, this will block until C<stop> is |
|
214 |
+Start watching for I/O and timer events, this will block until L</"stop"> is |
|
213 | 215 |
called or no events are being watched anymore. |
214 | 216 |
|
215 | 217 |
=head2 stop |
... | ... |
@@ -2,7 +2,6 @@ package Mojo::Server; |
2 | 2 |
use Mojo::Base 'Mojo::EventEmitter'; |
3 | 3 |
|
4 | 4 |
use Carp 'croak'; |
5 |
-use FindBin; |
|
6 | 5 |
use Mojo::Loader; |
7 | 6 |
use Mojo::Util 'md5_sum'; |
8 | 7 |
use Scalar::Util 'blessed'; |
... | ... |
@@ -27,9 +26,10 @@ sub build_tx { shift->app->build_tx } |
27 | 26 |
sub load_app { |
28 | 27 |
my ($self, $path) = @_; |
29 | 28 |
|
30 |
- # Clean environment (reset FindBin) |
|
29 |
+ # Clean environment (reset FindBin defensively) |
|
31 | 30 |
{ |
32 | 31 |
local $0 = $path; |
32 |
+ require FindBin; |
|
33 | 33 |
FindBin->again; |
34 | 34 |
local $ENV{MOJO_APP_LOADER} = 1; |
35 | 35 |
local $ENV{MOJO_EXE}; |
... | ... |
@@ -55,6 +55,8 @@ sub run { croak 'Method "run" not implemented by subclass' } |
55 | 55 |
|
56 | 56 |
1; |
57 | 57 |
|
58 |
+=encoding utf8 |
|
59 |
+ |
|
58 | 60 |
=head1 NAME |
59 | 61 |
|
60 | 62 |
Mojo::Server - HTTP server base class |
... | ... |
@@ -121,8 +123,8 @@ the following new ones. |
121 | 123 |
|
122 | 124 |
my $server = Mojo::Server->new; |
123 | 125 |
|
124 |
-Construct a new L<Mojo::Server> object and subscribe to C<request> event with |
|
125 |
-default request handling. |
|
126 |
+Construct a new L<Mojo::Server> object and subscribe to L</"request"> event |
|
127 |
+with default request handling. |
|
126 | 128 |
|
127 | 129 |
=head2 build_app |
128 | 130 |
|
... | ... |
@@ -10,11 +10,14 @@ sub run { |
10 | 10 |
my $req = $tx->req->parse(\%ENV); |
11 | 11 |
$tx->local_port($ENV{SERVER_PORT})->remote_address($ENV{REMOTE_ADDR}); |
12 | 12 |
|
13 |
- # Request body |
|
13 |
+ # Request body (may block if we try to read too much) |
|
14 | 14 |
binmode STDIN; |
15 |
+ my $len = $req->headers->content_length; |
|
15 | 16 |
until ($req->is_finished) { |
16 |
- last unless my $read = STDIN->read(my $buffer, 131072, 0); |
|
17 |
+ my $chunk = ($len && $len < 131072) ? $len : 131072; |
|
18 |
+ last unless my $read = STDIN->read(my $buffer, $chunk, 0); |
|
17 | 19 |
$req->parse($buffer); |
20 |
+ last if ($len -= $read) <= 0; |
|
18 | 21 |
} |
19 | 22 |
|
20 | 23 |
# Handle request |
... | ... |
@@ -65,6 +68,8 @@ sub _write { |
65 | 68 |
|
66 | 69 |
1; |
67 | 70 |
|
71 |
+=encoding utf8 |
|
72 |
+ |
|
68 | 73 |
=head1 NAME |
69 | 74 |
|
70 | 75 |
Mojo::Server::CGI - CGI server |
... | ... |
@@ -109,8 +114,8 @@ implements the following new ones. |
109 | 114 |
|
110 | 115 |
=head2 nph |
111 | 116 |
|
112 |
- my $nph = $cgi->nph; |
|
113 |
- $cgi = $cgi->nph(1); |
|
117 |
+ my $bool = $cgi->nph; |
|
118 |
+ $cgi = $cgi->nph($bool); |
|
114 | 119 |
|
115 | 120 |
Activate non-parsed header mode. |
116 | 121 |
|
... | ... |
@@ -9,6 +9,7 @@ use Scalar::Util 'weaken'; |
9 | 9 |
|
10 | 10 |
use constant DEBUG => $ENV{MOJO_DAEMON_DEBUG} || 0; |
11 | 11 |
|
12 |
+has acceptors => sub { [] }; |
|
12 | 13 |
has [qw(backlog group silent user)]; |
13 | 14 |
has inactivity_timeout => sub { defined $ENV{MOJO_INACTIVITY_TIMEOUT} ? $ENV{MOJO_INACTIVITY_TIMEOUT} : 15 }; |
14 | 15 |
has ioloop => sub { Mojo::IOLoop->singleton }; |
... | ... |
@@ -20,7 +21,7 @@ sub DESTROY { |
20 | 21 |
my $self = shift; |
21 | 22 |
return unless my $loop = $self->ioloop; |
22 | 23 |
$self->_remove($_) for keys %{$self->{connections} || {}}; |
23 |
- $loop->remove($_) for @{$self->{acceptors} || []}; |
|
24 |
+ $loop->remove($_) for @{$self->acceptors}; |
|
24 | 25 |
} |
25 | 26 |
|
26 | 27 |
sub run { |
... | ... |
@@ -54,9 +55,9 @@ sub start { |
54 | 55 |
|
55 | 56 |
# Resume accepting connections |
56 | 57 |
my $loop = $self->ioloop; |
57 |
- if (my $acceptors = $self->{acceptors}) { |
|
58 |
- push @$acceptors, $loop->acceptor(delete $self->{servers}{$_}) |
|
59 |
- for keys %{$self->{servers}}; |
|
58 |
+ if (my $servers = $self->{servers}) { |
|
59 |
+ push @{$self->acceptors}, $loop->acceptor(delete $servers->{$_}) |
|
60 |
+ for keys %$servers; |
|
60 | 61 |
} |
61 | 62 |
|
62 | 63 |
# Start listening |
... | ... |
@@ -71,7 +72,7 @@ sub stop { |
71 | 72 |
|
72 | 73 |
# Suspend accepting connections but keep listen sockets open |
73 | 74 |
my $loop = $self->ioloop; |
74 |
- while (my $id = shift @{$self->{acceptors}}) { |
|
75 |
+ while (my $id = shift @{$self->acceptors}) { |
|
75 | 76 |
my $server = $self->{servers}{$id} = $loop->acceptor($id); |
76 | 77 |
$loop->remove($id); |
77 | 78 |
$server->stop; |
... | ... |
@@ -162,20 +163,19 @@ sub _listen { |
162 | 163 |
my $url = Mojo::URL->new($listen); |
163 | 164 |
my $query = $url->query; |
164 | 165 |
my $options = { |
165 |
- address => $url->host, |
|
166 |
- backlog => $self->backlog, |
|
167 |
- port => $url->port, |
|
168 |
- tls_ca => scalar $query->param('ca'), |
|
169 |
- tls_cert => scalar $query->param('cert'), |
|
170 |
- tls_key => scalar $query->param('key') |
|
166 |
+ address => $url->host, |
|
167 |
+ backlog => $self->backlog, |
|
168 |
+ port => $url->port, |
|
169 |
+ reuse => scalar $query->param('reuse'), |
|
171 | 170 |
}; |
171 |
+ $options->{"tls_$_"} = scalar $query->param($_) for qw(ca cert ciphers key); |
|
172 | 172 |
my $verify = $query->param('verify'); |
173 | 173 |
$options->{tls_verify} = hex $verify if defined $verify; |
174 | 174 |
delete $options->{address} if $options->{address} eq '*'; |
175 |
- my $tls = $options->{tls} = $url->protocol eq 'https' ? 1 : undef; |
|
175 |
+ my $tls = $options->{tls} = $url->protocol eq 'https'; |
|
176 | 176 |
|
177 | 177 |
weaken $self; |
178 |
- my $id = $self->ioloop->server( |
|
178 |
+ push @{$self->acceptors}, $self->ioloop->server( |
|
179 | 179 |
$options => sub { |
180 | 180 |
my ($loop, $stream, $id) = @_; |
181 | 181 |
|
... | ... |
@@ -196,12 +196,12 @@ sub _listen { |
196 | 196 |
sub { $self->app->log->debug('Inactivity timeout.') if $c->{tx} }); |
197 | 197 |
} |
198 | 198 |
); |
199 |
- push @{$self->{acceptors} ||= []}, $id; |
|
200 | 199 |
|
201 | 200 |
return if $self->silent; |
202 |
- $self->app->log->info(qq{Listening at "$listen".}); |
|
203 |
- $listen =~ s!//\*!//127.0.0.1!i; |
|
204 |
- say "Server available at $listen."; |
|
201 |
+ $self->app->log->info(qq{Listening at "$url".}); |
|
202 |
+ $query->params([]); |
|
203 |
+ $url->host('127.0.0.1') if $url->host eq '*'; |
|
204 |
+ say "Server available at $url."; |
|
205 | 205 |
} |
206 | 206 |
|
207 | 207 |
sub _read { |
... | ... |
@@ -260,6 +260,8 @@ sub _write { |
260 | 260 |
|
261 | 261 |
1; |
262 | 262 |
|
263 |
+=encoding utf8 |
|
264 |
+ |
|
263 | 265 |
=head1 NAME |
264 | 266 |
|
265 | 267 |
Mojo::Server::Daemon - Non-blocking I/O HTTP and WebSocket server |
... | ... |
@@ -290,14 +292,15 @@ Mojo::Server::Daemon - Non-blocking I/O HTTP and WebSocket server |
290 | 292 |
=head1 DESCRIPTION |
291 | 293 |
|
292 | 294 |
L<Mojo::Server::Daemon> is a full featured, highly portable non-blocking I/O |
293 |
-HTTP and WebSocket server, with C<IPv6>, C<TLS>, C<Comet> (long polling), |
|
294 |
-C<keep-alive>, connection pooling, timeout, cookie, multipart and multiple |
|
295 |
-event loop support. |
|
295 |
+HTTP and WebSocket server, with IPv6, TLS, Comet (long polling), keep-alive, |
|
296 |
+connection pooling, timeout, cookie, multipart and multiple event loop |
|
297 |
+support. |
|
296 | 298 |
|
297 |
-Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
|
298 |
-L<IO::Socket::SSL> (1.75+) are supported transparently through |
|
299 |
-L<Mojo::IOLoop>, and used if installed. Individual features can also be |
|
300 |
-disabled with the MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
299 |
+For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS |
|
300 |
+support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
|
301 |
+L<IO::Socket::SSL> (1.75+) will be used automatically by L<Mojo::IOLoop> if |
|
302 |
+they are installed. Individual features can also be disabled with the |
|
303 |
+MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
301 | 304 |
|
302 | 305 |
See L<Mojolicious::Guides::Cookbook> for more. |
303 | 306 |
|
... | ... |
@@ -310,6 +313,13 @@ L<Mojo::Server::Daemon> inherits all events from L<Mojo::Server>. |
310 | 313 |
L<Mojo::Server::Daemon> inherits all attributes from L<Mojo::Server> and |
311 | 314 |
implements the following new ones. |
312 | 315 |
|
316 |
+=head2 acceptors |
|
317 |
+ |
|
318 |
+ my $acceptors = $daemon->acceptors; |
|
319 |
+ $daemon = $daemon->acceptors([]); |
|
320 |
+ |
|
321 |
+Active acceptors. |
|
322 |
+ |
|
313 | 323 |
=head2 backlog |
314 | 324 |
|
315 | 325 |
my $backlog = $daemon->backlog; |
... | ... |
@@ -350,6 +360,9 @@ L<Mojo::IOLoop> singleton. |
350 | 360 |
List of one or more locations to listen on, defaults to the value of the |
351 | 361 |
MOJO_LISTEN environment variable or C<http://*:3000>. |
352 | 362 |
|
363 |
+ # Allow multiple servers to use the same port (SO_REUSEPORT) |
|
364 |
+ $daemon->listen(['http://*:8080?reuse=1']); |
|
365 |
+ |
|
353 | 366 |
# Listen on IPv6 interface |
354 | 367 |
$daemon->listen(['http://[::1]:4000']); |
355 | 368 |
|
... | ... |
@@ -365,22 +378,43 @@ MOJO_LISTEN environment variable or C<http://*:3000>. |
365 | 378 |
|
366 | 379 |
These parameters are currently available: |
367 | 380 |
|
368 |
-=over 4 |
|
381 |
+=over 2 |
|
369 | 382 |
|
370 | 383 |
=item ca |
371 | 384 |
|
385 |
+ ca=/etc/tls/ca.crt |
|
386 |
+ |
|
372 | 387 |
Path to TLS certificate authority file. |
373 | 388 |
|
374 | 389 |
=item cert |
375 | 390 |
|
391 |
+ cert=/etc/tls/server.crt |
|
392 |
+ |
|
376 | 393 |
Path to the TLS cert file, defaults to a built-in test certificate. |
377 | 394 |
|
395 |
+=item ciphers |
|
396 |
+ |
|
397 |
+ ciphers=AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH |
|
398 |
+ |
|
399 |
+Cipher specification string. |
|
400 |
+ |
|
378 | 401 |
=item key |
379 | 402 |
|
403 |
+ key=/etc/tls/server.key |
|
404 |
+ |
|
380 | 405 |
Path to the TLS key file, defaults to a built-in test key. |
381 | 406 |
|
407 |
+=item reuse |
|
408 |
+ |
|
409 |
+ reuse=1 |
|
410 |
+ |
|
411 |
+Allow multiple servers to use the same port with the C<SO_REUSEPORT> socket |
|
412 |
+option. |
|
413 |
+ |
|
382 | 414 |
=item verify |
383 | 415 |
|
416 |
+ verify=0x00 |
|
417 |
+ |
|
384 | 418 |
TLS verification mode, defaults to C<0x03>. |
385 | 419 |
|
386 | 420 |
=back |
... | ... |
@@ -401,8 +435,8 @@ Maximum number of keep-alive requests per connection, defaults to C<25>. |
401 | 435 |
|
402 | 436 |
=head2 silent |
403 | 437 |
|
404 |
- my $silent = $daemon->silent; |
|
405 |
- $daemon = $daemon->silent(1); |
|
438 |
+ my $bool = $daemon->silent; |
|
439 |
+ $daemon = $daemon->silent($bool); |
|
406 | 440 |
|
407 | 441 |
Disable console messages. |
408 | 442 |
|
... | ... |
@@ -56,7 +56,7 @@ sub run { |
56 | 56 |
exit 0 if $pid; |
57 | 57 |
setsid or die "Can't start a new session: $!"; |
58 | 58 |
|
59 |
- # Close file handles |
|
59 |
+ # Close filehandles |
|
60 | 60 |
open STDIN, '</dev/null'; |
61 | 61 |
open STDOUT, '>/dev/null'; |
62 | 62 |
open STDERR, '>&STDOUT'; |
... | ... |
@@ -134,7 +134,7 @@ sub _reap { |
134 | 134 |
# Clean up failed upgrade |
135 | 135 |
return unless ($self->{new} || '') eq $pid; |
136 | 136 |
$self->{prefork}->app->log->info('Zero downtime software upgrade failed.'); |
137 |
- delete $self->{$_} for qw(new upgrade); |
|
137 |
+ delete @$self{qw(new upgrade)}; |
|
138 | 138 |
} |
139 | 139 |
|
140 | 140 |
sub _stop { |
... | ... |
@@ -146,6 +146,8 @@ sub _stop { |
146 | 146 |
|
147 | 147 |
1; |
148 | 148 |
|
149 |
+=encoding utf8 |
|
150 |
+ |
|
149 | 151 |
=head1 NAME |
150 | 152 |
|
151 | 153 |
Mojo::Server::Hypnotoad - ALL GLORY TO THE HYPNOTOAD! |
... | ... |
@@ -161,13 +163,15 @@ Mojo::Server::Hypnotoad - ALL GLORY TO THE HYPNOTOAD! |
161 | 163 |
|
162 | 164 |
L<Mojo::Server::Hypnotoad> is a full featured, UNIX optimized, preforking |
163 | 165 |
non-blocking I/O HTTP and WebSocket server, built around the very well tested |
164 |
-and reliable L<Mojo::Server::Prefork>, with C<IPv6>, C<TLS>, C<Comet> (long |
|
165 |
-polling), C<keep-alive>, connection pooling, timeout, cookie, multipart, |
|
166 |
-multiple event loop and hot deployment support that just works. Note that the |
|
167 |
-server uses signals for process management, so you should avoid modifying |
|
168 |
-signal handlers in your applications. |
|
169 |
- |
|
170 |
-To start applications with it you can use the L<hypnotoad> script. |
|
166 |
+and reliable L<Mojo::Server::Prefork>, with IPv6, TLS, Comet (long polling), |
|
167 |
+keep-alive, connection pooling, timeout, cookie, multipart, multiple event |
|
168 |
+loop and hot deployment support that just works. Note that the server uses |
|
169 |
+signals for process management, so you should avoid modifying signal handlers |
|
170 |
+in your applications. |
|
171 |
+ |
|
172 |
+To start applications with it you can use the L<hypnotoad> script, for |
|
173 |
+L<Mojolicious> and L<Mojolicious::Lite> applications it will default to |
|
174 |
+C<production> mode. |
|
171 | 175 |
|
172 | 176 |
$ hypnotoad myapp.pl |
173 | 177 |
Server available at http://127.0.0.1:8080. |
... | ... |
@@ -177,42 +181,39 @@ You can run the same command again for automatic hot deployment. |
177 | 181 |
$ hypnotoad myapp.pl |
178 | 182 |
Starting hot deployment for Hypnotoad server 31841. |
179 | 183 |
|
180 |
-For L<Mojolicious> and L<Mojolicious::Lite> applications it will default to |
|
181 |
-C<production> mode. |
|
184 |
+This second invocation will load the application again, detect the process id |
|
185 |
+file with it, and send a L</"USR2"> signal to the already running server. |
|
182 | 186 |
|
183 |
-Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
|
184 |
-L<IO::Socket::SSL> (1.75+) are supported transparently through |
|
185 |
-L<Mojo::IOLoop>, and used if installed. Individual features can also be |
|
186 |
-disabled with the MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
187 |
+For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS |
|
188 |
+support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
|
189 |
+L<IO::Socket::SSL> (1.75+) will be used automatically by L<Mojo::IOLoop> if |
|
190 |
+they are installed. Individual features can also be disabled with the |
|
191 |
+MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
187 | 192 |
|
188 | 193 |
See L<Mojolicious::Guides::Cookbook> for more. |
189 | 194 |
|
190 |
-=head1 SIGNALS |
|
195 |
+=head1 MANAGER SIGNALS |
|
191 | 196 |
|
192 |
-L<Mojo::Server::Hypnotoad> can be controlled at runtime with the following |
|
193 |
-signals. |
|
197 |
+The L<Mojo::Server::Hypnotoad> manager process can be controlled at runtime |
|
198 |
+with the following signals. |
|
194 | 199 |
|
195 |
-=head2 Manager |
|
196 |
- |
|
197 |
-=over 2 |
|
198 |
- |
|
199 |
-=item INT, TERM |
|
200 |
+=head2 INT, TERM |
|
200 | 201 |
|
201 | 202 |
Shutdown server immediately. |
202 | 203 |
|
203 |
-=item QUIT |
|
204 |
+=head2 QUIT |
|
204 | 205 |
|
205 | 206 |
Shutdown server gracefully. |
206 | 207 |
|
207 |
-=item TTIN |
|
208 |
+=head2 TTIN |
|
208 | 209 |
|
209 | 210 |
Increase worker pool by one. |
210 | 211 |
|
211 |
-=item TTOU |
|
212 |
+=head2 TTOU |
|
212 | 213 |
|
213 | 214 |
Decrease worker pool by one. |
214 | 215 |
|
215 |
-=item USR2 |
|
216 |
+=head2 USR2 |
|
216 | 217 |
|
217 | 218 |
Attempt zero downtime software upgrade (hot deployment) without losing any |
218 | 219 |
incoming connections. |
... | ... |
@@ -228,25 +229,22 @@ incoming connections. |
228 | 229 |
|- Worker [3] |
229 | 230 |
+- Worker [4] |
230 | 231 |
|
231 |
-The new manager will automatically send a C<QUIT> signal to the old manager |
|
232 |
+The new manager will automatically send a L</"QUIT"> signal to the old manager |
|
232 | 233 |
and take over serving requests after starting up successfully. |
233 | 234 |
|
234 |
-=back |
|
235 |
+=head1 WORKER SIGNALS |
|
235 | 236 |
|
236 |
-=head2 Worker |
|
237 |
+L<Mojo::Server::Hypnotoad> worker processes can be controlled at runtime with |
|
238 |
+the following signals. |
|
237 | 239 |
|
238 |
-=over 2 |
|
239 |
- |
|
240 |
-=item INT, TERM |
|
240 |
+=head2 INT, TERM |
|
241 | 241 |
|
242 | 242 |
Stop worker immediately. |
243 | 243 |
|
244 |
-=item QUIT |
|
244 |
+=head2 QUIT |
|
245 | 245 |
|
246 | 246 |
Stop worker gracefully. |
247 | 247 |
|
248 |
-=back |
|
249 |
- |
|
250 | 248 |
=head1 SETTINGS |
251 | 249 |
|
252 | 250 |
L<Mojo::Server::Hypnotoad> can be configured with the following settings, see |
... | ... |
@@ -282,7 +280,7 @@ Listen backlog size, defaults to C<SOMAXCONN>. |
282 | 280 |
|
283 | 281 |
Maximum number of parallel client connections per worker process, defaults to |
284 | 282 |
C<1000>. Note that depending on how much your application may block, you might |
285 |
-want to decrease this value and increase C<workers> instead for better |
|
283 |
+want to decrease this value and increase L</"workers"> instead for better |
|
286 | 284 |
performance. |
287 | 285 |
|
288 | 286 |
=head2 graceful_timeout |
... | ... |
@@ -100,6 +100,8 @@ sub _spawn { |
100 | 100 |
|
101 | 101 |
1; |
102 | 102 |
|
103 |
+=encoding utf8 |
|
104 |
+ |
|
103 | 105 |
=head1 NAME |
104 | 106 |
|
105 | 107 |
Mojo::Server::Morbo - DOOOOOOOOOOOOOOOOOOM! |
... | ... |
@@ -115,20 +117,21 @@ Mojo::Server::Morbo - DOOOOOOOOOOOOOOOOOOM! |
115 | 117 |
|
116 | 118 |
L<Mojo::Server::Morbo> is a full featured, self-restart capable non-blocking |
117 | 119 |
I/O HTTP and WebSocket server, built around the very well tested and reliable |
118 |
-L<Mojo::Server::Daemon>, with C<IPv6>, C<TLS>, C<Comet> (long polling), |
|
119 |
-C<keep-alive>, connection pooling, timeout, cookie, multipart and multiple |
|
120 |
-event loop support. Note that the server uses signals for process management, |
|
121 |
-so you should avoid modifying signal handlers in your applications. |
|
120 |
+L<Mojo::Server::Daemon>, with IPv6, TLS, Comet (long polling), keep-alive, |
|
121 |
+connection pooling, timeout, cookie, multipart and multiple event loop |
|
122 |
+support. Note that the server uses signals for process management, so you |
|
123 |
+should avoid modifying signal handlers in your applications. |
|
122 | 124 |
|
123 | 125 |
To start applications with it you can use the L<morbo> script. |
124 | 126 |
|
125 | 127 |
$ morbo myapp.pl |
126 | 128 |
Server available at http://127.0.0.1:3000. |
127 | 129 |
|
128 |
-Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
|
129 |
-L<IO::Socket::SSL> (1.75+) are supported transparently through |
|
130 |
-L<Mojo::IOLoop>, and used if installed. Individual features can also be |
|
131 |
-disabled with the MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
130 |
+For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS |
|
131 |
+support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
|
132 |
+L<IO::Socket::SSL> (1.75+) will be used automatically by L<Mojo::IOLoop> if |
|
133 |
+they are installed. Individual features can also be disabled with the |
|
134 |
+MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
132 | 135 |
|
133 | 136 |
See L<Mojolicious::Guides::Cookbook> for more. |
134 | 137 |
|
... | ... |
@@ -152,7 +155,7 @@ the following new ones. |
152 | 155 |
|
153 | 156 |
=head2 check_file |
154 | 157 |
|
155 |
- my $success = $morbo->check_file('/home/sri/lib/MyApp.pm'); |
|
158 |
+ my $bool = $morbo->check_file('/home/sri/lib/MyApp.pm'); |
|
156 | 159 |
|
157 | 160 |
Check if file has been modified since last check. |
158 | 161 |
|
... | ... |
@@ -8,7 +8,7 @@ sub run { |
8 | 8 |
my $req = $tx->req->parse($env); |
9 | 9 |
$tx->local_port($env->{SERVER_PORT})->remote_address($env->{REMOTE_ADDR}); |
10 | 10 |
|
11 |
- # Request body |
|
11 |
+ # Request body (may block if we try to read too much) |
|
12 | 12 |
my $len = $env->{CONTENT_LENGTH}; |
13 | 13 |
until ($req->is_finished) { |
14 | 14 |
my $chunk = ($len && $len < 131072) ? $len : 131072; |
... | ... |
@@ -66,6 +66,8 @@ sub getline { |
66 | 66 |
|
67 | 67 |
1; |
68 | 68 |
|
69 |
+=encoding utf8 |
|
70 |
+ |
|
69 | 71 |
=head1 NAME |
70 | 72 |
|
71 | 73 |
Mojo::Server::PSGI - PSGI server |
... | ... |
@@ -64,7 +64,10 @@ sub run { |
64 | 64 |
# Clean manager environment |
65 | 65 |
local $SIG{INT} = local $SIG{TERM} = sub { $self->_term }; |
66 | 66 |
local $SIG{CHLD} = sub { |
67 |
- while ((my $pid = waitpid -1, WNOHANG) > 0) { $self->_reap($pid) } |
|
67 |
+ while ((my $pid = waitpid -1, WNOHANG) > 0) { |
|
68 |
+ $self->app->log->debug("Worker $pid stopped.") |
|
69 |
+ if delete $self->emit(reap => $pid)->{pool}{$pid}; |
|
70 |
+ } |
|
68 | 71 |
}; |
69 | 72 |
local $SIG{QUIT} = sub { $self->_term(1) }; |
70 | 73 |
local $SIG{TTIN} = sub { $self->workers($self->workers + 1) }; |
... | ... |
@@ -110,7 +113,8 @@ sub _manage { |
110 | 113 |
# Manage workers |
111 | 114 |
$self->emit('wait')->_heartbeat; |
112 | 115 |
my $log = $self->app->log; |
113 |
- while (my ($pid, $w) = each %{$self->{pool}}) { |
|
116 |
+ for my $pid (keys %{$self->{pool}}) { |
|
117 |
+ next unless my $w = $self->{pool}{$pid}; |
|
114 | 118 |
|
115 | 119 |
# No heartbeat (graceful stop) |
116 | 120 |
my $interval = $self->heartbeat_interval; |
... | ... |
@@ -151,14 +155,6 @@ sub _pid_file { |
151 | 155 |
print $handle $$; |
152 | 156 |
} |
153 | 157 |
|
154 |
-sub _reap { |
|
155 |
- my ($self, $pid) = @_; |
|
156 |
- |
|
157 |
- # Clean up dead worker |
|
158 |
- $self->app->log->debug("Worker $pid stopped.") |
|
159 |
- if delete $self->emit(reap => $pid)->{pool}{$pid}; |
|
160 |
-} |
|
161 |
- |
|
162 | 158 |
sub _spawn { |
163 | 159 |
my $self = shift; |
164 | 160 |
|
... | ... |
@@ -179,21 +175,21 @@ sub _spawn { |
179 | 175 |
sub { |
180 | 176 |
|
181 | 177 |
# Blocking ("ualarm" can't be imported on Windows) |
182 |
- my $l; |
|
178 |
+ my $lock; |
|
183 | 179 |
if ($_[1]) { |
184 | 180 |
eval { |
185 | 181 |
local $SIG{ALRM} = sub { die "alarm\n" }; |
186 | 182 |
my $old = Time::HiRes::ualarm $self->lock_timeout * 1000000; |
187 |
- $l = flock $handle, LOCK_EX; |
|
183 |
+ $lock = flock $handle, LOCK_EX; |
|
188 | 184 |
Time::HiRes::ualarm $old; |
189 | 185 |
}; |
190 |
- if ($@) { $l = $@ eq "alarm\n" ? 0 : die($@) } |
|
186 |
+ if ($@) { $lock = $@ eq "alarm\n" ? 0 : die($@) } |
|
191 | 187 |
} |
192 | 188 |
|
193 | 189 |
# Non blocking |
194 |
- else { $l = flock $handle, LOCK_EX | LOCK_NB } |
|
190 |
+ else { $lock = flock $handle, LOCK_EX | LOCK_NB } |
|
195 | 191 |
|
196 |
- return $l; |
|
192 |
+ return $lock; |
|
197 | 193 |
} |
198 | 194 |
); |
199 | 195 |
$loop->unlock(sub { flock $handle, LOCK_UN }); |
... | ... |
@@ -210,7 +206,7 @@ sub _spawn { |
210 | 206 |
# Clean worker environment |
211 | 207 |
$SIG{$_} = 'DEFAULT' for qw(INT TERM CHLD TTIN TTOU); |
212 | 208 |
$SIG{QUIT} = sub { $loop->max_connections(0) }; |
213 |
- delete $self->{$_} for qw(poll reader); |
|
209 |
+ delete @$self{qw(poll reader)}; |
|
214 | 210 |
|
215 | 211 |
$self->app->log->debug("Worker $$ started."); |
216 | 212 |
$loop->start; |
... | ... |
@@ -225,6 +221,8 @@ sub _term { |
225 | 221 |
|
226 | 222 |
1; |
227 | 223 |
|
224 |
+=encoding utf8 |
|
225 |
+ |
|
228 | 226 |
=head1 NAME |
229 | 227 |
|
230 | 228 |
Mojo::Server::Prefork - Preforking non-blocking I/O HTTP and WebSocket server |
... | ... |
@@ -256,60 +254,53 @@ Mojo::Server::Prefork - Preforking non-blocking I/O HTTP and WebSocket server |
256 | 254 |
|
257 | 255 |
L<Mojo::Server::Prefork> is a full featured, UNIX optimized, preforking |
258 | 256 |
non-blocking I/O HTTP and WebSocket server, built around the very well tested |
259 |
-and reliable L<Mojo::Server::Daemon>, with C<IPv6>, C<TLS>, C<Comet> (long |
|
260 |
-polling), C<keep-alive>, connection pooling, timeout, cookie, multipart and |
|
261 |
-multiple event loop support. Note that the server uses signals for process |
|
262 |
-management, so you should avoid modifying signal handlers in your |
|
263 |
-applications. |
|
257 |
+and reliable L<Mojo::Server::Daemon>, with IPv6, TLS, Comet (long polling), |
|
258 |
+keep-alive, connection pooling, timeout, cookie, multipart and multiple event |
|
259 |
+loop support. Note that the server uses signals for process management, so you |
|
260 |
+should avoid modifying signal handlers in your applications. |
|
264 | 261 |
|
265 |
-Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
|
266 |
-L<IO::Socket::SSL> (1.75+) are supported transparently through |
|
267 |
-L<Mojo::IOLoop>, and used if installed. Individual features can also be |
|
268 |
-disabled with the MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
262 |
+For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS |
|
263 |
+support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
|
264 |
+L<IO::Socket::SSL> (1.75+) will be used automatically by L<Mojo::IOLoop> if |
|
265 |
+they are installed. Individual features can also be disabled with the |
|
266 |
+MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
269 | 267 |
|
270 | 268 |
See L<Mojolicious::Guides::Cookbook> for more. |
271 | 269 |
|
272 |
-=head1 SIGNALS |
|
270 |
+=head1 MANAGER SIGNALS |
|
273 | 271 |
|
274 |
-L<Mojo::Server::Prefork> can be controlled at runtime with the following |
|
275 |
-signals. |
|
272 |
+The L<Mojo::Server::Prefork> manager process can be controlled at runtime with |
|
273 |
+the following signals. |
|
276 | 274 |
|
277 |
-=head2 Manager |
|
278 |
- |
|
279 |
-=over 2 |
|
280 |
- |
|
281 |
-=item INT, TERM |
|
275 |
+=head2 INT, TERM |
|
282 | 276 |
|
283 | 277 |
Shutdown server immediately. |
284 | 278 |
|
285 |
-=item QUIT |
|
279 |
+=head2 QUIT |
|
286 | 280 |
|
287 | 281 |
Shutdown server gracefully. |
288 | 282 |
|
289 |
-=item TTIN |
|
283 |
+=head2 TTIN |
|
290 | 284 |
|
291 | 285 |
Increase worker pool by one. |
292 | 286 |
|
293 |
-=item TTOU |
|
287 |
+=head2 TTOU |
|
294 | 288 |
|
295 | 289 |
Decrease worker pool by one. |
296 | 290 |
|
297 |
-=back |
|
291 |
+=head1 WORKER SIGNALS |
|
298 | 292 |
|
299 |
-=head2 Worker |
|
293 |
+L<Mojo::Server::Prefork> worker processes can be controlled at runtime with |
|
294 |
+the following signals. |
|
300 | 295 |
|
301 |
-=over 2 |
|
302 |
- |
|
303 |
-=item INT, TERM |
|
296 |
+=head2 INT, TERM |
|
304 | 297 |
|
305 | 298 |
Stop worker immediately. |
306 | 299 |
|
307 |
-=item QUIT |
|
300 |
+=head2 QUIT |
|
308 | 301 |
|
309 | 302 |
Stop worker gracefully. |
310 | 303 |
|
311 |
-=back |
|
312 |
- |
|
313 | 304 |
=head1 EVENTS |
314 | 305 |
|
315 | 306 |
L<Mojo::Server::Prefork> inherits all events from L<Mojo::Server::Daemon> and |
... | ... |
@@ -482,8 +473,8 @@ implements the following new ones. |
482 | 473 |
|
483 | 474 |
my $pid = $prefork->check_pid; |
484 | 475 |
|
485 |
-Get process id for running server from C<pid_file> or delete it if server is |
|
486 |
-not running. |
|
476 |
+Get process id for running server from L</"pid_file"> or delete it if server |
|
477 |
+is not running. |
|
487 | 478 |
|
488 | 479 |
say 'Server is not running' unless $prefork->check_pid; |
489 | 480 |
|
... | ... |
@@ -317,6 +317,8 @@ sub _wrap { |
317 | 317 |
|
318 | 318 |
1; |
319 | 319 |
|
320 |
+=encoding utf8 |
|
321 |
+ |
|
320 | 322 |
=head1 NAME |
321 | 323 |
|
322 | 324 |
Mojo::Template - Perl-ish templates! |
... | ... |
@@ -382,8 +384,8 @@ automatically enabled. |
382 | 384 |
%# Comment line, useful for debugging |
383 | 385 |
%% Replaced with "%", useful for generating templates |
384 | 386 |
|
385 |
-Escaping behavior can be reversed with the C<auto_escape> attribute, this is |
|
386 |
-the default in L<Mojolicious> C<.ep> templates for example. |
|
387 |
+Escaping behavior can be reversed with the L</"auto_escape"> attribute, this |
|
388 |
+is the default in L<Mojolicious> C<.ep> templates for example. |
|
387 | 389 |
|
388 | 390 |
<%= Perl expression, replaced with XML escaped result %> |
389 | 391 |
<%== Perl expression, replaced with result %> |
... | ... |
@@ -452,8 +454,8 @@ L<Mojo::Template> implements the following attributes. |
452 | 454 |
|
453 | 455 |
=head2 auto_escape |
454 | 456 |
|
455 |
- my $escape = $mt->auto_escape; |
|
456 |
- $mt = $mt->auto_escape(1); |
|
457 |
+ my $bool = $mt->auto_escape; |
|
458 |
+ $mt = $mt->auto_escape($bool); |
|
457 | 459 |
|
458 | 460 |
Activate automatic escaping. |
459 | 461 |
|
... | ... |
@@ -5,13 +5,25 @@ use Carp 'croak'; |
5 | 5 |
use Mojo::Message::Request; |
6 | 6 |
use Mojo::Message::Response; |
7 | 7 |
|
8 |
-has [qw(kept_alive local_address local_port previous remote_port)]; |
|
8 |
+has [qw(kept_alive local_address local_port remote_port)]; |
|
9 | 9 |
has req => sub { Mojo::Message::Request->new }; |
10 | 10 |
has res => sub { Mojo::Message::Response->new }; |
11 | 11 |
|
12 | 12 |
sub client_close { |
13 |
- my $self = shift; |
|
14 |
- $self->res->finish; |
|
13 |
+ my ($self, $close) = @_; |
|
14 |
+ |
|
15 |
+ # Remove code from parser errors |
|
16 |
+ my $res = $self->res->finish; |
|
17 |
+ if (my $err = $res->error) { $res->error($err) } |
|
18 |
+ |
|
19 |
+ # Premature connection close |
|
20 |
+ elsif ($close && !$res->code) { $res->error('Premature connection close') } |
|
21 |
+ |
|
22 |
+ # 400/500 |
|
23 |
+ elsif ($res->is_status_class(400) || $res->is_status_class(500)) { |
|
24 |
+ $res->error($res->message, $res->code); |
|
25 |
+ } |
|
26 |
+ |
|
15 | 27 |
return $self->server_close; |
16 | 28 |
} |
17 | 29 |
|
... | ... |
@@ -73,6 +85,8 @@ sub _state { |
73 | 85 |
|
74 | 86 |
1; |
75 | 87 |
|
88 |
+=encoding utf8 |
|
89 |
+ |
|
76 | 90 |
=head1 NAME |
77 | 91 |
|
78 | 92 |
Mojo::Transaction - Transaction base class |
... | ... |
@@ -148,16 +162,6 @@ Local interface address. |
148 | 162 |
|
149 | 163 |
Local interface port. |
150 | 164 |
|
151 |
-=head2 previous |
|
152 |
- |
|
153 |
- my $previous = $tx->previous; |
|
154 |
- $tx = $tx->previous(Mojo::Transaction->new); |
|
155 |
- |
|
156 |
-Previous transaction that triggered this followup transaction. |
|
157 |
- |
|
158 |
- # Path of previous request |
|
159 |
- say $tx->previous->req->url->path; |
|
160 |
- |
|
161 | 165 |
=head2 remote_port |
162 | 166 |
|
163 | 167 |
my $port = $tx->remote_port; |
... | ... |
@@ -187,8 +191,10 @@ implements the following new ones. |
187 | 191 |
=head2 client_close |
188 | 192 |
|
189 | 193 |
$tx->client_close; |
194 |
+ $tx->client_close(1); |
|
190 | 195 |
|
191 |
-Transaction closed client-side, used to implement user agents. |
|
196 |
+Transaction closed client-side, no actual connection close is assumed by |
|
197 |
+default, used to implement user agents. |
|
192 | 198 |
|
193 | 199 |
=head2 client_read |
194 | 200 |
|
... | ... |
@@ -220,7 +226,7 @@ Error and code. |
220 | 226 |
|
221 | 227 |
=head2 is_finished |
222 | 228 |
|
223 |
- my $success = $tx->is_finished; |
|
229 |
+ my $bool = $tx->is_finished; |
|
224 | 230 |
|
225 | 231 |
Check if transaction is finished. |
226 | 232 |
|
... | ... |
@@ -232,7 +238,7 @@ False. |
232 | 238 |
|
233 | 239 |
=head2 is_writing |
234 | 240 |
|
235 |
- my $success = $tx->is_writing; |
|
241 |
+ my $bool = $tx->is_writing; |
|
236 | 242 |
|
237 | 243 |
Check if transaction is writing. |
238 | 244 |
|
... | ... |
@@ -273,9 +279,9 @@ in a subclass. |
273 | 279 |
|
274 | 280 |
my $res = $tx->success; |
275 | 281 |
|
276 |
-Returns the L<Mojo::Message::Response> object (C<res>) if transaction was |
|
277 |
-successful or C<undef> otherwise. Connection and parser errors have only a |
|
278 |
-message in C<error>, 400 and 500 responses also a code. |
|
282 |
+Returns the L<Mojo::Message::Response> object from L</"res"> if transaction |
|
283 |
+was successful or C<undef> otherwise. Connection and parser errors have only a |
|
284 |
+message in L</"error">, 400 and 500 responses also a code. |
|
279 | 285 |
|
280 | 286 |
# Sensible exception handling |
281 | 287 |
if (my $res = $tx->success) { say $res->body } |
... | ... |
@@ -284,9 +290,6 @@ message in C<error>, 400 and 500 responses also a code. |
284 | 290 |
say $code ? "$code response: $err" : "Connection error: $err"; |
285 | 291 |
} |
286 | 292 |
|
287 |
-Error messages can be accessed with the C<error> method of the transaction |
|
288 |
-object. |
|
289 |
- |
|
290 | 293 |
=head1 SEE ALSO |
291 | 294 |
|
292 | 295 |
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
... | ... |
@@ -3,6 +3,8 @@ use Mojo::Base 'Mojo::Transaction'; |
3 | 3 |
|
4 | 4 |
use Mojo::Transaction::WebSocket; |
5 | 5 |
|
6 |
+has 'previous'; |
|
7 |
+ |
|
6 | 8 |
sub client_read { |
7 | 9 |
my ($self, $chunk) = @_; |
8 | 10 |
|
... | ... |
@@ -40,6 +42,13 @@ sub keep_alive { |
40 | 42 |
return !($req->version eq '1.0' || $res->version eq '1.0'); |
41 | 43 |
} |
42 | 44 |
|
45 |
+sub redirects { |
|
46 |
+ my $previous = shift; |
|
47 |
+ my @redirects; |
|
48 |
+ unshift @redirects, $previous while $previous = $previous->previous; |
|
49 |
+ return \@redirects; |
|
50 |
+} |
|
51 |
+ |
|
43 | 52 |
sub server_read { |
44 | 53 |
my ($self, $chunk) = @_; |
45 | 54 |
|
... | ... |
@@ -162,6 +171,8 @@ sub _write { |
162 | 171 |
|
163 | 172 |
1; |
164 | 173 |
|
174 |
+=encoding utf8 |
|
175 |
+ |
|
165 | 176 |
=head1 NAME |
166 | 177 |
|
167 | 178 |
Mojo::Transaction::HTTP - HTTP transaction |
... | ... |
@@ -245,7 +256,19 @@ object. |
245 | 256 |
|
246 | 257 |
=head1 ATTRIBUTES |
247 | 258 |
|
248 |
-L<Mojo::Transaction::HTTP> inherits all attributes from L<Mojo::Transaction>. |
|
259 |
+L<Mojo::Transaction::HTTP> inherits all attributes from L<Mojo::Transaction> |
|
260 |
+and implements the following new ones. |
|
261 |
+ |
|
262 |
+=head2 previous |
|
263 |
+ |
|
264 |
+ my $previous = $tx->previous; |
|
265 |
+ $tx = $tx->previous(Mojo::Transaction->new); |
|
266 |
+ |
|
267 |
+Previous transaction that triggered this followup transaction. |
|
268 |
+ |
|
269 |
+ # Paths of previous requests |
|
270 |
+ say $tx->previous->previous->req->url->path; |
|
271 |
+ say $tx->previous->req->url->path; |
|
249 | 272 |
|
250 | 273 |
=head1 METHODS |
251 | 274 |
|
... | ... |
@@ -266,16 +289,26 @@ Write data client-side, used to implement user agents. |
266 | 289 |
|
267 | 290 |
=head2 is_empty |
268 | 291 |
|
269 |
- my $success = $tx->is_empty; |
|
292 |
+ my $bool = $tx->is_empty; |
|
270 | 293 |
|
271 | 294 |
Check transaction for C<HEAD> request and C<1xx>, C<204> or C<304> response. |
272 | 295 |
|
273 | 296 |
=head2 keep_alive |
274 | 297 |
|
275 |
- my $success = $tx->keep_alive; |
|
298 |
+ my $bool = $tx->keep_alive; |
|
276 | 299 |
|
277 | 300 |
Check if connection can be kept alive. |
278 | 301 |
|
302 |
+=head2 redirects |
|
303 |
+ |
|
304 |
+ my $redirects = $tx->redirects; |
|
305 |
+ |
|
306 |
+Return a list of all previous transactions that preceded this followup |
|
307 |
+transaction. |
|
308 |
+ |
|
309 |
+ # Paths of all previous requests |
|
310 |
+ say $_->req->url->path for @{$tx->redirects}; |
|
311 |
+ |
|
279 | 312 |
=head2 server_read |
280 | 313 |
|
281 | 314 |
$tx->server_read($bytes); |
... | ... |
@@ -69,12 +69,12 @@ sub build_frame { |
69 | 69 |
warn "-- Extended 64bit payload ($len)\n$payload\n" if DEBUG; |
70 | 70 |
vec($prefix, 0, 8) = $masked ? (127 | 0b10000000) : 127; |
71 | 71 |
$frame .= $prefix; |
72 |
- $frame .= pack('NN', 0, $len & 0xFFFFFFFF); |
|
72 |
+ $frame .= pack('NN', 0, $len & 0xffffffff); |
|
73 | 73 |
} |
74 | 74 |
|
75 | 75 |
# Mask payload |
76 | 76 |
if ($masked) { |
77 |
- my $mask = pack 'N', int(rand 9999999); |
|
77 |
+ my $mask = pack 'N', int(rand 9 x 7); |
|
78 | 78 |
$payload = $mask . xor_encode($payload, $mask x 128); |
79 | 79 |
} |
80 | 80 |
|
... | ... |
@@ -95,9 +95,9 @@ sub client_handshake { |
95 | 95 |
$headers->connection('Upgrade') unless $headers->connection; |
96 | 96 |
$headers->sec_websocket_version(13) unless $headers->sec_websocket_version; |
97 | 97 |
|
98 |
- # Generate WebSocket challenge |
|
99 |
- $headers->sec_websocket_key(b64_encode(pack('N*', int(rand 9999999)), '')) |
|
100 |
- unless $headers->sec_websocket_key; |
|
98 |
+ # Generate 16 byte WebSocket challenge |
|
99 |
+ my $challenge = b64_encode sprintf('%16u', int(rand 9 x 16)), ''; |
|
100 |
+ $headers->sec_websocket_key($challenge) unless $headers->sec_websocket_key; |
|
101 | 101 |
} |
102 | 102 |
|
103 | 103 |
sub client_read { shift->server_read(@_) } |
... | ... |
@@ -211,8 +211,8 @@ sub send { |
211 | 211 |
: [1, 0, 0, 0, BINARY, $frame->{binary}]; |
212 | 212 |
} |
213 | 213 |
|
214 |
- # Text or object (forcing stringification) |
|
215 |
- $frame = [1, 0, 0, 0, TEXT, encode('UTF-8', "$frame")] |
|
214 |
+ # Text |
|
215 |
+ $frame = [1, 0, 0, 0, TEXT, encode('UTF-8', $frame)] |
|
216 | 216 |
if ref $frame ne 'ARRAY'; |
217 | 217 |
|
218 | 218 |
$self->once(drain => $cb) if $cb; |
... | ... |
@@ -302,6 +302,8 @@ sub _message { |
302 | 302 |
|
303 | 303 |
1; |
304 | 304 |
|
305 |
+=encoding utf8 |
|
306 |
+ |
|
305 | 307 |
=head1 NAME |
306 | 308 |
|
307 | 309 |
Mojo::Transaction::WebSocket - WebSocket transaction |
... | ... |
@@ -451,8 +453,8 @@ object. |
451 | 453 |
|
452 | 454 |
=head2 masked |
453 | 455 |
|
454 |
- my $masked = $ws->masked; |
|
455 |
- $ws = $ws->masked(1); |
|
456 |
+ my $bool = $ws->masked; |
|
457 |
+ $ws = $ws->masked($bool); |
|
456 | 458 |
|
457 | 459 |
Mask outgoing frames with XOR cipher and a random 32bit key. |
458 | 460 |
|
... | ... |
@@ -474,7 +476,7 @@ L<Mojo::Transaction> and implements the following new ones. |
474 | 476 |
my $ws = Mojo::Transaction::WebSocket->new; |
475 | 477 |
|
476 | 478 |
Construct a new L<Mojo::Transaction::WebSocket> object and subscribe to |
477 |
-C<frame> event with default message parser, which also handles C<PING> and |
|
479 |
+L</"frame"> event with default message parser, which also handles C<PING> and |
|
478 | 480 |
C<CLOSE> frames automatically. |
479 | 481 |
|
480 | 482 |
=head2 build_frame |
... | ... |
@@ -503,7 +505,7 @@ Build WebSocket frame. |
503 | 505 |
|
504 | 506 |
=head2 client_challenge |
505 | 507 |
|
506 |
- my $success = $ws->client_challenge; |
|
508 |
+ my $bool = $ws->client_challenge; |
|
507 | 509 |
|
508 | 510 |
Check WebSocket handshake challenge client-side, used to implement user |
509 | 511 |
agents. |
... | ... |
@@ -607,7 +609,7 @@ Handshake response, usually a L<Mojo::Message::Response> object. |
607 | 609 |
|
608 | 610 |
$ws = $ws->resume; |
609 | 611 |
|
610 |
-Resume C<handshake> transaction. |
|
612 |
+Resume L</"handshake"> transaction. |
|
611 | 613 |
|
612 | 614 |
=head2 send |
613 | 615 |
|
... | ... |
@@ -615,7 +617,6 @@ Resume C<handshake> transaction. |
615 | 617 |
$ws = $ws->send({text => $bytes}); |
616 | 618 |
$ws = $ws->send({json => {test => [1, 2, 3]}}); |
617 | 619 |
$ws = $ws->send([$fin, $rsv1, $rsv2, $rsv3, $op, $bytes]); |
618 |
- $ws = $ws->send(Mojo::ByteStream->new($chars)); |
|
619 | 620 |
$ws = $ws->send($chars); |
620 | 621 |
$ws = $ws->send($chars => sub {...}); |
621 | 622 |
|
... | ... |
@@ -1,9 +1,6 @@ |
1 | 1 |
package Mojo::URL; |
2 | 2 |
use Mojo::Base -base; |
3 |
-use overload |
|
4 |
- 'bool' => sub {1}, |
|
5 |
- '""' => sub { shift->to_string }, |
|
6 |
- fallback => 1; |
|
3 |
+use overload bool => sub {1}, '""' => sub { shift->to_string }, fallback => 1; |
|
7 | 4 |
|
8 | 5 |
use Mojo::Parameters; |
9 | 6 |
use Mojo::Path; |
... | ... |
@@ -179,8 +176,7 @@ sub to_rel { |
179 | 176 |
my @base_parts = @{$base_path->parts}; |
180 | 177 |
pop @base_parts unless $base_path->trailing_slash; |
181 | 178 |
while (@parts && @base_parts && $parts[0] eq $base_parts[0]) { |
182 |
- shift @parts; |
|
183 |
- shift @base_parts; |
|
179 |
+ shift @$_ for \@parts, \@base_parts; |
|
184 | 180 |
} |
185 | 181 |
my $path = $rel->path(Mojo::Path->new)->path; |
186 | 182 |
$path->leading_slash(1) if $rel->authority; |
... | ... |
@@ -243,7 +239,6 @@ Mojo::URL - Uniform Resource Locator |
243 | 239 |
$url->host('example.com'); |
244 | 240 |
$url->port(3000); |
245 | 241 |
$url->path('/foo/bar'); |
246 |
- $url->path('baz'); |
|
247 | 242 |
$url->query->param(foo => 'bar'); |
248 | 243 |
$url->fragment(23); |
249 | 244 |
say "$url"; |
... | ... |
@@ -309,7 +304,7 @@ following new ones. |
309 | 304 |
my $url = Mojo::URL->new; |
310 | 305 |
my $url = Mojo::URL->new('http://127.0.0.1:3000/foo?f=b&baz=2#foo'); |
311 | 306 |
|
312 |
-Construct a new L<Mojo::URL> object and C<parse> URL if necessary. |
|
307 |
+Construct a new L<Mojo::URL> object and L</"parse"> URL if necessary. |
|
313 | 308 |
|
314 | 309 |
=head2 authority |
315 | 310 |
|
... | ... |
@@ -336,7 +331,7 @@ Host part of this URL in punycode format. |
336 | 331 |
|
337 | 332 |
=head2 is_abs |
338 | 333 |
|
339 |
- my $success = $url->is_abs; |
|
334 |
+ my $bool = $url->is_abs; |
|
340 | 335 |
|
341 | 336 |
Check if URL is absolute. |
342 | 337 |
|
... | ... |
@@ -378,7 +373,7 @@ defaults to a L<Mojo::Path> object. |
378 | 373 |
|
379 | 374 |
my $proto = $url->protocol; |
380 | 375 |
|
381 |
-Normalized version of C<scheme>. |
|
376 |
+Normalized version of L</"scheme">. |
|
382 | 377 |
|
383 | 378 |
# "http" |
384 | 379 |
Mojo::URL->new('HtTp://example.com')->protocol; |
... | ... |
@@ -417,14 +412,40 @@ appended, defaults to a L<Mojo::Parameters> object. |
417 | 412 |
my $abs = $url->to_abs; |
418 | 413 |
my $abs = $url->to_abs(Mojo::URL->new('http://example.com/foo')); |
419 | 414 |
|
420 |
-Clone relative URL and turn it into an absolute one. |
|
415 |
+Clone relative URL and turn it into an absolute one using L</"base"> or |
|
416 |
+provided base URL. |
|
417 |
+ |
|
418 |
+ # "http://example.com/foo/baz.xml?test=123" |
|
419 |
+ Mojo::URL->new('baz.xml?test=123') |
|
420 |
+ ->to_abs(Mojo::URL->new('http://example.com/foo/bar.html')); |
|
421 |
+ |
|
422 |
+ # "http://example.com/baz.xml?test=123" |
|
423 |
+ Mojo::URL->new('/baz.xml?test=123') |
|
424 |
+ ->to_abs(Mojo::URL->new('http://example.com/foo/bar.html')); |
|
425 |
+ |
|
426 |
+ # "http://example.com/foo/baz.xml?test=123" |
|
427 |
+ Mojo::URL->new('//example.com/foo/baz.xml?test=123') |
|
428 |
+ ->to_abs(Mojo::URL->new('http://example.com/foo/bar.html')); |
|
421 | 429 |
|
422 | 430 |
=head2 to_rel |
423 | 431 |
|
424 | 432 |
my $rel = $url->to_rel; |
425 | 433 |
my $rel = $url->to_rel(Mojo::URL->new('http://example.com/foo')); |
426 | 434 |
|
427 |
-Clone absolute URL and turn it into a relative one. |
|
435 |
+Clone absolute URL and turn it into a relative one using L</"base"> or |
|
436 |
+provided base URL. |
|
437 |
+ |
|
438 |
+ # "foo/bar.html?test=123" |
|
439 |
+ Mojo::URL->new('http://example.com/foo/bar.html?test=123') |
|
440 |
+ ->to_rel(Mojo::URL->new('http://example.com')); |
|
441 |
+ |
|
442 |
+ # "bar.html?test=123" |
|
443 |
+ Mojo::URL->new('http://example.com/foo/bar.html?test=123') |
|
444 |
+ ->to_rel(Mojo::URL->new('http://example.com/foo/')); |
|
445 |
+ |
|
446 |
+ # "//example.com/foo/bar.html?test=123" |
|
447 |
+ Mojo::URL->new('http://example.com/foo/bar.html?test=123') |
|
448 |
+ ->to_rel(Mojo::URL->new('http://')); |
|
428 | 449 |
|
429 | 450 |
=head2 to_string |
430 | 451 |
|
... | ... |
@@ -19,6 +19,8 @@ sub slurp { shift->asset->slurp } |
19 | 19 |
|
20 | 20 |
1; |
21 | 21 |
|
22 |
+=encoding utf8 |
|
23 |
+ |
|
22 | 24 |
=head1 NAME |
23 | 25 |
|
24 | 26 |
Mojo::Upload - Upload |
... | ... |
@@ -4,12 +4,12 @@ use Mojo::Base 'Mojo::EventEmitter'; |
4 | 4 |
# "Fry: Since when is the Internet about robbing people of their privacy? |
5 | 5 |
# Bender: August 6, 1991." |
6 | 6 |
use Carp 'croak'; |
7 |
-use List::Util 'first'; |
|
8 | 7 |
use Mojo::IOLoop; |
9 |
-use Mojo::Server::Daemon; |
|
10 | 8 |
use Mojo::URL; |
11 |
-use Mojo::Util 'monkey_patch'; |
|
9 |
+use Mojo::Util qw(deprecated monkey_patch); |
|
12 | 10 |
use Mojo::UserAgent::CookieJar; |
11 |
+use Mojo::UserAgent::Proxy; |
|
12 |
+use Mojo::UserAgent::Server; |
|
13 | 13 |
use Mojo::UserAgent::Transactor; |
14 | 14 |
use Scalar::Util 'weaken'; |
15 | 15 |
|
... | ... |
@@ -19,14 +19,15 @@ has ca => sub { $ENV{MOJO_CA_FILE} }; |
19 | 19 |
has cert => sub { $ENV{MOJO_CERT_FILE} }; |
20 | 20 |
has connect_timeout => sub { $ENV{MOJO_CONNECT_TIMEOUT} || 10 }; |
21 | 21 |
has cookie_jar => sub { Mojo::UserAgent::CookieJar->new }; |
22 |
-has [qw(http_proxy https_proxy local_address no_proxy)]; |
|
22 |
+has 'local_address'; |
|
23 | 23 |
has inactivity_timeout => sub { defined $ENV{MOJO_INACTIVITY_TIMEOUT} ? $ENV{MOJO_INACTIVITY_TIMEOUT} : 20 }; |
24 | 24 |
has ioloop => sub { Mojo::IOLoop->new }; |
25 | 25 |
has key => sub { $ENV{MOJO_KEY_FILE} }; |
26 | 26 |
has max_connections => 5; |
27 | 27 |
has max_redirects => sub { $ENV{MOJO_MAX_REDIRECTS} || 0 }; |
28 |
-has name => 'Mojolicious (Perl)'; |
|
28 |
+has proxy => sub { Mojo::UserAgent::Proxy->new }; |
|
29 | 29 |
has request_timeout => sub { defined $ENV{MOJO_REQUEST_TIMEOUT} ? $ENV{MOJO_REQUEST_TIMEOUT} : 0 }; |
30 |
+has server => sub { Mojo::UserAgent::Server->new(ioloop => shift->ioloop) }; |
|
30 | 31 |
has transactor => sub { Mojo::UserAgent::Transactor->new }; |
31 | 32 |
|
32 | 33 |
# Common HTTP methods |
... | ... |
@@ -40,51 +41,89 @@ for my $name (qw(DELETE GET HEAD OPTIONS PATCH POST PUT)) { |
40 | 41 |
|
41 | 42 |
sub DESTROY { shift->_cleanup } |
42 | 43 |
|
43 |
-our $singleton_app; |
|
44 |
-sub app { |
|
45 |
- my ($self, $app) = @_; |
|
46 |
- |
|
47 |
- # Singleton application |
|
48 |
- return $singleton_app = $app ? $app : $singleton_app unless ref $self; |
|
49 |
- |
|
50 |
- # Default to singleton application |
|
51 |
- return $self->{app} || $singleton_app unless $app; |
|
52 |
- $self->{app} = $app; |
|
44 |
+# DEPRECATED in Top Hat! |
|
45 |
+sub new { |
|
46 |
+ my $self = shift->SUPER::new; |
|
47 |
+ while (my $name = shift) { $self->$name(shift) } |
|
53 | 48 |
return $self; |
54 | 49 |
} |
55 | 50 |
|
51 |
+# DEPRECATED in Top Hat! |
|
52 |
+sub app { |
|
53 |
+ deprecated "Mojo::UserAgent::app is DEPRECATED in favor of" |
|
54 |
+ . " Mojo::UserAgent::Server::app"; |
|
55 |
+ shift->_delegate('server', 'app', @_); |
|
56 |
+} |
|
57 |
+ |
|
58 |
+# DEPRECATED in Top Hat! |
|
56 | 59 |
sub app_url { |
57 |
- my $self = shift; |
|
58 |
- $self->_server(@_); |
|
59 |
- return Mojo::URL->new("$self->{proto}://localhost:$self->{port}/"); |
|
60 |
+ deprecated "Mojo::UserAgent::app_url is DEPRECATED in favor of" |
|
61 |
+ . " Mojo::UserAgent::Server::url"; |
|
62 |
+ shift->_delegate('server', 'url', @_); |
|
60 | 63 |
} |
61 | 64 |
|
62 | 65 |
sub build_tx { shift->transactor->tx(@_) } |
63 | 66 |
sub build_websocket_tx { shift->transactor->websocket(@_) } |
64 | 67 |
|
68 |
+# DEPRECATED in Top Hat! |
|
65 | 69 |
sub detect_proxy { |
66 |
- my $self = shift; |
|
67 |
- $self->http_proxy($ENV{HTTP_PROXY} || $ENV{http_proxy}); |
|
68 |
- $self->https_proxy($ENV{HTTPS_PROXY} || $ENV{https_proxy}); |
|
69 |
- return $self->no_proxy([split /,/, $ENV{NO_PROXY} || $ENV{no_proxy} || '']); |
|
70 |
+ deprecated "Mojo::UserAgent::detect_proxy is DEPRECATED in favor of" |
|
71 |
+ . " Mojo::UserAgent::Proxy::detect"; |
|
72 |
+ shift->tap(sub { $_->proxy->detect }); |
|
70 | 73 |
} |
71 | 74 |
|
75 |
+# DEPRECATED in Top Hat! |
|
76 |
+sub http_proxy { |
|
77 |
+ deprecated "Mojo::UserAgent::http_proxy is DEPRECATED in favor of" |
|
78 |
+ . " Mojo::UserAgent::Proxy::http"; |
|
79 |
+ shift->_delegate('proxy', 'http', @_); |
|
80 |
+} |
|
81 |
+ |
|
82 |
+# DEPRECATED in Top Hat! |
|
83 |
+sub https_proxy { |
|
84 |
+ deprecated "Mojo::UserAgent::https_proxy is DEPRECATED in favor of" |
|
85 |
+ . " Mojo::UserAgent::Proxy::https"; |
|
86 |
+ shift->_delegate('proxy', 'https', @_); |
|
87 |
+} |
|
88 |
+ |
|
89 |
+# DEPRECATED in Top Hat! |
|
90 |
+sub name { |
|
91 |
+ deprecated "Mojo::UserAgent::name is DEPRECATED in favor of" |
|
92 |
+ . " Mojo::UserAgent::Transactor::name"; |
|
93 |
+ shift->_delegate('transactor', 'name', @_); |
|
94 |
+} |
|
95 |
+ |
|
96 |
+# DEPRECATED in Top Hat! |
|
97 |
+sub no_proxy { |
|
98 |
+ deprecated "Mojo::UserAgent::no_proxy is DEPRECATED in favor of" |
|
99 |
+ . " Mojo::UserAgent::Proxy::not"; |
|
100 |
+ shift->_delegate('proxy', 'not', @_); |
|
101 |
+} |
|
102 |
+ |
|
103 |
+# DEPRECATED in Top Hat! |
|
72 | 104 |
sub need_proxy { |
73 |
- my ($self, $host) = @_; |
|
74 |
- return !first { $host =~ /\Q$_\E$/ } @{$self->no_proxy || []}; |
|
105 |
+ deprecated "Mojo::UserAgent::need_proxy is DEPRECATED in favor of" |
|
106 |
+ . " Mojo::UserAgent::Proxy::is_needed"; |
|
107 |
+ shift->proxy->is_needed(@_); |
|
75 | 108 |
} |
76 | 109 |
|
77 | 110 |
sub start { |
78 | 111 |
my ($self, $tx, $cb) = @_; |
79 | 112 |
|
113 |
+ # Fork safety |
|
114 |
+ unless (($self->{pid} = defined $self->{pid} ? $self->{pid} : $$) eq $$) { |
|
115 |
+ delete $self->_cleanup->{pid}; |
|
116 |
+ $self->server->restart; |
|
117 |
+ } |
|
118 |
+ |
|
80 | 119 |
# Non-blocking |
81 | 120 |
if ($cb) { |
82 | 121 |
warn "-- Non-blocking request (@{[$tx->req->url->to_abs]})\n" if DEBUG; |
83 | 122 |
unless ($self->{nb}) { |
84 | 123 |
croak 'Blocking request in progress' if keys %{$self->{connections}}; |
85 | 124 |
warn "-- Switching to non-blocking mode\n" if DEBUG; |
86 |
- $self->_cleanup; |
|
87 |
- $self->{nb}++; |
|
125 |
+ $self->server->ioloop(Mojo::IOLoop->singleton); |
|
126 |
+ $self->_cleanup->{nb}++; |
|
88 | 127 |
} |
89 | 128 |
return $self->_start($tx, $cb); |
90 | 129 |
} |
... | ... |
@@ -94,8 +133,8 @@ sub start { |
94 | 133 |
if ($self->{nb}) { |
95 | 134 |
croak 'Non-blocking requests in progress' if keys %{$self->{connections}}; |
96 | 135 |
warn "-- Switching to blocking mode\n" if DEBUG; |
97 |
- $self->_cleanup; |
|
98 |
- delete $self->{nb}; |
|
136 |
+ $self->server->ioloop($self->ioloop); |
|
137 |
+ delete $self->_cleanup->{nb}; |
|
99 | 138 |
} |
100 | 139 |
$self->_start($tx => sub { shift->ioloop->stop; $tx = shift }); |
101 | 140 |
$self->ioloop->start; |
... | ... |
@@ -109,50 +148,17 @@ sub websocket { |
109 | 148 |
$self->start($self->build_websocket_tx(@_), $cb); |
110 | 149 |
} |
111 | 150 |
|
112 |
-sub _cache { |
|
113 |
- my ($self, $name, $id) = @_; |
|
114 |
- |
|
115 |
- # Enqueue and enforce connection limit |
|
116 |
- my $old = $self->{cache} ||= []; |
|
117 |
- if ($id) { |
|
118 |
- my $max = $self->max_connections; |
|
119 |
- $self->_remove(shift(@$old)->[1]) while @$old > $max; |
|
120 |
- push @$old, [$name, $id] if $max; |
|
121 |
- return undef; |
|
122 |
- } |
|
123 |
- |
|
124 |
- # Dequeue |
|
125 |
- my $found; |
|
126 |
- my $loop = $self->_loop; |
|
127 |
- my $new = $self->{cache} = []; |
|
128 |
- for my $cached (@$old) { |
|
129 |
- |
|
130 |
- # Search for id/name and remove corrupted connections |
|
131 |
- if (!$found && ($cached->[1] eq $name || $cached->[0] eq $name)) { |
|
132 |
- my $stream = $loop->stream($cached->[1]); |
|
133 |
- if ($stream && !$stream->is_readable) { $found = $cached->[1] } |
|
134 |
- else { $loop->remove($cached->[1]) } |
|
135 |
- } |
|
136 |
- |
|
137 |
- # Requeue |
|
138 |
- else { push @$new, $cached } |
|
139 |
- } |
|
140 |
- |
|
141 |
- return $found; |
|
142 |
-} |
|
143 |
- |
|
144 | 151 |
sub _cleanup { |
145 | 152 |
my $self = shift; |
146 | 153 |
return unless my $loop = $self->_loop; |
147 | 154 |
|
148 | 155 |
# Clean up active connections (by closing them) |
149 |
- $self->_handle($_ => 1) for keys %{$self->{connections} || {}}; |
|
156 |
+ $self->_handle($_, 1) for keys %{$self->{connections} || {}}; |
|
150 | 157 |
|
151 | 158 |
# Clean up keep-alive connections |
152 |
- $loop->remove($_->[1]) for @{delete $self->{cache} || []}; |
|
159 |
+ $loop->remove($_->[1]) for @{delete $self->{queue} || []}; |
|
153 | 160 |
|
154 |
- # Stop server |
|
155 |
- delete $self->{server}; |
|
161 |
+ return $self; |
|
156 | 162 |
} |
157 | 163 |
|
158 | 164 |
sub _connect { |
... | ... |
@@ -166,7 +172,7 @@ sub _connect { |
166 | 172 |
local_address => $self->local_address, |
167 | 173 |
port => $port, |
168 | 174 |
timeout => $self->connect_timeout, |
169 |
- tls => $proto eq 'https' ? 1 : 0, |
|
175 |
+ tls => $proto eq 'https', |
|
170 | 176 |
tls_ca => $self->ca, |
171 | 177 |
tls_cert => $self->cert, |
172 | 178 |
tls_key => $self->key, |
... | ... |
@@ -178,10 +184,11 @@ sub _connect { |
178 | 184 |
return $self->_error($id, $err) if $err; |
179 | 185 |
|
180 | 186 |
# Connection established |
181 |
- $stream->on(timeout => sub { $self->_error($id, 'Inactivity timeout') }); |
|
182 |
- $stream->on(close => sub { $self->_handle($id => 1) }); |
|
183 |
- $stream->on(error => sub { $self && $self->_error($id, pop, 1) }); |
|
184 |
- $stream->on(read => sub { $self->_read($id => pop) }); |
|
187 |
+ $stream->on( |
|
188 |
+ timeout => sub { $self->_error($id, 'Inactivity timeout', 1) }); |
|
189 |
+ $stream->on(close => sub { $self->_handle($id, 1) }); |
|
190 |
+ $stream->on(error => sub { $self && $self->_error($id, pop) }); |
|
191 |
+ $stream->on(read => sub { $self->_read($id, pop) }); |
|
185 | 192 |
$cb->(); |
186 | 193 |
} |
187 | 194 |
); |
... | ... |
@@ -196,19 +203,19 @@ sub _connect_proxy { |
196 | 203 |
$new => sub { |
197 | 204 |
my ($self, $tx) = @_; |
198 | 205 |
|
199 |
- # CONNECT failed |
|
200 |
- unless ((defined $tx->res->code ? $tx->res->code : '') eq '200') { |
|
206 |
+ # CONNECT failed (connection needs to be kept alive) |
|
207 |
+ unless ($tx->keep_alive && $tx->res->is_status_class(200)) { |
|
201 | 208 |
$old->req->error('Proxy connection failed'); |
202 |
- return $self->_finish($old, $cb); |
|
209 |
+ return $self->$cb($old); |
|
203 | 210 |
} |
204 | 211 |
|
205 | 212 |
# Prevent proxy reassignment and start real transaction |
206 | 213 |
$old->req->proxy(0); |
207 |
- return $self->_start($old->connection($tx->connection), $cb) |
|
214 |
+ my $id = $tx->connection; |
|
215 |
+ return $self->_start($old->connection($id), $cb) |
|
208 | 216 |
unless $tx->req->url->protocol eq 'https'; |
209 | 217 |
|
210 | 218 |
# TLS upgrade |
211 |
- return unless my $id = $tx->connection; |
|
212 | 219 |
my $loop = $self->_loop; |
213 | 220 |
my $handle = $loop->stream($id)->steal_handle; |
214 | 221 |
my $c = delete $self->{connections}{$id}; |
... | ... |
@@ -245,7 +252,7 @@ sub _connection { |
245 | 252 |
# Reuse connection |
246 | 253 |
my $id = $tx->connection; |
247 | 254 |
my ($proto, $host, $port) = $self->transactor->endpoint($tx); |
248 |
- $id ||= $self->_cache("$proto:$host:$port"); |
|
255 |
+ $id ||= $self->_dequeue("$proto:$host:$port", 1); |
|
249 | 256 |
if ($id && !ref $id) { |
250 | 257 |
warn "-- Reusing connection ($proto:$host:$port)\n" if DEBUG; |
251 | 258 |
$self->{connections}{$id} = {cb => $cb, tx => $tx}; |
... | ... |
@@ -268,32 +275,47 @@ sub _connection { |
268 | 275 |
return $id; |
269 | 276 |
} |
270 | 277 |
|
271 |
-sub _error { |
|
272 |
- my ($self, $id, $err, $emit) = @_; |
|
273 |
- if (my $tx = $self->{connections}{$id}{tx}) { $tx->res->error($err) } |
|
274 |
- $self->emit(error => $err) if $emit; |
|
275 |
- $self->_handle($id => $err); |
|
278 |
+# DEPRECATED in Top Hat! |
|
279 |
+sub _delegate { |
|
280 |
+ my ($self, $attr, $name) = (shift, shift, shift); |
|
281 |
+ return $self->$attr->$name unless @_; |
|
282 |
+ $self->$attr->$name(@_); |
|
283 |
+ return $self; |
|
276 | 284 |
} |
277 | 285 |
|
278 |
-sub _finish { |
|
279 |
- my ($self, $tx, $cb, $close) = @_; |
|
286 |
+sub _dequeue { |
|
287 |
+ my ($self, $name, $test) = @_; |
|
280 | 288 |
|
281 |
- # Remove code from parser errors |
|
282 |
- my $res = $tx->res; |
|
283 |
- if (my $err = $res->error) { $res->error($err) } |
|
289 |
+ my $found; |
|
290 |
+ my $loop = $self->_loop; |
|
291 |
+ my $old = $self->{queue} || []; |
|
292 |
+ my $new = $self->{queue} = []; |
|
293 |
+ for my $queued (@$old) { |
|
294 |
+ push @$new, $queued and next if $found || !grep { $_ eq $name } @$queued; |
|
295 |
+ |
|
296 |
+ # Search for id/name and sort out corrupted connections if necessary |
|
297 |
+ next unless my $stream = $loop->stream($queued->[1]); |
|
298 |
+ $test && $stream->is_readable ? $stream->close : ($found = $queued->[1]); |
|
299 |
+ } |
|
284 | 300 |
|
285 |
- else { |
|
301 |
+ return $found; |
|
302 |
+} |
|
286 | 303 |
|
287 |
- # Premature connection close |
|
288 |
- if ($close && !$res->code) { $res->error('Premature connection close') } |
|
304 |
+sub _enqueue { |
|
305 |
+ my ($self, $name, $id) = @_; |
|
289 | 306 |
|
290 |
- # 400/500 |
|
291 |
- elsif ($res->is_status_class(400) || $res->is_status_class(500)) { |
|
292 |
- $res->error($res->message, $res->code); |
|
293 |
- } |
|
294 |
- } |
|
307 |
+ # Enforce connection limit |
|
308 |
+ my $queue = $self->{queue} ||= []; |
|
309 |
+ my $max = $self->max_connections; |
|
310 |
+ $self->_remove(shift(@$queue)->[1]) while @$queue > $max; |
|
311 |
+ push @$queue, [$name, $id] if $max; |
|
312 |
+} |
|
295 | 313 |
|
296 |
- $self->$cb($tx); |
|
314 |
+sub _error { |
|
315 |
+ my ($self, $id, $err, $timeout) = @_; |
|
316 |
+ if (my $tx = $self->{connections}{$id}{tx}) { $tx->res->error($err) } |
|
317 |
+ elsif (!$timeout) { return $self->emit(error => $err) } |
|
318 |
+ $self->_handle($id, 1); |
|
297 | 319 |
} |
298 | 320 |
|
299 | 321 |
sub _handle { |
... | ... |
@@ -316,7 +338,7 @@ sub _handle { |
316 | 338 |
elsif ($old && (my $new = $self->_upgrade($id))) { |
317 | 339 |
if (my $jar = $self->cookie_jar) { $jar->extract($old) } |
318 | 340 |
$old->client_close; |
319 |
- $self->_finish($new, $c->{cb}); |
|
341 |
+ $c->{cb}->($self, $new); |
|
320 | 342 |
$new->client_read($old->res->content->leftovers); |
321 | 343 |
} |
322 | 344 |
|
... | ... |
@@ -325,11 +347,10 @@ sub _handle { |
325 | 347 |
$self->_remove($id, $close); |
326 | 348 |
return unless $old; |
327 | 349 |
if (my $jar = $self->cookie_jar) { $jar->extract($old) } |
328 |
- $old->client_close; |
|
350 |
+ $old->client_close($close); |
|
329 | 351 |
|
330 | 352 |
# Handle redirects |
331 |
- $self->_finish($new || $old, $c->{cb}, $close) |
|
332 |
- unless $self->_redirect($c, $old); |
|
353 |
+ $c->{cb}->($self, $new || $old) unless $self->_redirect($c, $old); |
|
333 | 354 |
} |
334 | 355 |
} |
335 | 356 |
|
... | ... |
@@ -354,77 +375,34 @@ sub _remove { |
354 | 375 |
|
355 | 376 |
# Close connection |
356 | 377 |
my $tx = (delete($self->{connections}{$id}) || {})->{tx}; |
357 |
- unless (!$close && $tx && $tx->keep_alive && !$tx->error) { |
|
358 |
- $self->_cache($id); |
|
378 |
+ if ($close || !$tx || !$tx->keep_alive || $tx->error) { |
|
379 |
+ $self->_dequeue($id); |
|
359 | 380 |
return $self->_loop->remove($id); |
360 | 381 |
} |
361 | 382 |
|
362 |
- # Keep connection alive |
|
363 |
- $self->_cache(join(':', $self->transactor->endpoint($tx)), $id) |
|
364 |
- unless uc $tx->req->method eq 'CONNECT' && (defined $tx->res->code ? $tx->res->code : '') eq '200'; |
|
383 |
+ # Keep connection alive (CONNECT requests get upgraded) |
|
384 |
+ $self->_enqueue(join(':', $self->transactor->endpoint($tx)), $id) |
|
385 |
+ unless uc $tx->req->method eq 'CONNECT'; |
|
365 | 386 |
} |
366 | 387 |
|
367 | 388 |
sub _redirect { |
368 | 389 |
my ($self, $c, $old) = @_; |
369 |
- |
|
370 |
- # Follow redirect unless the maximum has been reached already |
|
371 | 390 |
return undef unless my $new = $self->transactor->redirect($old); |
372 |
- my $redirects = delete $c->{redirects} || 0; |
|
373 |
- return undef unless $redirects < $self->max_redirects; |
|
374 |
- my $id = $self->_start($new, delete $c->{cb}); |
|
375 |
- |
|
376 |
- return $self->{connections}{$id}{redirects} = $redirects + 1; |
|
377 |
-} |
|
378 |
- |
|
379 |
-sub _server { |
|
380 |
- my ($self, $proto) = @_; |
|
381 |
- |
|
382 |
- # Reuse server |
|
383 |
- return $self->{server} if $self->{server} && !$proto; |
|
384 |
- |
|
385 |
- # Start application server |
|
386 |
- my $loop = $self->_loop; |
|
387 |
- my $server = $self->{server} |
|
388 |
- = Mojo::Server::Daemon->new(ioloop => $loop, silent => 1); |
|
389 |
- my $port = $self->{port} ||= $loop->generate_port; |
|
390 |
- die "Couldn't find a free TCP port for application.\n" unless $port; |
|
391 |
- $self->{proto} = $proto ||= 'http'; |
|
392 |
- $server->listen(["$proto://127.0.0.1:$port"])->start; |
|
393 |
- warn "-- Application server started ($proto://127.0.0.1:$port)\n" if DEBUG; |
|
394 |
- return $server; |
|
391 |
+ return undef unless @{$old->redirects} < $self->max_redirects; |
|
392 |
+ return $self->_start($new, delete $c->{cb}); |
|
395 | 393 |
} |
396 | 394 |
|
397 | 395 |
sub _start { |
398 | 396 |
my ($self, $tx, $cb) = @_; |
399 | 397 |
|
400 |
- # Embedded server (update application if necessary) |
|
401 |
- my $req = $tx->req; |
|
402 |
- my $url = $req->url; |
|
403 |
- if ($self->{port} || !$url->is_abs) { |
|
404 |
- if (my $app = $self->app) { $self->_server->app($app) } |
|
405 |
- my $base = $self->app_url; |
|
406 |
- $url->scheme($base->scheme)->authority($base->authority) |
|
407 |
- unless $url->is_abs; |
|
398 |
+ # Application server |
|
399 |
+ my $url = $tx->req->url; |
|
400 |
+ unless ($url->is_abs) { |
|
401 |
+ my $base = $self->server->url; |
|
402 |
+ $url->scheme($base->scheme)->authority($base->authority); |
|
408 | 403 |
} |
409 | 404 |
|
410 |
- # Proxy |
|
411 |
- $self->detect_proxy if $ENV{MOJO_PROXY}; |
|
412 |
- my $proto = $url->protocol; |
|
413 |
- if ($self->need_proxy($url->host)) { |
|
414 |
- |
|
415 |
- # HTTP proxy |
|
416 |
- my $http = $self->http_proxy; |
|
417 |
- $req->proxy($http) if $http && !defined $req->proxy && $proto eq 'http'; |
|
418 |
- |
|
419 |
- # HTTPS proxy |
|
420 |
- my $https = $self->https_proxy; |
|
421 |
- $req->proxy($https) if $https && !defined $req->proxy && $proto eq 'https'; |
|
422 |
- } |
|
423 |
- |
|
424 |
- # We identify ourselves and accept gzip compression |
|
425 |
- my $headers = $req->headers; |
|
426 |
- $headers->user_agent($self->name) unless $headers->user_agent; |
|
427 |
- $headers->accept_encoding('gzip') if (! $headers->accept_encoding && $Compress::Raw::Zlib::VERSION); |
|
405 |
+ $self->proxy->inject($tx); |
|
428 | 406 |
if (my $jar = $self->cookie_jar) { $jar->inject($tx) } |
429 | 407 |
|
430 | 408 |
# Connect and add request timeout if necessary |
... | ... |
@@ -432,7 +410,7 @@ sub _start { |
432 | 410 |
if (my $timeout = $self->request_timeout) { |
433 | 411 |
weaken $self; |
434 | 412 |
$self->{connections}{$id}{timeout} = $self->_loop->timer( |
435 |
- $timeout => sub { $self->_error($id => 'Request timeout') }); |
|
413 |
+ $timeout => sub { $self->_error($id, 'Request timeout') }); |
|
436 | 414 |
} |
437 | 415 |
|
438 | 416 |
return $id; |
... | ... |
@@ -494,15 +472,14 @@ Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent |
494 | 472 |
} |
495 | 473 |
|
496 | 474 |
# Quick JSON API request with Basic authentication |
497 |
- say $ua->get('https://sri:s3cret@search.twitter.com/search.json?q=perl') |
|
498 |
- ->res->json('/results/0/text'); |
|
475 |
+ say $ua->get('https://sri:s3cret@example.com/search.json?q=perl') |
|
476 |
+ ->res->json('/results/0/title'); |
|
499 | 477 |
|
500 | 478 |
# Extract data from HTML and XML resources |
501 |
- say $ua->get('mojolicio.us')->res->dom->html->head->title->text; |
|
479 |
+ say $ua->get('www.perl.org')->res->dom->html->head->title->text; |
|
502 | 480 |
|
503 | 481 |
# Scrape the latest headlines from a news site |
504 |
- say $ua->max_redirects(5)->get('www.reddit.com/r/perl/') |
|
505 |
- ->res->dom('p.title > a.title')->pluck('text')->shuffle; |
|
482 |
+ say $ua->get('perlnews.org')->res->dom('h2 > a')->text->shuffle; |
|
506 | 483 |
|
507 | 484 |
# IPv6 PUT request with content |
508 | 485 |
my $tx |
... | ... |
@@ -514,7 +491,7 @@ Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent |
514 | 491 |
|
515 | 492 |
# TLS certificate authentication and JSON POST |
516 | 493 |
my $tx = $ua->cert('tls.crt')->key('tls.key') |
517 |
- ->post('https://mojolicio.us' => json => {top => 'secret'}); |
|
494 |
+ ->post('https://example.com' => json => {top => 'secret'}); |
|
518 | 495 |
|
519 | 496 |
# Blocking parallel requests (does not work inside a running event loop) |
520 | 497 |
my $delay = Mojo::IOLoop->delay; |
... | ... |
@@ -557,14 +534,19 @@ Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent |
557 | 534 |
=head1 DESCRIPTION |
558 | 535 |
|
559 | 536 |
L<Mojo::UserAgent> is a full featured non-blocking I/O HTTP and WebSocket user |
560 |
-agent, with C<IPv6>, C<TLS>, C<SNI>, C<IDNA>, C<Comet> (long polling), |
|
561 |
-C<keep-alive>, connection pooling, timeout, cookie, multipart, proxy, C<gzip> |
|
562 |
-compression and multiple event loop support. |
|
537 |
+agent, with IPv6, TLS, SNI, IDNA, Comet (long polling), keep-alive, connection |
|
538 |
+pooling, timeout, cookie, multipart, proxy, gzip compression and multiple |
|
539 |
+event loop support. |
|
540 |
+ |
|
541 |
+All connections will be reset automatically if a new process has been forked, |
|
542 |
+this allows multiple processes to share the same L<Mojo::UserAgent> object |
|
543 |
+safely. |
|
563 | 544 |
|
564 |
-Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
|
565 |
-L<IO::Socket::SSL> (1.75+) are supported transparently through |
|
566 |
-L<Mojo::IOLoop>, and used if installed. Individual features can also be |
|
567 |
-disabled with the MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
545 |
+For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS |
|
546 |
+support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
|
547 |
+L<IO::Socket::SSL> (1.75+) will be used automatically by L<Mojo::IOLoop> if |
|
548 |
+they are installed. Individual features can also be disabled with the |
|
549 |
+MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
568 | 550 |
|
569 | 551 |
See L<Mojolicious::Guides::Cookbook> for more. |
570 | 552 |
|
... | ... |
@@ -580,7 +562,8 @@ the following new ones. |
580 | 562 |
... |
581 | 563 |
}); |
582 | 564 |
|
583 |
-Emitted if an error occurs that can't be associated with a transaction. |
|
565 |
+Emitted if an error occurs that can't be associated with a transaction, fatal |
|
566 |
+if unhandled. |
|
584 | 567 |
|
585 | 568 |
$ua->on(error => sub { |
586 | 569 |
my ($ua, $err) = @_; |
... | ... |
@@ -646,20 +629,6 @@ L<Mojo::UserAgent::CookieJar> object. |
646 | 629 |
# Disable cookie jar |
647 | 630 |
$ua->cookie_jar(0); |
648 | 631 |
|
649 |
-=head2 http_proxy |
|
650 |
- |
|
651 |
- my $proxy = $ua->http_proxy; |
|
652 |
- $ua = $ua->http_proxy('http://sri:secret@127.0.0.1:8080'); |
|
653 |
- |
|
654 |
-Proxy server to use for HTTP and WebSocket requests. |
|
655 |
- |
|
656 |
-=head2 https_proxy |
|
657 |
- |
|
658 |
- my $proxy = $ua->https_proxy; |
|
659 |
- $ua = $ua->https_proxy('http://sri:secret@127.0.0.1:8080'); |
|
660 |
- |
|
661 |
-Proxy server to use for HTTPS and WebSocket requests. |
|
662 |
- |
|
663 | 632 |
=head2 inactivity_timeout |
664 | 633 |
|
665 | 634 |
my $timeout = $ua->inactivity_timeout; |
... | ... |
@@ -699,7 +668,7 @@ Local address to bind to. |
699 | 668 |
$ua = $ua->max_connections(5); |
700 | 669 |
|
701 | 670 |
Maximum number of keep-alive connections that the user agent will retain |
702 |
-before it starts closing the oldest cached ones, defaults to C<5>. |
|
671 |
+before it starts closing the oldest ones, defaults to C<5>. |
|
703 | 672 |
|
704 | 673 |
=head2 max_redirects |
705 | 674 |
|
... | ... |
@@ -709,19 +678,15 @@ before it starts closing the oldest cached ones, defaults to C<5>. |
709 | 678 |
Maximum number of redirects the user agent will follow before it fails, |
710 | 679 |
defaults to the value of the MOJO_MAX_REDIRECTS environment variable or C<0>. |
711 | 680 |
|
712 |
-=head2 name |
|
713 |
- |
|
714 |
- my $name = $ua->name; |
|
715 |
- $ua = $ua->name('Mojolicious'); |
|
716 |
- |
|
717 |
-Value for C<User-Agent> request header, defaults to C<Mojolicious (Perl)>. |
|
681 |
+=head2 proxy |
|
718 | 682 |
|
719 |
-=head2 no_proxy |
|
683 |
+ my $proxy = $ua->proxy; |
|
684 |
+ $ua = $ua->proxy(Mojo::UserAgent::Proxy->new); |
|
720 | 685 |
|
721 |
- my $no_proxy = $ua->no_proxy; |
|
722 |
- $ua = $ua->no_proxy([qw(localhost intranet.mojolicio.us)]); |
|
686 |
+Proxy manager, defaults to a L<Mojo::UserAgent::Proxy> object. |
|
723 | 687 |
|
724 |
-Domains that don't require a proxy server to be used. |
|
688 |
+ # Detect proxy servers from environment |
|
689 |
+ $ua->proxy->detect; |
|
725 | 690 |
|
726 | 691 |
=head2 request_timeout |
727 | 692 |
|
... | ... |
@@ -737,47 +702,37 @@ indefinitely. The timeout will reset for every followed redirect. |
737 | 702 |
# Total limit of 5 seconds, of which 3 seconds may be spent connecting |
738 | 703 |
$ua->max_redirects(0)->connect_timeout(3)->request_timeout(5); |
739 | 704 |
|
740 |
-=head2 transactor |
|
705 |
+=head2 server |
|
741 | 706 |
|
742 |
- my $t = $ua->transactor; |
|
743 |
- $ua = $ua->transactor(Mojo::UserAgent::Transactor->new); |
|
707 |
+ my $server = $ua->server; |
|
708 |
+ $ua = $ua->server(Mojo::UserAgent::Server->new); |
|
744 | 709 |
|
745 |
-Transaction builder, defaults to a L<Mojo::UserAgent::Transactor> object. |
|
746 |
- |
|
747 |
-=head1 METHODS |
|
748 |
- |
|
749 |
-L<Mojo::UserAgent> inherits all methods from L<Mojo::EventEmitter> and |
|
750 |
-implements the following new ones. |
|
751 |
- |
|
752 |
-=head2 app |
|
753 |
- |
|
754 |
- my $app = Mojo::UserAgent->app; |
|
755 |
- Mojo::UserAgent->app(MyApp->new); |
|
756 |
- my $app = $ua->app; |
|
757 |
- $ua = $ua->app(MyApp->new); |
|
758 |
- |
|
759 |
-Application relative URLs will be processed with, instance specific |
|
760 |
-applications override the global default. |
|
710 |
+Application server relative URLs will be processed with, defaults to a |
|
711 |
+L<Mojo::UserAgent::Server> object. |
|
761 | 712 |
|
762 | 713 |
# Introspect |
763 |
- say $ua->app->secret; |
|
714 |
+ say $ua->server->app->secret; |
|
764 | 715 |
|
765 | 716 |
# Change log level |
766 |
- $ua->app->log->level('fatal'); |
|
717 |
+ $ua->server->app->log->level('fatal'); |
|
718 |
+ |
|
719 |
+ # Port currently used for processing relative URLs |
|
720 |
+ say $ua->server->url->port; |
|
767 | 721 |
|
768 |
- # Change application behavior |
|
769 |
- $ua->app->defaults(testing => 'oh yea!'); |
|
722 |
+=head2 transactor |
|
770 | 723 |
|
771 |
-=head2 app_url |
|
724 |
+ my $t = $ua->transactor; |
|
725 |
+ $ua = $ua->transactor(Mojo::UserAgent::Transactor->new); |
|
772 | 726 |
|
773 |
- my $url = $ua->app_url; |
|
774 |
- my $url = $ua->app_url('http'); |
|
775 |
- my $url = $ua->app_url('https'); |
|
727 |
+Transaction builder, defaults to a L<Mojo::UserAgent::Transactor> object. |
|
776 | 728 |
|
777 |
-Get absolute L<Mojo::URL> object for C<app> and switch protocol if necessary. |
|
729 |
+ # Change name of user agent |
|
730 |
+ $ua->transactor->name('MyUA 1.0'); |
|
778 | 731 |
|
779 |
- # Port currently used for processing relative URLs |
|
780 |
- say $ua->app_url->port; |
|
732 |
+=head1 METHODS |
|
733 |
+ |
|
734 |
+L<Mojo::UserAgent> inherits all methods from L<Mojo::EventEmitter> and |
|
735 |
+implements the following new ones. |
|
781 | 736 |
|
782 | 737 |
=head2 build_tx |
783 | 738 |
|
... | ... |
@@ -814,7 +769,7 @@ L<Mojo::UserAgent::Transactor/"websocket">. |
814 | 769 |
my $tx = $ua->delete( |
815 | 770 |
'http://example.com' => {DNT => 1} => json => {a => 'b'}); |
816 | 771 |
|
817 |
-Perform blocking HTTP C<DELETE> request and return resulting |
|
772 |
+Perform blocking DELETE request and return resulting |
|
818 | 773 |
L<Mojo::Transaction::HTTP> object, takes the same arguments as |
819 | 774 |
L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also |
820 | 775 |
append a callback to perform requests non-blocking. |
... | ... |
@@ -825,14 +780,6 @@ append a callback to perform requests non-blocking. |
825 | 780 |
}); |
826 | 781 |
Mojo::IOLoop->start unless Mojo::IOLoop->is_running; |
827 | 782 |
|
828 |
-=head2 detect_proxy |
|
829 |
- |
|
830 |
- $ua = $ua->detect_proxy; |
|
831 |
- |
|
832 |
-Check environment variables HTTP_PROXY, http_proxy, HTTPS_PROXY, https_proxy, |
|
833 |
-NO_PROXY and no_proxy for proxy information. Automatic proxy detection can be |
|
834 |
-enabled with the MOJO_PROXY environment variable. |
|
835 |
- |
|
836 | 783 |
=head2 get |
837 | 784 |
|
838 | 785 |
my $tx = $ua->get('example.com'); |
... | ... |
@@ -840,10 +787,10 @@ enabled with the MOJO_PROXY environment variable. |
840 | 787 |
my $tx = $ua->get('http://example.com' => {DNT => 1} => form => {a => 'b'}); |
841 | 788 |
my $tx = $ua->get('http://example.com' => {DNT => 1} => json => {a => 'b'}); |
842 | 789 |
|
843 |
-Perform blocking HTTP C<GET> request and return resulting |
|
844 |
-L<Mojo::Transaction::HTTP> object, takes the same arguments as |
|
845 |
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also |
|
846 |
-append a callback to perform requests non-blocking. |
|
790 |
+Perform blocking GET request and return resulting L<Mojo::Transaction::HTTP> |
|
791 |
+object, takes the same arguments as L<Mojo::UserAgent::Transactor/"tx"> |
|
792 |
+(except for the method). You can also append a callback to perform requests |
|
793 |
+non-blocking. |
|
847 | 794 |
|
848 | 795 |
$ua->get('http://example.com' => sub { |
849 | 796 |
my ($ua, $tx) = @_; |
... | ... |
@@ -860,10 +807,10 @@ append a callback to perform requests non-blocking. |
860 | 807 |
my $tx = $ua->head( |
861 | 808 |
'http://example.com' => {DNT => 1} => json => {a => 'b'}); |
862 | 809 |
|
863 |
-Perform blocking HTTP C<HEAD> request and return resulting |
|
864 |
-L<Mojo::Transaction::HTTP> object, takes the same arguments as |
|
865 |
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also |
|
866 |
-append a callback to perform requests non-blocking. |
|
810 |
+Perform blocking HEAD request and return resulting L<Mojo::Transaction::HTTP> |
|
811 |
+object, takes the same arguments as L<Mojo::UserAgent::Transactor/"tx"> |
|
812 |
+(except for the method). You can also append a callback to perform requests |
|
813 |
+non-blocking. |
|
867 | 814 |
|
868 | 815 |
$ua->head('http://example.com' => sub { |
869 | 816 |
my ($ua, $tx) = @_; |
... | ... |
@@ -871,12 +818,6 @@ append a callback to perform requests non-blocking. |
871 | 818 |
}); |
872 | 819 |
Mojo::IOLoop->start unless Mojo::IOLoop->is_running; |
873 | 820 |
|
874 |
-=head2 need_proxy |
|
875 |
- |
|
876 |
- my $success = $ua->need_proxy('intranet.example.com'); |
|
877 |
- |
|
878 |
-Check if request for domain would use a proxy server. |
|
879 |
- |
|
880 | 821 |
=head2 options |
881 | 822 |
|
882 | 823 |
my $tx = $ua->options('example.com'); |
... | ... |
@@ -886,7 +827,7 @@ Check if request for domain would use a proxy server. |
886 | 827 |
my $tx = $ua->options( |
887 | 828 |
'http://example.com' => {DNT => 1} => json => {a => 'b'}); |
888 | 829 |
|
889 |
-Perform blocking HTTP C<OPTIONS> request and return resulting |
|
830 |
+Perform blocking OPTIONS request and return resulting |
|
890 | 831 |
L<Mojo::Transaction::HTTP> object, takes the same arguments as |
891 | 832 |
L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also |
892 | 833 |
append a callback to perform requests non-blocking. |
... | ... |
@@ -906,10 +847,10 @@ append a callback to perform requests non-blocking. |
906 | 847 |
my $tx = $ua->patch( |
907 | 848 |
'http://example.com' => {DNT => 1} => json => {a => 'b'}); |
908 | 849 |
|
909 |
-Perform blocking HTTP C<PATCH> request and return resulting |
|
910 |
-L<Mojo::Transaction::HTTP> object, takes the same arguments as |
|
911 |
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also |
|
912 |
-append a callback to perform requests non-blocking. |
|
850 |
+Perform blocking PATCH request and return resulting L<Mojo::Transaction::HTTP> |
|
851 |
+object, takes the same arguments as L<Mojo::UserAgent::Transactor/"tx"> |
|
852 |
+(except for the method). You can also append a callback to perform requests |
|
853 |
+non-blocking. |
|
913 | 854 |
|
914 | 855 |
$ua->patch('http://example.com' => sub { |
915 | 856 |
my ($ua, $tx) = @_; |
... | ... |
@@ -926,10 +867,10 @@ append a callback to perform requests non-blocking. |
926 | 867 |
my $tx = $ua->post( |
927 | 868 |
'http://example.com' => {DNT => 1} => json => {a => 'b'}); |
928 | 869 |
|
929 |
-Perform blocking HTTP C<POST> request and return resulting |
|
930 |
-L<Mojo::Transaction::HTTP> object, takes the same arguments as |
|
931 |
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also |
|
932 |
-append a callback to perform requests non-blocking. |
|
870 |
+Perform blocking POST request and return resulting L<Mojo::Transaction::HTTP> |
|
871 |
+object, takes the same arguments as L<Mojo::UserAgent::Transactor/"tx"> |
|
872 |
+(except for the method). You can also append a callback to perform requests |
|
873 |
+non-blocking. |
|
933 | 874 |
|
934 | 875 |
$ua->post('http://example.com' => sub { |
935 | 876 |
my ($ua, $tx) = @_; |
... | ... |
@@ -944,10 +885,10 @@ append a callback to perform requests non-blocking. |
944 | 885 |
my $tx = $ua->put('http://example.com' => {DNT => 1} => form => {a => 'b'}); |
945 | 886 |
my $tx = $ua->put('http://example.com' => {DNT => 1} => json => {a => 'b'}); |
946 | 887 |
|
947 |
-Perform blocking HTTP C<PUT> request and return resulting |
|
948 |
-L<Mojo::Transaction::HTTP> object, takes the same arguments as |
|
949 |
-L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also |
|
950 |
-append a callback to perform requests non-blocking. |
|
888 |
+Perform blocking PUT request and return resulting L<Mojo::Transaction::HTTP> |
|
889 |
+object, takes the same arguments as L<Mojo::UserAgent::Transactor/"tx"> |
|
890 |
+(except for the method). You can also append a callback to perform requests |
|
891 |
+non-blocking. |
|
951 | 892 |
|
952 | 893 |
$ua->put('http://example.com' => sub { |
953 | 894 |
my ($ua, $tx) = @_; |
... | ... |
@@ -19,12 +19,13 @@ sub add { |
19 | 19 |
next if length(defined $cookie->value ? $cookie->value : '') > $size; |
20 | 20 |
|
21 | 21 |
# Replace cookie |
22 |
- my $domain = $cookie->domain; |
|
22 |
+ my $origin = defined $cookie->origin ? $cookie->origin : ''; |
|
23 |
+ next unless my $domain = lc(defined $cookie->domain ? $cookie->domain : $origin); |
|
23 | 24 |
$domain =~ s/^\.//; |
24 |
- my $path = $cookie->path; |
|
25 |
- my $name = $cookie->name; |
|
26 |
- my $jar = $self->{jar}{$domain} ||= []; |
|
27 |
- @$jar = (grep({$_->path ne $path || $_->name ne $name} @$jar), $cookie); |
|
25 |
+ next unless my $path = $cookie->path; |
|
26 |
+ next unless length(my $name = defined $cookie->name ? $cookie->name : ''); |
|
27 |
+ my $jar = $self->{jar}{$domain} ||= []; |
|
28 |
+ @$jar = (grep({_compare($_, $path, $name, $origin)} @$jar), $cookie); |
|
28 | 29 |
} |
29 | 30 |
|
30 | 31 |
return $self; |
... | ... |
@@ -44,11 +45,10 @@ sub extract { |
44 | 45 |
|
45 | 46 |
# Validate domain |
46 | 47 |
my $host = $url->ihost; |
47 |
- my $domain = lc(defined $cookie->domain ? $cookie->domain : $host); |
|
48 |
+ my $domain = lc(defined $cookie->domain ? $cookie->domain : $cookie->origin($host)->origin); |
|
48 | 49 |
$domain =~ s/^\.//; |
49 | 50 |
next |
50 | 51 |
if $host ne $domain && ($host !~ /\Q.$domain\E$/ || $host =~ /\.\d+$/); |
51 |
- $cookie->domain($domain); |
|
52 | 52 |
|
53 | 53 |
# Validate path |
54 | 54 |
my $path = defined $cookie->path ? $cookie->path : $url->path->to_dir->to_abs_string; |
... | ... |
@@ -61,7 +61,7 @@ sub extract { |
61 | 61 |
sub find { |
62 | 62 |
my ($self, $url) = @_; |
63 | 63 |
|
64 |
- return unless my $domain = $url->ihost; |
|
64 |
+ return unless my $domain = my $host = $url->ihost; |
|
65 | 65 |
my $path = $url->path->to_abs_string; |
66 | 66 |
my @found; |
67 | 67 |
while ($domain =~ /[^.]+\.[^.]+|localhost$/) { |
... | ... |
@@ -70,6 +70,7 @@ sub find { |
70 | 70 |
# Grab cookies |
71 | 71 |
my $new = $self->{jar}{$domain} = []; |
72 | 72 |
for my $cookie (@$old) { |
73 |
+ next unless $cookie->domain || $host eq $cookie->origin; |
|
73 | 74 |
|
74 | 75 |
# Check if cookie has expired |
75 | 76 |
my $expires = $cookie->expires; |
... | ... |
@@ -98,10 +99,18 @@ sub inject { |
98 | 99 |
$req->cookies($self->find($req->url)); |
99 | 100 |
} |
100 | 101 |
|
102 |
+sub _compare { |
|
103 |
+ my ($cookie, $path, $name, $origin) = @_; |
|
104 |
+ return 1 if $cookie->path ne $path || $cookie->name ne $name; |
|
105 |
+ return (defined $cookie->origin ? $cookie->origin : '') ne $origin; |
|
106 |
+} |
|
107 |
+ |
|
101 | 108 |
sub _path { $_[0] eq '/' || $_[0] eq $_[1] || $_[1] =~ m!^\Q$_[0]/! } |
102 | 109 |
|
103 | 110 |
1; |
104 | 111 |
|
112 |
+=encoding utf8 |
|
113 |
+ |
|
105 | 114 |
=head1 NAME |
106 | 115 |
|
107 | 116 |
Mojo::UserAgent::CookieJar - Cookie jar for HTTP user agents |
... | ... |
@@ -169,19 +178,19 @@ Empty the jar. |
169 | 178 |
|
170 | 179 |
=head2 extract |
171 | 180 |
|
172 |
- $jar->extract($tx); |
|
181 |
+ $jar->extract(Mojo::Transaction::HTTP->new); |
|
173 | 182 |
|
174 | 183 |
Extract response cookies from transaction. |
175 | 184 |
|
176 | 185 |
=head2 find |
177 | 186 |
|
178 |
- my @cookies = $jar->find($url); |
|
187 |
+ my @cookies = $jar->find(Mojo::URL->new); |
|
179 | 188 |
|
180 | 189 |
Find L<Mojo::Cookie::Request> objects in the jar for L<Mojo::URL> object. |
181 | 190 |
|
182 | 191 |
=head2 inject |
183 | 192 |
|
184 |
- $jar->inject($tx); |
|
193 |
+ $jar->inject(Mojo::Transaction::HTTP->new); |
|
185 | 194 |
|
186 | 195 |
Inject request cookies into transaction. |
187 | 196 |
|
... | ... |
@@ -0,0 +1,109 @@ |
1 |
+package Mojo::UserAgent::Proxy; |
|
2 |
+use Mojo::Base -base; |
|
3 |
+ |
|
4 |
+has [qw(http https not)]; |
|
5 |
+ |
|
6 |
+sub detect { |
|
7 |
+ my $self = shift; |
|
8 |
+ $self->http($ENV{HTTP_PROXY} || $ENV{http_proxy}); |
|
9 |
+ $self->https($ENV{HTTPS_PROXY} || $ENV{https_proxy}); |
|
10 |
+ return $self->not([split /,/, $ENV{NO_PROXY} || $ENV{no_proxy} || '']); |
|
11 |
+} |
|
12 |
+ |
|
13 |
+sub inject { |
|
14 |
+ my ($self, $tx) = @_; |
|
15 |
+ |
|
16 |
+ $self->detect if $ENV{MOJO_PROXY}; |
|
17 |
+ my $req = $tx->req; |
|
18 |
+ my $url = $req->url; |
|
19 |
+ return if !$self->is_needed($url->host) || defined $req->proxy; |
|
20 |
+ |
|
21 |
+ # HTTP proxy |
|
22 |
+ my $proto = $url->protocol; |
|
23 |
+ my $http = $self->http; |
|
24 |
+ $req->proxy($http) if $http && $proto eq 'http'; |
|
25 |
+ |
|
26 |
+ # HTTPS proxy |
|
27 |
+ my $https = $self->https; |
|
28 |
+ $req->proxy($https) if $https && $proto eq 'https'; |
|
29 |
+} |
|
30 |
+ |
|
31 |
+sub is_needed { |
|
32 |
+ !grep { $_[1] =~ /\Q$_\E$/ } @{$_[0]->not || []}; |
|
33 |
+} |
|
34 |
+ |
|
35 |
+1; |
|
36 |
+ |
|
37 |
+=encoding utf8 |
|
38 |
+ |
|
39 |
+=head1 NAME |
|
40 |
+ |
|
41 |
+Mojo::UserAgent::Proxy - User agent proxy manager |
|
42 |
+ |
|
43 |
+=head1 SYNOPSIS |
|
44 |
+ |
|
45 |
+ use Mojo::UserAgent::Proxy; |
|
46 |
+ |
|
47 |
+ my $proxy = Mojo::UserAgent::Proxy->new; |
|
48 |
+ $proxy->detect; |
|
49 |
+ say $proxy->http; |
|
50 |
+ |
|
51 |
+=head1 DESCRIPTION |
|
52 |
+ |
|
53 |
+L<Mojo::UserAgent::Proxy> manages proxy servers for L<Mojo::UserAgent>. |
|
54 |
+ |
|
55 |
+=head1 ATTRIBUTES |
|
56 |
+ |
|
57 |
+L<Mojo::UserAgent::Proxy> implements the following attributes. |
|
58 |
+ |
|
59 |
+=head2 http |
|
60 |
+ |
|
61 |
+ my $http = $ua->http; |
|
62 |
+ $ua = $ua->http('http://sri:secret@127.0.0.1:8080'); |
|
63 |
+ |
|
64 |
+Proxy server to use for HTTP and WebSocket requests. |
|
65 |
+ |
|
66 |
+=head2 https |
|
67 |
+ |
|
68 |
+ my $https = $ua->https; |
|
69 |
+ $ua = $ua->https('http://sri:secret@127.0.0.1:8080'); |
|
70 |
+ |
|
71 |
+Proxy server to use for HTTPS and WebSocket requests. |
|
72 |
+ |
|
73 |
+=head2 not |
|
74 |
+ |
|
75 |
+ my $not = $proxy->not; |
|
76 |
+ $ua = $proxy->not([qw(localhost intranet.mojolicio.us)]); |
|
77 |
+ |
|
78 |
+Domains that don't require a proxy server to be used. |
|
79 |
+ |
|
80 |
+=head1 METHODS |
|
81 |
+ |
|
82 |
+L<Mojo::UserAgent::Proxy> inherits all methods from L<Mojo::Base> and |
|
83 |
+implements the following new ones. |
|
84 |
+ |
|
85 |
+=head2 detect |
|
86 |
+ |
|
87 |
+ $proxy = $proxy->detect; |
|
88 |
+ |
|
89 |
+Check environment variables HTTP_PROXY, http_proxy, HTTPS_PROXY, https_proxy, |
|
90 |
+NO_PROXY and no_proxy for proxy information. Automatic proxy detection can be |
|
91 |
+enabled with the MOJO_PROXY environment variable. |
|
92 |
+ |
|
93 |
+=head2 inject |
|
94 |
+ |
|
95 |
+ $proxy->inject(Mojo::Transaction::HTTP->new); |
|
96 |
+ |
|
97 |
+Inject proxy server information into transaction. |
|
98 |
+ |
|
99 |
+=head2 is_needed |
|
100 |
+ |
|
101 |
+ my $bool = $proxy->is_needed('intranet.example.com'); |
|
102 |
+ |
|
103 |
+Check if request for domain would use a proxy server. |
|
104 |
+ |
|
105 |
+=head1 SEE ALSO |
|
106 |
+ |
|
107 |
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
|
108 |
+ |
|
109 |
+=cut |
... | ... |
@@ -0,0 +1,115 @@ |
1 |
+package Mojo::UserAgent::Server; |
|
2 |
+use Mojo::Base -base; |
|
3 |
+ |
|
4 |
+use Mojo::IOLoop; |
|
5 |
+use Mojo::Server::Daemon; |
|
6 |
+ |
|
7 |
+has ioloop => sub { Mojo::IOLoop->singleton }; |
|
8 |
+ |
|
9 |
+sub app { |
|
10 |
+ my ($self, $app) = @_; |
|
11 |
+ |
|
12 |
+ # Singleton application |
|
13 |
+ our $state_singleton; |
|
14 |
+ return $state_singleton = $app ? $app : $state_singleton unless ref $self; |
|
15 |
+ |
|
16 |
+ # Default to singleton application |
|
17 |
+ return $self->{app} || $state_singleton unless $app; |
|
18 |
+ $self->{app} = $app; |
|
19 |
+ return $self; |
|
20 |
+} |
|
21 |
+ |
|
22 |
+sub restart { shift->_restart(1) } |
|
23 |
+ |
|
24 |
+sub url { |
|
25 |
+ my $self = shift; |
|
26 |
+ $self->_restart(0, @_) |
|
27 |
+ if !$self->{server} || $self->{server}->ioloop ne $self->ioloop || @_; |
|
28 |
+ return Mojo::URL->new("$self->{proto}://localhost:$self->{port}/"); |
|
29 |
+} |
|
30 |
+ |
|
31 |
+sub _restart { |
|
32 |
+ my ($self, $full, $proto) = @_; |
|
33 |
+ |
|
34 |
+ my $server = $self->{server} = Mojo::Server::Daemon->new( |
|
35 |
+ app => $self->app, |
|
36 |
+ ioloop => $self->ioloop, |
|
37 |
+ silent => 1 |
|
38 |
+ ); |
|
39 |
+ delete $self->{port} if $full; |
|
40 |
+ die "Couldn't find a free TCP port for application.\n" |
|
41 |
+ unless my $port = $self->{port} ||= Mojo::IOLoop->generate_port; |
|
42 |
+ $self->{proto} = $proto ||= 'http'; |
|
43 |
+ $server->listen(["$proto://127.0.0.1:$port"])->start; |
|
44 |
+} |
|
45 |
+ |
|
46 |
+1; |
|
47 |
+ |
|
48 |
+=encoding utf8 |
|
49 |
+ |
|
50 |
+=head1 NAME |
|
51 |
+ |
|
52 |
+Mojo::UserAgent::Server - Application server |
|
53 |
+ |
|
54 |
+=head1 SYNOPSIS |
|
55 |
+ |
|
56 |
+ use Mojo::UserAgent::Server; |
|
57 |
+ |
|
58 |
+ my $server = Mojo::UserAgent::Server->new; |
|
59 |
+ say $server->url; |
|
60 |
+ |
|
61 |
+=head1 DESCRIPTION |
|
62 |
+ |
|
63 |
+L<Mojo::UserAgent::Server> is an embedded web server based on |
|
64 |
+L<Mojo::Server::Daemon> that processes requests for L<Mojo::UserAgent>. |
|
65 |
+ |
|
66 |
+=head1 ATTRIBUTES |
|
67 |
+ |
|
68 |
+L<Mojo::UserAgent::Server> implements the following attributes. |
|
69 |
+ |
|
70 |
+=head2 ioloop |
|
71 |
+ |
|
72 |
+ my $loop = $server->ioloop; |
|
73 |
+ $server = $server->ioloop(Mojo::IOLoop->new); |
|
74 |
+ |
|
75 |
+Event loop object to use for I/O operations, defaults to the global |
|
76 |
+L<Mojo::IOLoop> singleton. |
|
77 |
+ |
|
78 |
+=head1 METHODS |
|
79 |
+ |
|
80 |
+L<Mojo::UserAgent::Server> inherits all methods from L<Mojo::Base> and |
|
81 |
+implements the following new ones. |
|
82 |
+ |
|
83 |
+=head2 app |
|
84 |
+ |
|
85 |
+ my $app = Mojo::UserAgent::Server->app; |
|
86 |
+ Mojo::UserAgent::Server->app(MyApp->new); |
|
87 |
+ my $app = $server->app; |
|
88 |
+ $server = $server->app(MyApp->new); |
|
89 |
+ |
|
90 |
+Application this server handles, instance specific applications override the |
|
91 |
+global default. |
|
92 |
+ |
|
93 |
+ # Change application behavior |
|
94 |
+ $server->defaults(testing => 'oh yea!'); |
|
95 |
+ |
|
96 |
+=head2 restart |
|
97 |
+ |
|
98 |
+ $server->restart; |
|
99 |
+ |
|
100 |
+Restart server with new port. |
|
101 |
+ |
|
102 |
+=head2 url |
|
103 |
+ |
|
104 |
+ my $url = $ua->url; |
|
105 |
+ my $url = $ua->url('http'); |
|
106 |
+ my $url = $ua->url('https'); |
|
107 |
+ |
|
108 |
+Get absolute L<Mojo::URL> object for L</"app"> and switch protocol if |
|
109 |
+necessary. |
|
110 |
+ |
|
111 |
+=head1 SEE ALSO |
|
112 |
+ |
|
113 |
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
|
114 |
+ |
|
115 |
+=cut |
... | ... |
@@ -13,12 +13,8 @@ use Mojo::Transaction::WebSocket; |
13 | 13 |
use Mojo::URL; |
14 | 14 |
use Mojo::Util 'encode'; |
15 | 15 |
|
16 |
-has generators => sub { {} }; |
|
17 |
- |
|
18 |
-sub new { |
|
19 |
- my $self = shift->SUPER::new(@_); |
|
20 |
- return $self->add_generator(form => \&_form)->add_generator(json => \&_json); |
|
21 |
-} |
|
16 |
+has generators => sub { {form => \&_form, json => \&_json} }; |
|
17 |
+has name => 'Mojolicious (Perl)'; |
|
22 | 18 |
|
23 | 19 |
sub add_generator { |
24 | 20 |
my ($self, $name, $cb) = @_; |
... | ... |
@@ -107,8 +103,11 @@ sub tx { |
107 | 103 |
$url = "http://$url" unless $url =~ m!^/|://!; |
108 | 104 |
ref $url ? $req->url($url) : $req->url->parse($url); |
109 | 105 |
|
110 |
- # Headers |
|
111 |
- $req->headers->from_hash(shift) if ref $_[0] eq 'HASH'; |
|
106 |
+ # Headers (we identify ourselves and accept gzip compression) |
|
107 |
+ my $headers = $req->headers; |
|
108 |
+ $headers->from_hash(shift) if ref $_[0] eq 'HASH'; |
|
109 |
+ $headers->user_agent($self->name) unless $headers->user_agent; |
|
110 |
+ $headers->accept_encoding('gzip') unless $headers->accept_encoding; |
|
112 | 111 |
|
113 | 112 |
# Generator |
114 | 113 |
if (@_ > 1) { |
... | ... |
@@ -253,6 +252,8 @@ sub _proxy { |
253 | 252 |
|
254 | 253 |
1; |
255 | 254 |
|
255 |
+=encoding utf8 |
|
256 |
+ |
|
256 | 257 |
=head1 NAME |
257 | 258 |
|
258 | 259 |
Mojo::UserAgent::Transactor - User agent transactor |
... | ... |
@@ -268,7 +269,7 @@ Mojo::UserAgent::Transactor - User agent transactor |
268 | 269 |
# PATCH request with "Do Not Track" header and content |
269 | 270 |
say $t->tx(PATCH => 'example.com' => {DNT => 1} => 'Hi!')->req->to_string; |
270 | 271 |
|
271 |
- # POST request with form data |
|
272 |
+ # POST request with form-data |
|
272 | 273 |
say $t->tx(POST => 'example.com' => form => {a => 'b'})->req->to_string; |
273 | 274 |
|
274 | 275 |
# PUT request with JSON data |
... | ... |
@@ -288,19 +289,21 @@ L<Mojo::UserAgent::Transactor> implements the following attributes. |
288 | 289 |
my $generators = $t->generators; |
289 | 290 |
$t = $t->generators({foo => sub {...}}); |
290 | 291 |
|
291 |
-Registered content generators. |
|
292 |
+Registered content generators, by default only C<form> and C<json> are already |
|
293 |
+defined. |
|
292 | 294 |
|
293 |
-=head1 METHODS |
|
295 |
+=head2 name |
|
294 | 296 |
|
295 |
-L<Mojo::UserAgent::Transactor> inherits all methods from L<Mojo::Base> and |
|
296 |
-implements the following new ones. |
|
297 |
+ my $name = $t->name; |
|
298 |
+ $t = $t->name('Mojolicious'); |
|
297 | 299 |
|
298 |
-=head2 new |
|
300 |
+Value for C<User-Agent> request header of generated transactions, defaults to |
|
301 |
+C<Mojolicious (Perl)>. |
|
299 | 302 |
|
300 |
- my $t = Mojo::UserAgent::Transactor->new; |
|
303 |
+=head1 METHODS |
|
301 | 304 |
|
302 |
-Construct a new transactor and register C<form> and C<json> content |
|
303 |
-generators. |
|
305 |
+L<Mojo::UserAgent::Transactor> inherits all methods from L<Mojo::Base> and |
|
306 |
+implements the following new ones. |
|
304 | 307 |
|
305 | 308 |
=head2 add_generator |
306 | 309 |
|
... | ... |
@@ -351,51 +354,70 @@ C<307> or C<308> redirect response if possible. |
351 | 354 |
Versatile general purpose L<Mojo::Transaction::HTTP> transaction builder for |
352 | 355 |
requests, with support for content generators. |
353 | 356 |
|
354 |
- # Inspect generated request |
|
357 |
+ # Generate and inspect custom GET request with DNT header and content |
|
355 | 358 |
say $t->tx(GET => 'example.com' => {DNT => 1} => 'Bye!')->req->to_string; |
356 | 359 |
|
357 |
- # Streaming response |
|
360 |
+ # Use a custom socket for processing this transaction |
|
358 | 361 |
my $tx = $t->tx(GET => 'http://example.com'); |
359 |
- $tx->res->content->unsubscribe('read')->on(read => sub { say $_[1] }); |
|
362 |
+ $tx->connection($sock); |
|
360 | 363 |
|
361 |
- # Custom socket |
|
364 |
+ # Stream response content to STDOUT |
|
362 | 365 |
my $tx = $t->tx(GET => 'http://example.com'); |
363 |
- $tx->connection($sock); |
|
366 |
+ $tx->res->content->unsubscribe('read')->on(read => sub { say $_[1] }); |
|
364 | 367 |
|
365 |
- # Generate query parameters |
|
368 |
+ # PUT request with content streamed from file |
|
369 |
+ my $tx = $t->tx(PUT => 'http://example.com'); |
|
370 |
+ $tx->req->content->asset(Mojo::Asset::File->new(path => '/foo.txt')); |
|
371 |
+ |
|
372 |
+ # GET request with query parameters |
|
366 | 373 |
my $tx = $t->tx(GET => 'http://example.com' => form => {a => 'b'}); |
367 | 374 |
|
368 |
- # Use form generator with custom charset |
|
375 |
+ # POST request with "application/json" content |
|
369 | 376 |
my $tx = $t->tx( |
370 |
- PUT => 'http://example.com' => form => {a => 'b'} => charset => 'UTF-8'); |
|
377 |
+ POST => 'http://example.com' => json => {a => 'b', c => [1, 2, 3]}); |
|
371 | 378 |
|
372 |
- # Multiple form values with the same name |
|
373 |
- my $tx = $t->tx(PUT => 'http://example.com' => form => {a => [qw(b c d)]}); |
|
379 |
+ # POST request with "application/x-www-form-urlencoded" content |
|
380 |
+ my $tx = $t->tx( |
|
381 |
+ POST => 'http://example.com' => form => {a => 'b', c => 'd'}); |
|
374 | 382 |
|
375 |
- # Multipart upload streamed from file |
|
383 |
+ # PUT request with UTF-8 encoded form values |
|
376 | 384 |
my $tx = $t->tx( |
377 |
- PUT => 'http://example.com' => form => {mytext => {file => '/foo.txt'}}); |
|
385 |
+ PUT => 'http://example.com' => form => {a => 'b'} => charset => 'UTF-8'); |
|
386 |
+ |
|
387 |
+ # POST request with form values sharing the same name |
|
388 |
+ my $tx = $t->tx(POST => 'http://example.com' => form => {a => [qw(b c d)]}); |
|
378 | 389 |
|
379 |
- # Multipart upload with in-memory content |
|
390 |
+ # POST request with "multipart/form-data" content |
|
380 | 391 |
my $tx = $t->tx( |
381 | 392 |
POST => 'http://example.com' => form => {mytext => {content => 'lala'}}); |
382 | 393 |
|
383 |
- # Upload multiple files with same name |
|
394 |
+ # POST request with upload streamed from file |
|
395 |
+ my $tx = $t->tx( |
|
396 |
+ POST => 'http://example.com' => form => {mytext => {file => '/foo.txt'}}); |
|
397 |
+ |
|
398 |
+ # POST request with upload streamed from asset |
|
399 |
+ my $asset = Mojo::Asset::Memory->new->add_chunk('lalala'); |
|
400 |
+ my $tx = $t->tx( |
|
401 |
+ POST => 'http://example.com' => form => {mytext => {file => $asset}}); |
|
402 |
+ |
|
403 |
+ # POST request with multiple files sharing the same name |
|
384 | 404 |
my $tx = $t->tx(POST => 'http://example.com' => |
385 | 405 |
form => {mytext => [{content => 'first'}, {content => 'second'}]}); |
386 | 406 |
|
387 |
- # Customized upload with filename and header |
|
407 |
+ # POST request with form values and customized upload (filename and header) |
|
388 | 408 |
my $tx = $t->tx(POST => 'http://example.com' => form => { |
389 |
- myzip => { |
|
390 |
- file => Mojo::Asset::Memory->new->add_chunk('lalala'), |
|
391 |
- filename => 'foo.zip', |
|
409 |
+ a => 'b', |
|
410 |
+ c => 'd', |
|
411 |
+ mytext => { |
|
412 |
+ content => 'lalala', |
|
413 |
+ filename => 'foo.txt', |
|
392 | 414 |
'Content-Type' => 'text/plain' |
393 | 415 |
} |
394 | 416 |
}); |
395 | 417 |
|
396 | 418 |
The C<form> content generator will automatically use query parameters for |
397 |
-C<GET>/C<HEAD> requests and the "application/x-www-form-urlencoded" content |
|
398 |
-type for everything else. Both get upgraded automatically to using the |
|
419 |
+GET/HEAD requests and the "application/x-www-form-urlencoded" content type for |
|
420 |
+everything else. Both get upgraded automatically to using the |
|
399 | 421 |
"multipart/form-data" content type when necessary or when the header has been |
400 | 422 |
set manually. |
401 | 423 |
|
... | ... |
@@ -2,8 +2,9 @@ package Mojo::Util; |
2 | 2 |
use Mojo::Base 'Exporter'; |
3 | 3 |
|
4 | 4 |
use Carp qw(carp croak); |
5 |
+use Data::Dumper (); |
|
5 | 6 |
use Digest::MD5 qw(md5 md5_hex); |
6 |
-BEGIN {eval {require Digest::SHA; import Digest::SHA qw(sha1 sha1_hex)}} |
|
7 |
+BEGIN {eval {require Digest::SHA; import Digest::SHA qw(hmac_sha1 sha1 sha1_hex)}} |
|
7 | 8 |
use Encode 'find_encoding'; |
8 | 9 |
use File::Basename 'dirname'; |
9 | 10 |
use File::Spec::Functions 'catfile'; |
... | ... |
@@ -25,7 +26,7 @@ use constant { |
25 | 26 |
PC_INITIAL_N => 128 |
26 | 27 |
}; |
27 | 28 |
|
28 |
-# To update HTML5 entities run this command |
|
29 |
+# To update HTML entities run this command |
|
29 | 30 |
# perl examples/entities.pl > lib/Mojo/entities.txt |
30 | 31 |
my %ENTITIES; |
31 | 32 |
for my $line (split "\x0a", slurp(catfile dirname(__FILE__), 'entities.txt')) { |
... | ... |
@@ -38,10 +39,10 @@ my %CACHE; |
38 | 39 |
|
39 | 40 |
our @EXPORT_OK = ( |
40 | 41 |
qw(b64_decode b64_encode camelize class_to_file class_to_path decamelize), |
41 |
- qw(decode deprecated encode get_line hmac_sha1_sum html_unescape md5_bytes), |
|
42 |
- qw(md5_sum monkey_patch punycode_decode punycode_encode quote), |
|
43 |
- qw(secure_compare sha1_bytes sha1_sum slurp spurt squish steady_time trim), |
|
44 |
- qw(unquote url_escape url_unescape xml_escape xor_encode) |
|
42 |
+ qw(decode deprecated dumper encode get_line hmac_sha1_sum html_unescape), |
|
43 |
+ qw(md5_bytes md5_sum monkey_patch punycode_decode punycode_encode quote), |
|
44 |
+ qw(secure_compare sha1_bytes sha1_sum slurp split_header spurt squish), |
|
45 |
+ qw(steady_time trim unquote url_escape url_unescape xml_escape xor_encode) |
|
45 | 46 |
); |
46 | 47 |
|
47 | 48 |
sub b64_decode { decode_base64($_[0]) } |
... | ... |
@@ -51,7 +52,7 @@ sub camelize { |
51 | 52 |
my $str = shift; |
52 | 53 |
return $str if $str =~ /^[A-Z]/; |
53 | 54 |
|
54 |
- # Camel case words |
|
55 |
+ # CamelCase words |
|
55 | 56 |
return join '::', map { |
56 | 57 |
join '', map { ucfirst lc } split /_/, $_ |
57 | 58 |
} split /-/, $str; |
... | ... |
@@ -74,7 +75,7 @@ sub decamelize { |
74 | 75 |
my @parts; |
75 | 76 |
for my $part (split /::/, $str) { |
76 | 77 |
|
77 |
- # Snake case words |
|
78 |
+ # snake_case words |
|
78 | 79 |
my @words; |
79 | 80 |
push @words, lc $1 while $part =~ s/([A-Z]{1}[^A-Z]*)//; |
80 | 81 |
push @parts, join '_', @words; |
... | ... |
@@ -95,6 +96,8 @@ sub deprecated { |
95 | 96 |
$ENV{MOJO_FATAL_DEPRECATIONS} ? croak(@_) : carp(@_); |
96 | 97 |
} |
97 | 98 |
|
99 |
+sub dumper { Data::Dumper->new([@_])->Indent(1)->Sortkeys(1)->Terse(1)->Dump } |
|
100 |
+ |
|
98 | 101 |
sub encode { _encoding($_[0])->encode("$_[1]") } |
99 | 102 |
|
100 | 103 |
sub get_line { |
... | ... |
@@ -109,21 +112,12 @@ sub get_line { |
109 | 112 |
return $line; |
110 | 113 |
} |
111 | 114 |
|
112 |
-sub hmac_sha1_sum { |
|
113 |
- my ($str, $secret) = @_; |
|
114 |
- $secret = $secret ? "$secret" : 'Very insecure!'; |
|
115 |
- $secret = sha1 $secret if length $secret > 64; |
|
116 |
- |
|
117 |
- my $ipad = $secret ^ (chr(0x36) x 64); |
|
118 |
- my $opad = $secret ^ (chr(0x5c) x 64); |
|
119 |
- return unpack 'H*', sha1($opad . sha1($ipad . $str)); |
|
120 |
-} |
|
115 |
+sub hmac_sha1_sum { unpack 'H*', hmac_sha1(@_) } |
|
121 | 116 |
|
122 | 117 |
sub html_unescape { |
123 | 118 |
my $str = shift; |
124 | 119 |
return $str if index($str, '&') == -1; |
125 |
- $str |
|
126 |
- =~ s/&(?:\#((?:\d{1,7}|x[[:xdigit:]]{1,6}));|(\w+;?))/_decode($1, $2)/ge; |
|
120 |
+ $str =~ s/&(?:\#((?:\d{1,7}|x[0-9a-fA-F]{1,6}));|(\w+;?))/_decode($1, $2)/ge; |
|
127 | 121 |
return $str; |
128 | 122 |
} |
129 | 123 |
|
... | ... |
@@ -142,36 +136,32 @@ sub punycode_decode { |
142 | 136 |
my $input = shift; |
143 | 137 |
use integer; |
144 | 138 |
|
145 |
- # Delimiter |
|
146 |
- my @output; |
|
147 |
- push @output, split //, $1 if $input =~ s/(.*)\x2d//s; |
|
148 |
- |
|
149 | 139 |
my $n = PC_INITIAL_N; |
150 | 140 |
my $i = 0; |
151 | 141 |
my $bias = PC_INITIAL_BIAS; |
142 |
+ my @output; |
|
143 |
+ |
|
144 |
+ # Consume all code points before the last delimiter |
|
145 |
+ push @output, split //, $1 if $input =~ s/(.*)\x2d//s; |
|
146 |
+ |
|
152 | 147 |
while (length $input) { |
153 | 148 |
my $oldi = $i; |
154 | 149 |
my $w = 1; |
155 | 150 |
|
156 | 151 |
# Base to infinity in steps of base |
157 | 152 |
for (my $k = PC_BASE; 1; $k += PC_BASE) { |
158 |
- |
|
159 |
- # Digit |
|
160 | 153 |
my $digit = ord substr $input, 0, 1, ''; |
161 | 154 |
$digit = $digit < 0x40 ? $digit + (26 - 0x30) : ($digit & 0x1f) - 1; |
162 | 155 |
$i += $digit * $w; |
163 | 156 |
my $t = $k - $bias; |
164 | 157 |
$t = $t < PC_TMIN ? PC_TMIN : $t > PC_TMAX ? PC_TMAX : $t; |
165 | 158 |
last if $digit < $t; |
166 |
- |
|
167 |
- $w *= (PC_BASE - $t); |
|
159 |
+ $w *= PC_BASE - $t; |
|
168 | 160 |
} |
169 | 161 |
|
170 |
- # Bias |
|
171 | 162 |
$bias = _adapt($i - $oldi, @output + 1, $oldi == 0); |
172 | 163 |
$n += $i / (@output + 1); |
173 | 164 |
$i = $i % (@output + 1); |
174 |
- |
|
175 | 165 |
splice @output, $i++, 0, chr $n; |
176 | 166 |
} |
177 | 167 |
|
... | ... |
@@ -183,35 +173,28 @@ sub punycode_encode { |
183 | 173 |
my $output = shift; |
184 | 174 |
use integer; |
185 | 175 |
|
186 |
- # Split input |
|
176 |
+ my $n = PC_INITIAL_N; |
|
177 |
+ my $delta = 0; |
|
178 |
+ my $bias = PC_INITIAL_BIAS; |
|
179 |
+ |
|
180 |
+ # Extract basic code points |
|
187 | 181 |
my $len = length $output; |
188 | 182 |
my @input = map {ord} split //, $output; |
189 | 183 |
my @chars = sort grep { $_ >= PC_INITIAL_N } @input; |
190 |
- |
|
191 |
- # Handle non-basic characters |
|
192 | 184 |
$output =~ s/[^\x00-\x7f]+//gs; |
193 | 185 |
my $h = my $b = length $output; |
194 | 186 |
$output .= "\x2d" if $b > 0; |
195 | 187 |
|
196 |
- my $n = PC_INITIAL_N; |
|
197 |
- my $delta = 0; |
|
198 |
- my $bias = PC_INITIAL_BIAS; |
|
199 | 188 |
for my $m (@chars) { |
200 |
- |
|
201 |
- # Basic character |
|
202 | 189 |
next if $m < $n; |
203 |
- |
|
204 |
- # Walk all code points in order |
|
205 | 190 |
$delta += ($m - $n) * ($h + 1); |
206 | 191 |
$n = $m; |
192 |
+ |
|
207 | 193 |
for (my $i = 0; $i < $len; $i++) { |
208 | 194 |
my $c = $input[$i]; |
209 | 195 |
|
210 |
- # Basic character |
|
211 |
- $delta++ if $c < $n; |
|
212 |
- |
|
213 |
- # Non basic character |
|
214 |
- if ($c == $n) { |
|
196 |
+ if ($c < $n) { $delta++ } |
|
197 |
+ elsif ($c == $n) { |
|
215 | 198 |
my $q = $delta; |
216 | 199 |
|
217 | 200 |
# Base to infinity in steps of base |
... | ... |
@@ -219,18 +202,12 @@ sub punycode_encode { |
219 | 202 |
my $t = $k - $bias; |
220 | 203 |
$t = $t < PC_TMIN ? PC_TMIN : $t > PC_TMAX ? PC_TMAX : $t; |
221 | 204 |
last if $q < $t; |
222 |
- |
|
223 |
- # Code point for digit "t" |
|
224 | 205 |
my $o = $t + (($q - $t) % (PC_BASE - $t)); |
225 | 206 |
$output .= chr $o + ($o < 26 ? 0x61 : 0x30 - 26); |
226 |
- |
|
227 | 207 |
$q = ($q - $t) / (PC_BASE - $t); |
228 | 208 |
} |
229 | 209 |
|
230 |
- # Code point for digit "q" |
|
231 | 210 |
$output .= chr $q + ($q < 26 ? 0x61 : 0x30 - 26); |
232 |
- |
|
233 |
- # Bias |
|
234 | 211 |
$bias = _adapt($delta, $h + 1, $h == $b); |
235 | 212 |
$delta = 0; |
236 | 213 |
$h++; |
... | ... |
@@ -269,6 +246,26 @@ sub slurp { |
269 | 246 |
return $content; |
270 | 247 |
} |
271 | 248 |
|
249 |
+sub split_header { |
|
250 |
+ my $str = shift; |
|
251 |
+ |
|
252 |
+ my (@tree, @token); |
|
253 |
+ while ($str =~ s/^[,;\s]*([^=;, ]+)\s*//) { |
|
254 |
+ push @token, $1, undef; |
|
255 |
+ $token[-1] = unquote($1) |
|
256 |
+ if $str =~ s/^=\s*("(?:\\\\|\\"|[^"])*"|[^;, ]*)\s*//; |
|
257 |
+ |
|
258 |
+ # Separator |
|
259 |
+ $str =~ s/^;\s*//; |
|
260 |
+ next unless $str =~ s/^,\s*//; |
|
261 |
+ push @tree, [@token]; |
|
262 |
+ @token = (); |
|
263 |
+ } |
|
264 |
+ |
|
265 |
+ # Take care of final token |
|
266 |
+ return [@token ? (@tree, \@token) : @tree]; |
|
267 |
+} |
|
268 |
+ |
|
272 | 269 |
sub spurt { |
273 | 270 |
my ($content, $path) = @_; |
274 | 271 |
croak qq{Can't open file "$path": $!} unless open my $file, '>', $path; |
... | ... |
@@ -313,7 +310,7 @@ sub url_escape { |
313 | 310 |
sub url_unescape { |
314 | 311 |
my $str = shift; |
315 | 312 |
return $str if index($str, '%') == -1; |
316 |
- $str =~ s/%([[:xdigit:]]{2})/chr(hex($1))/ge; |
|
313 |
+ $str =~ s/%([0-9a-fA-F]{2})/chr(hex($1))/ge; |
|
317 | 314 |
return $str; |
318 | 315 |
} |
319 | 316 |
|
... | ... |
@@ -342,8 +339,8 @@ sub xor_encode { |
342 | 339 |
|
343 | 340 |
sub _adapt { |
344 | 341 |
my ($delta, $numpoints, $firsttime) = @_; |
345 |
- |
|
346 | 342 |
use integer; |
343 |
+ |
|
347 | 344 |
$delta = $firsttime ? $delta / PC_DAMP : $delta / 2; |
348 | 345 |
$delta += $delta / $numpoints; |
349 | 346 |
my $k = 0; |
... | ... |
@@ -376,6 +373,8 @@ sub _encoding { |
376 | 373 |
|
377 | 374 |
1; |
378 | 375 |
|
376 |
+=encoding utf8 |
|
377 |
+ |
|
379 | 378 |
=head1 NAME |
380 | 379 |
|
381 | 380 |
Mojo::Util - Portable utility functions |
... | ... |
@@ -399,22 +398,22 @@ L<Mojo::Util> implements the following functions. |
399 | 398 |
|
400 | 399 |
=head2 b64_decode |
401 | 400 |
|
402 |
- my $str = b64_decode $b64; |
|
401 |
+ my $bytes = b64_decode $b64; |
|
403 | 402 |
|
404 |
-Base64 decode string. |
|
403 |
+Base64 decode bytes. |
|
405 | 404 |
|
406 | 405 |
=head2 b64_encode |
407 | 406 |
|
408 |
- my $b64 = b64_encode $str; |
|
409 |
- my $b64 = b64_encode $str, "\n"; |
|
407 |
+ my $b64 = b64_encode $bytes; |
|
408 |
+ my $b64 = b64_encode $bytes, "\n"; |
|
410 | 409 |
|
411 |
-Base64 encode string, the line ending defaults to a newline. |
|
410 |
+Base64 encode bytes, the line ending defaults to a newline. |
|
412 | 411 |
|
413 | 412 |
=head2 camelize |
414 | 413 |
|
415 | 414 |
my $camelcase = camelize $snakecase; |
416 | 415 |
|
417 |
-Convert snake case string to camel case and replace C<-> with C<::>. |
|
416 |
+Convert snake_case string to CamelCase and replace C<-> with C<::>. |
|
418 | 417 |
|
419 | 418 |
# "FooBar" |
420 | 419 |
camelize 'foo_bar'; |
... | ... |
@@ -431,10 +430,17 @@ Convert snake case string to camel case and replace C<-> with C<::>. |
431 | 430 |
|
432 | 431 |
Convert a class name to a file. |
433 | 432 |
|
434 |
- Foo::Bar -> foo_bar |
|
435 |
- FOO::Bar -> foobar |
|
436 |
- FooBar -> foo_bar |
|
437 |
- FOOBar -> foobar |
|
433 |
+ # "foo_bar" |
|
434 |
+ class_to_file 'Foo::Bar'; |
|
435 |
+ |
|
436 |
+ # "foobar" |
|
437 |
+ class_to_file 'FOO::Bar'; |
|
438 |
+ |
|
439 |
+ # "foo_bar" |
|
440 |
+ class_to_file 'FooBar'; |
|
441 |
+ |
|
442 |
+ # "foobar" |
|
443 |
+ class_to_file 'FOOBar'; |
|
438 | 444 |
|
439 | 445 |
=head2 class_to_path |
440 | 446 |
|
... | ... |
@@ -442,14 +448,17 @@ Convert a class name to a file. |
442 | 448 |
|
443 | 449 |
Convert class name to path. |
444 | 450 |
|
445 |
- Foo::Bar -> Foo/Bar.pm |
|
446 |
- FooBar -> FooBar.pm |
|
451 |
+ # "Foo/Bar.pm" |
|
452 |
+ class_to_path 'Foo::Bar'; |
|
453 |
+ |
|
454 |
+ # "FooBar.pm" |
|
455 |
+ class_to_path 'FooBar'; |
|
447 | 456 |
|
448 | 457 |
=head2 decamelize |
449 | 458 |
|
450 | 459 |
my $snakecase = decamelize $camelcase; |
451 | 460 |
|
452 |
-Convert camel case string to snake case and replace C<::> with C<->. |
|
461 |
+Convert CamelCase string to snake_case and replace C<::> with C<->. |
|
453 | 462 |
|
454 | 463 |
# "foo_bar" |
455 | 464 |
decamelize 'FooBar'; |
... | ... |
@@ -473,6 +482,12 @@ Decode bytes to characters and return C<undef> if decoding failed. |
473 | 482 |
Warn about deprecated feature from perspective of caller. You can also set the |
474 | 483 |
MOJO_FATAL_DEPRECATIONS environment variable to make them die instead. |
475 | 484 |
|
485 |
+=head2 dumper |
|
486 |
+ |
|
487 |
+ my $perl = dumper {some => 'data'}; |
|
488 |
+ |
|
489 |
+Dump a Perl data structure with L<Data::Dumper>. |
|
490 |
+ |
|
476 | 491 |
=head2 encode |
477 | 492 |
|
478 | 493 |
my $bytes = encode 'UTF-8', $chars; |
... | ... |
@@ -488,9 +503,9 @@ with C<0x0d 0x0a> or C<0x0a>. |
488 | 503 |
|
489 | 504 |
=head2 hmac_sha1_sum |
490 | 505 |
|
491 |
- my $checksum = hmac_sha1_sum $str, 'passw0rd'; |
|
506 |
+ my $checksum = hmac_sha1_sum $bytes, 'passw0rd'; |
|
492 | 507 |
|
493 |
-Generate HMAC-SHA1 checksum for string. |
|
508 |
+Generate HMAC-SHA1 checksum for bytes. |
|
494 | 509 |
|
495 | 510 |
=head2 html_unescape |
496 | 511 |
|
... | ... |
@@ -500,15 +515,15 @@ Unescape all HTML entities in string. |
500 | 515 |
|
501 | 516 |
=head2 md5_bytes |
502 | 517 |
|
503 |
- my $checksum = md5_bytes $str; |
|
518 |
+ my $checksum = md5_bytes $bytes; |
|
504 | 519 |
|
505 |
-Generate binary MD5 checksum for string. |
|
520 |
+Generate binary MD5 checksum for bytes. |
|
506 | 521 |
|
507 | 522 |
=head2 md5_sum |
508 | 523 |
|
509 |
- my $checksum = md5_sum $str; |
|
524 |
+ my $checksum = md5_sum $bytes; |
|
510 | 525 |
|
511 |
-Generate MD5 checksum for string. |
|
526 |
+Generate MD5 checksum for bytes. |
|
512 | 527 |
|
513 | 528 |
=head2 monkey_patch |
514 | 529 |
|
... | ... |
@@ -542,31 +557,46 @@ Quote string. |
542 | 557 |
|
543 | 558 |
=head2 secure_compare |
544 | 559 |
|
545 |
- my $success = secure_compare $str1, $str2; |
|
560 |
+ my $bool = secure_compare $str1, $str2; |
|
546 | 561 |
|
547 | 562 |
Constant time comparison algorithm to prevent timing attacks. |
548 | 563 |
|
549 | 564 |
=head2 sha1_bytes |
550 | 565 |
|
551 |
- my $checksum = sha1_bytes $str; |
|
566 |
+ my $checksum = sha1_bytes $bytes; |
|
552 | 567 |
|
553 |
-Generate binary SHA1 checksum for string. |
|
568 |
+Generate binary SHA1 checksum for bytes. |
|
554 | 569 |
|
555 | 570 |
=head2 sha1_sum |
556 | 571 |
|
557 |
- my $checksum = sha1_sum $str; |
|
572 |
+ my $checksum = sha1_sum $bytes; |
|
558 | 573 |
|
559 |
-Generate SHA1 checksum for string. |
|
574 |
+Generate SHA1 checksum for bytes. |
|
560 | 575 |
|
561 | 576 |
=head2 slurp |
562 | 577 |
|
563 |
- my $content = slurp '/etc/passwd'; |
|
578 |
+ my $bytes = slurp '/etc/passwd'; |
|
564 | 579 |
|
565 | 580 |
Read all data at once from file. |
566 | 581 |
|
582 |
+=head2 split_header |
|
583 |
+ |
|
584 |
+ my $tree = split_header 'foo="bar baz"; test=123, yada'; |
|
585 |
+ |
|
586 |
+Split HTTP header value. |
|
587 |
+ |
|
588 |
+ # "one" |
|
589 |
+ split_header('one; two="three four", five=six')->[0][0]; |
|
590 |
+ |
|
591 |
+ # "three four" |
|
592 |
+ split_header('one; two="three four", five=six')->[0][3]; |
|
593 |
+ |
|
594 |
+ # "five" |
|
595 |
+ split_header('one; two="three four", five=six')->[1][0]; |
|
596 |
+ |
|
567 | 597 |
=head2 spurt |
568 | 598 |
|
569 |
- $content = spurt $content, '/etc/passwd'; |
|
599 |
+ $bytes = spurt $bytes, '/etc/passwd'; |
|
570 | 600 |
|
571 | 601 |
Write all data at once to file. |
572 | 602 |
|
... | ... |
@@ -4,7 +4,7 @@ use Mojo::Base 'Mojo'; |
4 | 4 |
# "Fry: Shut up and take my money!" |
5 | 5 |
use Carp 'croak'; |
6 | 6 |
use Mojo::Exception; |
7 |
-use Mojo::Util 'decamelize'; |
|
7 |
+use Mojo::Util qw(decamelize deprecated); |
|
8 | 8 |
use Mojolicious::Commands; |
9 | 9 |
use Mojolicious::Controller; |
10 | 10 |
use Mojolicious::Plugins; |
... | ... |
@@ -13,6 +13,7 @@ use Mojolicious::Routes; |
13 | 13 |
use Mojolicious::Sessions; |
14 | 14 |
use Mojolicious::Static; |
15 | 15 |
use Mojolicious::Types; |
16 |
+use Mojolicious::Validator; |
|
16 | 17 |
use Scalar::Util qw(blessed weaken); |
17 | 18 |
use Time::HiRes 'gettimeofday'; |
18 | 19 |
|
... | ... |
@@ -36,12 +37,13 @@ has secret => sub { |
36 | 37 |
# Default to moniker |
37 | 38 |
return $self->moniker; |
38 | 39 |
}; |
39 |
-has sessions => sub { Mojolicious::Sessions->new }; |
|
40 |
-has static => sub { Mojolicious::Static->new }; |
|
41 |
-has types => sub { Mojolicious::Types->new }; |
|
40 |
+has sessions => sub { Mojolicious::Sessions->new }; |
|
41 |
+has static => sub { Mojolicious::Static->new }; |
|
42 |
+has types => sub { Mojolicious::Types->new }; |
|
43 |
+has validator => sub { Mojolicious::Validator->new }; |
|
42 | 44 |
|
43 | 45 |
our $CODENAME = 'Top Hat'; |
44 |
-our $VERSION = '4.07'; |
|
46 |
+our $VERSION = '4.57'; |
|
45 | 47 |
|
46 | 48 |
sub AUTOLOAD { |
47 | 49 |
my $self = shift; |
... | ... |
@@ -69,11 +71,11 @@ sub new { |
69 | 71 |
my $r = $self->routes->namespaces([ref $self]); |
70 | 72 |
|
71 | 73 |
# Hide controller attributes/methods and "handler" |
72 |
- $r->hide(qw(AUTOLOAD DESTROY app cookie finish flash handler on param)); |
|
73 |
- $r->hide(qw(redirect_to render render_exception render_maybe)); |
|
74 |
+ $r->hide(qw(app continue cookie finish flash handler match on param)); |
|
75 |
+ $r->hide(qw(redirect_to render render_exception render_later render_maybe)); |
|
74 | 76 |
$r->hide(qw(render_not_found render_static rendered req res respond_to)); |
75 |
- $r->hide(qw(send session signed_cookie stash tx ua url_for write)); |
|
76 |
- $r->hide('write_chunk'); |
|
77 |
+ $r->hide(qw(send session signed_cookie stash tx url_for validation write)); |
|
78 |
+ $r->hide(qw(write_chunk)); |
|
77 | 79 |
|
78 | 80 |
# Check if we have a log directory |
79 | 81 |
my $mode = $self->mode; |
... | ... |
@@ -89,9 +91,13 @@ sub new { |
89 | 91 |
# Reduced log output outside of development mode |
90 | 92 |
$self->log->level('info') unless $mode eq 'development'; |
91 | 93 |
|
92 |
- # Run mode before startup |
|
93 |
- if (my $sub = $self->can("${mode}_mode")) { $self->$sub(@_) } |
|
94 |
- $self->startup(@_); |
|
94 |
+ # DEPRECATED in Top Hat! |
|
95 |
+ if (my $sub = $self->can("${mode}_mode")) { |
|
96 |
+ deprecated qq{"sub ${mode}_mode {...}" in application class is DEPRECATED}; |
|
97 |
+ $self->$sub; |
|
98 |
+ } |
|
99 |
+ |
|
100 |
+ $self->startup; |
|
95 | 101 |
|
96 | 102 |
return $self; |
97 | 103 |
} |
... | ... |
@@ -154,6 +160,7 @@ sub handler { |
154 | 160 |
|
155 | 161 |
# Dispatcher has to be last in the chain |
156 | 162 |
++$self->{dispatch} |
163 |
+ and $self->hook(around_action => sub { $_[2]->($_[1]) }) |
|
157 | 164 |
and $self->hook(around_dispatch => sub { $_[1]->app->dispatch($_[1]) }) |
158 | 165 |
unless $self->{dispatch}; |
159 | 166 |
|
... | ... |
@@ -162,7 +169,7 @@ sub handler { |
162 | 169 |
|
163 | 170 |
# Delayed response |
164 | 171 |
$self->log->debug('Nothing has been rendered, expecting delayed response.') |
165 |
- unless $stash->{'mojo.rendered'} || $tx->is_writing; |
|
172 |
+ unless $tx->is_writing; |
|
166 | 173 |
} |
167 | 174 |
|
168 | 175 |
sub helper { |
... | ... |
@@ -193,6 +200,8 @@ sub _exception { |
193 | 200 |
|
194 | 201 |
1; |
195 | 202 |
|
203 |
+=encoding utf8 |
|
204 |
+ |
|
196 | 205 |
=head1 NAME |
197 | 206 |
|
198 | 207 |
Mojolicious - Real-time web framework |
... | ... |
@@ -223,6 +232,131 @@ Mojolicious - Real-time web framework |
223 | 232 |
|
224 | 233 |
Take a look at our excellent documentation in L<Mojolicious::Guides>! |
225 | 234 |
|
235 |
+=head1 HOOKS |
|
236 |
+ |
|
237 |
+L<Mojolicious> will emit the following hooks in the listed order. |
|
238 |
+ |
|
239 |
+=head2 after_build_tx |
|
240 |
+ |
|
241 |
+Emitted right after the transaction is built and before the HTTP request gets |
|
242 |
+parsed. |
|
243 |
+ |
|
244 |
+ $app->hook(after_build_tx => sub { |
|
245 |
+ my ($tx, $app) = @_; |
|
246 |
+ ... |
|
247 |
+ }); |
|
248 |
+ |
|
249 |
+This is a very powerful hook and should not be used lightly, it makes some |
|
250 |
+rather advanced features such as upload progress bars possible. Note that this |
|
251 |
+hook will not work for embedded applications. (Passed the transaction and |
|
252 |
+application object) |
|
253 |
+ |
|
254 |
+=head2 before_dispatch |
|
255 |
+ |
|
256 |
+Emitted right before the static file server and router start their work. |
|
257 |
+ |
|
258 |
+ $app->hook(before_dispatch => sub { |
|
259 |
+ my $c = shift; |
|
260 |
+ ... |
|
261 |
+ }); |
|
262 |
+ |
|
263 |
+Very useful for rewriting incoming requests and other preprocessing tasks. |
|
264 |
+(Passed the default controller object) |
|
265 |
+ |
|
266 |
+=head2 after_static |
|
267 |
+ |
|
268 |
+Emitted after a static file response has been generated by the static file |
|
269 |
+server. |
|
270 |
+ |
|
271 |
+ $app->hook(after_static => sub { |
|
272 |
+ my $c = shift; |
|
273 |
+ ... |
|
274 |
+ }); |
|
275 |
+ |
|
276 |
+Mostly used for post-processing static file responses. (Passed the default |
|
277 |
+controller object) |
|
278 |
+ |
|
279 |
+=head2 before_routes |
|
280 |
+ |
|
281 |
+Emitted after the static file server determined if a static file should be |
|
282 |
+served and before the router starts its work. |
|
283 |
+ |
|
284 |
+ $app->hook(before_routes => sub { |
|
285 |
+ my $c = shift; |
|
286 |
+ ... |
|
287 |
+ }); |
|
288 |
+ |
|
289 |
+Mostly used for custom dispatchers and collecting metrics. (Passed the default |
|
290 |
+controller object) |
|
291 |
+ |
|
292 |
+=head2 around_action |
|
293 |
+ |
|
294 |
+Emitted right before an action gets invoked and wraps around it, so you have |
|
295 |
+to manually forward to the next hook if you want to continue the chain. |
|
296 |
+Default action dispatching is the last hook in the chain, yours will run |
|
297 |
+before it. |
|
298 |
+ |
|
299 |
+ $app->hook(around_action => sub { |
|
300 |
+ my ($next, $c, $action, $last) = @_; |
|
301 |
+ ... |
|
302 |
+ return $next->(); |
|
303 |
+ }); |
|
304 |
+ |
|
305 |
+This is a very powerful hook and should not be used lightly, it allows you for |
|
306 |
+example to pass additional arguments to actions or handle return values |
|
307 |
+differently. (Passed a callback leading to the next hook, the current |
|
308 |
+controller object, the action callback and a flag indicating if this action is |
|
309 |
+an endpoint) |
|
310 |
+ |
|
311 |
+=head2 after_render |
|
312 |
+ |
|
313 |
+Emitted after content has been generated by the renderer that is not partial. |
|
314 |
+Note that this hook can trigger out of order due to its dynamic nature, and |
|
315 |
+with embedded applications will only work for the application that is |
|
316 |
+rendering. |
|
317 |
+ |
|
318 |
+ $app->hook(after_render => sub { |
|
319 |
+ my ($c, $output, $format) = @_; |
|
320 |
+ ... |
|
321 |
+ }); |
|
322 |
+ |
|
323 |
+Mostly used for post-processing dynamically generated content. (Passed the |
|
324 |
+current controller object, a reference to the content and the format) |
|
325 |
+ |
|
326 |
+=head2 after_dispatch |
|
327 |
+ |
|
328 |
+Emitted in reverse order after a response has been rendered. Note that this |
|
329 |
+hook can trigger out of order due to its dynamic nature, and with embedded |
|
330 |
+applications will only work for the application that is rendering. |
|
331 |
+ |
|
332 |
+ $app->hook(after_dispatch => sub { |
|
333 |
+ my $c = shift; |
|
334 |
+ ... |
|
335 |
+ }); |
|
336 |
+ |
|
337 |
+Useful for rewriting outgoing responses and other post-processing tasks. |
|
338 |
+(Passed the current controller object) |
|
339 |
+ |
|
340 |
+=head2 around_dispatch |
|
341 |
+ |
|
342 |
+Emitted right before the L</"before_dispatch"> hook and wraps around the whole |
|
343 |
+dispatch process, so you have to manually forward to the next hook if you want |
|
344 |
+to continue the chain. Default exception handling with |
|
345 |
+L<Mojolicious::Controller/"render_exception"> is the first hook in the chain |
|
346 |
+and a call to L</"dispatch"> the last, yours will be in between. |
|
347 |
+ |
|
348 |
+ $app->hook(around_dispatch => sub { |
|
349 |
+ my ($next, $c) = @_; |
|
350 |
+ ... |
|
351 |
+ $next->(); |
|
352 |
+ ... |
|
353 |
+ }); |
|
354 |
+ |
|
355 |
+This is a very powerful hook and should not be used lightly, it allows you for |
|
356 |
+example to customize application wide exception handling, consider it the |
|
357 |
+sledgehammer in your toolbox. (Passed a callback leading to the next hook and |
|
358 |
+the default controller object) |
|
359 |
+ |
|
226 | 360 |
=head1 ATTRIBUTES |
227 | 361 |
|
228 | 362 |
L<Mojolicious> inherits all attributes from L<Mojo> and implements the |
... | ... |
@@ -253,24 +387,10 @@ L<Mojolicious::Controller>. |
253 | 387 |
$app = $app->mode('production'); |
254 | 388 |
|
255 | 389 |
The operating mode for your application, defaults to a value from the |
256 |
-MOJO_MODE and PLACK_ENV environment variables or C<development>. You can also |
|
257 |
-add per mode logic to your application by defining methods named |
|
258 |
-C<${mode}_mode> in the application class, which will be called right before |
|
259 |
-C<startup>. |
|
260 |
- |
|
261 |
- sub development_mode { |
|
262 |
- my $self = shift; |
|
263 |
- ... |
|
264 |
- } |
|
265 |
- |
|
266 |
- sub production_mode { |
|
267 |
- my $self = shift; |
|
268 |
- ... |
|
269 |
- } |
|
270 |
- |
|
271 |
-Right before calling C<startup> and mode specific methods, L<Mojolicious> |
|
272 |
-will pick up the current mode, name the log file after it and raise the log |
|
273 |
-level from C<debug> to C<info> if it has a value other than C<development>. |
|
390 |
+MOJO_MODE and PLACK_ENV environment variables or C<development>. Right before |
|
391 |
+calling L</"startup">, L<Mojolicious> will pick up the current mode, name the |
|
392 |
+log file after it and raise the log level from C<debug> to C<info> if it has a |
|
393 |
+value other than C<development>. |
|
274 | 394 |
|
275 | 395 |
=head2 moniker |
276 | 396 |
|
... | ... |
@@ -287,7 +407,7 @@ L<Mojo::Util/"decamelize">. |
287 | 407 |
$app = $app->plugins(Mojolicious::Plugins->new); |
288 | 408 |
|
289 | 409 |
The plugin manager, defaults to a L<Mojolicious::Plugins> object. See the |
290 |
-C<plugin> method below if you want to load a plugin. |
|
410 |
+L</"plugin"> method below if you want to load a plugin. |
|
291 | 411 |
|
292 | 412 |
# Add another namespace to load plugins from |
293 | 413 |
push @{$app->plugins->namespaces}, 'MyApp::Plugin'; |
... | ... |
@@ -330,8 +450,8 @@ startup method to define the url endpoints for your application. |
330 | 450 |
$app = $app->secret('passw0rd'); |
331 | 451 |
|
332 | 452 |
A secret passphrase used for signed cookies and the like, defaults to the |
333 |
-C<moniker> of this application, which is not very secure, so you should change |
|
334 |
-it!!! As long as you are using the insecure default there will be debug |
|
453 |
+L</"moniker"> of this application, which is not very secure, so you should |
|
454 |
+change it!!! As long as you are using the insecure default there will be debug |
|
335 | 455 |
messages in the log file reminding you to change your passphrase. |
336 | 456 |
|
337 | 457 |
=head2 sessions |
... | ... |
@@ -372,6 +492,13 @@ L<Mojolicious::Types> object. |
372 | 492 |
# Add custom MIME type |
373 | 493 |
$app->types->type(twt => 'text/tweet'); |
374 | 494 |
|
495 |
+=head2 validator |
|
496 |
+ |
|
497 |
+ my $validator = $app->validator; |
|
498 |
+ $app = $app->validator(Mojolicious::Validator->new); |
|
499 |
+ |
|
500 |
+Validate form data, defaults to a L<Mojolicious::Validator> object. |
|
501 |
+ |
|
375 | 502 |
=head1 METHODS |
376 | 503 |
|
377 | 504 |
L<Mojolicious> inherits all methods from L<Mojo> and implements the following |
... | ... |
@@ -382,10 +509,10 @@ new ones. |
382 | 509 |
my $app = Mojolicious->new; |
383 | 510 |
|
384 | 511 |
Construct a new L<Mojolicious> application, calling C<${mode}_mode> and |
385 |
-C<startup> in the process. Will automatically detect your home directory and |
|
386 |
-set up logging based on your current operating mode. Also sets up the |
|
512 |
+L</"startup"> in the process. Will automatically detect your home directory |
|
513 |
+and set up logging based on your current operating mode. Also sets up the |
|
387 | 514 |
renderer, static file server, a default set of plugins and an |
388 |
-C<around_dispatch> hook with the default exception handling. |
|
515 |
+L</"around_dispatch"> hook with the default exception handling. |
|
389 | 516 |
|
390 | 517 |
=head2 build_tx |
391 | 518 |
|
... | ... |
@@ -411,9 +538,9 @@ request. |
411 | 538 |
|
412 | 539 |
$app->dispatch(Mojolicious::Controller->new); |
413 | 540 |
|
414 |
-The heart of every Mojolicious application, calls the C<static> and C<routes> |
|
415 |
-dispatchers for every request and passes them a L<Mojolicious::Controller> |
|
416 |
-object. |
|
541 |
+The heart of every L<Mojolicious> application, calls the L</"static"> and |
|
542 |
+L</"routes"> dispatchers for every request and passes them a |
|
543 |
+L<Mojolicious::Controller> object. |
|
417 | 544 |
|
418 | 545 |
=head2 handler |
419 | 546 |
|
... | ... |
@@ -445,7 +572,7 @@ and the application object, as well as a function in C<ep> templates. |
445 | 572 |
$app->hook(after_dispatch => sub {...}); |
446 | 573 |
|
447 | 574 |
Extend L<Mojolicious> with hooks, which allow code to be shared with all |
448 |
-requests indiscriminately. |
|
575 |
+requests indiscriminately, for a full list of available hooks see L</"HOOKS">. |
|
449 | 576 |
|
450 | 577 |
# Dispatchers will not run if there's already a response code defined |
451 | 578 |
$app->hook(before_dispatch => sub { |
... | ... |
@@ -454,114 +581,6 @@ requests indiscriminately. |
454 | 581 |
if $c->req->url->path->to_route =~ /do_not_dispatch/; |
455 | 582 |
}); |
456 | 583 |
|
457 |
-These hooks are currently available and are emitted in the listed order: |
|
458 |
- |
|
459 |
-=over 2 |
|
460 |
- |
|
461 |
-=item after_build_tx |
|
462 |
- |
|
463 |
-Emitted right after the transaction is built and before the HTTP request gets |
|
464 |
-parsed. |
|
465 |
- |
|
466 |
- $app->hook(after_build_tx => sub { |
|
467 |
- my ($tx, $app) = @_; |
|
468 |
- ... |
|
469 |
- }); |
|
470 |
- |
|
471 |
-This is a very powerful hook and should not be used lightly, it makes some |
|
472 |
-rather advanced features such as upload progress bars possible. Note that this |
|
473 |
-hook will not work for embedded applications. (Passed the transaction and |
|
474 |
-application object) |
|
475 |
- |
|
476 |
-=item before_dispatch |
|
477 |
- |
|
478 |
-Emitted right before the static file server and router start their work. |
|
479 |
- |
|
480 |
- $app->hook(before_dispatch => sub { |
|
481 |
- my $c = shift; |
|
482 |
- ... |
|
483 |
- }); |
|
484 |
- |
|
485 |
-Very useful for rewriting incoming requests and other preprocessing tasks. |
|
486 |
-(Passed the default controller object) |
|
487 |
- |
|
488 |
-=item after_static |
|
489 |
- |
|
490 |
-Emitted after a static file response has been generated by the static file |
|
491 |
-server. |
|
492 |
- |
|
493 |
- $app->hook(after_static => sub { |
|
494 |
- my $c = shift; |
|
495 |
- ... |
|
496 |
- }); |
|
497 |
- |
|
498 |
-Mostly used for post-processing static file responses. (Passed the default |
|
499 |
-controller object) |
|
500 |
- |
|
501 |
-=item before_routes |
|
502 |
- |
|
503 |
-Emitted after the static file server determined if a static file should be |
|
504 |
-served and before the router starts its work. |
|
505 |
- |
|
506 |
- $app->hook(before_routes => sub { |
|
507 |
- my $c = shift; |
|
508 |
- ... |
|
509 |
- }); |
|
510 |
- |
|
511 |
-Mostly used for custom dispatchers and collecting metrics. (Passed the default |
|
512 |
-controller object) |
|
513 |
- |
|
514 |
-=item after_render |
|
515 |
- |
|
516 |
-Emitted after content has been generated by the renderer that is not partial. |
|
517 |
-Note that this hook can trigger out of order due to its dynamic nature, and |
|
518 |
-with embedded applications will only work for the application that is |
|
519 |
-rendering. |
|
520 |
- |
|
521 |
- $app->hook(after_render => sub { |
|
522 |
- my ($c, $output, $format) = @_; |
|
523 |
- ... |
|
524 |
- }); |
|
525 |
- |
|
526 |
-Mostly used for post-processing dynamically generated content. (Passed the |
|
527 |
-current controller object, a reference to the content and the format) |
|
528 |
- |
|
529 |
-=item after_dispatch |
|
530 |
- |
|
531 |
-Emitted in reverse order after a response has been rendered. Note that this |
|
532 |
-hook can trigger out of order due to its dynamic nature, and with embedded |
|
533 |
-applications will only work for the application that is rendering. |
|
534 |
- |
|
535 |
- $app->hook(after_dispatch => sub { |
|
536 |
- my $c = shift; |
|
537 |
- ... |
|
538 |
- }); |
|
539 |
- |
|
540 |
-Useful for rewriting outgoing responses and other post-processing tasks. |
|
541 |
-(Passed the current controller object) |
|
542 |
- |
|
543 |
-=item around_dispatch |
|
544 |
- |
|
545 |
-Emitted right before the C<before_dispatch> hook and wraps around the whole |
|
546 |
-dispatch process, so you have to manually forward to the next hook if you want |
|
547 |
-to continue the chain. Default exception handling with |
|
548 |
-L<Mojolicious::Controller/"render_exception"> is the first hook in the chain |
|
549 |
-and a call to C<dispatch> the last, yours will be in between. |
|
550 |
- |
|
551 |
- $app->hook(around_dispatch => sub { |
|
552 |
- my ($next, $c) = @_; |
|
553 |
- ... |
|
554 |
- $next->(); |
|
555 |
- ... |
|
556 |
- }); |
|
557 |
- |
|
558 |
-This is a very powerful hook and should not be used lightly, it allows you to |
|
559 |
-customize application wide exception handling for example, consider it the |
|
560 |
-sledgehammer in your toolbox. (Passed a callback leading to the next hook and |
|
561 |
-the default controller object) |
|
562 |
- |
|
563 |
-=back |
|
564 |
- |
|
565 | 584 |
=head2 plugin |
566 | 585 |
|
567 | 586 |
$app->plugin('some_thing'); |
... | ... |
@@ -674,7 +693,7 @@ Sebastian Riedel, C<sri@cpan.org> |
674 | 693 |
|
675 | 694 |
Current members of the core team in alphabetical order: |
676 | 695 |
|
677 |
-=over 4 |
|
696 |
+=over 2 |
|
678 | 697 |
|
679 | 698 |
Abhijit Menon-Sen, C<ams@cpan.org> |
680 | 699 |
|
... | ... |
@@ -698,6 +717,8 @@ Adriano Ferreira |
698 | 717 |
|
699 | 718 |
Al Newkirk |
700 | 719 |
|
720 |
+Alex Efros |
|
721 |
+ |
|
701 | 722 |
Alex Salimon |
702 | 723 |
|
703 | 724 |
Alexey Likhatskiy |
... | ... |
@@ -734,6 +755,8 @@ Breno G. de Oliveira |
734 | 755 |
|
735 | 756 |
Brian Duggan |
736 | 757 |
|
758 |
+Brian Medley |
|
759 |
+ |
|
737 | 760 |
Burak Gursoy |
738 | 761 |
|
739 | 762 |
Ch Lamprecht |
... | ... |
@@ -780,10 +803,14 @@ Henry Tang |
780 | 803 |
|
781 | 804 |
Hideki Yamamura |
782 | 805 |
|
806 |
+Ian Goodacre |
|
807 |
+ |
|
783 | 808 |
Ilya Chesnokov |
784 | 809 |
|
785 | 810 |
James Duncan |
786 | 811 |
|
812 |
+Jan Henning Thorsen |
|
813 |
+ |
|
787 | 814 |
Jan Jona Javorsek |
788 | 815 |
|
789 | 816 |
Jan Schmidt |
... | ... |
@@ -798,6 +825,8 @@ John Kingsley |
798 | 825 |
|
799 | 826 |
Jonathan Yu |
800 | 827 |
|
828 |
+Josh Leder |
|
829 |
+ |
|
801 | 830 |
Kazuhiro Shibuya |
802 | 831 |
|
803 | 832 |
Kevin Old |
... | ... |
@@ -846,6 +875,8 @@ Paul Evans |
846 | 875 |
|
847 | 876 |
Paul Tomlin |
848 | 877 |
|
878 |
+Pavel Shaydo |
|
879 |
+ |
|
849 | 880 |
Pedro Melo |
850 | 881 |
|
851 | 882 |
Peter Edwards |
... | ... |
@@ -88,13 +88,15 @@ sub write_rel_file { |
88 | 88 |
|
89 | 89 |
1; |
90 | 90 |
|
91 |
+=encoding utf8 |
|
92 |
+ |
|
91 | 93 |
=head1 NAME |
92 | 94 |
|
93 | 95 |
Mojolicious::Command - Command base class |
94 | 96 |
|
95 | 97 |
=head1 SYNOPSIS |
96 | 98 |
|
97 |
- # Lower case command name |
|
99 |
+ # Lowercase command name |
|
98 | 100 |
package Mojolicious::Command::mycommand; |
99 | 101 |
use Mojo::Base 'Mojolicious::Command'; |
100 | 102 |
|
... | ... |
@@ -102,7 +104,7 @@ Mojolicious::Command - Command base class |
102 | 104 |
has description => "My first Mojo command.\n"; |
103 | 105 |
|
104 | 106 |
# Short usage message |
105 |
- has usage => <<"EOF"; |
|
107 |
+ has usage => <<EOF; |
|
106 | 108 |
usage: $0 mycommand [OPTIONS] |
107 | 109 |
|
108 | 110 |
These options are available: |
... | ... |
@@ -145,8 +147,8 @@ Short description of command, used for the command list. |
145 | 147 |
|
146 | 148 |
=head2 quiet |
147 | 149 |
|
148 |
- my $quiet = $command->quiet; |
|
149 |
- $command = $command->quiet(1); |
|
150 |
+ my $bool = $command->quiet; |
|
151 |
+ $command = $command->quiet($bool); |
|
150 | 152 |
|
151 | 153 |
Limited command output. |
152 | 154 |
|
... | ... |
@@ -5,7 +5,7 @@ use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case); |
5 | 5 |
use Mojo::Server::CGI; |
6 | 6 |
|
7 | 7 |
has description => "Start application with CGI.\n"; |
8 |
-has usage => <<"EOF"; |
|
8 |
+has usage => <<EOF; |
|
9 | 9 |
usage: $0 cgi [OPTIONS] |
10 | 10 |
|
11 | 11 |
These options are available: |
... | ... |
@@ -21,6 +21,8 @@ sub run { |
21 | 21 |
|
22 | 22 |
1; |
23 | 23 |
|
24 |
+=encoding utf8 |
|
25 |
+ |
|
24 | 26 |
=head1 NAME |
25 | 27 |
|
26 | 28 |
Mojolicious::Command::cgi - CGI command |
... | ... |
@@ -6,7 +6,7 @@ use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case); |
6 | 6 |
use Mojo::UserAgent; |
7 | 7 |
|
8 | 8 |
has description => "Upload distribution to CPAN.\n"; |
9 |
-has usage => <<"EOF"; |
|
9 |
+has usage => <<EOF; |
|
10 | 10 |
usage: $0 cpanify [OPTIONS] [FILE] |
11 | 11 |
|
12 | 12 |
mojo cpanify -u sri -p secr3t Mojolicious-Plugin-MyPlugin-0.01.tar.gz |
... | ... |
@@ -24,7 +24,7 @@ sub run { |
24 | 24 |
'u|user=s' => \(my $user = ''); |
25 | 25 |
die $self->usage unless my $file = shift @args; |
26 | 26 |
|
27 |
- my $tx = Mojo::UserAgent->new->detect_proxy->post( |
|
27 |
+ my $tx = Mojo::UserAgent->new->tap(sub { $_->proxy->detect })->post( |
|
28 | 28 |
"https://$user:$password\@pause.perl.org/pause/authenquery" => form => { |
29 | 29 |
HIDDENNAME => $user, |
30 | 30 |
CAN_MULTIPART => 1, |
... | ... |
@@ -48,6 +48,8 @@ sub run { |
48 | 48 |
|
49 | 49 |
1; |
50 | 50 |
|
51 |
+=encoding utf8 |
|
52 |
+ |
|
51 | 53 |
=head1 NAME |
52 | 54 |
|
53 | 55 |
Mojolicious::Command::cpanify - Cpanify command |
... | ... |
@@ -5,7 +5,7 @@ use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case); |
5 | 5 |
use Mojo::Server::Daemon; |
6 | 6 |
|
7 | 7 |
has description => "Start application with HTTP and WebSocket server.\n"; |
8 |
-has usage => <<"EOF"; |
|
8 |
+has usage => <<EOF; |
|
9 | 9 |
usage: $0 daemon [OPTIONS] |
10 | 10 |
|
11 | 11 |
These options are available: |
... | ... |
@@ -45,6 +45,8 @@ sub run { |
45 | 45 |
|
46 | 46 |
1; |
47 | 47 |
|
48 |
+=encoding utf8 |
|
49 |
+ |
|
48 | 50 |
=head1 NAME |
49 | 51 |
|
50 | 52 |
Mojolicious::Command::daemon - Daemon command |
... | ... |
@@ -4,32 +4,36 @@ use Mojo::Base 'Mojolicious::Command'; |
4 | 4 |
use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case); |
5 | 5 |
|
6 | 6 |
has description => "Run code against application.\n"; |
7 |
-has usage => <<"EOF"; |
|
7 |
+has usage => <<EOF; |
|
8 | 8 |
usage: $0 eval [OPTIONS] CODE |
9 | 9 |
|
10 | 10 |
mojo eval 'say app->ua->get("/")->res->body' |
11 | 11 |
mojo eval -v 'app->home' |
12 |
+ mojo eval -V 'app->renderer->paths' |
|
12 | 13 |
|
13 | 14 |
These options are available: |
14 | 15 |
-v, --verbose Print return value to STDOUT. |
16 |
+ -V Print returned data structure to STDOUT. |
|
15 | 17 |
EOF |
16 | 18 |
|
17 | 19 |
sub run { |
18 | 20 |
my ($self, @args) = @_; |
19 | 21 |
|
20 |
- GetOptionsFromArray \@args, 'v|verbose' => \my $verbose; |
|
22 |
+ GetOptionsFromArray \@args, 'v|verbose' => \my $v1, 'V' => \my $v2; |
|
21 | 23 |
my $code = shift @args || ''; |
22 | 24 |
|
23 | 25 |
# Run code against application |
24 | 26 |
my $app = $self->app; |
25 | 27 |
no warnings; |
26 |
- my $result = eval "package main; sub app { \$app }; $code"; |
|
27 |
- say $result if $verbose && defined $result; |
|
28 |
- return $@ ? die $@ : $result; |
|
28 |
+ my $result = eval "package main; sub app; local *app = sub { \$app }; $code"; |
|
29 |
+ return $@ ? die $@ : $result unless defined $result && ($v1 || $v2); |
|
30 |
+ $v2 ? print($app->dumper($result)) : say $result; |
|
29 | 31 |
} |
30 | 32 |
|
31 | 33 |
1; |
32 | 34 |
|
35 |
+=encoding utf8 |
|
36 |
+ |
|
33 | 37 |
=head1 NAME |
34 | 38 |
|
35 | 39 |
Mojolicious::Command::eval - Eval command |
... | ... |
@@ -2,11 +2,11 @@ package Mojolicious::Command::generate; |
2 | 2 |
use Mojo::Base 'Mojolicious::Commands'; |
3 | 3 |
|
4 | 4 |
has description => "Generate files and directories from templates.\n"; |
5 |
-has hint => <<"EOF"; |
|
5 |
+has hint => <<EOF; |
|
6 | 6 |
|
7 | 7 |
See '$0 generate help GENERATOR' for more information on a specific generator. |
8 | 8 |
EOF |
9 |
-has message => <<"EOF"; |
|
9 |
+has message => <<EOF; |
|
10 | 10 |
usage: $0 generate GENERATOR [OPTIONS] |
11 | 11 |
|
12 | 12 |
These generators are currently available: |
... | ... |
@@ -18,6 +18,8 @@ sub help { shift->run(@_) } |
18 | 18 |
|
19 | 19 |
1; |
20 | 20 |
|
21 |
+=encoding utf8 |
|
22 |
+ |
|
21 | 23 |
=head1 NAME |
22 | 24 |
|
23 | 25 |
Mojolicious::Command::generate - Generator command |
... | ... |
@@ -8,11 +8,11 @@ has usage => "usage: $0 generate app [NAME]\n"; |
8 | 8 |
|
9 | 9 |
sub run { |
10 | 10 |
my ($self, $class) = @_; |
11 |
- $class ||= 'MyMojoliciousApp'; |
|
11 |
+ $class ||= 'MyApp'; |
|
12 | 12 |
|
13 | 13 |
# Prevent bad applications |
14 | 14 |
die <<EOF unless $class =~ /^[A-Z](?:\w|::)+$/; |
15 |
-Your application name has to be a well formed (camel case) Perl module name |
|
15 |
+Your application name has to be a well formed (CamelCase) Perl module name |
|
16 | 16 |
like "MyApp". |
17 | 17 |
EOF |
18 | 18 |
|
... | ... |
@@ -94,8 +94,7 @@ sub welcome { |
94 | 94 |
my $self = shift; |
95 | 95 |
|
96 | 96 |
# Render template "example/welcome.html.ep" with message |
97 |
- $self->render( |
|
98 |
- message => 'Welcome to the Mojolicious real-time web framework!'); |
|
97 |
+ $self->render(msg => 'Welcome to the Mojolicious real-time web framework!'); |
|
99 | 98 |
} |
100 | 99 |
|
101 | 100 |
1; |
... | ... |
@@ -135,13 +134,16 @@ done_testing(); |
135 | 134 |
@@ welcome |
136 | 135 |
%% layout 'default'; |
137 | 136 |
%% title 'Welcome'; |
138 |
-<h2><%%= $message %></h2> |
|
137 |
+<h2><%%= $msg %></h2> |
|
139 | 138 |
This page was generated from the template "templates/example/welcome.html.ep" |
140 | 139 |
and the layout "templates/layouts/default.html.ep", |
141 | 140 |
<a href="<%%== url_for %>">click here</a> to reload the page or |
142 | 141 |
<a href="/index.html">here</a> to move forward to a static page. |
143 | 142 |
|
144 | 143 |
__END__ |
144 |
+ |
|
145 |
+=encoding utf8 |
|
146 |
+ |
|
145 | 147 |
=head1 NAME |
146 | 148 |
|
147 | 149 |
Mojolicious::Command::generate::app - App generator command |
... | ... |
@@ -42,6 +42,9 @@ Welcome to the Mojolicious real-time web framework! |
42 | 42 |
</html> |
43 | 43 |
|
44 | 44 |
__END__ |
45 |
+ |
|
46 |
+=encoding utf8 |
|
47 |
+ |
|
45 | 48 |
=head1 NAME |
46 | 49 |
|
47 | 50 |
Mojolicious::Command::generate::lite_app - Lite app generator command |
... | ... |
@@ -24,6 +24,9 @@ WriteMakefile( |
24 | 24 |
); |
25 | 25 |
|
26 | 26 |
__END__ |
27 |
+ |
|
28 |
+=encoding utf8 |
|
29 |
+ |
|
27 | 30 |
=head1 NAME |
28 | 31 |
|
29 | 32 |
Mojolicious::Command::generate::makefile - Makefile generator command |
... | ... |
@@ -41,6 +41,8 @@ sub register { |
41 | 41 |
1; |
42 | 42 |
<% %>__END__ |
43 | 43 |
|
44 |
+<% %>=encoding utf8 |
|
45 |
+ |
|
44 | 46 |
<% %>=head1 NAME |
45 | 47 |
|
46 | 48 |
<%= $class %> - Mojolicious Plugin |
... | ... |
@@ -110,6 +112,9 @@ WriteMakefile( |
110 | 112 |
); |
111 | 113 |
|
112 | 114 |
__END__ |
115 |
+ |
|
116 |
+=encoding utf8 |
|
117 |
+ |
|
113 | 118 |
=head1 NAME |
114 | 119 |
|
115 | 120 |
Mojolicious::Command::generate::plugin - Plugin generator command |
... | ... |
@@ -11,7 +11,7 @@ use Mojo::Util qw(decode encode); |
11 | 11 |
use Scalar::Util 'weaken'; |
12 | 12 |
|
13 | 13 |
has description => "Perform HTTP request.\n"; |
14 |
-has usage => <<"EOF"; |
|
14 |
+has usage => <<EOF; |
|
15 | 15 |
usage: $0 get [OPTIONS] URL [SELECTOR|JSON-POINTER] [COMMANDS] |
16 | 16 |
|
17 | 17 |
mojo get / |
... | ... |
@@ -24,7 +24,7 @@ usage: $0 get [OPTIONS] URL [SELECTOR|JSON-POINTER] [COMMANDS] |
24 | 24 |
mojo get mojolicio.us a attr href |
25 | 25 |
mojo get mojolicio.us '*' attr id |
26 | 26 |
mojo get mojolicio.us 'h1, h2, h3' 3 text |
27 |
- mojo get http://search.twitter.com/search.json /error |
|
27 |
+ mojo get https://api.metacpan.org/v0/author/SRI /name |
|
28 | 28 |
|
29 | 29 |
These options are available: |
30 | 30 |
-C, --charset <charset> Charset of HTML/XML content, defaults to auto |
... | ... |
@@ -57,7 +57,7 @@ sub run { |
57 | 57 |
|
58 | 58 |
# Detect proxy for absolute URLs |
59 | 59 |
my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton); |
60 |
- $url !~ m!^/! ? $ua->detect_proxy : $ua->app($self->app); |
|
60 |
+ $url !~ m!^/! ? $ua->proxy->detect : $ua->server->app($self->app); |
|
61 | 61 |
$ua->max_redirects(10) if $redirect; |
62 | 62 |
|
63 | 63 |
my $buffer = ''; |
... | ... |
@@ -135,7 +135,7 @@ sub _select { |
135 | 135 |
# Attribute |
136 | 136 |
elsif ($command eq 'attr') { |
137 | 137 |
next unless my $name = shift @args; |
138 |
- _say($_->attrs->{$name}) for @$results; |
|
138 |
+ _say($_->attr->{$name}) for @$results; |
|
139 | 139 |
} |
140 | 140 |
|
141 | 141 |
# Unknown |
... | ... |
@@ -148,6 +148,8 @@ sub _select { |
148 | 148 |
|
149 | 149 |
1; |
150 | 150 |
|
151 |
+=encoding utf8 |
|
152 |
+ |
|
151 | 153 |
=head1 NAME |
152 | 154 |
|
153 | 155 |
Mojolicious::Command::get - Get command |
... | ... |
@@ -14,19 +14,25 @@ sub run { |
14 | 14 |
my %all; |
15 | 15 |
my $app = $self->app; |
16 | 16 |
my $loader = Mojo::Loader->new; |
17 |
- %all = (%{$loader->data($_)}, %all) |
|
18 |
- for @{$app->renderer->classes}, @{$app->static->classes}; |
|
17 |
+ for my $class (@{$app->renderer->classes}, @{$app->static->classes}) { |
|
18 |
+ for my $name (keys %{$loader->data($class)}) { |
|
19 |
+ my $data = $loader->data($class, $name); |
|
20 |
+ $all{$name} |
|
21 |
+ = $loader->is_binary($class, $name) ? $data : encode('UTF-8', $data); |
|
22 |
+ } |
|
23 |
+ } |
|
19 | 24 |
|
20 | 25 |
# Turn them into real files |
21 |
- for my $file (keys %all) { |
|
22 |
- my $prefix = $file =~ /\.\w+\.\w+$/ ? 'templates' : 'public'; |
|
23 |
- my $path = $self->rel_file("$prefix/$file"); |
|
24 |
- $self->write_file($path, encode('UTF-8', $all{$file})); |
|
26 |
+ for my $name (keys %all) { |
|
27 |
+ my $prefix = $name =~ /\.\w+\.\w+$/ ? 'templates' : 'public'; |
|
28 |
+ $self->write_file($self->rel_file("$prefix/$name"), $all{$name}); |
|
25 | 29 |
} |
26 | 30 |
} |
27 | 31 |
|
28 | 32 |
1; |
29 | 33 |
|
34 |
+=encoding utf8 |
|
35 |
+ |
|
30 | 36 |
=head1 NAME |
31 | 37 |
|
32 | 38 |
Mojolicious::Command::inflate - Inflate command |
... | ... |
@@ -6,7 +6,7 @@ use Mojo::Server::Prefork; |
6 | 6 |
|
7 | 7 |
has description => |
8 | 8 |
"Start application with preforking HTTP and WebSocket server.\n"; |
9 |
-has usage => <<"EOF"; |
|
9 |
+has usage => <<EOF; |
|
10 | 10 |
usage: $0 prefork [OPTIONS] |
11 | 11 |
|
12 | 12 |
These options are available: |
... | ... |
@@ -72,6 +72,8 @@ sub run { |
72 | 72 |
|
73 | 73 |
1; |
74 | 74 |
|
75 |
+=encoding utf8 |
|
76 |
+ |
|
75 | 77 |
=head1 NAME |
76 | 78 |
|
77 | 79 |
Mojolicious::Command::prefork - Prefork command |
... | ... |
@@ -10,6 +10,8 @@ sub run { Mojo::Server::PSGI->new(app => shift->app)->to_psgi_app } |
10 | 10 |
|
11 | 11 |
1; |
12 | 12 |
|
13 |
+=encoding utf8 |
|
14 |
+ |
|
13 | 15 |
=head1 NAME |
14 | 16 |
|
15 | 17 |
Mojolicious::Command::psgi - PSGI command |
... | ... |
@@ -6,7 +6,7 @@ use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case); |
6 | 6 |
use Mojo::Util 'encode'; |
7 | 7 |
|
8 | 8 |
has description => "Show available routes.\n"; |
9 |
-has usage => <<"EOF"; |
|
9 |
+has usage => <<EOF; |
|
10 | 10 |
usage: $0 routes [OPTIONS] |
11 | 11 |
|
12 | 12 |
These options are available: |
... | ... |
@@ -51,7 +51,8 @@ sub _draw { |
51 | 51 |
my $format = (regexp_pattern($pattern->format_regex || ''))[0]; |
52 | 52 |
my $optional |
53 | 53 |
= !$pattern->constraints->{format} || $pattern->defaults->{format}; |
54 |
- $regex .= $optional ? "(?:$format)?" : $format if $format; |
|
54 |
+ $regex .= $optional ? "(?:$format)?" : $format |
|
55 |
+ if $format && !$node->[0]->partial; |
|
55 | 56 |
push @parts, $regex if $verbose; |
56 | 57 |
|
57 | 58 |
say encode('UTF-8', join(' ', @parts)); |
... | ... |
@@ -76,6 +77,8 @@ sub _walk { |
76 | 77 |
|
77 | 78 |
1; |
78 | 79 |
|
80 |
+=encoding utf8 |
|
81 |
+ |
|
79 | 82 |
=head1 NAME |
80 | 83 |
|
81 | 84 |
Mojolicious::Command::routes - Routes command |
... | ... |
@@ -8,7 +8,7 @@ use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case); |
8 | 8 |
use Mojo::Home; |
9 | 9 |
|
10 | 10 |
has description => "Run unit tests.\n"; |
11 |
-has usage => <<"EOF"; |
|
11 |
+has usage => <<EOF; |
|
12 | 12 |
usage: $0 test [OPTIONS] [TESTS] |
13 | 13 |
|
14 | 14 |
These options are available: |
... | ... |
@@ -42,6 +42,8 @@ sub run { |
42 | 42 |
|
43 | 43 |
1; |
44 | 44 |
|
45 |
+=encoding utf8 |
|
46 |
+ |
|
45 | 47 |
=head1 NAME |
46 | 48 |
|
47 | 49 |
Mojolicious::Command::test - Test command |
... | ... |
@@ -17,7 +17,7 @@ sub run { |
17 | 17 |
my $tls |
18 | 18 |
= Mojo::IOLoop::Server::TLS ? $IO::Socket::SSL::VERSION : 'not installed'; |
19 | 19 |
|
20 |
- print <<"EOF"; |
|
20 |
+ print <<EOF; |
|
21 | 21 |
CORE |
22 | 22 |
Perl ($^V, $^O) |
23 | 23 |
Mojolicious ($Mojolicious::VERSION, $Mojolicious::CODENAME) |
... | ... |
@@ -31,7 +31,8 @@ EOF |
31 | 31 |
|
32 | 32 |
# Check latest version on CPAN |
33 | 33 |
my $latest = eval { |
34 |
- my $ua = Mojo::UserAgent->new(max_redirects => 10)->detect_proxy; |
|
34 |
+ my $ua = Mojo::UserAgent->new(max_redirects => 10); |
|
35 |
+ $ua->proxy->detect; |
|
35 | 36 |
$ua->get('api.metacpan.org/v0/release/Mojolicious')->res->json->{version}; |
36 | 37 |
}; |
37 | 38 |
|
... | ... |
@@ -46,6 +47,8 @@ EOF |
46 | 47 |
|
47 | 48 |
1; |
48 | 49 |
|
50 |
+=encoding utf8 |
|
51 |
+ |
|
49 | 52 |
=head1 NAME |
50 | 53 |
|
51 | 54 |
Mojolicious::Command::version - Version command |
... | ... |
@@ -5,18 +5,18 @@ use Getopt::Long 'GetOptions'; |
5 | 5 |
use List::Util 'max'; |
6 | 6 |
use Mojo::Server; |
7 | 7 |
|
8 |
-has hint => <<"EOF"; |
|
8 |
+has hint => <<EOF; |
|
9 | 9 |
|
10 | 10 |
These options are available for all commands: |
11 | 11 |
-h, --help Get more information on a specific command. |
12 | 12 |
--home <path> Path to your applications home directory, defaults to |
13 | 13 |
the value of MOJO_HOME or auto detection. |
14 |
- -m, --mode <name> Run mode of your application, defaults to the value of |
|
15 |
- MOJO_MODE/PLACK_ENV or "development". |
|
14 |
+ -m, --mode <name> Operating mode for your application, defaults to the |
|
15 |
+ value of MOJO_MODE/PLACK_ENV or "development". |
|
16 | 16 |
|
17 | 17 |
See '$0 help COMMAND' for more information on a specific command. |
18 | 18 |
EOF |
19 |
-has message => <<"EOF"; |
|
19 |
+has message => <<EOF; |
|
20 | 20 |
usage: $0 COMMAND [OPTIONS] |
21 | 21 |
|
22 | 22 |
Tip: CGI and PSGI environments can be automatically detected very often and |
... | ... |
@@ -117,6 +117,8 @@ sub _command { |
117 | 117 |
|
118 | 118 |
1; |
119 | 119 |
|
120 |
+=encoding utf8 |
|
121 |
+ |
|
120 | 122 |
=head1 NAME |
121 | 123 |
|
122 | 124 |
Mojolicious::Commands - Command line interface |
... | ... |
@@ -39,6 +39,8 @@ sub AUTOLOAD { |
39 | 39 |
|
40 | 40 |
sub DESTROY { } |
41 | 41 |
|
42 |
+sub continue { $_[0]->app->routes->continue($_[0]) } |
|
43 |
+ |
|
42 | 44 |
sub cookie { |
43 | 45 |
my ($self, $name) = (shift, shift); |
44 | 46 |
|
... | ... |
@@ -374,15 +376,13 @@ sub stash { |
374 | 376 |
return $self; |
375 | 377 |
} |
376 | 378 |
|
377 |
-sub ua { shift->app->ua } |
|
378 |
- |
|
379 | 379 |
sub url_for { |
380 | 380 |
my $self = shift; |
381 | 381 |
my $target = shift; $target = defined $target ? $target : ''; |
382 | 382 |
|
383 | 383 |
# Absolute URL |
384 | 384 |
return $target if Scalar::Util::blessed $target && $target->isa('Mojo::URL'); |
385 |
- return Mojo::URL->new($target) if $target =~ m!^\w+://!; |
|
385 |
+ return Mojo::URL->new($target) if $target =~ m!^(?:[^:/?#]+:|//)!; |
|
386 | 386 |
|
387 | 387 |
# Base |
388 | 388 |
my $url = Mojo::URL->new; |
... | ... |
@@ -404,12 +404,6 @@ sub url_for { |
404 | 404 |
else { |
405 | 405 |
my ($generated, $ws) = $self->match->path_for($target, @_); |
406 | 406 |
$path->parse($generated) if $generated; |
407 |
- |
|
408 |
- # Fix trailing slash |
|
409 |
- $path->trailing_slash(1) |
|
410 |
- if (!$target || $target eq 'current') && $req->url->path->trailing_slash; |
|
411 |
- |
|
412 |
- # Fix scheme for WebSockets |
|
413 | 407 |
$base->scheme($base->protocol eq 'https' ? 'wss' : 'ws') if $ws; |
414 | 408 |
} |
415 | 409 |
|
... | ... |
@@ -421,6 +415,12 @@ sub url_for { |
421 | 415 |
return $url; |
422 | 416 |
} |
423 | 417 |
|
418 |
+sub validation { |
|
419 |
+ my $self = shift; |
|
420 |
+ return $self->stash->{'mojo.validation'} |
|
421 |
+ ||= $self->app->validator->validation->input($self->req->params->to_hash); |
|
422 |
+} |
|
423 |
+ |
|
424 | 424 |
sub write { |
425 | 425 |
my ($self, $chunk, $cb) = @_; |
426 | 426 |
($cb, $chunk) = ($chunk, undef) if ref $chunk eq 'CODE'; |
... | ... |
@@ -449,7 +449,7 @@ sub _fallbacks { |
449 | 449 |
# Inline template |
450 | 450 |
my $stash = $self->stash; |
451 | 451 |
return undef unless $stash->{format} eq 'html'; |
452 |
- delete $stash->{$_} for qw(extends layout); |
|
452 |
+ delete @$stash{qw(extends layout)}; |
|
453 | 453 |
return $self->render_maybe(%$options, inline => $inline, handler => 'ep'); |
454 | 454 |
} |
455 | 455 |
|
... | ... |
@@ -478,8 +478,8 @@ Mojolicious::Controller - Controller base class |
478 | 478 |
=head1 DESCRIPTION |
479 | 479 |
|
480 | 480 |
L<Mojolicious::Controller> is the base class for your L<Mojolicious> |
481 |
-controllers. It is also the default controller class for L<Mojolicious> |
|
482 |
-unless you set C<controller_class> in your application. |
|
481 |
+controllers. It is also the default controller class unless you set |
|
482 |
+L<Mojolicious/"controller_class">. |
|
483 | 483 |
|
484 | 484 |
=head1 ATTRIBUTES |
485 | 485 |
|
... | ... |
@@ -497,6 +497,9 @@ defaults to a L<Mojolicious> object. |
497 | 497 |
# Use application logger |
498 | 498 |
$c->app->log->debug('Hello Mojo!'); |
499 | 499 |
|
500 |
+ # Generate path |
|
501 |
+ my $path = $c->app->home->rel_file('templates/foo/bar.html.ep'); |
|
502 |
+ |
|
500 | 503 |
=head2 match |
501 | 504 |
|
502 | 505 |
my $m = $c->match; |
... | ... |
@@ -507,6 +510,7 @@ L<Mojolicious::Routes::Match> object. |
507 | 510 |
|
508 | 511 |
# Introspect |
509 | 512 |
my $foo = $c->match->endpoint->pattern->defaults->{foo}; |
513 |
+ my $bar = $c->match->stack->[-1]{bar}; |
|
510 | 514 |
|
511 | 515 |
=head2 tx |
512 | 516 |
|
... | ... |
@@ -514,16 +518,32 @@ L<Mojolicious::Routes::Match> object. |
514 | 518 |
$c = $c->tx(Mojo::Transaction::HTTP->new); |
515 | 519 |
|
516 | 520 |
The transaction that is currently being processed, usually a |
517 |
-L<Mojo::Transaction::HTTP> or L<Mojo::Transaction::WebSocket> object. |
|
521 |
+L<Mojo::Transaction::HTTP> or L<Mojo::Transaction::WebSocket> object. Note |
|
522 |
+that this reference is usually weakened, so the object needs to be referenced |
|
523 |
+elsewhere as well when you're performing non-blocking operations and the |
|
524 |
+underlying connection might get closed early. |
|
518 | 525 |
|
519 | 526 |
# Check peer information |
520 | 527 |
my $address = $c->tx->remote_address; |
528 |
+ my $port = $c->tx->remote_port; |
|
529 |
+ |
|
530 |
+ # Perform non-blocking operation without knowing the connection status |
|
531 |
+ my $tx = $c->tx; |
|
532 |
+ Mojo::IOLoop->timer(2 => sub { |
|
533 |
+ $c->app->log->debug($tx->is_finished ? 'Finished.' : 'In progress.'); |
|
534 |
+ }); |
|
521 | 535 |
|
522 | 536 |
=head1 METHODS |
523 | 537 |
|
524 | 538 |
L<Mojolicious::Controller> inherits all methods from L<Mojo::Base> and |
525 | 539 |
implements the following new ones. |
526 | 540 |
|
541 |
+=head2 continue |
|
542 |
+ |
|
543 |
+ $c->continue; |
|
544 |
+ |
|
545 |
+Continue dispatch chain. |
|
546 |
+ |
|
527 | 547 |
=head2 cookie |
528 | 548 |
|
529 | 549 |
my $value = $c->cookie('foo'); |
... | ... |
@@ -551,7 +571,8 @@ Close WebSocket connection or long poll stream gracefully. |
551 | 571 |
$c = $c->flash({foo => 'bar'}); |
552 | 572 |
$c = $c->flash(foo => 'bar'); |
553 | 573 |
|
554 |
-Data storage persistent only for the next request, stored in the C<session>. |
|
574 |
+Data storage persistent only for the next request, stored in the |
|
575 |
+L</"session">. |
|
555 | 576 |
|
556 | 577 |
# Show message after redirect |
557 | 578 |
$c->flash(message => 'User created successfully!'); |
... | ... |
@@ -561,8 +582,10 @@ Data storage persistent only for the next request, stored in the C<session>. |
561 | 582 |
|
562 | 583 |
my $cb = $c->on(finish => sub {...}); |
563 | 584 |
|
564 |
-Subscribe to events of C<tx>, which is usually a L<Mojo::Transaction::HTTP> or |
|
565 |
-L<Mojo::Transaction::WebSocket> object. |
|
585 |
+Subscribe to events of L</"tx">, which is usually a L<Mojo::Transaction::HTTP> |
|
586 |
+or L<Mojo::Transaction::WebSocket> object. Note that this method will |
|
587 |
+automatically respond to WebSocket handshake requests with a C<101> response |
|
588 |
+status. |
|
566 | 589 |
|
567 | 590 |
# Do something after the transaction has been finished |
568 | 591 |
$c->on(finish => sub { |
... | ... |
@@ -598,10 +621,12 @@ L<Mojo::Transaction::WebSocket> object. |
598 | 621 |
$c = $c->param(foo => 'ba;r'); |
599 | 622 |
$c = $c->param(foo => qw(ba;r ba;z)); |
600 | 623 |
|
601 |
-Access GET/POST parameters, file uploads and route placeholder values that are |
|
602 |
-not reserved stash values. Note that this method is context sensitive in some |
|
603 |
-cases and therefore needs to be used with care, there can always be multiple |
|
604 |
-values, which might have unexpected consequences. |
|
624 |
+Access route placeholder values that are not reserved stash values, file |
|
625 |
+uploads and GET/POST parameters, in that order. Note that this method is |
|
626 |
+context sensitive in some cases and therefore needs to be used with care, |
|
627 |
+there can always be multiple values, which might have unexpected consequences. |
|
628 |
+Parts of the request body need to be loaded into memory to parse POST |
|
629 |
+parameters, so you have to make sure it is not excessively large. |
|
605 | 630 |
|
606 | 631 |
# List context is ambiguous and should be avoided |
607 | 632 |
my $hash = {foo => $self->param('foo')}; |
... | ... |
@@ -625,12 +650,12 @@ For more control you can also access request information directly. |
625 | 650 |
|
626 | 651 |
=head2 redirect_to |
627 | 652 |
|
628 |
- $c = $c->redirect_to('named'); |
|
629 | 653 |
$c = $c->redirect_to('named', foo => 'bar'); |
630 |
- $c = $c->redirect_to('/path'); |
|
631 |
- $c = $c->redirect_to('http://127.0.0.1/foo/bar'); |
|
654 |
+ $c = $c->redirect_to('named', {foo => 'bar'}); |
|
655 |
+ $c = $c->redirect_to('/perldoc'); |
|
656 |
+ $c = $c->redirect_to('http://mojolicio.us/perldoc'); |
|
632 | 657 |
|
633 |
-Prepare a C<302> redirect response, takes the same arguments as C<url_for>. |
|
658 |
+Prepare a C<302> redirect response, takes the same arguments as L</"url_for">. |
|
634 | 659 |
|
635 | 660 |
# Conditional redirect |
636 | 661 |
return $c->redirect_to('login') unless $c->session('user'); |
... | ... |
@@ -641,21 +666,21 @@ Prepare a C<302> redirect response, takes the same arguments as C<url_for>. |
641 | 666 |
|
642 | 667 |
=head2 render |
643 | 668 |
|
644 |
- my $success = $c->render; |
|
645 |
- my $success = $c->render(controller => 'foo', action => 'bar'); |
|
646 |
- my $success = $c->render(template => 'foo/index'); |
|
647 |
- my $success = $c->render(template => 'index', format => 'html'); |
|
648 |
- my $success = $c->render(data => $bytes); |
|
649 |
- my $success = $c->render(text => 'Hello!'); |
|
650 |
- my $success = $c->render(json => {foo => 'bar'}); |
|
651 |
- my $success = $c->render(handler => 'something'); |
|
652 |
- my $success = $c->render('foo/index'); |
|
669 |
+ my $bool = $c->render; |
|
670 |
+ my $bool = $c->render(controller => 'foo', action => 'bar'); |
|
671 |
+ my $bool = $c->render(template => 'foo/index'); |
|
672 |
+ my $bool = $c->render(template => 'index', format => 'html'); |
|
673 |
+ my $bool = $c->render(data => $bytes); |
|
674 |
+ my $bool = $c->render(text => 'Hello!'); |
|
675 |
+ my $bool = $c->render(json => {foo => 'bar'}); |
|
676 |
+ my $bool = $c->render(handler => 'something'); |
|
677 |
+ my $bool = $c->render('foo/index'); |
|
653 | 678 |
my $output = $c->render('foo/index', partial => 1); |
654 | 679 |
|
655 |
-Render content using L<Mojolicious::Renderer/"render"> and emit |
|
656 |
-C<after_render> hook unless the result is C<partial>. If no template is |
|
657 |
-provided a default one based on controller and action or route name will be |
|
658 |
-generated, all additional values get merged into the C<stash>. |
|
680 |
+Render content using L<Mojolicious::Renderer/"render"> and emit hook |
|
681 |
+L<Mojolicious/"after_render"> unless the result is C<partial>. If no template |
|
682 |
+is provided a default one based on controller and action or route name will be |
|
683 |
+generated, all additional values get merged into the L</"stash">. |
|
659 | 684 |
|
660 | 685 |
=head2 render_exception |
661 | 686 |
|
... | ... |
@@ -665,7 +690,7 @@ generated, all additional values get merged into the C<stash>. |
665 | 690 |
Render the exception template C<exception.$mode.$format.*> or |
666 | 691 |
C<exception.$format.*> and set the response status code to C<500>. Also sets |
667 | 692 |
the stash values C<exception> to a L<Mojo::Exception> object and C<snapshot> |
668 |
-to a copy of the C<stash> for use in the templates. |
|
693 |
+to a copy of the L</"stash"> for use in the templates. |
|
669 | 694 |
|
670 | 695 |
=head2 render_later |
671 | 696 |
|
... | ... |
@@ -682,12 +707,12 @@ automatic rendering would result in a response. |
682 | 707 |
|
683 | 708 |
=head2 render_maybe |
684 | 709 |
|
685 |
- my $success = $c->render_maybe; |
|
686 |
- my $success = $c->render_maybe(controller => 'foo', action => 'bar'); |
|
687 |
- my $success = $c->render_maybe('foo/index', format => 'html'); |
|
710 |
+ my $bool = $c->render_maybe; |
|
711 |
+ my $bool = $c->render_maybe(controller => 'foo', action => 'bar'); |
|
712 |
+ my $bool = $c->render_maybe('foo/index', format => 'html'); |
|
688 | 713 |
|
689 |
-Try to render content but do not call C<render_not_found> if no response could |
|
690 |
-be generated, takes the same arguments as C<render>. |
|
714 |
+Try to render content but do not call L</"render_not_found"> if no response |
|
715 |
+could be generated, takes the same arguments as L</"render">. |
|
691 | 716 |
|
692 | 717 |
# Render template "index_local" only if it exists |
693 | 718 |
$self->render_maybe('index_local') or $self->render('index'); |
... | ... |
@@ -701,8 +726,8 @@ C<not_found.$format.*> and set the response status code to C<404>. |
701 | 726 |
|
702 | 727 |
=head2 render_static |
703 | 728 |
|
704 |
- my $success = $c->render_static('images/logo.png'); |
|
705 |
- my $success = $c->render_static('../lib/MyApp.pm'); |
|
729 |
+ my $bool = $c->render_static('images/logo.png'); |
|
730 |
+ my $bool = $c->render_static('../lib/MyApp.pm'); |
|
706 | 731 |
|
707 | 732 |
Render a static file using L<Mojolicious::Static/"serve">, usually from the |
708 | 733 |
C<public> directories or C<DATA> sections of your application. Note that this |
... | ... |
@@ -713,8 +738,8 @@ method does not protect from traversing to parent directories. |
713 | 738 |
$c = $c->rendered; |
714 | 739 |
$c = $c->rendered(302); |
715 | 740 |
|
716 |
-Finalize response and emit C<after_dispatch> hook, defaults to using a C<200> |
|
717 |
-response code. |
|
741 |
+Finalize response and emit hook L<Mojolicious/"after_dispatch">, defaults to |
|
742 |
+using a C<200> response code. |
|
718 | 743 |
|
719 | 744 |
=head2 req |
720 | 745 |
|
... | ... |
@@ -730,8 +755,11 @@ Get L<Mojo::Message::Request> object from L<Mojo::Transaction/"req">. |
730 | 755 |
my $userinfo = $c->req->url->to_abs->userinfo; |
731 | 756 |
my $host = $c->req->url->to_abs->host; |
732 | 757 |
my $agent = $c->req->headers->user_agent; |
733 |
- my $body = $c->req->body; |
|
758 |
+ my $bytes = $c->req->body; |
|
759 |
+ my $str = $c->req->text; |
|
760 |
+ my $hash = $c->req->json; |
|
734 | 761 |
my $foo = $c->req->json('/23/foo'); |
762 |
+ my $dom = $c->req->dom; |
|
735 | 763 |
my $bar = $c->req->dom('div.bar')->first->text; |
736 | 764 |
|
737 | 765 |
=head2 res |
... | ... |
@@ -773,12 +801,13 @@ is set to the value C<XMLHttpRequest>. |
773 | 801 |
$c = $c->send({text => $bytes}); |
774 | 802 |
$c = $c->send({json => {test => [1, 2, 3]}}); |
775 | 803 |
$c = $c->send([$fin, $rsv1, $rsv2, $rsv3, $op, $bytes]); |
776 |
- $c = $c->send(Mojo::ByteStream->new($chars)); |
|
777 | 804 |
$c = $c->send($chars); |
778 | 805 |
$c = $c->send($chars => sub {...}); |
779 | 806 |
|
780 | 807 |
Send message or frame non-blocking via WebSocket, the optional drain callback |
781 |
-will be invoked once all data has been written. |
|
808 |
+will be invoked once all data has been written. Note that this method will |
|
809 |
+automatically respond to WebSocket handshake requests with a C<101> response |
|
810 |
+status. |
|
782 | 811 |
|
783 | 812 |
# Send "Text" message |
784 | 813 |
$c->send('I ♥ Mojolicious!'); |
... | ... |
@@ -793,6 +822,12 @@ will be invoked once all data has been written. |
793 | 822 |
# Send "Ping" frame |
794 | 823 |
$c->send([1, 0, 0, 0, 9, 'Hello World!']); |
795 | 824 |
|
825 |
+ # Make sure previous message has been written before continuing |
|
826 |
+ $c->send('First message!' => sub { |
|
827 |
+ my $c = shift; |
|
828 |
+ $c->send('Second message!'); |
|
829 |
+ }); |
|
830 |
+ |
|
796 | 831 |
For mostly idle WebSockets you might also want to increase the inactivity |
797 | 832 |
timeout, which usually defaults to C<15> seconds. |
798 | 833 |
|
... | ... |
@@ -843,7 +878,7 @@ discarded. |
843 | 878 |
$c = $c->stash(foo => 'bar'); |
844 | 879 |
|
845 | 880 |
Non persistent data storage and exchange, application wide default values can |
846 |
-be set with L<Mojolicious/"defaults">. Many stash values have a special |
|
881 |
+be set with L<Mojolicious/"defaults">. Some stash values have a special |
|
847 | 882 |
meaning and are reserved, the full list is currently C<action>, C<app>, C<cb>, |
848 | 883 |
C<controller>, C<data>, C<extends>, C<format>, C<handler>, C<json>, C<layout>, |
849 | 884 |
C<namespace>, C<partial>, C<path>, C<status>, C<template> and C<text>. Note |
... | ... |
@@ -852,38 +887,6 @@ that all stash values with a C<mojo.*> prefix are reserved for internal use. |
852 | 887 |
# Remove value |
853 | 888 |
my $foo = delete $c->stash->{foo}; |
854 | 889 |
|
855 |
-=head2 ua |
|
856 |
- |
|
857 |
- my $ua = $c->ua; |
|
858 |
- |
|
859 |
-Get L<Mojo::UserAgent> object from L<Mojo/"ua">. |
|
860 |
- |
|
861 |
- # Longer version |
|
862 |
- my $ua = $c->app->ua; |
|
863 |
- |
|
864 |
- # Blocking |
|
865 |
- my $tx = $c->ua->get('http://example.com'); |
|
866 |
- my $tx = $c->ua->post('example.com/login' => form => {user => 'mojo'}); |
|
867 |
- |
|
868 |
- # Non-blocking |
|
869 |
- $c->ua->get('http://example.com' => sub { |
|
870 |
- my ($ua, $tx) = @_; |
|
871 |
- $c->render(data => $tx->res->body); |
|
872 |
- }); |
|
873 |
- |
|
874 |
- # Parallel non-blocking |
|
875 |
- my $delay = Mojo::IOLoop->delay(sub { |
|
876 |
- my ($delay, @titles) = @_; |
|
877 |
- $c->render(json => \@titles); |
|
878 |
- }); |
|
879 |
- for my $url ('http://mojolicio.us', 'https://metacpan.org') { |
|
880 |
- my $end = $delay->begin(0); |
|
881 |
- $c->ua->get($url => sub { |
|
882 |
- my ($ua, $tx) = @_; |
|
883 |
- $end->($tx->res->dom->html->head->title->text); |
|
884 |
- }); |
|
885 |
- } |
|
886 |
- |
|
887 | 890 |
=head2 url_for |
888 | 891 |
|
889 | 892 |
my $url = $c->url_for; |
... | ... |
@@ -892,10 +895,15 @@ Get L<Mojo::UserAgent> object from L<Mojo/"ua">. |
892 | 895 |
my $url = $c->url_for('test', name => 'sebastian'); |
893 | 896 |
my $url = $c->url_for('test', {name => 'sebastian'}); |
894 | 897 |
my $url = $c->url_for('/perldoc'); |
898 |
+ my $url = $c->url_for('//mojolicio.us/perldoc'); |
|
895 | 899 |
my $url = $c->url_for('http://mojolicio.us/perldoc'); |
900 |
+ my $url = $c->url_for('mailto:sri@example.com'); |
|
896 | 901 |
|
897 | 902 |
Generate a portable L<Mojo::URL> object with base for a route, path or URL. |
898 | 903 |
|
904 |
+ # "http://127.0.0.1:3000/perldoc" if application has been started with Morbo |
|
905 |
+ $c->url_for('/perldoc')->to_abs; |
|
906 |
+ |
|
899 | 907 |
# "/perldoc?foo=bar" if application is deployed under "/" |
900 | 908 |
$c->url_for('/perldoc')->query(foo => 'bar'); |
901 | 909 |
|
... | ... |
@@ -908,6 +916,19 @@ to inherit query parameters from the current request. |
908 | 916 |
# "/list?q=mojo&page=2" if current request was for "/list?q=mojo&page=1" |
909 | 917 |
$c->url_with->query([page => 2]); |
910 | 918 |
|
919 |
+=head2 validation |
|
920 |
+ |
|
921 |
+ my $validation = $c->validation; |
|
922 |
+ |
|
923 |
+Get L<Mojolicious::Validator::Validation> object for current request to |
|
924 |
+validate GET/POST parameters. Parts of the request body need to be loaded into |
|
925 |
+memory to parse POST parameters, so you have to make sure it is not |
|
926 |
+excessively large. |
|
927 |
+ |
|
928 |
+ my $validation = $c->validation; |
|
929 |
+ $validation->required('title')->size(3, 50); |
|
930 |
+ my $title = $validation->param('title'); |
|
931 |
+ |
|
911 | 932 |
=head2 write |
912 | 933 |
|
913 | 934 |
$c = $c->write; |
... | ... |
@@ -959,7 +980,7 @@ optional drain callback will be invoked once all data has been written. |
959 | 980 |
}); |
960 | 981 |
}); |
961 | 982 |
|
962 |
-You can call C<finish> at any time to end the stream. |
|
983 |
+You can call L</"finish"> at any time to end the stream. |
|
963 | 984 |
|
964 | 985 |
2 |
965 | 986 |
He |
... | ... |
@@ -1,4 +1,6 @@ |
1 | 1 |
|
2 |
+=encoding utf8 |
|
3 |
+ |
|
2 | 4 |
=head1 NAME |
3 | 5 |
|
4 | 6 |
Mojolicious::Guides - Mojolicious guide to the galaxy |
... | ... |
@@ -20,12 +22,6 @@ available in many formats. Both are excellent introductions to the language. |
20 | 22 |
For more books and documentation, check out |
21 | 23 |
L<learn.perl.org|http://learn.perl.org/>. |
22 | 24 |
|
23 |
-=head1 SCREENCASTS |
|
24 |
- |
|
25 |
-Before starting with the tutorial below, you should take a look at the |
|
26 |
-wonderful L<Mojocasts|http://mojocasts.com/e1>, they will give you a general |
|
27 |
-overview of what L<Mojolicious> is all about. |
|
28 |
- |
|
29 | 25 |
=head1 TUTORIAL |
30 | 26 |
|
31 | 27 |
=over 2 |
... | ... |
@@ -122,7 +118,20 @@ Test driven development toolkit for web applications. |
122 | 118 |
|
123 | 119 |
=item L<ojo> |
124 | 120 |
|
125 |
-Fun oneliners using everything above. |
|
121 |
+Fun one-liners using everything above. |
|
122 |
+ |
|
123 |
+=back |
|
124 |
+ |
|
125 |
+=head1 SPIN-OFFS |
|
126 |
+ |
|
127 |
+These modules are not part of the L<Mojolicious> distribution, but have been |
|
128 |
+designed to be used with it and are being developed under the same umbrella. |
|
129 |
+ |
|
130 |
+=over 2 |
|
131 |
+ |
|
132 |
+=item L<Mango> |
|
133 |
+ |
|
134 |
+Pure-Perl non-blocking I/O MongoDB driver. |
|
126 | 135 |
|
127 | 136 |
=back |
128 | 137 |
|
... | ... |
@@ -1,4 +1,6 @@ |
1 | 1 |
|
2 |
+=encoding utf8 |
|
3 |
+ |
|
2 | 4 |
=head1 NAME |
3 | 5 |
|
4 | 6 |
Mojolicious::Guides::Contributing - Contributing to Mojolicious |
... | ... |
@@ -100,8 +102,8 @@ All components should be reusable in other projects, and in a UNIXish way only |
100 | 102 |
loosely coupled. |
101 | 103 |
|
102 | 104 |
Especially for people new to Perl it should be as easy as possible to install |
103 |
-Mojolicious and get started. Writing web applications can be one of the most |
|
104 |
-fun ways to learn a language! |
|
105 |
+L<Mojolicious> and get started. Writing web applications can be one of the |
|
106 |
+most fun ways to learn a language! |
|
105 | 107 |
|
106 | 108 |
For developers of other web frameworks, it should be possible to reuse all the |
107 | 109 |
infrastructure and just consider the higher levels of the L<Mojolicious> |
... | ... |
@@ -164,7 +166,7 @@ feature branches for actual development. |
164 | 166 |
Code has to be run through L<Perl::Tidy> with the included C<.perltidyrc>, and |
165 | 167 |
everything should look like it was written by a single person. |
166 | 168 |
|
167 |
-Methods and functions should be as short as possible, no spaghetti code. |
|
169 |
+Functions and methods should be as short as possible, no spaghetti code. |
|
168 | 170 |
|
169 | 171 |
Comments should be correctly capitalized, and funny if possible, punctuation |
170 | 172 |
is optional if it doesn't increase readability. |
... | ... |
@@ -1,4 +1,6 @@ |
1 | 1 |
|
2 |
+=encoding utf8 |
|
3 |
+ |
|
2 | 4 |
=head1 NAME |
3 | 5 |
|
4 | 6 |
Mojolicious::Guides::Cookbook - Cookbook |
... | ... |
@@ -32,7 +34,8 @@ works on. |
32 | 34 |
|
33 | 35 |
Another huge advantage is that it supports TLS and WebSockets out of the box, |
34 | 36 |
a development certificate for testing purposes is built right in, so it just |
35 |
-works. |
|
37 |
+works, but you can specify all listen locations supported by |
|
38 |
+L<Mojo::Server::Daemon/"listen">. |
|
36 | 39 |
|
37 | 40 |
$ ./script/myapp daemon -l https://*:3000 |
38 | 41 |
Server available at https://127.0.0.1:3000. |
... | ... |
@@ -51,6 +54,20 @@ concurrent connections each worker is allowed to handle. |
51 | 54 |
$ ./script/myapp prefork -m production -w 10 -c 1 |
52 | 55 |
Server available at http://127.0.0.1:3000. |
53 | 56 |
|
57 |
+Your application is preloaded in the manager process during startup, to run |
|
58 |
+code whenever a new worker process has been forked you can use L<Mojo::IOLoop> |
|
59 |
+timers. |
|
60 |
+ |
|
61 |
+ use Mojolicious::Lite; |
|
62 |
+ |
|
63 |
+ Mojo::IOLoop->timer(0 => sub { |
|
64 |
+ app->log->info("Worker $$ star...ALL GLORY TO THE HYPNOTOAD!"); |
|
65 |
+ }); |
|
66 |
+ |
|
67 |
+ get '/' => {text => 'Hello Wor...ALL GLORY TO THE HYPNOTOAD!'}; |
|
68 |
+ |
|
69 |
+ app->start; |
|
70 |
+ |
|
54 | 71 |
=head2 Morbo |
55 | 72 |
|
56 | 73 |
After reading the L<Mojolicious::Lite> tutorial, you should already be |
... | ... |
@@ -85,8 +102,9 @@ environments out of the box. |
85 | 102 |
$ hypnotoad script/myapp |
86 | 103 |
Server available at http://127.0.0.1:8080. |
87 | 104 |
|
88 |
-You can tweak many configuration settings right from within your application, |
|
89 |
-for a full list see L<Mojo::Server::Hypnotoad/"SETTINGS">. |
|
105 |
+You can tweak many configuration settings right from within your application |
|
106 |
+with L<Mojo/"config">, for a full list see |
|
107 |
+L<Mojo::Server::Hypnotoad/"SETTINGS">. |
|
90 | 108 |
|
91 | 109 |
use Mojolicious::Lite; |
92 | 110 |
|
... | ... |
@@ -108,9 +126,9 @@ L<Mojolicious::Plugin::JSONConfig> configuration file. |
108 | 126 |
}; |
109 | 127 |
|
110 | 128 |
But one of its biggest advantages is the support for effortless zero downtime |
111 |
-software upgrades. That means you can upgrade L<Mojolicious>, Perl or even |
|
112 |
-system libraries at runtime without ever stopping the server or losing a |
|
113 |
-single incoming connection, just by running the command above again. |
|
129 |
+software upgrades (hot deployment). That means you can upgrade L<Mojolicious>, |
|
130 |
+Perl or even system libraries at runtime without ever stopping the server or |
|
131 |
+losing a single incoming connection, just by running the command above again. |
|
114 | 132 |
|
115 | 133 |
$ hypnotoad script/myapp |
116 | 134 |
Starting hot deployment for Hypnotoad server 31841. |
... | ... |
@@ -122,19 +140,25 @@ C<X-Forwarded-For> and C<X-Forwarded-HTTPS> headers. |
122 | 140 |
# myapp.conf |
123 | 141 |
{hypnotoad => {proxy => 1}}; |
124 | 142 |
|
125 |
-Your application is preloaded in the manager process during startup, to run |
|
126 |
-code whenever a new worker process has been forked you can use L<Mojo::IOLoop> |
|
127 |
-timers. |
|
143 |
+=head2 Zero downtime software upgrades |
|
128 | 144 |
|
129 |
- use Mojolicious::Lite; |
|
145 |
+Hypnotoad makes zero downtime software upgrades (hot deployment) very simple, |
|
146 |
+as you can see above, but on modern operating systems that support the |
|
147 |
+C<SO_REUSEPORT> socket option, there is also another method available that |
|
148 |
+works with all built-in web servers. |
|
130 | 149 |
|
131 |
- Mojo::IOLoop->timer(0 => sub { |
|
132 |
- app->log->info("Worker $$ star...ALL GLORY TO THE HYPNOTOAD!"); |
|
133 |
- }); |
|
150 |
+ $ ./script/myapp prefork -P /tmp/first.pid -l http://*:8080?reuse=1 |
|
151 |
+ Server available at http://127.0.0.1:8080. |
|
134 | 152 |
|
135 |
- get '/' => {text => 'Hello Wor...ALL GLORY TO THE HYPNOTOAD!'}; |
|
153 |
+All you have to do is start a second web server listening to the same port and |
|
154 |
+stop the first web server gracefully afterwards. |
|
136 | 155 |
|
137 |
- app->start; |
|
156 |
+ $ ./script/myapp prefork -P /tmp/second.pid -l http://*:8080?reuse=1 |
|
157 |
+ Server available at http://127.0.0.1:8080. |
|
158 |
+ $ kill -s TERM `cat /tmp/first.pid` |
|
159 |
+ |
|
160 |
+Just remember that both web servers need to be started with the C<reuse> |
|
161 |
+parameter. |
|
138 | 162 |
|
139 | 163 |
=head2 Nginx |
140 | 164 |
|
... | ... |
@@ -242,30 +266,37 @@ But you could even use middleware right in your application. |
242 | 266 |
Sometimes you might have to deploy your application in a blackbox environment |
243 | 267 |
where you can't just change the server configuration or behind a reverse proxy |
244 | 268 |
that passes along additional information with C<X-*> headers. In such cases |
245 |
-you can use a C<before_dispatch> hook to rewrite incoming requests. |
|
269 |
+you can use the hook L<Mojolicious/"before_dispatch"> to rewrite incoming |
|
270 |
+requests. |
|
246 | 271 |
|
247 | 272 |
# Change scheme if "X-Forwarded-Protocol" header is set to "https" |
248 | 273 |
app->hook(before_dispatch => sub { |
249 |
- my $self = shift; |
|
250 |
- $self->req->url->base->scheme('https') |
|
251 |
- if $self->req->headers->header('X-Forwarded-Protocol') eq 'https'; |
|
274 |
+ my $c = shift; |
|
275 |
+ $c->req->url->base->scheme('https') |
|
276 |
+ if $c->req->headers->header('X-Forwarded-Protocol') eq 'https'; |
|
252 | 277 |
}); |
253 | 278 |
|
254 | 279 |
Since reverse proxies generally don't pass along information about path |
255 | 280 |
prefixes your application might be deployed under, rewriting the base path of |
256 | 281 |
incoming requests is also quite common. |
257 | 282 |
|
258 |
- # Move first part from path to base path in production mode |
|
283 |
+ # Move first part and slash from path to base path in production mode |
|
259 | 284 |
app->hook(before_dispatch => sub { |
260 |
- my $self = shift; |
|
261 |
- push @{$self->req->url->base->path}, shift @{$self->req->url->path}; |
|
285 |
+ my $c = shift; |
|
286 |
+ push @{$c->req->url->base->path->trailing_slash(1)}, |
|
287 |
+ shift @{$c->req->url->path->leading_slash(0)}; |
|
262 | 288 |
}) if app->mode eq 'production'; |
263 | 289 |
|
290 |
+L<Mojo::URL> objects are very easy to manipulate, just make sure that the URL |
|
291 |
+(C<foo/bar?baz=yada>), which represents the routing destination, is always |
|
292 |
+relative to the base URL (C<http://example.com/myapp/>), which represents the |
|
293 |
+deployment location of your application. |
|
294 |
+ |
|
264 | 295 |
=head2 Application embedding |
265 | 296 |
|
266 | 297 |
From time to time you might want to reuse parts of L<Mojolicious> applications |
267 | 298 |
like configuration files, database connection or helpers for other scripts, |
268 |
-with this little mock server you can just embed them. |
|
299 |
+with this little L<Mojo::Server> based mock server you can just embed them. |
|
269 | 300 |
|
270 | 301 |
use Mojo::Server; |
271 | 302 |
|
... | ... |
@@ -280,8 +311,9 @@ with this little mock server you can just embed them. |
280 | 311 |
|
281 | 312 |
=head2 Web server embedding |
282 | 313 |
|
283 |
-You can also use the built-in web server to embed L<Mojolicious> applications |
|
284 |
-into alien environments like foreign event loops. |
|
314 |
+You can also use L<Mojo::IOLoop/"one_tick"> to embed the built-in web server |
|
315 |
+L<Mojo::Server::Daemon> into alien environments like foreign event loops that |
|
316 |
+for some reason can't just be integrated with a new reactor backend. |
|
285 | 317 |
|
286 | 318 |
use Mojolicious::Lite; |
287 | 319 |
use Mojo::IOLoop; |
... | ... |
@@ -316,37 +348,37 @@ latency backend web services. |
316 | 348 |
|
317 | 349 |
use Mojolicious::Lite; |
318 | 350 |
|
319 |
- # Search Twitter for "perl" |
|
351 |
+ # Search MetaCPAN for "mojolicious" |
|
320 | 352 |
get '/' => sub { |
321 | 353 |
my $self = shift; |
322 |
- $self->ua->get('http://search.twitter.com/search.json?q=perl' => sub { |
|
354 |
+ $self->ua->get('api.metacpan.org/v0/module/_search?q=mojolicious' => sub { |
|
323 | 355 |
my ($ua, $tx) = @_; |
324 |
- $self->render('twitter', results => $tx->res->json->{results}); |
|
356 |
+ $self->render('metacpan', hits => $tx->res->json->{hits}{hits}); |
|
325 | 357 |
}); |
326 | 358 |
}; |
327 | 359 |
|
328 | 360 |
app->start; |
329 | 361 |
__DATA__ |
330 | 362 |
|
331 |
- @@ twitter.html.ep |
|
363 |
+ @@ metacpan.html.ep |
|
332 | 364 |
<!DOCTYPE html> |
333 | 365 |
<html> |
334 |
- <head><title>Twitter results for "perl"</title></head> |
|
366 |
+ <head><title>MetaCPAN results for "mojolicious"</title></head> |
|
335 | 367 |
<body> |
336 |
- % for my $result (@$results) { |
|
337 |
- <p><%= $result->{text} %></p> |
|
368 |
+ % for my $hit (@$hits) { |
|
369 |
+ <p><%= $hit->{_source}{release} %></p> |
|
338 | 370 |
% } |
339 | 371 |
</body> |
340 | 372 |
</html> |
341 | 373 |
|
342 |
-Multiple events such as parallel requests can be easily synchronized with a |
|
343 |
-L<Mojo::IOLoop> delay. |
|
374 |
+Multiple events such as parallel requests can be easily synchronized with |
|
375 |
+L<Mojo::IOLoop/"delay">. |
|
344 | 376 |
|
345 | 377 |
use Mojolicious::Lite; |
346 | 378 |
use Mojo::IOLoop; |
347 | 379 |
use Mojo::URL; |
348 | 380 |
|
349 |
- # Search Twitter for "perl" and "python" |
|
381 |
+ # Search MetaCPAN for "mojo" and "mango" |
|
350 | 382 |
get '/' => sub { |
351 | 383 |
my $self = shift; |
352 | 384 |
|
... | ... |
@@ -356,17 +388,18 @@ L<Mojo::IOLoop> delay. |
356 | 388 |
# Parallel requests |
357 | 389 |
sub { |
358 | 390 |
my $delay = shift; |
359 |
- my $url = Mojo::URL->new('http://search.twitter.com/search.json'); |
|
360 |
- $self->ua->get($url->clone->query({q => 'perl'}) => $delay->begin); |
|
361 |
- $self->ua->get($url->clone->query({q => 'python'}) => $delay->begin); |
|
391 |
+ my $url = Mojo::URL->new('api.metacpan.org/v0/module/_search'); |
|
392 |
+ $url->query({sort => 'date:desc'}); |
|
393 |
+ $self->ua->get($url->clone->query({q => 'mojo'}) => $delay->begin); |
|
394 |
+ $self->ua->get($url->clone->query({q => 'mango'}) => $delay->begin); |
|
362 | 395 |
}, |
363 | 396 |
|
364 | 397 |
# Delayed rendering |
365 | 398 |
sub { |
366 |
- my ($delay, $perl, $python) = @_; |
|
399 |
+ my ($delay, $mojo, $mango) = @_; |
|
367 | 400 |
$self->render(json => { |
368 |
- perl => $perl->res->json('/results/0/text'), |
|
369 |
- python => $python->res->json('/results/0/text') |
|
401 |
+ mojo => $mojo->res->json('/hits/hits/0/_source/release'), |
|
402 |
+ mango => $mango->res->json('/hits/hits/0/_source/release') |
|
370 | 403 |
}); |
371 | 404 |
} |
372 | 405 |
); |
... | ... |
@@ -543,11 +576,11 @@ L<Test::Mojo> API to be used. |
543 | 576 |
|
544 | 577 |
=head2 EventSource web service |
545 | 578 |
|
546 |
-EventSource is a special form of long-polling where you can directly send DOM |
|
547 |
-events from servers to clients. It is uni-directional, that means you will |
|
548 |
-have to use Ajax requests for sending data from clients to servers, the |
|
549 |
-advantage however is low infrastructure requirements, since it reuses the HTTP |
|
550 |
-protocol for transport. |
|
579 |
+EventSource is a special form of long-polling where you can use |
|
580 |
+L<Mojolicious::Controller/"write"> to directly send DOM events from servers to |
|
581 |
+clients. It is uni-directional, that means you will have to use Ajax requests |
|
582 |
+for sending data from clients to servers, the advantage however is low |
|
583 |
+infrastructure requirements, since it reuses the HTTP protocol for transport. |
|
551 | 584 |
|
552 | 585 |
use Mojolicious::Lite; |
553 | 586 |
use Mojo::IOLoop; |
... | ... |
@@ -666,27 +699,7 @@ which can be combined to solve some of hardest problems in web development. |
666 | 699 |
|
667 | 700 |
Internally the L<Mojo::IOLoop> event loop can use multiple reactor backends, |
668 | 701 |
L<EV> for example will be automatically used if installed. Which in turn |
669 |
-allows other event loops like L<IO::Async> to just work. |
|
670 |
- |
|
671 |
- use Mojolicious::Lite; |
|
672 |
- use EV; |
|
673 |
- use IO::Async::Loop::EV; |
|
674 |
- use IO::Async::Timer::Absolute; |
|
675 |
- |
|
676 |
- my $loop = IO::Async::Loop::EV->new; |
|
677 |
- |
|
678 |
- # Wait 3 seconds before rendering a response |
|
679 |
- get '/' => sub { |
|
680 |
- my $self = shift; |
|
681 |
- $loop->add(IO::Async::Timer::Absolute->new( |
|
682 |
- time => time + 3, |
|
683 |
- on_expire => sub { $self->render(text => 'Delayed by 3 seconds!') } |
|
684 |
- )); |
|
685 |
- }; |
|
686 |
- |
|
687 |
- app->start; |
|
688 |
- |
|
689 |
-Same for L<AnyEvent>. |
|
702 |
+allows other event loops like L<AnyEvent> to just work. |
|
690 | 703 |
|
691 | 704 |
use Mojolicious::Lite; |
692 | 705 |
use EV; |
... | ... |
@@ -710,12 +723,12 @@ Who actually controls the event loop backend is not important. |
710 | 723 |
use EV; |
711 | 724 |
use AnyEvent; |
712 | 725 |
|
713 |
- # Search Twitter for "perl" |
|
726 |
+ # Search MetaCPAN for "mojolicious" |
|
714 | 727 |
my $cv = AE::cv; |
715 | 728 |
my $ua = Mojo::UserAgent->new; |
716 |
- $ua->get('http://search.twitter.com/search.json?q=perl' => sub { |
|
729 |
+ $ua->get('api.metacpan.org/v0/module/_search?q=mojolicious' => sub { |
|
717 | 730 |
my ($ua, $tx) = @_; |
718 |
- $cv->send($tx->res->json('/results/0/text')); |
|
731 |
+ $cv->send($tx->res->json('/hits/hits/0/_source/release')); |
|
719 | 732 |
}); |
720 | 733 |
say $cv->recv; |
721 | 734 |
|
... | ... |
@@ -745,8 +758,10 @@ When we say L<Mojolicious> is a web framework we actually mean it. |
745 | 758 |
=head2 Web scraping |
746 | 759 |
|
747 | 760 |
Scraping information from web sites has never been this much fun before. The |
748 |
-built-in HTML/XML parser L<Mojo::DOM> supports all CSS selectors that make |
|
749 |
-sense for a standalone parser. |
|
761 |
+built-in HTML/XML parser L<Mojo::DOM> is accessible through |
|
762 |
+L<Mojo::Message/"dom"> and supports all CSS selectors that make sense for a |
|
763 |
+standalone parser, it can be a very powerful tool especially for unit testing |
|
764 |
+web application. |
|
750 | 765 |
|
751 | 766 |
use Mojo::UserAgent; |
752 | 767 |
|
... | ... |
@@ -776,33 +791,26 @@ sense for a standalone parser. |
776 | 791 |
print $e->text_after(0) unless $e->next; |
777 | 792 |
} |
778 | 793 |
|
779 |
-Especially for unit testing your L<Mojolicious> applications this can be a |
|
780 |
-very powerful tool. |
|
794 |
+For a full list of available CSS selectors see L<Mojo::DOM::CSS/"SELECTORS">. |
|
781 | 795 |
|
782 | 796 |
=head2 JSON web services |
783 | 797 |
|
784 | 798 |
Most web services these days are based on the JSON data-interchange format. |
785 | 799 |
That's why L<Mojolicious> comes with the possibly fastest pure-Perl |
786 |
-implementation L<Mojo::JSON> built right in. |
|
800 |
+implementation L<Mojo::JSON> built right in, it is accessible through |
|
801 |
+L<Mojo::Message/"json">. |
|
787 | 802 |
|
788 | 803 |
use Mojo::UserAgent; |
789 |
- use Mojo::Util 'encode'; |
|
804 |
+ use Mojo::URL; |
|
790 | 805 |
|
791 | 806 |
# Fresh user agent |
792 | 807 |
my $ua = Mojo::UserAgent->new; |
793 | 808 |
|
794 |
- # Fetch the latest news about Mojolicious from Twitter |
|
795 |
- my $search = 'http://search.twitter.com/search.json?q=Mojolicious'; |
|
796 |
- for $tweet (@{$ua->get($search)->res->json->{results}}) { |
|
797 |
- |
|
798 |
- # Tweet text |
|
799 |
- my $text = $tweet->{text}; |
|
800 |
- |
|
801 |
- # Twitter user |
|
802 |
- my $user = $tweet->{from_user}; |
|
803 |
- |
|
804 |
- # Show both |
|
805 |
- say encode('UTF-8', "$text --$user"); |
|
809 |
+ # Search MetaCPAN for "mojolicious" and list latest releases |
|
810 |
+ my $url = Mojo::URL->new('http://api.metacpan.org/v0/release/_search'); |
|
811 |
+ $url->query({q => 'mojolicious', sort => 'date:desc'}); |
|
812 |
+ for my $hit (@{$ua->get($url)->res->json->{hits}{hits}}) { |
|
813 |
+ say "$hit->{_source}{name} ($hit->{_source}{author})"; |
|
806 | 814 |
} |
807 | 815 |
|
808 | 816 |
=head2 Basic authentication |
... | ... |
@@ -840,7 +848,9 @@ This even works for proxy C<CONNECT> requests. |
840 | 848 |
|
841 | 849 |
=head2 Content generators |
842 | 850 |
|
843 |
-Generate the same type of content repeatedly for multiple requests. |
|
851 |
+Content generators can be registered with |
|
852 |
+L<Mojo::UserAgent::Transactor/"add_generator"> to generate the same type of |
|
853 |
+content repeatedly for multiple requests. |
|
844 | 854 |
|
845 | 855 |
use Mojo::UserAgent; |
846 | 856 |
use Mojo::Asset::File; |
... | ... |
@@ -860,20 +870,58 @@ The C<json> and C<form> content generators are always available. |
860 | 870 |
|
861 | 871 |
use Mojo::UserAgent; |
862 | 872 |
|
863 |
- # Send JSON content via PATCH |
|
873 |
+ # Send "application/json" content via PATCH |
|
864 | 874 |
my $ua = Mojo::UserAgent->new; |
865 | 875 |
my $tx = $ua->patch('http://api.example.com' => json => {foo => 'bar'}); |
866 | 876 |
|
877 |
+ # Send query parameters via GET |
|
878 |
+ my $tx2 = $ua->get('http://search.example.com' => form => {q => 'test'}); |
|
879 |
+ |
|
867 | 880 |
# Send "application/x-www-form-urlencoded" content via POST |
868 |
- my $tx2 = $ua->post('http://search.example.com' => form => {q => 'test'}); |
|
881 |
+ my $tx3 = $ua->post('http://search.example.com' => form => {q => 'test'}); |
|
869 | 882 |
|
870 | 883 |
# Send "multipart/form-data" content via PUT |
871 |
- my $tx3 = $ua->put('http://upload.example.com' => |
|
884 |
+ my $tx4 = $ua->put('http://upload.example.com' => |
|
872 | 885 |
form => {test => {content => 'Hello World!'}}); |
873 | 886 |
|
874 | 887 |
For more information about available content generators see also |
875 | 888 |
L<Mojo::UserAgent::Transactor/"tx">. |
876 | 889 |
|
890 |
+=head2 Large file downloads |
|
891 |
+ |
|
892 |
+When downloading large files with L<Mojo::UserAgent> you don't have to worry |
|
893 |
+about memory usage at all, because it will automatically stream everything |
|
894 |
+above C<250KB> into a temporary file, which can then be moved into a permanent |
|
895 |
+file with L<Mojo::Asset::File/"move_to">. |
|
896 |
+ |
|
897 |
+ use Mojo::UserAgent; |
|
898 |
+ |
|
899 |
+ # Lets fetch the latest Mojolicious tarball |
|
900 |
+ my $ua = Mojo::UserAgent->new(max_redirects => 5); |
|
901 |
+ my $tx = $ua->get('latest.mojolicio.us'); |
|
902 |
+ $tx->res->content->asset->move_to('mojo.tar.gz'); |
|
903 |
+ |
|
904 |
+To protect you from excessively large files there is also a limit of C<10MB> |
|
905 |
+by default, which you can tweak with the MOJO_MAX_MESSAGE_SIZE environment |
|
906 |
+variable. |
|
907 |
+ |
|
908 |
+ # Increase limit to 1GB |
|
909 |
+ $ENV{MOJO_MAX_MESSAGE_SIZE} = 1073741824; |
|
910 |
+ |
|
911 |
+=head2 Large file upload |
|
912 |
+ |
|
913 |
+Uploading a large file is even easier. |
|
914 |
+ |
|
915 |
+ use Mojo::UserAgent; |
|
916 |
+ |
|
917 |
+ # Upload file via POST and "multipart/form-data" |
|
918 |
+ my $ua = Mojo::UserAgent->new; |
|
919 |
+ $ua->post('example.com/upload' => |
|
920 |
+ form => {image => {file => '/home/sri/hello.png'}}); |
|
921 |
+ |
|
922 |
+And once again you don't have to worry about memory usage, all data will be |
|
923 |
+streamed directly from the file. |
|
924 |
+ |
|
877 | 925 |
=head2 Streaming response |
878 | 926 |
|
879 | 927 |
Receiving a streaming response can be really tricky in most HTTP clients, but |
... | ... |
@@ -885,6 +933,9 @@ L<Mojo::UserAgent> makes it actually easy. |
885 | 933 |
my $ua = Mojo::UserAgent->new; |
886 | 934 |
my $tx = $ua->build_tx(GET => 'http://example.com'); |
887 | 935 |
|
936 |
+ # Accept response of indefinite size |
|
937 |
+ $tx->res->max_message_size(0); |
|
938 |
+ |
|
888 | 939 |
# Replace "read" events to disable default content parser |
889 | 940 |
$tx->res->content->unsubscribe('read')->on(read => sub { |
890 | 941 |
my ($content, $bytes) = @_; |
... | ... |
@@ -908,60 +959,26 @@ Sending a streaming request is almost just as easy. |
908 | 959 |
my $ua = Mojo::UserAgent->new; |
909 | 960 |
my $tx = $ua->build_tx(GET => 'http://example.com'); |
910 | 961 |
|
911 |
- # Prepare content |
|
912 |
- my $content = 'Hello world!'; |
|
913 |
- $tx->req->headers->content_length(length $content); |
|
962 |
+ # Prepare body |
|
963 |
+ my $body = 'Hello world!'; |
|
964 |
+ $tx->req->headers->content_length(length $body); |
|
914 | 965 |
|
915 | 966 |
# Start writing directly with a drain callback |
916 | 967 |
my $drain; |
917 | 968 |
$drain = sub { |
918 |
- my $req = shift; |
|
919 |
- my $chunk = substr $content, 0, 1, ''; |
|
920 |
- $drain = undef unless length $content; |
|
921 |
- $req->write($chunk, $drain); |
|
969 |
+ my $content = shift; |
|
970 |
+ my $chunk = substr $body, 0, 1, ''; |
|
971 |
+ $drain = undef unless length $body; |
|
972 |
+ $content->write($chunk, $drain); |
|
922 | 973 |
}; |
923 |
- $tx->req->$drain; |
|
974 |
+ $tx->req->content->$drain; |
|
924 | 975 |
|
925 | 976 |
# Process transaction |
926 | 977 |
$ua->start($tx); |
927 | 978 |
|
928 |
-The drain callback passed to L<Mojo::Message/"write"> will be invoked whenever |
|
979 |
+The drain callback passed to L<Mojo::Content/"write"> will be invoked whenever |
|
929 | 980 |
the entire previous chunk has actually been written. |
930 | 981 |
|
931 |
-=head2 Large file downloads |
|
932 |
- |
|
933 |
-When downloading large files with L<Mojo::UserAgent> you don't have to worry |
|
934 |
-about memory usage at all, because it will automatically stream everything |
|
935 |
-above C<250KB> into a temporary file. |
|
936 |
- |
|
937 |
- use Mojo::UserAgent; |
|
938 |
- |
|
939 |
- # Lets fetch the latest Mojolicious tarball |
|
940 |
- my $ua = Mojo::UserAgent->new(max_redirects => 5); |
|
941 |
- my $tx = $ua->get('latest.mojolicio.us'); |
|
942 |
- $tx->res->content->asset->move_to('mojo.tar.gz'); |
|
943 |
- |
|
944 |
-To protect you from excessively large files there is also a limit of C<5MB> by |
|
945 |
-default, which you can tweak with the MOJO_MAX_MESSAGE_SIZE environment |
|
946 |
-variable. |
|
947 |
- |
|
948 |
- # Increase limit to 1GB |
|
949 |
- $ENV{MOJO_MAX_MESSAGE_SIZE} = 1073741824; |
|
950 |
- |
|
951 |
-=head2 Large file upload |
|
952 |
- |
|
953 |
-Uploading a large file is even easier. |
|
954 |
- |
|
955 |
- use Mojo::UserAgent; |
|
956 |
- |
|
957 |
- # Upload file via POST and "multipart/form-data" |
|
958 |
- my $ua = Mojo::UserAgent->new; |
|
959 |
- $ua->post('example.com/upload' => |
|
960 |
- form => {image => {file => '/home/sri/hello.png'}}); |
|
961 |
- |
|
962 |
-And once again you don't have to worry about memory usage, all data will be |
|
963 |
-streamed directly from the file. |
|
964 |
- |
|
965 | 982 |
=head2 Non-blocking |
966 | 983 |
|
967 | 984 |
L<Mojo::UserAgent> has been designed from the ground up to be non-blocking, |
... | ... |
@@ -974,12 +991,12 @@ can keep many parallel connections active at the same time. |
974 | 991 |
|
975 | 992 |
# Parallel non-blocking requests |
976 | 993 |
my $ua = Mojo::UserAgent->new; |
977 |
- $ua->get('http://mojolicio.us' => sub { |
|
978 |
- my ($ua, $tx) = @_; |
|
994 |
+ $ua->get('http://metacpan.org/search?q=mojo' => sub { |
|
995 |
+ my ($ua, $mojo) = @_; |
|
979 | 996 |
... |
980 | 997 |
}); |
981 |
- $ua->get('http://mojolicio.us/perldoc' => sub { |
|
982 |
- my ($ua, $tx) = @_; |
|
998 |
+ $ua->get('http://metacpan.org/search?q=mango' => sub { |
|
999 |
+ my ($ua, $mango) = @_; |
|
983 | 1000 |
... |
984 | 1001 |
}); |
985 | 1002 |
|
... | ... |
@@ -990,34 +1007,34 @@ You can take full control of the L<Mojo::IOLoop> event loop. |
990 | 1007 |
|
991 | 1008 |
=head2 Parallel blocking requests |
992 | 1009 |
|
993 |
-You can emulate blocking behavior by using a L<Mojo::IOLoop> delay to |
|
994 |
-synchronize multiple non-blocking requests. Just be aware that the resulting |
|
995 |
-transactions will be in random order. |
|
1010 |
+You can emulate blocking behavior by using L<Mojo::IOLoop/"delay"> to |
|
1011 |
+synchronize multiple non-blocking requests. |
|
996 | 1012 |
|
997 | 1013 |
use Mojo::UserAgent; |
998 | 1014 |
use Mojo::IOLoop; |
999 | 1015 |
|
1000 |
- # Synchronize non-blocking requests and capture result |
|
1016 |
+ # Synchronize non-blocking requests and capture results |
|
1001 | 1017 |
my $ua = Mojo::UserAgent->new; |
1002 | 1018 |
my $delay = Mojo::IOLoop->delay; |
1003 |
- $ua->get('http://mojolicio.us' => $delay->begin); |
|
1004 |
- $ua->get('http://mojolicio.us/perldoc' => $delay->begin); |
|
1005 |
- my ($tx, $tx2) = $delay->wait; |
|
1019 |
+ $ua->get('http://metacpan.org/search?q=mojo' => $delay->begin); |
|
1020 |
+ $ua->get('http://metacpan.org/search?q=mango' => $delay->begin); |
|
1021 |
+ my ($mojo, $mango) = $delay->wait; |
|
1006 | 1022 |
|
1007 | 1023 |
The event L<Mojo::IOLoop::Delay/"finish"> can be used for code that needs to |
1008 |
-be able to work standalone as well as inside L<Mojolicious> applications. |
|
1024 |
+be able to work standalone as well as inside an already running event loop. |
|
1009 | 1025 |
|
1010 | 1026 |
use Mojo::UserAgent; |
1011 | 1027 |
use Mojo::IOLoop; |
1012 | 1028 |
|
1013 | 1029 |
# Synchronize non-blocking requests portably |
1014 | 1030 |
my $ua = Mojo::UserAgent->new; |
1015 |
- my $delay = Mojo::IOLoop->delay(sub { |
|
1016 |
- my ($delay, $tx, $tx2) = @_; |
|
1031 |
+ my $delay = Mojo::IOLoop->delay; |
|
1032 |
+ $delay->on(finish => sub { |
|
1033 |
+ my ($delay, $mojo, $mango) = @_; |
|
1017 | 1034 |
... |
1018 | 1035 |
}); |
1019 |
- $ua->get('http://mojolicio.us' => $delay->begin); |
|
1020 |
- $ua->get('http://mojolicio.us/perldoc' => $delay->begin); |
|
1036 |
+ $ua->get('http://metacpan.org/search?q=mojo' => $delay->begin); |
|
1037 |
+ $ua->get('http://metacpan.org/search?q=mango' => $delay->begin); |
|
1021 | 1038 |
$delay->wait unless Mojo::IOLoop->is_running; |
1022 | 1039 |
|
1023 | 1040 |
=head2 Command line |
... | ... |
@@ -1052,19 +1069,19 @@ The request can be customized as well. |
1052 | 1069 |
|
1053 | 1070 |
You can follow redirects and view the headers for all messages. |
1054 | 1071 |
|
1055 |
- $ mojo get -r -v http://reddit.com 'head > title' |
|
1072 |
+ $ mojo get -r -v http://google.com 'head > title' |
|
1056 | 1073 |
|
1057 | 1074 |
Extract just the information you really need from JSON data structures. |
1058 | 1075 |
|
1059 |
- $ mojo get http://search.twitter.com/search.json /error |
|
1076 |
+ $ mojo get https://api.metacpan.org/v0/author/SRI /name |
|
1060 | 1077 |
|
1061 | 1078 |
This can be an invaluable tool for testing your applications. |
1062 | 1079 |
|
1063 | 1080 |
$ ./myapp.pl get /welcome 'head > title' |
1064 | 1081 |
|
1065 |
-=head2 Oneliners |
|
1082 |
+=head2 One-liners |
|
1066 | 1083 |
|
1067 |
-For quick hacks and especially testing, L<ojo> oneliners are also a great |
|
1084 |
+For quick hacks and especially testing, L<ojo> one-liners are also a great |
|
1068 | 1085 |
choice. |
1069 | 1086 |
|
1070 | 1087 |
$ perl -Mojo -E 'say g("mojolicio.us")->dom->html->head->title->text' |
... | ... |
@@ -1097,7 +1114,7 @@ that they will be picked up automatically by the command line interface? |
1097 | 1114 |
|
1098 | 1115 |
1; |
1099 | 1116 |
|
1100 |
-There are many more useful methods and attributes in L<Mojolicious::Command> |
|
1117 |
+There are many more useful attributes and methods in L<Mojolicious::Command> |
|
1101 | 1118 |
that you can use or overload. |
1102 | 1119 |
|
1103 | 1120 |
$ mojo spy secret |
... | ... |
@@ -1124,32 +1141,34 @@ namespace. |
1124 | 1141 |
|
1125 | 1142 |
=head2 Running code against your application |
1126 | 1143 |
|
1127 |
-Ever thought about running a quick oneliner against your L<Mojolicious> |
|
1144 |
+Ever thought about running a quick one-liner against your L<Mojolicious> |
|
1128 | 1145 |
application to test something? Thanks to the C<eval> command you can do just |
1129 | 1146 |
that, the application object itself can be accessed via C<app>. |
1130 | 1147 |
|
1131 |
- $ mojo generate lite_app |
|
1148 |
+ $ mojo generate lite_app myapp.pl |
|
1132 | 1149 |
$ ./myapp.pl eval 'say for @{app->static->paths}' |
1133 | 1150 |
|
1134 |
-The C<verbose> option will automatically print the return value to C<STDOUT>. |
|
1151 |
+The C<verbose> options will automatically print the return value or returned |
|
1152 |
+data structure to C<STDOUT>. |
|
1135 | 1153 |
|
1136 | 1154 |
$ ./myapp.pl eval -v 'app->static->paths->[0]' |
1155 |
+ $ ./myapp.pl eval -V 'app->static->paths' |
|
1137 | 1156 |
|
1138 | 1157 |
=head2 Making your application installable |
1139 | 1158 |
|
1140 | 1159 |
Ever thought about releasing your L<Mojolicious> application to CPAN? It's |
1141 | 1160 |
actually much easier than you might think. |
1142 | 1161 |
|
1143 |
- $ mojo generate app |
|
1144 |
- $ cd my_mojolicious_app |
|
1145 |
- $ mv public lib/MyMojoliciousApp/ |
|
1146 |
- $ mv templates lib/MyMojoliciousApp/ |
|
1162 |
+ $ mojo generate app MyApp |
|
1163 |
+ $ cd my_app |
|
1164 |
+ $ mv public lib/MyApp/ |
|
1165 |
+ $ mv templates lib/MyApp/ |
|
1147 | 1166 |
|
1148 | 1167 |
The trick is to move the C<public> and C<templates> directories so they can |
1149 | 1168 |
get automatically installed with the modules. |
1150 | 1169 |
|
1151 | 1170 |
# Application |
1152 |
- package MyMojoliciousApp; |
|
1171 |
+ package MyApp; |
|
1153 | 1172 |
use Mojo::Base 'Mojolicious'; |
1154 | 1173 |
|
1155 | 1174 |
use File::Basename 'dirname'; |
... | ... |
@@ -1162,7 +1181,7 @@ get automatically installed with the modules. |
1162 | 1181 |
my $self = shift; |
1163 | 1182 |
|
1164 | 1183 |
# Switch to installable home directory |
1165 |
- $self->home->parse(catdir(dirname(__FILE__), 'MyMojoliciousApp')); |
|
1184 |
+ $self->home->parse(catdir(dirname(__FILE__), 'MyApp')); |
|
1166 | 1185 |
|
1167 | 1186 |
# Switch to installable "public" directory |
1168 | 1187 |
$self->static->paths->[0] = $self->home->rel_dir('public'); |
... | ... |
@@ -1181,7 +1200,7 @@ get automatically installed with the modules. |
1181 | 1200 |
That's really everything, now you can package your application like any other |
1182 | 1201 |
CPAN module. |
1183 | 1202 |
|
1184 |
- $ ./script/my_mojolicious_app generate makefile |
|
1203 |
+ $ ./script/my_app generate makefile |
|
1185 | 1204 |
$ perl Makefile.PL |
1186 | 1205 |
$ make test |
1187 | 1206 |
$ make manifest |
... | ... |
@@ -1190,7 +1209,7 @@ CPAN module. |
1190 | 1209 |
And if you have a C<PAUSE> account (which can be requested at |
1191 | 1210 |
L<http://pause.perl.org>) even upload it. |
1192 | 1211 |
|
1193 |
- $ mojo cpanify -u USER -p PASS MyMojoliciousApp-0.01.tar.gz |
|
1212 |
+ $ mojo cpanify -u USER -p PASS MyApp-0.01.tar.gz |
|
1194 | 1213 |
|
1195 | 1214 |
=head2 Hello World |
1196 | 1215 |
|
... | ... |
@@ -1206,10 +1225,10 @@ rendering kicks in even if no actual code gets executed by the router. The |
1206 | 1225 |
renderer just picks up the C<text> value from the stash and generates a |
1207 | 1226 |
response. |
1208 | 1227 |
|
1209 |
-=head2 Hello World oneliners |
|
1228 |
+=head2 Hello World one-liners |
|
1210 | 1229 |
|
1211 | 1230 |
The C<Hello World> example above can get even a little bit shorter in an |
1212 |
-L<ojo> oneliner. |
|
1231 |
+L<ojo> one-liner. |
|
1213 | 1232 |
|
1214 | 1233 |
$ perl -Mojo -E 'a({text => "Hello World!"})->start' daemon |
1215 | 1234 |
|
... | ... |
@@ -1,4 +1,6 @@ |
1 | 1 |
|
2 |
+=encoding utf8 |
|
3 |
+ |
|
2 | 4 |
=head1 NAME |
3 | 5 |
|
4 | 6 |
Mojolicious::Guides::FAQ - Frequently Asked Questions |
... | ... |
@@ -62,7 +64,7 @@ which we already have done in the past. |
62 | 64 |
To protect your applications from excessively large requests and responses, |
63 | 65 |
our HTTP parser has a cap after which it will automatically stop accepting new |
64 | 66 |
data, and in most cases force the connection to be closed. This limit is |
65 |
-around C<5MB> by default, you can use the MOJO_MAX_MESSAGE_SIZE environment |
|
67 |
+around C<10MB> by default, you can use the MOJO_MAX_MESSAGE_SIZE environment |
|
66 | 68 |
variable to change this value. |
67 | 69 |
|
68 | 70 |
=head2 What does the error "Maximum line size exceeded" mean? |
... | ... |
@@ -1,4 +1,6 @@ |
1 | 1 |
|
2 |
+=encoding utf8 |
|
3 |
+ |
|
2 | 4 |
=head1 NAME |
3 | 5 |
|
4 | 6 |
Mojolicious::Guides::Growing - Growing |
... | ... |
@@ -77,9 +79,8 @@ all session state is kept client-side. |
77 | 79 |
| | <- 200 OK <- | | |
78 | 80 |
+---------+ +------------+ |
79 | 81 |
|
80 |
-While HTTP methods such as C<PUT>, C<GET> and C<DELETE> are not directly part |
|
81 |
-of REST they go very well with it and are commonly used to manipulate |
|
82 |
-C<resources>. |
|
82 |
+While HTTP methods such as PUT, GET and DELETE are not directly part of REST |
|
83 |
+they go very well with it and are commonly used to manipulate C<resources>. |
|
83 | 84 |
|
84 | 85 |
=head2 Sessions |
85 | 86 |
|
... | ... |
@@ -163,8 +164,8 @@ organized CPAN distribution to maximize maintainability. |
163 | 164 |
|
164 | 165 |
Both application skeletons can be automatically generated. |
165 | 166 |
|
166 |
- $ mojo generate lite_app |
|
167 |
- $ mojo generate app |
|
167 |
+ $ mojo generate lite_app myapp.pl |
|
168 |
+ $ mojo generate app MyApp |
|
168 | 169 |
|
169 | 170 |
=head2 Foundation |
170 | 171 |
|
... | ... |
@@ -167,21 +167,37 @@ There is one big difference though, by calling it manually you can make sure |
167 | 167 |
that templates use the current controller object, and not the default |
168 | 168 |
controller specified with the attribute L<Mojolicious/"controller_class">. |
169 | 169 |
|
170 |
+ $self->render_later; |
|
171 |
+ |
|
172 |
+You can also disable automatic rendering with the method |
|
173 |
+L<Mojolicious::Controller/"render_later">, which can be very useful to delay |
|
174 |
+rendering when a non-blocking operation has to be performed first. |
|
175 |
+ |
|
170 | 176 |
=head2 Rendering templates |
171 | 177 |
|
172 |
-The renderer will always try to detect the right template but you can also |
|
173 |
-use the C<template> stash value to render a specific one. |
|
178 |
+The renderer will always try to detect the right template, but you can also |
|
179 |
+use the C<template> stash value to render a specific one. Everything before |
|
180 |
+the last slash will be interpreted as the subdirectory path in which to find |
|
181 |
+the template. |
|
174 | 182 |
|
175 |
- $self->render(template => 'foo/bar'); |
|
183 |
+ # foo/bar/baz.*.* |
|
184 |
+ $self->render(template => 'foo/bar/baz'); |
|
176 | 185 |
|
177 | 186 |
Choosing a specific C<format> and C<handler> is just as easy. |
178 | 187 |
|
179 |
- $self->render(template => 'foo/bar', format => 'txt', handler => 'epl'); |
|
188 |
+ # foo/bar/baz.txt.epl |
|
189 |
+ $self->render(template => 'foo/bar/baz', format => 'txt', handler => 'epl'); |
|
180 | 190 |
|
181 | 191 |
Because rendering a specific template is the most common task it also has a |
182 | 192 |
shortcut. |
183 | 193 |
|
184 |
- $self->render('foo/bar'); |
|
194 |
+ $self->render('foo/bar/baz'); |
|
195 |
+ |
|
196 |
+If you're not sure in advance if a template actually exists, you can also use |
|
197 |
+the method L<Mojolicious::Controller/"render_maybe"> to try multiple |
|
198 |
+alternatives. |
|
199 |
+ |
|
200 |
+ $self->render_maybe('localized/baz') or $self->render('foo/bar/baz'); |
|
185 | 201 |
|
186 | 202 |
=head2 Rendering inline templates |
187 | 203 |
|
... | ... |
@@ -199,7 +215,7 @@ too. |
199 | 215 |
Characters can be rendered to bytes with the C<text> stash value, the given |
200 | 216 |
content will be automatically encoded to bytes. |
201 | 217 |
|
202 |
- $self->render(text => 'Hello Wörld!'); |
|
218 |
+ $self->render(text => 'I ♥ Mojolicious!'); |
|
203 | 219 |
|
204 | 220 |
=head2 Rendering data |
205 | 221 |
|
... | ... |
@@ -226,7 +242,8 @@ C<partial> stash value. |
226 | 242 |
No encoding will be performed, making it easy to reuse the result in other |
227 | 243 |
templates or to generate binary data. |
228 | 244 |
|
229 |
- $self->render(data => $self->render('pdf_invoice', partial => 1)); |
|
245 |
+ my $pdf = $self->render('invoice', format => 'pdf', partial => 1); |
|
246 |
+ $self->render(data => $pdf, format => 'pdf'); |
|
230 | 247 |
|
231 | 248 |
=head2 Status code |
232 | 249 |
|
... | ... |
@@ -239,8 +256,12 @@ Response status codes can be changed with the C<status> stash value. |
239 | 256 |
The C<Content-Type> header of the response is actually based on the MIME type |
240 | 257 |
mapping of the C<format> stash value. |
241 | 258 |
|
259 |
+ # Content-Type: text/plain |
|
242 | 260 |
$self->render(text => 'Hello.', format => 'txt'); |
243 | 261 |
|
262 |
+ # Content-Type: image/png |
|
263 |
+ $self->render(data => $bytes, format => 'png'); |
|
264 |
+ |
|
244 | 265 |
These mappings can be easily extended or changed with L<Mojolicious/"types">. |
245 | 266 |
|
246 | 267 |
# Application |
... | ... |
@@ -299,12 +320,14 @@ L<Mojolicious::Controller/"render">. |
299 | 320 |
|
300 | 321 |
The best possible representation will be automatically selected from the |
301 | 322 |
C<Accept> request header, C<format> stash value or C<format> GET/POST |
302 |
-parameter. |
|
323 |
+parameter and stored in the C<format> stash value. To change MIME type |
|
324 |
+mappings for the C<Accept> request header or the C<Content-Type> response |
|
325 |
+header you can use L<Mojolicious/"types">. |
|
303 | 326 |
|
304 | 327 |
$self->respond_to( |
305 | 328 |
json => {json => {hello => 'world'}}, |
306 | 329 |
html => sub { |
307 |
- $self->content_for(head => '<meta name="author" content="sri" />'); |
|
330 |
+ $self->content_for(head => '<meta name="author" content="sri">'); |
|
308 | 331 |
$self->render(template => 'hello', message => 'world') |
309 | 332 |
} |
310 | 333 |
); |
... | ... |
@@ -591,6 +614,135 @@ template with named blocks that child templates can override. |
591 | 614 |
|
592 | 615 |
This chain could go on and on to allow a very high level of template reuse. |
593 | 616 |
|
617 |
+=head2 Form validation |
|
618 |
+ |
|
619 |
+You can use L<Mojolicious::Controller/"validation"> to validate GET/POST |
|
620 |
+parameters submitted to your application. All unknown fields will be ignored |
|
621 |
+by default, so you have to decide which should be required or optional before |
|
622 |
+you can perform checks on their values. Every check is performed right away, |
|
623 |
+so you can use the results immediately to build more advanced validation logic |
|
624 |
+with methods like L<Mojolicious::Validator::Validation/"is_valid">. |
|
625 |
+ |
|
626 |
+ use Mojolicious::Lite; |
|
627 |
+ |
|
628 |
+ get '/' => sub { |
|
629 |
+ my $self = shift; |
|
630 |
+ |
|
631 |
+ # Check if parameters have been submitted |
|
632 |
+ my $validation = $self->validation; |
|
633 |
+ return $self->render unless $validation->has_data; |
|
634 |
+ |
|
635 |
+ # Validate parameters ("pass_again" depends on "pass") |
|
636 |
+ $validation->required('user')->size(1, 20)->like(qr/^[e-t]+$/); |
|
637 |
+ $validation->required('pass_again')->equal_to('pass') |
|
638 |
+ if $validation->optional('pass')->size(7, 500)->is_valid; |
|
639 |
+ |
|
640 |
+ # Render confirmation if validation was successful |
|
641 |
+ $self->render('thanks') unless $validation->has_error; |
|
642 |
+ } => 'index'; |
|
643 |
+ |
|
644 |
+ app->start; |
|
645 |
+ __DATA__ |
|
646 |
+ |
|
647 |
+ @@ index.html.ep |
|
648 |
+ <!DOCTYPE html> |
|
649 |
+ <html> |
|
650 |
+ <head> |
|
651 |
+ %= stylesheet begin |
|
652 |
+ label.field-with-error { color: #dd7e5e } |
|
653 |
+ input.field-with-error { background-color: #fd9e7e } |
|
654 |
+ % end |
|
655 |
+ </head> |
|
656 |
+ <body> |
|
657 |
+ %= form_for index => begin |
|
658 |
+ %= label_for user => 'Username (required, 1-20 characters, only e-t)' |
|
659 |
+ <br> |
|
660 |
+ %= text_field 'user' |
|
661 |
+ %= submit_button |
|
662 |
+ <br> |
|
663 |
+ %= label_for pass => 'Password (optional, 7-500 characters)' |
|
664 |
+ <br> |
|
665 |
+ %= password_field 'pass' |
|
666 |
+ <br> |
|
667 |
+ %= label_for pass_again => 'Password again (equal to the value above)' |
|
668 |
+ <br> |
|
669 |
+ %= password_field 'pass_again' |
|
670 |
+ % end |
|
671 |
+ </body> |
|
672 |
+ </html> |
|
673 |
+ |
|
674 |
+ @@ thanks.html.ep |
|
675 |
+ <!DOCTYPE html> |
|
676 |
+ <html><body>Thank you <%= validation->param('user') %>.</body></html> |
|
677 |
+ |
|
678 |
+Form elements generated with tag helpers from |
|
679 |
+L<Mojolicious::Plugin::TagHelpers> will automatically remember their previous |
|
680 |
+values and add the class C<field-with-error> for fields that failed validation |
|
681 |
+to make styling with CSS easier. |
|
682 |
+ |
|
683 |
+ <label class="field-with-error" for="user"> |
|
684 |
+ Username (required, only characters e-t) |
|
685 |
+ </label> |
|
686 |
+ <input class="field-with-error" type="text" name="user" value="sri" /> |
|
687 |
+ |
|
688 |
+For a full list of available checks see also |
|
689 |
+L<Mojolicious::Validator/"CHECKS">. |
|
690 |
+ |
|
691 |
+=head2 Adding form validation checks |
|
692 |
+ |
|
693 |
+Validation checks can be registered with L<Mojolicious::Validator/"add_check"> |
|
694 |
+and return a false value if they were successful. A true value may be used to |
|
695 |
+pass along additional information which can then be retrieved with |
|
696 |
+L<Mojolicious::Validator::Validation/"error">. |
|
697 |
+ |
|
698 |
+ use Mojolicious::Lite; |
|
699 |
+ |
|
700 |
+ # Add "range" check |
|
701 |
+ app->validator->add_check(range => sub { |
|
702 |
+ my ($validation, $name, $value, $min, $max) = @_; |
|
703 |
+ return $value < $min || $value > $max; |
|
704 |
+ }); |
|
705 |
+ |
|
706 |
+ get '/' => 'form'; |
|
707 |
+ |
|
708 |
+ post '/test' => sub { |
|
709 |
+ my $self = shift; |
|
710 |
+ |
|
711 |
+ # Validate parameters with custom check |
|
712 |
+ my $validation = $self->validation; |
|
713 |
+ $validation->required('number')->range(3, 23); |
|
714 |
+ |
|
715 |
+ # Render form again if validation failed |
|
716 |
+ return $self->render('form') if $validation->has_error; |
|
717 |
+ |
|
718 |
+ # Prevent double submit with redirect |
|
719 |
+ $self->flash(number => $validation->param('number')); |
|
720 |
+ $self->redirect_to('form'); |
|
721 |
+ }; |
|
722 |
+ |
|
723 |
+ app->start; |
|
724 |
+ __DATA__ |
|
725 |
+ |
|
726 |
+ @@ form.html.ep |
|
727 |
+ <!DOCTYPE html> |
|
728 |
+ <html> |
|
729 |
+ <body> |
|
730 |
+ % if (my $number = flash 'number') { |
|
731 |
+ <p>Thanks, the number <%= $number %> was valid.</p> |
|
732 |
+ % } |
|
733 |
+ %= form_for test => begin |
|
734 |
+ % if (my $err = validation->error('number')) { |
|
735 |
+ <p> |
|
736 |
+ %= 'Value is required.' if $err->[0] eq 'required' |
|
737 |
+ %= 'Value needs to be between 3 and 23.' if $err->[0] eq 'range' |
|
738 |
+ </p> |
|
739 |
+ % } |
|
740 |
+ %= text_field 'number' |
|
741 |
+ %= submit_button |
|
742 |
+ % end |
|
743 |
+ </html> |
|
744 |
+ </html> |
|
745 |
+ |
|
594 | 746 |
=head2 Adding helpers |
595 | 747 |
|
596 | 748 |
Adding and redefining helpers is very easy, you can use them to do pretty much |
... | ... |
@@ -660,7 +812,8 @@ applications, plugins make that very simple. |
660 | 812 |
|
661 | 813 |
1; |
662 | 814 |
|
663 |
-The C<register> method will be called when you load the plugin. |
|
815 |
+The C<register> method will be called when you load the plugin and to add your |
|
816 |
+helper to the application you can use L<Mojolicious/"helper">. |
|
664 | 817 |
|
665 | 818 |
use Mojolicious::Lite; |
666 | 819 |
|
... | ... |
@@ -800,16 +953,17 @@ C<after_render>. |
800 | 953 |
use IO::Compress::Gzip 'gzip'; |
801 | 954 |
|
802 | 955 |
hook after_render => sub { |
803 |
- my ($self, $output, $format) = @_; |
|
956 |
+ my ($c, $output, $format) = @_; |
|
804 | 957 |
|
805 | 958 |
# Check if "gzip => 1" has been set in the stash |
806 |
- return unless $self->stash->{gzip}; |
|
959 |
+ return unless $c->stash->{gzip}; |
|
807 | 960 |
|
808 | 961 |
# Check if user agent accepts GZip compression |
809 |
- return unless ($self->req->headers->accept_encoding // '') =~ /gzip/i; |
|
962 |
+ return unless ($c->req->headers->accept_encoding // '') =~ /gzip/i; |
|
963 |
+ $c->res->headers->append(Vary => 'Accept-Encoding'); |
|
810 | 964 |
|
811 | 965 |
# Compress content with GZip |
812 |
- $self->res->headers->content_encoding('gzip'); |
|
966 |
+ $c->res->headers->content_encoding('gzip'); |
|
813 | 967 |
gzip $output, \my $compressed; |
814 | 968 |
$$output = $compressed; |
815 | 969 |
}; |
... | ... |
@@ -829,10 +983,10 @@ C<after_render>. |
829 | 983 |
=head2 Chunked transfer encoding |
830 | 984 |
|
831 | 985 |
For very dynamic content you might not know the response content length in |
832 |
-advance, that's where the C<chunked> transfer encoding comes in handy. A |
|
833 |
-common use would be to send the C<head> section of an HTML document to the |
|
834 |
-browser in advance and speed up preloading of referenced images and |
|
835 |
-stylesheets. |
|
986 |
+advance, that's where the C<chunked> transfer encoding and |
|
987 |
+L<Mojolicious::Controller/"write_chunk"> come in handy. A common use would be |
|
988 |
+to send the C<head> section of an HTML document to the browser in advance and |
|
989 |
+speed up preloading of referenced images and stylesheets. |
|
836 | 990 |
|
837 | 991 |
$self->write_chunk('<html><head><title>Example</title></head>' => sub { |
838 | 992 |
my $self = shift; |
... | ... |
@@ -856,7 +1010,7 @@ might not work perfectly in all deployment environments. |
856 | 1010 |
=head2 Encoding |
857 | 1011 |
|
858 | 1012 |
Templates stored in files are expected to be C<UTF-8> by default, but that can |
859 |
-be easily changed. |
|
1013 |
+be easily changed with L<Mojolicious::Renderer/"encoding">. |
|
860 | 1014 |
|
861 | 1015 |
# Application |
862 | 1016 |
package MyApp; |
... | ... |
@@ -931,7 +1085,8 @@ L<Mojo::Template> contains the whole list of available options. |
931 | 1085 |
|
932 | 1086 |
Maybe you would prefer a different template system than C<ep>, and there is |
933 | 1087 |
not already a plugin on CPAN for your favorite one, all you have to do is add |
934 |
-a new C<handler> when C<register> is called. |
|
1088 |
+a new C<handler> with L<Mojolicious::Renderer/"add_handler"> when C<register> |
|
1089 |
+is called. |
|
935 | 1090 |
|
936 | 1091 |
package Mojolicious::Plugin::MyRenderer; |
937 | 1092 |
use Mojo::Base 'Mojolicious::Plugin'; |
... | ... |
@@ -983,6 +1138,32 @@ the renderer provides methods to help you with that. |
983 | 1138 |
@@ index.html.mine |
984 | 1139 |
... |
985 | 1140 |
|
1141 |
+=head2 Adding a handler to generate binary data |
|
1142 |
+ |
|
1143 |
+By default the renderer assumes that every C<handler> generates characters |
|
1144 |
+that need to be automatically encoded, but this can be be easily disabled if |
|
1145 |
+you're generating bytes instead. |
|
1146 |
+ |
|
1147 |
+ use Mojolicious::Lite; |
|
1148 |
+ use Mango::BSON ':bson'; |
|
1149 |
+ |
|
1150 |
+ # Add "bson" handler |
|
1151 |
+ app->renderer->add_handler(bson => sub { |
|
1152 |
+ my ($renderer, $c, $output, $options) = @_; |
|
1153 |
+ |
|
1154 |
+ # Disable automatic encoding |
|
1155 |
+ delete $options->{encoding}; |
|
1156 |
+ |
|
1157 |
+ # Encode BSON data from stash value |
|
1158 |
+ $$output = bson_encode delete $c->stash->{bson}; |
|
1159 |
+ |
|
1160 |
+ return 1; |
|
1161 |
+ }); |
|
1162 |
+ |
|
1163 |
+ get '/' => {bson => {i => '♥ mojolicious'}, handler => 'bson'}; |
|
1164 |
+ |
|
1165 |
+ app->start; |
|
1166 |
+ |
|
986 | 1167 |
=head1 MORE |
987 | 1168 |
|
988 | 1169 |
You can continue with L<Mojolicious::Guides> now or take a look at the |
... | ... |
@@ -87,10 +87,12 @@ any time. |
87 | 87 |
/sebastian -> /:name -> {name => 'sebastian'} |
88 | 88 |
{name => 'sebastian'} -> /:name -> /sebastian |
89 | 89 |
|
90 |
+Every placeholder has a name, even if it's just an empty string. |
|
91 |
+ |
|
90 | 92 |
=head2 Generic placeholders |
91 | 93 |
|
92 |
-Generic placeholders are the simplest form of placeholders and match all |
|
93 |
-characters except C</> and C<.>. |
|
94 |
+Generic placeholders are the simplest form of placeholders, they use a colon |
|
95 |
+prefix and match all characters except C</> and C<.>. |
|
94 | 96 |
|
95 | 97 |
/hello -> /:name/hello -> undef |
96 | 98 |
/sebastian/23/hello -> /:name/hello -> undef |
... | ... |
@@ -109,10 +111,15 @@ surrounding text. |
109 | 111 |
/sebastian23hello -> /(:name)hello -> {name => 'sebastian23'} |
110 | 112 |
/sebastian 23hello -> /(:name)hello -> {name => 'sebastian 23'} |
111 | 113 |
|
114 |
+The colon prefix is optional for generic placeholders that are surrounded by |
|
115 |
+parentheses. |
|
116 |
+ |
|
117 |
+ /i♥mojolicious -> /(one)♥(two) -> {one => 'i', two => 'mojolicious'} |
|
118 |
+ |
|
112 | 119 |
=head2 Relaxed placeholders |
113 | 120 |
|
114 |
-Relaxed placeholders are just like generic placeholders, but match all |
|
115 |
-characters except C</>. |
|
121 |
+Relaxed placeholders are just like generic placeholders, but use a hash prefix |
|
122 |
+and match all characters except C</>. |
|
116 | 123 |
|
117 | 124 |
/hello -> /#name/hello -> undef |
118 | 125 |
/sebastian/23/hello -> /#name/hello -> undef |
... | ... |
@@ -123,8 +130,8 @@ characters except C</>. |
123 | 130 |
|
124 | 131 |
=head2 Wildcard placeholders |
125 | 132 |
|
126 |
-Wildcard placeholders are just like the two placeholders above, but match |
|
127 |
-absolutely everything, including C</> and C<.>. |
|
133 |
+Wildcard placeholders are just like the two placeholders above, but use an |
|
134 |
+asterisk prefix and match absolutely everything, including C</> and C<.>. |
|
128 | 135 |
|
129 | 136 |
/hello -> /*name/hello -> undef |
130 | 137 |
/sebastian/23/hello -> /*name/hello -> {name => 'sebastian/23'} |
... | ... |
@@ -139,8 +146,9 @@ Most commonly used features every L<Mojolicious> developer should know about. |
139 | 146 |
|
140 | 147 |
=head2 Minimal route |
141 | 148 |
|
142 |
-Every L<Mojolicious> application has a router object you can use to generate |
|
143 |
-route structures, that match in the same order in which they were defined. |
|
149 |
+The attribute L<Mojolicious/"routes"> contains a router you can use to |
|
150 |
+generate route structures, they match in the same order in which they were |
|
151 |
+defined. |
|
144 | 152 |
|
145 | 153 |
# Application |
146 | 154 |
package MyApp; |
... | ... |
@@ -158,8 +166,8 @@ route structures, that match in the same order in which they were defined. |
158 | 166 |
|
159 | 167 |
1; |
160 | 168 |
|
161 |
-The minimal static route above will load and instantiate the class |
|
162 |
-C<MyApp::Foo> and call its C<welcome> method. |
|
169 |
+The minimal route above will load and instantiate the class C<MyApp::Foo> and |
|
170 |
+call its C<welcome> method. |
|
163 | 171 |
|
164 | 172 |
# Controller |
165 | 173 |
package MyApp::Foo; |
... | ... |
@@ -359,7 +367,17 @@ separated by other characters than C</>. |
359 | 367 |
->to(controller => 'foo', action => 'bar'); |
360 | 368 |
|
361 | 369 |
Special stash values like C<controller> and C<action> can also be |
362 |
-placeholders, this allows for extremely flexible routes constructs. |
|
370 |
+placeholders, which is very convenient especially during development, but |
|
371 |
+should only be used very carefully, because every controller method becomes a |
|
372 |
+potential route. All uppercase methods as well as those starting with an |
|
373 |
+underscore are automatically hidden from the router and you can use |
|
374 |
+L<Mojolicious::Routes/"hide"> to add additional ones. |
|
375 |
+ |
|
376 |
+ # Hide "create" method in all controllers |
|
377 |
+ $r->hide('create'); |
|
378 |
+ |
|
379 |
+This has already been done for all attributes and methods from |
|
380 |
+L<Mojolicious::Controller>. |
|
363 | 381 |
|
364 | 382 |
=head2 More restrictive placeholders |
365 | 383 |
|
... | ... |
@@ -448,6 +466,9 @@ L<Mojolicious::Controller/"url_for"> for this. |
448 | 466 |
# Generate URL "/foo/sebastian" for route "test" |
449 | 467 |
my $url = $self->url_for('test', name => 'sebastian'); |
450 | 468 |
|
469 |
+ # Generate URL "http://127.0.0.1:3000/foo/sebastian" for route "test" |
|
470 |
+ my $url = $self->url_for('test', name => 'sebastian')->to_abs; |
|
471 |
+ |
|
451 | 472 |
Nameless routes get an automatically generated one assigned that is simply |
452 | 473 |
equal to the route itself without non-word characters. |
453 | 474 |
|
... | ... |
@@ -489,7 +510,7 @@ methods to pass. |
489 | 510 |
$r->route('/bye')->via('GET', 'POST') |
490 | 511 |
->to(controller => 'foo', action => 'bye'); |
491 | 512 |
|
492 |
-With one small exception, C<HEAD> requests are considered equal to C<GET> and |
|
513 |
+With one small exception, HEAD requests are considered equal to GET and |
|
493 | 514 |
content will not be sent with the response. |
494 | 515 |
|
495 | 516 |
# GET /test -> {controller => 'bar', action => 'test'} |
... | ... |
@@ -500,7 +521,7 @@ content will not be sent with the response. |
500 | 521 |
=head2 WebSockets |
501 | 522 |
|
502 | 523 |
With the method L<Mojolicious::Routes::Route/"websocket"> you can restrict |
503 |
-access to WebSocket handshakes, which are normal C<GET> requests with some |
|
524 |
+access to WebSocket handshakes, which are normal GET requests with some |
|
504 | 525 |
additional information. |
505 | 526 |
|
506 | 527 |
# /echo (WebSocket handshake) |
... | ... |
@@ -521,11 +542,16 @@ additional information. |
521 | 542 |
|
522 | 543 |
1; |
523 | 544 |
|
545 |
+The connection gets established when you respond to the WebSocket handshake |
|
546 |
+request with a C<101> response status, which happens automatically if you |
|
547 |
+subscribe to an event with L<Mojolicious::Controller/"on"> or send a message |
|
548 |
+with L<Mojolicious::Controller/"send"> right away. |
|
549 |
+ |
|
524 | 550 |
=head2 Bridges |
525 | 551 |
|
526 |
-Bridge routes can be used to share code with multiple nested routes, because |
|
527 |
-unlike normal nested routes, they always match and result in additional |
|
528 |
-dispatch cycles. |
|
552 |
+Bridge routes created with the method L<Mojolicious::Routes::Route/"bridge"> |
|
553 |
+can be used to share code with multiple nested routes, because unlike normal |
|
554 |
+nested routes, they always match and result in additional dispatch cycles. |
|
529 | 555 |
|
530 | 556 |
# /foo -> undef |
531 | 557 |
# /foo/bar -> {controller => 'foo', action => 'baz'} |
... | ... |
@@ -551,11 +577,36 @@ be broken, this makes bridges a very powerful tool for authentication. |
551 | 577 |
}); |
552 | 578 |
$foo->route('/bar')->to(controller => 'foo', action => 'bar'); |
553 | 579 |
|
580 |
+Broken dispatch chains can be continued by calling the method |
|
581 |
+L<Mojolicious::Controller/"continue">, this allows for example non-blocking |
|
582 |
+operations to finish before reaching the next dispatch cycle. |
|
583 |
+ |
|
584 |
+ # /foo -> undef |
|
585 |
+ # /foo/bar -> {cb => sub {...}} |
|
586 |
+ # -> {controller => 'foo', action => 'bar'} |
|
587 |
+ my $foo = $r->bridge('/foo')->to(cb => sub { |
|
588 |
+ my $self = shift; |
|
589 |
+ |
|
590 |
+ # Wait 3 seconds and then give visitors a 50% chance to continue |
|
591 |
+ Mojo::IOLoop->timer(3 => sub { |
|
592 |
+ |
|
593 |
+ # Loser |
|
594 |
+ return $self->render(text => 'No luck.') unless int rand 2; |
|
595 |
+ |
|
596 |
+ # Winner |
|
597 |
+ $self->continue; |
|
598 |
+ }); |
|
599 |
+ |
|
600 |
+ return undef; |
|
601 |
+ }); |
|
602 |
+ $foo->route('/bar')->to(controller => 'foo', action => 'bar'); |
|
603 |
+ |
|
554 | 604 |
=head2 More convenient routes |
555 | 605 |
|
556 | 606 |
From the tutorial you should already know L<Mojolicious::Lite> routes, which |
557 | 607 |
are in fact just a small convenience layer around everything described above |
558 |
-and also part of the normal router. |
|
608 |
+and accessible through methods like L<Mojolicious::Routes::Route/"get"> as |
|
609 |
+part of the normal router. |
|
559 | 610 |
|
560 | 611 |
# POST /foo -> {controller => 'foo', action => 'abc'} |
561 | 612 |
$r->post('/foo')->to(controller => 'foo', action => 'abc'); |
... | ... |
@@ -570,11 +621,13 @@ and also part of the normal router. |
570 | 621 |
# * /yada.json -> {controller => 'foo', action => 'yada', format => 'json'} |
571 | 622 |
$r->any('/yada' => [format => [qw(txt json)]])->to('foo#yada'); |
572 | 623 |
|
573 |
- # GET /foo/bar -> {controller => 'foo', action => 'bar'} |
|
574 |
- # PUT /foo/baz -> {controller => 'foo', action => 'baz'} |
|
624 |
+ # GET /foo/bar -> {controller => 'foo', action => 'bar'} |
|
625 |
+ # PUT /foo/baz -> {controller => 'foo', action => 'baz'} |
|
626 |
+ # PATCH /foo -> {controller => 'foo', action => 'yada'} |
|
575 | 627 |
my $foo = $r->any('/foo')->to('foo#'); |
576 | 628 |
$foo->get('/bar')->to('#bar'); |
577 | 629 |
$foo->put('/baz')->to('#baz'); |
630 |
+ $foo->patch->to('#yada'); |
|
578 | 631 |
|
579 | 632 |
This makes the process of growing your L<Mojolicious::Lite> prototypes into |
580 | 633 |
full L<Mojolicious> applications very straightforward. |
... | ... |
@@ -585,7 +638,8 @@ full L<Mojolicious> applications very straightforward. |
585 | 638 |
$self->render(text => 'Just like a Mojolicious::Lite action.'); |
586 | 639 |
}); |
587 | 640 |
|
588 |
-Even the more abstract concepts are available. |
|
641 |
+Even the more abstract concepts are available with methods like |
|
642 |
+L<Mojolicious::Routes::Route/"under">. |
|
589 | 643 |
|
590 | 644 |
# GET /yada |
591 | 645 |
# POST /yada |
... | ... |
@@ -602,8 +656,9 @@ Even the more abstract concepts are available. |
602 | 656 |
=head2 Hooks |
603 | 657 |
|
604 | 658 |
Hooks operate outside the routing system and allow you to extend |
605 |
-L<Mojolicious> itself by sharing code with all requests indiscriminately, |
|
606 |
-which makes them a very powerful tool especially for plugins. |
|
659 |
+the framework itself by sharing code with all requests indiscriminately |
|
660 |
+through L<Mojolicious/"hook">, which makes them a very powerful tool |
|
661 |
+especially for plugins. |
|
607 | 662 |
|
608 | 663 |
# Application |
609 | 664 |
package MyApp; |
... | ... |
@@ -614,9 +669,9 @@ which makes them a very powerful tool especially for plugins. |
614 | 669 |
|
615 | 670 |
# Check all requests for a "/test" prefix |
616 | 671 |
$self->hook(before_dispatch => sub { |
617 |
- my $self = shift; |
|
618 |
- $self->render(text => 'This request did not reach the router.') |
|
619 |
- if $self->req->url->path->contains('/test'); |
|
672 |
+ my $c = shift; |
|
673 |
+ $c->render(text => 'This request did not reach the router.') |
|
674 |
+ if $c->req->url->path->contains('/test'); |
|
620 | 675 |
}); |
621 | 676 |
|
622 | 677 |
# These will not be reached if the hook above renders a response |
... | ... |
@@ -627,29 +682,53 @@ which makes them a very powerful tool especially for plugins. |
627 | 682 |
|
628 | 683 |
1; |
629 | 684 |
|
630 |
-Post-processing tasks such as setting additional response headers are a very |
|
631 |
-common use. |
|
685 |
+Post-processing the response to set additional headers is a very common use. |
|
632 | 686 |
|
633 | 687 |
# Make sure static files are cached |
634 | 688 |
$self->hook(after_static => sub { |
635 |
- my $self = shift; |
|
636 |
- $self->res->headers->cache_control('max-age=3600, must-revalidate'); |
|
689 |
+ my $c = shift; |
|
690 |
+ $c->res->headers->cache_control('max-age=3600, must-revalidate'); |
|
637 | 691 |
}); |
638 | 692 |
|
639 |
-Same for monitoring tasks. |
|
693 |
+Same for pre-processing the request. |
|
694 |
+ |
|
695 |
+ # Allow "_method" query parameter to override request method |
|
696 |
+ $self->hook(before_dispatch => sub { |
|
697 |
+ my $c = shift; |
|
698 |
+ return unless my $method = $c->req->url->query->param('_method'); |
|
699 |
+ $c->req->method($method); |
|
700 |
+ }); |
|
701 |
+ |
|
702 |
+Or more advanced extensions to add monitoring to your application. |
|
640 | 703 |
|
641 | 704 |
# Forward exceptions to a web service |
642 | 705 |
$self->hook(after_dispatch => sub { |
643 |
- my $self = shift; |
|
644 |
- return unless my $e = $self->stash('exception'); |
|
645 |
- $self->ua->post('https://example.com/bugs' => form => {exception => $e}); |
|
706 |
+ my $c = shift; |
|
707 |
+ return unless my $e = $c->stash('exception'); |
|
708 |
+ $c->ua->post('https://example.com/bugs' => form => {exception => $e}); |
|
646 | 709 |
}); |
647 | 710 |
|
648 |
-For a full list of available hooks see L<Mojolicious/"hook">. |
|
711 |
+You can even extend much of the core functionality. |
|
712 |
+ |
|
713 |
+ # Make controller object available to actions as $_ |
|
714 |
+ $self->hook(around_action => sub { |
|
715 |
+ my ($next, $c, $action, $last) = @_; |
|
716 |
+ local $_ = $c; |
|
717 |
+ return $next->(); |
|
718 |
+ }); |
|
719 |
+ |
|
720 |
+ # Pass route name as argument to actions |
|
721 |
+ $self->hook(around_action => sub { |
|
722 |
+ my ($next, $c, $action, $last) = @_; |
|
723 |
+ return $c->$action($c->current_route); |
|
724 |
+ }); |
|
725 |
+ |
|
726 |
+For a full list of available hooks see L<Mojolicious/"HOOKS">. |
|
649 | 727 |
|
650 | 728 |
=head2 Shortcuts |
651 | 729 |
|
652 |
-You can also add your own shortcuts to make route generation more expressive. |
|
730 |
+You can also add your own shortcuts with L<Mojolicious::Routes/"add_shortcut"> |
|
731 |
+to make route generation more expressive. |
|
653 | 732 |
|
654 | 733 |
# Simple "resource" shortcut |
655 | 734 |
$r->add_shortcut(resource => sub { |
... | ... |
@@ -664,11 +743,19 @@ You can also add your own shortcuts to make route generation more expressive. |
664 | 743 |
# Handle GET requests |
665 | 744 |
$resource->get->to('#show')->name("show_$name"); |
666 | 745 |
|
746 |
+ # Handle OPTIONS requests |
|
747 |
+ $resource->options(sub { |
|
748 |
+ my $self = shift; |
|
749 |
+ $self->res->headers->allow('POST, GET, OPTIONS'); |
|
750 |
+ $self->render(data => '', status => 204); |
|
751 |
+ }); |
|
752 |
+ |
|
667 | 753 |
return $resource; |
668 | 754 |
}); |
669 | 755 |
|
670 |
- # POST /user -> {controller => 'user', action => 'create'} |
|
671 |
- # GET /user -> {controller => 'user', action => 'show'} |
|
756 |
+ # POST /user -> {controller => 'user', action => 'create'} |
|
757 |
+ # GET /user -> {controller => 'user', action => 'show'} |
|
758 |
+ # OPTIONS /user |
|
672 | 759 |
$r->resource('user'); |
673 | 760 |
|
674 | 761 |
Shortcuts can lead to anything, routes, bridges or maybe even both. And watch |
... | ... |
@@ -698,8 +785,9 @@ unescaped and decoded from bytes to characters. |
698 | 785 |
=head2 Rearranging routes |
699 | 786 |
|
700 | 787 |
Until the first request has been handled, all routes can still be moved around |
701 |
-or even removed. Especially for rearranging routes created by plugins this can |
|
702 |
-be very useful. |
|
788 |
+or even removed with methods like L<Mojolicious::Routes::Route/"add_child"> |
|
789 |
+and L<Mojolicious::Routes::Route/"remove">. Especially for rearranging routes |
|
790 |
+created by plugins this can be very useful. |
|
703 | 791 |
|
704 | 792 |
# GET /example/show -> {controller => 'example', action => 'show'} |
705 | 793 |
my $show = $r->get('/show')->to('example#show'); |
... | ... |
@@ -709,6 +797,8 @@ be very useful. |
709 | 797 |
$r->get('/secrets/show')->to('secrets#show')->name('show_secrets'); |
710 | 798 |
$r->find('show_secrets')->remove; |
711 | 799 |
|
800 |
+To find routes by their name you can use L<Mojolicious::Routes::Route/"find">. |
|
801 |
+ |
|
712 | 802 |
=head2 Conditions |
713 | 803 |
|
714 | 804 |
Sometimes you might need a little more power, for example to check the |
... | ... |
@@ -756,7 +846,7 @@ You can also package your conditions as reusable plugins. |
756 | 846 |
$app->routes->add_condition(werewolf => sub { |
757 | 847 |
my ($route, $c, $captures, $days) = @_; |
758 | 848 |
|
759 |
- # Keep the werewolfs out! |
|
849 |
+ # Keep the werewolves out! |
|
760 | 850 |
return undef if abs(14 - (phase(time))[2]) > ($days / 2); |
761 | 851 |
|
762 | 852 |
# It's ok, no werewolf |
... | ... |
@@ -4,7 +4,7 @@ use Mojo::Base 'Mojolicious'; |
4 | 4 |
# "Bender: Bite my shiny metal ass!" |
5 | 5 |
use File::Basename qw(basename dirname); |
6 | 6 |
use File::Spec::Functions 'catdir'; |
7 |
-use Mojo::UserAgent; |
|
7 |
+use Mojo::UserAgent::Server; |
|
8 | 8 |
use Mojo::Util 'monkey_patch'; |
9 | 9 |
|
10 | 10 |
sub import { |
... | ... |
@@ -20,17 +20,14 @@ sub import { |
20 | 20 |
my $caller = caller; |
21 | 21 |
no strict 'refs'; |
22 | 22 |
push @{"${caller}::ISA"}, 'Mojo'; |
23 |
- my $app = shift->new; |
|
24 | 23 |
|
25 | 24 |
# Generate moniker based on filename |
26 | 25 |
my $moniker = basename $ENV{MOJO_EXE}; |
27 | 26 |
$moniker =~ s/\.(?:pl|pm|t)$//i; |
28 |
- $app->moniker($moniker); |
|
27 |
+ my $app = shift->new(moniker => $moniker); |
|
29 | 28 |
|
30 | 29 |
# Initialize routes without namespaces |
31 | 30 |
my $routes = $app->routes->namespaces([]); |
32 |
- |
|
33 |
- # Default static and template class |
|
34 | 31 |
$app->static->classes->[0] = $app->renderer->classes->[0] = $caller; |
35 | 32 |
|
36 | 33 |
# The Mojolicious::Lite DSL |
... | ... |
@@ -42,8 +39,8 @@ sub import { |
42 | 39 |
for qw(new app); |
43 | 40 |
monkey_patch $caller, del => sub { $routes->delete(@_) }; |
44 | 41 |
monkey_patch $caller, group => sub (&) { |
45 |
- my $old = $root; |
|
46 |
- $_[0]->($root = $routes); |
|
42 |
+ (my $old, $root) = ($root, $routes); |
|
43 |
+ shift->(); |
|
47 | 44 |
($routes, $root) = ($root, $old); |
48 | 45 |
}; |
49 | 46 |
monkey_patch $caller, |
... | ... |
@@ -53,7 +50,7 @@ sub import { |
53 | 50 |
under => sub { $routes = $root->under(@_) }; |
54 | 51 |
|
55 | 52 |
# Make sure there's a default application for testing |
56 |
- Mojo::UserAgent->app($app) unless Mojo::UserAgent->app; |
|
53 |
+ Mojo::UserAgent::Server->app($app) unless Mojo::UserAgent::Server->app; |
|
57 | 54 |
|
58 | 55 |
# Lite apps are strict! |
59 | 56 |
Mojo::Base->import(-strict); |
... | ... |
@@ -112,7 +109,7 @@ featured web application. |
112 | 109 |
|
113 | 110 |
There is also a helper command to generate a small example application. |
114 | 111 |
|
115 |
- $ mojo generate lite_app |
|
112 |
+ $ mojo generate lite_app myapp.pl |
|
116 | 113 |
|
117 | 114 |
=head2 Commands |
118 | 115 |
|
... | ... |
@@ -147,6 +144,9 @@ every change. |
147 | 144 |
$ morbo myapp.pl |
148 | 145 |
Server available at http://127.0.0.1:3000. |
149 | 146 |
|
147 |
+For more information about how to deploy your application see also |
|
148 |
+L<Mojolicious::Guides::Cookbook/"DEPLOYMENT">. |
|
149 |
+ |
|
150 | 150 |
=head2 Routes |
151 | 151 |
|
152 | 152 |
Routes are basically just fancy paths that can contain different kinds of |
... | ... |
@@ -169,7 +169,7 @@ L<Mojolicious::Controller/"render">, but more about that later. |
169 | 169 |
|
170 | 170 |
=head2 GET/POST parameters |
171 | 171 |
|
172 |
-All C<GET> and C<POST> parameters sent with the request are accessible via |
|
172 |
+All GET and POST parameters sent with the request are accessible via |
|
173 | 173 |
L<Mojolicious::Controller/"param">. |
174 | 174 |
|
175 | 175 |
use Mojolicious::Lite; |
... | ... |
@@ -213,15 +213,21 @@ full access to all HTTP features and information. |
213 | 213 |
|
214 | 214 |
use Mojolicious::Lite; |
215 | 215 |
|
216 |
- # Access request and reponse information |
|
216 |
+ # Access request information |
|
217 | 217 |
get '/agent' => sub { |
218 | 218 |
my $self = shift; |
219 | 219 |
my $host = $self->req->url->to_abs->host; |
220 | 220 |
my $ua = $self->req->headers->user_agent; |
221 |
- $self->res->headers->header('X-Bender' => 'Bite my shiny metal ass!'); |
|
222 | 221 |
$self->render(text => "Request by $ua reached $host."); |
223 | 222 |
}; |
224 | 223 |
|
224 |
+ # Echo the request body and send custom header with response |
|
225 |
+ get '/echo' => sub { |
|
226 |
+ my $self = shift; |
|
227 |
+ $self->res->headers->header('X-Bender' => 'Bite my shiny metal ass!'); |
|
228 |
+ $self->render(data => $self->req->body); |
|
229 |
+ }; |
|
230 |
+ |
|
225 | 231 |
app->start; |
226 | 232 |
|
227 | 233 |
=head2 Route names |
... | ... |
@@ -280,6 +286,9 @@ L<Mojolicious::Plugin::DefaultHelpers/"content">. |
280 | 286 |
<body><%= content %></body> |
281 | 287 |
</html> |
282 | 288 |
|
289 |
+The stash or helpers like L<Mojolicious::Plugin::DefaultHelpers/"title"> can |
|
290 |
+be used to pass additional data to the layout. |
|
291 |
+ |
|
283 | 292 |
=head2 Blocks |
284 | 293 |
|
285 | 294 |
Template blocks can be used like normal Perl functions and are always |
... | ... |
@@ -464,7 +473,8 @@ Routes can be restricted to specific request methods with different keywords. |
464 | 473 |
|
465 | 474 |
=head2 Optional placeholders |
466 | 475 |
|
467 |
-Routes allow default values to make placeholders optional. |
|
476 |
+All placeholders require a value, but by assigning them default values you can |
|
477 |
+make capturing optional. |
|
468 | 478 |
|
469 | 479 |
use Mojolicious::Lite; |
470 | 480 |
|
... | ... |
@@ -520,8 +530,8 @@ is fine though. |
520 | 530 |
=head2 Under |
521 | 531 |
|
522 | 532 |
Authentication and code shared between multiple routes can be realized easily |
523 |
-with bridge routes generated by the C<under> statement. All following routes |
|
524 |
-are only evaluated if the callback returned a true value. |
|
533 |
+with bridge routes generated by the L</"under"> statement. All following |
|
534 |
+routes are only evaluated if the callback returned a true value. |
|
525 | 535 |
|
526 | 536 |
use Mojolicious::Lite; |
527 | 537 |
|
... | ... |
@@ -550,7 +560,7 @@ are only evaluated if the callback returned a true value. |
550 | 560 |
@@ index.html.ep |
551 | 561 |
Hi Bender. |
552 | 562 |
|
553 |
-Prefixing multiple routes is another good use for C<under>. |
|
563 |
+Prefixing multiple routes is another good use for L</"under">. |
|
554 | 564 |
|
555 | 565 |
use Mojolicious::Lite; |
556 | 566 |
|
... | ... |
@@ -564,15 +574,15 @@ Prefixing multiple routes is another good use for C<under>. |
564 | 574 |
get '/baz' => {text => 'foo baz'}; |
565 | 575 |
|
566 | 576 |
# / (reset) |
567 |
- under '/' => {message => 'whatever'}; |
|
577 |
+ under '/' => {msg => 'whatever'}; |
|
568 | 578 |
|
569 | 579 |
# /bar |
570 |
- get '/bar' => {inline => '<%= $message %> works'}; |
|
580 |
+ get '/bar' => {inline => '<%= $msg %> works'}; |
|
571 | 581 |
|
572 | 582 |
app->start; |
573 | 583 |
|
574 |
-You can also C<group> related routes, which allows nesting of multiple |
|
575 |
-C<under> statements. |
|
584 |
+You can also L</"group"> related routes, which allows nesting of multiple |
|
585 |
+L</"under"> statements. |
|
576 | 586 |
|
577 | 587 |
use Mojolicious::Lite; |
578 | 588 |
|
... | ... |
@@ -830,8 +840,8 @@ temporary file. |
830 | 840 |
</body> |
831 | 841 |
</html> |
832 | 842 |
|
833 |
-To protect you from excessively large files there is also a limit of C<5MB> by |
|
834 |
-default, which you can tweak with the MOJO_MAX_MESSAGE_SIZE environment |
|
843 |
+To protect you from excessively large files there is also a limit of C<10MB> |
|
844 |
+by default, which you can tweak with the MOJO_MAX_MESSAGE_SIZE environment |
|
835 | 845 |
variable. |
836 | 846 |
|
837 | 847 |
# Increase limit to 1GB |
... | ... |
@@ -839,21 +849,51 @@ variable. |
839 | 849 |
|
840 | 850 |
=head2 User agent |
841 | 851 |
|
842 |
-With L<Mojolicious::Controller/"ua"> there's a full featured HTTP and |
|
852 |
+With L<Mojo::UserAgent>, which is available through the helper |
|
853 |
+L<Mojolicious::Plugin::DefaultHelpers/"ua">, there's a full featured HTTP and |
|
843 | 854 |
WebSocket user agent built right in. Especially in combination with |
844 | 855 |
L<Mojo::JSON> and L<Mojo::DOM> this can be a very powerful tool. |
845 | 856 |
|
846 | 857 |
use Mojolicious::Lite; |
847 | 858 |
|
859 |
+ # Blocking |
|
848 | 860 |
get '/headers' => sub { |
849 | 861 |
my $self = shift; |
850 | 862 |
my $url = $self->param('url') || 'http://mojolicio.us'; |
851 | 863 |
my $dom = $self->ua->get($url)->res->dom; |
852 |
- $self->render(json => [$dom->find('h1, h2, h3')->pluck('text')->each]); |
|
864 |
+ $self->render(json => [$dom->find('h1, h2, h3')->text->each]); |
|
865 |
+ }; |
|
866 |
+ |
|
867 |
+ # Non-blocking |
|
868 |
+ get '/title' => sub { |
|
869 |
+ my $self = shift; |
|
870 |
+ $self->ua->get('mojolicio.us' => sub { |
|
871 |
+ my ($ua, $tx) = @_; |
|
872 |
+ $self->render(data => $tx->res->dom->at('title')->text); |
|
873 |
+ }); |
|
874 |
+ }; |
|
875 |
+ |
|
876 |
+ # Parallel non-blocking |
|
877 |
+ get '/titles' => sub { |
|
878 |
+ my $self = shift; |
|
879 |
+ my $delay = Mojo::IOLoop->delay(sub { |
|
880 |
+ my ($delay, @titles) = @_; |
|
881 |
+ $self->render(json => \@titles); |
|
882 |
+ }); |
|
883 |
+ for my $url ('http://mojolicio.us', 'https://metacpan.org') { |
|
884 |
+ my $end = $delay->begin(0); |
|
885 |
+ $self->ua->get($url => sub { |
|
886 |
+ my ($ua, $tx) = @_; |
|
887 |
+ $end->($tx->res->dom->html->head->title->text); |
|
888 |
+ }); |
|
889 |
+ } |
|
853 | 890 |
}; |
854 | 891 |
|
855 | 892 |
app->start; |
856 | 893 |
|
894 |
+For more information about the user agent see also |
|
895 |
+L<Mojolicious::Guides::Cookbook/"USER AGENT">. |
|
896 |
+ |
|
857 | 897 |
=head2 WebSockets |
858 | 898 |
|
859 | 899 |
WebSocket applications have never been this simple before. Just receive |
... | ... |
@@ -901,14 +941,18 @@ L<Mojolicious::Guides::Cookbook/"REAL-TIME WEB">. |
901 | 941 |
|
902 | 942 |
You can use the L<Mojo::Log> object from L<Mojo/"log"> to portably collect |
903 | 943 |
debug messages and automatically disable them later in a production setup by |
904 |
-changing the L<Mojolicious> operating mode. |
|
944 |
+changing the L<Mojolicious> operating mode, which can also be retrieved from |
|
945 |
+the attribute L<Mojolicious/"mode">. |
|
905 | 946 |
|
906 | 947 |
use Mojolicious::Lite; |
907 | 948 |
|
949 |
+ # Prepare mode specific message during startup |
|
950 |
+ my $msg = app->mode eq 'development' ? 'Development!' : 'Something else!'; |
|
951 |
+ |
|
908 | 952 |
get '/' => sub { |
909 | 953 |
my $self = shift; |
910 |
- $self->app->log->debug('Rendering "Hello World!" message.'); |
|
911 |
- $self->render(text => 'Hello World!'); |
|
954 |
+ $self->app->log->debug('Rendering mode specific message.'); |
|
955 |
+ $self->render(text => $msg); |
|
912 | 956 |
}; |
913 | 957 |
|
914 | 958 |
app->log->debug('Starting application.'); |
... | ... |
@@ -926,7 +970,7 @@ C<log> directory exists. |
926 | 970 |
|
927 | 971 |
$ mkdir log |
928 | 972 |
|
929 |
-Mode changes also affects a few other aspects of the framework, such as mode |
|
973 |
+Mode changes also affect a few other aspects of the framework, such as mode |
|
930 | 974 |
specific C<exception> and C<not_found> templates. |
931 | 975 |
|
932 | 976 |
=head2 Testing |
... | ... |
@@ -980,13 +1024,13 @@ The L<Mojolicious::Lite> application. |
980 | 1024 |
my $route = del '/:foo' => sub {...}; |
981 | 1025 |
|
982 | 1026 |
Generate route with L<Mojolicious::Routes::Route/"delete">, matching only |
983 |
-C<DELETE> requests. See also the tutorial above for more argument variations. |
|
1027 |
+DELETE requests. See also the tutorial above for more argument variations. |
|
984 | 1028 |
|
985 | 1029 |
=head2 get |
986 | 1030 |
|
987 | 1031 |
my $route = get '/:foo' => sub {...}; |
988 | 1032 |
|
989 |
-Generate route with L<Mojolicious::Routes::Route/"get">, matching only C<GET> |
|
1033 |
+Generate route with L<Mojolicious::Routes::Route/"get">, matching only GET |
|
990 | 1034 |
requests. See also the tutorial above for more argument variations. |
991 | 1035 |
|
992 | 1036 |
=head2 group |
... | ... |
@@ -1012,7 +1056,7 @@ Share code with L<Mojolicious/"hook">. |
1012 | 1056 |
my $route = options '/:foo' => sub {...}; |
1013 | 1057 |
|
1014 | 1058 |
Generate route with L<Mojolicious::Routes::Route/"options">, matching only |
1015 |
-C<OPTIONS> requests. See also the tutorial above for more argument |
|
1059 |
+OPTIONS requests. See also the tutorial above for more argument |
|
1016 | 1060 |
variations. |
1017 | 1061 |
|
1018 | 1062 |
=head2 patch |
... | ... |
@@ -1020,7 +1064,7 @@ variations. |
1020 | 1064 |
my $route = patch '/:foo' => sub {...}; |
1021 | 1065 |
|
1022 | 1066 |
Generate route with L<Mojolicious::Routes::Route/"patch">, matching only |
1023 |
-C<PATCH> requests. See also the tutorial above for more argument variations. |
|
1067 |
+PATCH requests. See also the tutorial above for more argument variations. |
|
1024 | 1068 |
|
1025 | 1069 |
=head2 plugin |
1026 | 1070 |
|
... | ... |
@@ -1033,19 +1077,19 @@ Load a plugin with L<Mojolicious/"plugin">. |
1033 | 1077 |
my $route = post '/:foo' => sub {...}; |
1034 | 1078 |
|
1035 | 1079 |
Generate route with L<Mojolicious::Routes::Route/"post">, matching only |
1036 |
-C<POST> requests. See also the tutorial above for more argument variations. |
|
1080 |
+POST requests. See also the tutorial above for more argument variations. |
|
1037 | 1081 |
|
1038 | 1082 |
=head2 put |
1039 | 1083 |
|
1040 | 1084 |
my $route = put '/:foo' => sub {...}; |
1041 | 1085 |
|
1042 |
-Generate route with L<Mojolicious::Routes::Route/"put">, matching only C<PUT> |
|
1086 |
+Generate route with L<Mojolicious::Routes::Route/"put">, matching only PUT |
|
1043 | 1087 |
requests. See also the tutorial above for more argument variations. |
1044 | 1088 |
|
1045 | 1089 |
=head2 under |
1046 | 1090 |
|
1047 |
- my $route = under sub {...}; |
|
1048 |
- my $route = under '/:foo'; |
|
1091 |
+ my $bridge = under sub {...}; |
|
1092 |
+ my $bridge = under '/:foo'; |
|
1049 | 1093 |
|
1050 | 1094 |
Generate bridge route with L<Mojolicious::Routes::Route/"under">, to which all |
1051 | 1095 |
following routes are automatically appended. See also the tutorial above for |
... | ... |
@@ -7,13 +7,15 @@ sub register { croak 'Method "register" not implemented by subclass' } |
7 | 7 |
|
8 | 8 |
1; |
9 | 9 |
|
10 |
+=encoding utf8 |
|
11 |
+ |
|
10 | 12 |
=head1 NAME |
11 | 13 |
|
12 | 14 |
Mojolicious::Plugin - Plugin base class |
13 | 15 |
|
14 | 16 |
=head1 SYNOPSIS |
15 | 17 |
|
16 |
- # Camel case plugin name |
|
18 |
+ # CamelCase plugin name |
|
17 | 19 |
package Mojolicious::Plugin::MyPlugin; |
18 | 20 |
use Mojo::Base 'Mojolicious::Plugin'; |
19 | 21 |
|
... | ... |
@@ -13,6 +13,8 @@ sub register { |
13 | 13 |
|
14 | 14 |
1; |
15 | 15 |
|
16 |
+=encoding utf8 |
|
17 |
+ |
|
16 | 18 |
=head1 NAME |
17 | 19 |
|
18 | 20 |
Mojolicious::Plugin::Charset - Charset plugin |
... | ... |
@@ -53,8 +55,8 @@ L<Mojolicious::Plugin> and implements the following new ones. |
53 | 55 |
|
54 | 56 |
$plugin->register(Mojolicious->new, {charset => 'Shift_JIS'}); |
55 | 57 |
|
56 |
-Register C<before_dispatch> hook in L<Mojolicious> application and change a |
|
57 |
-few defaults. |
|
58 |
+Register hook L<Mojolicious/"before_dispatch"> in application and change a few |
|
59 |
+defaults. |
|
58 | 60 |
|
59 | 61 |
=head1 SEE ALSO |
60 | 62 |
|
... | ... |
@@ -14,8 +14,9 @@ sub parse { |
14 | 14 |
my ($self, $content, $file, $conf, $app) = @_; |
15 | 15 |
|
16 | 16 |
# Run Perl code |
17 |
- my $config = eval 'package Mojolicious::Plugin::Config::Sandbox;' |
|
18 |
- . "no warnings; sub app { \$app }; use Mojo::Base -strict; $content"; |
|
17 |
+ my $config |
|
18 |
+ = eval 'package Mojolicious::Plugin::Config::Sandbox; no warnings;' |
|
19 |
+ . "sub app; local *app = sub { \$app }; use Mojo::Base -strict; $content"; |
|
19 | 20 |
die qq{Couldn't load configuration from file "$file": $@} if !$config && $@; |
20 | 21 |
die qq{Config file "$file" did not return a hash reference.\n} |
21 | 22 |
unless ref $config eq 'HASH'; |
... | ... |
@@ -58,6 +59,8 @@ sub register { |
58 | 59 |
|
59 | 60 |
1; |
60 | 61 |
|
62 |
+=encoding utf8 |
|
63 |
+ |
|
61 | 64 |
=head1 NAME |
62 | 65 |
|
63 | 66 |
Mojolicious::Plugin::Config - Perl-ish configuration plugin |
... | ... |
@@ -137,7 +140,7 @@ L<Mojolicious::Plugin> and implements the following new ones. |
137 | 140 |
|
138 | 141 |
$plugin->load($file, $conf, $app); |
139 | 142 |
|
140 |
-Loads configuration file and passes the content to C<parse>. |
|
143 |
+Loads configuration file and passes the content to L</"parse">. |
|
141 | 144 |
|
142 | 145 |
sub load { |
143 | 146 |
my ($self, $file, $conf, $app) = @_; |
... | ... |
@@ -1,14 +1,14 @@ |
1 | 1 |
package Mojolicious::Plugin::DefaultHelpers; |
2 | 2 |
use Mojo::Base 'Mojolicious::Plugin'; |
3 | 3 |
|
4 |
-use Data::Dumper (); |
|
5 | 4 |
use Mojo::ByteStream; |
5 |
+use Mojo::Util 'dumper'; |
|
6 | 6 |
|
7 | 7 |
sub register { |
8 | 8 |
my ($self, $app) = @_; |
9 | 9 |
|
10 | 10 |
# Controller alias helpers |
11 |
- for my $name (qw(app flash param stash session url_for)) { |
|
11 |
+ for my $name (qw(app flash param stash session url_for validation)) { |
|
12 | 12 |
$app->helper($name => sub { shift->$name(@_) }); |
13 | 13 |
} |
14 | 14 |
|
... | ... |
@@ -26,13 +26,13 @@ sub register { |
26 | 26 |
} |
27 | 27 |
|
28 | 28 |
$app->helper(config => sub { shift->app->config(@_) }); |
29 |
- |
|
30 | 29 |
$app->helper(content => \&_content); |
31 | 30 |
$app->helper(content_for => \&_content_for); |
32 | 31 |
$app->helper(current_route => \&_current_route); |
33 |
- $app->helper(dumper => \&_dumper); |
|
32 |
+ $app->helper(dumper => sub { shift; dumper(@_) }); |
|
34 | 33 |
$app->helper(include => \&_include); |
35 |
- $app->helper(url_with => \&_url_with); |
|
34 |
+ $app->helper(ua => sub { shift->app->ua }); |
|
35 |
+ $app->helper(url_with => \&_url_with); |
|
36 | 36 |
} |
37 | 37 |
|
38 | 38 |
sub _content { |
... | ... |
@@ -41,7 +41,7 @@ sub _content { |
41 | 41 |
|
42 | 42 |
# Set (first come) |
43 | 43 |
my $c = $self->stash->{'mojo.content'} ||= {}; |
44 |
- $c->{$name} ||= ref $content eq 'CODE' ? $content->() : $content |
|
44 |
+ $c->{$name} = defined $c->{$name} ? $c->{$name} : ref $content eq 'CODE' ? $content->() : $content |
|
45 | 45 |
if defined $content; |
46 | 46 |
|
47 | 47 |
# Get |
... | ... |
@@ -61,16 +61,10 @@ sub _current_route { |
61 | 61 |
return $endpoint->name eq shift; |
62 | 62 |
} |
63 | 63 |
|
64 |
-sub _dumper { |
|
65 |
- my $self = shift; |
|
66 |
- return Data::Dumper->new([@_])->Indent(1)->Sortkeys(1)->Terse(1)->Dump; |
|
67 |
-} |
|
68 |
- |
|
69 | 64 |
sub _include { |
70 | 65 |
my $self = shift; |
71 | 66 |
my $template = @_ % 2 ? shift : undef; |
72 |
- my $args = {@_}; |
|
73 |
- $args->{template} = $template if defined $template; |
|
67 |
+ my $args = {@_, defined $template ? (template => $template) : ()}; |
|
74 | 68 |
|
75 | 69 |
# "layout" and "extends" can't be localized |
76 | 70 |
my $layout = delete $args->{layout}; |
... | ... |
@@ -80,7 +74,7 @@ sub _include { |
80 | 74 |
my @keys = keys %$args; |
81 | 75 |
local @{$self->stash}{@keys} = @{$args}{@keys}; |
82 | 76 |
|
83 |
- return $self->render(partial => 1, layout => $layout, extend => $extends); |
|
77 |
+ return $self->render(partial => 1, layout => $layout, extends => $extends); |
|
84 | 78 |
} |
85 | 79 |
|
86 | 80 |
sub _url_with { |
... | ... |
@@ -90,6 +84,8 @@ sub _url_with { |
90 | 84 |
|
91 | 85 |
1; |
92 | 86 |
|
87 |
+=encoding utf8 |
|
88 |
+ |
|
93 | 89 |
=head1 NAME |
94 | 90 |
|
95 | 91 |
Mojolicious::Plugin::DefaultHelpers - Default helpers plugin |
... | ... |
@@ -136,7 +132,10 @@ Alias for L<Mojo/"config">. |
136 | 132 |
%= content 'bar' |
137 | 133 |
%= content |
138 | 134 |
|
139 |
-Store partial rendered content in named buffer and retrieve it. |
|
135 |
+Store partial rendered content in named buffer and retrieve it, defaults to |
|
136 |
+retrieving the named buffer C<content>, which is commonly used for the |
|
137 |
+renderers C<layout> and C<extends> features. Note that new content will be |
|
138 |
+ignored if the named buffer is already in use. |
|
140 | 139 |
|
141 | 140 |
=head2 content_for |
142 | 141 |
|
... | ... |
@@ -145,7 +144,8 @@ Store partial rendered content in named buffer and retrieve it. |
145 | 144 |
% end |
146 | 145 |
%= content_for 'foo' |
147 | 146 |
|
148 |
-Append partial rendered content to named buffer and retrieve it. |
|
147 |
+Append partial rendered content to named buffer and retrieve it. Note that |
|
148 |
+named buffers are shared with the L</"content"> helper. |
|
149 | 149 |
|
150 | 150 |
% content_for message => begin |
151 | 151 |
Hello |
... | ... |
@@ -168,14 +168,14 @@ Check or get name of current route. |
168 | 168 |
|
169 | 169 |
%= dumper {some => 'data'} |
170 | 170 |
|
171 |
-Dump a Perl data structure with L<Data::Dumper>. |
|
171 |
+Dump a Perl data structure with L<Mojo::Util/"dumper">. |
|
172 | 172 |
|
173 | 173 |
=head2 extends |
174 | 174 |
|
175 | 175 |
% extends 'blue'; |
176 | 176 |
% extends 'blue', title => 'Blue!'; |
177 | 177 |
|
178 |
-Extend a template. All additional values get merged into the C<stash>. |
|
178 |
+Extend a template. All additional values get merged into the L</"stash">. |
|
179 | 179 |
|
180 | 180 |
=head2 flash |
181 | 181 |
|
... | ... |
@@ -197,7 +197,7 @@ only available in the partial template. |
197 | 197 |
% layout 'green', title => 'Green!'; |
198 | 198 |
|
199 | 199 |
Render this template with a layout. All additional values get merged into the |
200 |
-C<stash>. |
|
200 |
+L</"stash">. |
|
201 | 201 |
|
202 | 202 |
=head2 param |
203 | 203 |
|
... | ... |
@@ -218,7 +218,7 @@ Alias for L<Mojolicious::Controller/"session">. |
218 | 218 |
|
219 | 219 |
Alias for L<Mojolicious::Controller/"stash">. |
220 | 220 |
|
221 |
- %= stash 'name' // 'Somebody' |
|
221 |
+ %= stash('name') // 'Somebody' |
|
222 | 222 |
|
223 | 223 |
=head2 title |
224 | 224 |
|
... | ... |
@@ -226,7 +226,13 @@ Alias for L<Mojolicious::Controller/"stash">. |
226 | 226 |
% title 'Welcome!', foo => 'bar'; |
227 | 227 |
%= title |
228 | 228 |
|
229 |
-Page title. All additional values get merged into the C<stash>. |
|
229 |
+Page title. All additional values get merged into the L</"stash">. |
|
230 |
+ |
|
231 |
+=head2 ua |
|
232 |
+ |
|
233 |
+ %= ua->get('mojolicio.us')->res->dom->at('title')->text |
|
234 |
+ |
|
235 |
+Alias for L<Mojo/"ua">. |
|
230 | 236 |
|
231 | 237 |
=head2 url_for |
232 | 238 |
|
... | ... |
@@ -238,11 +244,17 @@ Alias for L<Mojolicious::Controller/"url_for">. |
238 | 244 |
|
239 | 245 |
%= url_with 'named', controller => 'bar', action => 'baz' |
240 | 246 |
|
241 |
-Does the same as C<url_for>, but inherits query parameters from the current |
|
247 |
+Does the same as L</"url_for">, but inherits query parameters from the current |
|
242 | 248 |
request. |
243 | 249 |
|
244 | 250 |
%= url_with->query([page => 2]) |
245 | 251 |
|
252 |
+=head2 validation |
|
253 |
+ |
|
254 |
+ %= validation->param('foo') |
|
255 |
+ |
|
256 |
+Alias for L<Mojolicious::Controller/"validation">. |
|
257 |
+ |
|
246 | 258 |
=head1 METHODS |
247 | 259 |
|
248 | 260 |
L<Mojolicious::Plugin::DefaultHelpers> inherits all methods from |
... | ... |
@@ -64,6 +64,8 @@ sub _epl { |
64 | 64 |
|
65 | 65 |
1; |
66 | 66 |
|
67 |
+=encoding utf8 |
|
68 |
+ |
|
67 | 69 |
=head1 NAME |
68 | 70 |
|
69 | 71 |
Mojolicious::Plugin::EPLRenderer - Embedded Perl Lite renderer plugin |
... | ... |
@@ -2,8 +2,7 @@ package Mojolicious::Plugin::EPRenderer; |
2 | 2 |
use Mojo::Base 'Mojolicious::Plugin'; |
3 | 3 |
|
4 | 4 |
use Mojo::Template; |
5 |
-use Mojo::Util qw(encode md5_sum); |
|
6 |
-use Scalar::Util (); |
|
5 |
+use Mojo::Util qw(encode md5_sum monkey_patch); |
|
7 | 6 |
|
8 | 7 |
sub register { |
9 | 8 |
my ($self, $app, $conf) = @_; |
... | ... |
@@ -19,43 +18,50 @@ sub register { |
19 | 18 |
# Generate name |
20 | 19 |
my $path = $options->{inline} || $renderer->template_path($options); |
21 | 20 |
return undef unless defined $path; |
22 |
- my $id = encode 'UTF-8', join(', ', $path, sort keys %{$c->stash}); |
|
21 |
+ my @keys = sort grep {/^\w+$/} keys %{$c->stash}; |
|
22 |
+ my $id = encode 'UTF-8', join(',', $path, @keys); |
|
23 | 23 |
my $key = $options->{cache} = md5_sum $id; |
24 | 24 |
|
25 |
- # Compile helpers and stash values |
|
25 |
+ # Cache template for "epl" handler |
|
26 | 26 |
my $cache = $renderer->cache; |
27 |
- unless ($cache->get($key)) { |
|
28 |
- my $mt = Mojo::Template->new($template); |
|
27 |
+ my $mt = $cache->get($key); |
|
28 |
+ unless ($mt) { |
|
29 |
+ $mt = Mojo::Template->new($template); |
|
29 | 30 |
|
30 |
- # Be a bit more relaxed for helpers |
|
31 |
- my $prepend = 'my $self = shift; Scalar::Util::weaken $self;' |
|
32 |
- . q[no strict 'refs'; no warnings 'redefine';]; |
|
31 |
+ # Helpers (only once) |
|
32 |
+ ++$self->{helpers} and _helpers($mt->namespace, $renderer->helpers) |
|
33 |
+ unless $self->{helpers}; |
|
33 | 34 |
|
34 |
- # Helpers |
|
35 |
- $prepend .= 'my $_H = $self->app->renderer->helpers;'; |
|
36 |
- $prepend .= "sub $_; *$_ = sub { \$_H->{'$_'}->(\$self, \@_) };" |
|
37 |
- for grep {/^\w+$/} keys %{$renderer->helpers}; |
|
35 |
+ # Stash values (every time) |
|
36 |
+ my $prepend = 'my $self = shift; my $_S = $self->stash;'; |
|
37 |
+ $prepend .= " my \$$_ = \$_S->{'$_'};" for @keys; |
|
38 | 38 |
|
39 |
- # Be less relaxed for everything else |
|
40 |
- $prepend .= 'use strict;'; |
|
41 |
- |
|
42 |
- # Stash values |
|
43 |
- $prepend .= 'my $_S = $self->stash;'; |
|
44 |
- $prepend .= " my \$$_ = \$_S->{'$_'};" |
|
45 |
- for grep {/^\w+$/} keys %{$c->stash}; |
|
46 |
- |
|
47 |
- # Cache |
|
48 | 39 |
$cache->set($key => $mt->prepend($prepend . $mt->prepend)); |
49 | 40 |
} |
50 | 41 |
|
42 |
+ # Make current controller available |
|
43 |
+ no strict 'refs'; |
|
44 |
+ no warnings 'redefine'; |
|
45 |
+ local *{"@{[$mt->namespace]}::_C"} = sub {$c}; |
|
46 |
+ |
|
51 | 47 |
# Render with "epl" handler |
52 | 48 |
return $renderer->handlers->{epl}->($renderer, $c, $output, $options); |
53 | 49 |
} |
54 | 50 |
); |
55 | 51 |
} |
56 | 52 |
|
53 |
+sub _helpers { |
|
54 |
+ my ($namespace, $helpers) = @_; |
|
55 |
+ for my $name (grep {/^\w+$/} keys %$helpers) { |
|
56 |
+ monkey_patch $namespace, $name, |
|
57 |
+ sub { $helpers->{$name}->($namespace->_C, @_) }; |
|
58 |
+ } |
|
59 |
+} |
|
60 |
+ |
|
57 | 61 |
1; |
58 | 62 |
|
63 |
+=encoding utf8 |
|
64 |
+ |
|
59 | 65 |
=head1 NAME |
60 | 66 |
|
61 | 67 |
Mojolicious::Plugin::EPRenderer - Embedded Perl renderer plugin |
... | ... |
@@ -15,7 +15,7 @@ sub _check { |
15 | 15 |
my ($value, $pattern) = @_; |
16 | 16 |
return 1 |
17 | 17 |
if $value && $pattern && ref $pattern eq 'Regexp' && $value =~ $pattern; |
18 |
- return $value && defined $pattern && $pattern eq $value ? 1 : undef; |
|
18 |
+ return $value && defined $pattern && $pattern eq $value; |
|
19 | 19 |
} |
20 | 20 |
|
21 | 21 |
sub _headers { |
... | ... |
@@ -32,6 +32,8 @@ sub _headers { |
32 | 32 |
|
33 | 33 |
1; |
34 | 34 |
|
35 |
+=encoding utf8 |
|
36 |
+ |
|
35 | 37 |
=head1 NAME |
36 | 38 |
|
37 | 39 |
Mojolicious::Plugin::HeaderCondition - Header condition plugin |
... | ... |
@@ -24,7 +24,7 @@ sub render { |
24 | 24 |
|
25 | 25 |
# Application instance and helper |
26 | 26 |
my $prepend = q[my $app = shift; no strict 'refs'; no warnings 'redefine';]; |
27 |
- $prepend .= q[sub app; *app = sub { $app }; use Mojo::Base -strict;]; |
|
27 |
+ $prepend .= q[sub app; local *app = sub { $app }; use Mojo::Base -strict;]; |
|
28 | 28 |
|
29 | 29 |
# Render and encode for JSON decoding |
30 | 30 |
my $mt = Mojo::Template->new($conf->{template} || {})->name($file); |
... | ... |
@@ -34,6 +34,8 @@ sub render { |
34 | 34 |
|
35 | 35 |
1; |
36 | 36 |
|
37 |
+=encoding utf8 |
|
38 |
+ |
|
37 | 39 |
=head1 NAME |
38 | 40 |
|
39 | 41 |
Mojolicious::Plugin::JSONConfig - JSON configuration plugin |
... | ... |
@@ -99,7 +101,7 @@ L<Mojolicious::Plugin::Config> and implements the following new ones. |
99 | 101 |
|
100 | 102 |
$plugin->parse($content, $file, $conf, $app); |
101 | 103 |
|
102 |
-Process content with C<render> and parse it with L<Mojo::JSON>. |
|
104 |
+Process content with L</"render"> and parse it with L<Mojo::JSON>. |
|
103 | 105 |
|
104 | 106 |
sub parse { |
105 | 107 |
my ($self, $content, $file, $conf, $app) = @_; |
... | ... |
@@ -24,6 +24,8 @@ sub register { |
24 | 24 |
|
25 | 25 |
1; |
26 | 26 |
|
27 |
+=encoding utf8 |
|
28 |
+ |
|
27 | 29 |
=head1 NAME |
28 | 30 |
|
29 | 31 |
Mojolicious::Plugin::Mount - Application mount plugin |
... | ... |
@@ -4,13 +4,11 @@ use Mojo::Base 'Mojolicious::Plugin'; |
4 | 4 |
use Mojo::Asset::File; |
5 | 5 |
use Mojo::ByteStream 'b'; |
6 | 6 |
use Mojo::DOM; |
7 |
-use Mojo::Util qw'slurp url_escape'; |
|
7 |
+use Mojo::URL; |
|
8 |
+use Mojo::Util qw(slurp url_escape); |
|
8 | 9 |
BEGIN {eval {require Pod::Simple::HTML; import Pod::Simple::HTML}} |
9 | 10 |
BEGIN {eval {require Pod::Simple::Search; import Pod::Simple::Search}} |
10 | 11 |
|
11 |
-# Paths to search |
|
12 |
-my @PATHS = map { $_, "$_/pods" } @INC; |
|
13 |
- |
|
14 | 12 |
sub register { |
15 | 13 |
my ($self, $app, $conf) = @_; |
16 | 14 |
|
... | ... |
@@ -30,71 +28,50 @@ sub register { |
30 | 28 |
$app->helper(pod_to_html => sub { shift; b(_pod_to_html(@_)) }); |
31 | 29 |
|
32 | 30 |
# Perldoc browser |
33 |
- return if $conf->{no_perldoc}; |
|
31 |
+ return undef if $conf->{no_perldoc}; |
|
32 |
+ my $defaults = {module => 'Mojolicious/Guides', format => 'html'}; |
|
34 | 33 |
return $app->routes->any( |
35 |
- '/perldoc/*module' => {module => 'Mojolicious/Guides'} => \&_perldoc); |
|
34 |
+ '/perldoc/:module' => $defaults => [module => qr/[^.]+/] => \&_perldoc); |
|
36 | 35 |
} |
37 | 36 |
|
38 |
-sub _perldoc { |
|
39 |
- my $self = shift; |
|
40 |
- |
|
41 |
- # Find module or redirect to CPAN |
|
42 |
- my $module = $self->param('module'); |
|
43 |
- $module =~ s!/!::!g; |
|
44 |
- my $path = Pod::Simple::Search->new->find($module, @PATHS); |
|
45 |
- return $self->redirect_to("http://metacpan.org/module/$module") |
|
46 |
- unless $path && -r $path; |
|
47 |
- my $html = _pod_to_html(slurp $path); |
|
37 |
+sub _html { |
|
38 |
+ my ($self, $src) = @_; |
|
48 | 39 |
|
49 | 40 |
# Rewrite links |
50 |
- my $dom = Mojo::DOM->new("$html"); |
|
41 |
+ my $dom = Mojo::DOM->new(_pod_to_html($src)); |
|
51 | 42 |
my $perldoc = $self->url_for('/perldoc/'); |
52 |
- $dom->find('a[href]')->each( |
|
53 |
- sub { |
|
54 |
- my $attrs = shift->attrs; |
|
55 |
- $attrs->{href} =~ s!%3A%3A!/!gi |
|
56 |
- if $attrs->{href} =~ s!^http://search\.cpan\.org/perldoc\?!$perldoc!; |
|
57 |
- } |
|
58 |
- ); |
|
43 |
+ for my $e ($dom->find('a[href]')->each) { |
|
44 |
+ my $attrs = $e->attr; |
|
45 |
+ $attrs->{href} =~ s!%3A%3A!/!gi |
|
46 |
+ if $attrs->{href} =~ s!^http://search\.cpan\.org/perldoc\?!$perldoc!; |
|
47 |
+ } |
|
59 | 48 |
|
60 | 49 |
# Rewrite code blocks for syntax highlighting |
61 |
- $dom->find('pre')->each( |
|
62 |
- sub { |
|
63 |
- my $e = shift; |
|
64 |
- return if $e->all_text =~ /^\s*\$\s+/m; |
|
65 |
- my $attrs = $e->attrs; |
|
66 |
- my $class = $attrs->{class}; |
|
67 |
- $attrs->{class} = defined $class ? "$class prettyprint" : 'prettyprint'; |
|
68 |
- } |
|
69 |
- ); |
|
50 |
+ for my $e ($dom->find('pre')->each) { |
|
51 |
+ next if $e->all_text =~ /^\s*\$\s+/m; |
|
52 |
+ my $attrs = $e->attr; |
|
53 |
+ my $class = $attrs->{class}; |
|
54 |
+ $attrs->{class} = defined $class ? "$class prettyprint" : 'prettyprint'; |
|
55 |
+ } |
|
70 | 56 |
|
71 | 57 |
# Rewrite headers |
72 |
- my $url = $self->req->url->clone; |
|
58 |
+ my $toc = Mojo::URL->new->fragment('toc'); |
|
73 | 59 |
my (%anchors, @parts); |
74 |
- $dom->find('h1, h2, h3')->each( |
|
75 |
- sub { |
|
76 |
- my $e = shift; |
|
77 |
- |
|
78 |
- # Anchor and text |
|
79 |
- my $name = my $text = $e->all_text; |
|
80 |
- $name =~ s/\s+/_/g; |
|
81 |
- $name =~ s/[^\w\-]//g; |
|
82 |
- my $anchor = $name; |
|
83 |
- my $i = 1; |
|
84 |
- $anchor = $name . $i++ while $anchors{$anchor}++; |
|
85 |
- |
|
86 |
- # Rewrite |
|
87 |
- push @parts, [] if $e->type eq 'h1' || !@parts; |
|
88 |
- push @{$parts[-1]}, $text, $url->fragment($anchor)->to_abs; |
|
89 |
- $e->replace_content( |
|
90 |
- $self->link_to( |
|
91 |
- $text => $url->fragment('toc')->to_abs, |
|
92 |
- class => 'mojoscroll', |
|
93 |
- id => $anchor |
|
94 |
- ) |
|
95 |
- ); |
|
96 |
- } |
|
97 |
- ); |
|
60 |
+ for my $e ($dom->find('h1, h2, h3')->each) { |
|
61 |
+ |
|
62 |
+ # Anchor and text |
|
63 |
+ my $name = my $text = $e->all_text; |
|
64 |
+ $name =~ s/\s+/_/g; |
|
65 |
+ $name =~ s/[^\w\-]//g; |
|
66 |
+ my $anchor = $name; |
|
67 |
+ my $i = 1; |
|
68 |
+ $anchor = $name . $i++ while $anchors{$anchor}++; |
|
69 |
+ |
|
70 |
+ # Rewrite |
|
71 |
+ push @parts, [] if $e->type eq 'h1' || !@parts; |
|
72 |
+ push @{$parts[-1]}, $text, Mojo::URL->new->fragment($anchor); |
|
73 |
+ $e->replace_content($self->link_to($text => $toc, id => $anchor)); |
|
74 |
+ } |
|
98 | 75 |
|
99 | 76 |
# Try to find a title |
100 | 77 |
my $title = 'Perldoc'; |
... | ... |
@@ -104,11 +81,25 @@ sub _perldoc { |
104 | 81 |
$self->content_for(perldoc => "$dom"); |
105 | 82 |
my $template = $self->app->renderer->_bundled('perldoc'); |
106 | 83 |
$self->render(inline => $template, title => $title, parts => \@parts); |
107 |
- $self->res->headers->content_type('text/html;charset="UTF-8"'); |
|
84 |
+} |
|
85 |
+ |
|
86 |
+sub _perldoc { |
|
87 |
+ my $self = shift; |
|
88 |
+ |
|
89 |
+ # Find module or redirect to CPAN |
|
90 |
+ my $module = $self->param('module'); |
|
91 |
+ $module =~ s!/!::!g; |
|
92 |
+ my $path |
|
93 |
+ = Pod::Simple::Search->new->find($module, map { $_, "$_/pods" } @INC); |
|
94 |
+ return $self->redirect_to("http://metacpan.org/module/$module") |
|
95 |
+ unless $path && -r $path; |
|
96 |
+ |
|
97 |
+ my $src = slurp $path; |
|
98 |
+ $self->respond_to(txt => {data => $src}, html => sub { _html($self, $src) }); |
|
108 | 99 |
} |
109 | 100 |
|
110 | 101 |
sub _pod_to_html { |
111 |
- return undef unless defined(my $pod = shift); |
|
102 |
+ return '' unless defined(my $pod = shift); |
|
112 | 103 |
|
113 | 104 |
# Block |
114 | 105 |
$pod = $pod->() if ref $pod eq 'CODE'; |
... | ... |
@@ -130,6 +121,8 @@ sub _pod_to_html { |
130 | 121 |
|
131 | 122 |
1; |
132 | 123 |
|
124 |
+=encoding utf8 |
|
125 |
+ |
|
133 | 126 |
=head1 NAME |
134 | 127 |
|
135 | 128 |
Mojolicious::Plugin::PODRenderer - POD renderer plugin |
... | ... |
@@ -23,10 +23,10 @@ sub register { |
23 | 23 |
$app->helper(image => sub { _tag('img', src => shift->url_for(shift), @_) }); |
24 | 24 |
$app->helper(input_tag => sub { _input(@_) }); |
25 | 25 |
$app->helper(javascript => \&_javascript); |
26 |
+ $app->helper(label_for => \&_label_for); |
|
26 | 27 |
$app->helper(link_to => \&_link_to); |
27 | 28 |
|
28 |
- $app->helper(password_field => |
|
29 |
- sub { shift; _tag('input', name => shift, @_, type => 'password') }); |
|
29 |
+ $app->helper(password_field => \&_password_field); |
|
30 | 30 |
$app->helper(radio_button => |
31 | 31 |
sub { _input(shift, shift, value => shift, @_, type => 'radio') }); |
32 | 32 |
|
... | ... |
@@ -37,7 +37,8 @@ sub register { |
37 | 37 |
# "t" is just a shortcut for the "tag" helper |
38 | 38 |
$app->helper($_ => sub { shift; _tag(@_) }) for qw(t tag); |
39 | 39 |
|
40 |
- $app->helper(text_area => \&_text_area); |
|
40 |
+ $app->helper(tag_with_error => \&_tag_with_error); |
|
41 |
+ $app->helper(text_area => \&_text_area); |
|
41 | 42 |
} |
42 | 43 |
|
43 | 44 |
sub _form_for { |
... | ... |
@@ -46,7 +47,7 @@ sub _form_for { |
46 | 47 |
|
47 | 48 |
# POST detection |
48 | 49 |
my @post; |
49 |
- if (my $r = $self->app->routes->find($url[0])) { |
|
50 |
+ if (my $r = $self->app->routes->lookup($url[0])) { |
|
50 | 51 |
my %methods = (GET => 1, POST => 1); |
51 | 52 |
do { |
52 | 53 |
my @via = @{$r->via || []}; |
... | ... |
@@ -82,12 +83,9 @@ sub _input { |
82 | 83 |
|
83 | 84 |
# Others |
84 | 85 |
else { $attrs{value} = $values[0] } |
85 |
- |
|
86 |
- return _tag('input', name => $name, %attrs); |
|
87 | 86 |
} |
88 | 87 |
|
89 |
- # Empty tag |
|
90 |
- return _tag('input', name => $name, %attrs); |
|
88 |
+ return _validation($self, $name, 'input', %attrs, name => $name); |
|
91 | 89 |
} |
92 | 90 |
|
93 | 91 |
sub _javascript { |
... | ... |
@@ -95,8 +93,7 @@ sub _javascript { |
95 | 93 |
|
96 | 94 |
# CDATA |
97 | 95 |
my $cb = sub {''}; |
98 |
- if (ref $_[-1] eq 'CODE') { |
|
99 |
- my $old = pop; |
|
96 |
+ if (ref $_[-1] eq 'CODE' && (my $old = pop)) { |
|
100 | 97 |
$cb = sub { "//<![CDATA[\n" . $old->() . "\n//]]>" } |
101 | 98 |
} |
102 | 99 |
|
... | ... |
@@ -106,12 +103,18 @@ sub _javascript { |
106 | 103 |
return _tag('script', @_, $src ? (src => $src) : (), $cb); |
107 | 104 |
} |
108 | 105 |
|
106 |
+sub _label_for { |
|
107 |
+ my ($self, $name) = (shift, shift); |
|
108 |
+ my $content = ref $_[-1] eq 'CODE' ? pop : shift; |
|
109 |
+ return _validation($self, $name, 'label', for => $name, @_, $content); |
|
110 |
+} |
|
111 |
+ |
|
109 | 112 |
sub _link_to { |
110 | 113 |
my ($self, $content) = (shift, shift); |
111 | 114 |
my @url = ($content); |
112 | 115 |
|
113 | 116 |
# Content |
114 |
- unless (defined $_[-1] && ref $_[-1] eq 'CODE') { |
|
117 |
+ unless (ref $_[-1] eq 'CODE') { |
|
115 | 118 |
@url = (shift); |
116 | 119 |
push @_, $content; |
117 | 120 |
} |
... | ... |
@@ -122,47 +125,45 @@ sub _link_to { |
122 | 125 |
return _tag('a', href => $self->url_for(@url), @_); |
123 | 126 |
} |
124 | 127 |
|
125 |
-sub _select_field { |
|
126 |
- my ($self, $name, $options, %attrs) = (shift, shift, shift, @_); |
|
128 |
+sub _option { |
|
129 |
+ my ($values, $pair) = @_; |
|
130 |
+ $pair = [$pair => $pair] unless ref $pair eq 'ARRAY'; |
|
127 | 131 |
|
128 |
- # "option" callback |
|
129 |
- my %values = map { $_ => 1 } $self->param($name); |
|
130 |
- my $option = sub { |
|
131 |
- |
|
132 |
- # Pair |
|
133 |
- my $pair = shift; |
|
134 |
- $pair = [$pair => $pair] unless ref $pair eq 'ARRAY'; |
|
132 |
+ # Attributes |
|
133 |
+ my %attrs = (value => $pair->[1]); |
|
134 |
+ $attrs{selected} = 'selected' if exists $values->{$pair->[1]}; |
|
135 |
+ %attrs = (%attrs, @$pair[2 .. $#$pair]); |
|
135 | 136 |
|
136 |
- # Attributes |
|
137 |
- my %attrs = (value => $pair->[1]); |
|
138 |
- $attrs{selected} = 'selected' if exists $values{$pair->[1]}; |
|
139 |
- %attrs = (%attrs, @$pair[2 .. $#$pair]); |
|
137 |
+ return _tag('option', %attrs, sub { xml_escape $pair->[0] }); |
|
138 |
+} |
|
140 | 139 |
|
141 |
- return _tag('option', %attrs, sub { xml_escape $pair->[0] }); |
|
142 |
- }; |
|
140 |
+sub _password_field { |
|
141 |
+ my ($self, $name) = (shift, shift); |
|
142 |
+ return _validation($self, $name, 'input', @_, name => $name, |
|
143 |
+ type => 'password'); |
|
144 |
+} |
|
143 | 145 |
|
144 |
- # "optgroup" callback |
|
145 |
- my $optgroup = sub { |
|
146 |
+sub _select_field { |
|
147 |
+ my ($self, $name, $options, %attrs) = (shift, shift, shift, @_); |
|
146 | 148 |
|
147 |
- # Parts |
|
148 |
- my $parts = ''; |
|
149 |
- for my $group (@$options) { |
|
149 |
+ my %values = map { $_ => 1 } $self->param($name); |
|
150 | 150 |
|
151 |
- # "optgroup" tag |
|
152 |
- if (ref $group eq 'HASH') { |
|
153 |
- my ($label, $values) = each %$group; |
|
154 |
- my $content = join '', map { $option->($_) } @$values; |
|
155 |
- $parts .= _tag('optgroup', label => $label, sub {$content}); |
|
156 |
- } |
|
151 |
+ my $groups = ''; |
|
152 |
+ for my $group (@$options) { |
|
157 | 153 |
|
158 |
- # "option" tag |
|
159 |
- else { $parts .= $option->($group) } |
|
154 |
+ # "optgroup" tag |
|
155 |
+ if (ref $group eq 'HASH') { |
|
156 |
+ my ($label, $values) = each %$group; |
|
157 |
+ my $content = join '', map { _option(\%values, $_) } @$values; |
|
158 |
+ $groups .= _tag('optgroup', label => $label, sub {$content}); |
|
160 | 159 |
} |
161 | 160 |
|
162 |
- return $parts; |
|
163 |
- }; |
|
161 |
+ # "option" tag |
|
162 |
+ else { $groups .= _option(\%values, $group) } |
|
163 |
+ } |
|
164 | 164 |
|
165 |
- return _tag('select', name => $name, %attrs, $optgroup); |
|
165 |
+ return _validation($self, $name, 'select', %attrs, name => $name, |
|
166 |
+ sub {$groups}); |
|
166 | 167 |
} |
167 | 168 |
|
168 | 169 |
sub _stylesheet { |
... | ... |
@@ -170,15 +171,14 @@ sub _stylesheet { |
170 | 171 |
|
171 | 172 |
# CDATA |
172 | 173 |
my $cb; |
173 |
- if (ref $_[-1] eq 'CODE') { |
|
174 |
- my $old = pop; |
|
174 |
+ if (ref $_[-1] eq 'CODE' && (my $old = pop)) { |
|
175 | 175 |
$cb = sub { "/*<![CDATA[*/\n" . $old->() . "\n/*]]>*/" } |
176 | 176 |
} |
177 | 177 |
|
178 | 178 |
# "link" or "style" tag |
179 | 179 |
my $href = @_ % 2 ? $self->url_for(shift) : undef; |
180 | 180 |
return $href |
181 |
- ? _tag('link', rel => 'stylesheet', href => $href, media => 'screen', @_) |
|
181 |
+ ? _tag('link', rel => 'stylesheet', href => $href, @_) |
|
182 | 182 |
: _tag('style', @_, $cb); |
183 | 183 |
} |
184 | 184 |
|
... | ... |
@@ -199,39 +199,47 @@ sub _tag { |
199 | 199 |
|
200 | 200 |
# Attributes |
201 | 201 |
my %attrs = @_; |
202 |
- for my $key (sort keys %attrs) { |
|
203 |
- $tag .= qq{ $key="} . xml_escape(defined $attrs{$key} ? $attrs{$key} : '') . '"'; |
|
204 |
- } |
|
205 |
- |
|
206 |
- # End tag |
|
207 |
- if ($cb || defined $content) { |
|
208 |
- $tag .= '>' . ($cb ? $cb->() : xml_escape($content)) . "</$name>"; |
|
209 |
- } |
|
202 |
+ $tag .= qq{ $_="} . xml_escape(defined $attrs{$_} ? $attrs{$_} : '') . '"' for sort keys %attrs; |
|
210 | 203 |
|
211 | 204 |
# Empty element |
212 |
- else { $tag .= ' />' } |
|
205 |
+ unless ($cb || defined $content) { $tag .= ' />' } |
|
206 |
+ |
|
207 |
+ # End tag |
|
208 |
+ else { $tag .= '>' . ($cb ? $cb->() : xml_escape($content)) . "</$name>" } |
|
213 | 209 |
|
214 | 210 |
# Prevent escaping |
215 | 211 |
return Mojo::ByteStream->new($tag); |
216 | 212 |
} |
217 | 213 |
|
214 |
+sub _tag_with_error { |
|
215 |
+ my ($self, $tag) = (shift, shift); |
|
216 |
+ my ($content, %attrs) = (@_ % 2 ? pop : undef, @_); |
|
217 |
+ $attrs{class} .= $attrs{class} ? ' field-with-error' : 'field-with-error'; |
|
218 |
+ return _tag($tag, %attrs, defined $content ? $content : ()); |
|
219 |
+} |
|
220 |
+ |
|
218 | 221 |
sub _text_area { |
219 | 222 |
my ($self, $name) = (shift, shift); |
220 | 223 |
|
221 |
- # Content |
|
224 |
+ # Make sure content is wrapped |
|
222 | 225 |
my $cb = ref $_[-1] eq 'CODE' ? pop : sub {''}; |
223 | 226 |
my $content = @_ % 2 ? shift : undef; |
227 |
+ $cb = sub { xml_escape $content } |
|
228 |
+ if defined($content = defined $self->param($name) ? $self->param($name) : $content); |
|
224 | 229 |
|
225 |
- # Make sure content is wrapped |
|
226 |
- if (defined($content = defined $self->param($name) ? $self->param($name) : $content)) { |
|
227 |
- $cb = sub { xml_escape $content } |
|
228 |
- } |
|
230 |
+ return _validation($self, $name, 'textarea', @_, name => $name, $cb); |
|
231 |
+} |
|
229 | 232 |
|
230 |
- return _tag('textarea', name => $name, @_, $cb); |
|
233 |
+sub _validation { |
|
234 |
+ my ($self, $name) = (shift, shift); |
|
235 |
+ return _tag(@_) unless $self->validation->has_error($name); |
|
236 |
+ return $self->tag_with_error(@_); |
|
231 | 237 |
} |
232 | 238 |
|
233 | 239 |
1; |
234 | 240 |
|
241 |
+=encoding utf8 |
|
242 |
+ |
|
235 | 243 |
=head1 NAME |
236 | 244 |
|
237 | 245 |
Mojolicious::Plugin::TagHelpers - Tag helpers plugin |
... | ... |
@@ -259,6 +267,12 @@ necessary attributes always be generated automatically. |
259 | 267 |
<%= radio_button country => 'france' %> France |
260 | 268 |
<%= radio_button country => 'uk' %> UK |
261 | 269 |
|
270 |
+For fields that failed validation with L<Mojolicious::Controller/"validation"> |
|
271 |
+the C<field-with-error> class will be automatically added through the |
|
272 |
+C<tag_with_error> helper, to make styling with CSS easier. |
|
273 |
+ |
|
274 |
+ <input class="field-with-error" name="age" type="text" value="250" /> |
|
275 |
+ |
|
262 | 276 |
This is a core plugin, that means it is always enabled and its code a good |
263 | 277 |
example for learning how to build new plugins, you're welcome to fork it. |
264 | 278 |
|
... | ... |
@@ -358,8 +372,8 @@ Generate file input element. |
358 | 372 |
%= submit_button |
359 | 373 |
% end |
360 | 374 |
|
361 |
-Generate portable form for route, path or URL. For routes that allow C<POST> |
|
362 |
-but not C<GET>, a C<method> attribute will be automatically added. |
|
375 |
+Generate portable form tag for route, path or URL. For routes that allow POST |
|
376 |
+but not GET, a C<method> attribute will be automatically added. |
|
363 | 377 |
|
364 | 378 |
<form action="/path/to/login"> |
365 | 379 |
<input name="first_name" /> |
... | ... |
@@ -369,7 +383,7 @@ but not C<GET>, a C<method> attribute will be automatically added. |
369 | 383 |
<input name="first_name" /> |
370 | 384 |
<input value="Ok" type="submit" /> |
371 | 385 |
</form> |
372 |
- <form action="/login" method="POST"> |
|
386 |
+ <form action="/path/to/login" method="POST"> |
|
373 | 387 |
<input name="first_name" /> |
374 | 388 |
<input value="Ok" type="submit" /> |
375 | 389 |
</form> |
... | ... |
@@ -393,10 +407,10 @@ Generate hidden input element. |
393 | 407 |
%= image '/images/foo.png' |
394 | 408 |
%= image '/images/foo.png', alt => 'Foo' |
395 | 409 |
|
396 |
-Generate image tag. |
|
410 |
+Generate portable img tag. |
|
397 | 411 |
|
398 |
- <img src="/images/foo.png" /> |
|
399 |
- <img alt="Foo" src="/images/foo.png" /> |
|
412 |
+ <img src="/path/to/images/foo.png" /> |
|
413 |
+ <img alt="Foo" src="/path/to/images/foo.png" /> |
|
400 | 414 |
|
401 | 415 |
=head2 input_tag |
402 | 416 |
|
... | ... |
@@ -420,21 +434,43 @@ picked up and shown as default. |
420 | 434 |
|
421 | 435 |
Generate portable script tag for C<Javascript> asset. |
422 | 436 |
|
423 |
- <script src="/script.js" /> |
|
437 |
+ <script src="/path/to/script.js" /> |
|
424 | 438 |
<script><![CDATA[ |
425 | 439 |
var a = 'b'; |
426 | 440 |
]]></script> |
427 | 441 |
|
442 |
+=head2 label_for |
|
443 |
+ |
|
444 |
+ %= label_for first_name => 'First name' |
|
445 |
+ %= label_for first_name => 'First name', class => 'user' |
|
446 |
+ %= label_for first_name => begin |
|
447 |
+ First name |
|
448 |
+ % end |
|
449 |
+ %= label_for first_name => (class => 'user') => begin |
|
450 |
+ First name |
|
451 |
+ % end |
|
452 |
+ |
|
453 |
+Generate label. |
|
454 |
+ |
|
455 |
+ <label for="first_name">First name</label> |
|
456 |
+ <label class="user" for="first_name">First name</label> |
|
457 |
+ <label for="first_name"> |
|
458 |
+ First name |
|
459 |
+ </label> |
|
460 |
+ <label class="user" for="first_name"> |
|
461 |
+ First name |
|
462 |
+ </label> |
|
463 |
+ |
|
428 | 464 |
=head2 link_to |
429 | 465 |
|
430 | 466 |
%= link_to Home => 'index' |
431 |
- %= link_to Home => 'index' => {format => 'txt'} => (class => 'links') |
|
432 |
- %= link_to index => {format => 'txt'} => (class => 'links') => begin |
|
467 |
+ %= link_to Home => 'index' => {format => 'txt'} => (class => 'menu') |
|
468 |
+ %= link_to index => {format => 'txt'} => (class => 'menu') => begin |
|
433 | 469 |
Home |
434 | 470 |
% end |
435 |
- %= link_to Contact => Mojo::URL->new('mailto:sri@example.com') |
|
471 |
+ %= link_to Contact => 'mailto:sri@example.com' |
|
436 | 472 |
<%= link_to index => begin %>Home<% end %> |
437 |
- <%= link_to '/path/to/file' => begin %>File<% end %> |
|
473 |
+ <%= link_to '/file.txt' => begin %>File<% end %> |
|
438 | 474 |
<%= link_to 'http://mojolicio.us' => begin %>Mojolicious<% end %> |
439 | 475 |
<%= link_to url_for->query(foo => 'bar')->to_abs => begin %>Retry<% end %> |
440 | 476 |
|
... | ... |
@@ -442,13 +478,13 @@ Generate portable link to route, path or URL, defaults to using the |
442 | 478 |
capitalized link target as content. |
443 | 479 |
|
444 | 480 |
<a href="/path/to/index">Home</a> |
445 |
- <a class="links" href="/path/to/index.txt">Home</a> |
|
446 |
- <a class="links" href="/path/to/index.txt"> |
|
481 |
+ <a class="menu" href="/path/to/index.txt">Home</a> |
|
482 |
+ <a class="menu" href="/path/to/index.txt"> |
|
447 | 483 |
Home |
448 | 484 |
</a> |
449 | 485 |
<a href="mailto:sri@example.com">Contact</a> |
450 | 486 |
<a href="/path/to/index">Home</a> |
451 |
- <a href="/path/to/file">File</a> |
|
487 |
+ <a href="/path/to/file.txt">File</a> |
|
452 | 488 |
<a href="http://mojolicio.us">Mojolicious</a> |
453 | 489 |
<a href="http://127.0.0.1:3000/current/path?foo=bar">Retry</a> |
454 | 490 |
|
... | ... |
@@ -568,7 +604,7 @@ automatically get picked up and shown as default. |
568 | 604 |
|
569 | 605 |
Generate portable style or link tag for C<CSS> asset. |
570 | 606 |
|
571 |
- <link href="/foo.css" media="screen" rel="stylesheet" /> |
|
607 |
+ <link href="/path/to/foo.css" rel="stylesheet" /> |
|
572 | 608 |
<style><![CDATA[ |
573 | 609 |
body {color: #000} |
574 | 610 |
]]></style> |
... | ... |
@@ -587,7 +623,7 @@ Generate submit input element. |
587 | 623 |
|
588 | 624 |
%=t div => 'some & content' |
589 | 625 |
|
590 |
-Alias for C<tag>. |
|
626 |
+Alias for L</"tag">. |
|
591 | 627 |
|
592 | 628 |
<div>some & content</div> |
593 | 629 |
|
... | ... |
@@ -596,14 +632,22 @@ Alias for C<tag>. |
596 | 632 |
%= tag 'div' |
597 | 633 |
%= tag 'div', id => 'foo' |
598 | 634 |
%= tag div => 'some & content' |
599 |
- <%= tag div => begin %>some & content<% end %> |
|
635 |
+ %= tag div => (id => 'foo') => 'some & content' |
|
636 |
+ %= tag div => begin |
|
637 |
+ some & content |
|
638 |
+ % end |
|
639 |
+ <%= tag div => (id => 'foo') => begin %>some & content<% end %> |
|
600 | 640 |
|
601 | 641 |
HTML tag generator. |
602 | 642 |
|
603 | 643 |
<div /> |
604 | 644 |
<div id="foo" /> |
605 | 645 |
<div>some & content</div> |
606 |
- <div>some & content</div> |
|
646 |
+ <div id="foo">some & content</div> |
|
647 |
+ <div> |
|
648 |
+ some & content |
|
649 |
+ </div> |
|
650 |
+ <div id="foo">some & content</div> |
|
607 | 651 |
|
608 | 652 |
Very useful for reuse in more specific tag helpers. |
609 | 653 |
|
... | ... |
@@ -614,6 +658,14 @@ Very useful for reuse in more specific tag helpers. |
614 | 658 |
Results are automatically wrapped in L<Mojo::ByteStream> objects to prevent |
615 | 659 |
accidental double escaping. |
616 | 660 |
|
661 |
+=head2 tag_with_error |
|
662 |
+ |
|
663 |
+ %= tag_with_error 'input', class => 'foo' |
|
664 |
+ |
|
665 |
+Same as L</"tag">, but adds the class C<field-with-error>. |
|
666 |
+ |
|
667 |
+ <input class="foo field-with-error" /> |
|
668 |
+ |
|
617 | 669 |
=head2 tel_field |
618 | 670 |
|
619 | 671 |
%= tel_field 'work' |
... | ... |
@@ -19,9 +19,8 @@ sub emit_chain { |
19 | 19 |
my $next = $wrapper; |
20 | 20 |
$wrapper = sub { $cb->($next, @args) }; |
21 | 21 |
} |
22 |
- $wrapper->(); |
|
23 | 22 |
|
24 |
- return $self; |
|
23 |
+ !$wrapper ? return : return $wrapper->(); |
|
25 | 24 |
} |
26 | 25 |
|
27 | 26 |
sub emit_hook_reverse { |
... | ... |
@@ -56,11 +55,13 @@ sub _load { |
56 | 55 |
if (my $e = Mojo::Loader->new->load($module)) { |
57 | 56 |
ref $e ? die $e : return undef; |
58 | 57 |
} |
59 |
- return $module->isa('Mojolicious::Plugin') ? 1 : undef; |
|
58 |
+ return $module->isa('Mojolicious::Plugin'); |
|
60 | 59 |
} |
61 | 60 |
|
62 | 61 |
1; |
63 | 62 |
|
63 |
+=encoding utf8 |
|
64 |
+ |
|
64 | 65 |
=head1 NAME |
65 | 66 |
|
66 | 67 |
Mojolicious::Plugins - Plugin manager |
... | ... |
@@ -151,8 +152,8 @@ implements the following new ones. |
151 | 152 |
|
152 | 153 |
=head2 emit_chain |
153 | 154 |
|
154 |
- $plugins = $plugins->emit_chain('foo'); |
|
155 |
- $plugins = $plugins->emit_chain(foo => 123); |
|
155 |
+ $plugins->emit_chain('foo'); |
|
156 |
+ $plugins->emit_chain(foo => 123); |
|
156 | 157 |
|
157 | 158 |
Emit events as chained hooks. |
158 | 159 |
|
... | ... |
@@ -13,8 +13,15 @@ has classes => sub { ['main'] }; |
13 | 13 |
has default_format => 'html'; |
14 | 14 |
has 'default_handler'; |
15 | 15 |
has encoding => 'UTF-8'; |
16 |
-has [qw(handlers helpers)] => sub { {} }; |
|
17 |
-has paths => sub { [] }; |
|
16 |
+has handlers => sub { |
|
17 |
+ { |
|
18 |
+ data => sub { ${$_[2]} = $_[3]{data} }, |
|
19 |
+ text => sub { ${$_[2]} = $_[3]{text} }, |
|
20 |
+ json => sub { ${$_[2]} = Mojo::JSON->new->encode($_[3]{json}) } |
|
21 |
+ }; |
|
22 |
+}; |
|
23 |
+has helpers => sub { {} }; |
|
24 |
+has paths => sub { [] }; |
|
18 | 25 |
|
19 | 26 |
# Bundled templates |
20 | 27 |
my $HOME = Mojo::Home->new; |
... | ... |
@@ -22,14 +29,6 @@ $HOME->parse( |
22 | 29 |
$HOME->parse($HOME->mojo_lib_dir)->rel_dir('Mojolicious/templates')); |
23 | 30 |
my %TEMPLATES = map { $_ => slurp $HOME->rel_file($_) } @{$HOME->list_files}; |
24 | 31 |
|
25 |
-sub new { |
|
26 |
- my $self = shift->SUPER::new(@_); |
|
27 |
- $self->add_handler(data => sub { ${$_[2]} = $_[3]{data} }); |
|
28 |
- $self->add_handler(text => sub { ${$_[2]} = $_[3]{text} }); |
|
29 |
- return $self->add_handler( |
|
30 |
- json => sub { ${$_[2]} = Mojo::JSON->new->encode($_[3]{json}) }); |
|
31 |
-} |
|
32 |
- |
|
33 | 32 |
sub add_handler { shift->_add(handlers => @_) } |
34 | 33 |
sub add_helper { shift->_add(helpers => @_) } |
35 | 34 |
|
... | ... |
@@ -54,11 +53,11 @@ sub render { |
54 | 53 |
my ($self, $c, $args) = @_; |
55 | 54 |
$args ||= {}; |
56 | 55 |
|
57 |
- # Localize "extends" and "layout" |
|
58 |
- my $partial = $args->{partial}; |
|
59 |
- my $stash = $c->stash; |
|
60 |
- local $stash->{layout} = $partial ? undef : $stash->{layout}; |
|
61 |
- local $stash->{extends} = $partial ? undef : $stash->{extends}; |
|
56 |
+ # Localize "extends" and "layout" to allow argument overrides |
|
57 |
+ my $stash = $c->stash; |
|
58 |
+ local $stash->{layout} = $stash->{layout} if exists $stash->{layout}; |
|
59 |
+ local $stash->{extends} = $stash->{extends} if exists $stash->{extends}; |
|
60 |
+ delete @{$stash}{qw(layout extends)} if my $partial = $args->{partial}; |
|
62 | 61 |
|
63 | 62 |
# Merge stash and arguments |
64 | 63 |
@{$stash}{keys %$args} = values %$args; |
... | ... |
@@ -209,6 +208,8 @@ sub _render_template { |
209 | 208 |
|
210 | 209 |
1; |
211 | 210 |
|
211 |
+=encoding utf8 |
|
212 |
+ |
|
212 | 213 |
=head1 NAME |
213 | 214 |
|
214 | 215 |
Mojolicious::Renderer - Generate dynamic content |
... | ... |
@@ -269,14 +270,17 @@ detection doesn't work, like for C<inline> templates. |
269 | 270 |
my $encoding = $renderer->encoding; |
270 | 271 |
$renderer = $renderer->encoding('koi8-r'); |
271 | 272 |
|
272 |
-Will encode the content if set, defaults to C<UTF-8>. |
|
273 |
+Will encode generated content if set, defaults to C<UTF-8>. Note that many |
|
274 |
+renderers such as L<Mojolicious::Plugin::EPRenderer> also use this value to |
|
275 |
+determine if template files should be decoded before processing. |
|
273 | 276 |
|
274 | 277 |
=head2 handlers |
275 | 278 |
|
276 | 279 |
my $handlers = $renderer->handlers; |
277 | 280 |
$renderer = $renderer->handlers({epl => sub {...}}); |
278 | 281 |
|
279 |
-Registered handlers. |
|
282 |
+Registered handlers, by default only C<data>, C<text> and C<json> are already |
|
283 |
+defined. |
|
280 | 284 |
|
281 | 285 |
=head2 helpers |
282 | 286 |
|
... | ... |
@@ -300,13 +304,6 @@ Directories to look for templates in, first one has the highest precedence. |
300 | 304 |
L<Mojolicious::Renderer> inherits all methods from L<Mojo::Base> and |
301 | 305 |
implements the following new ones. |
302 | 306 |
|
303 |
-=head2 new |
|
304 |
- |
|
305 |
- my $renderer = Mojolicious::Renderer->new; |
|
306 |
- |
|
307 |
-Construct a new renderer and register C<data>, C<json> as well as C<text> |
|
308 |
-handlers. |
|
309 |
- |
|
310 | 307 |
=head2 add_handler |
311 | 308 |
|
312 | 309 |
$renderer = $renderer->add_handler(epl => sub {...}); |
... | ... |
@@ -24,6 +24,27 @@ sub auto_render { |
24 | 24 |
$c->render_maybe or $stash->{'mojo.routed'} or $c->render_not_found; |
25 | 25 |
} |
26 | 26 |
|
27 |
+sub continue { |
|
28 |
+ my ($self, $c) = @_; |
|
29 |
+ |
|
30 |
+ my $match = $c->match; |
|
31 |
+ my $stack = $match->stack; |
|
32 |
+ my $current = $match->current; |
|
33 |
+ return $self->auto_render($c) unless my $field = $stack->[$current]; |
|
34 |
+ |
|
35 |
+ # Merge captures into stash |
|
36 |
+ my @keys = keys %$field; |
|
37 |
+ my $stash = $c->stash; |
|
38 |
+ @{$stash}{@keys} = @{$stash->{'mojo.captures'}}{@keys} = values %$field; |
|
39 |
+ |
|
40 |
+ my $continue; |
|
41 |
+ my $last = !$stack->[++$current]; |
|
42 |
+ if (my $cb = $field->{cb}) { $continue = $self->_callback($c, $cb, $last) } |
|
43 |
+ else { $continue = $self->_controller($c, $field, $last) } |
|
44 |
+ $match->current($current); |
|
45 |
+ $self->continue($c) if $last || $continue; |
|
46 |
+} |
|
47 |
+ |
|
27 | 48 |
sub dispatch { |
28 | 49 |
my ($self, $c) = @_; |
29 | 50 |
|
... | ... |
@@ -59,9 +80,8 @@ sub dispatch { |
59 | 80 |
} |
60 | 81 |
} |
61 | 82 |
|
62 |
- # Dispatch |
|
63 |
- return undef unless $self->_walk($c); |
|
64 |
- $self->auto_render($c); |
|
83 |
+ return undef unless @{$c->match->stack}; |
|
84 |
+ $self->continue($c); |
|
65 | 85 |
return 1; |
66 | 86 |
} |
67 | 87 |
|
... | ... |
@@ -69,8 +89,8 @@ sub hide { push @{shift->hidden}, @_ } |
69 | 89 |
|
70 | 90 |
sub is_hidden { |
71 | 91 |
my ($self, $method) = @_; |
72 |
- my $hiding = $self->{hiding} ||= {map { $_ => 1 } @{$self->hidden}}; |
|
73 |
- return !!($hiding->{$method} || index($method, '_') == 0); |
|
92 |
+ my $h = $self->{hiding} ||= {map { $_ => 1 } @{$self->hidden}}; |
|
93 |
+ return !!($h->{$method} || index($method, '_') == 0 || $method !~ /[a-z]/); |
|
74 | 94 |
} |
75 | 95 |
|
76 | 96 |
sub lookup { |
... | ... |
@@ -85,6 +105,8 @@ sub route { |
85 | 105 |
shift->add_child(Mojolicious::Routes::Route->new(@_))->children->[-1]; |
86 | 106 |
} |
87 | 107 |
|
108 |
+sub _action { shift->plugins->emit_chain(around_action => @_) } |
|
109 |
+ |
|
88 | 110 |
sub _add { |
89 | 111 |
my ($self, $attr, $name, $cb) = @_; |
90 | 112 |
$self->$attr->{$name} = $cb; |
... | ... |
@@ -92,11 +114,11 @@ sub _add { |
92 | 114 |
} |
93 | 115 |
|
94 | 116 |
sub _callback { |
95 |
- my ($self, $c, $field, $nested) = @_; |
|
96 |
- $c->stash->{'mojo.routed'}++; |
|
97 |
- $c->app->log->debug('Routing to a callback.'); |
|
98 |
- my $continue = $field->{cb}->($c); |
|
99 |
- return !$nested || $continue ? 1 : undef; |
|
117 |
+ my ($self, $c, $cb, $last) = @_; |
|
118 |
+ $c->stash->{'mojo.routed'}++ if $last; |
|
119 |
+ my $app = $c->app; |
|
120 |
+ $app->log->debug('Routing to a callback.'); |
|
121 |
+ return _action($app, $c, $cb, $last); |
|
100 | 122 |
} |
101 | 123 |
|
102 | 124 |
sub _class { |
... | ... |
@@ -131,7 +153,9 @@ sub _class { |
131 | 153 |
} |
132 | 154 |
|
133 | 155 |
# Success |
134 |
- return $class->new($c); |
|
156 |
+ my $new = $class->new(%$c); |
|
157 |
+ weaken $new->{$_} for qw(app tx); |
|
158 |
+ return $new; |
|
135 | 159 |
} |
136 | 160 |
|
137 | 161 |
# Nothing found |
... | ... |
@@ -140,43 +164,44 @@ sub _class { |
140 | 164 |
} |
141 | 165 |
|
142 | 166 |
sub _controller { |
143 |
- my ($self, $c, $field, $nested) = @_; |
|
167 |
+ my ($self, $old, $field, $last) = @_; |
|
144 | 168 |
|
145 | 169 |
# Load and instantiate controller/application |
146 |
- my $app; |
|
147 |
- unless ($app = $self->_class($c, $field)) { return defined $app ? 1 : undef } |
|
170 |
+ my $new; |
|
171 |
+ unless ($new = $self->_class($old, $field)) { return !!defined $new } |
|
148 | 172 |
|
149 | 173 |
# Application |
150 |
- my $continue; |
|
151 |
- my $class = ref $app; |
|
152 |
- my $log = $c->app->log; |
|
153 |
- if (my $sub = $app->can('handler')) { |
|
174 |
+ my $class = ref $new; |
|
175 |
+ my $app = $old->app; |
|
176 |
+ my $log = $app->log; |
|
177 |
+ if (my $sub = $new->can('handler')) { |
|
154 | 178 |
$log->debug(qq{Routing to application "$class".}); |
155 | 179 |
|
156 | 180 |
# Try to connect routes |
157 |
- if (my $sub = $app->can('routes')) { |
|
158 |
- my $r = $app->$sub; |
|
159 |
- weaken $r->parent($c->match->endpoint)->{parent} unless $r->parent; |
|
181 |
+ if (my $sub = $new->can('routes')) { |
|
182 |
+ my $r = $new->$sub; |
|
183 |
+ weaken $r->parent($old->match->endpoint)->{parent} unless $r->parent; |
|
160 | 184 |
} |
161 |
- $app->$sub($c); |
|
162 |
- $c->stash->{'mojo.routed'}++; |
|
185 |
+ $new->$sub($old); |
|
186 |
+ $old->stash->{'mojo.routed'}++; |
|
163 | 187 |
} |
164 | 188 |
|
165 | 189 |
# Action |
166 |
- elsif (my $method = $self->_method($c, $field)) { |
|
167 |
- $log->debug(qq{Routing to controller "$class" and action "$method".}); |
|
190 |
+ elsif (my $method = $field->{action}) { |
|
191 |
+ if (!$self->is_hidden($method)) { |
|
192 |
+ $log->debug(qq{Routing to controller "$class" and action "$method".}); |
|
168 | 193 |
|
169 |
- # Try to call action |
|
170 |
- if (my $sub = $app->can($method)) { |
|
171 |
- $c->stash->{'mojo.routed'}++ unless $nested; |
|
172 |
- $continue = $app->$sub; |
|
173 |
- } |
|
194 |
+ if (my $sub = $new->can($method)) { |
|
195 |
+ $old->stash->{'mojo.routed'}++ if $last; |
|
196 |
+ return 1 if _action($app, $new, $sub, $last); |
|
197 |
+ } |
|
174 | 198 |
|
175 |
- # Action not found |
|
176 |
- else { $log->debug('Action not found in controller.') } |
|
199 |
+ else { $log->debug('Action not found in controller.') } |
|
200 |
+ } |
|
201 |
+ else { $log->debug(qq{Action "$method" is not allowed.}) } |
|
177 | 202 |
} |
178 | 203 |
|
179 |
- return !$nested || $continue ? 1 : undef; |
|
204 |
+ return undef; |
|
180 | 205 |
} |
181 | 206 |
|
182 | 207 |
sub _load { |
... | ... |
@@ -191,50 +216,10 @@ sub _load { |
191 | 216 |
return ++$self->{loaded}{$app}; |
192 | 217 |
} |
193 | 218 |
|
194 |
-sub _method { |
|
195 |
- my ($self, $c, $field) = @_; |
|
196 |
- |
|
197 |
- # Hidden |
|
198 |
- return undef unless my $method = $field->{action}; |
|
199 |
- $c->app->log->debug(qq{Action "$method" is not allowed.}) and return undef |
|
200 |
- if $self->is_hidden($method); |
|
201 |
- |
|
202 |
- # Invalid |
|
203 |
- $c->app->log->debug(qq{Action "$method" is invalid.}) and return undef |
|
204 |
- unless $method =~ /^[a-zA-Z0-9_:]+$/; |
|
205 |
- |
|
206 |
- return $method; |
|
207 |
-} |
|
208 |
- |
|
209 |
-sub _walk { |
|
210 |
- my ($self, $c) = @_; |
|
211 |
- |
|
212 |
- my $stack = $c->match->stack; |
|
213 |
- return undef unless my $nested = @$stack; |
|
214 |
- my $stash = $c->stash; |
|
215 |
- $stash->{'mojo.captures'} ||= {}; |
|
216 |
- for my $field (@$stack) { |
|
217 |
- $nested--; |
|
218 |
- |
|
219 |
- # Merge captures into stash |
|
220 |
- my @keys = keys %$field; |
|
221 |
- @{$stash}{@keys} = @{$stash->{'mojo.captures'}}{@keys} = values %$field; |
|
222 |
- |
|
223 |
- # Dispatch |
|
224 |
- my $continue |
|
225 |
- = $field->{cb} |
|
226 |
- ? $self->_callback($c, $field, $nested) |
|
227 |
- : $self->_controller($c, $field, $nested); |
|
228 |
- |
|
229 |
- # Break the chain |
|
230 |
- return undef if $nested && !$continue; |
|
231 |
- } |
|
232 |
- |
|
233 |
- return 1; |
|
234 |
-} |
|
235 |
- |
|
236 | 219 |
1; |
237 | 220 |
|
221 |
+=encoding utf8 |
|
222 |
+ |
|
238 | 223 |
=head1 NAME |
239 | 224 |
|
240 | 225 |
Mojolicious::Routes - Always find your destination with routes! |
... | ... |
@@ -294,7 +279,7 @@ Contains all available conditions. |
294 | 279 |
my $hidden = $r->hidden; |
295 | 280 |
$r = $r->hidden([qw(attr has new)]); |
296 | 281 |
|
297 |
-Controller methods and attributes that are hidden from router, defaults to |
|
282 |
+Controller attributes and methods that are hidden from router, defaults to |
|
298 | 283 |
C<attr>, C<has>, C<new> and C<tap>. |
299 | 284 |
|
300 | 285 |
=head2 namespaces |
... | ... |
@@ -337,9 +322,15 @@ Add a new shortcut. |
337 | 322 |
|
338 | 323 |
Automatic rendering. |
339 | 324 |
|
325 |
+=head2 continue |
|
326 |
+ |
|
327 |
+ $r->continue(Mojolicious::Controller->new); |
|
328 |
+ |
|
329 |
+Continue dispatch chain. |
|
330 |
+ |
|
340 | 331 |
=head2 dispatch |
341 | 332 |
|
342 |
- my $success = $r->dispatch(Mojolicious::Controller->new); |
|
333 |
+ my $bool = $r->dispatch(Mojolicious::Controller->new); |
|
343 | 334 |
|
344 | 335 |
Match routes with L<Mojolicious::Routes::Match> and dispatch. |
345 | 336 |
|
... | ... |
@@ -347,13 +338,13 @@ Match routes with L<Mojolicious::Routes::Match> and dispatch. |
347 | 338 |
|
348 | 339 |
$r = $r->hide(qw(foo bar)); |
349 | 340 |
|
350 |
-Hide controller methods and attributes from router. |
|
341 |
+Hide controller attributes and methods from router. |
|
351 | 342 |
|
352 | 343 |
=head2 is_hidden |
353 | 344 |
|
354 |
- my $success = $r->is_hidden('foo'); |
|
345 |
+ my $bool = $r->is_hidden('foo'); |
|
355 | 346 |
|
356 |
-Check if controller method or attribute is hidden from router. |
|
347 |
+Check if controller attribute or method is hidden from router. |
|
357 | 348 |
|
358 | 349 |
=head2 lookup |
359 | 350 |
|
... | ... |
@@ -1,6 +1,7 @@ |
1 | 1 |
package Mojolicious::Routes::Match; |
2 | 2 |
use Mojo::Base -base; |
3 | 3 |
|
4 |
+has current => 0; |
|
4 | 5 |
has [qw(endpoint root)]; |
5 | 6 |
has stack => sub { [] }; |
6 | 7 |
|
... | ... |
@@ -71,7 +72,7 @@ sub _match { |
71 | 72 |
if (($endpoint && $empty) || $r->inline) { |
72 | 73 |
push @{$self->stack}, {%$captures}; |
73 | 74 |
return $self->endpoint($r) if $endpoint && $empty; |
74 |
- delete $captures->{$_} for qw(app cb); |
|
75 |
+ delete @$captures{qw(app cb)}; |
|
75 | 76 |
} |
76 | 77 |
|
77 | 78 |
# Match children |
... | ... |
@@ -102,6 +103,8 @@ sub _values { |
102 | 103 |
|
103 | 104 |
1; |
104 | 105 |
|
106 |
+=encoding utf8 |
|
107 |
+ |
|
105 | 108 |
=head1 NAME |
106 | 109 |
|
107 | 110 |
Mojolicious::Routes::Match - Find routes |
... | ... |
@@ -137,6 +140,13 @@ structures. |
137 | 140 |
|
138 | 141 |
L<Mojolicious::Routes::Match> implements the following attributes. |
139 | 142 |
|
143 |
+=head2 current |
|
144 |
+ |
|
145 |
+ my $current = $match->current; |
|
146 |
+ $match = $match->current(2); |
|
147 |
+ |
|
148 |
+Current position on the L</"stack">, defaults to C<0>. |
|
149 |
+ |
|
140 | 150 |
=head2 endpoint |
141 | 151 |
|
142 | 152 |
my $endpoint = $match->endpoint; |
... | ... |
@@ -167,7 +177,8 @@ implements the following new ones. |
167 | 177 |
|
168 | 178 |
$match->match(Mojolicious::Controller->new, {method => 'GET', path => '/'}); |
169 | 179 |
|
170 |
-Match controller and options against C<root> to find appropriate C<endpoint>. |
|
180 |
+Match controller and options against L</"root"> to find appropriate |
|
181 |
+L</"endpoint">. |
|
171 | 182 |
|
172 | 183 |
=head2 path_for |
173 | 184 |
|
... | ... |
@@ -28,7 +28,7 @@ sub match_partial { |
28 | 28 |
|
29 | 29 |
# Match |
30 | 30 |
return undef unless my @captures = $$pathref =~ $regex; |
31 |
- $$pathref =~ s/$regex//; |
|
31 |
+ $$pathref = pop(@captures); |
|
32 | 32 |
|
33 | 33 |
# Merge captures |
34 | 34 |
my $captures = {%{$self->defaults}}; |
... | ... |
@@ -152,7 +152,7 @@ sub _compile { |
152 | 152 |
# Not rooted with a slash |
153 | 153 |
$regex = "$block$regex" if $block; |
154 | 154 |
|
155 |
- return $self->regex(qr/^$regex/s)->regex; |
|
155 |
+ return $self->regex(qr/^$regex(.*)/s)->regex; |
|
156 | 156 |
} |
157 | 157 |
|
158 | 158 |
sub _compile_format { |
... | ... |
@@ -189,16 +189,14 @@ sub _tokenize { |
189 | 189 |
my $pattern = $self->pattern; |
190 | 190 |
my $state = 'text'; |
191 | 191 |
my (@tree, $quoted); |
192 |
- while (length(my $char = substr $pattern, 0, 1, '')) { |
|
193 |
- |
|
194 |
- # Inside a placeholder |
|
192 |
+ for my $char (split '', $pattern) { |
|
195 | 193 |
my $inside = !!grep { $_ eq $state } qw(placeholder relaxed wildcard); |
196 | 194 |
|
197 | 195 |
# Quote start |
198 | 196 |
if ($char eq $quote_start) { |
199 | 197 |
$quoted = 1; |
200 |
- $state = 'placeholder'; |
|
201 | 198 |
push @tree, ['placeholder', '']; |
199 |
+ $state = 'placeholder'; |
|
202 | 200 |
} |
203 | 201 |
|
204 | 202 |
# Placeholder start |
... | ... |
@@ -230,13 +228,9 @@ sub _tokenize { |
230 | 228 |
|
231 | 229 |
# Text |
232 | 230 |
else { |
233 |
- $state = 'text'; |
|
234 |
- |
|
235 |
- # New text element |
|
236 | 231 |
push @tree, ['text', $char] and next unless $tree[-1][0] eq 'text'; |
237 |
- |
|
238 |
- # More text |
|
239 | 232 |
$tree[-1][-1] .= $char; |
233 |
+ $state = 'text'; |
|
240 | 234 |
} |
241 | 235 |
} |
242 | 236 |
|
... | ... |
@@ -245,6 +239,8 @@ sub _tokenize { |
245 | 239 |
|
246 | 240 |
1; |
247 | 241 |
|
242 |
+=encoding utf8 |
|
243 |
+ |
|
248 | 244 |
=head1 NAME |
249 | 245 |
|
250 | 246 |
Mojolicious::Routes::Pattern - Routes pattern engine |
... | ... |
@@ -365,8 +361,8 @@ implements the following new ones. |
365 | 361 |
= Mojolicious::Routes::Pattern->new('/:action', action => qr/\w+/); |
366 | 362 |
my $pattern = Mojolicious::Routes::Pattern->new(format => 0); |
367 | 363 |
|
368 |
-Construct a new L<Mojolicious::Routes::Pattern> object and C<parse> pattern if |
|
369 |
-necessary. |
|
364 |
+Construct a new L<Mojolicious::Routes::Pattern> object and L</"parse"> pattern |
|
365 |
+if necessary. |
|
370 | 366 |
|
371 | 367 |
=head2 match |
372 | 368 |
|
... | ... |
@@ -136,7 +136,7 @@ sub render { |
136 | 136 |
|
137 | 137 |
sub root { |
138 | 138 |
my $root = my $parent = shift; |
139 |
- while ($parent = $parent->parent) { $root = $parent } |
|
139 |
+ $root = $parent while $parent = $parent->parent; |
|
140 | 140 |
return $root; |
141 | 141 |
} |
142 | 142 |
|
... | ... |
@@ -249,6 +249,8 @@ sub _generate_route { |
249 | 249 |
|
250 | 250 |
1; |
251 | 251 |
|
252 |
+=encoding utf8 |
|
253 |
+ |
|
252 | 254 |
=head1 NAME |
253 | 255 |
|
254 | 256 |
Mojolicious::Routes::Route - Route |
... | ... |
@@ -277,10 +279,10 @@ The children of this route, used for nesting routes. |
277 | 279 |
|
278 | 280 |
=head2 inline |
279 | 281 |
|
280 |
- my $inline = $r->inline; |
|
281 |
- $r = $r->inline(1); |
|
282 |
+ my $bool = $r->inline; |
|
283 |
+ $r = $r->inline($bool); |
|
282 | 284 |
|
283 |
-Allow C<bridge> semantics for this route. |
|
285 |
+Allow L</"bridge"> semantics for this route. |
|
284 | 286 |
|
285 | 287 |
=head2 parent |
286 | 288 |
|
... | ... |
@@ -291,8 +293,8 @@ The parent of this route, used for nesting routes. |
291 | 293 |
|
292 | 294 |
=head2 partial |
293 | 295 |
|
294 |
- my $partial = $r->partial; |
|
295 |
- $r = $r->partial(1); |
|
296 |
+ my $bool = $r->partial; |
|
297 |
+ $r = $r->partial($bool); |
|
296 | 298 |
|
297 | 299 |
Route has no specific end, remaining characters will be captured in C<path>. |
298 | 300 |
|
... | ... |
@@ -343,7 +345,7 @@ also the L<Mojolicious::Lite> tutorial for more argument variations. |
343 | 345 |
my $bridge = $r->bridge('/:action', action => qr/\w+/); |
344 | 346 |
my $bridge = $r->bridge(format => 0); |
345 | 347 |
|
346 |
-Generate bridge route. |
|
348 |
+Generate bridge route with optional pattern and restrictive placeholders. |
|
347 | 349 |
|
348 | 350 |
my $auth = $r->bridge('/user')->to('user#auth'); |
349 | 351 |
$auth->get('/show')->to('#show'); |
... | ... |
@@ -353,7 +355,7 @@ Generate bridge route. |
353 | 355 |
|
354 | 356 |
my $route = $r->delete('/:foo' => sub {...}); |
355 | 357 |
|
356 |
-Generate route matching only C<DELETE> requests. See also the |
|
358 |
+Generate route matching only DELETE requests. See also the |
|
357 | 359 |
L<Mojolicious::Lite> tutorial for more argument variations. |
358 | 360 |
|
359 | 361 |
$r->delete('/user')->to('user#remove'); |
... | ... |
@@ -366,7 +368,7 @@ L<Mojolicious::Lite> tutorial for more argument variations. |
366 | 368 |
$r = $r->detour('MyApp', {foo => 'bar'}); |
367 | 369 |
|
368 | 370 |
Set default parameters for this route and allow partial matching to simplify |
369 |
-application embedding, takes the same arguments as C<to>. |
|
371 |
+application embedding, takes the same arguments as L</"to">. |
|
370 | 372 |
|
371 | 373 |
=head2 find |
372 | 374 |
|
... | ... |
@@ -381,38 +383,38 @@ generated ones. |
381 | 383 |
|
382 | 384 |
my $route = $r->get('/:foo' => sub {...}); |
383 | 385 |
|
384 |
-Generate route matching only C<GET> requests. See also the |
|
385 |
-L<Mojolicious::Lite> tutorial for more argument variations. |
|
386 |
+Generate route matching only GET requests. See also the L<Mojolicious::Lite> |
|
387 |
+tutorial for more argument variations. |
|
386 | 388 |
|
387 | 389 |
$r->get('/user')->to('user#show'); |
388 | 390 |
|
389 | 391 |
=head2 has_conditions |
390 | 392 |
|
391 |
- my $success = $r->has_conditions; |
|
393 |
+ my $bool = $r->has_conditions; |
|
392 | 394 |
|
393 | 395 |
Check if this route has active conditions. |
394 | 396 |
|
395 | 397 |
=head2 has_custom_name |
396 | 398 |
|
397 |
- my $success = $r->has_custom_name; |
|
399 |
+ my $bool = $r->has_custom_name; |
|
398 | 400 |
|
399 | 401 |
Check if this route has a custom name. |
400 | 402 |
|
401 | 403 |
=head2 has_websocket |
402 | 404 |
|
403 |
- my $success = $r->has_websocket; |
|
405 |
+ my $bool = $r->has_websocket; |
|
404 | 406 |
|
405 | 407 |
Check if this route has a WebSocket ancestor. |
406 | 408 |
|
407 | 409 |
=head2 is_endpoint |
408 | 410 |
|
409 |
- my $success = $r->is_endpoint; |
|
411 |
+ my $bool = $r->is_endpoint; |
|
410 | 412 |
|
411 | 413 |
Check if this route qualifies as an endpoint. |
412 | 414 |
|
413 | 415 |
=head2 is_websocket |
414 | 416 |
|
415 |
- my $success = $r->is_websocket; |
|
417 |
+ my $bool = $r->is_websocket; |
|
416 | 418 |
|
417 | 419 |
Check if this route is a WebSocket. |
418 | 420 |
|
... | ... |
@@ -431,7 +433,7 @@ the current route. |
431 | 433 |
|
432 | 434 |
my $route = $r->options('/:foo' => sub {...}); |
433 | 435 |
|
434 |
-Generate route matching only C<OPTIONS> requests. See also the |
|
436 |
+Generate route matching only OPTIONS requests. See also the |
|
435 | 437 |
L<Mojolicious::Lite> tutorial for more argument variations. |
436 | 438 |
|
437 | 439 |
$r->options('/user')->to('user#overview'); |
... | ... |
@@ -460,8 +462,8 @@ Parse pattern. |
460 | 462 |
|
461 | 463 |
my $route = $r->patch('/:foo' => sub {...}); |
462 | 464 |
|
463 |
-Generate route matching only C<PATCH> requests. See also the |
|
464 |
-L<Mojolicious::Lite> tutorial for more argument variations. |
|
465 |
+Generate route matching only PATCH requests. See also the L<Mojolicious::Lite> |
|
466 |
+tutorial for more argument variations. |
|
465 | 467 |
|
466 | 468 |
$r->patch('/user')->to('user#update'); |
467 | 469 |
|
... | ... |
@@ -469,8 +471,8 @@ L<Mojolicious::Lite> tutorial for more argument variations. |
469 | 471 |
|
470 | 472 |
my $route = $r->post('/:foo' => sub {...}); |
471 | 473 |
|
472 |
-Generate route matching only C<POST> requests. See also the |
|
473 |
-L<Mojolicious::Lite> tutorial for more argument variations. |
|
474 |
+Generate route matching only POST requests. See also the L<Mojolicious::Lite> |
|
475 |
+tutorial for more argument variations. |
|
474 | 476 |
|
475 | 477 |
$r->post('/user')->to('user#create'); |
476 | 478 |
|
... | ... |
@@ -478,8 +480,8 @@ L<Mojolicious::Lite> tutorial for more argument variations. |
478 | 480 |
|
479 | 481 |
my $route = $r->put('/:foo' => sub {...}); |
480 | 482 |
|
481 |
-Generate route matching only C<PUT> requests. See also the |
|
482 |
-L<Mojolicious::Lite> tutorial for more argument variations. |
|
483 |
+Generate route matching only PUT requests. See also the L<Mojolicious::Lite> |
|
484 |
+tutorial for more argument variations. |
|
483 | 485 |
|
484 | 486 |
$r->put('/user')->to('user#replace'); |
485 | 487 |
|
... | ... |
@@ -517,7 +519,8 @@ The L<Mojolicious::Routes> object this route is an descendent of. |
517 | 519 |
my $route = $r->route('/:action', action => qr/\w+/); |
518 | 520 |
my $route = $r->route(format => 0); |
519 | 521 |
|
520 |
-Generate route matching all HTTP request methods. |
|
522 |
+Generate route matching all HTTP request methods with optional pattern and |
|
523 |
+restrictive placeholders. |
|
521 | 524 |
|
522 | 525 |
=head2 to |
523 | 526 |
|
... | ... |
@@ -544,8 +547,8 @@ Stringify the whole route. |
544 | 547 |
|
545 | 548 |
=head2 under |
546 | 549 |
|
547 |
- my $route = $r->under(sub {...}); |
|
548 |
- my $route = $r->under('/:foo'); |
|
550 |
+ my $bridge = $r->under(sub {...}); |
|
551 |
+ my $bridge = $r->under('/:foo'); |
|
549 | 552 |
|
550 | 553 |
Generate bridge route. See also the L<Mojolicious::Lite> tutorial for more |
551 | 554 |
argument variations. |
... | ... |
@@ -61,6 +61,8 @@ sub store { |
61 | 61 |
|
62 | 62 |
1; |
63 | 63 |
|
64 |
+=encoding utf8 |
|
65 |
+ |
|
64 | 66 |
=head1 NAME |
65 | 67 |
|
66 | 68 |
Mojolicious::Sessions - Signed cookie based session manager |
... | ... |
@@ -127,8 +129,8 @@ C<expiration> and C<expires> session values. |
127 | 129 |
|
128 | 130 |
=head2 secure |
129 | 131 |
|
130 |
- my $secure = $sessions->secure; |
|
131 |
- $sessions = $sessions->secure(1); |
|
132 |
+ my $bool = $sessions->secure; |
|
133 |
+ $sessions = $sessions->secure($bool); |
|
132 | 134 |
|
133 | 135 |
Set the secure flag on all session cookies, so that browsers send them only |
134 | 136 |
over HTTPS connections. |
... | ... |
@@ -136,7 +138,7 @@ over HTTPS connections. |
136 | 138 |
=head1 METHODS |
137 | 139 |
|
138 | 140 |
L<Mojolicious::Sessions> inherits all methods from L<Mojo::Base> and |
139 |
-implements the following ones. |
|
141 |
+implements the following new ones. |
|
140 | 142 |
|
141 | 143 |
=head2 load |
142 | 144 |
|
... | ... |
@@ -121,6 +121,8 @@ sub _get_file { |
121 | 121 |
|
122 | 122 |
1; |
123 | 123 |
|
124 |
+=encoding utf8 |
|
125 |
+ |
|
124 | 126 |
=head1 NAME |
125 | 127 |
|
126 | 128 |
Mojolicious::Static - Serve static files |
... | ... |
@@ -170,24 +172,28 @@ the following new ones. |
170 | 172 |
|
171 | 173 |
=head2 dispatch |
172 | 174 |
|
173 |
- my $success = $static->dispatch(Mojolicious::Controller->new); |
|
175 |
+ my $bool = $static->dispatch(Mojolicious::Controller->new); |
|
174 | 176 |
|
175 | 177 |
Serve static file for L<Mojolicious::Controller> object. |
176 | 178 |
|
177 | 179 |
=head2 file |
178 | 180 |
|
179 |
- my $asset = $static->file('foo/bar.html'); |
|
181 |
+ my $asset = $static->file('images/logo.png'); |
|
182 |
+ my $asset = $static->file('../lib/MyApp.pm'); |
|
180 | 183 |
|
181 | 184 |
Get L<Mojo::Asset::File> or L<Mojo::Asset::Memory> object for a file, relative |
182 |
-to C<paths> or from C<classes>. |
|
185 |
+to L</"paths"> or from L</"classes">. Note that this method does not protect |
|
186 |
+from traversing to parent directories. |
|
183 | 187 |
|
184 | 188 |
my $content = $static->file('foo/bar.html')->slurp; |
185 | 189 |
|
186 | 190 |
=head2 serve |
187 | 191 |
|
188 |
- my $success = $static->serve(Mojolicious::Controller->new, 'foo/bar.html'); |
|
192 |
+ my $bool = $static->serve(Mojolicious::Controller->new, 'images/logo.png'); |
|
193 |
+ my $bool = $static->serve(Mojolicious::Controller->new, '../lib/MyApp.pm'); |
|
189 | 194 |
|
190 |
-Serve a specific file, relative to C<paths> or from C<classes>. |
|
195 |
+Serve a specific file, relative to L</"paths"> or from L</"classes">. Note |
|
196 |
+that this method does not protect from traversing to parent directories. |
|
191 | 197 |
|
192 | 198 |
=head2 serve_asset |
193 | 199 |
|
... | ... |
@@ -24,7 +24,7 @@ has types => sub { |
24 | 24 |
png => ['image/png'], |
25 | 25 |
rss => ['application/rss+xml'], |
26 | 26 |
svg => ['image/svg+xml'], |
27 |
- txt => ['text/plain'], |
|
27 |
+ txt => ['text/plain;charset=UTF-8'], |
|
28 | 28 |
webm => ['video/webm'], |
29 | 29 |
woff => ['application/font-woff'], |
30 | 30 |
xml => ['application/xml', 'text/xml'], |
... | ... |
@@ -37,7 +37,7 @@ sub detect { |
37 | 37 |
|
38 | 38 |
# Extract and prioritize MIME types |
39 | 39 |
my %types; |
40 |
- /^\s*([^,; ]+)(?:\s*\;\s*q=(\d+(?:\.\d+)?))?\s*$/i |
|
40 |
+ /^\s*([^,; ]+)(?:\s*\;\s*q\s*=\s*(\d+(?:\.\d+)?))?\s*$/i |
|
41 | 41 |
and $types{lc $1} = defined $2 ? $2 : 1 |
42 | 42 |
for split /,/, defined $accept ? $accept : ''; |
43 | 43 |
my @detected = sort { $types{$b} <=> $types{$a} } sort keys %types; |
... | ... |
@@ -62,6 +62,8 @@ sub type { |
62 | 62 |
|
63 | 63 |
1; |
64 | 64 |
|
65 |
+=encoding utf8 |
|
66 |
+ |
|
65 | 67 |
=head1 NAME |
66 | 68 |
|
67 | 69 |
Mojolicious::Types - MIME types |
... | ... |
@@ -99,7 +101,7 @@ L<Mojolicious::Types> manages MIME types for L<Mojolicious>. |
99 | 101 |
png -> image/png |
100 | 102 |
rss -> application/rss+xml |
101 | 103 |
svg -> image/svg+xml |
102 |
- txt -> text/plain |
|
104 |
+ txt -> text/plain;charset=UTF-8 |
|
103 | 105 |
webm -> video/webm |
104 | 106 |
woff -> application/font-woff |
105 | 107 |
xml -> application/xml,text/xml |
... | ... |
@@ -0,0 +1,128 @@ |
1 |
+package Mojolicious::Validator; |
|
2 |
+use Mojo::Base -base; |
|
3 |
+ |
|
4 |
+use Mojolicious::Validator::Validation; |
|
5 |
+ |
|
6 |
+has checks => sub { |
|
7 |
+ {equal_to => \&_equal_to, in => \&_in, like => \&_like, size => \&_size}; |
|
8 |
+}; |
|
9 |
+ |
|
10 |
+sub add_check { |
|
11 |
+ my ($self, $name, $cb) = @_; |
|
12 |
+ $self->checks->{$name} = $cb; |
|
13 |
+ return $self; |
|
14 |
+} |
|
15 |
+ |
|
16 |
+sub validation { |
|
17 |
+ Mojolicious::Validator::Validation->new(validator => shift); |
|
18 |
+} |
|
19 |
+ |
|
20 |
+sub _equal_to { |
|
21 |
+ my ($validation, $name, $value, $to) = @_; |
|
22 |
+ return 1 unless defined(my $other = $validation->input->{$to}); |
|
23 |
+ return $value ne $other; |
|
24 |
+} |
|
25 |
+ |
|
26 |
+sub _in { |
|
27 |
+ my ($validation, $name, $value) = (shift, shift, shift); |
|
28 |
+ $value eq $_ && return undef for @_; |
|
29 |
+ return 1; |
|
30 |
+} |
|
31 |
+ |
|
32 |
+sub _like { $_[2] !~ $_[3] } |
|
33 |
+ |
|
34 |
+sub _size { |
|
35 |
+ my ($validation, $name, $value, $min, $max) = @_; |
|
36 |
+ my $len = length $value; |
|
37 |
+ return $len < $min || $len > $max; |
|
38 |
+} |
|
39 |
+ |
|
40 |
+1; |
|
41 |
+ |
|
42 |
+=encoding utf8 |
|
43 |
+ |
|
44 |
+=head1 NAME |
|
45 |
+ |
|
46 |
+Mojolicious::Validator - Validate form data |
|
47 |
+ |
|
48 |
+=head1 SYNOPSIS |
|
49 |
+ |
|
50 |
+ use Mojolicious::Validator; |
|
51 |
+ |
|
52 |
+ my $validator = Mojolicious::Validator->new; |
|
53 |
+ my $validation = $validator->validation; |
|
54 |
+ $validation->input({foo => 'bar'}); |
|
55 |
+ $validation->required('foo')->like(qr/ar$/); |
|
56 |
+ say $validation->param('foo'); |
|
57 |
+ |
|
58 |
+=head1 DESCRIPTION |
|
59 |
+ |
|
60 |
+L<Mojolicious::Validator> validates form data for L<Mojolicious>. |
|
61 |
+ |
|
62 |
+=head1 CHECKS |
|
63 |
+ |
|
64 |
+These validation checks are available by default. |
|
65 |
+ |
|
66 |
+=head2 equal_to |
|
67 |
+ |
|
68 |
+ $validation->equal_to('foo'); |
|
69 |
+ |
|
70 |
+Value needs to be equal to the value of another field. |
|
71 |
+ |
|
72 |
+=head2 in |
|
73 |
+ |
|
74 |
+ $validation->in(qw(foo bar baz)); |
|
75 |
+ |
|
76 |
+Value needs to match one of the values in the list. |
|
77 |
+ |
|
78 |
+=head2 like |
|
79 |
+ |
|
80 |
+ $validation->like(qr/^[A-Z]/); |
|
81 |
+ |
|
82 |
+Value needs to match the regular expression. |
|
83 |
+ |
|
84 |
+=head2 size |
|
85 |
+ |
|
86 |
+ $validation->size(2, 5); |
|
87 |
+ |
|
88 |
+Value length in characters needs to be between these two values. |
|
89 |
+ |
|
90 |
+=head1 ATTRIBUTES |
|
91 |
+ |
|
92 |
+L<Mojolicious::Validator> implements the following attributes. |
|
93 |
+ |
|
94 |
+=head2 checks |
|
95 |
+ |
|
96 |
+ my $checks = $validator->checks; |
|
97 |
+ $validator = $validator->checks({size => sub {...}}); |
|
98 |
+ |
|
99 |
+Registered validation checks, by default only L</"equal_to">, L</"in">, |
|
100 |
+L</"like"> and L</"size"> are already defined. |
|
101 |
+ |
|
102 |
+=head1 METHODS |
|
103 |
+ |
|
104 |
+L<Mojolicious::Validator> inherits all methods from L<Mojo::Base> and |
|
105 |
+implements the following new ones. |
|
106 |
+ |
|
107 |
+=head2 add_check |
|
108 |
+ |
|
109 |
+ $validator = $validator->add_check(size => sub {...}); |
|
110 |
+ |
|
111 |
+Register a new validation check. |
|
112 |
+ |
|
113 |
+=head2 validation |
|
114 |
+ |
|
115 |
+ my $validation = $validator->validation; |
|
116 |
+ |
|
117 |
+Build L<Mojolicious::Validator::Validation> object to perform validations. |
|
118 |
+ |
|
119 |
+ my $validation = $validator->validation; |
|
120 |
+ $validation->input({foo => 'bar'}); |
|
121 |
+ $validation->required('foo')->size(1, 5); |
|
122 |
+ say $validation->param('foo'); |
|
123 |
+ |
|
124 |
+=head1 SEE ALSO |
|
125 |
+ |
|
126 |
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
|
127 |
+ |
|
128 |
+=cut |
... | ... |
@@ -0,0 +1,216 @@ |
1 |
+package Mojolicious::Validator::Validation; |
|
2 |
+use Mojo::Base -base; |
|
3 |
+ |
|
4 |
+use Carp 'croak'; |
|
5 |
+use Scalar::Util 'blessed'; |
|
6 |
+ |
|
7 |
+has [qw(input output)] => sub { {} }; |
|
8 |
+has [qw(topic validator)]; |
|
9 |
+ |
|
10 |
+sub AUTOLOAD { |
|
11 |
+ my $self = shift; |
|
12 |
+ |
|
13 |
+ my ($package, $method) = our $AUTOLOAD =~ /^([\w:]+)::(\w+)$/; |
|
14 |
+ croak "Undefined subroutine &${package}::$method called" |
|
15 |
+ unless blessed $self && $self->isa(__PACKAGE__); |
|
16 |
+ |
|
17 |
+ croak qq{Can't locate object method "$method" via package "$package"} |
|
18 |
+ unless $self->validator->checks->{$method}; |
|
19 |
+ return $self->check($method => @_); |
|
20 |
+} |
|
21 |
+ |
|
22 |
+sub DESTROY { } |
|
23 |
+ |
|
24 |
+sub check { |
|
25 |
+ my ($self, $check) = (shift, shift); |
|
26 |
+ |
|
27 |
+ return $self unless $self->is_valid; |
|
28 |
+ |
|
29 |
+ my $cb = $self->validator->checks->{$check}; |
|
30 |
+ my $name = $self->topic; |
|
31 |
+ my $input = $self->input->{$name}; |
|
32 |
+ for my $value (ref $input eq 'ARRAY' ? @$input : $input) { |
|
33 |
+ next unless my $result = $self->$cb($name, $value, @_); |
|
34 |
+ delete $self->output->{$name}; |
|
35 |
+ $self->{error}{$name} = [$check, $result, @_]; |
|
36 |
+ last; |
|
37 |
+ } |
|
38 |
+ |
|
39 |
+ return $self; |
|
40 |
+} |
|
41 |
+ |
|
42 |
+sub error { shift->{error}{shift()} } |
|
43 |
+ |
|
44 |
+sub has_data { !!keys %{shift->input} } |
|
45 |
+ |
|
46 |
+sub has_error { $_[1] ? exists $_[0]{error}{$_[1]} : !!keys %{$_[0]{error}} } |
|
47 |
+ |
|
48 |
+sub is_valid { exists $_[0]->output->{defined $_[1] ? $_[1] : $_[0]->topic} } |
|
49 |
+ |
|
50 |
+sub optional { |
|
51 |
+ my ($self, $name) = @_; |
|
52 |
+ |
|
53 |
+ my $input = $self->input->{$name}; |
|
54 |
+ my @input = ref $input eq 'ARRAY' ? @$input : $input; |
|
55 |
+ $self->output->{$name} = $input |
|
56 |
+ unless grep { !defined($_) || !length($_) } @input; |
|
57 |
+ |
|
58 |
+ return $self->topic($name); |
|
59 |
+} |
|
60 |
+ |
|
61 |
+sub param { |
|
62 |
+ my ($self, $name) = @_; |
|
63 |
+ |
|
64 |
+ # Multiple names |
|
65 |
+ return map { scalar $self->param($_) } @$name if ref $name eq 'ARRAY'; |
|
66 |
+ |
|
67 |
+ # List names |
|
68 |
+ return sort keys %{$self->output} unless defined $name; |
|
69 |
+ |
|
70 |
+ my $value = $self->output->{$name}; |
|
71 |
+ my @values = ref $value eq 'ARRAY' ? @$value : ($value); |
|
72 |
+ return wantarray ? @values : $values[0]; |
|
73 |
+} |
|
74 |
+ |
|
75 |
+sub required { |
|
76 |
+ my ($self, $name) = @_; |
|
77 |
+ $self->{error}{$name} = ['required'] unless $self->optional($name)->is_valid; |
|
78 |
+ return $self; |
|
79 |
+} |
|
80 |
+ |
|
81 |
+1; |
|
82 |
+ |
|
83 |
+=encoding utf8 |
|
84 |
+ |
|
85 |
+=head1 NAME |
|
86 |
+ |
|
87 |
+Mojolicious::Validator::Validation - Perform validations |
|
88 |
+ |
|
89 |
+=head1 SYNOPSIS |
|
90 |
+ |
|
91 |
+ use Mojolicious::Validator; |
|
92 |
+ use Mojolicious::Validator::Validation; |
|
93 |
+ |
|
94 |
+ my $validator = Mojolicious::Validator->new; |
|
95 |
+ my $validation |
|
96 |
+ = Mojolicious::Validator::Validation->new(validator => $validator); |
|
97 |
+ $validation->input({foo => 'bar'}); |
|
98 |
+ $validation->required('foo')->in(qw(bar baz)); |
|
99 |
+ say $validation->param('foo'); |
|
100 |
+ |
|
101 |
+=head1 DESCRIPTION |
|
102 |
+ |
|
103 |
+L<Mojolicious::Validator::Validation> performs L<Mojolicious::Validator> |
|
104 |
+validation checks. |
|
105 |
+ |
|
106 |
+=head1 ATTRIBUTES |
|
107 |
+ |
|
108 |
+L<Mojolicious::Validator::Validation> implements the following attributes. |
|
109 |
+ |
|
110 |
+=head2 input |
|
111 |
+ |
|
112 |
+ my $input = $validation->input; |
|
113 |
+ $validation = $validation->input({foo => 'bar', baz => [123, 'yada']}); |
|
114 |
+ |
|
115 |
+Data to be validated. |
|
116 |
+ |
|
117 |
+=head2 output |
|
118 |
+ |
|
119 |
+ my $output = $validation->output; |
|
120 |
+ $validation = $validation->output({}); |
|
121 |
+ |
|
122 |
+Validated data. |
|
123 |
+ |
|
124 |
+=head2 topic |
|
125 |
+ |
|
126 |
+ my $topic = $validation->topic; |
|
127 |
+ $validation = $validation->topic('foo'); |
|
128 |
+ |
|
129 |
+Name of field currently being validated. |
|
130 |
+ |
|
131 |
+=head2 validator |
|
132 |
+ |
|
133 |
+ my $validator = $validation->validator; |
|
134 |
+ $validation = $validation->validator(Mojolicious::Validator->new); |
|
135 |
+ |
|
136 |
+L<Mojolicious::Validator> object this validation belongs to. |
|
137 |
+ |
|
138 |
+=head1 METHODS |
|
139 |
+ |
|
140 |
+L<Mojolicious::Validator::Validation> inherits all methods from L<Mojo::Base> |
|
141 |
+and implements the following new ones. |
|
142 |
+ |
|
143 |
+=head2 check |
|
144 |
+ |
|
145 |
+ $validation = $validation->check('size', 2, 7); |
|
146 |
+ |
|
147 |
+Perform validation check on all values of the current L</"topic">, no more |
|
148 |
+checks will be performend on them after the first one failed. |
|
149 |
+ |
|
150 |
+=head2 error |
|
151 |
+ |
|
152 |
+ my $err = $validation->error('foo'); |
|
153 |
+ |
|
154 |
+Return details about failed validation check, at any given time there can only |
|
155 |
+be one per field. |
|
156 |
+ |
|
157 |
+ my ($check, $result, @args) = @{$validation->error('foo')}; |
|
158 |
+ |
|
159 |
+=head2 has_data |
|
160 |
+ |
|
161 |
+ my $bool = $validation->has_data; |
|
162 |
+ |
|
163 |
+Check if L</"input"> is available for validation. |
|
164 |
+ |
|
165 |
+=head2 has_error |
|
166 |
+ |
|
167 |
+ my $bool = $validation->has_error; |
|
168 |
+ my $bool = $validation->has_error('foo'); |
|
169 |
+ |
|
170 |
+Check if validation resulted in errors, defaults to checking all fields. |
|
171 |
+ |
|
172 |
+=head2 is_valid |
|
173 |
+ |
|
174 |
+ my $bool = $validation->is_valid; |
|
175 |
+ my $bool = $validation->is_valid('foo'); |
|
176 |
+ |
|
177 |
+Check if validation was successful and field has a value, defaults to checking |
|
178 |
+the current L</"topic">. |
|
179 |
+ |
|
180 |
+=head2 optional |
|
181 |
+ |
|
182 |
+ $validation = $validation->optional('foo'); |
|
183 |
+ |
|
184 |
+Change validation L</"topic">. |
|
185 |
+ |
|
186 |
+=head2 param |
|
187 |
+ |
|
188 |
+ my @names = $c->param; |
|
189 |
+ my $foo = $c->param('foo'); |
|
190 |
+ my @foo = $c->param('foo'); |
|
191 |
+ my ($foo, $bar) = $c->param(['foo', 'bar']); |
|
192 |
+ |
|
193 |
+Access validated parameters, similar to L<Mojolicious::Controller/"param">. |
|
194 |
+ |
|
195 |
+=head2 required |
|
196 |
+ |
|
197 |
+ $validation = $validation->required('foo'); |
|
198 |
+ |
|
199 |
+Change validation L</"topic"> and make sure a value is present and not an |
|
200 |
+empty string. |
|
201 |
+ |
|
202 |
+=head1 CHECKS |
|
203 |
+ |
|
204 |
+In addition to the methods above, you can also call validation checks provided |
|
205 |
+by L<Mojolicious::Validator> on L<Mojolicious::Validator::Validation> objects, |
|
206 |
+similar to L</"check">. |
|
207 |
+ |
|
208 |
+ $validation->required('foo')->size(2, 5)->like(qr/^[A-Z]/); |
|
209 |
+ $validation->optional('bar')->equal_to('foo'); |
|
210 |
+ $validation->optional('baz')->in(qw(test 123)); |
|
211 |
+ |
|
212 |
+=head1 SEE ALSO |
|
213 |
+ |
|
214 |
+L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
|
215 |
+ |
|
216 |
+=cut |
... | ... |
@@ -1,6 +1,6 @@ |
1 |
-/*! jQuery v2.0.1 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license |
|
2 |
-//@ sourceMappingURL=jquery-2.0.1.min.map |
|
1 |
+/*! jQuery v2.0.3 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license |
|
2 |
+//@ sourceMappingURL=jquery-2.0.3.min.map |
|
3 | 3 |
*/ |
4 |
-(function(e,undefined){var t,n,r=typeof undefined,i=e.location,o=e.document,s=o.documentElement,a=e.jQuery,u=e.$,l={},c=[],f="2.0.1",p=c.concat,h=c.push,d=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=f.trim,x=function(e,n){return new x.fn.init(e,n,t)},b=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^-ms-/,N=/-([\da-z])/gi,E=function(e,t){return t.toUpperCase()},S=function(){o.removeEventListener("DOMContentLoaded",S,!1),e.removeEventListener("load",S,!1),x.ready()};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,t,n){var r,i;if(!e)return this;if("string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:T.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof x?t[0]:t,x.merge(this,x.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:o,!0)),C.test(r[1])&&x.isPlainObject(t))for(r in t)x.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return i=o.getElementById(r[2]),i&&i.parentNode&&(this.length=1,this[0]=i),this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?n.ready(e):(e.selector!==undefined&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return d.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,t,n,r,i,o,s=arguments[0]||{},a=1,u=arguments.length,l=!1;for("boolean"==typeof s&&(l=s,s=arguments[1]||{},a=2),"object"==typeof s||x.isFunction(s)||(s={}),u===a&&(s=this,--a);u>a;a++)if(null!=(e=arguments[a]))for(t in e)n=s[t],r=e[t],s!==r&&(l&&r&&(x.isPlainObject(r)||(i=x.isArray(r)))?(i?(i=!1,o=n&&x.isArray(n)?n:[]):o=n&&x.isPlainObject(n)?n:{},s[t]=x.extend(l,o,r)):r!==undefined&&(s[t]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=a),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){(e===!0?--x.readyWait:x.isReady)||(x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(o,[x]),x.fn.trigger&&x(o).trigger("ready").off("ready")))},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray,isWindow:function(e){return null!=e&&e===e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if("object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(t){return!1}return!0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:JSON.parse,parseXML:function(e){var t,n;if(!e||"string"!=typeof e)return null;try{n=new DOMParser,t=n.parseFromString(e,"text/xml")}catch(r){t=undefined}return(!t||t.getElementsByTagName("parsererror").length)&&x.error("Invalid XML: "+e),t},noop:function(){},globalEval:function(e){var t,n=eval;e=x.trim(e),e&&(1===e.indexOf("use strict")?(t=o.createElement("script"),t.text=e,o.head.appendChild(t).parentNode.removeChild(t)):n(e))},camelCase:function(e){return e.replace(k,"ms-").replace(N,E)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,s=j(e);if(n){if(s){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(s){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:function(e){return null==e?"":v.call(e)},makeArray:function(e,t){var n=t||[];return null!=e&&(j(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:g.call(t,e,n)},merge:function(e,t){var n=t.length,r=e.length,i=0;if("number"==typeof n)for(;n>i;i++)e[r++]=t[i];else while(t[i]!==undefined)e[r++]=t[i++];return e.length=r,e},grep:function(e,t,n){var r,i=[],o=0,s=e.length;for(n=!!n;s>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,s=j(e),a=[];if(s)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(a[a.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(a[a.length]=r);return p.apply([],a)},guid:1,proxy:function(e,t){var n,r,i;return"string"==typeof t&&(n=e[t],t=e,e=n),x.isFunction(e)?(r=d.call(arguments,2),i=function(){return e.apply(t||this,r.concat(d.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):undefined},access:function(e,t,n,r,i,o,s){var a=0,u=e.length,l=null==n;if("object"===x.type(n)){i=!0;for(a in n)x.access(e,t,a,n[a],!0,o,s)}else if(r!==undefined&&(i=!0,x.isFunction(r)||(s=!0),l&&(s?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(x(e),n)})),t))for(;u>a;a++)t(e[a],n,s?r:r.call(e[a],a,t(e[a],n)));return i?e:l?t.call(e):u?t(e[0],n):o},now:Date.now,swap:function(e,t,n,r){var i,o,s={};for(o in t)s[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=s[o];return i}}),x.ready.promise=function(t){return n||(n=x.Deferred(),"complete"===o.readyState?setTimeout(x.ready):(o.addEventListener("DOMContentLoaded",S,!1),e.addEventListener("load",S,!1))),n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function j(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}t=x(o),function(e,undefined){var t,n,r,i,o,s,a,u,l,c,f,p,h,d,g,m,y,v="sizzle"+-new Date,b=e.document,w=0,T=0,C=at(),k=at(),N=at(),E=!1,S=function(){return 0},j=typeof undefined,D=1<<31,A={}.hasOwnProperty,L=[],H=L.pop,q=L.push,O=L.push,F=L.slice,P=L.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",W="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",$=W.replace("w","w#"),B="\\["+M+"*("+W+")"+M+"*(?:([*^$|!~]?=)"+M+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+$+")|)|)"+M+"*\\]",I=":("+W+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+B.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=RegExp("^"+M+"*,"+M+"*"),X=RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=RegExp(M+"*[+~]"),Y=RegExp("="+M+"*([^\\]'\"]*)"+M+"*\\]","g"),V=RegExp(I),G=RegExp("^"+$+"$"),J={ID:RegExp("^#("+W+")"),CLASS:RegExp("^\\.("+W+")"),TAG:RegExp("^("+W.replace("w","w*")+")"),ATTR:RegExp("^"+B),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:RegExp("^(?:"+R+")$","i"),needsContext:RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Q=/^[^{]+\{\s*\[native \w/,K=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,Z=/^(?:input|select|textarea|button)$/i,et=/^h\d$/i,tt=/'|\\/g,nt=RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),rt=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{O.apply(L=F.call(b.childNodes),b.childNodes),L[b.childNodes.length].nodeType}catch(it){O={apply:L.length?function(e,t){q.apply(e,F.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function ot(e,t,r,i){var o,s,a,u,l,p,g,m,x,w;if((t?t.ownerDocument||t:b)!==f&&c(t),t=t||f,r=r||[],!e||"string"!=typeof e)return r;if(1!==(u=t.nodeType)&&9!==u)return[];if(h&&!i){if(o=K.exec(e))if(a=o[1]){if(9===u){if(s=t.getElementById(a),!s||!s.parentNode)return r;if(s.id===a)return r.push(s),r}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(a))&&y(t,s)&&s.id===a)return r.push(s),r}else{if(o[2])return O.apply(r,t.getElementsByTagName(e)),r;if((a=o[3])&&n.getElementsByClassName&&t.getElementsByClassName)return O.apply(r,t.getElementsByClassName(a)),r}if(n.qsa&&(!d||!d.test(e))){if(m=g=v,x=t,w=9===u&&e,1===u&&"object"!==t.nodeName.toLowerCase()){p=vt(e),(g=t.getAttribute("id"))?m=g.replace(tt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",l=p.length;while(l--)p[l]=m+xt(p[l]);x=U.test(e)&&t.parentNode||t,w=p.join(",")}if(w)try{return O.apply(r,x.querySelectorAll(w)),r}catch(T){}finally{g||t.removeAttribute("id")}}}return St(e.replace(z,"$1"),t,r,i)}function st(e){return Q.test(e+"")}function at(){var e=[];function t(n,r){return e.push(n+=" ")>i.cacheLength&&delete t[e.shift()],t[n]=r}return t}function ut(e){return e[v]=!0,e}function lt(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t,n){e=e.split("|");var r,o=e.length,s=n?null:t;while(o--)(r=i.attrHandle[e[o]])&&r!==t||(i.attrHandle[e[o]]=s)}function ft(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:e[t]===!0?t.toLowerCase():null}function pt(e,t){return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}function ht(e){return"input"===e.nodeName.toLowerCase()?e.defaultValue:undefined}function dt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function gt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function mt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function yt(e){return ut(function(t){return t=+t,ut(function(n,r){var i,o=e([],n.length,t),s=o.length;while(s--)n[i=o[s]]&&(n[i]=!(r[i]=n[i]))})})}s=ot.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},n=ot.support={},c=ot.setDocument=function(e){var t=e?e.ownerDocument||e:b;return t!==f&&9===t.nodeType&&t.documentElement?(f=t,p=t.documentElement,h=!s(t),n.attributes=lt(function(e){return e.innerHTML="<a href='#'></a>",ct("type|href|height|width",pt,"#"===e.firstChild.getAttribute("href")),ct(R,ft,null==e.getAttribute("disabled")),e.className="i",!e.getAttribute("className")}),n.input=lt(function(e){return e.innerHTML="<input>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")}),ct("value",ht,n.attributes&&n.input),n.getElementsByTagName=lt(function(e){return e.appendChild(t.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=lt(function(e){return e.innerHTML="<div class='a'></div><div class='a i'></div>",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),n.getById=lt(function(e){return p.appendChild(e).id=v,!t.getElementsByName||!t.getElementsByName(v).length}),n.getById?(i.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(nt,rt);return function(e){return e.getAttribute("id")===t}}):(delete i.find.ID,i.filter.ID=function(e){var t=e.replace(nt,rt);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=n.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==j?t.getElementsByTagName(e):undefined}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.CLASS=n.getElementsByClassName&&function(e,t){return typeof t.getElementsByClassName!==j&&h?t.getElementsByClassName(e):undefined},g=[],d=[],(n.qsa=st(t.querySelectorAll))&&(lt(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||d.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll(":checked").length||d.push(":checked")}),lt(function(e){var n=t.createElement("input");n.setAttribute("type","hidden"),e.appendChild(n).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&d.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||d.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),d.push(",.*:")})),(n.matchesSelector=st(m=p.webkitMatchesSelector||p.mozMatchesSelector||p.oMatchesSelector||p.msMatchesSelector))&<(function(e){n.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",I)}),d=d.length&&RegExp(d.join("|")),g=g.length&&RegExp(g.join("|")),y=st(p.contains)||p.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},n.sortDetached=lt(function(e){return 1&e.compareDocumentPosition(t.createElement("div"))}),S=p.compareDocumentPosition?function(e,r){if(e===r)return E=!0,0;var i=r.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(r);return i?1&i||!n.sortDetached&&r.compareDocumentPosition(e)===i?e===t||y(b,e)?-1:r===t||y(b,r)?1:l?P.call(l,e)-P.call(l,r):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,n){var r,i=0,o=e.parentNode,s=n.parentNode,a=[e],u=[n];if(e===n)return E=!0,0;if(!o||!s)return e===t?-1:n===t?1:o?-1:s?1:l?P.call(l,e)-P.call(l,n):0;if(o===s)return dt(e,n);r=e;while(r=r.parentNode)a.unshift(r);r=n;while(r=r.parentNode)u.unshift(r);while(a[i]===u[i])i++;return i?dt(a[i],u[i]):a[i]===b?-1:u[i]===b?1:0},t):f},ot.matches=function(e,t){return ot(e,null,null,t)},ot.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&c(e),t=t.replace(Y,"='$1']"),!(!n.matchesSelector||!h||g&&g.test(t)||d&&d.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return ot(t,f,null,[e]).length>0},ot.contains=function(e,t){return(e.ownerDocument||e)!==f&&c(e),y(e,t)},ot.attr=function(e,t){(e.ownerDocument||e)!==f&&c(e);var r=i.attrHandle[t.toLowerCase()],o=r&&A.call(i.attrHandle,t.toLowerCase())?r(e,t,!h):undefined;return o===undefined?n.attributes||!h?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null:o},ot.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},ot.uniqueSort=function(e){var t,r=[],i=0,o=0;if(E=!n.detectDuplicates,l=!n.sortStable&&e.slice(0),e.sort(S),E){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return e},o=ot.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=ot.selectors={cacheLength:50,createPseudo:ut,match:J,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(nt,rt),e[3]=(e[4]||e[5]||"").replace(nt,rt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||ot.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&ot.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return J.CHILD.test(e[0])?null:(e[3]&&e[4]!==undefined?e[2]=e[4]:n&&V.test(n)&&(t=vt(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(nt,rt).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=C[e+" "];return t||(t=RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&C(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=ot.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),s="last"!==e.slice(-4),a="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,h,d,g=o!==s?"nextSibling":"previousSibling",m=t.parentNode,y=a&&t.nodeName.toLowerCase(),x=!u&&!a;if(m){if(o){while(g){f=t;while(f=f[g])if(a?f.nodeName.toLowerCase()===y:1===f.nodeType)return!1;d=g="only"===e&&!d&&"nextSibling"}return!0}if(d=[s?m.firstChild:m.lastChild],s&&x){c=m[v]||(m[v]={}),l=c[e]||[],h=l[0]===w&&l[1],p=l[0]===w&&l[2],f=h&&m.childNodes[h];while(f=++h&&f&&f[g]||(p=h=0)||d.pop())if(1===f.nodeType&&++p&&f===t){c[e]=[w,h,p];break}}else if(x&&(l=(t[v]||(t[v]={}))[e])&&l[0]===w)p=l[1];else while(f=++h&&f&&f[g]||(p=h=0)||d.pop())if((a?f.nodeName.toLowerCase()===y:1===f.nodeType)&&++p&&(x&&((f[v]||(f[v]={}))[e]=[w,p]),f===t))break;return p-=i,p===r||0===p%r&&p/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||ot.error("unsupported pseudo: "+e);return r[v]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ut(function(e,n){var i,o=r(e,t),s=o.length;while(s--)i=P.call(e,o[s]),e[i]=!(n[i]=o[s])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ut(function(e){var t=[],n=[],r=a(e.replace(z,"$1"));return r[v]?ut(function(e,t,n,i){var o,s=r(e,null,i,[]),a=e.length;while(a--)(o=s[a])&&(e[a]=!(t[a]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ut(function(e){return function(t){return ot(e,t).length>0}}),contains:ut(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ut(function(e){return G.test(e||"")||ot.error("unsupported lang: "+e),e=e.replace(nt,rt).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===p},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return et.test(e.nodeName)},input:function(e){return Z.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:yt(function(){return[0]}),last:yt(function(e,t){return[t-1]}),eq:yt(function(e,t,n){return[0>n?n+t:n]}),even:yt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:yt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:yt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:yt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(t in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[t]=gt(t);for(t in{submit:!0,reset:!0})i.pseudos[t]=mt(t);function vt(e,t){var n,r,o,s,a,u,l,c=k[e+" "];if(c)return t?0:c.slice(0);a=e,u=[],l=i.preFilter;while(a){(!n||(r=_.exec(a)))&&(r&&(a=a.slice(r[0].length)||a),u.push(o=[])),n=!1,(r=X.exec(a))&&(n=r.shift(),o.push({value:n,type:r[0].replace(z," ")}),a=a.slice(n.length));for(s in i.filter)!(r=J[s].exec(a))||l[s]&&!(r=l[s](r))||(n=r.shift(),o.push({value:n,type:s,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?ot.error(e):k(e,u).slice(0)}function xt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function bt(e,t,n){var i=t.dir,o=n&&"parentNode"===i,s=T++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,a){var u,l,c,f=w+" "+s;if(a){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,a))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[v]||(t[v]={}),(l=c[i])&&l[0]===f){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[f],l[1]=e(t,n,a)||r,l[1]===!0)return!0}}function wt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function Tt(e,t,n,r,i){var o,s=[],a=0,u=e.length,l=null!=t;for(;u>a;a++)(o=e[a])&&(!n||n(o,r,i))&&(s.push(o),l&&t.push(a));return s}function Ct(e,t,n,r,i,o){return r&&!r[v]&&(r=Ct(r)),i&&!i[v]&&(i=Ct(i,o)),ut(function(o,s,a,u){var l,c,f,p=[],h=[],d=s.length,g=o||Et(t||"*",a.nodeType?[a]:a,[]),m=!e||!o&&t?g:Tt(g,p,e,a,u),y=n?i||(o?e:d||r)?[]:s:m;if(n&&n(m,y,a,u),r){l=Tt(y,h),r(l,[],a,u),c=l.length;while(c--)(f=l[c])&&(y[h[c]]=!(m[h[c]]=f))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(f=y[c])&&l.push(m[c]=f);i(null,y=[],l,u)}c=y.length;while(c--)(f=y[c])&&(l=i?P.call(o,f):p[c])>-1&&(o[l]=!(s[l]=f))}}else y=Tt(y===s?y.splice(d,y.length):y),i?i(null,s,y,u):O.apply(s,y)})}function kt(e){var t,n,r,o=e.length,s=i.relative[e[0].type],a=s||i.relative[" "],l=s?1:0,c=bt(function(e){return e===t},a,!0),f=bt(function(e){return P.call(t,e)>-1},a,!0),p=[function(e,n,r){return!s&&(r||n!==u)||((t=n).nodeType?c(e,n,r):f(e,n,r))}];for(;o>l;l++)if(n=i.relative[e[l].type])p=[bt(wt(p),n)];else{if(n=i.filter[e[l].type].apply(null,e[l].matches),n[v]){for(r=++l;o>r;r++)if(i.relative[e[r].type])break;return Ct(l>1&&wt(p),l>1&&xt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&kt(e.slice(l,r)),o>r&&kt(e=e.slice(r)),o>r&&xt(e))}p.push(n)}return wt(p)}function Nt(e,t){var n=0,o=t.length>0,s=e.length>0,a=function(a,l,c,p,h){var d,g,m,y=[],v=0,x="0",b=a&&[],T=null!=h,C=u,k=a||s&&i.find.TAG("*",h&&l.parentNode||l),N=w+=null==C?1:Math.random()||.1;for(T&&(u=l!==f&&l,r=n);null!=(d=k[x]);x++){if(s&&d){g=0;while(m=e[g++])if(m(d,l,c)){p.push(d);break}T&&(w=N,r=++n)}o&&((d=!m&&d)&&v--,a&&b.push(d))}if(v+=x,o&&x!==v){g=0;while(m=t[g++])m(b,y,l,c);if(a){if(v>0)while(x--)b[x]||y[x]||(y[x]=H.call(p));y=Tt(y)}O.apply(p,y),T&&!a&&y.length>0&&v+t.length>1&&ot.uniqueSort(p)}return T&&(w=N,u=C),b};return o?ut(a):a}a=ot.compile=function(e,t){var n,r=[],i=[],o=N[e+" "];if(!o){t||(t=vt(e)),n=t.length;while(n--)o=kt(t[n]),o[v]?r.push(o):i.push(o);o=N(e,Nt(i,r))}return o};function Et(e,t,n){var r=0,i=t.length;for(;i>r;r++)ot(e,t[r],n);return n}function St(e,t,r,o){var s,u,l,c,f,p=vt(e);if(!o&&1===p.length){if(u=p[0]=p[0].slice(0),u.length>2&&"ID"===(l=u[0]).type&&n.getById&&9===t.nodeType&&h&&i.relative[u[1].type]){if(t=(i.find.ID(l.matches[0].replace(nt,rt),t)||[])[0],!t)return r;e=e.slice(u.shift().value.length)}s=J.needsContext.test(e)?0:u.length;while(s--){if(l=u[s],i.relative[c=l.type])break;if((f=i.find[c])&&(o=f(l.matches[0].replace(nt,rt),U.test(u[0].type)&&t.parentNode||t))){if(u.splice(s,1),e=o.length&&xt(u),!e)return O.apply(r,o),r;break}}}return a(e,p)(o,t,!h,r,U.test(e)),r}i.pseudos.nth=i.pseudos.eq;function jt(){}jt.prototype=i.filters=i.pseudos,i.setFilters=new jt,n.sortStable=v.split("").sort(S).join("")===v,c(),[0,0].sort(S),n.detectDuplicates=E,x.find=ot,x.expr=ot.selectors,x.expr[":"]=x.expr.pseudos,x.unique=ot.uniqueSort,x.text=ot.getText,x.isXMLDoc=ot.isXML,x.contains=ot.contains}(e);var D={};function A(e){var t=D[e]={};return x.each(e.match(w)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?D[e]||A(e):x.extend({},e);var t,n,r,i,o,s,a=[],u=!e.once&&[],l=function(f){for(t=e.memory&&f,n=!0,s=i||0,i=0,o=a.length,r=!0;a&&o>s;s++)if(a[s].apply(f[0],f[1])===!1&&e.stopOnFalse){t=!1;break}r=!1,a&&(u?u.length&&l(u.shift()):t?a=[]:c.disable())},c={add:function(){if(a){var n=a.length;(function s(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&c.has(n)||a.push(n):n&&n.length&&"string"!==r&&s(n)})})(arguments),r?o=a.length:t&&(i=n,l(t))}return this},remove:function(){return a&&x.each(arguments,function(e,t){var n;while((n=x.inArray(t,a,n))>-1)a.splice(n,1),r&&(o>=n&&o--,s>=n&&s--)}),this},has:function(e){return e?x.inArray(e,a)>-1:!(!a||!a.length)},empty:function(){return a=[],o=0,this},disable:function(){return a=u=t=undefined,this},disabled:function(){return!a},lock:function(){return u=undefined,t||c.disable(),this},locked:function(){return!u},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!a||n&&!u||(r?u.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!n}};return c},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var s=o[0],a=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var s=o[2],a=o[3];r[o[1]]=s.add,a&&s.add(function(){n=a},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=s.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=d.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),s=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?d.call(arguments):r,n===a?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},a,u,l;if(r>1)for(a=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(s(t,l,n)).fail(o.reject).progress(s(t,u,a)):--i;return i||o.resolveWith(l,n),o.promise()}}),x.support=function(t){var n=o.createElement("input"),r=o.createDocumentFragment(),i=o.createElement("div"),s=o.createElement("select"),a=s.appendChild(o.createElement("option"));return n.type?(n.type="checkbox",t.checkOn=""!==n.value,t.optSelected=a.selected,t.reliableMarginRight=!0,t.boxSizingReliable=!0,t.pixelPosition=!1,n.checked=!0,t.noCloneChecked=n.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!a.disabled,n=o.createElement("input"),n.value="t",n.type="radio",t.radioValue="t"===n.value,n.setAttribute("checked","t"),n.setAttribute("name","t"),r.appendChild(n),t.checkClone=r.cloneNode(!0).cloneNode(!0).lastChild.checked,t.focusinBubbles="onfocusin"in e,i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===i.style.backgroundClip,x(function(){var n,r,s="padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box",a=o.getElementsByTagName("body")[0];a&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",a.appendChild(n).appendChild(i),i.innerHTML="",i.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%",x.swap(a,null!=a.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===i.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(i,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(i,null)||{width:"4px"}).width,r=i.appendChild(o.createElement("div")),r.style.cssText=i.style.cssText=s,r.style.marginRight=r.style.width="0",i.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),a.removeChild(n))}),t):t}({});var L,H,q=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,O=/([A-Z])/g;function F(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=x.expando+Math.random()}F.uid=1,F.accepts=function(e){return e.nodeType?1===e.nodeType||9===e.nodeType:!0},F.prototype={key:function(e){if(!F.accepts(e))return 0;var t={},n=e[this.expando];if(!n){n=F.uid++;try{t[this.expando]={value:n},Object.defineProperties(e,t)}catch(r){t[this.expando]=n,x.extend(e,t)}}return this.cache[n]||(this.cache[n]={}),n},set:function(e,t,n){var r,i=this.key(e),o=this.cache[i];if("string"==typeof t)o[t]=n;else if(x.isEmptyObject(o))x.extend(this.cache[i],t);else for(r in t)o[r]=t[r];return o},get:function(e,t){var n=this.cache[this.key(e)];return t===undefined?n:n[t]},access:function(e,t,n){return t===undefined||t&&"string"==typeof t&&n===undefined?this.get(e,t):(this.set(e,t,n),n!==undefined?n:t)},remove:function(e,t){var n,r,i,o=this.key(e),s=this.cache[o];if(t===undefined)this.cache[o]={};else{x.isArray(t)?r=t.concat(t.map(x.camelCase)):(i=x.camelCase(t),t in s?r=[t,i]:(r=i,r=r in s?[r]:r.match(w)||[])),n=r.length;while(n--)delete s[r[n]]}},hasData:function(e){return!x.isEmptyObject(this.cache[e[this.expando]]||{})},discard:function(e){e[this.expando]&&delete this.cache[e[this.expando]]}},L=new F,H=new F,x.extend({acceptData:F.accepts,hasData:function(e){return L.hasData(e)||H.hasData(e)},data:function(e,t,n){return L.access(e,t,n)},removeData:function(e,t){L.remove(e,t)},_data:function(e,t,n){return H.access(e,t,n)},_removeData:function(e,t){H.remove(e,t)}}),x.fn.extend({data:function(e,t){var n,r,i=this[0],o=0,s=null;if(e===undefined){if(this.length&&(s=L.get(i),1===i.nodeType&&!H.get(i,"hasDataAttrs"))){for(n=i.attributes;n.length>o;o++)r=n[o].name,0===r.indexOf("data-")&&(r=x.camelCase(r.slice(5)),P(i,r,s[r]));H.set(i,"hasDataAttrs",!0)}return s}return"object"==typeof e?this.each(function(){L.set(this,e)}):x.access(this,function(t){var n,r=x.camelCase(e);if(i&&t===undefined){if(n=L.get(i,e),n!==undefined)return n;if(n=L.get(i,r),n!==undefined)return n;if(n=P(i,r,undefined),n!==undefined)return n}else this.each(function(){var n=L.get(this,r);L.set(this,r,t),-1!==e.indexOf("-")&&n!==undefined&&L.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){L.remove(this,e)})}});function P(e,t,n){var r;if(n===undefined&&1===e.nodeType)if(r="data-"+t.replace(O,"-$1").toLowerCase(),n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:q.test(n)?JSON.parse(n):n}catch(i){}L.set(e,t,n)}else n=undefined;return n}x.extend({queue:function(e,t,n){var r;return e?(t=(t||"fx")+"queue",r=H.get(e,t),n&&(!r||x.isArray(n)?r=H.access(e,t,x.makeArray(n)):r.push(n)),r||[]):undefined},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),s=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,s,o)),!r&&o&&o.empty.fire() |
|
5 |
-},_queueHooks:function(e,t){var n=t+"queueHooks";return H.get(e,n)||H.access(e,n,{empty:x.Callbacks("once memory").add(function(){H.remove(e,[t+"queue",n])})})}}),x.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),n>arguments.length?x.queue(this[0],e):t===undefined?this:this.each(function(){var n=x.queue(this,e,t);x._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=x.Deferred(),o=this,s=this.length,a=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=undefined),e=e||"fx";while(s--)n=H.get(o[s],e+"queueHooks"),n&&n.empty&&(r++,n.empty.add(a));return a(),i.promise(t)}});var R,M,W=/[\t\r\n\f]/g,$=/\r/g,B=/^(?:input|select|textarea|button)$/i;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[x.propFix[e]||e]})},addClass:function(e){var t,n,r,i,o,s=0,a=this.length,u="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,s=0,a=this.length,u=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,i="boolean"==typeof t;return x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,s=0,a=x(this),u=t,l=e.match(w)||[];while(o=l[s++])u=i?u:!a.hasClass(o),a[u?"addClass":"removeClass"](o)}else(n===r||"boolean"===n)&&(this.className&&H.set(this,"__className__",this.className),this.className=this.className||e===!1?"":H.get(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(W," ").indexOf(t)>=0)return!0;return!1},val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=x.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,x(this).val()):e,null==i?i="":"number"==typeof i?i+="":x.isArray(i)&&(i=x.map(i,function(e){return null==e?"":e+""})),t=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&t.set(this,i,"value")!==undefined||(this.value=i))});if(i)return t=x.valHooks[i.type]||x.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&(n=t.get(i,"value"))!==undefined?n:(n=i.value,"string"==typeof n?n.replace($,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,s=o?null:[],a=o?i+1:r.length,u=0>i?a:o?i:0;for(;a>u;u++)if(n=r[u],!(!n.selected&&u!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),s=i.length;while(s--)r=i[s],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,t,n){var i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===r?x.prop(e,t,n):(1===s&&x.isXMLDoc(e)||(t=t.toLowerCase(),i=x.attrHooks[t]||(x.expr.match.bool.test(t)?M:R)),n===undefined?i&&"get"in i&&null!==(o=i.get(e,t))?o:(o=x.find.attr(e,t),null==o?undefined:o):null!==n?i&&"set"in i&&(o=i.set(e,n,t))!==undefined?o:(e.setAttribute(t,n+""),n):(x.removeAttr(e,t),undefined))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)&&(e[r]=!1),e.removeAttribute(n)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,t,n){var r,i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return o=1!==s||!x.isXMLDoc(e),o&&(t=x.propFix[t]||t,i=x.propHooks[t]),n!==undefined?i&&"set"in i&&(r=i.set(e,n,t))!==undefined?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){return e.hasAttribute("tabindex")||B.test(e.nodeName)||e.href?e.tabIndex:-1}}}}),M={set:function(e,t,n){return t===!1?x.removeAttr(e,n):e.setAttribute(n,n),n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,t){var n=x.expr.attrHandle[t]||x.find.attr;x.expr.attrHandle[t]=function(e,t,r){var i=x.expr.attrHandle[t],o=r?undefined:(x.expr.attrHandle[t]=undefined)!=n(e,t,r)?t.toLowerCase():null;return x.expr.attrHandle[t]=i,o}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,t){return x.isArray(t)?e.checked=x.inArray(x(e).val(),t)>=0:undefined}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var I=/^key/,z=/^(?:mouse|contextmenu)|click/,_=/^(?:focusinfocus|focusoutblur)$/,X=/^([^.]*)(?:\.(.+)|)$/;function U(){return!0}function Y(){return!1}function V(){try{return o.activeElement}catch(e){}}x.event={global:{},add:function(e,t,n,i,o){var s,a,u,l,c,f,p,h,d,g,m,y=H.get(e);if(y){n.handler&&(s=n,n=s.handler,o=s.selector),n.guid||(n.guid=x.guid++),(l=y.events)||(l=y.events={}),(a=y.handle)||(a=y.handle=function(e){return typeof x===r||e&&x.event.triggered===e.type?undefined:x.event.dispatch.apply(a.elem,arguments)},a.elem=e),t=(t||"").match(w)||[""],c=t.length;while(c--)u=X.exec(t[c])||[],d=m=u[1],g=(u[2]||"").split(".").sort(),d&&(p=x.event.special[d]||{},d=(o?p.delegateType:p.bindType)||d,p=x.event.special[d]||{},f=x.extend({type:d,origType:m,data:i,handler:n,guid:n.guid,selector:o,needsContext:o&&x.expr.match.needsContext.test(o),namespace:g.join(".")},s),(h=l[d])||(h=l[d]=[],h.delegateCount=0,p.setup&&p.setup.call(e,i,g,a)!==!1||e.addEventListener&&e.addEventListener(d,a,!1)),p.add&&(p.add.call(e,f),f.handler.guid||(f.handler.guid=n.guid)),o?h.splice(h.delegateCount++,0,f):h.push(f),x.event.global[d]=!0);e=null}},remove:function(e,t,n,r,i){var o,s,a,u,l,c,f,p,h,d,g,m=H.hasData(e)&&H.get(e);if(m&&(u=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(a=X.exec(t[l])||[],h=g=a[1],d=(a[2]||"").split(".").sort(),h){f=x.event.special[h]||{},h=(r?f.delegateType:f.bindType)||h,p=u[h]||[],a=a[2]&&RegExp("(^|\\.)"+d.join("\\.(?:.*\\.|)")+"(\\.|$)"),s=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||a&&!a.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));s&&!p.length&&(f.teardown&&f.teardown.call(e,d,m.handle)!==!1||x.removeEvent(e,h,m.handle),delete u[h])}else for(h in u)x.event.remove(e,h+t[l],n,r,!0);x.isEmptyObject(u)&&(delete m.handle,H.remove(e,"events"))}},trigger:function(t,n,r,i){var s,a,u,l,c,f,p,h=[r||o],d=y.call(t,"type")?t.type:t,g=y.call(t,"namespace")?t.namespace.split("."):[];if(a=u=r=r||o,3!==r.nodeType&&8!==r.nodeType&&!_.test(d+x.event.triggered)&&(d.indexOf(".")>=0&&(g=d.split("."),d=g.shift(),g.sort()),c=0>d.indexOf(":")&&"on"+d,t=t[x.expando]?t:new x.Event(d,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=g.join("."),t.namespace_re=t.namespace?RegExp("(^|\\.)"+g.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=undefined,t.target||(t.target=r),n=null==n?[t]:x.makeArray(n,[t]),p=x.event.special[d]||{},i||!p.trigger||p.trigger.apply(r,n)!==!1)){if(!i&&!p.noBubble&&!x.isWindow(r)){for(l=p.delegateType||d,_.test(l+d)||(a=a.parentNode);a;a=a.parentNode)h.push(a),u=a;u===(r.ownerDocument||o)&&h.push(u.defaultView||u.parentWindow||e)}s=0;while((a=h[s++])&&!t.isPropagationStopped())t.type=s>1?l:p.bindType||d,f=(H.get(a,"events")||{})[t.type]&&H.get(a,"handle"),f&&f.apply(a,n),f=c&&a[c],f&&x.acceptData(a)&&f.apply&&f.apply(a,n)===!1&&t.preventDefault();return t.type=d,i||t.isDefaultPrevented()||p._default&&p._default.apply(h.pop(),n)!==!1||!x.acceptData(r)||c&&x.isFunction(r[d])&&!x.isWindow(r)&&(u=r[c],u&&(r[c]=null),x.event.triggered=d,r[d](),x.event.triggered=undefined,u&&(r[c]=u)),t.result}},dispatch:function(e){e=x.event.fix(e);var t,n,r,i,o,s=[],a=d.call(arguments),u=(H.get(this,"events")||{})[e.type]||[],l=x.event.special[e.type]||{};if(a[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),t=0;while((i=s[t++])&&!e.isPropagationStopped()){e.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(o.namespace))&&(e.handleObj=o,e.data=o.data,r=((x.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,a),r!==undefined&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,s=[],a=t.delegateCount,u=e.target;if(a&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!==this;u=u.parentNode||this)if(u.disabled!==!0||"click"!==e.type){for(r=[],n=0;a>n;n++)o=t[n],i=o.selector+" ",r[i]===undefined&&(r[i]=o.needsContext?x(i,this).index(u)>=0:x.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&s.push({elem:u,handlers:r})}return t.length>a&&s.push({elem:this,handlers:t.slice(a)}),s},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,t){var n,r,i,s=t.button;return null==e.pageX&&null!=t.clientX&&(n=e.target.ownerDocument||o,r=n.documentElement,i=n.body,e.pageX=t.clientX+(r&&r.scrollLeft||i&&i.scrollLeft||0)-(r&&r.clientLeft||i&&i.clientLeft||0),e.pageY=t.clientY+(r&&r.scrollTop||i&&i.scrollTop||0)-(r&&r.clientTop||i&&i.clientTop||0)),e.which||s===undefined||(e.which=1&s?1:2&s?3:4&s?2:0),e}},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,s=e,a=this.fixHooks[i];a||(this.fixHooks[i]=a=z.test(i)?this.mouseHooks:I.test(i)?this.keyHooks:{}),r=a.props?this.props.concat(a.props):this.props,e=new x.Event(s),t=r.length;while(t--)n=r[t],e[n]=s[n];return e.target||(e.target=o),3===e.target.nodeType&&(e.target=e.target.parentNode),a.filter?a.filter(e,s):e},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==V()&&this.focus?(this.focus(),!1):undefined},delegateType:"focusin"},blur:{trigger:function(){return this===V()&&this.blur?(this.blur(),!1):undefined},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&x.nodeName(this,"input")?(this.click(),!1):undefined},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==undefined&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)},x.Event=function(e,t){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.getPreventDefault&&e.getPreventDefault()?U:Y):this.type=e,t&&x.extend(this,t),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,undefined):new x.Event(e,t)},x.Event.prototype={isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=U,e&&e.preventDefault&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=U,e&&e.stopPropagation&&e.stopPropagation()},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=U,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,t,n,r,i){var o,s;if("object"==typeof e){"string"!=typeof t&&(n=n||t,t=undefined);for(s in e)this.on(s,t,n,e[s],i);return this}if(null==n&&null==r?(r=t,n=t=undefined):null==r&&("string"==typeof t?(r=n,n=undefined):(r=n,n=t,t=undefined)),r===!1)r=Y;else if(!r)return this;return 1===i&&(o=r,r=function(e){return x().off(e),o.apply(this,arguments)},r.guid=o.guid||(o.guid=x.guid++)),this.each(function(){x.event.add(this,e,r,n,t)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,x(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return(t===!1||"function"==typeof t)&&(n=t,t=undefined),n===!1&&(n=Y),this.each(function(){x.event.remove(this,e,n,t)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?x.event.trigger(e,t,n,!0):undefined}});var G=/^.[^:#\[\.,]*$/,J=/^(?:parents|prev(?:Until|All))/,Q=x.expr.match.needsContext,K={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t=x(e,this),n=t.length;return this.filter(function(){var e=0;for(;n>e;e++)if(x.contains(this,t[e]))return!0})},not:function(e){return this.pushStack(et(this,e||[],!0))},filter:function(e){return this.pushStack(et(this,e||[],!1))},is:function(e){return!!et(this,"string"==typeof e&&Q.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],s=Q.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(s?s.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?g.call(x(e),this[0]):g.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function Z(e,t){while((e=e[t])&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return Z(e,"nextSibling")},prev:function(e){return Z(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(K[e]||x.unique(i),J.test(e)&&i.reverse()),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,t,n){var r=[],i=n!==undefined;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&x(e).is(n))break;r.push(e)}return r},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function et(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(G.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return g.call(t,e)>=0!==n})}var tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,nt=/<([\w:]+)/,rt=/<|&#?\w+;/,it=/<(?:script|style|link)/i,ot=/^(?:checkbox|radio)$/i,st=/checked\s*(?:[^=]|=\s*.checked.)/i,at=/^$|\/(?:java|ecma)script/i,ut=/^true\/(.*)/,lt=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,ct={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ct.optgroup=ct.option,ct.tbody=ct.tfoot=ct.colgroup=ct.caption=ct.thead,ct.th=ct.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===undefined?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=ft(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=ft(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(mt(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&dt(mt(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++)1===e.nodeType&&(x.cleanData(mt(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var t=this[0]||{},n=0,r=this.length;if(e===undefined&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!it.test(e)&&!ct[(nt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(tt,"<$1></$2>");try{for(;r>n;n++)t=this[n]||{},1===t.nodeType&&(x.cleanData(mt(t,!1)),t.innerHTML=e);t=0}catch(i){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=p.apply([],e);var r,i,o,s,a,u,l=0,c=this.length,f=this,h=c-1,d=e[0],g=x.isFunction(d);if(g||!(1>=c||"string"!=typeof d||x.support.checkClone)&&st.test(d))return this.each(function(r){var i=f.eq(r);g&&(e[0]=d.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(r=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),i=r.firstChild,1===r.childNodes.length&&(r=i),i)){for(o=x.map(mt(r,"script"),pt),s=o.length;c>l;l++)a=r,l!==h&&(a=x.clone(a,!0,!0),s&&x.merge(o,mt(a,"script"))),t.call(this[l],a,l);if(s)for(u=o[o.length-1].ownerDocument,x.map(o,ht),l=0;s>l;l++)a=o[l],at.test(a.type||"")&&!H.access(a,"globalEval")&&x.contains(u,a)&&(a.src?x._evalUrl(a.src):x.globalEval(a.textContent.replace(lt,"")))}return this}}),x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=[],i=x(e),o=i.length-1,s=0;for(;o>=s;s++)n=s===o?this:this.clone(!0),x(i[s])[t](n),h.apply(r,n.get());return this.pushStack(r)}}),x.extend({clone:function(e,t,n){var r,i,o,s,a=e.cloneNode(!0),u=x.contains(e.ownerDocument,e);if(!(x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(s=mt(a),o=mt(e),r=0,i=o.length;i>r;r++)yt(o[r],s[r]);if(t)if(n)for(o=o||mt(e),s=s||mt(a),r=0,i=o.length;i>r;r++)gt(o[r],s[r]);else gt(e,a);return s=mt(a,"script"),s.length>0&&dt(s,!u&&mt(e,"script")),a},buildFragment:function(e,t,n,r){var i,o,s,a,u,l,c=0,f=e.length,p=t.createDocumentFragment(),h=[];for(;f>c;c++)if(i=e[c],i||0===i)if("object"===x.type(i))x.merge(h,i.nodeType?[i]:i);else if(rt.test(i)){o=o||p.appendChild(t.createElement("div")),s=(nt.exec(i)||["",""])[1].toLowerCase(),a=ct[s]||ct._default,o.innerHTML=a[1]+i.replace(tt,"<$1></$2>")+a[2],l=a[0];while(l--)o=o.firstChild;x.merge(h,o.childNodes),o=p.firstChild,o.textContent=""}else h.push(t.createTextNode(i));p.textContent="",c=0;while(i=h[c++])if((!r||-1===x.inArray(i,r))&&(u=x.contains(i.ownerDocument,i),o=mt(p.appendChild(i),"script"),u&&dt(o),n)){l=0;while(i=o[l++])at.test(i.type||"")&&n.push(i)}return p},cleanData:function(e){var t,n,r,i,o,s,a=x.event.special,u=0;for(;(n=e[u])!==undefined;u++){if(F.accepts(n)&&(o=n[H.expando],o&&(t=H.cache[o]))){if(r=Object.keys(t.events||{}),r.length)for(s=0;(i=r[s])!==undefined;s++)a[i]?x.event.remove(n,i):x.removeEvent(n,i,t.handle);H.cache[o]&&delete H.cache[o]}delete L.cache[n[L.expando]]}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}});function ft(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function pt(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function ht(e){var t=ut.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function dt(e,t){var n=e.length,r=0;for(;n>r;r++)H.set(e[r],"globalEval",!t||H.get(t[r],"globalEval"))}function gt(e,t){var n,r,i,o,s,a,u,l;if(1===t.nodeType){if(H.hasData(e)&&(o=H.access(e),s=H.set(t,o),l=o.events)){delete s.handle,s.events={};for(i in l)for(n=0,r=l[i].length;r>n;n++)x.event.add(t,i,l[i][n])}L.hasData(e)&&(a=L.access(e),u=x.extend({},a),L.set(t,u))}}function mt(e,t){var n=e.getElementsByTagName?e.getElementsByTagName(t||"*"):e.querySelectorAll?e.querySelectorAll(t||"*"):[];return t===undefined||t&&x.nodeName(e,t)?x.merge([e],n):n}function yt(e,t){var n=t.nodeName.toLowerCase();"input"===n&&ot.test(e.type)?t.checked=e.checked:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}x.fn.extend({wrapAll:function(e){var t;return x.isFunction(e)?this.each(function(t){x(this).wrapAll(e.call(this,t))}):(this[0]&&(t=x(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this)},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var vt,xt,bt=/^(none|table(?!-c[ea]).+)/,wt=/^margin/,Tt=RegExp("^("+b+")(.*)$","i"),Ct=RegExp("^("+b+")(?!px)[a-z%]+$","i"),kt=RegExp("^([+-])=("+b+")","i"),Nt={BODY:"block"},Et={position:"absolute",visibility:"hidden",display:"block"},St={letterSpacing:0,fontWeight:400},jt=["Top","Right","Bottom","Left"],Dt=["Webkit","O","Moz","ms"];function At(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=Dt.length;while(i--)if(t=Dt[i]+n,t in e)return t;return r}function Lt(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function Ht(t){return e.getComputedStyle(t,null)}function qt(e,t){var n,r,i,o=[],s=0,a=e.length;for(;a>s;s++)r=e[s],r.style&&(o[s]=H.get(r,"olddisplay"),n=r.style.display,t?(o[s]||"none"!==n||(r.style.display=""),""===r.style.display&&Lt(r)&&(o[s]=H.access(r,"olddisplay",Rt(r.nodeName)))):o[s]||(i=Lt(r),(n&&"none"!==n||!i)&&H.set(r,"olddisplay",i?n:x.css(r,"display"))));for(s=0;a>s;s++)r=e[s],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[s]||"":"none"));return e}x.fn.extend({css:function(e,t){return x.access(this,function(e,t,n){var r,i,o={},s=0;if(x.isArray(t)){for(r=Ht(e),i=t.length;i>s;s++)o[t[s]]=x.css(e,t[s],!1,r);return o}return n!==undefined?x.style(e,t,n):x.css(e,t)},e,t,arguments.length>1)},show:function(){return qt(this,!0)},hide:function(){return qt(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:Lt(this))?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=vt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,s,a=x.camelCase(t),u=e.style;return t=x.cssProps[a]||(x.cssProps[a]=At(u,a)),s=x.cssHooks[t]||x.cssHooks[a],n===undefined?s&&"get"in s&&(i=s.get(e,!1,r))!==undefined?i:u[t]:(o=typeof n,"string"===o&&(i=kt.exec(n))&&(n=(i[1]+1)*i[2]+parseFloat(x.css(e,t)),o="number"),null==n||"number"===o&&isNaN(n)||("number"!==o||x.cssNumber[a]||(n+="px"),x.support.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),s&&"set"in s&&(n=s.set(e,n,r))===undefined||(u[t]=n)),undefined)}},css:function(e,t,n,r){var i,o,s,a=x.camelCase(t);return t=x.cssProps[a]||(x.cssProps[a]=At(e.style,a)),s=x.cssHooks[t]||x.cssHooks[a],s&&"get"in s&&(i=s.get(e,!0,n)),i===undefined&&(i=vt(e,t,r)),"normal"===i&&t in St&&(i=St[t]),""===n||n?(o=parseFloat(i),n===!0||x.isNumeric(o)?o||0:i):i}}),vt=function(e,t,n){var r,i,o,s=n||Ht(e),a=s?s.getPropertyValue(t)||s[t]:undefined,u=e.style;return s&&(""!==a||x.contains(e.ownerDocument,e)||(a=x.style(e,t)),Ct.test(a)&&wt.test(t)&&(r=u.width,i=u.minWidth,o=u.maxWidth,u.minWidth=u.maxWidth=u.width=a,a=s.width,u.width=r,u.minWidth=i,u.maxWidth=o)),a};function Ot(e,t,n){var r=Tt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function Ft(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,s=0;for(;4>o;o+=2)"margin"===n&&(s+=x.css(e,n+jt[o],!0,i)),r?("content"===n&&(s-=x.css(e,"padding"+jt[o],!0,i)),"margin"!==n&&(s-=x.css(e,"border"+jt[o]+"Width",!0,i))):(s+=x.css(e,"padding"+jt[o],!0,i),"padding"!==n&&(s+=x.css(e,"border"+jt[o]+"Width",!0,i)));return s}function Pt(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Ht(e),s=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=vt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Ct.test(i))return i;r=s&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+Ft(e,t,n||(s?"border":"content"),r,o)+"px"}function Rt(e){var t=o,n=Nt[e];return n||(n=Mt(e,t),"none"!==n&&n||(xt=(xt||x("<iframe frameborder='0' width='0' height='0'/>").css("cssText","display:block !important")).appendTo(t.documentElement),t=(xt[0].contentWindow||xt[0].contentDocument).document,t.write("<!doctype html><html><body>"),t.close(),n=Mt(e,t),xt.detach()),Nt[e]=n),n}function Mt(e,t){var n=x(t.createElement(e)).appendTo(t.body),r=x.css(n[0],"display");return n.remove(),r}x.each(["height","width"],function(e,t){x.cssHooks[t]={get:function(e,n,r){return n?0===e.offsetWidth&&bt.test(x.css(e,"display"))?x.swap(e,Et,function(){return Pt(e,t,r)}):Pt(e,t,r):undefined},set:function(e,n,r){var i=r&&Ht(e);return Ot(e,n,r?Ft(e,t,r,x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,i),i):0)}}}),x(function(){x.support.reliableMarginRight||(x.cssHooks.marginRight={get:function(e,t){return t?x.swap(e,{display:"inline-block"},vt,[e,"marginRight"]):undefined}}),!x.support.pixelPosition&&x.fn.position&&x.each(["top","left"],function(e,t){x.cssHooks[t]={get:function(e,n){return n?(n=vt(e,t),Ct.test(n)?x(e).position()[t]+"px":n):undefined}}})}),x.expr&&x.expr.filters&&(x.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight},x.expr.filters.visible=function(e){return!x.expr.filters.hidden(e)}),x.each({margin:"",padding:"",border:"Width"},function(e,t){x.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+jt[r]+t]=o[r]||o[r-2]||o[0];return i}},wt.test(e)||(x.cssHooks[e+t].set=Ot)});var Wt=/%20/g,$t=/\[\]$/,Bt=/\r?\n/g,It=/^(?:submit|button|image|reset|file)$/i,zt=/^(?:input|select|textarea|keygen)/i;x.fn.extend({serialize:function(){return x.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=x.prop(this,"elements");return e?x.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!x(this).is(":disabled")&&zt.test(this.nodeName)&&!It.test(e)&&(this.checked||!ot.test(e))}).map(function(e,t){var n=x(this).val();return null==n?null:x.isArray(n)?x.map(n,function(e){return{name:t.name,value:e.replace(Bt,"\r\n")}}):{name:t.name,value:n.replace(Bt,"\r\n")}}).get()}}),x.param=function(e,t){var n,r=[],i=function(e,t){t=x.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(t===undefined&&(t=x.ajaxSettings&&x.ajaxSettings.traditional),x.isArray(e)||e.jquery&&!x.isPlainObject(e))x.each(e,function(){i(this.name,this.value)});else for(n in e)_t(n,e[n],t,i);return r.join("&").replace(Wt,"+")};function _t(e,t,n,r){var i;if(x.isArray(t))x.each(t,function(t,i){n||$t.test(e)?r(e,i):_t(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==x.type(t))r(e,t);else for(i in t)_t(e+"["+i+"]",t[i],n,r)}x.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){x.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),x.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n) |
|
6 |
-}});var Xt,Ut,Yt=x.now(),Vt=/\?/,Gt=/#.*$/,Jt=/([?&])_=[^&]*/,Qt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Kt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Zt=/^(?:GET|HEAD)$/,en=/^\/\//,tn=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,nn=x.fn.load,rn={},on={},sn="*/".concat("*");try{Ut=i.href}catch(an){Ut=o.createElement("a"),Ut.href="",Ut=Ut.href}Xt=tn.exec(Ut.toLowerCase())||[];function un(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(w)||[];if(x.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function ln(e,t,n,r){var i={},o=e===on;function s(a){var u;return i[a]=!0,x.each(e[a]||[],function(e,a){var l=a(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):undefined:(t.dataTypes.unshift(l),s(l),!1)}),u}return s(t.dataTypes[0])||!i["*"]&&s("*")}function cn(e,t){var n,r,i=x.ajaxSettings.flatOptions||{};for(n in t)t[n]!==undefined&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&x.extend(!0,e,r),e}x.fn.load=function(e,t,n){if("string"!=typeof e&&nn)return nn.apply(this,arguments);var r,i,o,s=this,a=e.indexOf(" ");return a>=0&&(r=e.slice(a),e=e.slice(0,a)),x.isFunction(t)?(n=t,t=undefined):t&&"object"==typeof t&&(i="POST"),s.length>0&&x.ajax({url:e,type:i,dataType:"html",data:t}).done(function(e){o=arguments,s.html(r?x("<div>").append(x.parseHTML(e)).find(r):e)}).complete(n&&function(e,t){s.each(n,o||[e.responseText,t,e])}),this},x.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){x.fn[t]=function(e){return this.on(t,e)}}),x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ut,type:"GET",isLocal:Kt.test(Xt[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":sn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":x.parseJSON,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?cn(cn(e,x.ajaxSettings),t):cn(x.ajaxSettings,e)},ajaxPrefilter:un(rn),ajaxTransport:un(on),ajax:function(e,t){"object"==typeof e&&(t=e,e=undefined),t=t||{};var n,r,i,o,s,a,u,l,c=x.ajaxSetup({},t),f=c.context||c,p=c.context&&(f.nodeType||f.jquery)?x(f):x.event,h=x.Deferred(),d=x.Callbacks("once memory"),g=c.statusCode||{},m={},y={},v=0,b="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(2===v){if(!o){o={};while(t=Qt.exec(i))o[t[1].toLowerCase()]=t[2]}t=o[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===v?i:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return v||(e=y[n]=y[n]||e,m[e]=t),this},overrideMimeType:function(e){return v||(c.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>v)for(t in e)g[t]=[g[t],e[t]];else T.always(e[T.status]);return this},abort:function(e){var t=e||b;return n&&n.abort(t),k(0,t),this}};if(h.promise(T).complete=d.add,T.success=T.done,T.error=T.fail,c.url=((e||c.url||Ut)+"").replace(Gt,"").replace(en,Xt[1]+"//"),c.type=t.method||t.type||c.method||c.type,c.dataTypes=x.trim(c.dataType||"*").toLowerCase().match(w)||[""],null==c.crossDomain&&(a=tn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===Xt[1]&&a[2]===Xt[2]&&(a[3]||("http:"===a[1]?"80":"443"))===(Xt[3]||("http:"===Xt[1]?"80":"443")))),c.data&&c.processData&&"string"!=typeof c.data&&(c.data=x.param(c.data,c.traditional)),ln(rn,c,t,T),2===v)return T;u=c.global,u&&0===x.active++&&x.event.trigger("ajaxStart"),c.type=c.type.toUpperCase(),c.hasContent=!Zt.test(c.type),r=c.url,c.hasContent||(c.data&&(r=c.url+=(Vt.test(r)?"&":"?")+c.data,delete c.data),c.cache===!1&&(c.url=Jt.test(r)?r.replace(Jt,"$1_="+Yt++):r+(Vt.test(r)?"&":"?")+"_="+Yt++)),c.ifModified&&(x.lastModified[r]&&T.setRequestHeader("If-Modified-Since",x.lastModified[r]),x.etag[r]&&T.setRequestHeader("If-None-Match",x.etag[r])),(c.data&&c.hasContent&&c.contentType!==!1||t.contentType)&&T.setRequestHeader("Content-Type",c.contentType),T.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+("*"!==c.dataTypes[0]?", "+sn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)T.setRequestHeader(l,c.headers[l]);if(c.beforeSend&&(c.beforeSend.call(f,T,c)===!1||2===v))return T.abort();b="abort";for(l in{success:1,error:1,complete:1})T[l](c[l]);if(n=ln(on,c,t,T)){T.readyState=1,u&&p.trigger("ajaxSend",[T,c]),c.async&&c.timeout>0&&(s=setTimeout(function(){T.abort("timeout")},c.timeout));try{v=1,n.send(m,k)}catch(C){if(!(2>v))throw C;k(-1,C)}}else k(-1,"No Transport");function k(e,t,o,a){var l,m,y,b,w,C=t;2!==v&&(v=2,s&&clearTimeout(s),n=undefined,i=a||"",T.readyState=e>0?4:0,l=e>=200&&300>e||304===e,o&&(b=fn(c,T,o)),b=pn(c,b,T,l),l?(c.ifModified&&(w=T.getResponseHeader("Last-Modified"),w&&(x.lastModified[r]=w),w=T.getResponseHeader("etag"),w&&(x.etag[r]=w)),204===e||"HEAD"===c.type?C="nocontent":304===e?C="notmodified":(C=b.state,m=b.data,y=b.error,l=!y)):(y=C,(e||!C)&&(C="error",0>e&&(e=0))),T.status=e,T.statusText=(t||C)+"",l?h.resolveWith(f,[m,C,T]):h.rejectWith(f,[T,C,y]),T.statusCode(g),g=undefined,u&&p.trigger(l?"ajaxSuccess":"ajaxError",[T,c,l?m:y]),d.fireWith(f,[T,C]),u&&(p.trigger("ajaxComplete",[T,c]),--x.active||x.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,t){return x.get(e,undefined,t,"script")}}),x.each(["get","post"],function(e,t){x[t]=function(e,n,r,i){return x.isFunction(n)&&(i=i||r,r=n,n=undefined),x.ajax({url:e,type:t,dataType:i,data:n,success:r})}});function fn(e,t,n){var r,i,o,s,a=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),r===undefined&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in a)if(a[i]&&a[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}s||(s=i)}o=o||s}return o?(o!==u[0]&&u.unshift(o),n[o]):undefined}function pn(e,t,n,r){var i,o,s,a,u,l={},c=e.dataTypes.slice();if(c[1])for(s in e.converters)l[s.toLowerCase()]=e.converters[s];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(s=l[u+" "+o]||l["* "+o],!s)for(i in l)if(a=i.split(" "),a[1]===o&&(s=l[u+" "+a[0]]||l["* "+a[0]])){s===!0?s=l[i]:l[i]!==!0&&(o=a[0],c.unshift(a[1]));break}if(s!==!0)if(s&&e["throws"])t=s(t);else try{t=s(t)}catch(f){return{state:"parsererror",error:s?f:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",function(e){e.cache===undefined&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),x.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(r,i){t=x("<script>").prop({async:!0,charset:e.scriptCharset,src:e.url}).on("load error",n=function(e){t.remove(),n=null,e&&i("error"===e.type?404:200,e.type)}),o.head.appendChild(t[0])},abort:function(){n&&n()}}}});var hn=[],dn=/(=)\?(?=&|$)|\?\?/;x.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=hn.pop()||x.expando+"_"+Yt++;return this[e]=!0,e}}),x.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,s,a=t.jsonp!==!1&&(dn.test(t.url)?"url":"string"==typeof t.data&&!(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&dn.test(t.data)&&"data");return a||"jsonp"===t.dataTypes[0]?(i=t.jsonpCallback=x.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,a?t[a]=t[a].replace(dn,"$1"+i):t.jsonp!==!1&&(t.url+=(Vt.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return s||x.error(i+" was not called"),s[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){s=arguments},r.always(function(){e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,hn.push(i)),s&&x.isFunction(o)&&o(s[0]),s=o=undefined}),"script"):undefined}),x.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(e){}};var gn=x.ajaxSettings.xhr(),mn={0:200,1223:204},yn=0,vn={};e.ActiveXObject&&x(e).on("unload",function(){for(var e in vn)vn[e]();vn=undefined}),x.support.cors=!!gn&&"withCredentials"in gn,x.support.ajax=gn=!!gn,x.ajaxTransport(function(e){var t;return x.support.cors||gn&&!e.crossDomain?{send:function(n,r){var i,o,s=e.xhr();if(s.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(i in e.xhrFields)s[i]=e.xhrFields[i];e.mimeType&&s.overrideMimeType&&s.overrideMimeType(e.mimeType),e.crossDomain||n["X-Requested-With"]||(n["X-Requested-With"]="XMLHttpRequest");for(i in n)s.setRequestHeader(i,n[i]);t=function(e){return function(){t&&(delete vn[o],t=s.onload=s.onerror=null,"abort"===e?s.abort():"error"===e?r(s.status||404,s.statusText):r(mn[s.status]||s.status,s.statusText,"string"==typeof s.responseText?{text:s.responseText}:undefined,s.getAllResponseHeaders()))}},s.onload=t(),s.onerror=t("error"),t=vn[o=yn++]=t("abort"),s.send(e.hasContent&&e.data||null)},abort:function(){t&&t()}}:undefined});var xn,bn,wn=/^(?:toggle|show|hide)$/,Tn=RegExp("^(?:([+-])=|)("+b+")([a-z%]*)$","i"),Cn=/queueHooks$/,kn=[An],Nn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=Tn.exec(t),o=i&&i[3]||(x.cssNumber[e]?"":"px"),s=(x.cssNumber[e]||"px"!==o&&+r)&&Tn.exec(x.css(n.elem,e)),a=1,u=20;if(s&&s[3]!==o){o=o||s[3],i=i||[],s=+r||1;do a=a||".5",s/=a,x.style(n.elem,e,s+o);while(a!==(a=n.cur()/r)&&1!==a&&--u)}return i&&(n.unit=o,n.start=+s||+r||0,n.end=i[1]?s+(i[1]+1)*i[2]:+i[2]),n}]};function En(){return setTimeout(function(){xn=undefined}),xn=x.now()}function Sn(e,t,n){var r,i=(Nn[t]||[]).concat(Nn["*"]),o=0,s=i.length;for(;s>o;o++)if(r=i[o].call(n,t,e))return r}function jn(e,t,n){var r,i,o=0,s=kn.length,a=x.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=xn||En(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,s=0,u=l.tweens.length;for(;u>s;s++)l.tweens[s].run(o);return a.notifyWith(e,[l,o,n]),1>o&&u?n:(a.resolveWith(e,[l]),!1)},l=a.promise({elem:e,props:x.extend({},t),opts:x.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:xn||En(),duration:n.duration,tweens:[],createTween:function(t,n){var r=x.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?a.resolveWith(e,[l,t]):a.rejectWith(e,[l,t]),this}}),c=l.props;for(Dn(c,l.opts.specialEasing);s>o;o++)if(r=kn[o].call(l,e,c,l.opts))return r;return x.map(c,Sn,l),x.isFunction(l.opts.start)&&l.opts.start.call(e,l),x.fx.timer(x.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function Dn(e,t){var n,r,i,o,s;for(n in e)if(r=x.camelCase(n),i=t[r],o=e[n],x.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),s=x.cssHooks[r],s&&"expand"in s){o=s.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}x.Animation=x.extend(jn,{tweener:function(e,t){x.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Nn[n]=Nn[n]||[],Nn[n].unshift(t)},prefilter:function(e,t){t?kn.unshift(e):kn.push(e)}});function An(e,t,n){var r,i,o,s,a,u,l=this,c={},f=e.style,p=e.nodeType&&Lt(e),h=H.get(e,"fxshow");n.queue||(a=x._queueHooks(e,"fx"),null==a.unqueued&&(a.unqueued=0,u=a.empty.fire,a.empty.fire=function(){a.unqueued||u()}),a.unqueued++,l.always(function(){l.always(function(){a.unqueued--,x.queue(e,"fx").length||a.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[f.overflow,f.overflowX,f.overflowY],"inline"===x.css(e,"display")&&"none"===x.css(e,"float")&&(f.display="inline-block")),n.overflow&&(f.overflow="hidden",l.always(function(){f.overflow=n.overflow[0],f.overflowX=n.overflow[1],f.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],wn.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(p?"hide":"show")){if("show"!==i||!h||h[r]===undefined)continue;p=!0}c[r]=h&&h[r]||x.style(e,r)}if(!x.isEmptyObject(c)){h?"hidden"in h&&(p=h.hidden):h=H.access(e,"fxshow",{}),o&&(h.hidden=!p),p?x(e).show():l.done(function(){x(e).hide()}),l.done(function(){var t;H.remove(e,"fxshow");for(t in c)x.style(e,t,c[t])});for(r in c)s=Sn(p?h[r]:0,r,l),r in h||(h[r]=s.start,p&&(s.end=s.start,s.start="width"===r||"height"===r?1:0))}}function Ln(e,t,n,r,i){return new Ln.prototype.init(e,t,n,r,i)}x.Tween=Ln,Ln.prototype={constructor:Ln,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(x.cssNumber[n]?"":"px")},cur:function(){var e=Ln.propHooks[this.prop];return e&&e.get?e.get(this):Ln.propHooks._default.get(this)},run:function(e){var t,n=Ln.propHooks[this.prop];return this.pos=t=this.options.duration?x.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):Ln.propHooks._default.set(this),this}},Ln.prototype.init.prototype=Ln.prototype,Ln.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=x.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){x.fx.step[e.prop]?x.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[x.cssProps[e.prop]]||x.cssHooks[e.prop])?x.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},Ln.propHooks.scrollTop=Ln.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},x.each(["toggle","show","hide"],function(e,t){var n=x.fn[t];x.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(Hn(t,!0),e,r,i)}}),x.fn.extend({fadeTo:function(e,t,n,r){return this.filter(Lt).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=x.isEmptyObject(e),o=x.speed(t,n,r),s=function(){var t=jn(this,x.extend({},e),o);s.finish=function(){t.stop(!0)},(i||H.get(this,"finish"))&&t.stop(!0)};return s.finish=s,i||o.queue===!1?this.each(s):this.queue(o.queue,s)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=undefined),t&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=x.timers,s=H.get(this);if(i)s[i]&&s[i].stop&&r(s[i]);else for(i in s)s[i]&&s[i].stop&&Cn.test(i)&&r(s[i]);for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));(t||!n)&&x.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=H.get(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=x.timers,s=r?r.length:0;for(n.finish=!0,x.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;s>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function Hn(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=jt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}x.each({slideDown:Hn("show"),slideUp:Hn("hide"),slideToggle:Hn("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){x.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),x.speed=function(e,t,n){var r=e&&"object"==typeof e?x.extend({},e):{complete:n||!n&&t||x.isFunction(e)&&e,duration:e,easing:n&&t||t&&!x.isFunction(t)&&t};return r.duration=x.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in x.fx.speeds?x.fx.speeds[r.duration]:x.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){x.isFunction(r.old)&&r.old.call(this),r.queue&&x.dequeue(this,r.queue)},r},x.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},x.timers=[],x.fx=Ln.prototype.init,x.fx.tick=function(){var e,t=x.timers,n=0;for(xn=x.now();t.length>n;n++)e=t[n],e()||t[n]!==e||t.splice(n--,1);t.length||x.fx.stop(),xn=undefined},x.fx.timer=function(e){e()&&x.timers.push(e)&&x.fx.start()},x.fx.interval=13,x.fx.start=function(){bn||(bn=setInterval(x.fx.tick,x.fx.interval))},x.fx.stop=function(){clearInterval(bn),bn=null},x.fx.speeds={slow:600,fast:200,_default:400},x.fx.step={},x.expr&&x.expr.filters&&(x.expr.filters.animated=function(e){return x.grep(x.timers,function(t){return e===t.elem}).length}),x.fn.offset=function(e){if(arguments.length)return e===undefined?this:this.each(function(t){x.offset.setOffset(this,e,t)});var t,n,i=this[0],o={top:0,left:0},s=i&&i.ownerDocument;if(s)return t=s.documentElement,x.contains(t,i)?(typeof i.getBoundingClientRect!==r&&(o=i.getBoundingClientRect()),n=qn(s),{top:o.top+n.pageYOffset-t.clientTop,left:o.left+n.pageXOffset-t.clientLeft}):o},x.offset={setOffset:function(e,t,n){var r,i,o,s,a,u,l,c=x.css(e,"position"),f=x(e),p={};"static"===c&&(e.style.position="relative"),a=f.offset(),o=x.css(e,"top"),u=x.css(e,"left"),l=("absolute"===c||"fixed"===c)&&(o+u).indexOf("auto")>-1,l?(r=f.position(),s=r.top,i=r.left):(s=parseFloat(o)||0,i=parseFloat(u)||0),x.isFunction(t)&&(t=t.call(e,n,a)),null!=t.top&&(p.top=t.top-a.top+s),null!=t.left&&(p.left=t.left-a.left+i),"using"in t?t.using.call(e,p):f.css(p)}},x.fn.extend({position:function(){if(this[0]){var e,t,n=this[0],r={top:0,left:0};return"fixed"===x.css(n,"position")?t=n.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),x.nodeName(e[0],"html")||(r=e.offset()),r.top+=x.css(e[0],"borderTopWidth",!0),r.left+=x.css(e[0],"borderLeftWidth",!0)),{top:t.top-r.top-x.css(n,"marginTop",!0),left:t.left-r.left-x.css(n,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||s;while(e&&!x.nodeName(e,"html")&&"static"===x.css(e,"position"))e=e.offsetParent;return e||s})}}),x.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,n){var r="pageYOffset"===n;x.fn[t]=function(i){return x.access(this,function(t,i,o){var s=qn(t);return o===undefined?s?s[n]:t[i]:(s?s.scrollTo(r?e.pageXOffset:o,r?o:e.pageYOffset):t[i]=o,undefined)},t,i,arguments.length,null)}});function qn(e){return x.isWindow(e)?e:9===e.nodeType&&e.defaultView}x.each({Height:"height",Width:"width"},function(e,t){x.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){x.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),s=n||(r===!0||i===!0?"margin":"border");return x.access(this,function(t,n,r){var i;return x.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):r===undefined?x.css(t,n,s):x.style(t,n,r,s)},t,o?r:undefined,o,null)}})}),x.fn.size=function(){return this.length},x.fn.andSelf=x.fn.addBack,"object"==typeof module&&module&&"object"==typeof module.exports?module.exports=x:"function"==typeof define&&define.amd&&define("jquery",[],function(){return x}),"object"==typeof e&&"object"==typeof e.document&&(e.jQuery=e.$=x)})(window); |
|
4 |
+(function(e,undefined){var t,n,r=typeof undefined,i=e.location,o=e.document,s=o.documentElement,a=e.jQuery,u=e.$,l={},c=[],p="2.0.3",f=c.concat,h=c.push,d=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,x=function(e,n){return new x.fn.init(e,n,t)},b=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^-ms-/,N=/-([\da-z])/gi,E=function(e,t){return t.toUpperCase()},S=function(){o.removeEventListener("DOMContentLoaded",S,!1),e.removeEventListener("load",S,!1),x.ready()};x.fn=x.prototype={jquery:p,constructor:x,init:function(e,t,n){var r,i;if(!e)return this;if("string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:T.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof x?t[0]:t,x.merge(this,x.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:o,!0)),C.test(r[1])&&x.isPlainObject(t))for(r in t)x.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return i=o.getElementById(r[2]),i&&i.parentNode&&(this.length=1,this[0]=i),this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?n.ready(e):(e.selector!==undefined&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return d.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,t,n,r,i,o,s=arguments[0]||{},a=1,u=arguments.length,l=!1;for("boolean"==typeof s&&(l=s,s=arguments[1]||{},a=2),"object"==typeof s||x.isFunction(s)||(s={}),u===a&&(s=this,--a);u>a;a++)if(null!=(e=arguments[a]))for(t in e)n=s[t],r=e[t],s!==r&&(l&&r&&(x.isPlainObject(r)||(i=x.isArray(r)))?(i?(i=!1,o=n&&x.isArray(n)?n:[]):o=n&&x.isPlainObject(n)?n:{},s[t]=x.extend(l,o,r)):r!==undefined&&(s[t]=r));return s},x.extend({expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=a),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){(e===!0?--x.readyWait:x.isReady)||(x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(o,[x]),x.fn.trigger&&x(o).trigger("ready").off("ready")))},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray,isWindow:function(e){return null!=e&&e===e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if("object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(t){return!1}return!0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:JSON.parse,parseXML:function(e){var t,n;if(!e||"string"!=typeof e)return null;try{n=new DOMParser,t=n.parseFromString(e,"text/xml")}catch(r){t=undefined}return(!t||t.getElementsByTagName("parsererror").length)&&x.error("Invalid XML: "+e),t},noop:function(){},globalEval:function(e){var t,n=eval;e=x.trim(e),e&&(1===e.indexOf("use strict")?(t=o.createElement("script"),t.text=e,o.head.appendChild(t).parentNode.removeChild(t)):n(e))},camelCase:function(e){return e.replace(k,"ms-").replace(N,E)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,s=j(e);if(n){if(s){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(s){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:function(e){return null==e?"":v.call(e)},makeArray:function(e,t){var n=t||[];return null!=e&&(j(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:g.call(t,e,n)},merge:function(e,t){var n=t.length,r=e.length,i=0;if("number"==typeof n)for(;n>i;i++)e[r++]=t[i];else while(t[i]!==undefined)e[r++]=t[i++];return e.length=r,e},grep:function(e,t,n){var r,i=[],o=0,s=e.length;for(n=!!n;s>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,s=j(e),a=[];if(s)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(a[a.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(a[a.length]=r);return f.apply([],a)},guid:1,proxy:function(e,t){var n,r,i;return"string"==typeof t&&(n=e[t],t=e,e=n),x.isFunction(e)?(r=d.call(arguments,2),i=function(){return e.apply(t||this,r.concat(d.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):undefined},access:function(e,t,n,r,i,o,s){var a=0,u=e.length,l=null==n;if("object"===x.type(n)){i=!0;for(a in n)x.access(e,t,a,n[a],!0,o,s)}else if(r!==undefined&&(i=!0,x.isFunction(r)||(s=!0),l&&(s?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(x(e),n)})),t))for(;u>a;a++)t(e[a],n,s?r:r.call(e[a],a,t(e[a],n)));return i?e:l?t.call(e):u?t(e[0],n):o},now:Date.now,swap:function(e,t,n,r){var i,o,s={};for(o in t)s[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=s[o];return i}}),x.ready.promise=function(t){return n||(n=x.Deferred(),"complete"===o.readyState?setTimeout(x.ready):(o.addEventListener("DOMContentLoaded",S,!1),e.addEventListener("load",S,!1))),n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function j(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}t=x(o),function(e,undefined){var t,n,r,i,o,s,a,u,l,c,p,f,h,d,g,m,y,v="sizzle"+-new Date,b=e.document,w=0,T=0,C=st(),k=st(),N=st(),E=!1,S=function(e,t){return e===t?(E=!0,0):0},j=typeof undefined,D=1<<31,A={}.hasOwnProperty,L=[],q=L.pop,H=L.push,O=L.push,F=L.slice,P=L.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",W="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",$=W.replace("w","w#"),B="\\["+M+"*("+W+")"+M+"*(?:([*^$|!~]?=)"+M+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+$+")|)|)"+M+"*\\]",I=":("+W+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+B.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=RegExp("^"+M+"*,"+M+"*"),X=RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=RegExp(M+"*[+~]"),Y=RegExp("="+M+"*([^\\]'\"]*)"+M+"*\\]","g"),V=RegExp(I),G=RegExp("^"+$+"$"),J={ID:RegExp("^#("+W+")"),CLASS:RegExp("^\\.("+W+")"),TAG:RegExp("^("+W.replace("w","w*")+")"),ATTR:RegExp("^"+B),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:RegExp("^(?:"+R+")$","i"),needsContext:RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Q=/^[^{]+\{\s*\[native \w/,K=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,Z=/^(?:input|select|textarea|button)$/i,et=/^h\d$/i,tt=/'|\\/g,nt=RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),rt=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{O.apply(L=F.call(b.childNodes),b.childNodes),L[b.childNodes.length].nodeType}catch(it){O={apply:L.length?function(e,t){H.apply(e,F.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function ot(e,t,r,i){var o,s,a,u,l,f,g,m,x,w;if((t?t.ownerDocument||t:b)!==p&&c(t),t=t||p,r=r||[],!e||"string"!=typeof e)return r;if(1!==(u=t.nodeType)&&9!==u)return[];if(h&&!i){if(o=K.exec(e))if(a=o[1]){if(9===u){if(s=t.getElementById(a),!s||!s.parentNode)return r;if(s.id===a)return r.push(s),r}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(a))&&y(t,s)&&s.id===a)return r.push(s),r}else{if(o[2])return O.apply(r,t.getElementsByTagName(e)),r;if((a=o[3])&&n.getElementsByClassName&&t.getElementsByClassName)return O.apply(r,t.getElementsByClassName(a)),r}if(n.qsa&&(!d||!d.test(e))){if(m=g=v,x=t,w=9===u&&e,1===u&&"object"!==t.nodeName.toLowerCase()){f=gt(e),(g=t.getAttribute("id"))?m=g.replace(tt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",l=f.length;while(l--)f[l]=m+mt(f[l]);x=U.test(e)&&t.parentNode||t,w=f.join(",")}if(w)try{return O.apply(r,x.querySelectorAll(w)),r}catch(T){}finally{g||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,r,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>i.cacheLength&&delete t[e.shift()],t[n]=r}return t}function at(e){return e[v]=!0,e}function ut(e){var t=p.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function lt(e,t){var n=e.split("|"),r=e.length;while(r--)i.attrHandle[n[r]]=t}function ct(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function pt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return at(function(t){return t=+t,at(function(n,r){var i,o=e([],n.length,t),s=o.length;while(s--)n[i=o[s]]&&(n[i]=!(r[i]=n[i]))})})}s=ot.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},n=ot.support={},c=ot.setDocument=function(e){var t=e?e.ownerDocument||e:b,r=t.defaultView;return t!==p&&9===t.nodeType&&t.documentElement?(p=t,f=t.documentElement,h=!s(t),r&&r.attachEvent&&r!==r.top&&r.attachEvent("onbeforeunload",function(){c()}),n.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ut(function(e){return e.appendChild(t.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=ut(function(e){return e.innerHTML="<div class='a'></div><div class='a i'></div>",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),n.getById=ut(function(e){return f.appendChild(e).id=v,!t.getElementsByName||!t.getElementsByName(v).length}),n.getById?(i.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(nt,rt);return function(e){return e.getAttribute("id")===t}}):(delete i.find.ID,i.filter.ID=function(e){var t=e.replace(nt,rt);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=n.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==j?t.getElementsByTagName(e):undefined}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.CLASS=n.getElementsByClassName&&function(e,t){return typeof t.getElementsByClassName!==j&&h?t.getElementsByClassName(e):undefined},g=[],d=[],(n.qsa=Q.test(t.querySelectorAll))&&(ut(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||d.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll(":checked").length||d.push(":checked")}),ut(function(e){var n=t.createElement("input");n.setAttribute("type","hidden"),e.appendChild(n).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&d.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||d.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),d.push(",.*:")})),(n.matchesSelector=Q.test(m=f.webkitMatchesSelector||f.mozMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&ut(function(e){n.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",I)}),d=d.length&&RegExp(d.join("|")),g=g.length&&RegExp(g.join("|")),y=Q.test(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},S=f.compareDocumentPosition?function(e,r){if(e===r)return E=!0,0;var i=r.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(r);return i?1&i||!n.sortDetached&&r.compareDocumentPosition(e)===i?e===t||y(b,e)?-1:r===t||y(b,r)?1:l?P.call(l,e)-P.call(l,r):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,n){var r,i=0,o=e.parentNode,s=n.parentNode,a=[e],u=[n];if(e===n)return E=!0,0;if(!o||!s)return e===t?-1:n===t?1:o?-1:s?1:l?P.call(l,e)-P.call(l,n):0;if(o===s)return ct(e,n);r=e;while(r=r.parentNode)a.unshift(r);r=n;while(r=r.parentNode)u.unshift(r);while(a[i]===u[i])i++;return i?ct(a[i],u[i]):a[i]===b?-1:u[i]===b?1:0},t):p},ot.matches=function(e,t){return ot(e,null,null,t)},ot.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Y,"='$1']"),!(!n.matchesSelector||!h||g&&g.test(t)||d&&d.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return ot(t,p,null,[e]).length>0},ot.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},ot.attr=function(e,t){(e.ownerDocument||e)!==p&&c(e);var r=i.attrHandle[t.toLowerCase()],o=r&&A.call(i.attrHandle,t.toLowerCase())?r(e,t,!h):undefined;return o===undefined?n.attributes||!h?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null:o},ot.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},ot.uniqueSort=function(e){var t,r=[],i=0,o=0;if(E=!n.detectDuplicates,l=!n.sortStable&&e.slice(0),e.sort(S),E){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return e},o=ot.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=ot.selectors={cacheLength:50,createPseudo:at,match:J,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(nt,rt),e[3]=(e[4]||e[5]||"").replace(nt,rt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||ot.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&ot.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return J.CHILD.test(e[0])?null:(e[3]&&e[4]!==undefined?e[2]=e[4]:n&&V.test(n)&&(t=gt(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(nt,rt).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=C[e+" "];return t||(t=RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&C(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=ot.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),s="last"!==e.slice(-4),a="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,h,d,g=o!==s?"nextSibling":"previousSibling",m=t.parentNode,y=a&&t.nodeName.toLowerCase(),x=!u&&!a;if(m){if(o){while(g){p=t;while(p=p[g])if(a?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;d=g="only"===e&&!d&&"nextSibling"}return!0}if(d=[s?m.firstChild:m.lastChild],s&&x){c=m[v]||(m[v]={}),l=c[e]||[],h=l[0]===w&&l[1],f=l[0]===w&&l[2],p=h&&m.childNodes[h];while(p=++h&&p&&p[g]||(f=h=0)||d.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[w,h,f];break}}else if(x&&(l=(t[v]||(t[v]={}))[e])&&l[0]===w)f=l[1];else while(p=++h&&p&&p[g]||(f=h=0)||d.pop())if((a?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(x&&((p[v]||(p[v]={}))[e]=[w,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||ot.error("unsupported pseudo: "+e);return r[v]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?at(function(e,n){var i,o=r(e,t),s=o.length;while(s--)i=P.call(e,o[s]),e[i]=!(n[i]=o[s])}):function(e){return r(e,0,n)}):r}},pseudos:{not:at(function(e){var t=[],n=[],r=a(e.replace(z,"$1"));return r[v]?at(function(e,t,n,i){var o,s=r(e,null,i,[]),a=e.length;while(a--)(o=s[a])&&(e[a]=!(t[a]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:at(function(e){return function(t){return ot(e,t).length>0}}),contains:at(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:at(function(e){return G.test(e||"")||ot.error("unsupported lang: "+e),e=e.replace(nt,rt).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return et.test(e.nodeName)},input:function(e){return Z.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},i.pseudos.nth=i.pseudos.eq;for(t in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[t]=pt(t);for(t in{submit:!0,reset:!0})i.pseudos[t]=ft(t);function dt(){}dt.prototype=i.filters=i.pseudos,i.setFilters=new dt;function gt(e,t){var n,r,o,s,a,u,l,c=k[e+" "];if(c)return t?0:c.slice(0);a=e,u=[],l=i.preFilter;while(a){(!n||(r=_.exec(a)))&&(r&&(a=a.slice(r[0].length)||a),u.push(o=[])),n=!1,(r=X.exec(a))&&(n=r.shift(),o.push({value:n,type:r[0].replace(z," ")}),a=a.slice(n.length));for(s in i.filter)!(r=J[s].exec(a))||l[s]&&!(r=l[s](r))||(n=r.shift(),o.push({value:n,type:s,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?ot.error(e):k(e,u).slice(0)}function mt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function yt(e,t,n){var i=t.dir,o=n&&"parentNode"===i,s=T++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,a){var u,l,c,p=w+" "+s;if(a){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,a))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[v]||(t[v]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,a)||r,l[1]===!0)return!0}}function vt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,s=[],a=0,u=e.length,l=null!=t;for(;u>a;a++)(o=e[a])&&(!n||n(o,r,i))&&(s.push(o),l&&t.push(a));return s}function bt(e,t,n,r,i,o){return r&&!r[v]&&(r=bt(r)),i&&!i[v]&&(i=bt(i,o)),at(function(o,s,a,u){var l,c,p,f=[],h=[],d=s.length,g=o||Ct(t||"*",a.nodeType?[a]:a,[]),m=!e||!o&&t?g:xt(g,f,e,a,u),y=n?i||(o?e:d||r)?[]:s:m;if(n&&n(m,y,a,u),r){l=xt(y,h),r(l,[],a,u),c=l.length;while(c--)(p=l[c])&&(y[h[c]]=!(m[h[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?P.call(o,p):f[c])>-1&&(o[l]=!(s[l]=p))}}else y=xt(y===s?y.splice(d,y.length):y),i?i(null,s,y,u):O.apply(s,y)})}function wt(e){var t,n,r,o=e.length,s=i.relative[e[0].type],a=s||i.relative[" "],l=s?1:0,c=yt(function(e){return e===t},a,!0),p=yt(function(e){return P.call(t,e)>-1},a,!0),f=[function(e,n,r){return!s&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>l;l++)if(n=i.relative[e[l].type])f=[yt(vt(f),n)];else{if(n=i.filter[e[l].type].apply(null,e[l].matches),n[v]){for(r=++l;o>r;r++)if(i.relative[e[r].type])break;return bt(l>1&&vt(f),l>1&&mt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&wt(e.slice(l,r)),o>r&&wt(e=e.slice(r)),o>r&&mt(e))}f.push(n)}return vt(f)}function Tt(e,t){var n=0,o=t.length>0,s=e.length>0,a=function(a,l,c,f,h){var d,g,m,y=[],v=0,x="0",b=a&&[],T=null!=h,C=u,k=a||s&&i.find.TAG("*",h&&l.parentNode||l),N=w+=null==C?1:Math.random()||.1;for(T&&(u=l!==p&&l,r=n);null!=(d=k[x]);x++){if(s&&d){g=0;while(m=e[g++])if(m(d,l,c)){f.push(d);break}T&&(w=N,r=++n)}o&&((d=!m&&d)&&v--,a&&b.push(d))}if(v+=x,o&&x!==v){g=0;while(m=t[g++])m(b,y,l,c);if(a){if(v>0)while(x--)b[x]||y[x]||(y[x]=q.call(f));y=xt(y)}O.apply(f,y),T&&!a&&y.length>0&&v+t.length>1&&ot.uniqueSort(f)}return T&&(w=N,u=C),b};return o?at(a):a}a=ot.compile=function(e,t){var n,r=[],i=[],o=N[e+" "];if(!o){t||(t=gt(e)),n=t.length;while(n--)o=wt(t[n]),o[v]?r.push(o):i.push(o);o=N(e,Tt(i,r))}return o};function Ct(e,t,n){var r=0,i=t.length;for(;i>r;r++)ot(e,t[r],n);return n}function kt(e,t,r,o){var s,u,l,c,p,f=gt(e);if(!o&&1===f.length){if(u=f[0]=f[0].slice(0),u.length>2&&"ID"===(l=u[0]).type&&n.getById&&9===t.nodeType&&h&&i.relative[u[1].type]){if(t=(i.find.ID(l.matches[0].replace(nt,rt),t)||[])[0],!t)return r;e=e.slice(u.shift().value.length)}s=J.needsContext.test(e)?0:u.length;while(s--){if(l=u[s],i.relative[c=l.type])break;if((p=i.find[c])&&(o=p(l.matches[0].replace(nt,rt),U.test(u[0].type)&&t.parentNode||t))){if(u.splice(s,1),e=o.length&&mt(u),!e)return O.apply(r,o),r;break}}}return a(e,f)(o,t,!h,r,U.test(e)),r}n.sortStable=v.split("").sort(S).join("")===v,n.detectDuplicates=E,c(),n.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(p.createElement("div"))}),ut(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||lt("type|href|height|width",function(e,t,n){return n?undefined:e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ut(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||lt("value",function(e,t,n){return n||"input"!==e.nodeName.toLowerCase()?undefined:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||lt(R,function(e,t,n){var r;return n?undefined:(r=e.getAttributeNode(t))&&r.specified?r.value:e[t]===!0?t.toLowerCase():null}),x.find=ot,x.expr=ot.selectors,x.expr[":"]=x.expr.pseudos,x.unique=ot.uniqueSort,x.text=ot.getText,x.isXMLDoc=ot.isXML,x.contains=ot.contains}(e);var D={};function A(e){var t=D[e]={};return x.each(e.match(w)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?D[e]||A(e):x.extend({},e);var t,n,r,i,o,s,a=[],u=!e.once&&[],l=function(p){for(t=e.memory&&p,n=!0,s=i||0,i=0,o=a.length,r=!0;a&&o>s;s++)if(a[s].apply(p[0],p[1])===!1&&e.stopOnFalse){t=!1;break}r=!1,a&&(u?u.length&&l(u.shift()):t?a=[]:c.disable())},c={add:function(){if(a){var n=a.length;(function s(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&c.has(n)||a.push(n):n&&n.length&&"string"!==r&&s(n)})})(arguments),r?o=a.length:t&&(i=n,l(t))}return this},remove:function(){return a&&x.each(arguments,function(e,t){var n;while((n=x.inArray(t,a,n))>-1)a.splice(n,1),r&&(o>=n&&o--,s>=n&&s--)}),this},has:function(e){return e?x.inArray(e,a)>-1:!(!a||!a.length)},empty:function(){return a=[],o=0,this},disable:function(){return a=u=t=undefined,this},disabled:function(){return!a},lock:function(){return u=undefined,t||c.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!a||n&&!u||(t=t||[],t=[e,t.slice?t.slice():t],r?u.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!n}};return c},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var s=o[0],a=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var s=o[2],a=o[3];r[o[1]]=s.add,a&&s.add(function(){n=a},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=s.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=d.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),s=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?d.call(arguments):r,n===a?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},a,u,l;if(r>1)for(a=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(s(t,l,n)).fail(o.reject).progress(s(t,u,a)):--i;return i||o.resolveWith(l,n),o.promise()}}),x.support=function(t){var n=o.createElement("input"),r=o.createDocumentFragment(),i=o.createElement("div"),s=o.createElement("select"),a=s.appendChild(o.createElement("option"));return n.type?(n.type="checkbox",t.checkOn=""!==n.value,t.optSelected=a.selected,t.reliableMarginRight=!0,t.boxSizingReliable=!0,t.pixelPosition=!1,n.checked=!0,t.noCloneChecked=n.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!a.disabled,n=o.createElement("input"),n.value="t",n.type="radio",t.radioValue="t"===n.value,n.setAttribute("checked","t"),n.setAttribute("name","t"),r.appendChild(n),t.checkClone=r.cloneNode(!0).cloneNode(!0).lastChild.checked,t.focusinBubbles="onfocusin"in e,i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===i.style.backgroundClip,x(function(){var n,r,s="padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box",a=o.getElementsByTagName("body")[0];a&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",a.appendChild(n).appendChild(i),i.innerHTML="",i.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%",x.swap(a,null!=a.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===i.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(i,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(i,null)||{width:"4px"}).width,r=i.appendChild(o.createElement("div")),r.style.cssText=i.style.cssText=s,r.style.marginRight=r.style.width="0",i.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),a.removeChild(n))}),t):t}({});var L,q,H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,O=/([A-Z])/g;function F(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=x.expando+Math.random()}F.uid=1,F.accepts=function(e){return e.nodeType?1===e.nodeType||9===e.nodeType:!0},F.prototype={key:function(e){if(!F.accepts(e))return 0;var t={},n=e[this.expando];if(!n){n=F.uid++;try{t[this.expando]={value:n},Object.defineProperties(e,t)}catch(r){t[this.expando]=n,x.extend(e,t)}}return this.cache[n]||(this.cache[n]={}),n},set:function(e,t,n){var r,i=this.key(e),o=this.cache[i];if("string"==typeof t)o[t]=n;else if(x.isEmptyObject(o))x.extend(this.cache[i],t);else for(r in t)o[r]=t[r];return o},get:function(e,t){var n=this.cache[this.key(e)];return t===undefined?n:n[t]},access:function(e,t,n){var r;return t===undefined||t&&"string"==typeof t&&n===undefined?(r=this.get(e,t),r!==undefined?r:this.get(e,x.camelCase(t))):(this.set(e,t,n),n!==undefined?n:t)},remove:function(e,t){var n,r,i,o=this.key(e),s=this.cache[o];if(t===undefined)this.cache[o]={};else{x.isArray(t)?r=t.concat(t.map(x.camelCase)):(i=x.camelCase(t),t in s?r=[t,i]:(r=i,r=r in s?[r]:r.match(w)||[])),n=r.length;while(n--)delete s[r[n]]}},hasData:function(e){return!x.isEmptyObject(this.cache[e[this.expando]]||{})},discard:function(e){e[this.expando]&&delete this.cache[e[this.expando]]}},L=new F,q=new F,x.extend({acceptData:F.accepts,hasData:function(e){return L.hasData(e)||q.hasData(e)},data:function(e,t,n){return L.access(e,t,n)},removeData:function(e,t){L.remove(e,t)},_data:function(e,t,n){return q.access(e,t,n)},_removeData:function(e,t){q.remove(e,t)}}),x.fn.extend({data:function(e,t){var n,r,i=this[0],o=0,s=null;if(e===undefined){if(this.length&&(s=L.get(i),1===i.nodeType&&!q.get(i,"hasDataAttrs"))){for(n=i.attributes;n.length>o;o++)r=n[o].name,0===r.indexOf("data-")&&(r=x.camelCase(r.slice(5)),P(i,r,s[r]));q.set(i,"hasDataAttrs",!0)}return s}return"object"==typeof e?this.each(function(){L.set(this,e)}):x.access(this,function(t){var n,r=x.camelCase(e);if(i&&t===undefined){if(n=L.get(i,e),n!==undefined)return n;if(n=L.get(i,r),n!==undefined)return n;if(n=P(i,r,undefined),n!==undefined)return n}else this.each(function(){var n=L.get(this,r);L.set(this,r,t),-1!==e.indexOf("-")&&n!==undefined&&L.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){L.remove(this,e)})}});function P(e,t,n){var r;if(n===undefined&&1===e.nodeType)if(r="data-"+t.replace(O,"-$1").toLowerCase(),n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:H.test(n)?JSON.parse(n):n}catch(i){}L.set(e,t,n)}else n=undefined;return n}x.extend({queue:function(e,t,n){var r;return e?(t=(t||"fx")+"queue",r=q.get(e,t),n&&(!r||x.isArray(n)?r=q.access(e,t,x.makeArray(n)):r.push(n)),r||[]):undefined},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),s=function(){x.dequeue(e,t) |
|
5 |
+};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,s,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return q.get(e,n)||q.access(e,n,{empty:x.Callbacks("once memory").add(function(){q.remove(e,[t+"queue",n])})})}}),x.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),n>arguments.length?x.queue(this[0],e):t===undefined?this:this.each(function(){var n=x.queue(this,e,t);x._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=x.Deferred(),o=this,s=this.length,a=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=undefined),e=e||"fx";while(s--)n=q.get(o[s],e+"queueHooks"),n&&n.empty&&(r++,n.empty.add(a));return a(),i.promise(t)}});var R,M,W=/[\t\r\n\f]/g,$=/\r/g,B=/^(?:input|select|textarea|button)$/i;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[x.propFix[e]||e]})},addClass:function(e){var t,n,r,i,o,s=0,a=this.length,u="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,s=0,a=this.length,u=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,i=0,o=x(this),s=e.match(w)||[];while(t=s[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===r||"boolean"===n)&&(this.className&&q.set(this,"__className__",this.className),this.className=this.className||e===!1?"":q.get(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(W," ").indexOf(t)>=0)return!0;return!1},val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=x.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,x(this).val()):e,null==i?i="":"number"==typeof i?i+="":x.isArray(i)&&(i=x.map(i,function(e){return null==e?"":e+""})),t=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&t.set(this,i,"value")!==undefined||(this.value=i))});if(i)return t=x.valHooks[i.type]||x.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&(n=t.get(i,"value"))!==undefined?n:(n=i.value,"string"==typeof n?n.replace($,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,s=o?null:[],a=o?i+1:r.length,u=0>i?a:o?i:0;for(;a>u;u++)if(n=r[u],!(!n.selected&&u!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),s=i.length;while(s--)r=i[s],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,t,n){var i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===r?x.prop(e,t,n):(1===s&&x.isXMLDoc(e)||(t=t.toLowerCase(),i=x.attrHooks[t]||(x.expr.match.bool.test(t)?M:R)),n===undefined?i&&"get"in i&&null!==(o=i.get(e,t))?o:(o=x.find.attr(e,t),null==o?undefined:o):null!==n?i&&"set"in i&&(o=i.set(e,n,t))!==undefined?o:(e.setAttribute(t,n+""),n):(x.removeAttr(e,t),undefined))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)&&(e[r]=!1),e.removeAttribute(n)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,t,n){var r,i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return o=1!==s||!x.isXMLDoc(e),o&&(t=x.propFix[t]||t,i=x.propHooks[t]),n!==undefined?i&&"set"in i&&(r=i.set(e,n,t))!==undefined?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){return e.hasAttribute("tabindex")||B.test(e.nodeName)||e.href?e.tabIndex:-1}}}}),M={set:function(e,t,n){return t===!1?x.removeAttr(e,n):e.setAttribute(n,n),n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,t){var n=x.expr.attrHandle[t]||x.find.attr;x.expr.attrHandle[t]=function(e,t,r){var i=x.expr.attrHandle[t],o=r?undefined:(x.expr.attrHandle[t]=undefined)!=n(e,t,r)?t.toLowerCase():null;return x.expr.attrHandle[t]=i,o}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,t){return x.isArray(t)?e.checked=x.inArray(x(e).val(),t)>=0:undefined}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var I=/^key/,z=/^(?:mouse|contextmenu)|click/,_=/^(?:focusinfocus|focusoutblur)$/,X=/^([^.]*)(?:\.(.+)|)$/;function U(){return!0}function Y(){return!1}function V(){try{return o.activeElement}catch(e){}}x.event={global:{},add:function(e,t,n,i,o){var s,a,u,l,c,p,f,h,d,g,m,y=q.get(e);if(y){n.handler&&(s=n,n=s.handler,o=s.selector),n.guid||(n.guid=x.guid++),(l=y.events)||(l=y.events={}),(a=y.handle)||(a=y.handle=function(e){return typeof x===r||e&&x.event.triggered===e.type?undefined:x.event.dispatch.apply(a.elem,arguments)},a.elem=e),t=(t||"").match(w)||[""],c=t.length;while(c--)u=X.exec(t[c])||[],d=m=u[1],g=(u[2]||"").split(".").sort(),d&&(f=x.event.special[d]||{},d=(o?f.delegateType:f.bindType)||d,f=x.event.special[d]||{},p=x.extend({type:d,origType:m,data:i,handler:n,guid:n.guid,selector:o,needsContext:o&&x.expr.match.needsContext.test(o),namespace:g.join(".")},s),(h=l[d])||(h=l[d]=[],h.delegateCount=0,f.setup&&f.setup.call(e,i,g,a)!==!1||e.addEventListener&&e.addEventListener(d,a,!1)),f.add&&(f.add.call(e,p),p.handler.guid||(p.handler.guid=n.guid)),o?h.splice(h.delegateCount++,0,p):h.push(p),x.event.global[d]=!0);e=null}},remove:function(e,t,n,r,i){var o,s,a,u,l,c,p,f,h,d,g,m=q.hasData(e)&&q.get(e);if(m&&(u=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(a=X.exec(t[l])||[],h=g=a[1],d=(a[2]||"").split(".").sort(),h){p=x.event.special[h]||{},h=(r?p.delegateType:p.bindType)||h,f=u[h]||[],a=a[2]&&RegExp("(^|\\.)"+d.join("\\.(?:.*\\.|)")+"(\\.|$)"),s=o=f.length;while(o--)c=f[o],!i&&g!==c.origType||n&&n.guid!==c.guid||a&&!a.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(f.splice(o,1),c.selector&&f.delegateCount--,p.remove&&p.remove.call(e,c));s&&!f.length&&(p.teardown&&p.teardown.call(e,d,m.handle)!==!1||x.removeEvent(e,h,m.handle),delete u[h])}else for(h in u)x.event.remove(e,h+t[l],n,r,!0);x.isEmptyObject(u)&&(delete m.handle,q.remove(e,"events"))}},trigger:function(t,n,r,i){var s,a,u,l,c,p,f,h=[r||o],d=y.call(t,"type")?t.type:t,g=y.call(t,"namespace")?t.namespace.split("."):[];if(a=u=r=r||o,3!==r.nodeType&&8!==r.nodeType&&!_.test(d+x.event.triggered)&&(d.indexOf(".")>=0&&(g=d.split("."),d=g.shift(),g.sort()),c=0>d.indexOf(":")&&"on"+d,t=t[x.expando]?t:new x.Event(d,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=g.join("."),t.namespace_re=t.namespace?RegExp("(^|\\.)"+g.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=undefined,t.target||(t.target=r),n=null==n?[t]:x.makeArray(n,[t]),f=x.event.special[d]||{},i||!f.trigger||f.trigger.apply(r,n)!==!1)){if(!i&&!f.noBubble&&!x.isWindow(r)){for(l=f.delegateType||d,_.test(l+d)||(a=a.parentNode);a;a=a.parentNode)h.push(a),u=a;u===(r.ownerDocument||o)&&h.push(u.defaultView||u.parentWindow||e)}s=0;while((a=h[s++])&&!t.isPropagationStopped())t.type=s>1?l:f.bindType||d,p=(q.get(a,"events")||{})[t.type]&&q.get(a,"handle"),p&&p.apply(a,n),p=c&&a[c],p&&x.acceptData(a)&&p.apply&&p.apply(a,n)===!1&&t.preventDefault();return t.type=d,i||t.isDefaultPrevented()||f._default&&f._default.apply(h.pop(),n)!==!1||!x.acceptData(r)||c&&x.isFunction(r[d])&&!x.isWindow(r)&&(u=r[c],u&&(r[c]=null),x.event.triggered=d,r[d](),x.event.triggered=undefined,u&&(r[c]=u)),t.result}},dispatch:function(e){e=x.event.fix(e);var t,n,r,i,o,s=[],a=d.call(arguments),u=(q.get(this,"events")||{})[e.type]||[],l=x.event.special[e.type]||{};if(a[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),t=0;while((i=s[t++])&&!e.isPropagationStopped()){e.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(o.namespace))&&(e.handleObj=o,e.data=o.data,r=((x.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,a),r!==undefined&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,s=[],a=t.delegateCount,u=e.target;if(a&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!==this;u=u.parentNode||this)if(u.disabled!==!0||"click"!==e.type){for(r=[],n=0;a>n;n++)o=t[n],i=o.selector+" ",r[i]===undefined&&(r[i]=o.needsContext?x(i,this).index(u)>=0:x.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&s.push({elem:u,handlers:r})}return t.length>a&&s.push({elem:this,handlers:t.slice(a)}),s},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,t){var n,r,i,s=t.button;return null==e.pageX&&null!=t.clientX&&(n=e.target.ownerDocument||o,r=n.documentElement,i=n.body,e.pageX=t.clientX+(r&&r.scrollLeft||i&&i.scrollLeft||0)-(r&&r.clientLeft||i&&i.clientLeft||0),e.pageY=t.clientY+(r&&r.scrollTop||i&&i.scrollTop||0)-(r&&r.clientTop||i&&i.clientTop||0)),e.which||s===undefined||(e.which=1&s?1:2&s?3:4&s?2:0),e}},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,s=e,a=this.fixHooks[i];a||(this.fixHooks[i]=a=z.test(i)?this.mouseHooks:I.test(i)?this.keyHooks:{}),r=a.props?this.props.concat(a.props):this.props,e=new x.Event(s),t=r.length;while(t--)n=r[t],e[n]=s[n];return e.target||(e.target=o),3===e.target.nodeType&&(e.target=e.target.parentNode),a.filter?a.filter(e,s):e},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==V()&&this.focus?(this.focus(),!1):undefined},delegateType:"focusin"},blur:{trigger:function(){return this===V()&&this.blur?(this.blur(),!1):undefined},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&x.nodeName(this,"input")?(this.click(),!1):undefined},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==undefined&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)},x.Event=function(e,t){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.getPreventDefault&&e.getPreventDefault()?U:Y):this.type=e,t&&x.extend(this,t),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,undefined):new x.Event(e,t)},x.Event.prototype={isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=U,e&&e.preventDefault&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=U,e&&e.stopPropagation&&e.stopPropagation()},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=U,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,t,n,r,i){var o,s;if("object"==typeof e){"string"!=typeof t&&(n=n||t,t=undefined);for(s in e)this.on(s,t,n,e[s],i);return this}if(null==n&&null==r?(r=t,n=t=undefined):null==r&&("string"==typeof t?(r=n,n=undefined):(r=n,n=t,t=undefined)),r===!1)r=Y;else if(!r)return this;return 1===i&&(o=r,r=function(e){return x().off(e),o.apply(this,arguments)},r.guid=o.guid||(o.guid=x.guid++)),this.each(function(){x.event.add(this,e,r,n,t)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,x(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return(t===!1||"function"==typeof t)&&(n=t,t=undefined),n===!1&&(n=Y),this.each(function(){x.event.remove(this,e,n,t)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?x.event.trigger(e,t,n,!0):undefined}});var G=/^.[^:#\[\.,]*$/,J=/^(?:parents|prev(?:Until|All))/,Q=x.expr.match.needsContext,K={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t=x(e,this),n=t.length;return this.filter(function(){var e=0;for(;n>e;e++)if(x.contains(this,t[e]))return!0})},not:function(e){return this.pushStack(et(this,e||[],!0))},filter:function(e){return this.pushStack(et(this,e||[],!1))},is:function(e){return!!et(this,"string"==typeof e&&Q.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],s=Q.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(s?s.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?g.call(x(e),this[0]):g.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function Z(e,t){while((e=e[t])&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return Z(e,"nextSibling")},prev:function(e){return Z(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return e.contentDocument||x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(K[e]||x.unique(i),J.test(e)&&i.reverse()),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,t,n){var r=[],i=n!==undefined;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&x(e).is(n))break;r.push(e)}return r},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function et(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(G.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return g.call(t,e)>=0!==n})}var tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,nt=/<([\w:]+)/,rt=/<|&#?\w+;/,it=/<(?:script|style|link)/i,ot=/^(?:checkbox|radio)$/i,st=/checked\s*(?:[^=]|=\s*.checked.)/i,at=/^$|\/(?:java|ecma)script/i,ut=/^true\/(.*)/,lt=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,ct={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ct.optgroup=ct.option,ct.tbody=ct.tfoot=ct.colgroup=ct.caption=ct.thead,ct.th=ct.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===undefined?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=pt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=pt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(mt(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&dt(mt(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++)1===e.nodeType&&(x.cleanData(mt(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var t=this[0]||{},n=0,r=this.length;if(e===undefined&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!it.test(e)&&!ct[(nt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(tt,"<$1></$2>");try{for(;r>n;n++)t=this[n]||{},1===t.nodeType&&(x.cleanData(mt(t,!1)),t.innerHTML=e);t=0}catch(i){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=f.apply([],e);var r,i,o,s,a,u,l=0,c=this.length,p=this,h=c-1,d=e[0],g=x.isFunction(d);if(g||!(1>=c||"string"!=typeof d||x.support.checkClone)&&st.test(d))return this.each(function(r){var i=p.eq(r);g&&(e[0]=d.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(r=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),i=r.firstChild,1===r.childNodes.length&&(r=i),i)){for(o=x.map(mt(r,"script"),ft),s=o.length;c>l;l++)a=r,l!==h&&(a=x.clone(a,!0,!0),s&&x.merge(o,mt(a,"script"))),t.call(this[l],a,l);if(s)for(u=o[o.length-1].ownerDocument,x.map(o,ht),l=0;s>l;l++)a=o[l],at.test(a.type||"")&&!q.access(a,"globalEval")&&x.contains(u,a)&&(a.src?x._evalUrl(a.src):x.globalEval(a.textContent.replace(lt,"")))}return this}}),x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=[],i=x(e),o=i.length-1,s=0;for(;o>=s;s++)n=s===o?this:this.clone(!0),x(i[s])[t](n),h.apply(r,n.get());return this.pushStack(r)}}),x.extend({clone:function(e,t,n){var r,i,o,s,a=e.cloneNode(!0),u=x.contains(e.ownerDocument,e);if(!(x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(s=mt(a),o=mt(e),r=0,i=o.length;i>r;r++)yt(o[r],s[r]);if(t)if(n)for(o=o||mt(e),s=s||mt(a),r=0,i=o.length;i>r;r++)gt(o[r],s[r]);else gt(e,a);return s=mt(a,"script"),s.length>0&&dt(s,!u&&mt(e,"script")),a},buildFragment:function(e,t,n,r){var i,o,s,a,u,l,c=0,p=e.length,f=t.createDocumentFragment(),h=[];for(;p>c;c++)if(i=e[c],i||0===i)if("object"===x.type(i))x.merge(h,i.nodeType?[i]:i);else if(rt.test(i)){o=o||f.appendChild(t.createElement("div")),s=(nt.exec(i)||["",""])[1].toLowerCase(),a=ct[s]||ct._default,o.innerHTML=a[1]+i.replace(tt,"<$1></$2>")+a[2],l=a[0];while(l--)o=o.lastChild;x.merge(h,o.childNodes),o=f.firstChild,o.textContent=""}else h.push(t.createTextNode(i));f.textContent="",c=0;while(i=h[c++])if((!r||-1===x.inArray(i,r))&&(u=x.contains(i.ownerDocument,i),o=mt(f.appendChild(i),"script"),u&&dt(o),n)){l=0;while(i=o[l++])at.test(i.type||"")&&n.push(i)}return f},cleanData:function(e){var t,n,r,i,o,s,a=x.event.special,u=0;for(;(n=e[u])!==undefined;u++){if(F.accepts(n)&&(o=n[q.expando],o&&(t=q.cache[o]))){if(r=Object.keys(t.events||{}),r.length)for(s=0;(i=r[s])!==undefined;s++)a[i]?x.event.remove(n,i):x.removeEvent(n,i,t.handle);q.cache[o]&&delete q.cache[o]}delete L.cache[n[L.expando]]}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}});function pt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function ft(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function ht(e){var t=ut.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function dt(e,t){var n=e.length,r=0;for(;n>r;r++)q.set(e[r],"globalEval",!t||q.get(t[r],"globalEval"))}function gt(e,t){var n,r,i,o,s,a,u,l;if(1===t.nodeType){if(q.hasData(e)&&(o=q.access(e),s=q.set(t,o),l=o.events)){delete s.handle,s.events={};for(i in l)for(n=0,r=l[i].length;r>n;n++)x.event.add(t,i,l[i][n])}L.hasData(e)&&(a=L.access(e),u=x.extend({},a),L.set(t,u))}}function mt(e,t){var n=e.getElementsByTagName?e.getElementsByTagName(t||"*"):e.querySelectorAll?e.querySelectorAll(t||"*"):[];return t===undefined||t&&x.nodeName(e,t)?x.merge([e],n):n}function yt(e,t){var n=t.nodeName.toLowerCase();"input"===n&&ot.test(e.type)?t.checked=e.checked:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}x.fn.extend({wrapAll:function(e){var t;return x.isFunction(e)?this.each(function(t){x(this).wrapAll(e.call(this,t))}):(this[0]&&(t=x(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this)},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var vt,xt,bt=/^(none|table(?!-c[ea]).+)/,wt=/^margin/,Tt=RegExp("^("+b+")(.*)$","i"),Ct=RegExp("^("+b+")(?!px)[a-z%]+$","i"),kt=RegExp("^([+-])=("+b+")","i"),Nt={BODY:"block"},Et={position:"absolute",visibility:"hidden",display:"block"},St={letterSpacing:0,fontWeight:400},jt=["Top","Right","Bottom","Left"],Dt=["Webkit","O","Moz","ms"];function At(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=Dt.length;while(i--)if(t=Dt[i]+n,t in e)return t;return r}function Lt(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function qt(t){return e.getComputedStyle(t,null)}function Ht(e,t){var n,r,i,o=[],s=0,a=e.length;for(;a>s;s++)r=e[s],r.style&&(o[s]=q.get(r,"olddisplay"),n=r.style.display,t?(o[s]||"none"!==n||(r.style.display=""),""===r.style.display&&Lt(r)&&(o[s]=q.access(r,"olddisplay",Rt(r.nodeName)))):o[s]||(i=Lt(r),(n&&"none"!==n||!i)&&q.set(r,"olddisplay",i?n:x.css(r,"display"))));for(s=0;a>s;s++)r=e[s],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[s]||"":"none"));return e}x.fn.extend({css:function(e,t){return x.access(this,function(e,t,n){var r,i,o={},s=0;if(x.isArray(t)){for(r=qt(e),i=t.length;i>s;s++)o[t[s]]=x.css(e,t[s],!1,r);return o}return n!==undefined?x.style(e,t,n):x.css(e,t)},e,t,arguments.length>1)},show:function(){return Ht(this,!0)},hide:function(){return Ht(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Lt(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=vt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,s,a=x.camelCase(t),u=e.style;return t=x.cssProps[a]||(x.cssProps[a]=At(u,a)),s=x.cssHooks[t]||x.cssHooks[a],n===undefined?s&&"get"in s&&(i=s.get(e,!1,r))!==undefined?i:u[t]:(o=typeof n,"string"===o&&(i=kt.exec(n))&&(n=(i[1]+1)*i[2]+parseFloat(x.css(e,t)),o="number"),null==n||"number"===o&&isNaN(n)||("number"!==o||x.cssNumber[a]||(n+="px"),x.support.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),s&&"set"in s&&(n=s.set(e,n,r))===undefined||(u[t]=n)),undefined)}},css:function(e,t,n,r){var i,o,s,a=x.camelCase(t);return t=x.cssProps[a]||(x.cssProps[a]=At(e.style,a)),s=x.cssHooks[t]||x.cssHooks[a],s&&"get"in s&&(i=s.get(e,!0,n)),i===undefined&&(i=vt(e,t,r)),"normal"===i&&t in St&&(i=St[t]),""===n||n?(o=parseFloat(i),n===!0||x.isNumeric(o)?o||0:i):i}}),vt=function(e,t,n){var r,i,o,s=n||qt(e),a=s?s.getPropertyValue(t)||s[t]:undefined,u=e.style;return s&&(""!==a||x.contains(e.ownerDocument,e)||(a=x.style(e,t)),Ct.test(a)&&wt.test(t)&&(r=u.width,i=u.minWidth,o=u.maxWidth,u.minWidth=u.maxWidth=u.width=a,a=s.width,u.width=r,u.minWidth=i,u.maxWidth=o)),a};function Ot(e,t,n){var r=Tt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function Ft(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,s=0;for(;4>o;o+=2)"margin"===n&&(s+=x.css(e,n+jt[o],!0,i)),r?("content"===n&&(s-=x.css(e,"padding"+jt[o],!0,i)),"margin"!==n&&(s-=x.css(e,"border"+jt[o]+"Width",!0,i))):(s+=x.css(e,"padding"+jt[o],!0,i),"padding"!==n&&(s+=x.css(e,"border"+jt[o]+"Width",!0,i)));return s}function Pt(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=qt(e),s=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=vt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Ct.test(i))return i;r=s&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+Ft(e,t,n||(s?"border":"content"),r,o)+"px"}function Rt(e){var t=o,n=Nt[e];return n||(n=Mt(e,t),"none"!==n&&n||(xt=(xt||x("<iframe frameborder='0' width='0' height='0'/>").css("cssText","display:block !important")).appendTo(t.documentElement),t=(xt[0].contentWindow||xt[0].contentDocument).document,t.write("<!doctype html><html><body>"),t.close(),n=Mt(e,t),xt.detach()),Nt[e]=n),n}function Mt(e,t){var n=x(t.createElement(e)).appendTo(t.body),r=x.css(n[0],"display");return n.remove(),r}x.each(["height","width"],function(e,t){x.cssHooks[t]={get:function(e,n,r){return n?0===e.offsetWidth&&bt.test(x.css(e,"display"))?x.swap(e,Et,function(){return Pt(e,t,r)}):Pt(e,t,r):undefined},set:function(e,n,r){var i=r&&qt(e);return Ot(e,n,r?Ft(e,t,r,x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,i),i):0)}}}),x(function(){x.support.reliableMarginRight||(x.cssHooks.marginRight={get:function(e,t){return t?x.swap(e,{display:"inline-block"},vt,[e,"marginRight"]):undefined}}),!x.support.pixelPosition&&x.fn.position&&x.each(["top","left"],function(e,t){x.cssHooks[t]={get:function(e,n){return n?(n=vt(e,t),Ct.test(n)?x(e).position()[t]+"px":n):undefined}}})}),x.expr&&x.expr.filters&&(x.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight},x.expr.filters.visible=function(e){return!x.expr.filters.hidden(e)}),x.each({margin:"",padding:"",border:"Width"},function(e,t){x.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+jt[r]+t]=o[r]||o[r-2]||o[0];return i}},wt.test(e)||(x.cssHooks[e+t].set=Ot)});var Wt=/%20/g,$t=/\[\]$/,Bt=/\r?\n/g,It=/^(?:submit|button|image|reset|file)$/i,zt=/^(?:input|select|textarea|keygen)/i;x.fn.extend({serialize:function(){return x.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=x.prop(this,"elements");return e?x.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!x(this).is(":disabled")&&zt.test(this.nodeName)&&!It.test(e)&&(this.checked||!ot.test(e))}).map(function(e,t){var n=x(this).val();return null==n?null:x.isArray(n)?x.map(n,function(e){return{name:t.name,value:e.replace(Bt,"\r\n")}}):{name:t.name,value:n.replace(Bt,"\r\n")}}).get()}}),x.param=function(e,t){var n,r=[],i=function(e,t){t=x.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(t===undefined&&(t=x.ajaxSettings&&x.ajaxSettings.traditional),x.isArray(e)||e.jquery&&!x.isPlainObject(e))x.each(e,function(){i(this.name,this.value)});else for(n in e)_t(n,e[n],t,i);return r.join("&").replace(Wt,"+")};function _t(e,t,n,r){var i;if(x.isArray(t))x.each(t,function(t,i){n||$t.test(e)?r(e,i):_t(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==x.type(t))r(e,t);else for(i in t)_t(e+"["+i+"]",t[i],n,r)}x.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){x.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),x.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t) |
|
6 |
+},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var Xt,Ut,Yt=x.now(),Vt=/\?/,Gt=/#.*$/,Jt=/([?&])_=[^&]*/,Qt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Kt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Zt=/^(?:GET|HEAD)$/,en=/^\/\//,tn=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,nn=x.fn.load,rn={},on={},sn="*/".concat("*");try{Ut=i.href}catch(an){Ut=o.createElement("a"),Ut.href="",Ut=Ut.href}Xt=tn.exec(Ut.toLowerCase())||[];function un(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(w)||[];if(x.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function ln(e,t,n,r){var i={},o=e===on;function s(a){var u;return i[a]=!0,x.each(e[a]||[],function(e,a){var l=a(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):undefined:(t.dataTypes.unshift(l),s(l),!1)}),u}return s(t.dataTypes[0])||!i["*"]&&s("*")}function cn(e,t){var n,r,i=x.ajaxSettings.flatOptions||{};for(n in t)t[n]!==undefined&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&x.extend(!0,e,r),e}x.fn.load=function(e,t,n){if("string"!=typeof e&&nn)return nn.apply(this,arguments);var r,i,o,s=this,a=e.indexOf(" ");return a>=0&&(r=e.slice(a),e=e.slice(0,a)),x.isFunction(t)?(n=t,t=undefined):t&&"object"==typeof t&&(i="POST"),s.length>0&&x.ajax({url:e,type:i,dataType:"html",data:t}).done(function(e){o=arguments,s.html(r?x("<div>").append(x.parseHTML(e)).find(r):e)}).complete(n&&function(e,t){s.each(n,o||[e.responseText,t,e])}),this},x.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){x.fn[t]=function(e){return this.on(t,e)}}),x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ut,type:"GET",isLocal:Kt.test(Xt[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":sn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":x.parseJSON,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?cn(cn(e,x.ajaxSettings),t):cn(x.ajaxSettings,e)},ajaxPrefilter:un(rn),ajaxTransport:un(on),ajax:function(e,t){"object"==typeof e&&(t=e,e=undefined),t=t||{};var n,r,i,o,s,a,u,l,c=x.ajaxSetup({},t),p=c.context||c,f=c.context&&(p.nodeType||p.jquery)?x(p):x.event,h=x.Deferred(),d=x.Callbacks("once memory"),g=c.statusCode||{},m={},y={},v=0,b="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(2===v){if(!o){o={};while(t=Qt.exec(i))o[t[1].toLowerCase()]=t[2]}t=o[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===v?i:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return v||(e=y[n]=y[n]||e,m[e]=t),this},overrideMimeType:function(e){return v||(c.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>v)for(t in e)g[t]=[g[t],e[t]];else T.always(e[T.status]);return this},abort:function(e){var t=e||b;return n&&n.abort(t),k(0,t),this}};if(h.promise(T).complete=d.add,T.success=T.done,T.error=T.fail,c.url=((e||c.url||Ut)+"").replace(Gt,"").replace(en,Xt[1]+"//"),c.type=t.method||t.type||c.method||c.type,c.dataTypes=x.trim(c.dataType||"*").toLowerCase().match(w)||[""],null==c.crossDomain&&(a=tn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===Xt[1]&&a[2]===Xt[2]&&(a[3]||("http:"===a[1]?"80":"443"))===(Xt[3]||("http:"===Xt[1]?"80":"443")))),c.data&&c.processData&&"string"!=typeof c.data&&(c.data=x.param(c.data,c.traditional)),ln(rn,c,t,T),2===v)return T;u=c.global,u&&0===x.active++&&x.event.trigger("ajaxStart"),c.type=c.type.toUpperCase(),c.hasContent=!Zt.test(c.type),r=c.url,c.hasContent||(c.data&&(r=c.url+=(Vt.test(r)?"&":"?")+c.data,delete c.data),c.cache===!1&&(c.url=Jt.test(r)?r.replace(Jt,"$1_="+Yt++):r+(Vt.test(r)?"&":"?")+"_="+Yt++)),c.ifModified&&(x.lastModified[r]&&T.setRequestHeader("If-Modified-Since",x.lastModified[r]),x.etag[r]&&T.setRequestHeader("If-None-Match",x.etag[r])),(c.data&&c.hasContent&&c.contentType!==!1||t.contentType)&&T.setRequestHeader("Content-Type",c.contentType),T.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+("*"!==c.dataTypes[0]?", "+sn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)T.setRequestHeader(l,c.headers[l]);if(c.beforeSend&&(c.beforeSend.call(p,T,c)===!1||2===v))return T.abort();b="abort";for(l in{success:1,error:1,complete:1})T[l](c[l]);if(n=ln(on,c,t,T)){T.readyState=1,u&&f.trigger("ajaxSend",[T,c]),c.async&&c.timeout>0&&(s=setTimeout(function(){T.abort("timeout")},c.timeout));try{v=1,n.send(m,k)}catch(C){if(!(2>v))throw C;k(-1,C)}}else k(-1,"No Transport");function k(e,t,o,a){var l,m,y,b,w,C=t;2!==v&&(v=2,s&&clearTimeout(s),n=undefined,i=a||"",T.readyState=e>0?4:0,l=e>=200&&300>e||304===e,o&&(b=pn(c,T,o)),b=fn(c,b,T,l),l?(c.ifModified&&(w=T.getResponseHeader("Last-Modified"),w&&(x.lastModified[r]=w),w=T.getResponseHeader("etag"),w&&(x.etag[r]=w)),204===e||"HEAD"===c.type?C="nocontent":304===e?C="notmodified":(C=b.state,m=b.data,y=b.error,l=!y)):(y=C,(e||!C)&&(C="error",0>e&&(e=0))),T.status=e,T.statusText=(t||C)+"",l?h.resolveWith(p,[m,C,T]):h.rejectWith(p,[T,C,y]),T.statusCode(g),g=undefined,u&&f.trigger(l?"ajaxSuccess":"ajaxError",[T,c,l?m:y]),d.fireWith(p,[T,C]),u&&(f.trigger("ajaxComplete",[T,c]),--x.active||x.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,t){return x.get(e,undefined,t,"script")}}),x.each(["get","post"],function(e,t){x[t]=function(e,n,r,i){return x.isFunction(n)&&(i=i||r,r=n,n=undefined),x.ajax({url:e,type:t,dataType:i,data:n,success:r})}});function pn(e,t,n){var r,i,o,s,a=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),r===undefined&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in a)if(a[i]&&a[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}s||(s=i)}o=o||s}return o?(o!==u[0]&&u.unshift(o),n[o]):undefined}function fn(e,t,n,r){var i,o,s,a,u,l={},c=e.dataTypes.slice();if(c[1])for(s in e.converters)l[s.toLowerCase()]=e.converters[s];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(s=l[u+" "+o]||l["* "+o],!s)for(i in l)if(a=i.split(" "),a[1]===o&&(s=l[u+" "+a[0]]||l["* "+a[0]])){s===!0?s=l[i]:l[i]!==!0&&(o=a[0],c.unshift(a[1]));break}if(s!==!0)if(s&&e["throws"])t=s(t);else try{t=s(t)}catch(p){return{state:"parsererror",error:s?p:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",function(e){e.cache===undefined&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),x.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(r,i){t=x("<script>").prop({async:!0,charset:e.scriptCharset,src:e.url}).on("load error",n=function(e){t.remove(),n=null,e&&i("error"===e.type?404:200,e.type)}),o.head.appendChild(t[0])},abort:function(){n&&n()}}}});var hn=[],dn=/(=)\?(?=&|$)|\?\?/;x.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=hn.pop()||x.expando+"_"+Yt++;return this[e]=!0,e}}),x.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,s,a=t.jsonp!==!1&&(dn.test(t.url)?"url":"string"==typeof t.data&&!(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&dn.test(t.data)&&"data");return a||"jsonp"===t.dataTypes[0]?(i=t.jsonpCallback=x.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,a?t[a]=t[a].replace(dn,"$1"+i):t.jsonp!==!1&&(t.url+=(Vt.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return s||x.error(i+" was not called"),s[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){s=arguments},r.always(function(){e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,hn.push(i)),s&&x.isFunction(o)&&o(s[0]),s=o=undefined}),"script"):undefined}),x.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(e){}};var gn=x.ajaxSettings.xhr(),mn={0:200,1223:204},yn=0,vn={};e.ActiveXObject&&x(e).on("unload",function(){for(var e in vn)vn[e]();vn=undefined}),x.support.cors=!!gn&&"withCredentials"in gn,x.support.ajax=gn=!!gn,x.ajaxTransport(function(e){var t;return x.support.cors||gn&&!e.crossDomain?{send:function(n,r){var i,o,s=e.xhr();if(s.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(i in e.xhrFields)s[i]=e.xhrFields[i];e.mimeType&&s.overrideMimeType&&s.overrideMimeType(e.mimeType),e.crossDomain||n["X-Requested-With"]||(n["X-Requested-With"]="XMLHttpRequest");for(i in n)s.setRequestHeader(i,n[i]);t=function(e){return function(){t&&(delete vn[o],t=s.onload=s.onerror=null,"abort"===e?s.abort():"error"===e?r(s.status||404,s.statusText):r(mn[s.status]||s.status,s.statusText,"string"==typeof s.responseText?{text:s.responseText}:undefined,s.getAllResponseHeaders()))}},s.onload=t(),s.onerror=t("error"),t=vn[o=yn++]=t("abort"),s.send(e.hasContent&&e.data||null)},abort:function(){t&&t()}}:undefined});var xn,bn,wn=/^(?:toggle|show|hide)$/,Tn=RegExp("^(?:([+-])=|)("+b+")([a-z%]*)$","i"),Cn=/queueHooks$/,kn=[An],Nn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=Tn.exec(t),o=i&&i[3]||(x.cssNumber[e]?"":"px"),s=(x.cssNumber[e]||"px"!==o&&+r)&&Tn.exec(x.css(n.elem,e)),a=1,u=20;if(s&&s[3]!==o){o=o||s[3],i=i||[],s=+r||1;do a=a||".5",s/=a,x.style(n.elem,e,s+o);while(a!==(a=n.cur()/r)&&1!==a&&--u)}return i&&(s=n.start=+s||+r||0,n.unit=o,n.end=i[1]?s+(i[1]+1)*i[2]:+i[2]),n}]};function En(){return setTimeout(function(){xn=undefined}),xn=x.now()}function Sn(e,t,n){var r,i=(Nn[t]||[]).concat(Nn["*"]),o=0,s=i.length;for(;s>o;o++)if(r=i[o].call(n,t,e))return r}function jn(e,t,n){var r,i,o=0,s=kn.length,a=x.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=xn||En(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,s=0,u=l.tweens.length;for(;u>s;s++)l.tweens[s].run(o);return a.notifyWith(e,[l,o,n]),1>o&&u?n:(a.resolveWith(e,[l]),!1)},l=a.promise({elem:e,props:x.extend({},t),opts:x.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:xn||En(),duration:n.duration,tweens:[],createTween:function(t,n){var r=x.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?a.resolveWith(e,[l,t]):a.rejectWith(e,[l,t]),this}}),c=l.props;for(Dn(c,l.opts.specialEasing);s>o;o++)if(r=kn[o].call(l,e,c,l.opts))return r;return x.map(c,Sn,l),x.isFunction(l.opts.start)&&l.opts.start.call(e,l),x.fx.timer(x.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function Dn(e,t){var n,r,i,o,s;for(n in e)if(r=x.camelCase(n),i=t[r],o=e[n],x.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),s=x.cssHooks[r],s&&"expand"in s){o=s.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}x.Animation=x.extend(jn,{tweener:function(e,t){x.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Nn[n]=Nn[n]||[],Nn[n].unshift(t)},prefilter:function(e,t){t?kn.unshift(e):kn.push(e)}});function An(e,t,n){var r,i,o,s,a,u,l=this,c={},p=e.style,f=e.nodeType&&Lt(e),h=q.get(e,"fxshow");n.queue||(a=x._queueHooks(e,"fx"),null==a.unqueued&&(a.unqueued=0,u=a.empty.fire,a.empty.fire=function(){a.unqueued||u()}),a.unqueued++,l.always(function(){l.always(function(){a.unqueued--,x.queue(e,"fx").length||a.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],"inline"===x.css(e,"display")&&"none"===x.css(e,"float")&&(p.display="inline-block")),n.overflow&&(p.overflow="hidden",l.always(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],wn.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(f?"hide":"show")){if("show"!==i||!h||h[r]===undefined)continue;f=!0}c[r]=h&&h[r]||x.style(e,r)}if(!x.isEmptyObject(c)){h?"hidden"in h&&(f=h.hidden):h=q.access(e,"fxshow",{}),o&&(h.hidden=!f),f?x(e).show():l.done(function(){x(e).hide()}),l.done(function(){var t;q.remove(e,"fxshow");for(t in c)x.style(e,t,c[t])});for(r in c)s=Sn(f?h[r]:0,r,l),r in h||(h[r]=s.start,f&&(s.end=s.start,s.start="width"===r||"height"===r?1:0))}}function Ln(e,t,n,r,i){return new Ln.prototype.init(e,t,n,r,i)}x.Tween=Ln,Ln.prototype={constructor:Ln,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(x.cssNumber[n]?"":"px")},cur:function(){var e=Ln.propHooks[this.prop];return e&&e.get?e.get(this):Ln.propHooks._default.get(this)},run:function(e){var t,n=Ln.propHooks[this.prop];return this.pos=t=this.options.duration?x.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):Ln.propHooks._default.set(this),this}},Ln.prototype.init.prototype=Ln.prototype,Ln.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=x.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){x.fx.step[e.prop]?x.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[x.cssProps[e.prop]]||x.cssHooks[e.prop])?x.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},Ln.propHooks.scrollTop=Ln.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},x.each(["toggle","show","hide"],function(e,t){var n=x.fn[t];x.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(qn(t,!0),e,r,i)}}),x.fn.extend({fadeTo:function(e,t,n,r){return this.filter(Lt).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=x.isEmptyObject(e),o=x.speed(t,n,r),s=function(){var t=jn(this,x.extend({},e),o);(i||q.get(this,"finish"))&&t.stop(!0)};return s.finish=s,i||o.queue===!1?this.each(s):this.queue(o.queue,s)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=undefined),t&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=x.timers,s=q.get(this);if(i)s[i]&&s[i].stop&&r(s[i]);else for(i in s)s[i]&&s[i].stop&&Cn.test(i)&&r(s[i]);for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));(t||!n)&&x.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=q.get(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=x.timers,s=r?r.length:0;for(n.finish=!0,x.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;s>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function qn(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=jt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}x.each({slideDown:qn("show"),slideUp:qn("hide"),slideToggle:qn("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){x.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),x.speed=function(e,t,n){var r=e&&"object"==typeof e?x.extend({},e):{complete:n||!n&&t||x.isFunction(e)&&e,duration:e,easing:n&&t||t&&!x.isFunction(t)&&t};return r.duration=x.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in x.fx.speeds?x.fx.speeds[r.duration]:x.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){x.isFunction(r.old)&&r.old.call(this),r.queue&&x.dequeue(this,r.queue)},r},x.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},x.timers=[],x.fx=Ln.prototype.init,x.fx.tick=function(){var e,t=x.timers,n=0;for(xn=x.now();t.length>n;n++)e=t[n],e()||t[n]!==e||t.splice(n--,1);t.length||x.fx.stop(),xn=undefined},x.fx.timer=function(e){e()&&x.timers.push(e)&&x.fx.start()},x.fx.interval=13,x.fx.start=function(){bn||(bn=setInterval(x.fx.tick,x.fx.interval))},x.fx.stop=function(){clearInterval(bn),bn=null},x.fx.speeds={slow:600,fast:200,_default:400},x.fx.step={},x.expr&&x.expr.filters&&(x.expr.filters.animated=function(e){return x.grep(x.timers,function(t){return e===t.elem}).length}),x.fn.offset=function(e){if(arguments.length)return e===undefined?this:this.each(function(t){x.offset.setOffset(this,e,t)});var t,n,i=this[0],o={top:0,left:0},s=i&&i.ownerDocument;if(s)return t=s.documentElement,x.contains(t,i)?(typeof i.getBoundingClientRect!==r&&(o=i.getBoundingClientRect()),n=Hn(s),{top:o.top+n.pageYOffset-t.clientTop,left:o.left+n.pageXOffset-t.clientLeft}):o},x.offset={setOffset:function(e,t,n){var r,i,o,s,a,u,l,c=x.css(e,"position"),p=x(e),f={};"static"===c&&(e.style.position="relative"),a=p.offset(),o=x.css(e,"top"),u=x.css(e,"left"),l=("absolute"===c||"fixed"===c)&&(o+u).indexOf("auto")>-1,l?(r=p.position(),s=r.top,i=r.left):(s=parseFloat(o)||0,i=parseFloat(u)||0),x.isFunction(t)&&(t=t.call(e,n,a)),null!=t.top&&(f.top=t.top-a.top+s),null!=t.left&&(f.left=t.left-a.left+i),"using"in t?t.using.call(e,f):p.css(f)}},x.fn.extend({position:function(){if(this[0]){var e,t,n=this[0],r={top:0,left:0};return"fixed"===x.css(n,"position")?t=n.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),x.nodeName(e[0],"html")||(r=e.offset()),r.top+=x.css(e[0],"borderTopWidth",!0),r.left+=x.css(e[0],"borderLeftWidth",!0)),{top:t.top-r.top-x.css(n,"marginTop",!0),left:t.left-r.left-x.css(n,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||s;while(e&&!x.nodeName(e,"html")&&"static"===x.css(e,"position"))e=e.offsetParent;return e||s})}}),x.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,n){var r="pageYOffset"===n;x.fn[t]=function(i){return x.access(this,function(t,i,o){var s=Hn(t);return o===undefined?s?s[n]:t[i]:(s?s.scrollTo(r?e.pageXOffset:o,r?o:e.pageYOffset):t[i]=o,undefined)},t,i,arguments.length,null)}});function Hn(e){return x.isWindow(e)?e:9===e.nodeType&&e.defaultView}x.each({Height:"height",Width:"width"},function(e,t){x.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){x.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),s=n||(r===!0||i===!0?"margin":"border");return x.access(this,function(t,n,r){var i;return x.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):r===undefined?x.css(t,n,s):x.style(t,n,r,s)},t,o?r:undefined,o,null)}})}),x.fn.size=function(){return this.length},x.fn.andSelf=x.fn.addBack,"object"==typeof module&&module&&"object"==typeof module.exports?module.exports=x:"function"==typeof define&&define.amd&&define("jquery",[],function(){return x}),"object"==typeof e&&"object"==typeof e.document&&(e.jQuery=e.$=x)})(window); |
... | ... |
@@ -0,0 +1 @@ |
1 |
+.str,.atv{color:#9daa7e}.kwd,.tag{color:#d5b57c}.com{color:#726d73}.typ,.atn,.dec{color:#dd7e5e}.lit{color:#fcf0a4}.pun,.opn,.clo{color:#a78353}.pln{color:#889dbc}pre.prettyprint{border:0;padding-bottom:0.75em;padding-top:0.75em} |
... | ... |
@@ -0,0 +1 @@ |
1 |
+.str{color:#718c00}.kwd{color:#8959a8}.com{color:#8e908c}.typ,.dec{color:#f5871f}.lit,.atn{color:#eab700}.pun,.opn,.clo,.pln{color:#4d4d4c}.tag,.var,.fun{color:#c82829}.atv{color:#3e999f}pre.prettyprint{border:1px solid #d1d1d1;padding:0;padding-bottom:1.5em;padding-top:1.5em} |
... | ... |
@@ -6,11 +6,11 @@ |
6 | 6 |
<meta http-equiv="Expires" content="-1"> |
7 | 7 |
%= javascript '/mojo/jquery/jquery.js' |
8 | 8 |
%= javascript '/mojo/prettify/run_prettify.js' |
9 |
- %= stylesheet '/mojo/prettify/prettify-mojo.css' |
|
9 |
+ %= stylesheet '/mojo/prettify/prettify-mojo-dark.css' |
|
10 | 10 |
%= stylesheet begin |
11 | 11 |
a img { border: 0 } |
12 | 12 |
body { |
13 |
- background-color: #f5f6f8; |
|
13 |
+ background: url(<%= url_for '/mojo/pinstripe-light.png' %>); |
|
14 | 14 |
color: #445555; |
15 | 15 |
font: 0.9em 'Helvetica Neue', Helvetica, sans-serif; |
16 | 16 |
font-weight: normal; |
... | ... |
@@ -18,7 +18,7 @@ |
18 | 18 |
margin: 0; |
19 | 19 |
} |
20 | 20 |
pre { |
21 |
- font: 0.8em Consolas, Menlo, Monaco, Courier, monospace; |
|
21 |
+ font: 0.9em Consolas, Menlo, Monaco, Courier, monospace; |
|
22 | 22 |
margin: 0; |
23 | 23 |
white-space: pre-wrap; |
24 | 24 |
} |
... | ... |
@@ -31,15 +31,13 @@ |
31 | 31 |
td { padding: 0.5em } |
32 | 32 |
.box { |
33 | 33 |
background-color: #fff; |
34 |
- -moz-box-shadow: 0px 0px 2px #ccc; |
|
35 |
- -webkit-box-shadow: 0px 0px 2px #ccc; |
|
36 |
- box-shadow: 0px 0px 2px #ccc; |
|
34 |
+ box-shadow: 0px 0px 2px #999; |
|
37 | 35 |
overflow: hidden; |
38 | 36 |
padding: 1em; |
39 | 37 |
} |
40 | 38 |
.code { |
41 | 39 |
background-color: #1a1a1a; |
42 |
- background: url(<%= url_for '/mojo/pinstripe.gif' %>); |
|
40 |
+ background: url(<%= url_for '/mojo/pinstripe-dark.png' %>); |
|
43 | 41 |
color: #eee; |
44 | 42 |
text-shadow: #333 0 1px 0; |
45 | 43 |
} |
... | ... |
@@ -78,16 +76,12 @@ |
78 | 76 |
} |
79 | 77 |
#showcase .key { padding-right: 0 } |
80 | 78 |
#more, #trace { |
81 |
- -moz-border-radius-bottomleft: 5px; |
|
82 | 79 |
border-bottom-left-radius: 5px; |
83 |
- -moz-border-radius-bottomright: 5px; |
|
84 | 80 |
border-bottom-right-radius: 5px; |
85 | 81 |
} |
86 | 82 |
#more .tap, #trace .tap { text-shadow: #ddd 0 1px 0 } |
87 | 83 |
#request { |
88 |
- -moz-border-radius-topleft: 5px; |
|
89 | 84 |
border-top-left-radius: 5px; |
90 |
- -moz-border-radius-topright: 5px; |
|
91 | 85 |
border-top-right-radius: 5px; |
92 | 86 |
margin-top: 1em; |
93 | 87 |
} |
... | ... |
@@ -193,7 +187,7 @@ |
193 | 187 |
%= $kv->(Stash => dumper $snapshot) |
194 | 188 |
%= $kv->(Session => dumper session) |
195 | 189 |
%= $kv->(Version => $req->version) |
196 |
- % for my $name (@{$self->req->headers->names}) { |
|
190 |
+ % for my $name (sort @{$self->req->headers->names}) { |
|
197 | 191 |
% my $value = $self->req->headers->header($name); |
198 | 192 |
%= $kv->($name, $value) |
199 | 193 |
% } |
... | ... |
@@ -3,21 +3,9 @@ |
3 | 3 |
%= stylesheet scoped => 'scoped', begin |
4 | 4 |
#mojobar { |
5 | 5 |
background-color: #1a1a1a; |
6 |
- background: -webkit-gradient( |
|
7 |
- linear, |
|
8 |
- 0% 0%, |
|
9 |
- 0% 100%, |
|
10 |
- color-stop(0%, #2a2a2a), |
|
11 |
- color-stop(100%, #000) |
|
12 |
- ); |
|
13 |
- background: -moz-linear-gradient( |
|
14 |
- top, |
|
15 |
- #2a2a2a 0%, |
|
16 |
- #000 100% |
|
17 |
- ); |
|
6 |
+ background: -webkit-linear-gradient(top, #2a2a2a 0%, #000 100%); |
|
7 |
+ background: -moz-linear-gradient(top, #2a2a2a 0%, #000 100%); |
|
18 | 8 |
background: linear-gradient(top, #2a2a2a 0%, #000 100%); |
19 |
- -moz-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.6); |
|
20 |
- -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.6); |
|
21 | 9 |
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.6); |
22 | 10 |
height: 46px; |
23 | 11 |
overflow: hidden; |
... | ... |
@@ -37,22 +25,26 @@ |
37 | 25 |
float: right; |
38 | 26 |
height: 60px; |
39 | 27 |
margin-right: 5em; |
40 |
- margin-top: 1em; |
|
28 |
+ margin-top: 0.8em; |
|
41 | 29 |
} |
42 | 30 |
#mojobar-links a { |
43 | 31 |
color: #ccc; |
44 | 32 |
font: 1em 'Helvetica Neue', Helvetica, sans-serif; |
45 | 33 |
font-weight: 300; |
46 | 34 |
margin-left: 0.5em; |
47 |
- padding-bottom: 1em; |
|
48 |
- padding-top: 1em; |
|
49 | 35 |
text-decoration: none; |
50 |
- -webkit-transition: all 200ms ease-in-out; |
|
51 |
- -moz-transition: all 200ms ease-in-out; |
|
52 |
- -o-transition: all 200ms ease-in-out; |
|
53 | 36 |
transition: all 200ms ease-in-out; |
54 | 37 |
} |
55 | 38 |
#mojobar-links a:hover { color: #fff } |
39 |
+ #mojobar-links input { |
|
40 |
+ border: 0; |
|
41 |
+ border-radius: 10px; |
|
42 |
+ display: inline; |
|
43 |
+ margin-left: 1em; |
|
44 |
+ padding: 3px; |
|
45 |
+ } |
|
46 |
+ #mojobar-links input:focus { outline: 0 } |
|
47 |
+ #mojobar-links form { display: inline } |
|
56 | 48 |
% end |
57 | 49 |
<div id="mojobar-logo"> |
58 | 50 |
%= link_to 'http://mojolicio.us' => begin |
... | ... |
@@ -61,13 +53,17 @@ |
61 | 53 |
</div> |
62 | 54 |
<div id="mojobar-links"> |
63 | 55 |
%= link_to Documentation => 'http://mojolicio.us/perldoc' |
64 |
- %= link_to Screencasts => 'http://mojocasts.com' |
|
65 | 56 |
%= link_to Wiki => 'https://github.com/kraih/mojo/wiki' |
66 | 57 |
%= link_to GitHub => 'https://github.com/kraih/mojo' |
67 | 58 |
%= link_to CPAN => 'http://metacpan.org/release/Mojolicious/' |
68 | 59 |
%= link_to MailingList => 'http://groups.google.com/group/mojolicious' |
69 | 60 |
%= link_to Blog => 'http://blog.kraih.com' |
70 | 61 |
%= link_to Twitter => 'http://twitter.com/kraih' |
62 |
+ %= form_for 'http://google.com/cse' => (target => '_blank') => begin |
|
63 |
+ %= hidden_field cx => '014527573091551588235:pwfplkjpgbi' |
|
64 |
+ %= hidden_field ie => 'UTF-8' |
|
65 |
+ %= search_field 'q', placeholder => 'Search' |
|
66 |
+ %= end |
|
71 | 67 |
</div> |
72 | 68 |
</div> |
73 | 69 |
%= javascript begin |
... | ... |
@@ -92,6 +88,7 @@ |
92 | 88 |
}); |
93 | 89 |
}); |
94 | 90 |
$(document).ready(function() { |
91 |
+ $('a[href^="#"]').addClass('mojoscroll'); |
|
95 | 92 |
$(".mojoscroll").click(function(e) { |
96 | 93 |
e.preventDefault(); |
97 | 94 |
e.stopPropagation(); |
... | ... |
@@ -2,11 +2,9 @@ |
2 | 2 |
<html> |
3 | 3 |
<head> |
4 | 4 |
<title>Page not found</title> |
5 |
- %= javascript '/mojo/prettify/run_prettify.js' |
|
6 |
- %= stylesheet '/mojo/prettify/prettify-mojo.css' |
|
7 | 5 |
%= stylesheet begin |
8 | 6 |
body { |
9 |
- background-color: #f5f6f8; |
|
7 |
+ background: url(<%= url_for '/mojo/pinstripe-light.png' %>); |
|
10 | 8 |
color: #445555; |
11 | 9 |
font: 0.9em 'Helvetica Neue', Helvetica, sans-serif; |
12 | 10 |
font-weight: normal; |
... | ... |
@@ -16,10 +14,9 @@ |
16 | 14 |
code { |
17 | 15 |
background-color: #eef9ff; |
18 | 16 |
border: solid #cce4ff 1px; |
19 |
- -moz-border-radius: 5px; |
|
20 | 17 |
border-radius: 5px; |
21 | 18 |
color: #333; |
22 |
- font: 0.8em Consolas, Menlo, Monaco, Courier, monospace; |
|
19 |
+ font: 0.9em Consolas, Menlo, Monaco, Courier, monospace; |
|
23 | 20 |
padding: 0.4em; |
24 | 21 |
} |
25 | 22 |
h1 { |
... | ... |
@@ -28,7 +25,7 @@ |
28 | 25 |
margin: 0; |
29 | 26 |
} |
30 | 27 |
pre { |
31 |
- font: 0.8em Consolas, Menlo, Monaco, Courier, monospace; |
|
28 |
+ font: 0.9em Consolas, Menlo, Monaco, Courier, monospace; |
|
32 | 29 |
margin: 0; |
33 | 30 |
white-space: pre-wrap; |
34 | 31 |
} |
... | ... |
@@ -55,13 +52,9 @@ |
55 | 52 |
} |
56 | 53 |
#routes { |
57 | 54 |
background-color: #fff; |
58 |
- -moz-border-radius-bottomleft: 5px; |
|
59 | 55 |
border-bottom-left-radius: 5px; |
60 |
- -moz-border-radius-bottomright: 5px; |
|
61 | 56 |
border-bottom-right-radius: 5px; |
62 |
- -moz-box-shadow: 0px 0px 2px #ccc; |
|
63 |
- -webkit-box-shadow: 0px 0px 2px #ccc; |
|
64 |
- box-shadow: 0px 0px 2px #ccc; |
|
57 |
+ box-shadow: 0px 0px 2px #999; |
|
65 | 58 |
margin-left: 5em; |
66 | 59 |
margin-right: 5em; |
67 | 60 |
padding: 1em; |
... | ... |
@@ -3,13 +3,13 @@ |
3 | 3 |
<head> |
4 | 4 |
<title><%= $title %></title> |
5 | 5 |
%= javascript '/mojo/prettify/run_prettify.js' |
6 |
- %= stylesheet '/mojo/prettify/prettify-mojo.css' |
|
6 |
+ %= stylesheet '/mojo/prettify/prettify-mojo-light.css' |
|
7 | 7 |
%= stylesheet begin |
8 | 8 |
a { color: inherit } |
9 | 9 |
a:hover { color: #2a2a2a } |
10 | 10 |
a img { border: 0 } |
11 | 11 |
body { |
12 |
- background-color: #f5f6f8; |
|
12 |
+ background: url(<%= url_for '/mojo/pinstripe-light.png' %>); |
|
13 | 13 |
color: #445555; |
14 | 14 |
font: 0.9em 'Helvetica Neue', Helvetica, sans-serif; |
15 | 15 |
font-weight: normal; |
... | ... |
@@ -23,17 +23,16 @@ |
23 | 23 |
} |
24 | 24 |
h1 a, h2 a, h3 a { text-decoration: none } |
25 | 25 |
pre { |
26 |
- background-color: #eee; |
|
27 |
- background: url(<%= url_for '/mojo/pinstripe.gif' %>); |
|
28 |
- -moz-border-radius: 5px; |
|
29 |
- border-radius: 5px; |
|
30 |
- color: #eee; |
|
31 |
- font: 0.8em Consolas, Menlo, Monaco, Courier, monospace; |
|
32 |
- line-height: 1.7em; |
|
33 |
- text-align: left; |
|
34 |
- text-shadow: #333 0 1px 0; |
|
26 |
+ background: url(<%= url_for '/mojo/stripes.png' %>); |
|
27 |
+ border: 1px solid #d1d1d1; |
|
28 |
+ box-shadow: 0 1px #fff, inset -1px 1px 4px rgba(0, 0, 0, 0.1); |
|
29 |
+ color: #4d4d4c; |
|
30 |
+ font: 0.9em Consolas, Menlo, Monaco, Courier, monospace; |
|
31 |
+ line-height: 1.5em; |
|
35 | 32 |
padding-bottom: 1.5em; |
36 | 33 |
padding-top: 1.5em; |
34 |
+ text-align: left; |
|
35 |
+ text-shadow: #eee 0 1px 0; |
|
37 | 36 |
white-space: pre-wrap; |
38 | 37 |
} |
39 | 38 |
#footer { |
... | ... |
@@ -42,19 +41,16 @@ |
42 | 41 |
} |
43 | 42 |
#perldoc { |
44 | 43 |
background-color: #fff; |
45 |
- -moz-border-radius-bottomleft: 5px; |
|
46 | 44 |
border-bottom-left-radius: 5px; |
47 |
- -moz-border-radius-bottomright: 5px; |
|
48 | 45 |
border-bottom-right-radius: 5px; |
49 |
- -moz-box-shadow: 0px 0px 2px #ccc; |
|
50 |
- -webkit-box-shadow: 0px 0px 2px #ccc; |
|
51 |
- box-shadow: 0px 0px 2px #ccc; |
|
46 |
+ box-shadow: 0px 0px 2px #999; |
|
52 | 47 |
margin-left: 5em; |
53 | 48 |
margin-right: 5em; |
54 | 49 |
padding: 3em; |
55 | 50 |
padding-top: 70px; |
56 | 51 |
} |
57 | 52 |
#perldoc > ul:first-of-type a { text-decoration: none } |
53 |
+ #source { padding-bottom: 1em } |
|
58 | 54 |
#wrapperlicious { |
59 | 55 |
max-width: 1000px; |
60 | 56 |
margin: 0 auto; |
... | ... |
@@ -63,20 +59,26 @@ |
63 | 59 |
</head> |
64 | 60 |
<body> |
65 | 61 |
%= include inline => app->renderer->_bundled('mojobar') |
66 |
- % my $link = begin |
|
67 |
- %= link_to shift, shift, class => "mojoscroll" |
|
68 |
- % end |
|
69 | 62 |
<div id="wrapperlicious"> |
70 | 63 |
<div id="perldoc"> |
64 |
+ <div id="source"> |
|
65 |
+ % my $path; |
|
66 |
+ % for my $part (split '/', $module) { |
|
67 |
+ %= '::' if $path |
|
68 |
+ % $path .= "/$part"; |
|
69 |
+ %= link_to $part => url_for("/perldoc$path") |
|
70 |
+ % } |
|
71 |
+ (<%= link_to 'source' => url_for("/perldoc$path.txt") %>) |
|
72 |
+ </div> |
|
71 | 73 |
<h1><a id="toc">TABLE OF CONTENTS</a></h1> |
72 | 74 |
<ul> |
73 | 75 |
% for my $part (@$parts) { |
74 | 76 |
<li> |
75 |
- %= $link->(splice @$part, 0, 2) |
|
77 |
+ %= link_to splice(@$part, 0, 2) |
|
76 | 78 |
% if (@$part) { |
77 | 79 |
<ul> |
78 | 80 |
% while (@$part) { |
79 |
- <li><%= $link->(splice @$part, 0, 2) %></li> |
|
81 |
+ <li><%= link_to splice(@$part, 0, 2) %></li> |
|
80 | 82 |
% } |
81 | 83 |
</ul> |
82 | 84 |
% } |
... | ... |
@@ -30,33 +30,33 @@ sub new { |
30 | 30 |
|
31 | 31 |
sub app { |
32 | 32 |
my ($self, $app) = @_; |
33 |
- return $self->ua->app unless $app; |
|
34 |
- $self->ua->app($app); |
|
33 |
+ return $self->ua->server->app unless $app; |
|
34 |
+ $self->ua->server->app($app); |
|
35 | 35 |
return $self; |
36 | 36 |
} |
37 | 37 |
|
38 | 38 |
sub content_is { |
39 | 39 |
my ($self, $value, $desc) = @_; |
40 | 40 |
$desc ||= 'exact match for content'; |
41 |
- return $self->_test('is', $self->_get_content($self->tx), $value, $desc); |
|
41 |
+ return $self->_test('is', $self->tx->res->text, $value, $desc); |
|
42 | 42 |
} |
43 | 43 |
|
44 | 44 |
sub content_isnt { |
45 | 45 |
my ($self, $value, $desc) = @_; |
46 | 46 |
$desc ||= 'no match for content'; |
47 |
- return $self->_test('isnt', $self->_get_content($self->tx), $value, $desc); |
|
47 |
+ return $self->_test('isnt', $self->tx->res->text, $value, $desc); |
|
48 | 48 |
} |
49 | 49 |
|
50 | 50 |
sub content_like { |
51 | 51 |
my ($self, $regex, $desc) = @_; |
52 | 52 |
$desc ||= 'content is similar'; |
53 |
- return $self->_test('like', $self->_get_content($self->tx), $regex, $desc); |
|
53 |
+ return $self->_test('like', $self->tx->res->text, $regex, $desc); |
|
54 | 54 |
} |
55 | 55 |
|
56 | 56 |
sub content_unlike { |
57 | 57 |
my ($self, $regex, $desc) = @_; |
58 | 58 |
$desc ||= 'content is not similar'; |
59 |
- return $self->_test('unlike', $self->_get_content($self->tx), $regex, $desc); |
|
59 |
+ return $self->_test('unlike', $self->tx->res->text, $regex, $desc); |
|
60 | 60 |
} |
61 | 61 |
|
62 | 62 |
sub content_type_is { |
... | ... |
@@ -301,13 +301,6 @@ sub websocket_ok { |
301 | 301 |
return $self->_test('ok', $self->tx->res->code eq 101, $desc); |
302 | 302 |
} |
303 | 303 |
|
304 |
-sub _get_content { |
|
305 |
- my ($self, $tx) = @_; |
|
306 |
- my $content = $tx->res->body; |
|
307 |
- my $charset = $tx->res->content->charset; |
|
308 |
- return $charset ? decode($charset, $content) : $content; |
|
309 |
-} |
|
310 |
- |
|
311 | 304 |
sub _json { |
312 | 305 |
my ($self, $method, $p) = @_; |
313 | 306 |
return Mojo::JSON::Pointer->new->$method( |
... | ... |
@@ -442,7 +435,7 @@ User agent used for testing, defaults to a L<Mojo::UserAgent> object. |
442 | 435 |
$t->ua->max_redirects(10); |
443 | 436 |
|
444 | 437 |
# Use absolute URL for request with Basic authentication |
445 |
- my $url = $t->ua->app_url->userinfo('sri:secr3t')->path('/secrets.json'); |
|
438 |
+ my $url = $t->ua->server->url->userinfo('sri:secr3t')->path('/secrets.json'); |
|
446 | 439 |
$t->post_ok($url => json => {limit => 10}) |
447 | 440 |
->status_is(200) |
448 | 441 |
->json_is('/1/content', 'Mojo rocks!'); |
... | ... |
@@ -482,9 +475,9 @@ Access application with L<Mojo::UserAgent/"app">. |
482 | 475 |
|
483 | 476 |
# Change application behavior |
484 | 477 |
$t->app->hook(before_dispatch => sub { |
485 |
- my $self = shift; |
|
486 |
- $self->render(text => 'This request did not reach the router.') |
|
487 |
- if $self->req->url->path->contains('/user'); |
|
478 |
+ my $c = shift; |
|
479 |
+ $c->render(text => 'This request did not reach the router.') |
|
480 |
+ if $c->req->url->path->contains('/user'); |
|
488 | 481 |
}); |
489 | 482 |
|
490 | 483 |
# Extract additional information |
... | ... |
@@ -496,28 +489,30 @@ Access application with L<Mojo::UserAgent/"app">. |
496 | 489 |
$t = $t->content_is('working!'); |
497 | 490 |
$t = $t->content_is('working!', 'right content'); |
498 | 491 |
|
499 |
-Check response content for exact match. |
|
492 |
+Check response content for exact match after retrieving it from |
|
493 |
+L<Mojo::Message/"text">. |
|
500 | 494 |
|
501 | 495 |
=head2 content_isnt |
502 | 496 |
|
503 | 497 |
$t = $t->content_isnt('working!'); |
504 | 498 |
$t = $t->content_isnt('working!', 'different content'); |
505 | 499 |
|
506 |
-Opposite of C<content_is>. |
|
500 |
+Opposite of L</"content_is">. |
|
507 | 501 |
|
508 | 502 |
=head2 content_like |
509 | 503 |
|
510 | 504 |
$t = $t->content_like(qr/working!/); |
511 | 505 |
$t = $t->content_like(qr/working!/, 'right content'); |
512 | 506 |
|
513 |
-Check response content for similar match. |
|
507 |
+Check response content for similar match after retrieving it from |
|
508 |
+L<Mojo::Message/"text">. |
|
514 | 509 |
|
515 | 510 |
=head2 content_unlike |
516 | 511 |
|
517 | 512 |
$t = $t->content_unlike(qr/working!/); |
518 | 513 |
$t = $t->content_unlike(qr/working!/, 'different content'); |
519 | 514 |
|
520 |
-Opposite of C<content_like>. |
|
515 |
+Opposite of L</"content_like">. |
|
521 | 516 |
|
522 | 517 |
=head2 content_type_is |
523 | 518 |
|
... | ... |
@@ -531,7 +526,7 @@ Check response C<Content-Type> header for exact match. |
531 | 526 |
$t = $t->content_type_isnt('text/html'); |
532 | 527 |
$t = $t->content_type_isnt('text/html', 'different content type'); |
533 | 528 |
|
534 |
-Opposite of C<content_type_is>. |
|
529 |
+Opposite of L</"content_type_is">. |
|
535 | 530 |
|
536 | 531 |
=head2 content_type_like |
537 | 532 |
|
... | ... |
@@ -545,7 +540,7 @@ Check response C<Content-Type> header for similar match. |
545 | 540 |
$t = $t->content_type_unlike(qr/text/); |
546 | 541 |
$t = $t->content_type_unlike(qr/text/, 'different content type'); |
547 | 542 |
|
548 |
-Opposite of C<content_type_like>. |
|
543 |
+Opposite of L</"content_type_like">. |
|
549 | 544 |
|
550 | 545 |
=head2 delete_ok |
551 | 546 |
|
... | ... |
@@ -554,7 +549,7 @@ Opposite of C<content_type_like>. |
554 | 549 |
$t = $t->delete_ok('/foo' => {DNT => 1} => form => {a => 'b'}); |
555 | 550 |
$t = $t->delete_ok('/foo' => {DNT => 1} => json => {a => 'b'}); |
556 | 551 |
|
557 |
-Perform a C<DELETE> request and check for transport errors, takes the same |
|
552 |
+Perform a DELETE request and check for transport errors, takes the same |
|
558 | 553 |
arguments as L<Mojo::UserAgent/"delete">, except for the callback. |
559 | 554 |
|
560 | 555 |
=head2 element_exists |
... | ... |
@@ -570,7 +565,7 @@ L<Mojo::DOM>. |
570 | 565 |
$t = $t->element_exists_not('div.foo[x=y]'); |
571 | 566 |
$t = $t->element_exists_not('html head title', 'has no title'); |
572 | 567 |
|
573 |
-Opposite of C<element_exists>. |
|
568 |
+Opposite of L</"element_exists">. |
|
574 | 569 |
|
575 | 570 |
=head2 finish_ok |
576 | 571 |
|
... | ... |
@@ -593,7 +588,7 @@ Wait for WebSocket connection to be closed gracefully and check status. |
593 | 588 |
$t = $t->get_ok('/foo' => {DNT => 1} => form => {a => 'b'}); |
594 | 589 |
$t = $t->get_ok('/foo' => {DNT => 1} => json => {a => 'b'}); |
595 | 590 |
|
596 |
-Perform a C<GET> request and check for transport errors, takes the same |
|
591 |
+Perform a GET request and check for transport errors, takes the same |
|
597 | 592 |
arguments as L<Mojo::UserAgent/"get">, except for the callback. |
598 | 593 |
|
599 | 594 |
=head2 head_ok |
... | ... |
@@ -603,7 +598,7 @@ arguments as L<Mojo::UserAgent/"get">, except for the callback. |
603 | 598 |
$t = $t->head_ok('/foo' => {DNT => 1} => form => {a => 'b'}); |
604 | 599 |
$t = $t->head_ok('/foo' => {DNT => 1} => json => {a => 'b'}); |
605 | 600 |
|
606 |
-Perform a C<HEAD> request and check for transport errors, takes the same |
|
601 |
+Perform a HEAD request and check for transport errors, takes the same |
|
607 | 602 |
arguments as L<Mojo::UserAgent/"head">, except for the callback. |
608 | 603 |
|
609 | 604 |
=head2 header_is |
... | ... |
@@ -618,7 +613,7 @@ Check response header for exact match. |
618 | 613 |
$t = $t->header_isnt(Expect => 'fun'); |
619 | 614 |
$t = $t->header_isnt(Expect => 'fun', 'different header'); |
620 | 615 |
|
621 |
-Opposite of C<header_is>. |
|
616 |
+Opposite of L</"header_is">. |
|
622 | 617 |
|
623 | 618 |
=head2 header_like |
624 | 619 |
|
... | ... |
@@ -632,7 +627,7 @@ Check response header for similar match. |
632 | 627 |
$t = $t->header_like(Expect => qr/fun/); |
633 | 628 |
$t = $t->header_like(Expect => qr/fun/, 'different header'); |
634 | 629 |
|
635 |
-Opposite of C<header_like>. |
|
630 |
+Opposite of L</"header_like">. |
|
636 | 631 |
|
637 | 632 |
=head2 json_has |
638 | 633 |
|
... | ... |
@@ -647,7 +642,7 @@ JSON Pointer with L<Mojo::JSON::Pointer>. |
647 | 642 |
$t = $t->json_hasnt('/foo'); |
648 | 643 |
$t = $t->json_hasnt('/minibar', 'no minibar'); |
649 | 644 |
|
650 |
-Opposite of C<json_has>. |
|
645 |
+Opposite of L</"json_has">. |
|
651 | 646 |
|
652 | 647 |
=head2 json_is |
653 | 648 |
|
... | ... |
@@ -672,7 +667,7 @@ the given JSON Pointer with L<Mojo::JSON::Pointer>. |
672 | 667 |
$t = $t->json_message_hasnt('/foo'); |
673 | 668 |
$t = $t->json_message_hasnt('/minibar', 'no minibar'); |
674 | 669 |
|
675 |
-Opposite of C<json_message_has>. |
|
670 |
+Opposite of L</"json_message_has">. |
|
676 | 671 |
|
677 | 672 |
=head2 json_message_is |
678 | 673 |
|
... | ... |
@@ -701,7 +696,7 @@ Check WebSocket message for exact match. |
701 | 696 |
$t = $t->message_isnt('working!'); |
702 | 697 |
$t = $t->message_isnt('working!', 'different message'); |
703 | 698 |
|
704 |
-Opposite of C<message_is>. |
|
699 |
+Opposite of L</"message_is">. |
|
705 | 700 |
|
706 | 701 |
=head2 message_like |
707 | 702 |
|
... | ... |
@@ -733,7 +728,7 @@ Wait for next WebSocket message to arrive. |
733 | 728 |
$t = $t->message_unlike(qr/working!/); |
734 | 729 |
$t = $t->message_unlike(qr/working!/, 'different message'); |
735 | 730 |
|
736 |
-Opposite of C<message_like>. |
|
731 |
+Opposite of L</"message_like">. |
|
737 | 732 |
|
738 | 733 |
=head2 options_ok |
739 | 734 |
|
... | ... |
@@ -742,7 +737,7 @@ Opposite of C<message_like>. |
742 | 737 |
$t = $t->options_ok('/foo' => {DNT => 1} => form => {a => 'b'}); |
743 | 738 |
$t = $t->options_ok('/foo' => {DNT => 1} => json => {a => 'b'}); |
744 | 739 |
|
745 |
-Perform a C<OPTIONS> request and check for transport errors, takes the same |
|
740 |
+Perform a OPTIONS request and check for transport errors, takes the same |
|
746 | 741 |
arguments as L<Mojo::UserAgent/"options">, except for the callback. |
747 | 742 |
|
748 | 743 |
=head2 or |
... | ... |
@@ -762,7 +757,7 @@ Invoke callback if previous test failed. |
762 | 757 |
$t = $t->patch_ok('/foo' => {DNT => 1} => form => {a => 'b'}); |
763 | 758 |
$t = $t->patch_ok('/foo' => {DNT => 1} => json => {a => 'b'}); |
764 | 759 |
|
765 |
-Perform a C<PATCH> request and check for transport errors, takes the same |
|
760 |
+Perform a PATCH request and check for transport errors, takes the same |
|
766 | 761 |
arguments as L<Mojo::UserAgent/"patch">, except for the callback. |
767 | 762 |
|
768 | 763 |
=head2 post_ok |
... | ... |
@@ -772,7 +767,7 @@ arguments as L<Mojo::UserAgent/"patch">, except for the callback. |
772 | 767 |
$t = $t->post_ok('/foo' => {DNT => 1} => form => {a => 'b'}); |
773 | 768 |
$t = $t->post_ok('/foo' => {DNT => 1} => json => {a => 'b'}); |
774 | 769 |
|
775 |
-Perform a C<POST> request and check for transport errors, takes the same |
|
770 |
+Perform a POST request and check for transport errors, takes the same |
|
776 | 771 |
arguments as L<Mojo::UserAgent/"post">, except for the callback. |
777 | 772 |
|
778 | 773 |
# Test file upload |
... | ... |
@@ -791,7 +786,7 @@ arguments as L<Mojo::UserAgent/"post">, except for the callback. |
791 | 786 |
$t = $t->put_ok('/foo' => {DNT => 1} => form => {a => 'b'}); |
792 | 787 |
$t = $t->put_ok('/foo' => {DNT => 1} => json => {a => 'b'}); |
793 | 788 |
|
794 |
-Perform a C<PUT> request and check for transport errors, takes the same |
|
789 |
+Perform a PUT request and check for transport errors, takes the same |
|
795 | 790 |
arguments as L<Mojo::UserAgent/"put">, except for the callback. |
796 | 791 |
|
797 | 792 |
=head2 request_ok |
... | ... |
@@ -841,7 +836,7 @@ Check response status for exact match. |
841 | 836 |
$t = $t->status_isnt(200); |
842 | 837 |
$t = $t->status_isnt(200, 'different status'); |
843 | 838 |
|
844 |
-Opposite of C<status_is>. |
|
839 |
+Opposite of L</"status_is">. |
|
845 | 840 |
|
846 | 841 |
=head2 text_is |
847 | 842 |
|
... | ... |
@@ -856,7 +851,7 @@ exact match with L<Mojo::DOM>. |
856 | 851 |
$t = $t->text_isnt('div.foo[x=y]' => 'Hello!'); |
857 | 852 |
$t = $t->text_isnt('html head title' => 'Hello!', 'different title'); |
858 | 853 |
|
859 |
-Opposite of C<text_is>. |
|
854 |
+Opposite of L</"text_is">. |
|
860 | 855 |
|
861 | 856 |
=head2 text_like |
862 | 857 |
|
... | ... |
@@ -871,7 +866,7 @@ similar match with L<Mojo::DOM>. |
871 | 866 |
$t = $t->text_unlike('div.foo[x=y]' => qr/Hello/); |
872 | 867 |
$t = $t->text_unlike('html head title' => qr/Hello/, 'different title'); |
873 | 868 |
|
874 |
-Opposite of C<text_like>. |
|
869 |
+Opposite of L</"text_like">. |
|
875 | 870 |
|
876 | 871 |
=head2 websocket_ok |
877 | 872 |
|
... | ... |
@@ -6,12 +6,12 @@ use Mojo::Collection 'c'; |
6 | 6 |
use Mojo::DOM; |
7 | 7 |
use Mojo::JSON 'j'; |
8 | 8 |
use Mojo::UserAgent; |
9 |
-use Mojo::Util 'monkey_patch'; |
|
9 |
+use Mojo::Util qw(dumper monkey_patch); |
|
10 | 10 |
|
11 |
-# Silent oneliners |
|
11 |
+# Silent one-liners |
|
12 | 12 |
$ENV{MOJO_LOG_LEVEL} ||= 'fatal'; |
13 | 13 |
|
14 |
-# Singleton user agent for oneliners |
|
14 |
+# Singleton user agent for one-liners |
|
15 | 15 |
my $UA = Mojo::UserAgent->new; |
16 | 16 |
|
17 | 17 |
sub import { |
... | ... |
@@ -19,14 +19,15 @@ sub import { |
19 | 19 |
# Mojolicious::Lite |
20 | 20 |
my $caller = caller; |
21 | 21 |
eval "package $caller; use Mojolicious::Lite;"; |
22 |
- $UA->app($caller->app); |
|
22 |
+ my $server = $UA->server->app($caller->app); |
|
23 |
+ $server->app->hook(around_action => sub { local $_ = $_[1]; $_[0]->() }); |
|
23 | 24 |
|
24 | 25 |
$UA->max_redirects(10) unless defined $ENV{MOJO_MAX_REDIRECTS}; |
25 |
- $UA->detect_proxy unless defined $ENV{MOJO_PROXY}; |
|
26 |
+ $UA->proxy->detect unless defined $ENV{MOJO_PROXY}; |
|
26 | 27 |
|
27 | 28 |
# The ojo DSL |
28 | 29 |
monkey_patch $caller, |
29 |
- a => sub { $caller->can('any')->(@_) and return $UA->app }, |
|
30 |
+ a => sub { $caller->can('any')->(@_) and return $UA->server->app }, |
|
30 | 31 |
b => \&b, |
31 | 32 |
c => \&c, |
32 | 33 |
d => sub { _request($UA->build_tx(DELETE => @_)) }, |
... | ... |
@@ -35,9 +36,9 @@ sub import { |
35 | 36 |
j => \&j, |
36 | 37 |
o => sub { _request($UA->build_tx(OPTIONS => @_)) }, |
37 | 38 |
p => sub { _request($UA->build_tx(POST => @_)) }, |
38 |
- r => sub { $UA->app->dumper(@_) }, |
|
39 |
- t => sub { _request($UA->build_tx(PATCH => @_)) }, |
|
40 |
- u => sub { _request($UA->build_tx(PUT => @_)) }, |
|
39 |
+ r => \&dumper, |
|
40 |
+ t => sub { _request($UA->build_tx(PATCH => @_)) }, |
|
41 |
+ u => sub { _request($UA->build_tx(PUT => @_)) }, |
|
41 | 42 |
x => sub { Mojo::DOM->new(@_) }; |
42 | 43 |
} |
43 | 44 |
|
... | ... |
@@ -51,9 +52,11 @@ sub _request { |
51 | 52 |
|
52 | 53 |
1; |
53 | 54 |
|
55 |
+=encoding utf8 |
|
56 |
+ |
|
54 | 57 |
=head1 NAME |
55 | 58 |
|
56 |
-ojo - Fun oneliners with Mojo! |
|
59 |
+ojo - Fun one-liners with Mojo! |
|
57 | 60 |
|
58 | 61 |
=head1 SYNOPSIS |
59 | 62 |
|
... | ... |
@@ -61,7 +64,7 @@ ojo - Fun oneliners with Mojo! |
61 | 64 |
|
62 | 65 |
=head1 DESCRIPTION |
63 | 66 |
|
64 |
-A collection of automatically exported functions for fun Perl oneliners. Ten |
|
67 |
+A collection of automatically exported functions for fun Perl one-liners. Ten |
|
65 | 68 |
redirects will be followed by default, you can change this behavior with the |
66 | 69 |
MOJO_MAX_REDIRECTS environment variable. |
67 | 70 |
|
... | ... |
@@ -78,11 +81,12 @@ L<ojo> implements the following functions. |
78 | 81 |
|
79 | 82 |
=head2 a |
80 | 83 |
|
81 |
- my $app = a('/hello' => sub { shift->render(json => {hello => 'world'}) }); |
|
84 |
+ my $app = a('/hello' => sub { $_->render(json => {hello => 'world'}) }); |
|
82 | 85 |
|
83 | 86 |
Create a route with L<Mojolicious::Lite/"any"> and return the current |
84 |
-L<Mojolicious::Lite> object. See also the L<Mojolicious::Lite> tutorial for |
|
85 |
-more argument variations. |
|
87 |
+L<Mojolicious::Lite> object. The current controller object is also available |
|
88 |
+to actions as C<$_>. See also the L<Mojolicious::Lite> tutorial for more |
|
89 |
+argument variations. |
|
86 | 90 |
|
87 | 91 |
$ perl -Mojo -E 'a("/hello" => {text => "Hello Mojo!"})->start' daemon |
88 | 92 |
|
... | ... |
@@ -105,25 +109,25 @@ Turn list into a L<Mojo::Collection> object. |
105 | 109 |
my $res = d('example.com'); |
106 | 110 |
my $res = d('http://example.com' => {DNT => 1} => 'Hi!'); |
107 | 111 |
|
108 |
-Perform C<DELETE> request with L<Mojo::UserAgent/"delete"> and return |
|
109 |
-resulting L<Mojo::Message::Response> object. |
|
112 |
+Perform DELETE request with L<Mojo::UserAgent/"delete"> and return resulting |
|
113 |
+L<Mojo::Message::Response> object. |
|
110 | 114 |
|
111 | 115 |
=head2 g |
112 | 116 |
|
113 | 117 |
my $res = g('example.com'); |
114 | 118 |
my $res = g('http://example.com' => {DNT => 1} => 'Hi!'); |
115 | 119 |
|
116 |
-Perform C<GET> request with L<Mojo::UserAgent/"get"> and return resulting |
|
120 |
+Perform GET request with L<Mojo::UserAgent/"get"> and return resulting |
|
117 | 121 |
L<Mojo::Message::Response> object. |
118 | 122 |
|
119 |
- $ perl -Mojo -E 'say g("mojolicio.us")->dom("h1, h2, h3")->pluck("text")' |
|
123 |
+ $ perl -Mojo -E 'say g("mojolicio.us")->dom("h1, h2, h3")->text' |
|
120 | 124 |
|
121 | 125 |
=head2 h |
122 | 126 |
|
123 | 127 |
my $res = h('example.com'); |
124 | 128 |
my $res = h('http://example.com' => {DNT => 1} => 'Hi!'); |
125 | 129 |
|
126 |
-Perform C<HEAD> request with L<Mojo::UserAgent/"head"> and return resulting |
|
130 |
+Perform HEAD request with L<Mojo::UserAgent/"head"> and return resulting |
|
127 | 131 |
L<Mojo::Message::Response> object. |
128 | 132 |
|
129 | 133 |
=head2 j |
... | ... |
@@ -141,22 +145,22 @@ Encode Perl data structure or decode JSON with L<Mojo::JSON>. |
141 | 145 |
my $res = o('example.com'); |
142 | 146 |
my $res = o('http://example.com' => {DNT => 1} => 'Hi!'); |
143 | 147 |
|
144 |
-Perform C<OPTIONS> request with L<Mojo::UserAgent/"options"> and return |
|
145 |
-resulting L<Mojo::Message::Response> object. |
|
148 |
+Perform OPTIONS request with L<Mojo::UserAgent/"options"> and return resulting |
|
149 |
+L<Mojo::Message::Response> object. |
|
146 | 150 |
|
147 | 151 |
=head2 p |
148 | 152 |
|
149 | 153 |
my $res = p('example.com'); |
150 | 154 |
my $res = p('http://example.com' => {DNT => 1} => 'Hi!'); |
151 | 155 |
|
152 |
-Perform C<POST> request with L<Mojo::UserAgent/"post"> and return resulting |
|
156 |
+Perform POST request with L<Mojo::UserAgent/"post"> and return resulting |
|
153 | 157 |
L<Mojo::Message::Response> object. |
154 | 158 |
|
155 | 159 |
=head2 r |
156 | 160 |
|
157 | 161 |
my $perl = r({data => 'structure'}); |
158 | 162 |
|
159 |
-Dump a Perl data structure with L<Data::Dumper>. |
|
163 |
+Dump a Perl data structure with L<Mojo::Util/"dumper">. |
|
160 | 164 |
|
161 | 165 |
perl -Mojo -E 'say r(g("example.com")->headers->to_hash)' |
162 | 166 |
|
... | ... |
@@ -165,7 +169,7 @@ Dump a Perl data structure with L<Data::Dumper>. |
165 | 169 |
my $res = t('example.com'); |
166 | 170 |
my $res = t('http://example.com' => {DNT => 1} => 'Hi!'); |
167 | 171 |
|
168 |
-Perform C<PATCH> request with L<Mojo::UserAgent/"patch"> and return resulting |
|
172 |
+Perform PATCH request with L<Mojo::UserAgent/"patch"> and return resulting |
|
169 | 173 |
L<Mojo::Message::Response> object. |
170 | 174 |
|
171 | 175 |
=head2 u |
... | ... |
@@ -173,7 +177,7 @@ L<Mojo::Message::Response> object. |
173 | 177 |
my $res = u('example.com'); |
174 | 178 |
my $res = u('http://example.com' => {DNT => 1} => 'Hi!'); |
175 | 179 |
|
176 |
-Perform C<PUT> request with L<Mojo::UserAgent/"put"> and return resulting |
|
180 |
+Perform PUT request with L<Mojo::UserAgent/"put"> and return resulting |
|
177 | 181 |
L<Mojo::Message::Response> object. |
178 | 182 |
|
179 | 183 |
=head2 x |
... | ... |
@@ -8,14 +8,13 @@ BEGIN { unshift @INC, "$FindBin::Bin/../lib" } |
8 | 8 |
|
9 | 9 |
use Getopt::Long qw(GetOptions :config no_auto_abbrev no_ignore_case); |
10 | 10 |
|
11 |
-GetOptions( |
|
11 |
+GetOptions |
|
12 | 12 |
'f|foreground' => sub { $ENV{HYPNOTOAD_FOREGROUND} = 1 }, |
13 | 13 |
'h|help' => \my $help, |
14 | 14 |
's|stop' => sub { $ENV{HYPNOTOAD_STOP} = 1 }, |
15 |
- 't|test' => sub { $ENV{HYPNOTOAD_TEST} = 1 } |
|
16 |
-); |
|
15 |
+ 't|test' => sub { $ENV{HYPNOTOAD_TEST} = 1 }; |
|
17 | 16 |
|
18 |
-die <<"EOF" if $help || !(my $app = shift || $ENV{HYPNOTOAD_APP}); |
|
17 |
+die <<EOF if $help || !(my $app = shift || $ENV{HYPNOTOAD_APP}); |
|
19 | 18 |
usage: $0 [OPTIONS] [APPLICATION] |
20 | 19 |
|
21 | 20 |
hypnotoad script/myapp |
... | ... |
@@ -32,6 +31,8 @@ EOF |
32 | 31 |
require Mojo::Server::Hypnotoad; |
33 | 32 |
Mojo::Server::Hypnotoad->new->run($app); |
34 | 33 |
|
34 |
+=encoding utf8 |
|
35 |
+ |
|
35 | 36 |
=head1 NAME |
36 | 37 |
|
37 | 38 |
hypnotoad - Hypnotoad HTTP and WebSocket server |
... | ... |
@@ -9,6 +9,8 @@ BEGIN { unshift @INC, "$FindBin::Bin/../lib" } |
9 | 9 |
require Mojolicious::Commands; |
10 | 10 |
Mojolicious::Commands->start_app('Mojo::HelloWorld'); |
11 | 11 |
|
12 |
+=encoding utf8 |
|
13 |
+ |
|
12 | 14 |
=head1 NAME |
13 | 15 |
|
14 | 16 |
mojo - The Mojolicious command system |
... | ... |
@@ -8,18 +8,19 @@ BEGIN { unshift @INC, "$FindBin::Bin/../lib" } |
8 | 8 |
|
9 | 9 |
use Getopt::Long qw(GetOptions :config no_auto_abbrev no_ignore_case); |
10 | 10 |
|
11 |
-GetOptions( |
|
11 |
+GetOptions |
|
12 | 12 |
'h|help' => \my $help, |
13 | 13 |
'l|listen=s' => \my @listen, |
14 |
+ 'm|mode=s' => sub { $ENV{MOJO_MODE} = $_[1] }, |
|
14 | 15 |
'v|verbose' => sub { $ENV{MORBO_VERBOSE} = 1 }, |
15 |
- 'w|watch=s' => \my @watch |
|
16 |
-); |
|
16 |
+ 'w|watch=s' => \my @watch; |
|
17 | 17 |
|
18 |
-die <<"EOF" if $help || !(my $app = shift); |
|
18 |
+die <<EOF if $help || !(my $app = shift); |
|
19 | 19 |
usage: $0 [OPTIONS] [APPLICATION] |
20 | 20 |
|
21 | 21 |
morbo script/myapp |
22 | 22 |
morbo myapp.pl |
23 |
+ morbo -m production -l https://*:443 |
|
23 | 24 |
morbo -w /usr/local/lib -w public myapp.pl |
24 | 25 |
|
25 | 26 |
These options are available: |
... | ... |
@@ -27,6 +28,9 @@ These options are available: |
27 | 28 |
-l, --listen <location> Set one or more locations you want to listen |
28 | 29 |
on, defaults to the value of MOJO_LISTEN or |
29 | 30 |
"http://*:3000". |
31 |
+ -m, --mode <name> Operating mode for your application, defaults |
|
32 |
+ to the value of MOJO_MODE/PLACK_ENV or |
|
33 |
+ "development". |
|
30 | 34 |
-v, --verbose Print details about what files changed to |
31 | 35 |
STDOUT. |
32 | 36 |
-w, --watch <directory/file> Set one or more directories and files to |
... | ... |
@@ -42,6 +46,8 @@ my $morbo = Mojo::Server::Morbo->new; |
42 | 46 |
$morbo->watch(\@watch) if @watch; |
43 | 47 |
$morbo->run($app); |
44 | 48 |
|
49 |
+=encoding utf8 |
|
50 |
+ |
|
45 | 51 |
=head1 NAME |
46 | 52 |
|
47 | 53 |
morbo - Morbo HTTP and WebSocket development server |