... | ... |
@@ -130,7 +130,7 @@ plugins, since non-blocking requests that are already in progress will |
130 | 130 |
interfere with new blocking ones. |
131 | 131 |
|
132 | 132 |
# Perform blocking request |
133 |
- my $body = $app->ua->get('mojolicio.us')->res->body; |
|
133 |
+ my $body = $app->ua->get('example.com')->res->body; |
|
134 | 134 |
|
135 | 135 |
=head1 METHODS |
136 | 136 |
|
... | ... |
@@ -153,17 +153,15 @@ object. |
153 | 153 |
|
154 | 154 |
=head2 config |
155 | 155 |
|
156 |
- my $config = $app->config; |
|
157 |
- my $foo = $app->config('foo'); |
|
158 |
- $app = $app->config({foo => 'bar'}); |
|
159 |
- $app = $app->config(foo => 'bar'); |
|
156 |
+ my $hash = $app->config; |
|
157 |
+ my $foo = $app->config('foo'); |
|
158 |
+ $app = $app->config({foo => 'bar'}); |
|
159 |
+ $app = $app->config(foo => 'bar'); |
|
160 | 160 |
|
161 | 161 |
Application configuration. |
162 | 162 |
|
163 |
- # Manipulate configuration |
|
164 |
- $app->config->{foo} = 'bar'; |
|
165 |
- my $foo = $app->config->{foo}; |
|
166 |
- delete $app->config->{foo}; |
|
163 |
+ # Remove value |
|
164 |
+ my $foo = delete $app->config->{foo}; |
|
167 | 165 |
|
168 | 166 |
=head2 handler |
169 | 167 |
|
... | ... |
@@ -83,9 +83,10 @@ subclass. |
83 | 83 |
=head2 get_chunk |
84 | 84 |
|
85 | 85 |
my $bytes = $asset->get_chunk($offset); |
86 |
+ my $bytes = $asset->get_chunk($offset, $max); |
|
86 | 87 |
|
87 |
-Get chunk of data starting from a specific position. Meant to be overloaded |
|
88 |
-in a subclass. |
|
88 |
+Get chunk of data starting from a specific position, defaults to a maximum |
|
89 |
+chunk size of C<131072> bytes. Meant to be overloaded in a subclass. |
|
89 | 90 |
|
90 | 91 |
=head2 is_file |
91 | 92 |
|
... | ... |
@@ -22,7 +22,7 @@ has handle => sub { |
22 | 22 |
} |
23 | 23 |
|
24 | 24 |
# Open new or temporary file |
25 |
- my $base = catfile File::Spec::Functions::tmpdir, 'mojo.tmp'; |
|
25 |
+ my $base = catfile $self->tmpdir, 'mojo.tmp'; |
|
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}; |
... | ... |
@@ -57,14 +57,14 @@ sub add_chunk { |
57 | 57 |
} |
58 | 58 |
|
59 | 59 |
sub contains { |
60 |
- my ($self, $string) = @_; |
|
60 |
+ my ($self, $str) = @_; |
|
61 | 61 |
|
62 | 62 |
my $handle = $self->handle; |
63 | 63 |
$handle->sysseek($self->start_range, SEEK_SET); |
64 | 64 |
|
65 | 65 |
# Calculate window size |
66 | 66 |
my $end = defined $self->end_range ? $self->end_range : $self->size; |
67 |
- my $len = length $string; |
|
67 |
+ my $len = length $str; |
|
68 | 68 |
my $size = $len > 131072 ? $len : 131072; |
69 | 69 |
$size = $end - $self->start_range if $size > $end - $self->start_range; |
70 | 70 |
|
... | ... |
@@ -79,7 +79,7 @@ sub contains { |
79 | 79 |
$window .= $buffer; |
80 | 80 |
|
81 | 81 |
# Search window |
82 |
- my $pos = index $window, $string; |
|
82 |
+ my $pos = index $window, $str; |
|
83 | 83 |
return $offset + $pos if $pos >= 0; |
84 | 84 |
$offset += $read; |
85 | 85 |
return -1 if $read == 0 || $offset == $end; |
... | ... |
@@ -92,19 +92,20 @@ sub contains { |
92 | 92 |
} |
93 | 93 |
|
94 | 94 |
sub get_chunk { |
95 |
- my ($self, $start) = @_; |
|
95 |
+ my ($self, $offset, $max) = @_; |
|
96 |
+ $max = defined $max ? $max : 131072; |
|
96 | 97 |
|
97 |
- $start += $self->start_range; |
|
98 |
+ $offset += $self->start_range; |
|
98 | 99 |
my $handle = $self->handle; |
99 |
- $handle->sysseek($start, SEEK_SET); |
|
100 |
+ $handle->sysseek($offset, SEEK_SET); |
|
100 | 101 |
|
101 | 102 |
my $buffer; |
102 | 103 |
if (defined(my $end = $self->end_range)) { |
103 |
- my $chunk = $end + 1 - $start; |
|
104 |
+ my $chunk = $end + 1 - $offset; |
|
104 | 105 |
return '' if $chunk <= 0; |
105 |
- $handle->sysread($buffer, $chunk > 131072 ? 131072 : $chunk); |
|
106 |
+ $handle->sysread($buffer, $chunk > $max ? $max : $chunk); |
|
106 | 107 |
} |
107 |
- else { $handle->sysread($buffer, 131072) } |
|
108 |
+ else { $handle->sysread($buffer, $max) } |
|
108 | 109 |
|
109 | 110 |
return $buffer; |
110 | 111 |
} |
... | ... |
@@ -199,7 +200,7 @@ necessary. |
199 | 200 |
$file = $file->tmpdir('/tmp'); |
200 | 201 |
|
201 | 202 |
Temporary directory used to generate C<path>, defaults to the value of the |
202 |
-C<MOJO_TMPDIR> environment variable or auto detection. |
|
203 |
+MOJO_TMPDIR environment variable or auto detection. |
|
203 | 204 |
|
204 | 205 |
=head1 METHODS |
205 | 206 |
|
... | ... |
@@ -220,9 +221,11 @@ Check if asset contains a specific string. |
220 | 221 |
|
221 | 222 |
=head2 get_chunk |
222 | 223 |
|
223 |
- my $bytes = $file->get_chunk($start); |
|
224 |
+ my $bytes = $file->get_chunk($offset); |
|
225 |
+ my $bytes = $file->get_chunk($offset, $max); |
|
224 | 226 |
|
225 |
-Get chunk of data starting from a specific position. |
|
227 |
+Get chunk of data starting from a specific position, defaults to a maximum |
|
228 |
+chunk size of C<131072> bytes. |
|
226 | 229 |
|
227 | 230 |
=head2 is_file |
228 | 231 |
|
... | ... |
@@ -21,26 +21,26 @@ sub add_chunk { |
21 | 21 |
} |
22 | 22 |
|
23 | 23 |
sub contains { |
24 |
- my ($self, $string) = @_; |
|
24 |
+ my ($self, $str) = @_; |
|
25 | 25 |
|
26 | 26 |
my $start = $self->start_range; |
27 |
- my $pos = index $self->{content}, $string, $start; |
|
27 |
+ my $pos = index $self->{content}, $str, $start; |
|
28 | 28 |
$pos -= $start if $start && $pos >= 0; |
29 | 29 |
my $end = $self->end_range; |
30 | 30 |
|
31 |
- return $end && ($pos + length $string) >= $end ? -1 : $pos; |
|
31 |
+ return $end && ($pos + length $str) >= $end ? -1 : $pos; |
|
32 | 32 |
} |
33 | 33 |
|
34 | 34 |
sub get_chunk { |
35 |
- my ($self, $start) = @_; |
|
35 |
+ my ($self, $offset, $max) = @_; |
|
36 |
+ $max = defined $max ? $max : 131072; |
|
36 | 37 |
|
37 |
- $start += $self->start_range; |
|
38 |
- my $size = 131072; |
|
38 |
+ $offset += $self->start_range; |
|
39 | 39 |
if (my $end = $self->end_range) { |
40 |
- $size = $end + 1 - $start if ($start + $size) > $end; |
|
40 |
+ $max = $end + 1 - $offset if ($offset + $max) > $end; |
|
41 | 41 |
} |
42 | 42 |
|
43 |
- return substr shift->{content}, $start, $size; |
|
43 |
+ return substr shift->{content}, $offset, $max; |
|
44 | 44 |
} |
45 | 45 |
|
46 | 46 |
sub move_to { |
... | ... |
@@ -110,7 +110,7 @@ automatically upgrade to a L<Mojo::Asset::File> object. |
110 | 110 |
|
111 | 111 |
Maximum size in bytes of data to keep in memory before automatically upgrading |
112 | 112 |
to a L<Mojo::Asset::File> object, defaults to the value of the |
113 |
-C<MOJO_MAX_MEMORY_SIZE> environment variable or C<262144>. |
|
113 |
+MOJO_MAX_MEMORY_SIZE environment variable or C<262144>. |
|
114 | 114 |
|
115 | 115 |
=head1 METHODS |
116 | 116 |
|
... | ... |
@@ -139,8 +139,10 @@ Check if asset contains a specific string. |
139 | 139 |
=head2 get_chunk |
140 | 140 |
|
141 | 141 |
my $bytes = $mem->get_chunk($offset); |
142 |
+ my $bytes = $mem->get_chunk($offset, $max); |
|
142 | 143 |
|
143 |
-Get chunk of data starting from a specific position. |
|
144 |
+Get chunk of data starting from a specific position, defaults to a maximum |
|
145 |
+chunk size of C<131072> bytes. |
|
144 | 146 |
|
145 | 147 |
=head2 move_to |
146 | 148 |
|
... | ... |
@@ -213,7 +213,7 @@ pass it either a hash or a hash reference with attribute values. |
213 | 213 |
Create attribute accessor for hash-based objects, an array reference can be |
214 | 214 |
used to create more than one at a time. Pass an optional second argument to |
215 | 215 |
set a default value, it should be a constant or a callback. The callback will |
216 |
-be excuted at accessor read time if there's no set value. Accessors can be |
|
216 |
+be executed at accessor read time if there's no set value. Accessors can be |
|
217 | 217 |
chained, that means they return their invocant when they are called with an |
218 | 218 |
argument. |
219 | 219 |
|
... | ... |
@@ -222,7 +222,8 @@ argument. |
222 | 222 |
$object = $object->tap(sub {...}); |
223 | 223 |
|
224 | 224 |
K combinator, tap into a method chain to perform operations on an object |
225 |
-within the chain. |
|
225 |
+within the chain. The object will be the first argument passed to the closure |
|
226 |
+and is also available via C<$_>. |
|
226 | 227 |
|
227 | 228 |
=head2 C<say> |
228 | 229 |
|
... | ... |
@@ -230,7 +231,7 @@ Backported from perl-5.10.1 |
230 | 231 |
|
231 | 232 |
=head1 DEBUGGING |
232 | 233 |
|
233 |
-You can set the C<MOJO_BASE_DEBUG> environment variable to get some advanced |
|
234 |
+You can set the MOJO_BASE_DEBUG environment variable to get some advanced |
|
234 | 235 |
diagnostics information printed to C<STDERR>. |
235 | 236 |
|
236 | 237 |
MOJO_BASE_DEBUG=1 |
... | ... |
@@ -10,12 +10,11 @@ our @EXPORT_OK = ('b'); |
10 | 10 |
|
11 | 11 |
# Turn most functions from Mojo::Util into methods |
12 | 12 |
my @UTILS = ( |
13 |
- qw(b64_decode b64_encode camelize decamelize hmac_md5_sum hmac_sha1_sum), |
|
14 |
- qw(html_unescape md5_bytes md5_sum punycode_decode punycode_encode quote), |
|
15 |
- qw(sha1_bytes sha1_sum slurp spurt squish trim unquote url_escape), |
|
16 |
- qw(url_unescape xml_escape xor_encode) |
|
13 |
+ qw(b64_decode b64_encode camelize decamelize hmac_sha1_sum html_unescape), |
|
14 |
+ qw(md5_bytes md5_sum punycode_decode punycode_encode quote sha1_bytes), |
|
15 |
+ qw(sha1_sum slurp spurt squish trim unquote url_escape url_unescape), |
|
16 |
+ qw(xml_escape xor_encode) |
|
17 | 17 |
); |
18 |
-push @UTILS, 'html_escape'; # DEPRECATED in Rainbow! |
|
19 | 18 |
for my $name (@UTILS) { |
20 | 19 |
my $sub = Mojo::Util->can($name); |
21 | 20 |
Mojo::Util::monkey_patch __PACKAGE__, $name, sub { |
... | ... |
@@ -165,12 +164,6 @@ Encode bytestream with L<Mojo::Util/"encode">, defaults to C<UTF-8>. |
165 | 164 |
|
166 | 165 |
$stream->trim->quote->encode->say; |
167 | 166 |
|
168 |
-=head2 hmac_md5_sum |
|
169 |
- |
|
170 |
- $stream = $stream->hmac_md5_sum('passw0rd'); |
|
171 |
- |
|
172 |
-Generate HMAC-MD5 checksum for bytestream with L<Mojo::Util/"hmac_md5_sum">. |
|
173 |
- |
|
174 | 167 |
=head2 hmac_sha1_sum |
175 | 168 |
|
176 | 169 |
$stream = $stream->hmac_sha1_sum('passw0rd'); |
... | ... |
@@ -226,7 +219,7 @@ Print bytestream to handle and append a newline, defaults to C<STDOUT>. |
226 | 219 |
|
227 | 220 |
=head2 secure_compare |
228 | 221 |
|
229 |
- my $success = $stream->secure_compare($string); |
|
222 |
+ my $success = $stream->secure_compare($str); |
|
230 | 223 |
|
231 | 224 |
Compare bytestream with L<Mojo::Util/"secure_compare">. |
232 | 225 |
|
... | ... |
@@ -284,8 +277,8 @@ L<Mojo::Util/"squish">. |
284 | 277 |
|
285 | 278 |
=head2 to_string |
286 | 279 |
|
287 |
- my $string = $stream->to_string; |
|
288 |
- my $string = "$stream"; |
|
280 |
+ my $str = $stream->to_string; |
|
281 |
+ my $str = "$stream"; |
|
289 | 282 |
|
290 | 283 |
Stringify bytestream. |
291 | 284 |
|
... | ... |
@@ -28,16 +28,15 @@ sub each { |
28 | 28 |
|
29 | 29 |
sub first { |
30 | 30 |
my ($self, $cb) = @_; |
31 |
- return $cb ? do { (ref $cb) eq 'CODE' ? List::Util::first { $cb->($_) } @$self : List::Util::first { $_ =~ $cb } @$self } : $self->[0]; |
|
31 |
+ return $self->[0] unless $cb; |
|
32 |
+ return List::Util::first { $cb->($_) } @$self if ref $cb eq 'CODE'; |
|
33 |
+ return List::Util::first { $_ =~ $cb } @$self; |
|
32 | 34 |
} |
33 | 35 |
|
34 | 36 |
sub grep { |
35 | 37 |
my ($self, $cb) = @_; |
36 |
- if ((ref $cb) eq 'CODE') { |
|
37 |
- return $self->new(grep { $cb->($_) } @$self); |
|
38 |
- } else { |
|
39 |
- return $self->new(grep { $_ =~ $cb } @$self); |
|
40 |
- } |
|
38 |
+ return $self->new(grep { $cb->($_) } @$self) if ref $cb eq 'CODE'; |
|
39 |
+ return $self->new(grep { $_ =~ $cb } @$self); |
|
41 | 40 |
} |
42 | 41 |
|
43 | 42 |
sub join { |
... | ... |
@@ -232,6 +231,13 @@ from the results. |
232 | 231 |
|
233 | 232 |
Create a new collection without duplicate elements. |
234 | 233 |
|
234 |
+=head1 ELEMENTS |
|
235 |
+ |
|
236 |
+Direct array reference access to elements is also possible. |
|
237 |
+ |
|
238 |
+ say $collection->[23]; |
|
239 |
+ say for @$collection; |
|
240 |
+ |
|
235 | 241 |
=head1 SEE ALSO |
236 | 242 |
|
237 | 243 |
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
... | ... |
@@ -27,7 +27,7 @@ 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 = shift->headers->content_type || ''; |
|
30 |
+ my $type = do {my $tmp = shift->headers->content_type; defined $tmp ? $tmp : ''}; |
|
31 | 31 |
return $type =~ /charset="?([^"\s;]+)"?/i ? $1 : undef; |
32 | 32 |
} |
33 | 33 |
|
... | ... |
@@ -64,15 +64,13 @@ sub get_header_chunk { |
64 | 64 |
return substr $self->{header_buffer}, $offset, 131072; |
65 | 65 |
} |
66 | 66 |
|
67 |
-sub has_leftovers { !!length shift->leftovers } |
|
68 |
- |
|
69 | 67 |
sub header_size { length shift->build_headers } |
70 | 68 |
|
71 | 69 |
sub is_chunked { !!shift->headers->transfer_encoding } |
72 | 70 |
|
73 |
-sub is_compressed { (shift->headers->content_encoding || '') =~ /^gzip$/i } |
|
71 |
+sub is_compressed { (do {my $tmp = shift->headers->content_encoding; defined $tmp ? $tmp : ''}) =~ /^gzip$/i } |
|
74 | 72 |
|
75 |
-sub is_dynamic { $_[0]->{dynamic} && !defined $_[0]->headers->content_length } |
|
73 |
+sub is_dynamic { $_[0]{dynamic} && !defined $_[0]->headers->content_length } |
|
76 | 74 |
|
77 | 75 |
sub is_finished { my $tmp = shift->{state}; (defined $tmp ? $tmp : '') eq 'finished' } |
78 | 76 |
|
... | ... |
@@ -117,8 +115,8 @@ sub parse { |
117 | 115 |
# Relaxed parsing |
118 | 116 |
my $headers = $self->headers; |
119 | 117 |
if ($self->auto_relax) { |
120 |
- my $connection = $headers->connection || ''; |
|
121 |
- my $len = defined $headers->content_length ? $headers->content_length : ''; |
|
118 |
+ my $connection = defined $headers->connection ? $headers->connection : ''; |
|
119 |
+ my $len = defined $headers->content_length ? $headers->content_length : ''; |
|
122 | 120 |
$self->relaxed(1) |
123 | 121 |
if !length $len && ($connection =~ /close/i || $headers->content_type); |
124 | 122 |
} |
... | ... |
@@ -155,7 +153,7 @@ sub parse_body { |
155 | 153 |
sub progress { |
156 | 154 |
my $self = shift; |
157 | 155 |
return 0 unless my $state = $self->{state}; |
158 |
- return 0 unless grep { $_ eq $state } qw(body finished); |
|
156 |
+ return 0 unless $state eq 'body' || $state eq 'finished'; |
|
159 | 157 |
return $self->{raw_size} - ($self->{header_size} || 0); |
160 | 158 |
} |
161 | 159 |
|
... | ... |
@@ -398,7 +396,7 @@ Content headers, defaults to a L<Mojo::Headers> object. |
398 | 396 |
$content = $content->max_buffer_size(1024); |
399 | 397 |
|
400 | 398 |
Maximum size in bytes of buffer for content parser, defaults to the value of |
401 |
-the C<MOJO_MAX_BUFFER_SIZE> environment variable or C<262144>. |
|
399 |
+the MOJO_MAX_BUFFER_SIZE environment variable or C<262144>. |
|
402 | 400 |
|
403 | 401 |
=head2 max_leftover_size |
404 | 402 |
|
... | ... |
@@ -406,7 +404,7 @@ the C<MOJO_MAX_BUFFER_SIZE> environment variable or C<262144>. |
406 | 404 |
$content = $content->max_leftover_size(1024); |
407 | 405 |
|
408 | 406 |
Maximum size in bytes of buffer for pipelined HTTP requests, defaults to the |
409 |
-value of the C<MOJO_MAX_LEFTOVER_SIZE> environment variable or C<262144>. |
|
407 |
+value of the MOJO_MAX_LEFTOVER_SIZE environment variable or C<262144>. |
|
410 | 408 |
|
411 | 409 |
=head2 relaxed |
412 | 410 |
|
... | ... |
@@ -449,13 +447,13 @@ Extract multipart boundary from C<Content-Type> header. |
449 | 447 |
|
450 | 448 |
=head2 build_body |
451 | 449 |
|
452 |
- my $string = $content->build_body; |
|
450 |
+ my $str = $content->build_body; |
|
453 | 451 |
|
454 | 452 |
Render whole body. |
455 | 453 |
|
456 | 454 |
=head2 build_headers |
457 | 455 |
|
458 |
- my $string = $content->build_headers; |
|
456 |
+ my $str = $content->build_headers; |
|
459 | 457 |
|
460 | 458 |
Render all headers. |
461 | 459 |
|
... | ... |
@@ -481,20 +479,14 @@ Generate dynamic content. |
481 | 479 |
|
482 | 480 |
my $bytes = $content->get_body_chunk(0); |
483 | 481 |
|
484 |
-Get a chunk of content starting from a specfic position. Meant to be |
|
482 |
+Get a chunk of content starting from a specific position. Meant to be |
|
485 | 483 |
overloaded in a subclass. |
486 | 484 |
|
487 | 485 |
=head2 get_header_chunk |
488 | 486 |
|
489 | 487 |
my $bytes = $content->get_header_chunk(13); |
490 | 488 |
|
491 |
-Get a chunk of the headers starting from a specfic position. |
|
492 |
- |
|
493 |
-=head2 has_leftovers |
|
494 |
- |
|
495 |
- my $success = $content->has_leftovers; |
|
496 |
- |
|
497 |
-Check if there are leftovers. |
|
489 |
+Get a chunk of the headers starting from a specific position. |
|
498 | 490 |
|
499 | 491 |
=head2 header_size |
500 | 492 |
|
... | ... |
@@ -52,7 +52,7 @@ sub build_boundary { |
52 | 52 |
|
53 | 53 |
# Add boundary to Content-Type header |
54 | 54 |
my $headers = $self->headers; |
55 |
- ($headers->content_type || '') =~ m!^(.*multipart/[^;]+)(.*)$!; |
|
55 |
+ (defined $headers->content_type ? $headers->content_type : '') =~ m!^(.*multipart/[^;]+)(.*)$!; |
|
56 | 56 |
my $before = $1 || 'multipart/mixed'; |
57 | 57 |
my $after = $2 || ''; |
58 | 58 |
$headers->content_type("$before; boundary=$boundary$after"); |
... | ... |
@@ -289,7 +289,7 @@ Clone content if possible, otherwise return C<undef>. |
289 | 289 |
|
290 | 290 |
my $bytes = $multi->get_body_chunk(0); |
291 | 291 |
|
292 |
-Get a chunk of content starting from a specfic position. |
|
292 |
+Get a chunk of content starting from a specific position. |
|
293 | 293 |
|
294 | 294 |
=head2 is_multipart |
295 | 295 |
|
... | ... |
@@ -145,7 +145,7 @@ Clone content if possible, otherwise return C<undef>. |
145 | 145 |
|
146 | 146 |
my $bytes = $single->get_body_chunk(0); |
147 | 147 |
|
148 |
-Get a chunk of content starting from a specfic position. |
|
148 |
+Get a chunk of content starting from a specific position. |
|
149 | 149 |
|
150 | 150 |
=head2 parse |
151 | 151 |
|
... | ... |
@@ -154,7 +154,7 @@ Get a chunk of content starting from a specfic position. |
154 | 154 |
= $single->parse("Content-Type: multipart/form-data\x0d\x0a\x0d\x0a"); |
155 | 155 |
|
156 | 156 |
Parse content chunk and upgrade to L<Mojo::Content::MultiPart> object if |
157 |
-possible. |
|
157 |
+necessary. |
|
158 | 158 |
|
159 | 159 |
=head1 SEE ALSO |
160 | 160 |
|
... | ... |
@@ -14,30 +14,26 @@ sub parse { croak 'Method "parse" not implemented by subclass' } |
14 | 14 |
sub to_string { croak 'Method "to_string" not implemented by subclass' } |
15 | 15 |
|
16 | 16 |
sub _tokenize { |
17 |
- my ($self, $string) = @_; |
|
17 |
+ my ($self, $str) = @_; |
|
18 | 18 |
|
19 | 19 |
# Nibbling parser |
20 | 20 |
my (@tree, @token); |
21 |
- while ($string) { |
|
22 |
- |
|
23 |
- # Name |
|
24 |
- last unless $string =~ s/^\s*([^=;,]+)\s*=?\s*//; |
|
21 |
+ while ($str =~ s/^\s*([^=;,]+)\s*=?\s*//) { |
|
25 | 22 |
my $name = $1; |
26 | 23 |
|
27 | 24 |
# "expires" is a special case, thank you Netscape... |
28 |
- $string =~ s/^([^;,]+,?[^;,]+)/"$1"/ if $name =~ /^expires$/i; |
|
25 |
+ $str =~ s/^([^;,]+,?[^;,]+)/"$1"/ if $name =~ /^expires$/i; |
|
29 | 26 |
|
30 | 27 |
# Value |
31 | 28 |
my $value; |
32 |
- $value = unquote $1 if $string =~ s/^("(?:\\\\|\\"|[^"])+"|[^;,]+)\s*//; |
|
29 |
+ $value = unquote $1 if $str =~ s/^("(?:\\\\|\\"|[^"])+"|[^;,]+)\s*//; |
|
33 | 30 |
push @token, [$name, $value]; |
34 | 31 |
|
35 | 32 |
# Separator |
36 |
- $string =~ s/^\s*;\s*//; |
|
37 |
- if ($string =~ s/^\s*,\s*//) { |
|
38 |
- push @tree, [@token]; |
|
39 |
- @token = (); |
|
40 |
- } |
|
33 |
+ $str =~ s/^\s*;\s*//; |
|
34 |
+ next unless $str =~ s/^\s*,\s*//; |
|
35 |
+ push @tree, [@token]; |
|
36 |
+ @token = (); |
|
41 | 37 |
} |
42 | 38 |
|
43 | 39 |
# Take care of final token |
... | ... |
@@ -60,7 +56,8 @@ Mojo::Cookie - HTTP cookie base class |
60 | 56 |
|
61 | 57 |
=head1 DESCRIPTION |
62 | 58 |
|
63 |
-L<Mojo::Cookie> is an abstract base class for HTTP cookies. |
|
59 |
+L<Mojo::Cookie> is an abstract base class for HTTP cookies as described in RFC |
|
60 |
+6265. |
|
64 | 61 |
|
65 | 62 |
=head1 ATTRIBUTES |
66 | 63 |
|
... | ... |
@@ -87,14 +84,14 @@ following new ones. |
87 | 84 |
|
88 | 85 |
=head2 parse |
89 | 86 |
|
90 |
- my $cookies = $cookie->parse($string); |
|
87 |
+ my $cookies = $cookie->parse($str); |
|
91 | 88 |
|
92 | 89 |
Parse cookies. Meant to be overloaded in a subclass. |
93 | 90 |
|
94 | 91 |
=head2 to_string |
95 | 92 |
|
96 |
- my $string = $cookie->to_string; |
|
97 |
- my $string = "$cookie"; |
|
93 |
+ my $str = $cookie->to_string; |
|
94 |
+ my $str = "$cookie"; |
|
98 | 95 |
|
99 | 96 |
Render cookie. Meant to be overloaded in a subclass. |
100 | 97 |
|
... | ... |
@@ -4,14 +4,13 @@ use Mojo::Base 'Mojo::Cookie'; |
4 | 4 |
use Mojo::Util 'quote'; |
5 | 5 |
|
6 | 6 |
sub parse { |
7 |
- my ($self, $string) = @_; |
|
7 |
+ my ($self, $str) = @_; |
|
8 | 8 |
|
9 | 9 |
my @cookies; |
10 |
- for my $token (map {@$_} $self->_tokenize($string)) { |
|
10 |
+ for my $token (map {@$_} $self->_tokenize(defined $str ? $str : '')) { |
|
11 | 11 |
my ($name, $value) = @$token; |
12 | 12 |
next if $name =~ /^\$/; |
13 |
- push @cookies, |
|
14 |
- Mojo::Cookie::Request->new(name => $name, value => defined $value ? $value : ''); |
|
13 |
+ push @cookies, $self->new(name => $name, value => defined $value ? $value : ''); |
|
15 | 14 |
} |
16 | 15 |
|
17 | 16 |
return \@cookies; |
... | ... |
@@ -42,7 +41,8 @@ Mojo::Cookie::Request - HTTP request cookie |
42 | 41 |
|
43 | 42 |
=head1 DESCRIPTION |
44 | 43 |
|
45 |
-L<Mojo::Cookie::Request> is a container for HTTP request cookies. |
|
44 |
+L<Mojo::Cookie::Request> is a container for HTTP request cookies as described |
|
45 |
+in RFC 6265. |
|
46 | 46 |
|
47 | 47 |
=head1 ATTRIBUTES |
48 | 48 |
|
... | ... |
@@ -55,13 +55,13 @@ implements the following new ones. |
55 | 55 |
|
56 | 56 |
=head2 parse |
57 | 57 |
|
58 |
- my $cookies = $cookie->parse('f=b; g=a'); |
|
58 |
+ my $cookies = Mojo::Cookie::Request->parse('f=b; g=a'); |
|
59 | 59 |
|
60 | 60 |
Parse cookies. |
61 | 61 |
|
62 | 62 |
=head2 to_string |
63 | 63 |
|
64 |
- my $string = $cookie->to_string; |
|
64 |
+ my $str = $cookie->to_string; |
|
65 | 65 |
|
66 | 66 |
Render cookie. |
67 | 67 |
|
... | ... |
@@ -10,10 +10,8 @@ sub expires { |
10 | 10 |
my $self = shift; |
11 | 11 |
|
12 | 12 |
# Upgrade |
13 |
- return $self->{expires} |
|
14 |
- = defined $self->{expires} && !ref $self->{expires} |
|
15 |
- ? Mojo::Date->new($self->{expires}) |
|
16 |
- : $self->{expires} |
|
13 |
+ my $e = $self->{expires}; |
|
14 |
+ return $self->{expires} = defined $e && !ref $e ? Mojo::Date->new($e) : $e |
|
17 | 15 |
unless @_; |
18 | 16 |
$self->{expires} = shift; |
19 | 17 |
|
... | ... |
@@ -21,23 +19,21 @@ sub expires { |
21 | 19 |
} |
22 | 20 |
|
23 | 21 |
sub parse { |
24 |
- my ($self, $string) = @_; |
|
22 |
+ my ($self, $str) = @_; |
|
25 | 23 |
|
26 | 24 |
my @cookies; |
27 |
- for my $token ($self->_tokenize($string)) { |
|
25 |
+ for my $token ($self->_tokenize(defined $str ? $str : '')) { |
|
28 | 26 |
for my $i (0 .. $#$token) { |
29 | 27 |
my ($name, $value) = @{$token->[$i]}; |
30 | 28 |
|
31 | 29 |
# This will only run once |
32 |
- push(@cookies, |
|
33 |
- Mojo::Cookie::Response->new(name => $name, value => defined $value ? $value : '')) |
|
34 |
- and next |
|
30 |
+ push @cookies, $self->new(name => $name, value => defined $value ? $value : '') and next |
|
35 | 31 |
unless $i; |
36 | 32 |
|
37 | 33 |
# Attributes (Netscape and RFC 6265) |
38 |
- next |
|
39 |
- unless my @match |
|
34 |
+ my @match |
|
40 | 35 |
= $name =~ /^(expires|domain|path|secure|Max-Age|HttpOnly)$/msi; |
36 |
+ next unless @match; |
|
41 | 37 |
my $attr = lc $match[0]; |
42 | 38 |
$attr =~ tr/-/_/; |
43 | 39 |
$cookies[-1]->$attr($attr =~ /(?:secure|HttpOnly)/i ? 1 : $value); |
... | ... |
@@ -94,12 +90,13 @@ Mojo::Cookie::Response - HTTP response cookie |
94 | 90 |
|
95 | 91 |
=head1 DESCRIPTION |
96 | 92 |
|
97 |
-L<Mojo::Cookie::Response> is a container for HTTP response cookies. |
|
93 |
+L<Mojo::Cookie::Response> is a container for HTTP response cookies as |
|
94 |
+described in RFC 6265. |
|
98 | 95 |
|
99 | 96 |
=head1 ATTRIBUTES |
100 | 97 |
|
101 | 98 |
L<Mojo::Cookie::Response> inherits all attributes from L<Mojo::Cookie> and |
102 |
-implements the followign new ones. |
|
99 |
+implements the following new ones. |
|
103 | 100 |
|
104 | 101 |
=head2 domain |
105 | 102 |
|
... | ... |
@@ -153,13 +150,13 @@ Expiration for cookie. |
153 | 150 |
|
154 | 151 |
=head2 parse |
155 | 152 |
|
156 |
- my $cookies = $cookie->parse('f=b; path=/'); |
|
153 |
+ my $cookies = Mojo::Cookie::Response->parse('f=b; path=/'); |
|
157 | 154 |
|
158 | 155 |
Parse cookies. |
159 | 156 |
|
160 | 157 |
=head2 to_string |
161 | 158 |
|
162 |
- my $string = $cookie->to_string; |
|
159 |
+ my $str = $cookie->to_string; |
|
163 | 160 |
|
164 | 161 |
Render cookie. |
165 | 162 |
|
... | ... |
@@ -18,12 +18,11 @@ use Scalar::Util qw(blessed weaken); |
18 | 18 |
sub AUTOLOAD { |
19 | 19 |
my $self = shift; |
20 | 20 |
|
21 |
- # Method |
|
22 | 21 |
my ($package, $method) = our $AUTOLOAD =~ /^([\w:]+)::(\w+)$/; |
23 | 22 |
croak "Undefined subroutine &${package}::$method called" |
24 | 23 |
unless blessed $self && $self->isa(__PACKAGE__); |
25 | 24 |
|
26 |
- # Search children |
|
25 |
+ # Search children of current element |
|
27 | 26 |
my $children = $self->children($method); |
28 | 27 |
return @$children > 1 ? $children : $children->[0] if @$children; |
29 | 28 |
croak qq{Can't locate object method "$method" via package "$package"}; |
... | ... |
@@ -38,9 +37,8 @@ sub new { |
38 | 37 |
} |
39 | 38 |
|
40 | 39 |
sub all_text { |
41 |
- my ($self, $trim) = @_; |
|
42 |
- my $tree = $self->tree; |
|
43 |
- return _text(_elements($tree), 1, _trim($tree, $trim)); |
|
40 |
+ my $tree = shift->tree; |
|
41 |
+ return _text(_elements($tree), 1, _trim($tree, @_)); |
|
44 | 42 |
} |
45 | 43 |
|
46 | 44 |
sub append { shift->_add(1, @_) } |
... | ... |
@@ -71,21 +69,17 @@ sub attrs { |
71 | 69 |
return $self; |
72 | 70 |
} |
73 | 71 |
|
74 |
-sub charset { shift->_html(charset => @_) } |
|
75 |
- |
|
76 | 72 |
sub children { |
77 | 73 |
my ($self, $type) = @_; |
78 | 74 |
|
79 | 75 |
my @children; |
80 |
- my $charset = $self->charset; |
|
81 |
- my $xml = $self->xml; |
|
82 |
- my $tree = $self->tree; |
|
76 |
+ my $xml = $self->xml; |
|
77 |
+ my $tree = $self->tree; |
|
83 | 78 |
for my $e (@$tree[($tree->[0] eq 'root' ? 1 : 4) .. $#$tree]) { |
84 | 79 |
|
85 | 80 |
# Make sure child is the right type |
86 |
- next unless $e->[0] eq 'tag'; |
|
87 |
- next if defined $type && $e->[1] ne $type; |
|
88 |
- push @children, $self->new->charset($charset)->tree($e)->xml($xml); |
|
81 |
+ next if $e->[0] ne 'tag' || (defined $type && $e->[1] ne $type); |
|
82 |
+ push @children, $self->new->tree($e)->xml($xml); |
|
89 | 83 |
} |
90 | 84 |
|
91 | 85 |
return Mojo::Collection->new(@children); |
... | ... |
@@ -94,23 +88,20 @@ sub children { |
94 | 88 |
sub content_xml { |
95 | 89 |
my $self = shift; |
96 | 90 |
|
97 |
- # Render children |
|
98 |
- my $tree = $self->tree; |
|
99 |
- my $charset = $self->charset; |
|
100 |
- my $xml = $self->xml; |
|
101 |
- return join '', map { |
|
102 |
- Mojo::DOM::HTML->new(charset => $charset, tree => $_, xml => $xml)->render |
|
103 |
- } @$tree[($tree->[0] eq 'root' ? 1 : 4) .. $#$tree]; |
|
91 |
+ # Render children individually |
|
92 |
+ my $tree = $self->tree; |
|
93 |
+ 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]; |
|
104 | 97 |
} |
105 | 98 |
|
106 | 99 |
sub find { |
107 | 100 |
my ($self, $selector) = @_; |
108 |
- |
|
109 |
- my $charset = $self->charset; |
|
110 |
- my $xml = $self->xml; |
|
111 |
- return Mojo::Collection->new( |
|
112 |
- map { $self->new->charset($charset)->tree($_)->xml($xml) } |
|
113 |
- @{Mojo::DOM::CSS->new(tree => $self->tree)->select($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); |
|
114 | 105 |
} |
115 | 106 |
|
116 | 107 |
sub namespace { |
... | ... |
@@ -119,8 +110,7 @@ sub namespace { |
119 | 110 |
# Extract namespace prefix and search parents |
120 | 111 |
return '' if (my $current = $self->tree)->[0] eq 'root'; |
121 | 112 |
my $ns = $current->[1] =~ /^(.*?):/ ? "xmlns:$1" : undef; |
122 |
- while ($current) { |
|
123 |
- last if $current->[0] eq 'root'; |
|
113 |
+ while ($current->[0] ne 'root') { |
|
124 | 114 |
|
125 | 115 |
# Namespace for prefix |
126 | 116 |
my $attrs = $current->[2]; |
... | ... |
@@ -141,15 +131,10 @@ sub next { shift->_sibling(1) } |
141 | 131 |
sub parent { |
142 | 132 |
my $self = shift; |
143 | 133 |
return undef if (my $tree = $self->tree)->[0] eq 'root'; |
144 |
- return $self->new->charset($self->charset)->tree($tree->[3]) |
|
145 |
- ->xml($self->xml); |
|
134 |
+ return $self->new->tree($tree->[3])->xml($self->xml); |
|
146 | 135 |
} |
147 | 136 |
|
148 |
-sub parse { |
|
149 |
- my $self = shift; |
|
150 |
- $self->[0]->parse(@_); |
|
151 |
- return $self; |
|
152 |
-} |
|
137 |
+sub parse { shift->_html(parse => shift) } |
|
153 | 138 |
|
154 | 139 |
sub prepend { shift->_add(0, @_) } |
155 | 140 |
|
... | ... |
@@ -168,12 +153,10 @@ sub remove { shift->replace('') } |
168 | 153 |
sub replace { |
169 | 154 |
my ($self, $new) = @_; |
170 | 155 |
|
171 |
- # Parse |
|
172 | 156 |
my $tree = $self->tree; |
173 | 157 |
if ($tree->[0] eq 'root') { return $self->xml(undef)->parse($new) } |
174 | 158 |
else { $new = $self->_parse("$new") } |
175 | 159 |
|
176 |
- # Find and replace |
|
177 | 160 |
my $parent = $tree->[3]; |
178 | 161 |
my $i = $parent->[0] eq 'root' ? 1 : 4; |
179 | 162 |
for my $e (@$parent[$i .. $#$parent]) { |
... | ... |
@@ -202,13 +185,12 @@ sub root { |
202 | 185 |
$root = $parent; |
203 | 186 |
} |
204 | 187 |
|
205 |
- return $self->new->charset($self->charset)->tree($root)->xml($self->xml); |
|
188 |
+ return $self->new->tree($root)->xml($self->xml); |
|
206 | 189 |
} |
207 | 190 |
|
208 | 191 |
sub text { |
209 |
- my ($self, $trim) = @_; |
|
210 |
- my $tree = $self->tree; |
|
211 |
- return _text(_elements($tree), 0, _trim($tree, $trim)); |
|
192 |
+ my $tree = shift->tree; |
|
193 |
+ return _text(_elements($tree), 0, _trim($tree, @_)); |
|
212 | 194 |
} |
213 | 195 |
|
214 | 196 |
sub text_after { |
... | ... |
@@ -309,11 +291,7 @@ sub _parent { |
309 | 291 |
return \@new; |
310 | 292 |
} |
311 | 293 |
|
312 |
-sub _parse { |
|
313 |
- my $self = shift; |
|
314 |
- Mojo::DOM::HTML->new(charset => $self->charset, xml => $self->xml) |
|
315 |
- ->parse(shift)->tree; |
|
316 |
-} |
|
294 |
+sub _parse { Mojo::DOM::HTML->new(xml => shift->xml)->parse(shift)->tree } |
|
317 | 295 |
|
318 | 296 |
sub _sibling { |
319 | 297 |
my ($self, $next) = @_; |
... | ... |
@@ -410,7 +388,7 @@ Mojo::DOM - Minimalistic HTML/XML DOM parser with CSS selectors |
410 | 388 |
$dom->div->p->[1]->append('<p id="c">C</p>'); |
411 | 389 |
|
412 | 390 |
# Render |
413 |
- say $dom; |
|
391 |
+ say "$dom"; |
|
414 | 392 |
|
415 | 393 |
=head1 DESCRIPTION |
416 | 394 |
|
... | ... |
@@ -452,7 +430,8 @@ following new ones. |
452 | 430 |
my $dom = Mojo::DOM->new; |
453 | 431 |
my $dom = Mojo::DOM->new('<foo bar="baz">test</foo>'); |
454 | 432 |
|
455 |
-Construct a new array-based L<Mojo::DOM> object. |
|
433 |
+Construct a new array-based L<Mojo::DOM> object and C<parse> HTML/XML document |
|
434 |
+if necessary. |
|
456 | 435 |
|
457 | 436 |
=head2 all_text |
458 | 437 |
|
... | ... |
@@ -472,7 +451,7 @@ enabled by default. |
472 | 451 |
|
473 | 452 |
$dom = $dom->append('<p>Hi!</p>'); |
474 | 453 |
|
475 |
-Append to element. |
|
454 |
+Append HTML/XML to element. |
|
476 | 455 |
|
477 | 456 |
# "<div><h1>A</h1><h2>B</h2></div>" |
478 | 457 |
$dom->parse('<div><h1>A</h1></div>')->at('h1')->append('<h2>B</h2>')->root; |
... | ... |
@@ -481,7 +460,7 @@ Append to element. |
481 | 460 |
|
482 | 461 |
$dom = $dom->append_content('<p>Hi!</p>'); |
483 | 462 |
|
484 |
-Append to element content. |
|
463 |
+Append HTML/XML to element content. |
|
485 | 464 |
|
486 | 465 |
# "<div><h1>AB</h1></div>" |
487 | 466 |
$dom->parse('<div><h1>A</h1></div>')->at('h1')->append_content('B')->root; |
... | ... |
@@ -490,8 +469,9 @@ Append to element content. |
490 | 469 |
|
491 | 470 |
my $result = $dom->at('html title'); |
492 | 471 |
|
493 |
-Find a single element with CSS selectors. All selectors from L<Mojo::DOM::CSS> |
|
494 |
-are supported. |
|
472 |
+Find first element matching the CSS selector and return it as a L<Mojo::DOM> |
|
473 |
+object or return C<undef> if none could be found. All selectors from |
|
474 |
+L<Mojo::DOM::CSS> are supported. |
|
495 | 475 |
|
496 | 476 |
# Find first element with "svg" namespace definition |
497 | 477 |
my $namespace = $dom->at('[xmlns\:svg]')->{'xmlns:svg'}; |
... | ... |
@@ -505,20 +485,13 @@ are supported. |
505 | 485 |
|
506 | 486 |
Element attributes. |
507 | 487 |
|
508 |
-=head2 charset |
|
509 |
- |
|
510 |
- my $charset = $dom->charset; |
|
511 |
- $dom = $dom->charset('UTF-8'); |
|
512 |
- |
|
513 |
-Charset used for decoding and encoding HTML/XML. |
|
514 |
- |
|
515 | 488 |
=head2 children |
516 | 489 |
|
517 | 490 |
my $collection = $dom->children; |
518 | 491 |
my $collection = $dom->children('div'); |
519 | 492 |
|
520 |
-Return a L<Mojo::Collection> object containing the children of this element, |
|
521 |
-similar to C<find>. |
|
493 |
+Return a L<Mojo::Collection> object containing the children of this element as |
|
494 |
+L<Mojo::DOM> objects, similar to C<find>. |
|
522 | 495 |
|
523 | 496 |
# Show type of random child element |
524 | 497 |
say $dom->children->shuffle->first->type; |
... | ... |
@@ -527,8 +500,7 @@ similar to C<find>. |
527 | 500 |
|
528 | 501 |
my $xml = $dom->content_xml; |
529 | 502 |
|
530 |
-Render content of this element to XML. Note that the XML will be encoded if a |
|
531 |
-C<charset> has been defined. |
|
503 |
+Render content of this element to XML. |
|
532 | 504 |
|
533 | 505 |
# "<b>test</b>" |
534 | 506 |
$dom->parse('<div><b>test</b></div>')->div->content_xml; |
... | ... |
@@ -537,8 +509,9 @@ C<charset> has been defined. |
537 | 509 |
|
538 | 510 |
my $collection = $dom->find('html title'); |
539 | 511 |
|
540 |
-Find elements with CSS selectors and return a L<Mojo::Collection> object. All |
|
541 |
-selectors from L<Mojo::DOM::CSS> are supported. |
|
512 |
+Find all elements matching the CSS selector and return a L<Mojo::Collection> |
|
513 |
+object containing these elements as L<Mojo::DOM> objects. All selectors from |
|
514 |
+L<Mojo::DOM::CSS> are supported. |
|
542 | 515 |
|
543 | 516 |
# Find a specific element and extract information |
544 | 517 |
my $id = $dom->find('div')->[23]{id}; |
... | ... |
@@ -562,7 +535,8 @@ Find element namespace. |
562 | 535 |
|
563 | 536 |
my $sibling = $dom->next; |
564 | 537 |
|
565 |
-Next sibling of element. |
|
538 |
+Return L<Mojo::DOM> object for next sibling of element or C<undef> if there |
|
539 |
+are no more siblings. |
|
566 | 540 |
|
567 | 541 |
# "<h2>B</h2>" |
568 | 542 |
$dom->parse('<div><h1>A</h1><h2>B</h2></div>')->at('h1')->next; |
... | ... |
@@ -571,7 +545,8 @@ Next sibling of element. |
571 | 545 |
|
572 | 546 |
my $parent = $dom->parent; |
573 | 547 |
|
574 |
-Parent of element. |
|
548 |
+Return L<Mojo::DOM> object for parent of element or C<undef> if this element |
|
549 |
+has no parent. |
|
575 | 550 |
|
576 | 551 |
=head2 parse |
577 | 552 |
|
... | ... |
@@ -579,14 +554,14 @@ Parent of element. |
579 | 554 |
|
580 | 555 |
Parse HTML/XML document with L<Mojo::DOM::HTML>. |
581 | 556 |
|
582 |
- # Parse UTF-8 encoded XML |
|
583 |
- my $dom = Mojo::DOM->new->charset('UTF-8')->xml(1)->parse($xml); |
|
557 |
+ # Parse XML |
|
558 |
+ my $dom = Mojo::DOM->new->xml(1)->parse($xml); |
|
584 | 559 |
|
585 | 560 |
=head2 prepend |
586 | 561 |
|
587 | 562 |
$dom = $dom->prepend('<p>Hi!</p>'); |
588 | 563 |
|
589 |
-Prepend to element. |
|
564 |
+Prepend HTML/XML to element. |
|
590 | 565 |
|
591 | 566 |
# "<div><h1>A</h1><h2>B</h2></div>" |
592 | 567 |
$dom->parse('<div><h2>B</h2></div>')->at('h2')->prepend('<h1>A</h1>')->root; |
... | ... |
@@ -595,7 +570,7 @@ Prepend to element. |
595 | 570 |
|
596 | 571 |
$dom = $dom->prepend_content('<p>Hi!</p>'); |
597 | 572 |
|
598 |
-Prepend to element content. |
|
573 |
+Prepend HTML/XML to element content. |
|
599 | 574 |
|
600 | 575 |
# "<div><h2>AB</h2></div>" |
601 | 576 |
$dom->parse('<div><h2>B</h2></div>')->at('h2')->prepend_content('A')->root; |
... | ... |
@@ -604,7 +579,8 @@ Prepend to element content. |
604 | 579 |
|
605 | 580 |
my $sibling = $dom->previous; |
606 | 581 |
|
607 |
-Previous sibling of element. |
|
582 |
+Return L<Mojo::DOM> object for previous sibling of element or C<undef> if |
|
583 |
+there are no more siblings. |
|
608 | 584 |
|
609 | 585 |
# "<h1>A</h1>" |
610 | 586 |
$dom->parse('<div><h1>A</h1><h2>B</h2></div>')->at('h2')->previous; |
... | ... |
@@ -613,7 +589,7 @@ Previous sibling of element. |
613 | 589 |
|
614 | 590 |
my $old = $dom->remove; |
615 | 591 |
|
616 |
-Remove element. |
|
592 |
+Remove element and return it as a L<Mojo::DOM> object. |
|
617 | 593 |
|
618 | 594 |
# "<div></div>" |
619 | 595 |
$dom->parse('<div><h1>A</h1></div>')->at('h1')->remove->root; |
... | ... |
@@ -622,7 +598,8 @@ Remove element. |
622 | 598 |
|
623 | 599 |
my $old = $dom->replace('<div>test</div>'); |
624 | 600 |
|
625 |
-Replace element. |
|
601 |
+Replace element with HTML/XML and return the replaced element as a |
|
602 |
+L<Mojo::DOM> object. |
|
626 | 603 |
|
627 | 604 |
# "<div><h2>B</h2></div>" |
628 | 605 |
$dom->parse('<div><h1>A</h1></div>')->at('h1')->replace('<h2>B</h2>')->root; |
... | ... |
@@ -632,9 +609,9 @@ Replace element. |
632 | 609 |
|
633 | 610 |
=head2 replace_content |
634 | 611 |
|
635 |
- $dom = $dom->replace_content('test'); |
|
612 |
+ $dom = $dom->replace_content('<p>test</p>'); |
|
636 | 613 |
|
637 |
-Replace element content. |
|
614 |
+Replace element content with HTML/XML. |
|
638 | 615 |
|
639 | 616 |
# "<div><h1>B</h1></div>" |
640 | 617 |
$dom->parse('<div><h1>A</h1></div>')->at('h1')->replace_content('B')->root; |
... | ... |
@@ -646,7 +623,7 @@ Replace element content. |
646 | 623 |
|
647 | 624 |
my $root = $dom->root; |
648 | 625 |
|
649 |
-Find root node. |
|
626 |
+Return L<Mojo::DOM> object for root node. |
|
650 | 627 |
|
651 | 628 |
=head2 text |
652 | 629 |
|
... | ... |
@@ -695,8 +672,7 @@ is enabled by default. |
695 | 672 |
my $xml = $dom->to_xml; |
696 | 673 |
my $xml = "$dom"; |
697 | 674 |
|
698 |
-Render this element and its content to XML. Note that the XML will be encoded |
|
699 |
-if a C<charset> has been defined. |
|
675 |
+Render this element and its content to XML. |
|
700 | 676 |
|
701 | 677 |
# "<b>test</b>" |
702 | 678 |
$dom->parse('<div><b>test</b></div>')->div->b->to_xml; |
... | ... |
@@ -704,9 +680,10 @@ if a C<charset> has been defined. |
704 | 680 |
=head2 tree |
705 | 681 |
|
706 | 682 |
my $tree = $dom->tree; |
707 |
- $dom = $dom->tree(['root', [qw(text lalala)]]); |
|
683 |
+ $dom = $dom->tree(['root', ['text', 'foo']]); |
|
708 | 684 |
|
709 |
-Document Object Model. |
|
685 |
+Document Object Model. Note that this structure should only be used very |
|
686 |
+carefully since it is very dynamic. |
|
710 | 687 |
|
711 | 688 |
=head2 type |
712 | 689 |
|
... | ... |
@@ -52,7 +52,7 @@ sub select { |
52 | 52 |
|
53 | 53 |
# Try all selectors with element |
54 | 54 |
for my $part (@$pattern) { |
55 |
- push(@results, $current) and last |
|
55 |
+ push @results, $current and last |
|
56 | 56 |
if $self->_combinator([reverse @$part], $current, $tree); |
57 | 57 |
} |
58 | 58 |
} |
... | ... |
@@ -600,9 +600,10 @@ L<Mojo::DOM::CSS> implements the following attributes. |
600 | 600 |
=head2 tree |
601 | 601 |
|
602 | 602 |
my $tree = $css->tree; |
603 |
- $css = $css->tree(['root', [qw(text lalala)]]); |
|
603 |
+ $css = $css->tree(['root', ['text', 'foo']]); |
|
604 | 604 |
|
605 |
-Document Object Model. |
|
605 |
+Document Object Model. Note that this structure should only be used very |
|
606 |
+carefully since it is very dynamic. |
|
606 | 607 |
|
607 | 608 |
=head1 METHODS |
608 | 609 |
|
... | ... |
@@ -1,14 +1,13 @@ |
1 | 1 |
package Mojo::DOM::HTML; |
2 | 2 |
use Mojo::Base -base; |
3 | 3 |
|
4 |
-use Mojo::Util qw(decode encode html_unescape xml_escape); |
|
4 |
+use Mojo::Util qw(html_unescape xml_escape); |
|
5 | 5 |
use Scalar::Util 'weaken'; |
6 | 6 |
|
7 |
-has [qw(charset xml)]; |
|
7 |
+has 'xml'; |
|
8 | 8 |
has tree => sub { ['root'] }; |
9 | 9 |
|
10 | 10 |
my $ATTR_RE = qr/ |
11 |
- \s* |
|
12 | 11 |
([^=\s>]+) # Key |
13 | 12 |
(?: |
14 | 13 |
\s*=\s* |
... | ... |
@@ -42,6 +41,7 @@ my $TOKEN_RE = qr/ |
42 | 41 |
<( |
43 | 42 |
\s* |
44 | 43 |
[^>\s]+ # Tag |
44 |
+ \s* |
|
45 | 45 |
(?:$ATTR_RE)* # Attributes |
46 | 46 |
)> |
47 | 47 |
)?? |
... | ... |
@@ -76,9 +76,6 @@ my %INLINE = map { $_ => 1 } ( |
76 | 76 |
sub parse { |
77 | 77 |
my ($self, $html) = @_; |
78 | 78 |
|
79 |
- my $charset = $self->charset; |
|
80 |
- defined ($html = decode($charset, $html)) || return $self->charset(undef) if $charset; |
|
81 |
- |
|
82 | 79 |
my $tree = ['root']; |
83 | 80 |
my $current = $tree; |
84 | 81 |
while ($html =~ m/\G$TOKEN_RE/gcs) { |
... | ... |
@@ -86,22 +83,22 @@ sub parse { |
86 | 83 |
= ($1, $2, $3, $4, $5, $6); |
87 | 84 |
|
88 | 85 |
# Text |
89 |
- if (length $text) { |
|
90 |
- $text = html_unescape $text if (index $text, '&') >= 0; |
|
91 |
- $self->_text($text, \$current); |
|
92 |
- } |
|
86 |
+ if (length $text) { push @$current, ['text', html_unescape($text)] } |
|
93 | 87 |
|
94 | 88 |
# DOCTYPE |
95 |
- if ($doctype) { $self->_doctype($doctype, \$current) } |
|
89 |
+ if ($doctype) { push @$current, ['doctype', $doctype] } |
|
96 | 90 |
|
97 | 91 |
# Comment |
98 |
- elsif ($comment) { $self->_comment($comment, \$current) } |
|
92 |
+ elsif ($comment) { push @$current, ['comment', $comment] } |
|
99 | 93 |
|
100 | 94 |
# CDATA |
101 |
- elsif ($cdata) { $self->_cdata($cdata, \$current) } |
|
95 |
+ elsif ($cdata) { push @$current, ['cdata', $cdata] } |
|
102 | 96 |
|
103 |
- # Processing instruction |
|
104 |
- elsif ($pi) { $self->_pi($pi, \$current) } |
|
97 |
+ # Processing instruction (try to detect XML) |
|
98 |
+ elsif ($pi) { |
|
99 |
+ $self->xml(1) if !defined $self->xml && $pi =~ /xml/i; |
|
100 |
+ push @$current, ['pi', $pi]; |
|
101 |
+ } |
|
105 | 102 |
|
106 | 103 |
# End |
107 | 104 |
next unless $tag; |
... | ... |
@@ -121,12 +118,10 @@ sub parse { |
121 | 118 |
# Empty tag |
122 | 119 |
next if $key eq '/'; |
123 | 120 |
|
124 |
- # Add unescaped value |
|
125 |
- $value = html_unescape $value if $value && (index $value, '&') >= 0; |
|
126 |
- $attrs{$key} = $value; |
|
121 |
+ $attrs{$key} = defined $value ? html_unescape($value) : $value; |
|
127 | 122 |
} |
128 | 123 |
|
129 |
- # Start |
|
124 |
+ # Tag |
|
130 | 125 |
$self->_start($start, \%attrs, \$current); |
131 | 126 |
|
132 | 127 |
# Empty element |
... | ... |
@@ -134,9 +129,9 @@ sub parse { |
134 | 129 |
if (!$self->xml && $VOID{$start}) || $attr =~ m!/\s*$!; |
135 | 130 |
|
136 | 131 |
# Relaxed "script" or "style" |
137 |
- if (grep { $_ eq $start } qw(script style)) { |
|
132 |
+ if ($start eq 'script' || $start eq 'style') { |
|
138 | 133 |
if ($html =~ m!\G(.*?)<\s*/\s*$start\s*>!gcsi) { |
139 |
- $self->_raw($1, \$current); |
|
134 |
+ push @$current, ['raw', $1]; |
|
140 | 135 |
$self->_end($start, \$current); |
141 | 136 |
} |
142 | 137 |
} |
... | ... |
@@ -146,17 +141,7 @@ sub parse { |
146 | 141 |
return $self->tree($tree); |
147 | 142 |
} |
148 | 143 |
|
149 |
-sub render { |
|
150 |
- my $self = shift; |
|
151 |
- my $content = $self->_render($self->tree); |
|
152 |
- my $charset = $self->charset; |
|
153 |
- return $charset ? encode($charset, $content) : $content; |
|
154 |
-} |
|
155 |
- |
|
156 |
-sub _cdata { |
|
157 |
- my ($self, $cdata, $current) = @_; |
|
158 |
- push @$$current, ['cdata', $cdata]; |
|
159 |
-} |
|
144 |
+sub render { $_[0]->_render($_[0]->tree) } |
|
160 | 145 |
|
161 | 146 |
sub _close { |
162 | 147 |
my ($self, $current, $tags, $stop) = @_; |
... | ... |
@@ -165,8 +150,7 @@ sub _close { |
165 | 150 |
|
166 | 151 |
# Check if parents need to be closed |
167 | 152 |
my $parent = $$current; |
168 |
- while ($parent) { |
|
169 |
- last if $parent->[0] eq 'root' || $parent->[1] eq $stop; |
|
153 |
+ while ($parent->[0] ne 'root' && $parent->[1] ne $stop) { |
|
170 | 154 |
|
171 | 155 |
# Close |
172 | 156 |
$tags->{$parent->[1]} and $self->_end($parent->[1], $current); |
... | ... |
@@ -176,27 +160,13 @@ sub _close { |
176 | 160 |
} |
177 | 161 |
} |
178 | 162 |
|
179 |
-sub _comment { |
|
180 |
- my ($self, $comment, $current) = @_; |
|
181 |
- push @$$current, ['comment', $comment]; |
|
182 |
-} |
|
183 |
- |
|
184 |
-sub _doctype { |
|
185 |
- my ($self, $doctype, $current) = @_; |
|
186 |
- push @$$current, ['doctype', $doctype]; |
|
187 |
-} |
|
188 |
- |
|
189 | 163 |
sub _end { |
190 | 164 |
my ($self, $end, $current) = @_; |
191 | 165 |
|
192 |
- # Not a tag |
|
193 |
- return if $$current->[0] eq 'root'; |
|
194 |
- |
|
195 | 166 |
# Search stack for start tag |
196 | 167 |
my $found = 0; |
197 | 168 |
my $next = $$current; |
198 |
- while ($next) { |
|
199 |
- last if $next->[0] eq 'root'; |
|
169 |
+ while ($next->[0] ne 'root') { |
|
200 | 170 |
|
201 | 171 |
# Right tag |
202 | 172 |
++$found and last if $next->[1] eq $end; |
... | ... |
@@ -213,17 +183,14 @@ sub _end { |
213 | 183 |
|
214 | 184 |
# Walk backwards |
215 | 185 |
$next = $$current; |
216 |
- while ($$current = $next) { |
|
217 |
- last if $$current->[0] eq 'root'; |
|
186 |
+ while (($$current = $next) && $$current->[0] ne 'root') { |
|
218 | 187 |
$next = $$current->[3]; |
219 | 188 |
|
220 | 189 |
# Match |
221 | 190 |
if ($end eq $$current->[1]) { return $$current = $$current->[3] } |
222 | 191 |
|
223 | 192 |
# Optional elements |
224 |
- elsif ($OPTIONAL{$$current->[1]}) { |
|
225 |
- $self->_end($$current->[1], $current); |
|
226 |
- } |
|
193 |
+ elsif ($OPTIONAL{$$current->[1]}) { $self->_end($$current->[1], $current) } |
|
227 | 194 |
|
228 | 195 |
# Table |
229 | 196 |
elsif ($end eq 'table') { $self->_close($current) } |
... | ... |
@@ -233,18 +200,6 @@ sub _end { |
233 | 200 |
} |
234 | 201 |
} |
235 | 202 |
|
236 |
-# Try to detect XML from processing instructions |
|
237 |
-sub _pi { |
|
238 |
- my ($self, $pi, $current) = @_; |
|
239 |
- $self->xml(1) if !defined $self->xml && $pi =~ /xml/i; |
|
240 |
- push @$$current, ['pi', $pi]; |
|
241 |
-} |
|
242 |
- |
|
243 |
-sub _raw { |
|
244 |
- my ($self, $raw, $current) = @_; |
|
245 |
- push @$$current, ['raw', $raw]; |
|
246 |
-} |
|
247 |
- |
|
248 | 203 |
sub _render { |
249 | 204 |
my ($self, $tree) = @_; |
250 | 205 |
|
... | ... |
@@ -338,21 +293,18 @@ sub _start { |
338 | 293 |
elsif ($start eq 'tr') { $self->_close($current, {tr => 1}) } |
339 | 294 |
|
340 | 295 |
# "<th>" and "<td>" |
341 |
- elsif (grep { $_ eq $start } qw(th td)) { |
|
342 |
- $self->_close($current, {th => 1}); |
|
343 |
- $self->_close($current, {td => 1}); |
|
296 |
+ elsif ($start eq 'th' || $start eq 'td') { |
|
297 |
+ $self->_close($current, {$_ => 1}) for qw(th td); |
|
344 | 298 |
} |
345 | 299 |
|
346 | 300 |
# "<dt>" and "<dd>" |
347 |
- elsif (grep { $_ eq $start } qw(dt dd)) { |
|
348 |
- $self->_end('dt', $current); |
|
349 |
- $self->_end('dd', $current); |
|
301 |
+ elsif ($start eq 'dt' || $start eq 'dd') { |
|
302 |
+ $self->_end($_, $current) for qw(dt dd); |
|
350 | 303 |
} |
351 | 304 |
|
352 | 305 |
# "<rt>" and "<rp>" |
353 |
- elsif (grep { $_ eq $start } qw(rt rp)) { |
|
354 |
- $self->_end('rt', $current); |
|
355 |
- $self->_end('rp', $current); |
|
306 |
+ elsif ($start eq 'rt' || $start eq 'rp') { |
|
307 |
+ $self->_end($_, $current) for qw(rt rp); |
|
356 | 308 |
} |
357 | 309 |
} |
358 | 310 |
|
... | ... |
@@ -363,11 +315,6 @@ sub _start { |
363 | 315 |
$$current = $new; |
364 | 316 |
} |
365 | 317 |
|
366 |
-sub _text { |
|
367 |
- my ($self, $text, $current) = @_; |
|
368 |
- push @$$current, ['text', $text]; |
|
369 |
-} |
|
370 |
- |
|
371 | 318 |
1; |
372 | 319 |
|
373 | 320 |
=head1 NAME |
... | ... |
@@ -391,19 +338,13 @@ L<Mojo::DOM::HTML> is the HTML/XML engine used by L<Mojo::DOM>. |
391 | 338 |
|
392 | 339 |
L<Mojo::DOM::HTML> implements the following attributes. |
393 | 340 |
|
394 |
-=head2 charset |
|
395 |
- |
|
396 |
- my $charset = $html->charset; |
|
397 |
- $html = $html->charset('UTF-8'); |
|
398 |
- |
|
399 |
-Charset used for decoding and encoding HTML/XML. |
|
400 |
- |
|
401 | 341 |
=head2 tree |
402 | 342 |
|
403 | 343 |
my $tree = $html->tree; |
404 |
- $html = $html->tree(['root', [qw(text lalala)]]); |
|
344 |
+ $html = $html->tree(['root', ['text', 'foo']]); |
|
405 | 345 |
|
406 |
-Document Object Model. |
|
346 |
+Document Object Model. Note that this structure should only be used very |
|
347 |
+carefully since it is very dynamic. |
|
407 | 348 |
|
408 | 349 |
=head2 xml |
409 | 350 |
|
... | ... |
@@ -57,7 +57,7 @@ sub to_string { |
57 | 57 |
|
58 | 58 |
# RFC 2616 (Sun, 06 Nov 1994 08:49:37 GMT) |
59 | 59 |
my ($s, $m, $h, $mday, $month, $year, $wday) = gmtime(defined $self->epoch ? $self->epoch : time); |
60 |
- return sprintf "%s, %02d %s %04d %02d:%02d:%02d GMT", $DAYS[$wday], $mday, |
|
60 |
+ return sprintf '%s, %02d %s %04d %02d:%02d:%02d GMT', $DAYS[$wday], $mday, |
|
61 | 61 |
$MONTHS[$month], $year + 1900, $h, $m, $s; |
62 | 62 |
} |
63 | 63 |
|
... | ... |
@@ -109,7 +109,7 @@ following new ones. |
109 | 109 |
my $date = Mojo::Date->new; |
110 | 110 |
my $date = Mojo::Date->new('Sun Nov 6 08:49:37 1994'); |
111 | 111 |
|
112 |
-Construct a new L<Mojo::Date> object. |
|
112 |
+Construct a new L<Mojo::Date> object and C<parse> date if necessary. |
|
113 | 113 |
|
114 | 114 |
=head2 parse |
115 | 115 |
|
... | ... |
@@ -131,8 +131,8 @@ Parse date. |
131 | 131 |
|
132 | 132 |
=head2 to_string |
133 | 133 |
|
134 |
- my $string = $date->to_string; |
|
135 |
- my $string = "$date"; |
|
134 |
+ my $str = $date->to_string; |
|
135 |
+ my $str = "$date"; |
|
136 | 136 |
|
137 | 137 |
Render date suitable for HTTP messages. |
138 | 138 |
|
... | ... |
@@ -198,7 +198,7 @@ Unsubscribe from event. |
198 | 198 |
|
199 | 199 |
=head1 DEBUGGING |
200 | 200 |
|
201 |
-You can set the C<MOJO_EVENTEMITTER_DEBUG> environment variable to get some |
|
201 |
+You can set the MOJO_EVENTEMITTER_DEBUG environment variable to get some |
|
202 | 202 |
advanced diagnostics information printed to C<STDERR>. |
203 | 203 |
|
204 | 204 |
MOJO_EVENTEMITTER_DEBUG=1 |
... | ... |
@@ -9,7 +9,7 @@ use Scalar::Util 'blessed'; |
9 | 9 |
|
10 | 10 |
has [qw(frames line lines_before lines_after)] => sub { [] }; |
11 | 11 |
has message => 'Exception!'; |
12 |
-has verbose => sub { $ENV{MOJO_EXCEPTION_VERBOSE} || 0 }; |
|
12 |
+has 'verbose'; |
|
13 | 13 |
|
14 | 14 |
sub new { |
15 | 15 |
my $self = shift->SUPER::new; |
... | ... |
@@ -22,19 +22,19 @@ sub to_string { |
22 | 22 |
my $self = shift; |
23 | 23 |
|
24 | 24 |
return $self->message unless $self->verbose; |
25 |
- my $string = $self->message ? $self->message : ''; |
|
25 |
+ my $str = $self->message ? $self->message : ''; |
|
26 | 26 |
|
27 | 27 |
# Before |
28 |
- $string .= $_->[0] . ': ' . $_->[1] . "\n" for @{$self->lines_before}; |
|
28 |
+ $str .= $_->[0] . ': ' . $_->[1] . "\n" for @{$self->lines_before}; |
|
29 | 29 |
|
30 | 30 |
# Line |
31 |
- $string .= ($self->line->[0] . ': ' . $self->line->[1] . "\n") |
|
31 |
+ $str .= ($self->line->[0] . ': ' . $self->line->[1] . "\n") |
|
32 | 32 |
if $self->line->[0]; |
33 | 33 |
|
34 | 34 |
# After |
35 |
- $string .= $_->[0] . ': ' . $_->[1] . "\n" for @{$self->lines_after}; |
|
35 |
+ $str .= $_->[0] . ': ' . $_->[1] . "\n" for @{$self->lines_after}; |
|
36 | 36 |
|
37 |
- return $string; |
|
37 |
+ return $str; |
|
38 | 38 |
} |
39 | 39 |
|
40 | 40 |
sub trace { |
... | ... |
@@ -100,8 +100,7 @@ sub _detect { |
100 | 100 |
|
101 | 101 |
# Search for context in files |
102 | 102 |
for my $frame (@trace) { |
103 |
- next unless -r $frame->[0]; |
|
104 |
- open my $handle, '<:utf8', $frame->[0]; |
|
103 |
+ next unless -r $frame->[0] && open my $handle, '<:utf8', $frame->[0]; |
|
105 | 104 |
$self->_context($frame->[1], [[<$handle>]]); |
106 | 105 |
return $self; |
107 | 106 |
} |
... | ... |
@@ -148,21 +147,21 @@ Stacktrace. |
148 | 147 |
my $line = $e->line; |
149 | 148 |
$e = $e->line([3 => 'foo']); |
150 | 149 |
|
151 |
-The line where the exception occured. |
|
150 |
+The line where the exception occurred. |
|
152 | 151 |
|
153 | 152 |
=head2 lines_after |
154 | 153 |
|
155 | 154 |
my $lines = $e->lines_after; |
156 | 155 |
$e = $e->lines_after([[1 => 'bar'], [2 => 'baz']]); |
157 | 156 |
|
158 |
-Lines after the line where the exception occured. |
|
157 |
+Lines after the line where the exception occurred. |
|
159 | 158 |
|
160 | 159 |
=head2 lines_before |
161 | 160 |
|
162 | 161 |
my $lines = $e->lines_before; |
163 | 162 |
$e = $e->lines_before([[4 => 'bar'], [5 => 'baz']]); |
164 | 163 |
|
165 |
-Lines before the line where the exception occured. |
|
164 |
+Lines before the line where the exception occurred. |
|
166 | 165 |
|
167 | 166 |
=head2 message |
168 | 167 |
|
... | ... |
@@ -176,8 +175,7 @@ Exception message. |
176 | 175 |
my $verbose = $e->verbose; |
177 | 176 |
$e = $e->verbose(1); |
178 | 177 |
|
179 |
-Activate verbose rendering, defaults to the value of the |
|
180 |
-C<MOJO_EXCEPTION_VERBOSE> environment variable or C<0>. |
|
178 |
+Render exception with context. |
|
181 | 179 |
|
182 | 180 |
=head1 METHODS |
183 | 181 |
|
... | ... |
@@ -200,8 +198,8 @@ Throw exception with stacktrace. |
200 | 198 |
|
201 | 199 |
=head2 to_string |
202 | 200 |
|
203 |
- my $string = $e->to_string; |
|
204 |
- my $string = "$e"; |
|
201 |
+ my $str = $e->to_string; |
|
202 |
+ my $str = "$e"; |
|
205 | 203 |
|
206 | 204 |
Render exception. |
207 | 205 |
|
... | ... |
@@ -29,21 +29,18 @@ sub add { |
29 | 29 |
my ($self, $name) = (shift, shift); |
30 | 30 |
|
31 | 31 |
# Make sure we have a normal case entry for name |
32 |
- my $lcname = lc $name; |
|
33 |
- $self->{normalcase}{$lcname} = $self->{normalcase}{$lcname} ? $self->{normalcase}{$lcname} : $name unless $NORMALCASE{$lcname}; |
|
32 |
+ my $key = lc $name; |
|
33 |
+ $self->{normalcase}{$key} = defined $self->{normalcase}{$key} ? $self->{normalcase}{$key} : $name unless $NORMALCASE{$key}; |
|
34 | 34 |
|
35 | 35 |
# Add lines |
36 |
- push @{$self->{headers}{$lcname}}, map { ref $_ eq 'ARRAY' ? $_ : [$_] } @_; |
|
36 |
+ push @{$self->{headers}{$key}}, map { ref $_ eq 'ARRAY' ? $_ : [$_] } @_; |
|
37 | 37 |
|
38 | 38 |
return $self; |
39 | 39 |
} |
40 | 40 |
|
41 | 41 |
sub clone { |
42 |
- my $self = shift; |
|
43 |
- my $clone = $self->new; |
|
44 |
- $clone->{headers}{$_} = [@{$self->{headers}{$_}}] |
|
45 |
- for keys %{$self->{headers}}; |
|
46 |
- return $clone; |
|
42 |
+ my $self = shift; |
|
43 |
+ return $self->new->from_hash($self->to_hash(1)); |
|
47 | 44 |
} |
48 | 45 |
|
49 | 46 |
sub from_hash { |
... | ... |
@@ -102,10 +99,10 @@ sub parse { |
102 | 99 |
} |
103 | 100 |
|
104 | 101 |
# New header |
105 |
- if ($line =~ /^(\S+)\s*:\s*(.*)$/) { push @$headers, $1, $2 } |
|
102 |
+ if ($line =~ /^(\S+)\s*:\s*(.*)$/) { push @$headers, $1, [$2] } |
|
106 | 103 |
|
107 | 104 |
# Multiline |
108 |
- elsif (@$headers && $line =~ s/^\s+//) { $headers->[-1] .= " $line" } |
|
105 |
+ elsif (@$headers && $line =~ s/^\s+//) { push @{$headers->[-1]}, $line } |
|
109 | 106 |
|
110 | 107 |
# Empty line |
111 | 108 |
else { |
... | ... |
@@ -132,30 +129,16 @@ sub remove { |
132 | 129 |
|
133 | 130 |
sub to_hash { |
134 | 131 |
my ($self, $multi) = @_; |
135 |
- |
|
136 | 132 |
my %hash; |
137 |
- for my $header (@{$self->names}) { |
|
138 |
- my @headers = $self->header($header); |
|
139 |
- |
|
140 |
- # Multi line |
|
141 |
- if ($multi) { $hash{$header} = [@headers] } |
|
142 |
- |
|
143 |
- # Flat |
|
144 |
- else { |
|
145 |
- |
|
146 |
- # Turn single value arrays into strings |
|
147 |
- @$_ == 1 and $_ = $_->[0] for @headers; |
|
148 |
- $hash{$header} = @headers > 1 ? [@headers] : $headers[0]; |
|
149 |
- } |
|
150 |
- } |
|
151 |
- |
|
133 |
+ $hash{$_} = $multi ? [$self->header($_)] : scalar $self->header($_) |
|
134 |
+ for @{$self->names}; |
|
152 | 135 |
return \%hash; |
153 | 136 |
} |
154 | 137 |
|
155 | 138 |
sub to_string { |
156 | 139 |
my $self = shift; |
157 | 140 |
|
158 |
- # Format multiline values |
|
141 |
+ # Make sure multiline values are formatted correctly |
|
159 | 142 |
my @headers; |
160 | 143 |
for my $name (@{$self->names}) { |
161 | 144 |
push @headers, "$name: " . join("\x0d\x0a ", @$_) for $self->header($name); |
... | ... |
@@ -201,7 +184,7 @@ L<Mojo::Headers> implements the following attributes. |
201 | 184 |
$headers = $headers->max_line_size(1024); |
202 | 185 |
|
203 | 186 |
Maximum header line size in bytes, defaults to the value of the |
204 |
-C<MOJO_MAX_LINE_SIZE> environment variable or C<10240>. |
|
187 |
+MOJO_MAX_LINE_SIZE environment variable or C<10240>. |
|
205 | 188 |
|
206 | 189 |
=head1 METHODS |
207 | 190 |
|
... | ... |
@@ -245,9 +228,11 @@ Shortcut for the C<Accept-Ranges> header. |
245 | 228 |
|
246 | 229 |
=head2 add |
247 | 230 |
|
248 |
- $headers = $headers->add('Content-Type', 'text/plain'); |
|
231 |
+ $headers = $headers->add(Foo => 'one value'); |
|
232 |
+ $headers = $headers->add(Foo => 'first value', 'second value'); |
|
233 |
+ $headers = $headers->add(Foo => ['first line', 'second line']); |
|
249 | 234 |
|
250 |
-Add one or more header lines. |
|
235 |
+Add one or more header values with one or more lines. |
|
251 | 236 |
|
252 | 237 |
=head2 authorization |
253 | 238 |
|
... | ... |
@@ -359,13 +344,15 @@ Shortcut for the C<Expires> header. |
359 | 344 |
$headers = $headers->from_hash({'Content-Type' => 'text/html'}); |
360 | 345 |
$headers = $headers->from_hash({}); |
361 | 346 |
|
362 |
-Parse headers from a hash reference. |
|
347 |
+Parse headers from a hash reference, an empty hash removes all headers. |
|
363 | 348 |
|
364 | 349 |
=head2 header |
365 | 350 |
|
366 |
- my $string = $headers->header('Content-Type'); |
|
367 |
- my @lines = $headers->header('Content-Type'); |
|
368 |
- $headers = $headers->header('Content-Type' => 'text/plain'); |
|
351 |
+ my $value = $headers->header('Foo'); |
|
352 |
+ my @values = $headers->header('Foo'); |
|
353 |
+ $headers = $headers->header(Foo => 'one value'); |
|
354 |
+ $headers = $headers->header(Foo => 'first value', 'second value'); |
|
355 |
+ $headers = $headers->header(Foo => ['first line', 'second line']); |
|
369 | 356 |
|
370 | 357 |
Get or replace the current header values. |
371 | 358 |
|
... | ... |
@@ -466,14 +453,14 @@ Shortcut for the C<Range> header. |
466 | 453 |
=head2 referrer |
467 | 454 |
|
468 | 455 |
my $referrer = $headers->referrer; |
469 |
- $headers = $headers->referrer('http://mojolicio.us'); |
|
456 |
+ $headers = $headers->referrer('http://example.com'); |
|
470 | 457 |
|
471 | 458 |
Shortcut for the C<Referer> header, there was a typo in RFC 2068 which |
472 | 459 |
resulted in C<Referer> becoming an official header. |
473 | 460 |
|
474 | 461 |
=head2 remove |
475 | 462 |
|
476 |
- $headers = $headers->remove('Content-Type'); |
|
463 |
+ $headers = $headers->remove('Foo'); |
|
477 | 464 |
|
478 | 465 |
Remove a header. |
479 | 466 |
|
... | ... |
@@ -500,8 +487,8 @@ Shortcut for the C<Sec-WebSocket-Key> header from RFC 6455. |
500 | 487 |
|
501 | 488 |
=head2 sec_websocket_protocol |
502 | 489 |
|
503 |
- my $protocol = $headers->sec_websocket_protocol; |
|
504 |
- $headers = $headers->sec_websocket_protocol('sample'); |
|
490 |
+ my $proto = $headers->sec_websocket_protocol; |
|
491 |
+ $headers = $headers->sec_websocket_protocol('sample'); |
|
505 | 492 |
|
506 | 493 |
Shortcut for the C<Sec-WebSocket-Protocol> header from RFC 6455. |
507 | 494 |
|
... | ... |
@@ -545,14 +532,14 @@ Shortcut for the C<TE> header. |
545 | 532 |
my $single = $headers->to_hash; |
546 | 533 |
my $multi = $headers->to_hash(1); |
547 | 534 |
|
548 |
-Turn headers into hash reference, nested array references to represent multi |
|
549 |
-line values are disabled by default. |
|
535 |
+Turn headers into hash reference, nested array references to represent |
|
536 |
+multiline values are disabled by default. |
|
550 | 537 |
|
551 | 538 |
say $headers->to_hash->{DNT}; |
552 | 539 |
|
553 | 540 |
=head2 to_string |
554 | 541 |
|
555 |
- my $string = $headers->to_string; |
|
542 |
+ my $str = $headers->to_string; |
|
556 | 543 |
|
557 | 544 |
Turn headers into a string, suitable for HTTP messages. |
558 | 545 |
|
... | ... |
@@ -80,14 +80,6 @@ sub parse { |
80 | 80 |
sub rel_dir { catdir(@{shift->{parts} || []}, split '/', shift) } |
81 | 81 |
sub rel_file { catfile(@{shift->{parts} || []}, split '/', shift) } |
82 | 82 |
|
83 |
-# DEPRECATED in Rainbow! |
|
84 |
-sub slurp_rel_file { |
|
85 |
- warn <<EOF; |
|
86 |
-Mojo::Home->slurp_rel_file is DEPRECATED in favor of Mojo::Util->slurp!!! |
|
87 |
-EOF |
|
88 |
- slurp shift->rel_file(@_); |
|
89 |
-} |
|
90 |
- |
|
91 | 83 |
sub to_string { catdir(@{shift->{parts} || []}) } |
92 | 84 |
|
93 | 85 |
1; |
... | ... |
@@ -121,15 +113,15 @@ following new ones. |
121 | 113 |
my $home = Mojo::Home->new; |
122 | 114 |
my $home = Mojo::Home->new('/home/sri/myapp'); |
123 | 115 |
|
124 |
-Construct a new L<Mojo::Home> object. |
|
116 |
+Construct a new L<Mojo::Home> object and C<parse> home directory if necessary. |
|
125 | 117 |
|
126 | 118 |
=head2 detect |
127 | 119 |
|
128 | 120 |
$home = $home->detect; |
129 | 121 |
$home = $home->detect('My::App'); |
130 | 122 |
|
131 |
-Detect home directory from the value of the C<MOJO_HOME> environment variable |
|
132 |
-or application class. |
|
123 |
+Detect home directory from the value of the MOJO_HOME environment variable or |
|
124 |
+application class. |
|
133 | 125 |
|
134 | 126 |
=head2 lib_dir |
135 | 127 |
|
... | ... |
@@ -143,9 +135,9 @@ Path to C<lib> directory of application. |
143 | 135 |
my $files = $home->list_files('foo/bar'); |
144 | 136 |
|
145 | 137 |
Portably list all files recursively in directory relative to the home |
146 |
-diectory. |
|
138 |
+directory. |
|
147 | 139 |
|
148 |
- $home->rel_file($home->list_files('templates/layouts')->[1]); |
|
140 |
+ say $home->rel_file($home->list_files('templates/layouts')->[1]); |
|
149 | 141 |
|
150 | 142 |
=head2 mojo_lib_dir |
151 | 143 |
|
... | ... |
@@ -174,8 +166,8 @@ Portably generate an absolute path for a file relative to the home directory. |
174 | 166 |
|
175 | 167 |
=head2 to_string |
176 | 168 |
|
177 |
- my $string = $home->to_string; |
|
178 |
- my $string = "$home"; |
|
169 |
+ my $str = $home->to_string; |
|
170 |
+ my $str = "$home"; |
|
179 | 171 |
|
180 | 172 |
Home directory. |
181 | 173 |
|
... | ... |
@@ -9,9 +9,8 @@ use Mojo::IOLoop::Delay; |
9 | 9 |
use Mojo::IOLoop::Server; |
10 | 10 |
use Mojo::IOLoop::Stream; |
11 | 11 |
use Mojo::Reactor::Poll; |
12 |
-use Mojo::Util 'md5_sum'; |
|
12 |
+use Mojo::Util qw(md5_sum steady_time); |
|
13 | 13 |
use Scalar::Util 'weaken'; |
14 |
-use Time::HiRes 'time'; |
|
15 | 14 |
|
16 | 15 |
use constant DEBUG => $ENV{MOJO_IOLOOP_DEBUG} || 0; |
17 | 16 |
|
... | ... |
@@ -39,9 +38,6 @@ sub acceptor { |
39 | 38 |
# Find acceptor for id |
40 | 39 |
return $self->{acceptors}{$acceptor} unless ref $acceptor; |
41 | 40 |
|
42 |
- # Make sure connection manager is running |
|
43 |
- $self->_manager; |
|
44 |
- |
|
45 | 41 |
# Connect acceptor with reactor |
46 | 42 |
my $id = $self->_id; |
47 | 43 |
$self->{acceptors}{$id} = $acceptor; |
... | ... |
@@ -58,8 +54,8 @@ sub client { |
58 | 54 |
my ($self, $cb) = (shift, pop); |
59 | 55 |
$self = $self->singleton unless ref $self; |
60 | 56 |
|
61 |
- # Make sure connection manager is running |
|
62 |
- $self->_manager; |
|
57 |
+ # Make sure timers are running |
|
58 |
+ $self->_timers; |
|
63 | 59 |
|
64 | 60 |
my $id = $self->_id; |
65 | 61 |
my $c = $self->{connections}{$id} ||= {}; |
... | ... |
@@ -117,7 +113,8 @@ sub recurring { |
117 | 113 |
sub remove { |
118 | 114 |
my ($self, $id) = @_; |
119 | 115 |
$self = $self->singleton unless ref $self; |
120 |
- if (my $c = $self->{connections}{$id}) { return $c->{finish} = 1 } |
|
116 |
+ my $c = $self->{connections}{$id}; |
|
117 |
+ if ($c && (my $stream = $c->{stream})) { return $stream->close_gracefully } |
|
121 | 118 |
$self->_remove($id); |
122 | 119 |
} |
123 | 120 |
|
... | ... |
@@ -182,18 +179,20 @@ sub timer { |
182 | 179 |
sub _accepting { |
183 | 180 |
my $self = shift; |
184 | 181 |
|
185 |
- # Check connection limit |
|
186 |
- return if $self->{accepting}; |
|
182 |
+ # Check if we have acceptors |
|
187 | 183 |
my $acceptors = $self->{acceptors} ||= {}; |
188 |
- return unless keys %$acceptors; |
|
184 |
+ return $self->_remove(delete $self->{accept}) unless keys %$acceptors; |
|
185 |
+ |
|
186 |
+ # Check connection limit |
|
189 | 187 |
my $i = keys %{$self->{connections}}; |
190 | 188 |
my $max = $self->max_connections; |
191 | 189 |
return unless $i < $max; |
192 | 190 |
|
193 | 191 |
# Acquire accept mutex |
194 | 192 |
if (my $cb = $self->lock) { return unless $self->$cb(!$i) } |
193 |
+ $self->_remove(delete $self->{accept}); |
|
195 | 194 |
|
196 |
- # Check if multi-accept is desirable and start accepting |
|
195 |
+ # Check if multi-accept is desirable |
|
197 | 196 |
my $multi = $self->multi_accept; |
198 | 197 |
$_->multi_accept($max < $multi ? 1 : $multi)->start for values %$acceptors; |
199 | 198 |
$self->{accepting}++; |
... | ... |
@@ -202,38 +201,17 @@ sub _accepting { |
202 | 201 |
sub _id { |
203 | 202 |
my $self = shift; |
204 | 203 |
my $id; |
205 |
- do { $id = md5_sum('c' . time . rand 999) } |
|
204 |
+ do { $id = md5_sum('c' . steady_time . rand 999) } |
|
206 | 205 |
while $self->{connections}{$id} || $self->{acceptors}{$id}; |
207 | 206 |
return $id; |
208 | 207 |
} |
209 | 208 |
|
210 |
-sub _manage { |
|
211 |
- my $self = shift; |
|
212 |
- |
|
213 |
- # Try to acquire accept mutex |
|
214 |
- $self->_accepting; |
|
215 |
- |
|
216 |
- # Close connections gracefully |
|
217 |
- my $connections = $self->{connections} ||= {}; |
|
218 |
- while (my ($id, $c) = each %$connections) { |
|
219 |
- $self->_remove($id) |
|
220 |
- if $c->{finish} && (!$c->{stream} || !$c->{stream}->is_writing); |
|
221 |
- } |
|
222 |
- |
|
223 |
- # Graceful stop |
|
224 |
- $self->_remove(delete $self->{manager}) |
|
225 |
- unless keys %$connections || keys %{$self->{acceptors}}; |
|
226 |
- $self->stop if $self->max_connections == 0 && keys %$connections == 0; |
|
227 |
-} |
|
228 |
- |
|
229 |
-sub _manager { |
|
230 |
- my $self = shift; |
|
231 |
- $self->{manager} ||= $self->recurring($self->accept_interval => \&_manage); |
|
232 |
-} |
|
233 |
- |
|
234 | 209 |
sub _not_accepting { |
235 | 210 |
my $self = shift; |
236 | 211 |
|
212 |
+ # Make sure timers are running |
|
213 |
+ $self->_timers; |
|
214 |
+ |
|
237 | 215 |
# Release accept mutex |
238 | 216 |
return unless delete $self->{accepting}; |
239 | 217 |
return unless my $cb = $self->unlock; |
... | ... |
@@ -250,33 +228,42 @@ sub _remove { |
250 | 228 |
return if $reactor->remove($id); |
251 | 229 |
|
252 | 230 |
# Acceptor |
253 |
- if (delete $self->{acceptors}{$id}) { delete $self->{accepting} } |
|
231 |
+ if (delete $self->{acceptors}{$id}) { $self->_not_accepting } |
|
254 | 232 |
|
255 |
- # Connection (stream needs to be deleted first) |
|
256 |
- else { |
|
257 |
- delete(($self->{connections}{$id} || {})->{stream}); |
|
258 |
- delete $self->{connections}{$id}; |
|
259 |
- } |
|
233 |
+ # Connection |
|
234 |
+ else { delete $self->{connections}{$id} } |
|
235 |
+} |
|
236 |
+ |
|
237 |
+sub _stop { |
|
238 |
+ my $self = shift; |
|
239 |
+ return if keys %{$self->{connections}}; |
|
240 |
+ $self->stop if $self->max_connections == 0; |
|
241 |
+ return if keys %{$self->{acceptors}}; |
|
242 |
+ $self->{$_} && $self->_remove(delete $self->{$_}) for qw(accept stop); |
|
260 | 243 |
} |
261 | 244 |
|
262 | 245 |
sub _stream { |
263 | 246 |
my ($self, $stream, $id) = @_; |
264 | 247 |
|
265 |
- # Make sure connection manager is running |
|
266 |
- $self->_manager; |
|
248 |
+ # Make sure timers are running |
|
249 |
+ $self->_timers; |
|
267 | 250 |
|
268 | 251 |
# Connect stream with reactor |
269 | 252 |
$self->{connections}{$id}{stream} = $stream; |
270 | 253 |
weaken $stream->reactor($self->reactor)->{reactor}; |
271 |
- |
|
272 |
- # Start streaming |
|
273 | 254 |
weaken $self; |
274 |
- $stream->on(close => sub { $self->{connections}{$id}{finish} = 1 }); |
|
255 |
+ $stream->on(close => sub { $self && $self->_remove($id) }); |
|
275 | 256 |
$stream->start; |
276 | 257 |
|
277 | 258 |
return $id; |
278 | 259 |
} |
279 | 260 |
|
261 |
+sub _timers { |
|
262 |
+ my $self = shift; |
|
263 |
+ $self->{accept} ||= $self->recurring($self->accept_interval => \&_accepting); |
|
264 |
+ $self->{stop} ||= $self->recurring(1 => \&_stop); |
|
265 |
+} |
|
266 |
+ |
|
280 | 267 |
1; |
281 | 268 |
|
282 | 269 |
=head1 NAME |
... | ... |
@@ -334,12 +321,14 @@ solid and scalable non-blocking TCP clients and servers. |
334 | 321 |
|
335 | 322 |
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
336 | 323 |
L<IO::Socket::SSL> (1.75+) are supported transparently, and used if installed. |
337 |
-Individual features can also be disabled with the C<MOJO_NO_IPV6> and |
|
338 |
-C<MOJO_NO_TLS> environment variables. |
|
324 |
+Individual features can also be disabled with the MOJO_NO_IPV6 and MOJO_NO_TLS |
|
325 |
+environment variables. |
|
339 | 326 |
|
340 |
-A TLS certificate and key are also built right in, to make writing test |
|
341 |
-servers as easy as possible. Also note that for convenience the C<PIPE> signal |
|
342 |
-will be set to C<IGNORE> when L<Mojo::IOLoop> is loaded. |
|
327 |
+The event loop will be resilient to time jumps if a monotonic clock is |
|
328 |
+available through L<Time::HiRes>. A TLS certificate and key are also built |
|
329 |
+right in, to make writing test servers as easy as possible. Also note that for |
|
330 |
+convenience the C<PIPE> signal will be set to C<IGNORE> when L<Mojo::IOLoop> |
|
331 |
+is loaded. |
|
343 | 332 |
|
344 | 333 |
See L<Mojolicious::Guides::Cookbook> for more. |
345 | 334 |
|
... | ... |
@@ -352,9 +341,9 @@ L<Mojo::IOLoop> implements the following attributes. |
352 | 341 |
my $interval = $loop->accept_interval; |
353 | 342 |
$loop = $loop->accept_interval(0.5); |
354 | 343 |
|
355 |
-Interval in seconds for trying to reacquire the accept mutex and connection |
|
356 |
-management, defaults to C<0.025>. Note that changing this value can affect |
|
357 |
-performance and idle CPU usage. |
|
344 |
+Interval in seconds for trying to reacquire the accept mutex, defaults to |
|
345 |
+C<0.025>. Note that changing this value can affect performance and idle CPU |
|
346 |
+usage. |
|
358 | 347 |
|
359 | 348 |
=head2 lock |
360 | 349 |
|
... | ... |
@@ -462,17 +451,17 @@ L<Mojo::IOLoop::Client/"connect">. |
462 | 451 |
my $delay = $loop->delay(sub {...}); |
463 | 452 |
my $delay = $loop->delay(sub {...}, sub {...}); |
464 | 453 |
|
465 |
-Get L<Mojo::IOLoop::Delay> object to control the flow of events. A single |
|
466 |
-callback will be treated as a subscriber to the C<finish> event, and multiple |
|
467 |
-ones as a chain of steps. |
|
454 |
+Get L<Mojo::IOLoop::Delay> object to manage callbacks and control the flow of |
|
455 |
+events. A single callback will be treated as a subscriber to the C<finish> |
|
456 |
+event, and multiple ones as a chain of steps. |
|
468 | 457 |
|
469 | 458 |
# Synchronize multiple events |
470 | 459 |
my $delay = Mojo::IOLoop->delay(sub { say 'BOOM!' }); |
471 | 460 |
for my $i (1 .. 10) { |
472 |
- $delay->begin; |
|
461 |
+ my $end = $delay->begin; |
|
473 | 462 |
Mojo::IOLoop->timer($i => sub { |
474 | 463 |
say 10 - $i; |
475 |
- $delay->end; |
|
464 |
+ $end->(); |
|
476 | 465 |
}); |
477 | 466 |
} |
478 | 467 |
|
... | ... |
@@ -614,7 +603,7 @@ seconds. |
614 | 603 |
|
615 | 604 |
=head1 DEBUGGING |
616 | 605 |
|
617 |
-You can set the C<MOJO_IOLOOP_DEBUG> environment variable to get some advanced |
|
606 |
+You can set the MOJO_IOLOOP_DEBUG environment variable to get some advanced |
|
618 | 607 |
diagnostics information printed to C<STDERR>. |
619 | 608 |
|
620 | 609 |
MOJO_IOLOOP_DEBUG=1 |
... | ... |
@@ -149,7 +149,7 @@ Mojo::IOLoop::Client - Non-blocking TCP client |
149 | 149 |
my ($client, $err) = @_; |
150 | 150 |
... |
151 | 151 |
}); |
152 |
- $client->connect(address => 'mojolicio.us', port => 80); |
|
152 |
+ $client->connect(address => 'example.com', port => 80); |
|
153 | 153 |
|
154 | 154 |
# Start reactor if necessary |
155 | 155 |
$client->reactor->start unless $client->reactor->is_running; |
... | ... |
@@ -6,13 +6,12 @@ use Mojo::IOLoop; |
6 | 6 |
has ioloop => sub { Mojo::IOLoop->singleton }; |
7 | 7 |
|
8 | 8 |
sub begin { |
9 |
- my $self = shift; |
|
10 |
- my $id = $self->{counter}++; |
|
11 |
- return sub { shift; $self->_step($id, @_) }; |
|
9 |
+ my ($self, $ignore) = @_; |
|
10 |
+ $self->{pending}++; |
|
11 |
+ my $id = $self->{counter}++; |
|
12 |
+ return sub { (defined $ignore ? $ignore : 1) and shift; $self->_step($id, @_) }; |
|
12 | 13 |
} |
13 | 14 |
|
14 |
-sub end { shift->_step(undef, @_) } |
|
15 |
- |
|
16 | 15 |
sub steps { |
17 | 16 |
my $self = shift; |
18 | 17 |
$self->{steps} = [@_]; |
... | ... |
@@ -31,25 +30,16 @@ sub wait { |
31 | 30 |
sub _step { |
32 | 31 |
my ($self, $id) = (shift, shift); |
33 | 32 |
|
34 |
- # Arguments |
|
35 |
- my $ordered = $self->{ordered} ||= []; |
|
36 |
- my $unordered = $self->{unordered} ||= []; |
|
37 |
- if (defined $id) { $ordered->[$id] = [@_] } |
|
38 |
- else { push @$unordered, @_ } |
|
39 |
- |
|
40 |
- # Wait for more events |
|
41 |
- return $self->{counter} if --$self->{counter}; |
|
42 |
- |
|
43 |
- # Next step |
|
44 |
- my $cb = shift @{$self->{steps} ||= []}; |
|
45 |
- $self->{$_} = [] for qw(ordered unordered); |
|
46 |
- my @args = ((map {@$_} grep {defined} @$ordered), @$unordered); |
|
47 |
- $self->$cb(@args) if $cb; |
|
33 |
+ $self->{args}[$id] = [@_]; |
|
34 |
+ return $self->{pending} if --$self->{pending} || $self->{lock}; |
|
35 |
+ local $self->{lock} = 1; |
|
36 |
+ my @args = map {@$_} @{delete $self->{args}}; |
|
48 | 37 |
|
49 |
- # Finished |
|
50 |
- $self->emit('finish', @args) |
|
51 |
- if !$self->{counter} && !@{$self->{steps}} && !$self->{finished}++; |
|
38 |
+ $self->{counter} = 0; |
|
39 |
+ if (my $cb = shift @{$self->{steps} ||= []}) { $self->$cb(@args) } |
|
52 | 40 |
|
41 |
+ if (!$self->{counter}) { $self->emit(finish => @args) } |
|
42 |
+ elsif (!$self->{pending}) { $self->ioloop->timer(0 => $self->begin) } |
|
53 | 43 |
return 0; |
54 | 44 |
} |
55 | 45 |
|
... | ... |
@@ -57,7 +47,7 @@ sub _step { |
57 | 47 |
|
58 | 48 |
=head1 NAME |
59 | 49 |
|
60 |
-Mojo::IOLoop::Delay - Control the flow of events |
|
50 |
+Mojo::IOLoop::Delay - Manage callbacks and control the flow of events |
|
61 | 51 |
|
62 | 52 |
=head1 SYNOPSIS |
63 | 53 |
|
... | ... |
@@ -67,10 +57,10 @@ Mojo::IOLoop::Delay - Control the flow of events |
67 | 57 |
my $delay = Mojo::IOLoop::Delay->new; |
68 | 58 |
$delay->on(finish => sub { say 'BOOM!' }); |
69 | 59 |
for my $i (1 .. 10) { |
70 |
- $delay->begin; |
|
60 |
+ my $end = $delay->begin; |
|
71 | 61 |
Mojo::IOLoop->timer($i => sub { |
72 | 62 |
say 10 - $i; |
73 |
- $delay->end; |
|
63 |
+ $end->(); |
|
74 | 64 |
}); |
75 | 65 |
} |
76 | 66 |
|
... | ... |
@@ -105,7 +95,8 @@ Mojo::IOLoop::Delay - Control the flow of events |
105 | 95 |
|
106 | 96 |
=head1 DESCRIPTION |
107 | 97 |
|
108 |
-L<Mojo::IOLoop::Delay> controls the flow of events for L<Mojo::IOLoop>. |
|
98 |
+L<Mojo::IOLoop::Delay> manages callbacks and controls the flow of events for |
|
99 |
+L<Mojo::IOLoop>. |
|
109 | 100 |
|
110 | 101 |
=head1 EVENTS |
111 | 102 |
|
... | ... |
@@ -142,22 +133,17 @@ implements the following new ones. |
142 | 133 |
=head2 begin |
143 | 134 |
|
144 | 135 |
my $cb = $delay->begin; |
136 |
+ my $cb = $delay->begin(0); |
|
145 | 137 |
|
146 |
-Increment active event counter, the returned callback can be used instead of |
|
147 |
-C<end>, which has the advantage of preserving the order of arguments. Note |
|
148 |
-that the first argument passed to the callback will be ignored. |
|
138 |
+Increment active event counter, the returned callback can be used to decrement |
|
139 |
+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. |
|
149 | 142 |
|
143 |
+ # Capture all arguments |
|
150 | 144 |
my $delay = Mojo::IOLoop->delay; |
151 |
- Mojo::UserAgent->new->get('mojolicio.us' => $delay->begin); |
|
152 |
- my $tx = $delay->wait; |
|
153 |
- |
|
154 |
-=head2 end |
|
155 |
- |
|
156 |
- my $remaining = $delay->end; |
|
157 |
- my $remaining = $delay->end(@args); |
|
158 |
- |
|
159 |
-Decrement active event counter, all arguments are queued for the next step or |
|
160 |
-C<finish> event and C<wait> method. |
|
145 |
+ Mojo::IOLoop->client({port => 3000} => $delay->begin(0)); |
|
146 |
+ my ($loop, $err, $stream) = $delay->wait; |
|
161 | 147 |
|
162 | 148 |
=head2 steps |
163 | 149 |
|
... | ... |
@@ -165,7 +151,8 @@ C<finish> event and C<wait> method. |
165 | 151 |
|
166 | 152 |
Sequentialize multiple events, the first callback will run right away, and the |
167 | 153 |
next one once the active event counter reaches zero, this chain will continue |
168 |
-until there are no more callbacks left. |
|
154 |
+until there are no more callbacks or a callback does not increment the active |
|
155 |
+event counter. |
|
169 | 156 |
|
170 | 157 |
=head2 wait |
171 | 158 |
|
... | ... |
@@ -109,7 +109,7 @@ sub start { |
109 | 109 |
$self->{handle} => sub { $self->_accept for 1 .. $self->multi_accept }); |
110 | 110 |
} |
111 | 111 |
|
112 |
-sub stop { $_[0]->reactor->remove($_[0]->{handle}) } |
|
112 |
+sub stop { $_[0]->reactor->remove($_[0]{handle}) } |
|
113 | 113 |
|
114 | 114 |
sub _accept { |
115 | 115 |
my $self = shift; |
... | ... |
@@ -3,56 +3,65 @@ use Mojo::Base 'Mojo::EventEmitter'; |
3 | 3 |
|
4 | 4 |
use Errno qw(EAGAIN ECONNRESET EINTR EPIPE EWOULDBLOCK); |
5 | 5 |
use Scalar::Util 'weaken'; |
6 |
-use Time::HiRes 'time'; |
|
7 | 6 |
|
8 | 7 |
has reactor => sub { |
9 | 8 |
require Mojo::IOLoop; |
10 | 9 |
Mojo::IOLoop->singleton->reactor; |
11 | 10 |
}; |
12 |
-has timeout => 15; |
|
13 | 11 |
|
14 | 12 |
sub DESTROY { shift->close } |
15 | 13 |
|
16 |
-sub new { shift->SUPER::new(handle => shift, buffer => '', active => time) } |
|
14 |
+sub new { shift->SUPER::new(handle => shift, buffer => '') } |
|
17 | 15 |
|
18 | 16 |
sub close { |
19 | 17 |
my $self = shift; |
20 | 18 |
|
21 | 19 |
# Cleanup |
22 | 20 |
return unless my $reactor = $self->{reactor}; |
23 |
- $reactor->remove(delete $self->{timer}) if $self->{timer}; |
|
24 |
- return unless my $handle = delete $self->{handle}; |
|
21 |
+ return unless my $handle = delete $self->timeout(0)->{handle}; |
|
25 | 22 |
$reactor->remove($handle); |
26 | 23 |
|
27 | 24 |
close $handle; |
28 | 25 |
$self->emit_safe('close'); |
29 | 26 |
} |
30 | 27 |
|
28 |
+sub close_gracefully { |
|
29 |
+ my $self = shift; |
|
30 |
+ return $self->{graceful} = 1 if $self->is_writing; |
|
31 |
+ $self->close; |
|
32 |
+} |
|
33 |
+ |
|
31 | 34 |
sub handle { shift->{handle} } |
32 | 35 |
|
33 | 36 |
sub is_readable { |
34 | 37 |
my $self = shift; |
35 |
- $self->{active} = time; |
|
38 |
+ $self->_again; |
|
36 | 39 |
return $self->{handle} && $self->reactor->is_readable($self->{handle}); |
37 | 40 |
} |
38 | 41 |
|
39 | 42 |
sub is_writing { |
40 | 43 |
my $self = shift; |
41 |
- return undef unless exists $self->{handle}; |
|
44 |
+ return undef unless $self->{handle}; |
|
42 | 45 |
return !!length($self->{buffer}) || $self->has_subscribers('drain'); |
43 | 46 |
} |
44 | 47 |
|
45 | 48 |
sub start { |
46 | 49 |
my $self = shift; |
47 |
- return $self->_startup unless $self->{startup}++; |
|
48 |
- return unless delete $self->{paused}; |
|
49 |
- $self->reactor->watch($self->{handle}, 1, $self->is_writing); |
|
50 |
+ |
|
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 |
+ # Resume |
|
57 |
+ $reactor->watch($self->{handle}, 1, $self->is_writing) |
|
58 |
+ if delete $self->{paused}; |
|
50 | 59 |
} |
51 | 60 |
|
52 | 61 |
sub stop { |
53 | 62 |
my $self = shift; |
54 |
- return if $self->{paused}++; |
|
55 |
- $self->reactor->watch($self->{handle}, 0, $self->is_writing); |
|
63 |
+ $self->reactor->watch($self->{handle}, 0, $self->is_writing) |
|
64 |
+ unless $self->{paused}++; |
|
56 | 65 |
} |
57 | 66 |
|
58 | 67 |
sub steal_handle { |
... | ... |
@@ -61,6 +70,21 @@ sub steal_handle { |
61 | 70 |
return delete $self->{handle}; |
62 | 71 |
} |
63 | 72 |
|
73 |
+sub timeout { |
|
74 |
+ my $self = shift; |
|
75 |
+ |
|
76 |
+ return $self->{timeout} unless @_; |
|
77 |
+ |
|
78 |
+ my $reactor = $self->reactor; |
|
79 |
+ $reactor->remove(delete $self->{timer}) if $self->{timer}; |
|
80 |
+ return $self unless my $timeout = $self->{timeout} = shift; |
|
81 |
+ weaken $self; |
|
82 |
+ $self->{timer} |
|
83 |
+ = $reactor->timer($timeout => sub { $self->emit_safe('timeout')->close }); |
|
84 |
+ |
|
85 |
+ return $self; |
|
86 |
+} |
|
87 |
+ |
|
64 | 88 |
sub write { |
65 | 89 |
my ($self, $chunk, $cb) = @_; |
66 | 90 |
|
... | ... |
@@ -73,42 +97,27 @@ sub write { |
73 | 97 |
return $self; |
74 | 98 |
} |
75 | 99 |
|
76 |
-sub _read { |
|
77 |
- my $self = shift; |
|
78 |
- |
|
79 |
- my $read = $self->{handle}->sysread(my $buffer, 131072, 0); |
|
80 |
- unless (defined $read) { |
|
81 |
- |
|
82 |
- # Retry |
|
83 |
- return if grep { $_ == $! } EAGAIN, EINTR, EWOULDBLOCK; |
|
100 |
+sub _again { $_[0]->reactor->again($_[0]{timer}) if $_[0]{timer} } |
|
84 | 101 |
|
85 |
- # Closed |
|
86 |
- return $self->close if grep { $_ == $! } ECONNRESET, EPIPE; |
|
102 |
+sub _error { |
|
103 |
+ my $self = shift; |
|
87 | 104 |
|
88 |
- # Read error |
|
89 |
- return $self->emit_safe(error => $!)->close; |
|
90 |
- } |
|
105 |
+ # Retry |
|
106 |
+ return if $! == EAGAIN || $! == EINTR || $! == EWOULDBLOCK; |
|
91 | 107 |
|
92 |
- # EOF |
|
93 |
- return $self->close if $read == 0; |
|
108 |
+ # Closed |
|
109 |
+ return $self->close if $! == ECONNRESET || $! == EPIPE; |
|
94 | 110 |
|
95 |
- $self->emit_safe(read => $buffer)->{active} = time; |
|
111 |
+ # Error |
|
112 |
+ $self->emit_safe(error => $!)->close; |
|
96 | 113 |
} |
97 | 114 |
|
98 |
-sub _startup { |
|
115 |
+sub _read { |
|
99 | 116 |
my $self = shift; |
100 |
- |
|
101 |
- # Timeout (ignore 0 timeout) |
|
102 |
- my $reactor = $self->reactor; |
|
103 |
- weaken $self; |
|
104 |
- $self->{timer} = $reactor->recurring( |
|
105 |
- 0.5 => sub { |
|
106 |
- return unless my $t = $self->timeout; |
|
107 |
- $self->emit_safe('timeout')->close if (time - $self->{active}) >= $t; |
|
108 |
- } |
|
109 |
- ); |
|
110 |
- |
|
111 |
- $reactor->io($self->{handle}, sub { pop() ? $self->_write : $self->_read }); |
|
117 |
+ my $read = $self->{handle}->sysread(my $buffer, 131072, 0); |
|
118 |
+ return $self->_error unless defined $read; |
|
119 |
+ return $self->close if $read == 0; |
|
120 |
+ $self->emit_safe(read => $buffer)->_again; |
|
112 | 121 |
} |
113 | 122 |
|
114 | 123 |
sub _write { |
... | ... |
@@ -117,25 +126,15 @@ sub _write { |
117 | 126 |
my $handle = $self->{handle}; |
118 | 127 |
if (length $self->{buffer}) { |
119 | 128 |
my $written = $handle->syswrite($self->{buffer}); |
120 |
- unless (defined $written) { |
|
121 |
- |
|
122 |
- # Retry |
|
123 |
- return if grep { $_ == $! } EAGAIN, EINTR, EWOULDBLOCK; |
|
124 |
- |
|
125 |
- # Closed |
|
126 |
- return $self->close if grep { $_ == $! } ECONNRESET, EPIPE; |
|
127 |
- |
|
128 |
- # Write error |
|
129 |
- return $self->emit_safe(error => $!)->close; |
|
130 |
- } |
|
131 |
- |
|
129 |
+ return $self->_error unless defined $written; |
|
132 | 130 |
$self->emit_safe(write => substr($self->{buffer}, 0, $written, '')); |
133 |
- $self->{active} = time; |
|
131 |
+ $self->_again; |
|
134 | 132 |
} |
135 | 133 |
|
136 |
- $self->emit_safe('drain') if !length $self->{buffer}; |
|
134 |
+ $self->emit_safe('drain') unless length $self->{buffer}; |
|
137 | 135 |
return if $self->is_writing; |
138 |
- $self->reactor->watch($handle, !$self->{paused}, 0); |
|
136 |
+ return $self->close if $self->{graceful}; |
|
137 |
+ $self->reactor->watch($handle, !$self->{paused}, 0) if $self->{handle}; |
|
139 | 138 |
} |
140 | 139 |
|
141 | 140 |
1; |
... | ... |
@@ -247,15 +246,6 @@ L<Mojo::IOLoop::Stream> implements the following attributes. |
247 | 246 |
Low level event reactor, defaults to the C<reactor> attribute value of the |
248 | 247 |
global L<Mojo::IOLoop> singleton. |
249 | 248 |
|
250 |
-=head2 timeout |
|
251 |
- |
|
252 |
- my $timeout = $stream->timeout; |
|
253 |
- $stream = $stream->timeout(45); |
|
254 |
- |
|
255 |
-Maximum amount of time in seconds stream can be inactive before getting closed |
|
256 |
-automatically, defaults to C<15>. Setting the value to C<0> will allow this |
|
257 |
-stream to be inactive indefinitely. |
|
258 |
- |
|
259 | 249 |
=head1 METHODS |
260 | 250 |
|
261 | 251 |
L<Mojo::IOLoop::Stream> inherits all methods from L<Mojo::EventEmitter> and |
... | ... |
@@ -273,6 +263,12 @@ Construct a new L<Mojo::IOLoop::Stream> object. |
273 | 263 |
|
274 | 264 |
Close stream immediately. |
275 | 265 |
|
266 |
+=head2 close_gracefully |
|
267 |
+ |
|
268 |
+ $stream->close_gracefully; |
|
269 |
+ |
|
270 |
+Close stream gracefully. |
|
271 |
+ |
|
276 | 272 |
=head2 handle |
277 | 273 |
|
278 | 274 |
my $handle = $stream->handle; |
... | ... |
@@ -310,6 +306,15 @@ Stop watching for new data on the stream. |
310 | 306 |
|
311 | 307 |
Steal handle from stream and prevent it from getting closed automatically. |
312 | 308 |
|
309 |
+=head2 timeout |
|
310 |
+ |
|
311 |
+ my $timeout = $stream->timeout; |
|
312 |
+ $stream = $stream->timeout(45); |
|
313 |
+ |
|
314 |
+Maximum amount of time in seconds stream can be inactive before getting closed |
|
315 |
+automatically, defaults to C<15>. Setting the value to C<0> will allow this |
|
316 |
+stream to be inactive indefinitely. |
|
317 |
+ |
|
313 | 318 |
=head2 write |
314 | 319 |
|
315 | 320 |
$stream = $stream->write($bytes); |
... | ... |
@@ -98,7 +98,7 @@ sub decode { |
98 | 98 |
|
99 | 99 |
sub encode { |
100 | 100 |
my ($self, $ref) = @_; |
101 |
- return Mojo::Util::encode 'UTF-8', _encode_values($ref); |
|
101 |
+ return Mojo::Util::encode 'UTF-8', _encode_value($ref); |
|
102 | 102 |
} |
103 | 103 |
|
104 | 104 |
sub false {$FALSE} |
... | ... |
@@ -251,23 +251,23 @@ sub _decode_value { |
251 | 251 |
|
252 | 252 |
sub _encode_array { |
253 | 253 |
my $array = shift; |
254 |
- return '[' . join(',', map { _encode_values($_) } @$array) . ']'; |
|
254 |
+ return '[' . join(',', map { _encode_value($_) } @$array) . ']'; |
|
255 | 255 |
} |
256 | 256 |
|
257 | 257 |
sub _encode_object { |
258 | 258 |
my $object = shift; |
259 |
- my @pairs = map { _encode_string($_) . ':' . _encode_values($object->{$_}) } |
|
259 |
+ my @pairs = map { _encode_string($_) . ':' . _encode_value($object->{$_}) } |
|
260 | 260 |
keys %$object; |
261 | 261 |
return '{' . join(',', @pairs) . '}'; |
262 | 262 |
} |
263 | 263 |
|
264 | 264 |
sub _encode_string { |
265 |
- my $string = shift; |
|
266 |
- $string =~ s!([\x00-\x1F\x7F\x{2028}\x{2029}\\"/\b\f\n\r\t])!$REVERSE{$1}!gs; |
|
267 |
- return "\"$string\""; |
|
265 |
+ my $str = shift; |
|
266 |
+ $str =~ s!([\x00-\x1F\x7F\x{2028}\x{2029}\\"/\b\f\n\r\t])!$REVERSE{$1}!gs; |
|
267 |
+ return "\"$str\""; |
|
268 | 268 |
} |
269 | 269 |
|
270 |
-sub _encode_values { |
|
270 |
+sub _encode_value { |
|
271 | 271 |
my $value = shift; |
272 | 272 |
|
273 | 273 |
# Reference |
... | ... |
@@ -285,7 +285,7 @@ sub _encode_values { |
285 | 285 |
|
286 | 286 |
# Blessed reference with TO_JSON method |
287 | 287 |
if (blessed $value && (my $sub = $value->can('TO_JSON'))) { |
288 |
- return _encode_values($value->$sub); |
|
288 |
+ return _encode_value($value->$sub); |
|
289 | 289 |
} |
290 | 290 |
} |
291 | 291 |
|
... | ... |
@@ -294,8 +294,7 @@ sub _encode_values { |
294 | 294 |
|
295 | 295 |
# Number |
296 | 296 |
my $flags = B::svref_2object(\$value)->FLAGS; |
297 |
- return $value |
|
298 |
- if $flags & (B::SVp_IOK | B::SVp_NOK) && !($flags & B::SVp_POK); |
|
297 |
+ return 0 + $value if $flags & (B::SVp_IOK | B::SVp_NOK) && $value * 0 == 0; |
|
299 | 298 |
|
300 | 299 |
# String |
301 | 300 |
return _encode_string($value); |
... | ... |
@@ -1,7 +1,6 @@ |
1 | 1 |
package Mojo::JSON::Pointer; |
2 | 2 |
use Mojo::Base -base; |
3 | 3 |
|
4 |
-use Mojo::Util qw(decode url_unescape); |
|
5 | 4 |
use Scalar::Util 'looks_like_number'; |
6 | 5 |
|
7 | 6 |
sub contains { shift->_pointer(1, @_) } |
... | ... |
@@ -10,9 +9,8 @@ sub get { shift->_pointer(0, @_) } |
10 | 9 |
sub _pointer { |
11 | 10 |
my ($self, $contains, $data, $pointer) = @_; |
12 | 11 |
|
13 |
- $pointer = decode('UTF-8', url_unescape $pointer); |
|
14 | 12 |
return $data unless $pointer =~ s!^/!!; |
15 |
- for my $p (split '/', $pointer) { |
|
13 |
+ for my $p ($pointer eq '' ? ($pointer) : (split '/', $pointer)) { |
|
16 | 14 |
$p =~ s/~0/~/g; |
17 | 15 |
$p =~ s!~1!/!g; |
18 | 16 |
|
... | ... |
@@ -33,6 +31,8 @@ sub _pointer { |
33 | 31 |
|
34 | 32 |
1; |
35 | 33 |
|
34 |
+=encoding utf8 |
|
35 |
+ |
|
36 | 36 |
=head1 NAME |
37 | 37 |
|
38 | 38 |
Mojo::JSON::Pointer - JSON Pointers |
... | ... |
@@ -47,7 +47,7 @@ Mojo::JSON::Pointer - JSON Pointers |
47 | 47 |
|
48 | 48 |
=head1 DESCRIPTION |
49 | 49 |
|
50 |
-L<Mojo::JSON::Pointer> implements JSON Pointers. |
|
50 |
+L<Mojo::JSON::Pointer> is a relaxed implementation of RFC 6901. |
|
51 | 51 |
|
52 | 52 |
=head1 METHODS |
53 | 53 |
|
... | ... |
@@ -59,10 +59,12 @@ Check if data structure contains a value that can be identified with the given |
59 | 59 |
JSON Pointer. |
60 | 60 |
|
61 | 61 |
# True |
62 |
+ $pointer->contains({'♥' => 'mojolicious'}, '/♥'); |
|
62 | 63 |
$pointer->contains({foo => 'bar', baz => [4, 5, 6]}, '/foo'); |
63 | 64 |
$pointer->contains({foo => 'bar', baz => [4, 5, 6]}, '/baz/2'); |
64 | 65 |
|
65 | 66 |
# False |
67 |
+ $pointer->contains({'♥' => 'mojolicious'}, '/☃'); |
|
66 | 68 |
$pointer->contains({foo => 'bar', baz => [4, 5, 6]}, '/bar'); |
67 | 69 |
$pointer->contains({foo => 'bar', baz => [4, 5, 6]}, '/baz/9'); |
68 | 70 |
|
... | ... |
@@ -72,6 +74,9 @@ JSON Pointer. |
72 | 74 |
|
73 | 75 |
Extract value identified by the given JSON Pointer. |
74 | 76 |
|
77 |
+ # "mojolicious" |
|
78 |
+ $pointer->get({'♥' => 'mojolicious'}, '/♥'); |
|
79 |
+ |
|
75 | 80 |
# "bar" |
76 | 81 |
$pointer->get({foo => 'bar', baz => [4, 5, 6]}, '/foo'); |
77 | 82 |
|
... | ... |
@@ -3,18 +3,18 @@ use Mojo::Base 'Mojo::EventEmitter'; |
3 | 3 |
|
4 | 4 |
use Carp 'croak'; |
5 | 5 |
use Fcntl ':flock'; |
6 |
+use Mojo::Util 'encode'; |
|
6 | 7 |
|
7 | 8 |
has handle => sub { |
8 | 9 |
|
9 | 10 |
# File |
10 | 11 |
if (my $path = shift->path) { |
11 | 12 |
croak qq{Can't open log file "$path": $!} |
12 |
- unless open my $file, '>>:utf8', $path; |
|
13 |
+ unless open my $file, '>>', $path; |
|
13 | 14 |
return $file; |
14 | 15 |
} |
15 | 16 |
|
16 | 17 |
# STDERR |
17 |
- binmode STDERR, ':utf8'; |
|
18 | 18 |
return \*STDERR; |
19 | 19 |
}; |
20 | 20 |
has level => 'debug'; |
... | ... |
@@ -63,7 +63,7 @@ sub _message { |
63 | 63 |
|
64 | 64 |
flock $handle, LOCK_EX; |
65 | 65 |
croak "Can't write to log: $!" |
66 |
- unless defined $handle->syswrite($self->format($level, @lines)); |
|
66 |
+ unless $handle->print(encode 'UTF-8', $self->format($level, @lines)); |
|
67 | 67 |
flock $handle, LOCK_UN; |
68 | 68 |
} |
69 | 69 |
|
... | ... |
@@ -131,8 +131,8 @@ or C<STDERR>. |
131 | 131 |
my $level = $log->level; |
132 | 132 |
$log = $log->level('debug'); |
133 | 133 |
|
134 |
-Active log level, defaults to the value of the C<MOJO_LOG_LEVEL> environment |
|
135 |
-variable or C<debug>. |
|
134 |
+Active log level, defaults to C<debug>. Note that the MOJO_LOG_LEVEL |
|
135 |
+environment variable can override this value. |
|
136 | 136 |
|
137 | 137 |
These levels are currently available: |
138 | 138 |
|
... | ... |
@@ -10,7 +10,6 @@ use Mojo::JSON::Pointer; |
10 | 10 |
use Mojo::Parameters; |
11 | 11 |
use Mojo::Upload; |
12 | 12 |
use Mojo::Util 'decode'; |
13 |
-use Scalar::Util 'weaken'; |
|
14 | 13 |
|
15 | 14 |
has content => sub { Mojo::Content::Single->new }; |
16 | 15 |
has default_charset => 'UTF-8'; |
... | ... |
@@ -22,20 +21,15 @@ sub body { |
22 | 21 |
my $self = shift; |
23 | 22 |
|
24 | 23 |
# Downgrade multipart content |
25 |
- $self->content(Mojo::Content::Single->new) if $self->content->is_multipart; |
|
26 | 24 |
my $content = $self->content; |
25 |
+ $content = $self->content(Mojo::Content::Single->new)->content |
|
26 |
+ if $content->is_multipart; |
|
27 | 27 |
|
28 | 28 |
# Get |
29 |
- return $content->asset->slurp unless defined(my $new = shift); |
|
30 |
- |
|
31 |
- # Callback |
|
32 |
- if (ref $new eq 'CODE') { |
|
33 |
- weaken $self; |
|
34 |
- return $content->unsubscribe('read')->on(read => sub { $self->$new(pop) }); |
|
35 |
- } |
|
29 |
+ return $content->asset->slurp unless @_; |
|
36 | 30 |
|
37 | 31 |
# Set raw content |
38 |
- else { $content->asset(Mojo::Asset::Memory->new->add_chunk($new)) } |
|
32 |
+ $content->asset(Mojo::Asset::Memory->new->add_chunk(@_)); |
|
39 | 33 |
|
40 | 34 |
return $self; |
41 | 35 |
} |
... | ... |
@@ -48,18 +42,15 @@ sub body_params { |
48 | 42 |
$params->charset($self->content->charset || $self->default_charset); |
49 | 43 |
|
50 | 44 |
# "x-application-urlencoded" and "application/x-www-form-urlencoded" |
51 |
- my $type = $self->headers->content_type || ''; |
|
45 |
+ my $type = defined $self->headers->content_type ? $self->headers->content_type : ''; |
|
52 | 46 |
if ($type =~ m!(?:x-application|application/x-www-form)-urlencoded!i) { |
53 | 47 |
$params->parse($self->content->asset->slurp); |
54 | 48 |
} |
55 | 49 |
|
56 | 50 |
# "multipart/formdata" |
57 | 51 |
elsif ($type =~ m!multipart/form-data!i) { |
58 |
- my $formdata = $self->_parse_formdata; |
|
59 |
- for my $data (@$formdata) { |
|
60 |
- my ($name, $filename, $value) = @$data; |
|
61 |
- next if defined $filename; |
|
62 |
- $params->append($name, $value); |
|
52 |
+ for my $data (@{$self->_parse_formdata}) { |
|
53 |
+ $params->append($data->[0], $data->[2]) unless defined $data->[1]; |
|
63 | 54 |
} |
64 | 55 |
} |
65 | 56 |
|
... | ... |
@@ -72,23 +63,18 @@ sub build_body { shift->_build('get_body_chunk') } |
72 | 63 |
sub build_headers { shift->_build('get_header_chunk') } |
73 | 64 |
sub build_start_line { shift->_build('get_start_line_chunk') } |
74 | 65 |
|
75 |
-sub cookie { |
|
76 |
- my ($self, $name) = @_; |
|
77 |
- $self->{cookies} ||= _nest($self->cookies); |
|
78 |
- return unless my $cookies = $self->{cookies}{$name}; |
|
79 |
- my @cookies = ref $cookies eq 'ARRAY' ? @$cookies : ($cookies); |
|
80 |
- return wantarray ? @cookies : $cookies[0]; |
|
81 |
-} |
|
66 |
+sub cookie { shift->_cache(cookies => @_) } |
|
82 | 67 |
|
83 | 68 |
sub cookies { croak 'Method "cookies" not implemented by subclass' } |
84 | 69 |
|
85 | 70 |
sub dom { |
86 | 71 |
my $self = shift; |
87 | 72 |
|
88 |
- return undef if $self->is_multipart; |
|
89 |
- my $dom = $self->{dom} |
|
90 |
- ||= Mojo::DOM->new->charset(defined $self->content->charset ? $self->content->charset : undef) |
|
91 |
- ->parse($self->body); |
|
73 |
+ 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); |
|
92 | 78 |
|
93 | 79 |
return @_ ? $dom->find(@_) : $dom; |
94 | 80 |
} |
... | ... |
@@ -121,9 +107,10 @@ sub fix_headers { |
121 | 107 |
my $self = shift; |
122 | 108 |
|
123 | 109 |
# Content-Length or Connection (unless chunked transfer encoding is used) |
124 |
- return $self if $self->{fix}++ || $self->is_chunked; |
|
110 |
+ my $content = $self->content; |
|
111 |
+ return $self if $self->{fix}++ || $content->is_chunked; |
|
125 | 112 |
my $headers = $self->headers; |
126 |
- $self->is_dynamic |
|
113 |
+ $content->is_dynamic |
|
127 | 114 |
? $headers->connection('close') |
128 | 115 |
: $headers->content_length($self->body_size) |
129 | 116 |
unless $headers->content_length; |
... | ... |
@@ -152,39 +139,28 @@ sub get_start_line_chunk { |
152 | 139 |
croak 'Method "get_start_line_chunk" not implemented by subclass'; |
153 | 140 |
} |
154 | 141 |
|
155 |
-sub has_leftovers { shift->content->has_leftovers } |
|
156 |
- |
|
157 | 142 |
sub header_size { shift->fix_headers->content->header_size } |
158 | 143 |
|
159 |
-sub headers { shift->content->headers } |
|
160 |
-sub is_chunked { shift->content->is_chunked } |
|
161 |
-sub is_dynamic { shift->content->is_dynamic } |
|
144 |
+sub headers { shift->content->headers } |
|
162 | 145 |
|
163 | 146 |
sub is_finished { do { my $tmp = shift->{state}; defined $tmp ? $tmp : ''} eq 'finished' } |
164 | 147 |
|
165 |
-sub is_limit_exceeded { |
|
166 |
- return undef unless my $code = (shift->error)[1]; |
|
167 |
- return !!grep { $_ eq $code } 413, 431; |
|
168 |
-} |
|
169 |
- |
|
170 |
-sub is_multipart { shift->content->is_multipart } |
|
148 |
+sub is_limit_exceeded { !!shift->{limit} } |
|
171 | 149 |
|
172 | 150 |
sub json { |
173 | 151 |
my ($self, $pointer) = @_; |
174 |
- return undef if $self->is_multipart; |
|
152 |
+ return undef if $self->content->is_multipart; |
|
175 | 153 |
my $data = $self->{json} ||= Mojo::JSON->new->decode($self->body); |
176 | 154 |
return $pointer ? Mojo::JSON::Pointer->new->get($data, $pointer) : $data; |
177 | 155 |
} |
178 | 156 |
|
179 |
-sub leftovers { shift->content->leftovers } |
|
180 |
- |
|
181 | 157 |
sub param { shift->body_params->param(@_) } |
182 | 158 |
|
183 | 159 |
sub parse { |
184 | 160 |
my ($self, $chunk) = @_; |
185 | 161 |
|
186 | 162 |
# Check message size |
187 |
- return $self->error('Maximum message size exceeded', 413) |
|
163 |
+ return $self->_limit('Maximum message size exceeded', 413) |
|
188 | 164 |
if ($self->{raw_size} += length($chunk = defined $chunk ? $chunk : '')) > $self->max_message_size; |
189 | 165 |
|
190 | 166 |
$self->{buffer} .= $chunk; |
... | ... |
@@ -195,18 +171,19 @@ sub parse { |
195 | 171 |
# Check line size |
196 | 172 |
my $len = index $self->{buffer}, "\x0a"; |
197 | 173 |
$len = length $self->{buffer} if $len < 0; |
198 |
- return $self->error('Maximum line size exceeded', 431) |
|
174 |
+ return $self->_limit('Maximum line size exceeded', 431) |
|
199 | 175 |
if $len > $self->max_line_size; |
200 | 176 |
|
201 | 177 |
$self->{state} = 'content' if $self->extract_start_line(\$self->{buffer}); |
202 | 178 |
} |
203 | 179 |
|
204 | 180 |
# Content |
181 |
+ my $state = defined $self->{state} ? $self->{state} : ''; |
|
205 | 182 |
$self->content($self->content->parse(delete $self->{buffer})) |
206 |
- if grep { $_ eq (defined $self->{state} ? $self->{state} : '') } qw(content finished); |
|
183 |
+ if $state eq 'content' || $state eq 'finished'; |
|
207 | 184 |
|
208 | 185 |
# Check line size |
209 |
- return $self->error('Maximum line size exceeded', 431) |
|
186 |
+ return $self->_limit('Maximum line size exceeded', 431) |
|
210 | 187 |
if $self->headers->is_limit_exceeded; |
211 | 188 |
|
212 | 189 |
# Check buffer size |
... | ... |
@@ -223,31 +200,23 @@ sub to_string { |
223 | 200 |
return $self->build_start_line . $self->build_headers . $self->build_body; |
224 | 201 |
} |
225 | 202 |
|
226 |
-sub upload { |
|
227 |
- my ($self, $name) = @_; |
|
228 |
- $self->{uploads} ||= _nest($self->uploads); |
|
229 |
- return unless my $uploads = $self->{uploads}{$name}; |
|
230 |
- my @uploads = ref $uploads eq 'ARRAY' ? @$uploads : ($uploads); |
|
231 |
- return wantarray ? @uploads : $uploads[0]; |
|
232 |
-} |
|
203 |
+sub upload { shift->_cache(uploads => @_) } |
|
233 | 204 |
|
234 | 205 |
sub uploads { |
235 | 206 |
my $self = shift; |
236 | 207 |
|
237 | 208 |
my @uploads; |
238 |
- my $formdata = $self->_parse_formdata; |
|
239 |
- for my $data (@$formdata) { |
|
240 |
- my ($name, $filename, $part) = @$data; |
|
209 |
+ for my $data (@{$self->_parse_formdata}) { |
|
241 | 210 |
|
242 | 211 |
# Just a form value |
243 |
- next unless defined $filename; |
|
212 |
+ next unless defined $data->[1]; |
|
244 | 213 |
|
245 | 214 |
# Uploaded file |
246 | 215 |
my $upload = Mojo::Upload->new( |
247 |
- name => $name, |
|
248 |
- asset => $part->asset, |
|
249 |
- filename => $filename, |
|
250 |
- headers => $part->headers |
|
216 |
+ name => $data->[0], |
|
217 |
+ filename => $data->[1], |
|
218 |
+ asset => $data->[2]->asset, |
|
219 |
+ headers => $data->[2]->headers |
|
251 | 220 |
); |
252 | 221 |
push @uploads, $upload; |
253 | 222 |
} |
... | ... |
@@ -255,9 +224,6 @@ sub uploads { |
255 | 224 |
return \@uploads; |
256 | 225 |
} |
257 | 226 |
|
258 |
-sub write { shift->_write(write => @_) } |
|
259 |
-sub write_chunk { shift->_write(write_chunk => @_) } |
|
260 |
- |
|
261 | 227 |
sub _build { |
262 | 228 |
my ($self, $method) = @_; |
263 | 229 |
|
... | ... |
@@ -278,25 +244,23 @@ sub _build { |
278 | 244 |
return $buffer; |
279 | 245 |
} |
280 | 246 |
|
281 |
-sub _nest { |
|
282 |
- my $array = shift; |
|
283 |
- |
|
284 |
- # Turn array of objects into hash |
|
285 |
- my $hash = {}; |
|
286 |
- for my $object (@$array) { |
|
287 |
- my $name = $object->name; |
|
247 |
+sub _cache { |
|
248 |
+ my ($self, $method, $name) = @_; |
|
288 | 249 |
|
289 |
- # Multiple objects with same name |
|
290 |
- if (exists $hash->{$name}) { |
|
291 |
- $hash->{$name} = [$hash->{$name}] unless ref $hash->{$name} eq 'ARRAY'; |
|
292 |
- push @{$hash->{$name}}, $object; |
|
293 |
- } |
|
294 |
- |
|
295 |
- # Single object |
|
296 |
- else { $hash->{$name} = $object } |
|
250 |
+ # Cache objects by name |
|
251 |
+ unless ($self->{$method}) { |
|
252 |
+ $self->{$method} = {}; |
|
253 |
+ push @{$self->{$method}{$_->name}}, $_ for @{$self->$method}; |
|
297 | 254 |
} |
298 | 255 |
|
299 |
- return $hash; |
|
256 |
+ return unless my $objects = $self->{$method}{$name}; |
|
257 |
+ return wantarray ? @$objects : $objects->[0]; |
|
258 |
+} |
|
259 |
+ |
|
260 |
+sub _limit { |
|
261 |
+ my $self = shift; |
|
262 |
+ $self->{limit} = 1; |
|
263 |
+ $self->error(@_); |
|
300 | 264 |
} |
301 | 265 |
|
302 | 266 |
sub _parse_formdata { |
... | ... |
@@ -309,8 +273,7 @@ sub _parse_formdata { |
309 | 273 |
my $charset = $content->charset || $self->default_charset; |
310 | 274 |
|
311 | 275 |
# Check all parts for form data |
312 |
- my @parts; |
|
313 |
- push @parts, $content; |
|
276 |
+ my @parts = ($content); |
|
314 | 277 |
while (my $part = shift @parts) { |
315 | 278 |
|
316 | 279 |
# Nested multipart content |
... | ... |
@@ -320,8 +283,7 @@ sub _parse_formdata { |
320 | 283 |
} |
321 | 284 |
|
322 | 285 |
# Extract information from Content-Disposition header |
323 |
- my $disposition = $part->headers->content_disposition; |
|
324 |
- next unless $disposition; |
|
286 |
+ next unless my $disposition = $part->headers->content_disposition; |
|
325 | 287 |
my ($name) = $disposition =~ /[; ]name="?([^";]+)"?/; |
326 | 288 |
my ($filename) = $disposition =~ /[; ]filename="?([^"]*)"?/; |
327 | 289 |
if ($charset) { |
... | ... |
@@ -342,13 +304,6 @@ sub _parse_formdata { |
342 | 304 |
return \@formdata; |
343 | 305 |
} |
344 | 306 |
|
345 |
-sub _write { |
|
346 |
- my ($self, $method, $chunk, $cb) = @_; |
|
347 |
- weaken $self; |
|
348 |
- $self->content->$method($chunk => sub { shift and $self->$cb(@_) if $cb }); |
|
349 |
- return $self; |
|
350 |
-} |
|
351 |
- |
|
352 | 307 |
1; |
353 | 308 |
|
354 | 309 |
=head1 NAME |
... | ... |
@@ -436,7 +391,7 @@ Default charset used for form data parsing, defaults to C<UTF-8>. |
436 | 391 |
$msg = $msg->max_line_size(1024); |
437 | 392 |
|
438 | 393 |
Maximum start line size in bytes, defaults to the value of the |
439 |
-C<MOJO_MAX_LINE_SIZE> environment variable or C<10240>. |
|
394 |
+MOJO_MAX_LINE_SIZE environment variable or C<10240>. |
|
440 | 395 |
|
441 | 396 |
=head2 max_message_size |
442 | 397 |
|
... | ... |
@@ -444,10 +399,10 @@ C<MOJO_MAX_LINE_SIZE> environment variable or C<10240>. |
444 | 399 |
$msg = $msg->max_message_size(1024); |
445 | 400 |
|
446 | 401 |
Maximum message size in bytes, defaults to the value of the |
447 |
-C<MOJO_MAX_MESSAGE_SIZE> environment variable or C<5242880>. Note that |
|
448 |
-increasing this value can also drastically increase memory usage, should you |
|
449 |
-for example attempt to parse an excessively large message body with the |
|
450 |
-C<body_params>, C<dom> or C<json> methods. |
|
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. |
|
451 | 406 |
|
452 | 407 |
=head2 version |
453 | 408 |
|
... | ... |
@@ -465,14 +420,8 @@ implements the following new ones. |
465 | 420 |
|
466 | 421 |
my $bytes = $msg->body; |
467 | 422 |
$msg = $msg->body('Hello!'); |
468 |
- my $cb = $msg->body(sub {...}); |
|
469 | 423 |
|
470 |
-Access C<content> data or replace all subscribers of the C<read> event. |
|
471 |
- |
|
472 |
- $msg->body(sub { |
|
473 |
- my ($msg, $bytes) = @_; |
|
474 |
- say "Streaming: $bytes"; |
|
475 |
- }); |
|
424 |
+Slurp or replace C<content>. |
|
476 | 425 |
|
477 | 426 |
=head2 body_params |
478 | 427 |
|
... | ... |
@@ -556,7 +505,7 @@ Error and code. |
556 | 505 |
|
557 | 506 |
=head2 extract_start_line |
558 | 507 |
|
559 |
- my $success = $msg->extract_start_line(\$string); |
|
508 |
+ my $success = $msg->extract_start_line(\$str); |
|
560 | 509 |
|
561 | 510 |
Extract start line from string. Meant to be overloaded in a subclass. |
562 | 511 |
|
... | ... |
@@ -591,12 +540,6 @@ Get a chunk of header data, starting from a specific position. |
591 | 540 |
Get a chunk of start line data starting from a specific position. Meant to be |
592 | 541 |
overloaded in a subclass. |
593 | 542 |
|
594 |
-=head2 has_leftovers |
|
595 |
- |
|
596 |
- my $success = $msg->has_leftovers; |
|
597 |
- |
|
598 |
-Check if there are leftovers. |
|
599 |
- |
|
600 | 543 |
=head2 header_size |
601 | 544 |
|
602 | 545 |
my $size = $msg->header_size; |
... | ... |
@@ -609,19 +552,6 @@ Size of headers in bytes. |
609 | 552 |
|
610 | 553 |
Message headers, usually a L<Mojo::Headers> object. |
611 | 554 |
|
612 |
-=head2 is_chunked |
|
613 |
- |
|
614 |
- my $success = $msg->is_chunked; |
|
615 |
- |
|
616 |
-Check if content is chunked. |
|
617 |
- |
|
618 |
-=head2 is_dynamic |
|
619 |
- |
|
620 |
- my $success = $msg->is_dynamic; |
|
621 |
- |
|
622 |
-Check if content will be dynamically generated, which prevents C<clone> from |
|
623 |
-working. |
|
624 |
- |
|
625 | 555 |
=head2 is_finished |
626 | 556 |
|
627 | 557 |
my $success = $msg->is_finished; |
... | ... |
@@ -634,12 +564,6 @@ Check if message parser/generator is finished. |
634 | 564 |
|
635 | 565 |
Check if message has exceeded C<max_line_size> or C<max_message_size>. |
636 | 566 |
|
637 |
-=head2 is_multipart |
|
638 |
- |
|
639 |
- my $success = $msg->is_multipart; |
|
640 |
- |
|
641 |
-Check if content is a L<Mojo::Content::MultiPart> object. |
|
642 |
- |
|
643 | 567 |
=head2 json |
644 | 568 |
|
645 | 569 |
my $hash = $msg->json; |
... | ... |
@@ -655,12 +579,6 @@ it should not be called before the entire message body has been received. |
655 | 579 |
say $msg->json->{foo}{bar}[23]; |
656 | 580 |
say $msg->json('/foo/bar/23'); |
657 | 581 |
|
658 |
-=head2 leftovers |
|
659 |
- |
|
660 |
- my $bytes = $msg->leftovers; |
|
661 |
- |
|
662 |
-Get leftover data from content parser. |
|
663 |
- |
|
664 | 582 |
=head2 param |
665 | 583 |
|
666 | 584 |
my @names = $msg->param; |
... | ... |
@@ -684,7 +602,7 @@ Size of the start line in bytes. |
684 | 602 |
|
685 | 603 |
=head2 to_string |
686 | 604 |
|
687 |
- my $string = $msg->to_string; |
|
605 |
+ my $str = $msg->to_string; |
|
688 | 606 |
|
689 | 607 |
Render whole message. |
690 | 608 |
|
... | ... |
@@ -706,22 +624,6 @@ entire message body has been received. |
706 | 624 |
|
707 | 625 |
All C<multipart/form-data> file uploads, usually L<Mojo::Upload> objects. |
708 | 626 |
|
709 |
-=head2 write |
|
710 |
- |
|
711 |
- $msg = $msg->write($bytes); |
|
712 |
- $msg = $msg->write($bytes => sub {...}); |
|
713 |
- |
|
714 |
-Write dynamic content non-blocking, the optional drain callback will be |
|
715 |
-invoked once all data has been written. |
|
716 |
- |
|
717 |
-=head2 write_chunk |
|
718 |
- |
|
719 |
- $msg = $msg->write_chunk($bytes); |
|
720 |
- $msg = $msg->write_chunk($bytes => sub {...}); |
|
721 |
- |
|
722 |
-Write dynamic content non-blocking with C<chunked> transfer encoding, the |
|
723 |
-optional drain callback will be invoked once all data has been written. |
|
724 |
- |
|
725 | 627 |
=head1 SEE ALSO |
726 | 628 |
|
727 | 629 |
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
... | ... |
@@ -2,7 +2,6 @@ package Mojo::Message::Request; |
2 | 2 |
use Mojo::Base 'Mojo::Message'; |
3 | 3 |
|
4 | 4 |
use Mojo::Cookie::Request; |
5 |
-use Mojo::Parameters; |
|
6 | 5 |
use Mojo::Util qw(b64_encode b64_decode get_line); |
7 | 6 |
use Mojo::URL; |
8 | 7 |
|
... | ... |
@@ -11,7 +10,7 @@ has method => 'GET'; |
11 | 10 |
has url => sub { Mojo::URL->new }; |
12 | 11 |
|
13 | 12 |
my $START_LINE_RE = qr| |
14 |
- ^\s* |
|
13 |
+ ^ |
|
15 | 14 |
([a-zA-Z]+) # Method |
16 | 15 |
\s+ |
17 | 16 |
([0-9a-zA-Z\-._~:/?#[\]\@!\$&'()*+,;=\%]+) # Path |
... | ... |
@@ -79,11 +78,8 @@ sub fix_headers { |
79 | 78 |
$headers->authorization('Basic ' . b64_encode($auth, '')) |
80 | 79 |
if $auth && !$headers->authorization; |
81 | 80 |
|
82 |
- # Proxy |
|
81 |
+ # Basic proxy authentication |
|
83 | 82 |
if (my $proxy = $self->proxy) { |
84 |
- $url = $proxy if $self->method eq 'CONNECT'; |
|
85 |
- |
|
86 |
- # Basic proxy authentication |
|
87 | 83 |
my $proxy_auth = $proxy->userinfo; |
88 | 84 |
$headers->proxy_authorization('Basic ' . b64_encode($proxy_auth, '')) |
89 | 85 |
if $proxy_auth && !$headers->proxy_authorization; |
... | ... |
@@ -119,7 +115,7 @@ sub get_start_line_chunk { |
119 | 115 |
# Proxy |
120 | 116 |
elsif ($self->proxy) { |
121 | 117 |
my $clone = $url = $url->clone->userinfo(undef); |
122 |
- my $upgrade = lc($self->headers->upgrade || ''); |
|
118 |
+ my $upgrade = lc(defined $self->headers->upgrade ? $self->headers->upgrade : ''); |
|
123 | 119 |
$path = $clone |
124 | 120 |
unless $upgrade eq 'websocket' || $url->protocol eq 'https'; |
125 | 121 |
} |
... | ... |
@@ -137,7 +133,7 @@ sub is_secure { |
137 | 133 |
} |
138 | 134 |
|
139 | 135 |
sub is_xhr { |
140 |
- (shift->headers->header('X-Requested-With') || '') =~ /XMLHttpRequest/i; |
|
136 |
+ (do {my $tmp = shift->headers->header('X-Requested-With'); defined $tmp ? $tmp : ''}) =~ /XMLHttpRequest/i; |
|
141 | 137 |
} |
142 | 138 |
|
143 | 139 |
sub param { shift->params->param(@_) } |
... | ... |
@@ -145,7 +141,7 @@ sub param { shift->params->param(@_) } |
145 | 141 |
sub params { |
146 | 142 |
my $self = shift; |
147 | 143 |
return $self->{params} |
148 |
- ||= Mojo::Parameters->new->merge($self->body_params, $self->query_params); |
|
144 |
+ ||= $self->body_params->clone->merge($self->query_params); |
|
149 | 145 |
} |
150 | 146 |
|
151 | 147 |
sub parse { |
... | ... |
@@ -233,7 +229,7 @@ sub _parse_env { |
233 | 229 |
$self->method($env->{REQUEST_METHOD}) if $env->{REQUEST_METHOD}; |
234 | 230 |
|
235 | 231 |
# Scheme/Version |
236 |
- if (($env->{SERVER_PROTOCOL} || '') =~ m!^([^/]+)/([^/]+)$!) { |
|
232 |
+ if ((defined $env->{SERVER_PROTOCOL} ? $env->{SERVER_PROTOCOL} : '') =~ m!^([^/]+)/([^/]+)$!) { |
|
237 | 233 |
$base->scheme($1); |
238 | 234 |
$self->version($2); |
239 | 235 |
} |
... | ... |
@@ -291,7 +287,7 @@ Mojo::Message::Request - HTTP request |
291 | 287 |
=head1 DESCRIPTION |
292 | 288 |
|
293 | 289 |
L<Mojo::Message::Request> is a container for HTTP requests as described in RFC |
294 |
-2616. |
|
290 |
+2616 and RFC 2817. |
|
295 | 291 |
|
296 | 292 |
=head1 EVENTS |
297 | 293 |
|
... | ... |
@@ -329,8 +325,10 @@ HTTP request method, defaults to C<GET>. |
329 | 325 |
|
330 | 326 |
HTTP request URL, defaults to a L<Mojo::URL> object. |
331 | 327 |
|
332 |
- # Get request path |
|
333 |
- say $req->url->path; |
|
328 |
+ # Get request information |
|
329 |
+ say $req->url->to_abs->userinfo; |
|
330 |
+ say $req->url->to_abs->host; |
|
331 |
+ say $req->url->to_abs->path; |
|
334 | 332 |
|
335 | 333 |
=head1 METHODS |
336 | 334 |
|
... | ... |
@@ -353,7 +351,7 @@ Access request cookies, usually L<Mojo::Cookie::Request> objects. |
353 | 351 |
|
354 | 352 |
=head2 extract_start_line |
355 | 353 |
|
356 |
- my $success = $req->extract_start_line(\$string); |
|
354 |
+ my $success = $req->extract_start_line(\$str); |
|
357 | 355 |
|
358 | 356 |
Extract request line from string. |
359 | 357 |
|
... | ... |
@@ -129,7 +129,7 @@ sub get_start_line_chunk { |
129 | 129 |
sub is_empty { |
130 | 130 |
my $self = shift; |
131 | 131 |
return undef unless my $code = $self->code; |
132 |
- return $self->is_status_class(100) || grep { $_ eq $code } qw(204 304); |
|
132 |
+ return $self->is_status_class(100) || $code eq 204 || $code eq 304; |
|
133 | 133 |
} |
134 | 134 |
|
135 | 135 |
sub is_status_class { |
... | ... |
@@ -214,7 +214,7 @@ Generate default response message for code. |
214 | 214 |
|
215 | 215 |
=head2 extract_start_line |
216 | 216 |
|
217 |
- my $success = $req->extract_start_line(\$string); |
|
217 |
+ my $success = $res->extract_start_line(\$str); |
|
218 | 218 |
|
219 | 219 |
Extract status line from string. |
220 | 220 |
|
... | ... |
@@ -1,14 +1,14 @@ |
1 | 1 |
package Mojo::Parameters; |
2 | 2 |
use Mojo::Base -base; |
3 | 3 |
use overload |
4 |
+ '@{}' => sub { shift->params }, |
|
4 | 5 |
'bool' => sub {1}, |
5 | 6 |
'""' => sub { shift->to_string }, |
6 | 7 |
fallback => 1; |
7 | 8 |
|
8 | 9 |
use Mojo::Util qw(decode encode url_escape url_unescape); |
9 | 10 |
|
10 |
-has charset => 'UTF-8'; |
|
11 |
-has pair_separator => '&'; |
|
11 |
+has charset => 'UTF-8'; |
|
12 | 12 |
|
13 | 13 |
sub new { shift->SUPER::new->parse(@_) } |
14 | 14 |
|
... | ... |
@@ -33,8 +33,7 @@ sub append { |
33 | 33 |
sub clone { |
34 | 34 |
my $self = shift; |
35 | 35 |
|
36 |
- my $clone = Mojo::Parameters->new->charset($self->charset) |
|
37 |
- ->pair_separator($self->pair_separator); |
|
36 |
+ my $clone = $self->new->charset($self->charset); |
|
38 | 37 |
if (defined $self->{string}) { $clone->{string} = $self->{string} } |
39 | 38 |
else { $clone->params([@{$self->params}]) } |
40 | 39 |
|
... | ... |
@@ -78,18 +77,15 @@ sub params { |
78 | 77 |
} |
79 | 78 |
|
80 | 79 |
# Parse string |
81 |
- if (defined(my $string = delete $self->{string})) { |
|
80 |
+ if (defined(my $str = delete $self->{string})) { |
|
82 | 81 |
my $params = $self->{params} = []; |
83 |
- |
|
84 |
- # Detect pair separator for reconstruction |
|
85 |
- return $params unless length(defined $string ? $string : ''); |
|
86 |
- $self->pair_separator(';') if $string =~ /;/ && $string !~ /\&/; |
|
82 |
+ return $params unless length $str; |
|
87 | 83 |
|
88 | 84 |
# W3C suggests to also accept ";" as a separator |
89 | 85 |
my $charset = $self->charset; |
90 |
- for my $pair (split /[\&\;]+/, $string) { |
|
91 |
- $pair =~ /^([^=]*)(?:=(.*))?$/; |
|
92 |
- my $name = defined $1 ? $1 : ''; |
|
86 |
+ for my $pair (split /&|;/, $str) { |
|
87 |
+ next unless $pair =~ /^([^=]+)(?:=(.*))?$/; |
|
88 |
+ my $name = $1; |
|
93 | 89 |
my $value = defined $2 ? $2 : ''; |
94 | 90 |
|
95 | 91 |
# Replace "+" with whitespace, unescape and decode |
... | ... |
@@ -157,9 +153,9 @@ sub to_string { |
157 | 153 |
|
158 | 154 |
# String |
159 | 155 |
my $charset = $self->charset; |
160 |
- if (defined(my $string = $self->{string})) { |
|
161 |
- $string = encode $charset, $string if $charset; |
|
162 |
- return url_escape $string, '^A-Za-z0-9\-._~!$&\'()*+,;=%:@/?'; |
|
156 |
+ if (defined(my $str = $self->{string})) { |
|
157 |
+ $str = encode $charset, $str if $charset; |
|
158 |
+ return url_escape $str, '^A-Za-z0-9\-._~!$&\'()*+,;=%:@/?'; |
|
163 | 159 |
} |
164 | 160 |
|
165 | 161 |
# Build pairs |
... | ... |
@@ -170,23 +166,22 @@ sub to_string { |
170 | 166 |
my ($name, $value) = @{$params}[$i, $i + 1]; |
171 | 167 |
|
172 | 168 |
# Escape and replace whitespace with "+" |
173 |
- $name = encode $charset, $name if $charset; |
|
174 |
- $name = url_escape $name, '^A-Za-z0-9\-._~!$\'()*,:@/?'; |
|
175 |
- $name =~ s/\%20/\+/g; |
|
176 |
- if ($value) { |
|
177 |
- $value = encode $charset, $value if $charset; |
|
178 |
- $value = url_escape $value, '^A-Za-z0-9\-._~!$\'()*,:@/?'; |
|
179 |
- $value =~ s/\%20/\+/g; |
|
180 |
- } |
|
169 |
+ $name = encode $charset, $name if $charset; |
|
170 |
+ $name = url_escape $name, '^A-Za-z0-9\-._~!$\'()*,:@/?'; |
|
171 |
+ $value = encode $charset, $value if $charset; |
|
172 |
+ $value = url_escape $value, '^A-Za-z0-9\-._~!$\'()*,:@/?'; |
|
173 |
+ s/\%20/\+/g for $name, $value; |
|
181 | 174 |
|
182 |
- push @pairs, defined $value ? "$name=$value" : $name; |
|
175 |
+ push @pairs, "$name=$value"; |
|
183 | 176 |
} |
184 | 177 |
|
185 |
- return join $self->pair_separator, @pairs; |
|
178 |
+ return join '&', @pairs; |
|
186 | 179 |
} |
187 | 180 |
|
188 | 181 |
1; |
189 | 182 |
|
183 |
+=encoding utf8 |
|
184 |
+ |
|
190 | 185 |
=head1 NAME |
191 | 186 |
|
192 | 187 |
Mojo::Parameters - Parameters |
... | ... |
@@ -201,11 +196,12 @@ Mojo::Parameters - Parameters |
201 | 196 |
|
202 | 197 |
# Build |
203 | 198 |
my $params = Mojo::Parameters->new(foo => 'bar', baz => 23); |
199 |
+ push @$params, i => '♥ mojolicious'; |
|
204 | 200 |
say "$params"; |
205 | 201 |
|
206 | 202 |
=head1 DESCRIPTION |
207 | 203 |
|
208 |
-L<Mojo::Parameters> is a container for form parameters. |
|
204 |
+L<Mojo::Parameters> is a container for form parameters used by L<Mojo::URL>. |
|
209 | 205 |
|
210 | 206 |
=head1 ATTRIBUTES |
211 | 207 |
|
... | ... |
@@ -221,13 +217,6 @@ Charset used for encoding and decoding parameters, defaults to C<UTF-8>. |
221 | 217 |
# Disable encoding and decoding |
222 | 218 |
$params->charset(undef); |
223 | 219 |
|
224 |
-=head2 pair_separator |
|
225 |
- |
|
226 |
- my $separator = $params->pair_separator; |
|
227 |
- $params = $params->pair_separator(';'); |
|
228 |
- |
|
229 |
-Separator for parameter pairs, defaults to C<&>. |
|
230 |
- |
|
231 | 220 |
=head1 METHODS |
232 | 221 |
|
233 | 222 |
L<Mojo::Parameters> inherits all methods from L<Mojo::Base> and implements the |
... | ... |
@@ -241,7 +230,8 @@ following new ones. |
241 | 230 |
my $params = Mojo::Parameters->new(foo => ['ba;r', 'b;az']); |
242 | 231 |
my $params = Mojo::Parameters->new(foo => ['ba;r', 'b;az'], bar => 23); |
243 | 232 |
|
244 |
-Construct a new L<Mojo::Parameters> object. |
|
233 |
+Construct a new L<Mojo::Parameters> object and C<parse> parameters if |
|
234 |
+necessary. |
|
245 | 235 |
|
246 | 236 |
=head2 append |
247 | 237 |
|
... | ... |
@@ -249,7 +239,7 @@ Construct a new L<Mojo::Parameters> object. |
249 | 239 |
$params = $params->append(foo => ['ba;r', 'b;az']); |
250 | 240 |
$params = $params->append(foo => ['ba;r', 'b;az'], bar => 23); |
251 | 241 |
|
252 |
-Append parameters. |
|
242 |
+Append parameters. Note that this method will normalize the parameters. |
|
253 | 243 |
|
254 | 244 |
# "foo=bar&foo=baz" |
255 | 245 |
Mojo::Parameters->new('foo=bar')->append(foo => 'baz'); |
... | ... |
@@ -270,7 +260,8 @@ Clone parameters. |
270 | 260 |
|
271 | 261 |
$params = $params->merge(Mojo::Parameters->new(foo => 'b;ar', baz => 23)); |
272 | 262 |
|
273 |
-Merge L<Mojo::Parameters> objects. |
|
263 |
+Merge L<Mojo::Parameters> objects. Note that this method will normalize the |
|
264 |
+parameters. |
|
274 | 265 |
|
275 | 266 |
=head2 param |
276 | 267 |
|
... | ... |
@@ -280,14 +271,18 @@ Merge L<Mojo::Parameters> objects. |
280 | 271 |
my $foo = $params->param(foo => 'ba;r'); |
281 | 272 |
my @foo = $params->param(foo => qw(ba;r ba;z)); |
282 | 273 |
|
283 |
-Check and replace parameter values. |
|
274 |
+Check and replace parameter value. Be aware that if you request a parameter by |
|
275 |
+name in scalar context, you will receive only the I<first> value for that |
|
276 |
+parameter, if there are multiple values for that name. In list context you |
|
277 |
+will receive I<all> of the values for that name. Note that this method will |
|
278 |
+normalize the parameters. |
|
284 | 279 |
|
285 | 280 |
=head2 params |
286 | 281 |
|
287 | 282 |
my $array = $params->params; |
288 | 283 |
$params = $params->params([foo => 'b;ar', baz => 23]); |
289 | 284 |
|
290 |
-Parsed parameters. |
|
285 |
+Parsed parameters. Note that this method will normalize the parameters. |
|
291 | 286 |
|
292 | 287 |
=head2 parse |
293 | 288 |
|
... | ... |
@@ -299,7 +294,7 @@ Parse parameters. |
299 | 294 |
|
300 | 295 |
$params = $params->remove('foo'); |
301 | 296 |
|
302 |
-Remove parameters. |
|
297 |
+Remove parameters. Note that this method will normalize the parameters. |
|
303 | 298 |
|
304 | 299 |
# "bar=yada" |
305 | 300 |
Mojo::Parameters->new('foo=bar&foo=baz&bar=yada')->remove('foo'); |
... | ... |
@@ -308,18 +303,27 @@ Remove parameters. |
308 | 303 |
|
309 | 304 |
my $hash = $params->to_hash; |
310 | 305 |
|
311 |
-Turn parameters into a hash reference. |
|
306 |
+Turn parameters into a hash reference. Note that this method will normalize |
|
307 |
+the parameters. |
|
312 | 308 |
|
313 | 309 |
# "baz" |
314 | 310 |
Mojo::Parameters->new('foo=bar&foo=baz')->to_hash->{foo}[1]; |
315 | 311 |
|
316 | 312 |
=head2 to_string |
317 | 313 |
|
318 |
- my $string = $params->to_string; |
|
319 |
- my $string = "$params"; |
|
314 |
+ my $str = $params->to_string; |
|
315 |
+ my $str = "$params"; |
|
320 | 316 |
|
321 | 317 |
Turn parameters into a string. |
322 | 318 |
|
319 |
+=head1 PARAMETERS |
|
320 |
+ |
|
321 |
+Direct array reference access to the parsed parameters is also possible. Note |
|
322 |
+that this will normalize the parameters. |
|
323 |
+ |
|
324 |
+ say $params->[0]; |
|
325 |
+ say for @$params; |
|
326 |
+ |
|
323 | 327 |
=head1 SEE ALSO |
324 | 328 |
|
325 | 329 |
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
... | ... |
@@ -1,6 +1,7 @@ |
1 | 1 |
package Mojo::Path; |
2 | 2 |
use Mojo::Base -base; |
3 | 3 |
use overload |
4 |
+ '@{}' => sub { shift->parts }, |
|
4 | 5 |
'bool' => sub {1}, |
5 | 6 |
'""' => sub { shift->to_string }, |
6 | 7 |
fallback => 1; |
... | ... |
@@ -8,8 +9,6 @@ use overload |
8 | 9 |
use Mojo::Util qw(decode encode url_escape url_unescape); |
9 | 10 |
|
10 | 11 |
has charset => 'UTF-8'; |
11 |
-has [qw(leading_slash trailing_slash)]; |
|
12 |
-has parts => sub { [] }; |
|
13 | 12 |
|
14 | 13 |
sub new { shift->SUPER::new->parse(@_) } |
15 | 14 |
|
... | ... |
@@ -21,15 +20,11 @@ sub canonicalize { |
21 | 20 |
|
22 | 21 |
# ".." |
23 | 22 |
if ($part eq '..') { |
24 |
- unless (@parts && $parts[-1] ne '..') { push @parts, '..' } |
|
25 |
- else { pop @parts } |
|
26 |
- next; |
|
23 |
+ (@parts && $parts[-1] ne '..') ? pop @parts : push @parts, '..'; |
|
27 | 24 |
} |
28 | 25 |
|
29 |
- # "." |
|
30 |
- next if grep { $_ eq $part } '.', ''; |
|
31 |
- |
|
32 |
- push @parts, $part; |
|
26 |
+ # Something else than "." |
|
27 |
+ elsif ($part ne '.' && $part ne '') { push @parts, $part } |
|
33 | 28 |
} |
34 | 29 |
$self->trailing_slash(undef) unless @parts; |
35 | 30 |
|
... | ... |
@@ -37,25 +32,25 @@ sub canonicalize { |
37 | 32 |
} |
38 | 33 |
|
39 | 34 |
sub clone { |
40 |
- my $self = shift; |
|
41 |
- my $clone = Mojo::Path->new; |
|
42 |
- $clone->leading_slash($self->leading_slash); |
|
43 |
- $clone->trailing_slash($self->trailing_slash); |
|
44 |
- return $clone->charset($self->charset)->parts([@{$self->parts}]); |
|
35 |
+ my $self = shift; |
|
36 |
+ |
|
37 |
+ my $clone = $self->new->charset($self->charset); |
|
38 |
+ if (my $parts = $self->{parts}) { |
|
39 |
+ $clone->{$_} = $self->{$_} for qw(leading_slash trailing_slash); |
|
40 |
+ $clone->{parts} = [@$parts]; |
|
41 |
+ } |
|
42 |
+ else { $clone->{path} = $self->{path} } |
|
43 |
+ |
|
44 |
+ return $clone; |
|
45 | 45 |
} |
46 | 46 |
|
47 | 47 |
sub contains { |
48 | 48 |
my ($self, $path) = @_; |
49 |
- |
|
50 |
- my $parts = $self->new($path)->parts; |
|
51 |
- for my $part (@{$self->parts}) { |
|
52 |
- return 1 unless defined(my $try = shift @$parts); |
|
53 |
- return undef unless $part eq $try; |
|
54 |
- } |
|
55 |
- |
|
56 |
- return !@$parts; |
|
49 |
+ return $path eq '/' || $self->to_route =~ m!^\Q$path\E(?:/|$)!; |
|
57 | 50 |
} |
58 | 51 |
|
52 |
+sub leading_slash { shift->_parse(leading_slash => @_) } |
|
53 |
+ |
|
59 | 54 |
sub merge { |
60 | 55 |
my ($self, $path) = @_; |
61 | 56 |
|
... | ... |
@@ -70,19 +65,19 @@ sub merge { |
70 | 65 |
} |
71 | 66 |
|
72 | 67 |
sub parse { |
73 |
- my ($self, $path) = @_; |
|
68 |
+ my $self = shift; |
|
69 |
+ $self->{path} = shift; |
|
70 |
+ delete $self->{$_} for qw(leading_slash parts trailing_slash); |
|
71 |
+ return $self; |
|
72 |
+} |
|
74 | 73 |
|
75 |
- $path = url_unescape defined $path ? $path : ''; |
|
76 |
- my $charset = $self->charset; |
|
77 |
- $path = do { my $tmp = decode($charset, $path); defined $tmp ? $tmp : $path } if $charset; |
|
78 |
- $self->leading_slash($path =~ s!^/!! ? 1 : undef); |
|
79 |
- $self->trailing_slash($path =~ s!/$!! ? 1 : undef); |
|
74 |
+sub parts { shift->_parse(parts => @_) } |
|
80 | 75 |
|
81 |
- return $self->parts([split '/', $path, -1]); |
|
76 |
+sub to_abs_string { |
|
77 |
+ my $path = shift->to_string; |
|
78 |
+ return $path =~ m!^/! ? $path : "/$path"; |
|
82 | 79 |
} |
83 | 80 |
|
84 |
-sub to_abs_string { $_[0]->leading_slash ? "$_[0]" : "/$_[0]" } |
|
85 |
- |
|
86 | 81 |
sub to_dir { |
87 | 82 |
my $clone = shift->clone; |
88 | 83 |
pop @{$clone->parts} unless $clone->trailing_slash; |
... | ... |
@@ -90,24 +85,50 @@ sub to_dir { |
90 | 85 |
} |
91 | 86 |
|
92 | 87 |
sub to_route { |
93 |
- my $self = shift; |
|
94 |
- return '/' . join('/', @{$self->parts}) . ($self->trailing_slash ? '/' : ''); |
|
88 |
+ my $clone = shift->clone; |
|
89 |
+ my $route = join '/', @{$clone->parts}; |
|
90 |
+ return "/$route" . ($clone->trailing_slash ? '/' : ''); |
|
95 | 91 |
} |
96 | 92 |
|
97 | 93 |
sub to_string { |
98 | 94 |
my $self = shift; |
99 | 95 |
|
100 |
- my @parts = @{$self->parts}; |
|
96 |
+ # Path |
|
101 | 97 |
my $charset = $self->charset; |
98 |
+ if (defined(my $path = $self->{path})) { |
|
99 |
+ $path = encode $charset, $path if $charset; |
|
100 |
+ return url_escape $path, '^A-Za-z0-9\-._~!$&\'()*+,;=%:@/'; |
|
101 |
+ } |
|
102 |
+ |
|
103 |
+ # Build path |
|
104 |
+ my @parts = @{$self->parts}; |
|
102 | 105 |
@parts = map { encode $charset, $_ } @parts if $charset; |
103 | 106 |
my $path = join '/', |
104 | 107 |
map { url_escape $_, '^A-Za-z0-9\-._~!$&\'()*+,;=:@' } @parts; |
105 | 108 |
$path = "/$path" if $self->leading_slash; |
106 | 109 |
$path = "$path/" if $self->trailing_slash; |
107 |
- |
|
108 | 110 |
return $path; |
109 | 111 |
} |
110 | 112 |
|
113 |
+sub trailing_slash { shift->_parse(trailing_slash => @_) } |
|
114 |
+ |
|
115 |
+sub _parse { |
|
116 |
+ my ($self, $name) = (shift, shift); |
|
117 |
+ |
|
118 |
+ unless ($self->{parts}) { |
|
119 |
+ my $path = url_unescape do { my $tmp = delete($self->{path}); defined $tmp ? $tmp : '' }; |
|
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]; |
|
125 |
+ } |
|
126 |
+ |
|
127 |
+ return $self->{$name} unless @_; |
|
128 |
+ $self->{$name} = shift; |
|
129 |
+ return $self; |
|
130 |
+} |
|
131 |
+ |
|
111 | 132 |
1; |
112 | 133 |
|
113 | 134 |
=encoding utf8 |
... | ... |
@@ -120,13 +141,18 @@ Mojo::Path - Path |
120 | 141 |
|
121 | 142 |
use Mojo::Path; |
122 | 143 |
|
144 |
+ # Parse |
|
123 | 145 |
my $path = Mojo::Path->new('/foo%2Fbar%3B/baz.html'); |
124 |
- shift @{$path->parts}; |
|
146 |
+ say $path->[0]; |
|
147 |
+ |
|
148 |
+ # Build |
|
149 |
+ my $path = Mojo::Path->new('/i/♥'); |
|
150 |
+ push @$path, 'mojolicious'; |
|
125 | 151 |
say "$path"; |
126 | 152 |
|
127 | 153 |
=head1 DESCRIPTION |
128 | 154 |
|
129 |
-L<Mojo::Path> is a container for URL paths. |
|
155 |
+L<Mojo::Path> is a container for paths used by L<Mojo::URL>. |
|
130 | 156 |
|
131 | 157 |
=head1 ATTRIBUTES |
132 | 158 |
|
... | ... |
@@ -142,30 +168,6 @@ Charset used for encoding and decoding, defaults to C<UTF-8>. |
142 | 168 |
# Disable encoding and decoding |
143 | 169 |
$path->charset(undef); |
144 | 170 |
|
145 |
-=head2 leading_slash |
|
146 |
- |
|
147 |
- my $leading_slash = $path->leading_slash; |
|
148 |
- $path = $path->leading_slash(1); |
|
149 |
- |
|
150 |
-Path has a leading slash. |
|
151 |
- |
|
152 |
-=head2 parts |
|
153 |
- |
|
154 |
- my $parts = $path->parts; |
|
155 |
- $path = $path->parts([qw(foo bar baz)]); |
|
156 |
- |
|
157 |
-The path parts. |
|
158 |
- |
|
159 |
- # Part with slash |
|
160 |
- push @{$path->parts}, 'foo/bar'; |
|
161 |
- |
|
162 |
-=head2 trailing_slash |
|
163 |
- |
|
164 |
- my $trailing_slash = $path->trailing_slash; |
|
165 |
- $path = $path->trailing_slash(1); |
|
166 |
- |
|
167 |
-Path has a trailing slash. |
|
168 |
- |
|
169 | 171 |
=head1 METHODS |
170 | 172 |
|
171 | 173 |
L<Mojo::Path> inherits all methods from L<Mojo::Base> and implements the |
... | ... |
@@ -176,7 +178,7 @@ following new ones. |
176 | 178 |
my $path = Mojo::Path->new; |
177 | 179 |
my $path = Mojo::Path->new('/foo%2Fbar%3B/baz.html'); |
178 | 180 |
|
179 |
-Construct a new L<Mojo::Path> object. |
|
181 |
+Construct a new L<Mojo::Path> object and C<parse> path if necessary. |
|
180 | 182 |
|
181 | 183 |
=head2 canonicalize |
182 | 184 |
|
... | ... |
@@ -185,7 +187,7 @@ Construct a new L<Mojo::Path> object. |
185 | 187 |
Canonicalize path. |
186 | 188 |
|
187 | 189 |
# "/foo/baz" |
188 |
- Mojo::Path->new('/foo/bar/../baz')->canonicalize; |
|
190 |
+ Mojo::Path->new('/foo/./bar/../baz')->canonicalize; |
|
189 | 191 |
|
190 | 192 |
=head2 clone |
191 | 193 |
|
... | ... |
@@ -209,13 +211,22 @@ Check if path contains given prefix. |
209 | 211 |
Mojo::Path->new('/foo/bar')->contains('/bar'); |
210 | 212 |
Mojo::Path->new('/foo/bar')->contains('/whatever'); |
211 | 213 |
|
214 |
+=head2 leading_slash |
|
215 |
+ |
|
216 |
+ my $slash = $path->leading_slash; |
|
217 |
+ $path = $path->leading_slash(1); |
|
218 |
+ |
|
219 |
+Path has a leading slash. Note that this method will normalize the path and |
|
220 |
+that C<%2F> will be treated as C</> for security reasons. |
|
221 |
+ |
|
212 | 222 |
=head2 merge |
213 | 223 |
|
214 | 224 |
$path = $path->merge('/foo/bar'); |
215 | 225 |
$path = $path->merge('foo/bar'); |
216 | 226 |
$path = $path->merge(Mojo::Path->new('foo/bar')); |
217 | 227 |
|
218 |
-Merge paths. |
|
228 |
+Merge paths. Note that this method will normalize both paths if necessary and |
|
229 |
+that C<%2F> will be treated as C</> for security reasons. |
|
219 | 230 |
|
220 | 231 |
# "/baz/yada" |
221 | 232 |
Mojo::Path->new('/foo/bar')->merge('/baz/yada'); |
... | ... |
@@ -230,17 +241,29 @@ Merge paths. |
230 | 241 |
|
231 | 242 |
$path = $path->parse('/foo%2Fbar%3B/baz.html'); |
232 | 243 |
|
233 |
-Parse path. Note that C<%2F> will be treated as C</> for security reasons. |
|
244 |
+Parse path. |
|
234 | 245 |
|
235 | 246 |
=head2 to_abs_string |
236 | 247 |
|
237 |
- my $string = $path->to_abs_string; |
|
248 |
+ my $str = $path->to_abs_string; |
|
238 | 249 |
|
239 | 250 |
Turn path into an absolute string. |
240 | 251 |
|
241 | 252 |
# "/i/%E2%99%A5/mojolicious" |
253 |
+ Mojo::Path->new('/i/%E2%99%A5/mojolicious')->to_abs_string; |
|
242 | 254 |
Mojo::Path->new('i/%E2%99%A5/mojolicious')->to_abs_string; |
243 | 255 |
|
256 |
+=head2 parts |
|
257 |
+ |
|
258 |
+ my $parts = $path->parts; |
|
259 |
+ $path = $path->parts([qw(foo bar baz)]); |
|
260 |
+ |
|
261 |
+The path parts. Note that this method will normalize the path and that C<%2F> |
|
262 |
+will be treated as C</> for security reasons. |
|
263 |
+ |
|
264 |
+ # Part with slash |
|
265 |
+ push @{$path->parts}, 'foo/bar'; |
|
266 |
+ |
|
244 | 267 |
=head2 to_dir |
245 | 268 |
|
246 | 269 |
my $dir = $route->to_dir; |
... | ... |
@@ -248,6 +271,9 @@ Turn path into an absolute string. |
248 | 271 |
Clone path and remove everything after the right-most slash. |
249 | 272 |
|
250 | 273 |
# "/i/%E2%99%A5/" |
274 |
+ Mojo::Path->new('/i/%E2%99%A5/mojolicious')->to_dir->to_abs_string; |
|
275 |
+ |
|
276 |
+ # "i/%E2%99%A5/" |
|
251 | 277 |
Mojo::Path->new('i/%E2%99%A5/mojolicious')->to_dir->to_abs_string; |
252 | 278 |
|
253 | 279 |
=head2 to_route |
... | ... |
@@ -257,18 +283,39 @@ Clone path and remove everything after the right-most slash. |
257 | 283 |
Turn path into a route. |
258 | 284 |
|
259 | 285 |
# "/i/♥/mojolicious" |
286 |
+ Mojo::Path->new('/i/%E2%99%A5/mojolicious')->to_route; |
|
260 | 287 |
Mojo::Path->new('i/%E2%99%A5/mojolicious')->to_route; |
261 | 288 |
|
262 | 289 |
=head2 to_string |
263 | 290 |
|
264 |
- my $string = $path->to_string; |
|
265 |
- my $string = "$path"; |
|
291 |
+ my $str = $path->to_string; |
|
292 |
+ my $str = "$path"; |
|
266 | 293 |
|
267 | 294 |
Turn path into a string. |
268 | 295 |
|
296 |
+ # "/i/%E2%99%A5/mojolicious" |
|
297 |
+ Mojo::Path->new('/i/%E2%99%A5/mojolicious')->to_string; |
|
298 |
+ |
|
269 | 299 |
# "i/%E2%99%A5/mojolicious" |
270 | 300 |
Mojo::Path->new('i/%E2%99%A5/mojolicious')->to_string; |
271 | 301 |
|
302 |
+=head2 trailing_slash |
|
303 |
+ |
|
304 |
+ my $slash = $path->trailing_slash; |
|
305 |
+ $path = $path->trailing_slash(1); |
|
306 |
+ |
|
307 |
+Path has a trailing slash. Note that this method will normalize the path and |
|
308 |
+that C<%2F> will be treated as C</> for security reasons. |
|
309 |
+ |
|
310 |
+=head1 PATH PARTS |
|
311 |
+ |
|
312 |
+Direct array reference access to path parts is also possible. Note that this |
|
313 |
+will normalize the path and that C<%2F> will be treated as C</> for security |
|
314 |
+reasons. |
|
315 |
+ |
|
316 |
+ say $path->[0]; |
|
317 |
+ say for @$path; |
|
318 |
+ |
|
272 | 319 |
=head1 SEE ALSO |
273 | 320 |
|
274 | 321 |
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
... | ... |
@@ -5,6 +5,8 @@ use Carp 'croak'; |
5 | 5 |
use IO::Poll qw(POLLERR POLLHUP POLLIN); |
6 | 6 |
use Mojo::Loader; |
7 | 7 |
|
8 |
+sub again { croak 'Method "again" not implemented by subclass' } |
|
9 |
+ |
|
8 | 10 |
sub detect { |
9 | 11 |
my $try = $ENV{MOJO_REACTOR} || 'Mojo::Reactor::EV'; |
10 | 12 |
return Mojo::Loader->new->load($try) ? 'Mojo::Reactor::Poll' : $try; |
... | ... |
@@ -46,6 +48,7 @@ Mojo::Reactor - Low level event reactor base class |
46 | 48 |
|
47 | 49 |
$ENV{MOJO_REACTOR} ||= 'Mojo::Reactor::MyEventLoop'; |
48 | 50 |
|
51 |
+ sub again {...} |
|
49 | 52 |
sub io {...} |
50 | 53 |
sub is_running {...} |
51 | 54 |
sub one_tick {...} |
... | ... |
@@ -84,12 +87,18 @@ Emitted safely for exceptions caught in callbacks. |
84 | 87 |
L<Mojo::Reactor> inherits all methods from L<Mojo::EventEmitter> and |
85 | 88 |
implements the following new ones. |
86 | 89 |
|
90 |
+=head2 again |
|
91 |
+ |
|
92 |
+ $reactor->again($id); |
|
93 |
+ |
|
94 |
+Restart active timer. Meant to be overloaded in a subclass. |
|
95 |
+ |
|
87 | 96 |
=head2 detect |
88 | 97 |
|
89 | 98 |
my $class = Mojo::Reactor->detect; |
90 | 99 |
|
91 | 100 |
Detect and load the best reactor implementation available, will try the value |
92 |
-of the C<MOJO_REACTOR> environment variable, L<Mojo::Reactor::EV> or |
|
101 |
+of the MOJO_REACTOR environment variable, L<Mojo::Reactor::EV> or |
|
93 | 102 |
L<Mojo::Reactor::Poll>. |
94 | 103 |
|
95 | 104 |
# Instantiate best reactor implementation available |
... | ... |
@@ -13,6 +13,8 @@ sub DESTROY { undef $EV } |
13 | 13 |
# We have to fall back to Mojo::Reactor::Poll, since EV is unique |
14 | 14 |
sub new { $EV++ ? Mojo::Reactor::Poll->new : shift->SUPER::new } |
15 | 15 |
|
16 |
+sub again { shift->{timers}{shift()}{watcher}->again } |
|
17 |
+ |
|
16 | 18 |
sub is_running { !!EV::depth } |
17 | 19 |
|
18 | 20 |
sub one_tick { EV::run(EV::RUN_ONCE) } |
... | ... |
@@ -59,7 +61,7 @@ sub _timer { |
59 | 61 |
my $id = $self->SUPER::_timer(0, 0, $cb); |
60 | 62 |
weaken $self; |
61 | 63 |
$self->{timers}{$id}{watcher} = EV::timer( |
62 |
- $after => ($recurring ? $after : 0) => sub { |
|
64 |
+ $after => $after => sub { |
|
63 | 65 |
$self->_sandbox("Timer $id", $self->{timers}{$id}{cb}); |
64 | 66 |
delete $self->{timers}{$id} unless $recurring; |
65 | 67 |
} |
... | ... |
@@ -117,6 +119,12 @@ implements the following new ones. |
117 | 119 |
|
118 | 120 |
Construct a new L<Mojo::Reactor::EV> object. |
119 | 121 |
|
122 |
+=head2 again |
|
123 |
+ |
|
124 |
+ $reactor->again($id); |
|
125 |
+ |
|
126 |
+Restart active timer. |
|
127 |
+ |
|
120 | 128 |
=head2 is_running |
121 | 129 |
|
122 | 130 |
my $success = $reactor->is_running; |
... | ... |
@@ -3,8 +3,13 @@ use Mojo::Base 'Mojo::Reactor'; |
3 | 3 |
|
4 | 4 |
use IO::Poll qw(POLLERR POLLHUP POLLIN POLLOUT); |
5 | 5 |
use List::Util 'min'; |
6 |
-use Mojo::Util 'md5_sum'; |
|
7 |
-use Time::HiRes qw(time usleep); |
|
6 |
+use Mojo::Util qw(md5_sum steady_time); |
|
7 |
+use Time::HiRes 'usleep'; |
|
8 |
+ |
|
9 |
+sub again { |
|
10 |
+ my $timer = shift->{timers}{shift()}; |
|
11 |
+ $timer->{time} = steady_time + $timer->{after}; |
|
12 |
+} |
|
8 | 13 |
|
9 | 14 |
sub io { |
10 | 15 |
my ($self, $handle, $cb) = @_; |
... | ... |
@@ -31,7 +36,7 @@ sub one_tick { |
31 | 36 |
|
32 | 37 |
# Calculate ideal timeout based on timers |
33 | 38 |
my $min = min map { $_->{time} } values %{$self->{timers}}; |
34 |
- my $timeout = defined $min ? ($min - time) : 0.5; |
|
39 |
+ my $timeout = defined $min ? ($min - steady_time) : 0.5; |
|
35 | 40 |
$timeout = 0 if $timeout < 0; |
36 | 41 |
|
37 | 42 |
# I/O |
... | ... |
@@ -46,12 +51,14 @@ sub one_tick { |
46 | 51 |
# Wait for timeout if poll can't be used |
47 | 52 |
elsif ($timeout) { usleep $timeout * 1000000 } |
48 | 53 |
|
49 |
- # Timers |
|
50 |
- while (my ($id, $t) = each %{$self->{timers} || {}}) { |
|
51 |
- next unless $t->{time} <= (my $time = time); |
|
54 |
+ # Timers (time should not change in between timers) |
|
55 |
+ my $now = steady_time; |
|
56 |
+ for my $id (keys %{$self->{timers}}) { |
|
57 |
+ next unless my $t = $self->{timers}{$id}; |
|
58 |
+ next unless $t->{time} <= $now; |
|
52 | 59 |
|
53 | 60 |
# Recurring timer |
54 |
- if (exists $t->{recurring}) { $t->{time} = $time + $t->{recurring} } |
|
61 |
+ if (exists $t->{recurring}) { $t->{time} = $now + $t->{recurring} } |
|
55 | 62 |
|
56 | 63 |
# Normal timer |
57 | 64 |
else { $self->remove($id) } |
... | ... |
@@ -105,10 +112,12 @@ sub _sandbox { |
105 | 112 |
sub _timer { |
106 | 113 |
my ($self, $recurring, $after, $cb) = @_; |
107 | 114 |
|
115 |
+ my $timers = $self->{timers} = defined $self->{timers} ? $self->{timers} : {}; |
|
108 | 116 |
my $id; |
109 |
- do { $id = md5_sum('t' . time . rand 999) } while $self->{timers}{$id}; |
|
110 |
- my $t = $self->{timers}{$id} = {cb => $cb, time => time + $after}; |
|
111 |
- $t->{recurring} = $after if $recurring; |
|
117 |
+ do { $id = md5_sum('t' . steady_time . rand 999) } while $timers->{$id}; |
|
118 |
+ my $timer = $timers->{$id} |
|
119 |
+ = {cb => $cb, after => $after, time => steady_time + $after}; |
|
120 |
+ $timer->{recurring} = $after if $recurring; |
|
112 | 121 |
|
113 | 122 |
return $id; |
114 | 123 |
} |
... | ... |
@@ -145,9 +154,7 @@ Mojo::Reactor::Poll - Low level event reactor with poll support |
145 | 154 |
|
146 | 155 |
=head1 DESCRIPTION |
147 | 156 |
|
148 |
-L<Mojo::Reactor::Poll> is a low level event reactor based on L<IO::Poll>. Note |
|
149 |
-that this reactor was designed for maximum portability, and therefore does not |
|
150 |
-use a monotonic clock to handle time jumps. |
|
157 |
+L<Mojo::Reactor::Poll> is a low level event reactor based on L<IO::Poll>. |
|
151 | 158 |
|
152 | 159 |
=head1 EVENTS |
153 | 160 |
|
... | ... |
@@ -158,6 +165,12 @@ L<Mojo::Reactor::Poll> inherits all events from L<Mojo::Reactor>. |
158 | 165 |
L<Mojo::Reactor::Poll> inherits all methods from L<Mojo::Reactor> and |
159 | 166 |
implements the following new ones. |
160 | 167 |
|
168 |
+=head2 again |
|
169 |
+ |
|
170 |
+ $reactor->again($id); |
|
171 |
+ |
|
172 |
+Restart active timer. |
|
173 |
+ |
|
161 | 174 |
=head2 io |
162 | 175 |
|
163 | 176 |
$reactor = $reactor->io($handle => sub {...}); |
... | ... |
@@ -35,7 +35,7 @@ sub load_app { |
35 | 35 |
local $ENV{MOJO_EXE}; |
36 | 36 |
|
37 | 37 |
# Try to load application from script into sandbox |
38 |
- $self->app(my $app = eval sprintf <<'EOF', md5_sum($path . $$)); |
|
38 |
+ my $app = eval sprintf <<'EOF', md5_sum($path . $$); |
|
39 | 39 |
package Mojo::Server::SandBox::%s; |
40 | 40 |
my $app = do $path; |
41 | 41 |
if (!$app && (my $e = $@ || $!)) { die $e } |
... | ... |
@@ -44,6 +44,7 @@ EOF |
44 | 44 |
die qq{Couldn't load application from file "$path": $@} if !$app && $@; |
45 | 45 |
die qq{File "$path" did not return an application object.\n} |
46 | 46 |
unless blessed $app && $app->isa('Mojo'); |
47 |
+ $self->app($app); |
|
47 | 48 |
}; |
48 | 49 |
FindBin->again; |
49 | 50 |
|
... | ... |
@@ -34,7 +34,7 @@ sub run { |
34 | 34 |
return undef unless _write($res, 'get_header_chunk'); |
35 | 35 |
|
36 | 36 |
# Response body |
37 |
- return undef unless _write($res, 'get_body_chunk'); |
|
37 |
+ $tx->is_empty or _write($res, 'get_body_chunk') or return undef; |
|
38 | 38 |
|
39 | 39 |
# Finish transaction |
40 | 40 |
$tx->server_close; |
... | ... |
@@ -112,7 +112,7 @@ implements the following new ones. |
112 | 112 |
my $nph = $cgi->nph; |
113 | 113 |
$cgi = $cgi->nph(1); |
114 | 114 |
|
115 |
-Activate non parsed header mode. |
|
115 |
+Activate non-parsed header mode. |
|
116 | 116 |
|
117 | 117 |
=head1 METHODS |
118 | 118 |
|
... | ... |
@@ -108,9 +108,7 @@ sub _build_tx { |
108 | 108 |
); |
109 | 109 |
|
110 | 110 |
# Kept alive if we have more than one request on the connection |
111 |
- $tx->kept_alive(1) if ++$c->{requests} > 1; |
|
112 |
- |
|
113 |
- return $tx; |
|
111 |
+ return ++$c->{requests} > 1 ? $tx->kept_alive(1) : $tx; |
|
114 | 112 |
} |
115 | 113 |
|
116 | 114 |
sub _close { |
... | ... |
@@ -153,9 +151,9 @@ sub _finish { |
153 | 151 |
return $self->_remove($id) if $req->error || !$tx->keep_alive; |
154 | 152 |
|
155 | 153 |
# Build new transaction for leftovers |
156 |
- return unless $req->has_leftovers; |
|
154 |
+ return unless length(my $leftovers = $req->content->leftovers); |
|
157 | 155 |
$tx = $c->{tx} = $self->_build_tx($id, $c); |
158 |
- $tx->server_read($req->leftovers); |
|
156 |
+ $tx->server_read($leftovers); |
|
159 | 157 |
} |
160 | 158 |
|
161 | 159 |
sub _listen { |
... | ... |
@@ -210,12 +208,12 @@ sub _read { |
210 | 208 |
my ($self, $id, $chunk) = @_; |
211 | 209 |
|
212 | 210 |
# Make sure we have a transaction and parse chunk |
213 |
- my $c = $self->{connections}{$id}; |
|
211 |
+ return unless my $c = $self->{connections}{$id}; |
|
214 | 212 |
my $tx = $c->{tx} ||= $self->_build_tx($id, $c); |
215 | 213 |
warn "-- Server <<< Client (@{[$tx->req->url->to_abs]})\n$chunk\n" if DEBUG; |
216 | 214 |
$tx->server_read($chunk); |
217 | 215 |
|
218 |
- # Last keep alive request or corrupted connection |
|
216 |
+ # Last keep-alive request or corrupted connection |
|
219 | 217 |
$tx->res->headers->connection('close') |
220 | 218 |
if (($c->{requests} || 0) >= $self->max_requests) || $tx->req->error; |
221 | 219 |
|
... | ... |
@@ -234,7 +232,7 @@ sub _write { |
234 | 232 |
my ($self, $id) = @_; |
235 | 233 |
|
236 | 234 |
# Not writing |
237 |
- my $c = $self->{connections}{$id}; |
|
235 |
+ return unless my $c = $self->{connections}{$id}; |
|
238 | 236 |
return unless my $tx = $c->{tx}; |
239 | 237 |
return unless $tx->is_writing; |
240 | 238 |
|
... | ... |
@@ -257,7 +255,7 @@ sub _write { |
257 | 255 |
return unless $c->{tx}; |
258 | 256 |
} |
259 | 257 |
} |
260 |
- $stream->write('', $cb); |
|
258 |
+ $stream->write('' => $cb); |
|
261 | 259 |
} |
262 | 260 |
|
263 | 261 |
1; |
... | ... |
@@ -292,13 +290,14 @@ Mojo::Server::Daemon - Non-blocking I/O HTTP and WebSocket server |
292 | 290 |
=head1 DESCRIPTION |
293 | 291 |
|
294 | 292 |
L<Mojo::Server::Daemon> is a full featured, highly portable non-blocking I/O |
295 |
-HTTP and WebSocket server, with C<IPv6>, C<TLS>, C<Comet> (long polling) and |
|
296 |
-multiple event loop support. |
|
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. |
|
297 | 296 |
|
298 | 297 |
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
299 | 298 |
L<IO::Socket::SSL> (1.75+) are supported transparently through |
300 | 299 |
L<Mojo::IOLoop>, and used if installed. Individual features can also be |
301 |
-disabled with the C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables. |
|
300 |
+disabled with the MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
302 | 301 |
|
303 | 302 |
See L<Mojolicious::Guides::Cookbook> for more. |
304 | 303 |
|
... | ... |
@@ -331,7 +330,7 @@ Group for server process. |
331 | 330 |
$daemon = $daemon->inactivity_timeout(5); |
332 | 331 |
|
333 | 332 |
Maximum amount of time in seconds a connection can be inactive before getting |
334 |
-closed, defaults to the value of the C<MOJO_INACTIVITY_TIMEOUT> environment |
|
333 |
+closed, defaults to the value of the MOJO_INACTIVITY_TIMEOUT environment |
|
335 | 334 |
variable or C<15>. Setting the value to C<0> will allow connections to be |
336 | 335 |
inactive indefinitely. |
337 | 336 |
|
... | ... |
@@ -349,7 +348,7 @@ L<Mojo::IOLoop> singleton. |
349 | 348 |
$daemon = $daemon->listen(['https://localhost:3000']); |
350 | 349 |
|
351 | 350 |
List of one or more locations to listen on, defaults to the value of the |
352 |
-C<MOJO_LISTEN> environment variable or C<http://*:3000>. |
|
351 |
+MOJO_LISTEN environment variable or C<http://*:3000>. |
|
353 | 352 |
|
354 | 353 |
# Listen on IPv6 interface |
355 | 354 |
$daemon->listen(['http://[::1]:4000']); |
... | ... |
@@ -398,7 +397,7 @@ Maximum number of parallel client connections, defaults to C<1000>. |
398 | 397 |
my $max = $daemon->max_requests; |
399 | 398 |
$daemon = $daemon->max_requests(100); |
400 | 399 |
|
401 |
-Maximum number of keep alive requests per connection, defaults to C<25>. |
|
400 |
+Maximum number of keep-alive requests per connection, defaults to C<25>. |
|
402 | 401 |
|
403 | 402 |
=head2 silent |
404 | 403 |
|
... | ... |
@@ -445,7 +444,7 @@ Stop accepting connections. |
445 | 444 |
|
446 | 445 |
=head1 DEBUGGING |
447 | 446 |
|
448 |
-You can set the C<MOJO_DAEMON_DEBUG> environment variable to get some advanced |
|
447 |
+You can set the MOJO_DAEMON_DEBUG environment variable to get some advanced |
|
449 | 448 |
diagnostics information printed to C<STDERR>. |
450 | 449 |
|
451 | 450 |
MOJO_DAEMON_DEBUG=1 |
... | ... |
@@ -7,6 +7,7 @@ use Cwd 'abs_path'; |
7 | 7 |
use File::Basename 'dirname'; |
8 | 8 |
use File::Spec::Functions 'catfile'; |
9 | 9 |
use Mojo::Server::Prefork; |
10 |
+use Mojo::Util 'steady_time'; |
|
10 | 11 |
use POSIX 'setsid'; |
11 | 12 |
use Scalar::Util 'weaken'; |
12 | 13 |
|
... | ... |
@@ -62,7 +63,7 @@ sub run { |
62 | 63 |
} |
63 | 64 |
|
64 | 65 |
# Start accepting connections |
65 |
- local $SIG{USR2} = sub { $self->{upgrade} ||= time }; |
|
66 |
+ local $SIG{USR2} = sub { $self->{upgrade} ||= steady_time }; |
|
66 | 67 |
$prefork->run; |
67 | 68 |
} |
68 | 69 |
|
... | ... |
@@ -123,7 +124,7 @@ sub _manage { |
123 | 124 |
|
124 | 125 |
# Timeout |
125 | 126 |
kill 'KILL', $self->{new} |
126 |
- if $self->{upgrade} + $self->{upgrade_timeout} <= time; |
|
127 |
+ if $self->{upgrade} + $self->{upgrade_timeout} <= steady_time; |
|
127 | 128 |
} |
128 | 129 |
} |
129 | 130 |
|
... | ... |
@@ -161,9 +162,10 @@ Mojo::Server::Hypnotoad - ALL GLORY TO THE HYPNOTOAD! |
161 | 162 |
L<Mojo::Server::Hypnotoad> is a full featured, UNIX optimized, preforking |
162 | 163 |
non-blocking I/O HTTP and WebSocket server, built around the very well tested |
163 | 164 |
and reliable L<Mojo::Server::Prefork>, with C<IPv6>, C<TLS>, C<Comet> (long |
164 |
-polling), multiple event loop and hot deployment support that just works. Note |
|
165 |
-that the server uses signals for process management, so you should avoid |
|
166 |
-modifying signal handlers in your applications. |
|
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. |
|
167 | 169 |
|
168 | 170 |
To start applications with it you can use the L<hypnotoad> script. |
169 | 171 |
|
... | ... |
@@ -181,7 +183,7 @@ C<production> mode. |
181 | 183 |
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
182 | 184 |
L<IO::Socket::SSL> (1.75+) are supported transparently through |
183 | 185 |
L<Mojo::IOLoop>, and used if installed. Individual features can also be |
184 |
-disabled with the C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables. |
|
186 |
+disabled with the MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
185 | 187 |
|
186 | 188 |
See L<Mojolicious::Guides::Cookbook> for more. |
187 | 189 |
|
... | ... |
@@ -254,9 +256,9 @@ L<Mojolicious::Guides::Cookbook/"Hypnotoad"> for examples. |
254 | 256 |
|
255 | 257 |
accept_interval => 0.5 |
256 | 258 |
|
257 |
-Interval in seconds for trying to reacquire the accept mutex and connection |
|
258 |
-management, defaults to C<0.025>. Note that changing this value can affect |
|
259 |
-performance and idle CPU usage. |
|
259 |
+Interval in seconds for trying to reacquire the accept mutex, defaults to |
|
260 |
+C<0.025>. Note that changing this value can affect performance and idle CPU |
|
261 |
+usage. |
|
260 | 262 |
|
261 | 263 |
=head2 accepts |
262 | 264 |
|
... | ... |
@@ -321,7 +323,7 @@ be inactive indefinitely. |
321 | 323 |
|
322 | 324 |
keep_alive_requests => 50 |
323 | 325 |
|
324 |
-Number of keep alive requests per connection, defaults to C<25>. |
|
326 |
+Number of keep-alive requests per connection, defaults to C<25>. |
|
325 | 327 |
|
326 | 328 |
=head2 listen |
327 | 329 |
|
... | ... |
@@ -339,10 +341,11 @@ appended, defaults to a random temporary path. |
339 | 341 |
|
340 | 342 |
=head2 lock_timeout |
341 | 343 |
|
342 |
- lock_timeout => 1 |
|
344 |
+ lock_timeout => 0.5 |
|
343 | 345 |
|
344 | 346 |
Maximum amount of time in seconds a worker may block when waiting for the |
345 |
-accept mutex, defaults to C<0.5>. |
|
347 |
+accept mutex, defaults to C<1>. Note that changing this value can affect |
|
348 |
+performance and idle CPU usage. |
|
346 | 349 |
|
347 | 350 |
=head2 multi_accept |
348 | 351 |
|
... | ... |
@@ -364,7 +367,7 @@ the server has been stopped. |
364 | 367 |
|
365 | 368 |
Activate reverse proxy support, which allows for the C<X-Forwarded-For> and |
366 | 369 |
C<X-Forwarded-HTTPS> headers to be picked up automatically, defaults to the |
367 |
-value of the C<MOJO_REVERSE_PROXY> environment variable. |
|
370 |
+value of the MOJO_REVERSE_PROXY environment variable. |
|
368 | 371 |
|
369 | 372 |
=head2 upgrade_timeout |
370 | 373 |
|
... | ... |
@@ -25,7 +25,7 @@ sub check_file { |
25 | 25 |
sub run { |
26 | 26 |
my ($self, $app) = @_; |
27 | 27 |
|
28 |
- # Prepare environment |
|
28 |
+ # Clean manager environment |
|
29 | 29 |
local $SIG{CHLD} = sub { $self->_reap }; |
30 | 30 |
local $SIG{INT} = local $SIG{TERM} = local $SIG{QUIT} = sub { |
31 | 31 |
$self->{finished} = 1; |
... | ... |
@@ -37,7 +37,6 @@ sub run { |
37 | 37 |
# Prepare and cache listen sockets for smooth restarting |
38 | 38 |
my $daemon = Mojo::Server::Daemon->new(silent => 1)->start->stop; |
39 | 39 |
|
40 |
- # Watch files and manage worker |
|
41 | 40 |
$self->_manage while !$self->{finished} || $self->{running}; |
42 | 41 |
exit 0; |
43 | 42 |
} |
... | ... |
@@ -116,8 +115,10 @@ Mojo::Server::Morbo - DOOOOOOOOOOOOOOOOOOM! |
116 | 115 |
|
117 | 116 |
L<Mojo::Server::Morbo> is a full featured, self-restart capable non-blocking |
118 | 117 |
I/O HTTP and WebSocket server, built around the very well tested and reliable |
119 |
-L<Mojo::Server::Daemon>, with C<IPv6>, C<TLS>, C<Comet> (long polling) and |
|
120 |
-multiple event loop support. |
|
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. |
|
121 | 122 |
|
122 | 123 |
To start applications with it you can use the L<morbo> script. |
123 | 124 |
|
... | ... |
@@ -127,7 +128,7 @@ To start applications with it you can use the L<morbo> script. |
127 | 128 |
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
128 | 129 |
L<IO::Socket::SSL> (1.75+) are supported transparently through |
129 | 130 |
L<Mojo::IOLoop>, and used if installed. Individual features can also be |
130 |
-disabled with the C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables. |
|
131 |
+disabled with the MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
131 | 132 |
|
132 | 133 |
See L<Mojolicious::Guides::Cookbook> for more. |
133 | 134 |
|
... | ... |
@@ -29,8 +29,8 @@ sub run { |
29 | 29 |
} |
30 | 30 |
|
31 | 31 |
# PSGI response |
32 |
- return [$res->code || 404, |
|
33 |
- \@headers, Mojo::Server::PSGI::_IO->new(tx => $tx)]; |
|
32 |
+ my $io = Mojo::Server::PSGI::_IO->new(tx => $tx, empty => $tx->is_empty); |
|
33 |
+ return [$res->code || 404, \@headers, $io]; |
|
34 | 34 |
} |
35 | 35 |
|
36 | 36 |
sub to_psgi_app { |
... | ... |
@@ -50,6 +50,9 @@ sub close { shift->{tx}->server_close } |
50 | 50 |
sub getline { |
51 | 51 |
my $self = shift; |
52 | 52 |
|
53 |
+ # Empty |
|
54 |
+ return undef if $self->{empty}; |
|
55 |
+ |
|
53 | 56 |
# No content yet, try again later |
54 | 57 |
my $chunk = $self->{tx}->res->get_body_chunk($self->{offset} = defined $self->{offset} ? $self->{offset} : 0); |
55 | 58 |
return '' unless defined $chunk; |
... | ... |
@@ -5,6 +5,7 @@ use Fcntl ':flock'; |
5 | 5 |
use File::Spec::Functions qw(catfile tmpdir); |
6 | 6 |
use IO::Poll 'POLLIN'; |
7 | 7 |
use List::Util 'shuffle'; |
8 |
+use Mojo::Util 'steady_time'; |
|
8 | 9 |
use POSIX 'WNOHANG'; |
9 | 10 |
use Scalar::Util 'weaken'; |
10 | 11 |
use Time::HiRes (); |
... | ... |
@@ -14,7 +15,7 @@ has accept_interval => 0.025; |
14 | 15 |
has [qw(graceful_timeout heartbeat_timeout)] => 20; |
15 | 16 |
has heartbeat_interval => 5; |
16 | 17 |
has lock_file => sub { catfile tmpdir, 'prefork.lock' }; |
17 |
-has lock_timeout => 0.5; |
|
18 |
+has lock_timeout => 1; |
|
18 | 19 |
has multi_accept => 50; |
19 | 20 |
has pid_file => sub { catfile tmpdir, 'prefork.pid' }; |
20 | 21 |
has workers => 4; |
... | ... |
@@ -70,10 +71,10 @@ sub run { |
70 | 71 |
local $SIG{TTOU} = sub { |
71 | 72 |
$self->workers($self->workers - 1) if $self->workers > 0; |
72 | 73 |
return unless $self->workers; |
73 |
- $self->{pool}{shuffle keys %{$self->{pool}}}{graceful} ||= time; |
|
74 |
+ $self->{pool}{shuffle keys %{$self->{pool}}}{graceful} ||= steady_time; |
|
74 | 75 |
}; |
75 | 76 |
|
76 |
- # Preload application and start accepting connections |
|
77 |
+ # Preload application before starting workers |
|
77 | 78 |
$self->start->app->log->info("Manager $$ started."); |
78 | 79 |
$self->{running} = 1; |
79 | 80 |
$self->_manage while $self->{running}; |
... | ... |
@@ -89,7 +90,8 @@ sub _heartbeat { |
89 | 90 |
return unless $self->{reader}->sysread(my $chunk, 4194304); |
90 | 91 |
|
91 | 92 |
# Update heartbeats |
92 |
- $self->{pool}{$1} and $self->emit(heartbeat => $1)->{pool}{$1}{time} = time |
|
93 |
+ my $time = steady_time; |
|
94 |
+ $self->{pool}{$1} and $self->emit(heartbeat => $1)->{pool}{$1}{time} = $time |
|
93 | 95 |
while $chunk =~ /(\d+)\n/g; |
94 | 96 |
} |
95 | 97 |
|
... | ... |
@@ -113,17 +115,18 @@ sub _manage { |
113 | 115 |
# No heartbeat (graceful stop) |
114 | 116 |
my $interval = $self->heartbeat_interval; |
115 | 117 |
my $timeout = $self->heartbeat_timeout; |
116 |
- if (!$w->{graceful} && ($w->{time} + $interval + $timeout <= time)) { |
|
118 |
+ my $time = steady_time; |
|
119 |
+ if (!$w->{graceful} && ($w->{time} + $interval + $timeout <= $time)) { |
|
117 | 120 |
$log->info("Worker $pid has no heartbeat, restarting."); |
118 |
- $w->{graceful} = time; |
|
121 |
+ $w->{graceful} = $time; |
|
119 | 122 |
} |
120 | 123 |
|
121 | 124 |
# Graceful stop with timeout |
122 |
- $w->{graceful} ||= time if $self->{graceful}; |
|
125 |
+ $w->{graceful} ||= $time if $self->{graceful}; |
|
123 | 126 |
if ($w->{graceful}) { |
124 | 127 |
$log->debug("Trying to stop worker $pid gracefully."); |
125 | 128 |
kill 'QUIT', $pid; |
126 |
- $w->{force} = 1 if $w->{graceful} + $self->graceful_timeout <= time; |
|
129 |
+ $w->{force} = 1 if $w->{graceful} + $self->graceful_timeout <= $time; |
|
127 | 130 |
} |
128 | 131 |
|
129 | 132 |
# Normal stop |
... | ... |
@@ -151,7 +154,7 @@ sub _pid_file { |
151 | 154 |
sub _reap { |
152 | 155 |
my ($self, $pid) = @_; |
153 | 156 |
|
154 |
- # CLean up dead worker |
|
157 |
+ # Clean up dead worker |
|
155 | 158 |
$self->app->log->debug("Worker $pid stopped.") |
156 | 159 |
if delete $self->emit(reap => $pid)->{pool}{$pid}; |
157 | 160 |
} |
... | ... |
@@ -161,7 +164,8 @@ sub _spawn { |
161 | 164 |
|
162 | 165 |
# Manager |
163 | 166 |
die "Can't fork: $!" unless defined(my $pid = fork); |
164 |
- return $self->emit(spawn => $pid)->{pool}{$pid} = {time => time} if $pid; |
|
167 |
+ return $self->emit(spawn => $pid)->{pool}{$pid} = {time => steady_time} |
|
168 |
+ if $pid; |
|
165 | 169 |
|
166 | 170 |
# Prepare lock file |
167 | 171 |
my $file = $self->{lock_file}; |
... | ... |
@@ -208,7 +212,6 @@ sub _spawn { |
208 | 212 |
$SIG{QUIT} = sub { $loop->max_connections(0) }; |
209 | 213 |
delete $self->{$_} for qw(poll reader); |
210 | 214 |
|
211 |
- # Start event loop |
|
212 | 215 |
$self->app->log->debug("Worker $$ started."); |
213 | 216 |
$loop->start; |
214 | 217 |
exit 0; |
... | ... |
@@ -254,14 +257,15 @@ Mojo::Server::Prefork - Preforking non-blocking I/O HTTP and WebSocket server |
254 | 257 |
L<Mojo::Server::Prefork> is a full featured, UNIX optimized, preforking |
255 | 258 |
non-blocking I/O HTTP and WebSocket server, built around the very well tested |
256 | 259 |
and reliable L<Mojo::Server::Daemon>, with C<IPv6>, C<TLS>, C<Comet> (long |
257 |
-polling) and multiple event loop support. Note that the server uses signals |
|
258 |
-for process management, so you should avoid modifying signal handlers in your |
|
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 |
|
259 | 263 |
applications. |
260 | 264 |
|
261 | 265 |
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
262 | 266 |
L<IO::Socket::SSL> (1.75+) are supported transparently through |
263 | 267 |
L<Mojo::IOLoop>, and used if installed. Individual features can also be |
264 |
-disabled with the C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables. |
|
268 |
+disabled with the MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
265 | 269 |
|
266 | 270 |
See L<Mojolicious::Guides::Cookbook> for more. |
267 | 271 |
|
... | ... |
@@ -392,9 +396,9 @@ and implements the following new ones. |
392 | 396 |
my $interval = $prefork->accept_interval; |
393 | 397 |
$prefork = $prefork->accept_interval(0.5); |
394 | 398 |
|
395 |
-Interval in seconds for trying to reacquire the accept mutex and connection |
|
396 |
-management, defaults to C<0.025>. Note that changing this value can affect |
|
397 |
-performance and idle CPU usage. |
|
399 |
+Interval in seconds for trying to reacquire the accept mutex, defaults to |
|
400 |
+C<0.025>. Note that changing this value can affect performance and idle CPU |
|
401 |
+usage. |
|
398 | 402 |
|
399 | 403 |
=head2 accepts |
400 | 404 |
|
... | ... |
@@ -441,10 +445,11 @@ appended, defaults to a random temporary path. |
441 | 445 |
=head2 lock_timeout |
442 | 446 |
|
443 | 447 |
my $timeout = $prefork->lock_timeout; |
444 |
- $prefork = $prefork->lock_timeout(1); |
|
448 |
+ $prefork = $prefork->lock_timeout(0.5); |
|
445 | 449 |
|
446 | 450 |
Maximum amount of time in seconds a worker may block when waiting for the |
447 |
-accept mutex, defaults to C<0.5>. |
|
451 |
+accept mutex, defaults to C<1>. Note that changing this value can affect |
|
452 |
+performance and idle CPU usage. |
|
448 | 453 |
|
449 | 454 |
=head2 multi_accept |
450 | 455 |
|
... | ... |
@@ -27,6 +27,7 @@ sub build { |
27 | 27 |
my $self = shift; |
28 | 28 |
|
29 | 29 |
my (@lines, $cpst, $multi); |
30 |
+ my $escape = $self->auto_escape; |
|
30 | 31 |
for my $line (@{$self->tree}) { |
31 | 32 |
push @lines, ''; |
32 | 33 |
for (my $j = 0; $j < @{$line}; $j += 2) { |
... | ... |
@@ -58,13 +59,12 @@ sub build { |
58 | 59 |
if ($type eq 'code' || $multi) { $lines[-1] .= "$value" } |
59 | 60 |
|
60 | 61 |
# Expression |
61 |
- if (grep { $_ eq $type } qw(expr escp)) { |
|
62 |
+ if ($type eq 'expr' || $type eq 'escp') { |
|
62 | 63 |
|
63 | 64 |
# Start |
64 | 65 |
unless ($multi) { |
65 | 66 |
|
66 | 67 |
# Escaped |
67 |
- my $escape = $self->auto_escape; |
|
68 | 68 |
if (($type eq 'escp' && !$escape) || ($type eq 'expr' && $escape)) { |
69 | 69 |
$lines[-1] .= "\$_M .= _escape"; |
70 | 70 |
$lines[-1] .= " scalar $value" if length $value; |
... | ... |
@@ -99,7 +99,9 @@ sub compile { |
99 | 99 |
|
100 | 100 |
# Compile with line directive |
101 | 101 |
return undef unless my $code = $self->code; |
102 |
- my $compiled = eval qq{#line 1 "@{[$self->name]}"\n$code}; |
|
102 |
+ my $name = $self->name; |
|
103 |
+ $name =~ s/"//g; |
|
104 |
+ my $compiled = eval qq{#line 1 "$name"\n$code}; |
|
103 | 105 |
$self->compiled($compiled) and return undef unless $@; |
104 | 106 |
|
105 | 107 |
# Use local stacktrace for compile exceptions |
... | ... |
@@ -124,10 +126,10 @@ sub interpret { |
124 | 126 |
} |
125 | 127 |
|
126 | 128 |
sub parse { |
127 |
- my ($self, $tmpl) = @_; |
|
129 |
+ my ($self, $template) = @_; |
|
128 | 130 |
|
129 | 131 |
# Clean start |
130 |
- delete $self->template($tmpl)->{tree}; |
|
132 |
+ my $tree = $self->template($template)->tree([])->tree; |
|
131 | 133 |
|
132 | 134 |
my $tag = $self->tag_start; |
133 | 135 |
my $replace = $self->replace_mark; |
... | ... |
@@ -140,7 +142,6 @@ sub parse { |
140 | 142 |
my $end = $self->tag_end; |
141 | 143 |
my $start = $self->line_start; |
142 | 144 |
|
143 |
- # Precompile |
|
144 | 145 |
my $token_re = qr/ |
145 | 146 |
( |
146 | 147 |
\Q$tag$replace\E # Replace |
... | ... |
@@ -174,28 +175,23 @@ sub parse { |
174 | 175 |
# Split lines |
175 | 176 |
my $state = 'text'; |
176 | 177 |
my ($trimming, @capture_token); |
177 |
- for my $line (split /\n/, $tmpl) { |
|
178 |
+ for my $line (split /\n/, $template) { |
|
178 | 179 |
$trimming = 0 if $state eq 'text'; |
179 | 180 |
|
180 |
- # Perl line |
|
181 |
+ # Turn Perl line into mixed line |
|
181 | 182 |
if ($state eq 'text' && $line !~ s/^(\s*)\Q$start$replace\E/$1$start/) { |
182 |
- $line =~ s/^(\s*)\Q$start\E(\Q$expr\E)?// |
|
183 |
- and $line = $2 ? "$1$tag$2$line $end" : "$tag$line $trim$end"; |
|
184 |
- } |
|
185 |
- |
|
186 |
- # Escaped line ending |
|
187 |
- if ($line =~ /(\\+)$/) { |
|
188 |
- my $len = length $1; |
|
183 |
+ if ($line =~ s/^(\s*)\Q$start\E(?:(\Q$cmnt\E)|(\Q$expr\E))?//) { |
|
189 | 184 |
|
190 |
- # Newline |
|
191 |
- if ($len == 1) { $line =~ s/\\$// } |
|
185 |
+ # Comment |
|
186 |
+ if ($2) { $line = "$tag$2 $trim$end" } |
|
192 | 187 |
|
193 |
- # Backslash |
|
194 |
- elsif ($len > 1) { $line =~ s/\\\\$/\\\n/ } |
|
188 |
+ # Expression or code |
|
189 |
+ else { $line = $3 ? "$1$tag$3$line $end" : "$tag$line $trim$end" } |
|
190 |
+ } |
|
195 | 191 |
} |
196 | 192 |
|
197 |
- # Normal line ending |
|
198 |
- else { $line .= "\n" } |
|
193 |
+ # Escaped line ending |
|
194 |
+ $line .= "\n" unless $line =~ s/\\\\$/\\\n/ || $line =~ s/\\$//; |
|
199 | 195 |
|
200 | 196 |
# Mixed line |
201 | 197 |
my @token; |
... | ... |
@@ -233,7 +229,7 @@ sub parse { |
233 | 229 |
# Comment |
234 | 230 |
elsif ($token =~ /^\Q$tag$cmnt\E$/) { $state = 'cmnt' } |
235 | 231 |
|
236 |
- # Value |
|
232 |
+ # Text |
|
237 | 233 |
else { |
238 | 234 |
|
239 | 235 |
# Replace |
... | ... |
@@ -251,27 +247,27 @@ sub parse { |
251 | 247 |
@capture_token = (); |
252 | 248 |
} |
253 | 249 |
} |
254 |
- push @{$self->tree}, \@token; |
|
250 |
+ push @$tree, \@token; |
|
255 | 251 |
} |
256 | 252 |
|
257 | 253 |
return $self; |
258 | 254 |
} |
259 | 255 |
|
260 | 256 |
sub render { |
261 |
- my $self = shift->parse(shift)->build; |
|
262 |
- return $self->compile || $self->interpret(@_); |
|
257 |
+ my $self = shift; |
|
258 |
+ return $self->parse(shift)->build->compile || $self->interpret(@_); |
|
263 | 259 |
} |
264 | 260 |
|
265 | 261 |
sub render_file { |
266 | 262 |
my ($self, $path) = (shift, shift); |
267 | 263 |
|
268 | 264 |
$self->name($path) unless defined $self->{name}; |
269 |
- my $tmpl = slurp $path; |
|
265 |
+ my $template = slurp $path; |
|
270 | 266 |
my $encoding = $self->encoding; |
271 | 267 |
croak qq{Template "$path" has invalid encoding.} |
272 |
- if $encoding && !defined($tmpl = decode $encoding, $tmpl); |
|
268 |
+ if $encoding && !defined($template = decode $encoding, $template); |
|
273 | 269 |
|
274 |
- return $self->render($tmpl, @_); |
|
270 |
+ return $self->render($template, @_); |
|
275 | 271 |
} |
276 | 272 |
|
277 | 273 |
sub _trim { |
... | ... |
@@ -280,8 +276,8 @@ sub _trim { |
280 | 276 |
# Walk line backwards |
281 | 277 |
for (my $j = @$line - 4; $j >= 0; $j -= 2) { |
282 | 278 |
|
283 |
- # Skip capture |
|
284 |
- next if grep { $_ eq $line->[$j] } qw(cpst cpen); |
|
279 |
+ # Skip captures |
|
280 |
+ next if $line->[$j] eq 'cpst' || $line->[$j] eq 'cpen'; |
|
285 | 281 |
|
286 | 282 |
# Only trim text |
287 | 283 |
return unless $line->[$j] eq 'text'; |
... | ... |
@@ -344,14 +340,14 @@ Mojo::Template - Perl-ish templates! |
344 | 340 |
|
345 | 341 |
# More advanced |
346 | 342 |
my $output = $mt->render(<<'EOF', 23, 'foo bar'); |
347 |
- % my ($number, $text) = @_; |
|
343 |
+ % my ($num, $text) = @_; |
|
348 | 344 |
%= 5 * 5 |
349 | 345 |
<!DOCTYPE html> |
350 | 346 |
<html> |
351 | 347 |
<head><title>More advanced</title></head> |
352 | 348 |
<body> |
353 | 349 |
test 123 |
354 |
- foo <% my $i = $number + 2; %> |
|
350 |
+ foo <% my $i = $num + 2; %> |
|
355 | 351 |
% for (1 .. 23) { |
356 | 352 |
* some text <%= $i++ %> |
357 | 353 |
% } |
... | ... |
@@ -383,7 +379,7 @@ automatically enabled. |
383 | 379 |
% Perl code line, treated as "<% line =%>" |
384 | 380 |
%= Perl expression line, treated as "<%= line %>" |
385 | 381 |
%== Perl expression line, treated as "<%== line %>" |
386 |
- %# Comment line, treated as "<%# line =%>" |
|
382 |
+ %# Comment line, useful for debugging |
|
387 | 383 |
%% Replaced with "%", useful for generating templates |
388 | 384 |
|
389 | 385 |
Escaping behavior can be reversed with the C<auto_escape> attribute, this is |
... | ... |
@@ -616,14 +612,15 @@ Characters indicating the end of a tag, defaults to C<%E<gt>>. |
616 | 612 |
my $template = $mt->template; |
617 | 613 |
$mt = $mt->template($template); |
618 | 614 |
|
619 |
-Raw template. |
|
615 |
+Raw unparsed template. |
|
620 | 616 |
|
621 | 617 |
=head2 tree |
622 | 618 |
|
623 | 619 |
my $tree = $mt->tree; |
624 |
- $mt = $mt->tree($tree); |
|
620 |
+ $mt = $mt->tree([['text', 'foo']]); |
|
625 | 621 |
|
626 |
-Parsed tree. |
|
622 |
+Template in parsed form. Note that this structure should only be used very |
|
623 |
+carefully since it is very dynamic. |
|
627 | 624 |
|
628 | 625 |
=head2 trim_mark |
629 | 626 |
|
... | ... |
@@ -639,12 +636,6 @@ Character activating automatic whitespace trimming, defaults to C<=>. |
639 | 636 |
L<Mojo::Template> inherits all methods from L<Mojo::Base> and implements the |
640 | 637 |
following new ones. |
641 | 638 |
|
642 |
-=head2 new |
|
643 |
- |
|
644 |
- my $mt = Mojo::Template->new; |
|
645 |
- |
|
646 |
-Construct a new L<Mojo::Template> object. |
|
647 |
- |
|
648 | 639 |
=head2 build |
649 | 640 |
|
650 | 641 |
$mt = $mt->build; |
... | ... |
@@ -693,8 +684,8 @@ Render template file. |
693 | 684 |
|
694 | 685 |
=head1 DEBUGGING |
695 | 686 |
|
696 |
-You can set the C<MOJO_TEMPLATE_DEBUG> environment variable to get some |
|
697 |
-advanced diagnostics information printed to C<STDERR>. |
|
687 |
+You can set the MOJO_TEMPLATE_DEBUG environment variable to get some advanced |
|
688 |
+diagnostics information printed to C<STDERR>. |
|
698 | 689 |
|
699 | 690 |
MOJO_TEMPLATE_DEBUG=1 |
700 | 691 |
|
... | ... |
@@ -12,7 +12,7 @@ has res => sub { Mojo::Message::Response->new }; |
12 | 12 |
sub client_close { |
13 | 13 |
my $self = shift; |
14 | 14 |
$self->res->finish; |
15 |
- return $self->server_close(@_); |
|
15 |
+ return $self->server_close; |
|
16 | 16 |
} |
17 | 17 |
|
18 | 18 |
sub client_read { croak 'Method "client_read" not implemented by subclass' } |
... | ... |
@@ -36,11 +36,7 @@ sub is_finished { do {my $tmp = shift->{state}; defined $tmp ? $tmp : ''} eq 'fi |
36 | 36 |
|
37 | 37 |
sub is_websocket {undef} |
38 | 38 |
|
39 |
-sub is_writing { |
|
40 |
- return 1 unless my $state = shift->{state}; |
|
41 |
- return !!grep { $_ eq $state } |
|
42 |
- qw(write write_start_line write_headers write_body); |
|
43 |
-} |
|
39 |
+sub is_writing { (do {my $tmp = shift->{state}; defined $tmp ? $tmp : 'write'}) eq 'write' } |
|
44 | 40 |
|
45 | 41 |
sub remote_address { |
46 | 42 |
my $self = shift; |
... | ... |
@@ -54,31 +50,27 @@ sub remote_address { |
54 | 50 |
# Reverse proxy |
55 | 51 |
if ($ENV{MOJO_REVERSE_PROXY}) { |
56 | 52 |
return $self->{forwarded_for} if $self->{forwarded_for}; |
57 |
- my $forwarded = $self->req->headers->header('X-Forwarded-For') || ''; |
|
53 |
+ my $forwarded = defined $self->req->headers->header('X-Forwarded-For') ? $self->req->headers->header('X-Forwarded-For') : ''; |
|
58 | 54 |
$forwarded =~ /([^,\s]+)$/ and return $self->{forwarded_for} = $1; |
59 | 55 |
} |
60 | 56 |
|
61 | 57 |
return $self->{remote_address}; |
62 | 58 |
} |
63 | 59 |
|
64 |
-sub resume { |
|
65 |
- my $self = shift; |
|
66 |
- if ((defined $self->{state} ? $self->{state} : '') eq 'paused') { $self->{state} = 'write_body' } |
|
67 |
- elsif (!$self->is_writing) { $self->{state} = 'write' } |
|
68 |
- return $self->emit('resume'); |
|
69 |
-} |
|
70 |
- |
|
71 |
-sub server_close { |
|
72 |
- my $self = shift; |
|
73 |
- $self->{state} = 'finished'; |
|
74 |
- return $self->emit('finish'); |
|
75 |
-} |
|
60 |
+sub resume { shift->_state(qw(write resume)) } |
|
61 |
+sub server_close { shift->_state(qw(finished finish)) } |
|
76 | 62 |
|
77 | 63 |
sub server_read { croak 'Method "server_read" not implemented by subclass' } |
78 | 64 |
sub server_write { croak 'Method "server_write" not implemented by subclass' } |
79 | 65 |
|
80 | 66 |
sub success { $_[0]->error ? undef : $_[0]->res } |
81 | 67 |
|
68 |
+sub _state { |
|
69 |
+ my ($self, $state, $event) = @_; |
|
70 |
+ $self->{state} = $state; |
|
71 |
+ return $self->emit($event); |
|
72 |
+} |
|
73 |
+ |
|
82 | 74 |
1; |
83 | 75 |
|
84 | 76 |
=head1 NAME |
... | ... |
@@ -8,32 +8,35 @@ sub client_read { |
8 | 8 |
|
9 | 9 |
# Skip body for HEAD request |
10 | 10 |
my $res = $self->res; |
11 |
- $res->content->skip_body(1) if $self->req->method eq 'HEAD'; |
|
11 |
+ $res->content->skip_body(1) if uc $self->req->method eq 'HEAD'; |
|
12 | 12 |
return unless $res->parse($chunk)->is_finished; |
13 | 13 |
|
14 |
- # Unexpected 1xx reponse |
|
14 |
+ # Unexpected 1xx response |
|
15 | 15 |
return $self->{state} = 'finished' |
16 | 16 |
if !$res->is_status_class(100) || $res->headers->upgrade; |
17 | 17 |
$self->res($res->new)->emit(unexpected => $res); |
18 |
- $self->client_read($res->leftovers) if $res->has_leftovers; |
|
18 |
+ return unless length(my $leftovers = $res->content->leftovers); |
|
19 |
+ $self->client_read($leftovers); |
|
19 | 20 |
} |
20 | 21 |
|
21 | 22 |
sub client_write { shift->_write(0) } |
22 | 23 |
|
24 |
+sub is_empty { !!(uc $_[0]->req->method eq 'HEAD' || $_[0]->res->is_empty) } |
|
25 |
+ |
|
23 | 26 |
sub keep_alive { |
24 | 27 |
my $self = shift; |
25 | 28 |
|
26 | 29 |
# Close |
27 | 30 |
my $req = $self->req; |
28 | 31 |
my $res = $self->res; |
29 |
- my $req_conn = lc($req->headers->connection || ''); |
|
30 |
- my $res_conn = lc($res->headers->connection || ''); |
|
32 |
+ my $req_conn = lc(defined $req->headers->connection ? $req->headers->connection : ''); |
|
33 |
+ my $res_conn = lc(defined $res->headers->connection ? $res->headers->connection : ''); |
|
31 | 34 |
return undef if $req_conn eq 'close' || $res_conn eq 'close'; |
32 | 35 |
|
33 |
- # Keep alive |
|
36 |
+ # Keep-alive |
|
34 | 37 |
return 1 if $req_conn eq 'keep-alive' || $res_conn eq 'keep-alive'; |
35 | 38 |
|
36 |
- # No keep alive for 1.0 |
|
39 |
+ # No keep-alive for 1.0 |
|
37 | 40 |
return !($req->version eq '1.0' || $res->version eq '1.0'); |
38 | 41 |
} |
39 | 42 |
|
... | ... |
@@ -48,7 +51,7 @@ sub server_read { |
48 | 51 |
# Generate response |
49 | 52 |
return unless $req->is_finished && !$self->{handled}++; |
50 | 53 |
$self->emit(upgrade => Mojo::Transaction::WebSocket->new(handshake => $self)) |
51 |
- if lc($req->headers->upgrade || '') eq 'websocket'; |
|
54 |
+ if lc(defined $req->headers->upgrade ? $req->headers->upgrade : '') eq 'websocket'; |
|
52 | 55 |
$self->emit('request'); |
53 | 56 |
} |
54 | 57 |
|
... | ... |
@@ -57,10 +60,10 @@ sub server_write { shift->_write(1) } |
57 | 60 |
sub _body { |
58 | 61 |
my ($self, $msg, $finish) = @_; |
59 | 62 |
|
60 |
- # Prepare chunk |
|
63 |
+ # Prepare body chunk |
|
61 | 64 |
my $buffer = $msg->get_body_chunk($self->{offset}); |
62 | 65 |
my $written = defined $buffer ? length $buffer : 0; |
63 |
- $self->{write} = $msg->is_dynamic ? 1 : ($self->{write} - $written); |
|
66 |
+ $self->{write} = $msg->content->is_dynamic ? 1 : ($self->{write} - $written); |
|
64 | 67 |
$self->{offset} = $self->{offset} + $written; |
65 | 68 |
if (defined $buffer) { delete $self->{delay} } |
66 | 69 |
|
... | ... |
@@ -71,7 +74,7 @@ sub _body { |
71 | 74 |
} |
72 | 75 |
|
73 | 76 |
# Finished |
74 |
- $self->{state} = $finish ? 'finished' : 'read_response' |
|
77 |
+ $self->{state} = $finish ? 'finished' : 'read' |
|
75 | 78 |
if $self->{write} <= 0 || (defined $buffer && !length $buffer); |
76 | 79 |
|
77 | 80 |
return defined $buffer ? $buffer : ''; |
... | ... |
@@ -80,24 +83,23 @@ sub _body { |
80 | 83 |
sub _headers { |
81 | 84 |
my ($self, $msg, $head) = @_; |
82 | 85 |
|
83 |
- # Prepare chunk |
|
86 |
+ # Prepare header chunk |
|
84 | 87 |
my $buffer = $msg->get_header_chunk($self->{offset}); |
85 | 88 |
my $written = defined $buffer ? length $buffer : 0; |
86 | 89 |
$self->{write} = $self->{write} - $written; |
87 | 90 |
$self->{offset} = $self->{offset} + $written; |
88 | 91 |
|
89 |
- # Write body |
|
92 |
+ # Switch to body |
|
90 | 93 |
if ($self->{write} <= 0) { |
91 | 94 |
$self->{offset} = 0; |
92 | 95 |
|
93 | 96 |
# Response without body |
94 |
- $head = $head && ($self->req->method eq 'HEAD' || $msg->is_empty); |
|
95 |
- if ($head) { $self->{state} = 'finished' } |
|
97 |
+ if ($head && $self->is_empty) { $self->{state} = 'finished' } |
|
96 | 98 |
|
97 | 99 |
# Body |
98 | 100 |
else { |
99 |
- $self->{state} = 'write_body'; |
|
100 |
- $self->{write} = $msg->is_dynamic ? 1 : $msg->body_size; |
|
101 |
+ $self->{http_state} = 'body'; |
|
102 |
+ $self->{write} = $msg->content->is_dynamic ? 1 : $msg->body_size; |
|
101 | 103 |
} |
102 | 104 |
} |
103 | 105 |
|
... | ... |
@@ -107,17 +109,17 @@ sub _headers { |
107 | 109 |
sub _start_line { |
108 | 110 |
my ($self, $msg) = @_; |
109 | 111 |
|
110 |
- # Prepare chunk |
|
112 |
+ # Prepare start line chunk |
|
111 | 113 |
my $buffer = $msg->get_start_line_chunk($self->{offset}); |
112 | 114 |
my $written = defined $buffer ? length $buffer : 0; |
113 | 115 |
$self->{write} = $self->{write} - $written; |
114 | 116 |
$self->{offset} = $self->{offset} + $written; |
115 | 117 |
|
116 |
- # Write headers |
|
118 |
+ # Switch to headers |
|
117 | 119 |
if ($self->{write} <= 0) { |
118 |
- $self->{state} = 'write_headers'; |
|
119 |
- $self->{write} = $msg->header_size; |
|
120 |
- $self->{offset} = 0; |
|
120 |
+ $self->{http_state} = 'headers'; |
|
121 |
+ $self->{write} = $msg->header_size; |
|
122 |
+ $self->{offset} = 0; |
|
121 | 123 |
} |
122 | 124 |
|
123 | 125 |
return $buffer; |
... | ... |
@@ -126,31 +128,34 @@ sub _start_line { |
126 | 128 |
sub _write { |
127 | 129 |
my ($self, $server) = @_; |
128 | 130 |
|
129 |
- # Start writing |
|
131 |
+ # Client starts writing right away |
|
132 |
+ $self->{state} ||= 'write' unless $server; |
|
133 |
+ return '' unless $self->{state} eq 'write'; |
|
134 |
+ |
|
135 |
+ # Nothing written yet |
|
130 | 136 |
$self->{$_} ||= 0 for qw(offset write); |
131 | 137 |
my $msg = $server ? $self->res : $self->req; |
132 |
- if ($server ? ($self->{state} eq 'write') : !$self->{state}) { |
|
138 |
+ unless ($self->{http_state}) { |
|
133 | 139 |
|
134 | 140 |
# Connection header |
135 | 141 |
my $headers = $msg->headers; |
136 | 142 |
$headers->connection($self->keep_alive ? 'keep-alive' : 'close') |
137 | 143 |
unless $headers->connection; |
138 | 144 |
|
139 |
- # Write start line |
|
140 |
- $self->{state} = 'write_start_line'; |
|
141 |
- $self->{write} = $msg->start_line_size; |
|
145 |
+ # Switch to start line |
|
146 |
+ $self->{http_state} = 'start_line'; |
|
147 |
+ $self->{write} = $msg->start_line_size; |
|
142 | 148 |
} |
143 | 149 |
|
144 | 150 |
# Start line |
145 | 151 |
my $chunk = ''; |
146 |
- $chunk .= $self->_start_line($msg) if $self->{state} eq 'write_start_line'; |
|
152 |
+ $chunk .= $self->_start_line($msg) if $self->{http_state} eq 'start_line'; |
|
147 | 153 |
|
148 | 154 |
# Headers |
149 |
- $chunk .= $self->_headers($msg, $server) |
|
150 |
- if $self->{state} eq 'write_headers'; |
|
155 |
+ $chunk .= $self->_headers($msg, $server) if $self->{http_state} eq 'headers'; |
|
151 | 156 |
|
152 | 157 |
# Body |
153 |
- $chunk .= $self->_body($msg, $server) if $self->{state} eq 'write_body'; |
|
158 |
+ $chunk .= $self->_body($msg, $server) if $self->{http_state} eq 'body'; |
|
154 | 159 |
|
155 | 160 |
return $chunk; |
156 | 161 |
} |
... | ... |
@@ -168,7 +173,7 @@ Mojo::Transaction::HTTP - HTTP transaction |
168 | 173 |
# Client |
169 | 174 |
my $tx = Mojo::Transaction::HTTP->new; |
170 | 175 |
$tx->req->method('GET'); |
171 |
- $tx->req->url->parse('http://mojolicio.us'); |
|
176 |
+ $tx->req->url->parse('http://example.com'); |
|
172 | 177 |
$tx->req->headers->accept('application/json'); |
173 | 178 |
say $tx->res->code; |
174 | 179 |
say $tx->res->headers->content_type; |
... | ... |
@@ -259,6 +264,12 @@ Read data client-side, used to implement user agents. |
259 | 264 |
|
260 | 265 |
Write data client-side, used to implement user agents. |
261 | 266 |
|
267 |
+=head2 is_empty |
|
268 |
+ |
|
269 |
+ my $success = $tx->is_empty; |
|
270 |
+ |
|
271 |
+Check transaction for C<HEAD> request and C<1xx>, C<204> or C<304> response. |
|
272 |
+ |
|
262 | 273 |
=head2 keep_alive |
263 | 274 |
|
264 | 275 |
my $success = $tx->keep_alive; |
... | ... |
@@ -2,15 +2,17 @@ package Mojo::Transaction::WebSocket; |
2 | 2 |
use Mojo::Base 'Mojo::Transaction'; |
3 | 3 |
|
4 | 4 |
use Config; |
5 |
+use Mojo::JSON; |
|
5 | 6 |
use Mojo::Transaction::HTTP; |
6 | 7 |
use Mojo::Util qw(b64_encode decode encode sha1_bytes xor_encode); |
7 | 8 |
|
8 | 9 |
use constant DEBUG => $ENV{MOJO_WEBSOCKET_DEBUG} || 0; |
9 | 10 |
|
10 |
-# 64bit Perl |
|
11 |
-use constant MODERN => $Config{ivsize} > 4; |
|
11 |
+# Perl with support for quads |
|
12 |
+use constant MODERN => |
|
13 |
+ ((defined $Config{use64bitint} ? $Config{use64bitint} : '') eq 'define' || $Config{longsize} >= 8); |
|
12 | 14 |
|
13 |
-# Unique value from the spec |
|
15 |
+# Unique value from RFC 6455 |
|
14 | 16 |
use constant GUID => '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; |
15 | 17 |
|
16 | 18 |
# Opcodes |
... | ... |
@@ -89,10 +91,8 @@ sub client_handshake { |
89 | 91 |
my $self = shift; |
90 | 92 |
|
91 | 93 |
my $headers = $self->req->headers; |
92 |
- $headers->upgrade('websocket') unless $headers->upgrade; |
|
93 |
- $headers->connection('Upgrade') unless $headers->connection; |
|
94 |
- $headers->sec_websocket_protocol('mojo') |
|
95 |
- unless $headers->sec_websocket_protocol; |
|
94 |
+ $headers->upgrade('websocket') unless $headers->upgrade; |
|
95 |
+ $headers->connection('Upgrade') unless $headers->connection; |
|
96 | 96 |
$headers->sec_websocket_version(13) unless $headers->sec_websocket_version; |
97 | 97 |
|
98 | 98 |
# Generate WebSocket challenge |
... | ... |
@@ -107,7 +107,13 @@ sub connection { shift->handshake->connection } |
107 | 107 |
|
108 | 108 |
sub finish { |
109 | 109 |
my $self = shift; |
110 |
- $self->send([1, 0, 0, 0, CLOSE, ''])->{finished} = 1; |
|
110 |
+ |
|
111 |
+ my $close = $self->{close} = [@_]; |
|
112 |
+ my $payload = $close->[0] ? pack('n', $close->[0]) : ''; |
|
113 |
+ $payload .= encode 'UTF-8', $close->[1] if defined $close->[1]; |
|
114 |
+ $close->[0] = defined $close->[0] ? $close->[0] : 1005; |
|
115 |
+ $self->send([1, 0, 0, 0, CLOSE, $payload])->{finished} = 1; |
|
116 |
+ |
|
111 | 117 |
return $self; |
112 | 118 |
} |
113 | 119 |
|
... | ... |
@@ -159,7 +165,7 @@ sub parse_frame { |
159 | 165 |
} |
160 | 166 |
|
161 | 167 |
# Check message size |
162 |
- $self->finish and return undef if $len > $self->max_websocket_size; |
|
168 |
+ $self->finish(1009) and return undef if $len > $self->max_websocket_size; |
|
163 | 169 |
|
164 | 170 |
# Check if whole packet has arrived |
165 | 171 |
my $masked = vec($head, 1, 8) & 0b10000000; |
... | ... |
@@ -193,16 +199,21 @@ sub resume { |
193 | 199 |
sub send { |
194 | 200 |
my ($self, $frame, $cb) = @_; |
195 | 201 |
|
196 |
- # Binary or raw text |
|
197 | 202 |
if (ref $frame eq 'HASH') { |
203 |
+ |
|
204 |
+ # JSON |
|
205 |
+ $frame->{text} = Mojo::JSON->new->encode($frame->{json}) if $frame->{json}; |
|
206 |
+ |
|
207 |
+ # Binary or raw text |
|
198 | 208 |
$frame |
199 | 209 |
= exists $frame->{text} |
200 | 210 |
? [1, 0, 0, 0, TEXT, $frame->{text}] |
201 | 211 |
: [1, 0, 0, 0, BINARY, $frame->{binary}]; |
202 | 212 |
} |
203 | 213 |
|
204 |
- # Text |
|
205 |
- elsif (!ref $frame) { $frame = [1, 0, 0, 0, TEXT, encode('UTF-8', $frame)] } |
|
214 |
+ # Text or object (forcing stringification) |
|
215 |
+ $frame = [1, 0, 0, 0, TEXT, encode('UTF-8', "$frame")] |
|
216 |
+ if ref $frame ne 'ARRAY'; |
|
206 | 217 |
|
207 | 218 |
$self->once(drain => $cb) if $cb; |
208 | 219 |
$self->{write} .= $self->build_frame(@$frame); |
... | ... |
@@ -211,14 +222,19 @@ sub send { |
211 | 222 |
return $self->emit('resume'); |
212 | 223 |
} |
213 | 224 |
|
225 |
+sub server_close { |
|
226 |
+ my $self = shift; |
|
227 |
+ $self->{state} = 'finished'; |
|
228 |
+ return $self->emit(finish => $self->{close} ? (@{$self->{close}}) : 1006); |
|
229 |
+} |
|
230 |
+ |
|
214 | 231 |
sub server_handshake { |
215 | 232 |
my $self = shift; |
216 | 233 |
|
217 |
- # WebSocket handshake |
|
218 | 234 |
my $res_headers = $self->res->code(101)->headers; |
219 | 235 |
$res_headers->upgrade('websocket')->connection('Upgrade'); |
220 | 236 |
my $req_headers = $self->req->headers; |
221 |
- ($req_headers->sec_websocket_protocol || '') =~ /^\s*([^,]+)/ |
|
237 |
+ (defined $req_headers->sec_websocket_protocol ? $req_headers->sec_websocket_protocol : '') =~ /^\s*([^,]+)/ |
|
222 | 238 |
and $res_headers->sec_websocket_protocol($1); |
223 | 239 |
$res_headers->sec_websocket_accept( |
224 | 240 |
_challenge($req_headers->sec_websocket_key)); |
... | ... |
@@ -259,25 +275,29 @@ sub _message { |
259 | 275 |
return if $op == PONG; |
260 | 276 |
|
261 | 277 |
# Close |
262 |
- return $self->finish if $op == CLOSE; |
|
278 |
+ if ($op == CLOSE) { |
|
279 |
+ return $self->finish unless length $frame->[5] >= 2; |
|
280 |
+ return $self->finish(unpack('n', substr($frame->[5], 0, 2, '')), |
|
281 |
+ decode('UTF-8', $frame->[5])); |
|
282 |
+ } |
|
263 | 283 |
|
264 | 284 |
# Append chunk and check message size |
265 | 285 |
$self->{op} = $op unless exists $self->{op}; |
266 | 286 |
$self->{message} .= $frame->[5]; |
267 |
- $self->finish and last |
|
287 |
+ return $self->finish(1009) |
|
268 | 288 |
if length $self->{message} > $self->max_websocket_size; |
269 | 289 |
|
270 | 290 |
# No FIN bit (Continuation) |
271 | 291 |
return unless $frame->[0]; |
272 | 292 |
|
273 |
- # Message |
|
293 |
+ # Whole message |
|
274 | 294 |
my $msg = delete $self->{message}; |
275 |
- if (delete $self->{op} == TEXT) { |
|
276 |
- $self->emit(text => $msg); |
|
277 |
- $msg = decode 'UTF-8', $msg if $msg; |
|
278 |
- } |
|
279 |
- else { $self->emit(binary => $msg); } |
|
280 |
- $self->emit(message => $msg); |
|
295 |
+ $self->emit(json => Mojo::JSON->new->decode($msg)) |
|
296 |
+ if $self->has_subscribers('json'); |
|
297 |
+ $op = delete $self->{op}; |
|
298 |
+ $self->emit($op == TEXT ? 'text' : 'binary' => $msg); |
|
299 |
+ $self->emit(message => $op == TEXT ? decode('UTF-8', $msg) : $msg) |
|
300 |
+ if $self->has_subscribers('message'); |
|
281 | 301 |
} |
282 | 302 |
|
283 | 303 |
1; |
... | ... |
@@ -298,15 +318,15 @@ Mojo::Transaction::WebSocket - WebSocket transaction |
298 | 318 |
say "Message: $msg"; |
299 | 319 |
}); |
300 | 320 |
$ws->on(finish => sub { |
301 |
- my $ws = shift; |
|
302 |
- say 'WebSocket closed.'; |
|
321 |
+ my ($ws, $code, $reason) = @_; |
|
322 |
+ say "WebSocket closed with status $code."; |
|
303 | 323 |
}); |
304 | 324 |
|
305 | 325 |
=head1 DESCRIPTION |
306 | 326 |
|
307 | 327 |
L<Mojo::Transaction::WebSocket> is a container for WebSocket transactions as |
308 |
-described in RFC 6455. Note that 64bit frames require a Perl with 64bit |
|
309 |
-integer support, or they are limited to 32bit. |
|
328 |
+described in RFC 6455. Note that 64bit frames require a Perl with support for |
|
329 |
+quads or they are limited to 32bit. |
|
310 | 330 |
|
311 | 331 |
=head1 EVENTS |
312 | 332 |
|
... | ... |
@@ -341,6 +361,15 @@ Emitted once all data has been sent. |
341 | 361 |
$ws->send(time); |
342 | 362 |
}); |
343 | 363 |
|
364 |
+=head2 finish |
|
365 |
+ |
|
366 |
+ $ws->on(finish => sub { |
|
367 |
+ my ($ws, $code, $reason) = @_; |
|
368 |
+ ... |
|
369 |
+ }); |
|
370 |
+ |
|
371 |
+Emitted when transaction is finished. |
|
372 |
+ |
|
344 | 373 |
=head2 frame |
345 | 374 |
|
346 | 375 |
$ws->on(frame => sub { |
... | ... |
@@ -361,6 +390,22 @@ Emitted when a WebSocket frame has been received. |
361 | 390 |
say "Payload: $frame->[5]"; |
362 | 391 |
}); |
363 | 392 |
|
393 |
+=head2 json |
|
394 |
+ |
|
395 |
+ $ws->on(json => sub { |
|
396 |
+ my ($ws, $json) = @_; |
|
397 |
+ ... |
|
398 |
+ }); |
|
399 |
+ |
|
400 |
+Emitted when a complete WebSocket message has been received, all text and |
|
401 |
+binary messages will be automatically JSON decoded. Note that this event only |
|
402 |
+gets emitted when it has at least one subscriber. |
|
403 |
+ |
|
404 |
+ $ws->on(json => sub { |
|
405 |
+ my ($ws, $hash) = @_; |
|
406 |
+ say "Message: $hash->{msg}"; |
|
407 |
+ }); |
|
408 |
+ |
|
364 | 409 |
=head2 message |
365 | 410 |
|
366 | 411 |
$ws->on(message => sub { |
... | ... |
@@ -369,7 +414,8 @@ Emitted when a WebSocket frame has been received. |
369 | 414 |
}); |
370 | 415 |
|
371 | 416 |
Emitted when a complete WebSocket message has been received, text messages |
372 |
-will be automatically decoded. |
|
417 |
+will be automatically decoded. Note that this event only gets emitted when it |
|
418 |
+has at least one subscriber. |
|
373 | 419 |
|
374 | 420 |
$ws->on(message => sub { |
375 | 421 |
my ($ws, $msg) = @_; |
... | ... |
@@ -416,7 +462,7 @@ Mask outgoing frames with XOR cipher and a random 32bit key. |
416 | 462 |
$ws = $ws->max_websocket_size(1024); |
417 | 463 |
|
418 | 464 |
Maximum WebSocket message size in bytes, defaults to the value of the |
419 |
-C<MOJO_MAX_WEBSOCKET_SIZE> environment variable or C<262144>. |
|
465 |
+MOJO_MAX_WEBSOCKET_SIZE environment variable or C<262144>. |
|
420 | 466 |
|
421 | 467 |
=head1 METHODS |
422 | 468 |
|
... | ... |
@@ -425,7 +471,7 @@ L<Mojo::Transaction> and implements the following new ones. |
425 | 471 |
|
426 | 472 |
=head2 new |
427 | 473 |
|
428 |
- my $multi = Mojo::Content::MultiPart->new; |
|
474 |
+ my $ws = Mojo::Transaction::WebSocket->new; |
|
429 | 475 |
|
430 | 476 |
Construct a new L<Mojo::Transaction::WebSocket> object and subscribe to |
431 | 477 |
C<frame> event with default message parser, which also handles C<PING> and |
... | ... |
@@ -437,15 +483,15 @@ C<CLOSE> frames automatically. |
437 | 483 |
|
438 | 484 |
Build WebSocket frame. |
439 | 485 |
|
440 |
- # Continuation frame with FIN bit and payload |
|
441 |
- say $ws->build_frame(1, 0, 0, 0, 0, 'World!'); |
|
442 |
- |
|
443 |
- # Text frame with payload |
|
444 |
- say $ws->build_frame(0, 0, 0, 0, 1, 'Hello'); |
|
445 |
- |
|
446 | 486 |
# Binary frame with FIN bit and payload |
447 | 487 |
say $ws->build_frame(1, 0, 0, 0, 2, 'Hello World!'); |
448 | 488 |
|
489 |
+ # Text frame with payload but without FIN bit |
|
490 |
+ say $ws->build_frame(0, 0, 0, 0, 1, 'Hello '); |
|
491 |
+ |
|
492 |
+ # Continuation frame with FIN bit and payload |
|
493 |
+ say $ws->build_frame(1, 0, 0, 0, 0, 'World!'); |
|
494 |
+ |
|
449 | 495 |
# Close frame with FIN bit and without payload |
450 | 496 |
say $ws->build_frame(1, 0, 0, 0, 8, ''); |
451 | 497 |
|
... | ... |
@@ -489,8 +535,10 @@ Connection identifier or socket. |
489 | 535 |
=head2 finish |
490 | 536 |
|
491 | 537 |
$ws = $ws->finish; |
538 |
+ $ws = $ws->finish(1000); |
|
539 |
+ $ws = $ws->finish(1003 => 'Cannot accept data!'); |
|
492 | 540 |
|
493 |
-Finish the WebSocket connection gracefully. |
|
541 |
+Close WebSocket connection gracefully. |
|
494 | 542 |
|
495 | 543 |
=head2 is_websocket |
496 | 544 |
|
... | ... |
@@ -565,7 +613,9 @@ Resume C<handshake> transaction. |
565 | 613 |
|
566 | 614 |
$ws = $ws->send({binary => $bytes}); |
567 | 615 |
$ws = $ws->send({text => $bytes}); |
616 |
+ $ws = $ws->send({json => {test => [1, 2, 3]}}); |
|
568 | 617 |
$ws = $ws->send([$fin, $rsv1, $rsv2, $rsv3, $op, $bytes]); |
618 |
+ $ws = $ws->send(Mojo::ByteStream->new($chars)); |
|
569 | 619 |
$ws = $ws->send($chars); |
570 | 620 |
$ws = $ws->send($chars => sub {...}); |
571 | 621 |
|
... | ... |
@@ -575,6 +625,12 @@ will be invoked once all data has been written. |
575 | 625 |
# Send "Ping" frame |
576 | 626 |
$ws->send([1, 0, 0, 0, 9, 'Hello World!']); |
577 | 627 |
|
628 |
+=head2 server_close |
|
629 |
+ |
|
630 |
+ $ws->server_close; |
|
631 |
+ |
|
632 |
+Transaction closed server-side, used to implement web servers. |
|
633 |
+ |
|
578 | 634 |
=head2 server_handshake |
579 | 635 |
|
580 | 636 |
$ws->server_handshake; |
... | ... |
@@ -595,8 +651,8 @@ Write data server-side, used to implement web servers. |
595 | 651 |
|
596 | 652 |
=head1 DEBUGGING |
597 | 653 |
|
598 |
-You can set the C<MOJO_WEBSOCKET_DEBUG> environment variable to get some |
|
599 |
-advanced diagnostics information printed to C<STDERR>. |
|
654 |
+You can set the MOJO_WEBSOCKET_DEBUG environment variable to get some advanced |
|
655 |
+diagnostics information printed to C<STDERR>. |
|
600 | 656 |
|
601 | 657 |
MOJO_WEBSOCKET_DEBUG=1 |
602 | 658 |
|
... | ... |
@@ -15,10 +15,11 @@ has [qw(fragment host port scheme userinfo)]; |
15 | 15 |
sub new { shift->SUPER::new->parse(@_) } |
16 | 16 |
|
17 | 17 |
sub authority { |
18 |
- my ($self, $authority) = @_; |
|
18 |
+ my $self = shift; |
|
19 | 19 |
|
20 | 20 |
# New authority |
21 |
- if (defined $authority) { |
|
21 |
+ if (@_) { |
|
22 |
+ return $self unless defined(my $authority = shift); |
|
22 | 23 |
|
23 | 24 |
# Userinfo |
24 | 25 |
$authority =~ s/^([^\@]+)\@// and $self->userinfo(url_unescape $1); |
... | ... |
@@ -32,10 +33,11 @@ sub authority { |
32 | 33 |
} |
33 | 34 |
|
34 | 35 |
# Build authority |
35 |
- my $userinfo = $self->userinfo; |
|
36 |
- $authority .= url_escape($userinfo, '^A-Za-z0-9\-._~!$&\'()*+,;=:') . '@' |
|
37 |
- if $userinfo; |
|
38 |
- $authority .= defined $self->ihost ? $self->ihost : ''; |
|
36 |
+ return undef unless defined(my $authority = $self->ihost); |
|
37 |
+ if (my $userinfo = $self->userinfo) { |
|
38 |
+ $userinfo = url_escape $userinfo, '^A-Za-z0-9\-._~!$&\'()*+,;=:'; |
|
39 |
+ $authority = $userinfo . '@' . $authority; |
|
40 |
+ } |
|
39 | 41 |
if (my $port = $self->port) { $authority .= ":$port" } |
40 | 42 |
|
41 | 43 |
return $authority; |
... | ... |
@@ -44,14 +46,10 @@ sub authority { |
44 | 46 |
sub clone { |
45 | 47 |
my $self = shift; |
46 | 48 |
|
47 |
- my $clone = Mojo::URL->new; |
|
48 |
- $clone->scheme($self->scheme); |
|
49 |
- $clone->userinfo($self->userinfo); |
|
50 |
- $clone->host($self->host); |
|
51 |
- $clone->port($self->port); |
|
49 |
+ my $clone = $self->new; |
|
50 |
+ $clone->$_($self->$_) for qw(scheme userinfo host port fragment); |
|
52 | 51 |
$clone->path($self->path->clone); |
53 | 52 |
$clone->query($self->query->clone); |
54 |
- $clone->fragment($self->fragment); |
|
55 | 53 |
$clone->base($self->base->clone) if $self->{base}; |
56 | 54 |
|
57 | 55 |
return $clone; |
... | ... |
@@ -66,11 +64,11 @@ sub ihost { |
66 | 64 |
if @_; |
67 | 65 |
|
68 | 66 |
# Check if host needs to be encoded |
69 |
- return undef unless my $host = $self->host; |
|
67 |
+ return undef unless defined(my $host = $self->host); |
|
70 | 68 |
return lc $host unless $host =~ /[^\x00-\x7f]/; |
71 | 69 |
|
72 | 70 |
# Encode |
73 |
- return join '.', |
|
71 |
+ return lc join '.', |
|
74 | 72 |
map { /[^\x00-\x7f]/ ? ('xn--' . punycode_encode $_) : $_ } split /\./, |
75 | 73 |
$host; |
76 | 74 |
} |
... | ... |
@@ -81,25 +79,20 @@ sub parse { |
81 | 79 |
my ($self, $url) = @_; |
82 | 80 |
return $self unless $url; |
83 | 81 |
|
84 |
- # Official regex |
|
85 |
- $url =~ m!(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?!; |
|
86 |
- $self->scheme($1); |
|
87 |
- $self->authority($2); |
|
88 |
- $self->path->parse($3); |
|
89 |
- $self->query($4); |
|
90 |
- $self->fragment($5); |
|
91 |
- |
|
92 |
- return $self; |
|
82 |
+ # Official regex from RFC 3986 |
|
83 |
+ $url =~ m!^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?!; |
|
84 |
+ return $self->scheme($2)->authority($4)->path($5)->query($7)->fragment($9); |
|
93 | 85 |
} |
94 | 86 |
|
95 | 87 |
sub path { |
96 |
- my ($self, $path) = @_; |
|
88 |
+ my $self = shift; |
|
97 | 89 |
|
98 | 90 |
# Old path |
99 | 91 |
$self->{path} ||= Mojo::Path->new; |
100 |
- return $self->{path} unless $path; |
|
92 |
+ return $self->{path} unless @_; |
|
101 | 93 |
|
102 | 94 |
# New path |
95 |
+ my $path = shift; |
|
103 | 96 |
$self->{path} = ref $path ? $path : $self->{path}->merge($path); |
104 | 97 |
|
105 | 98 |
return $self; |
... | ... |
@@ -200,32 +193,24 @@ sub to_rel { |
200 | 193 |
sub to_string { |
201 | 194 |
my $self = shift; |
202 | 195 |
|
203 |
- # Protocol |
|
196 |
+ # Scheme |
|
204 | 197 |
my $url = ''; |
205 |
- if (my $proto = $self->protocol) { $url .= "$proto://" } |
|
198 |
+ if (my $proto = $self->protocol) { $url .= "$proto:" } |
|
206 | 199 |
|
207 | 200 |
# Authority |
208 | 201 |
my $authority = $self->authority; |
209 |
- $url .= $url ? $authority : $authority ? "//$authority" : ''; |
|
210 |
- |
|
211 |
- # Relative path |
|
212 |
- my $path = $self->path; |
|
213 |
- if (!$url) { $url .= "$path" } |
|
202 |
+ $url .= "//$authority" if defined $authority; |
|
214 | 203 |
|
215 |
- # Absolute path |
|
216 |
- elsif ($path->leading_slash) { $url .= "$path" } |
|
217 |
- else { $url .= @{$path->parts} ? "/$path" : '' } |
|
204 |
+ # Path |
|
205 |
+ my $path = $self->path->to_string; |
|
206 |
+ $url .= !$authority || $path eq '' || $path =~ m!^/! ? $path : "/$path"; |
|
218 | 207 |
|
219 | 208 |
# Query |
220 |
- my $query = join '', $self->query; |
|
221 |
- $url .= "?$query" if length $query; |
|
209 |
+ if (length(my $query = $self->query->to_string)) { $url .= "?$query" } |
|
222 | 210 |
|
223 | 211 |
# Fragment |
224 |
- my $fragment = $self->fragment; |
|
225 |
- $url .= '#' . url_escape $fragment, '^A-Za-z0-9\-._~!$&\'()*+,;=%:@/?' |
|
226 |
- if $fragment; |
|
227 |
- |
|
228 |
- return $url; |
|
212 |
+ return $url unless defined(my $fragment = $self->fragment); |
|
213 |
+ return $url . '#' . url_escape $fragment, '^A-Za-z0-9\-._~!$&\'()*+,;=%:@/?'; |
|
229 | 214 |
} |
230 | 215 |
|
231 | 216 |
1; |
... | ... |
@@ -242,7 +227,7 @@ Mojo::URL - Uniform Resource Locator |
242 | 227 |
|
243 | 228 |
# Parse |
244 | 229 |
my $url |
245 |
- = Mojo::URL->new('http://sri:foobar@kraih.com:3000/foo/bar?foo=bar#23'); |
|
230 |
+ = Mojo::URL->new('http://sri:foobar@example.com:3000/foo/bar?foo=bar#23'); |
|
246 | 231 |
say $url->scheme; |
247 | 232 |
say $url->userinfo; |
248 | 233 |
say $url->host; |
... | ... |
@@ -255,7 +240,7 @@ Mojo::URL - Uniform Resource Locator |
255 | 240 |
my $url = Mojo::URL->new; |
256 | 241 |
$url->scheme('http'); |
257 | 242 |
$url->userinfo('sri:foobar'); |
258 |
- $url->host('kraih.com'); |
|
243 |
+ $url->host('example.com'); |
|
259 | 244 |
$url->port(3000); |
260 | 245 |
$url->path('/foo/bar'); |
261 | 246 |
$url->path('baz'); |
... | ... |
@@ -324,7 +309,7 @@ following new ones. |
324 | 309 |
my $url = Mojo::URL->new; |
325 | 310 |
my $url = Mojo::URL->new('http://127.0.0.1:3000/foo?f=b&baz=2#foo'); |
326 | 311 |
|
327 |
-Construct a new L<Mojo::URL> object. |
|
312 |
+Construct a new L<Mojo::URL> object and C<parse> URL if necessary. |
|
328 | 313 |
|
329 | 314 |
=head2 authority |
330 | 315 |
|
... | ... |
@@ -359,7 +344,16 @@ Check if URL is absolute. |
359 | 344 |
|
360 | 345 |
$url = $url->parse('http://127.0.0.1:3000/foo/bar?fo=o&baz=23#foo'); |
361 | 346 |
|
362 |
-Parse URL. |
|
347 |
+Parse relative or absolute URL. |
|
348 |
+ |
|
349 |
+ # "/test/123" |
|
350 |
+ $url->parse('/test/123?foo=bar')->path; |
|
351 |
+ |
|
352 |
+ # "example.com" |
|
353 |
+ $url->parse('http://example.com/test/123?foo=bar')->host; |
|
354 |
+ |
|
355 |
+ # "sri@example.com" |
|
356 |
+ $url->parse('mailto:sri@example.com')->path; |
|
363 | 357 |
|
364 | 358 |
=head2 path |
365 | 359 |
|
... | ... |
@@ -368,17 +362,17 @@ Parse URL. |
368 | 362 |
$url = $url->path('foo/bar'); |
369 | 363 |
$url = $url->path(Mojo::Path->new); |
370 | 364 |
|
371 |
-Path part of this URL, relative paths will be appended to the existing path, |
|
365 |
+Path part of this URL, relative paths will be merged with the existing path, |
|
372 | 366 |
defaults to a L<Mojo::Path> object. |
373 | 367 |
|
374 |
- # "http://mojolicio.us/DOM/HTML" |
|
375 |
- Mojo::URL->new('http://mojolicio.us/perldoc/Mojo')->path('/DOM/HTML'); |
|
368 |
+ # "http://example.com/DOM/HTML" |
|
369 |
+ Mojo::URL->new('http://example.com/perldoc/Mojo')->path('/DOM/HTML'); |
|
376 | 370 |
|
377 |
- # "http://mojolicio.us/perldoc/DOM/HTML" |
|
378 |
- Mojo::URL->new('http://mojolicio.us/perldoc/Mojo')->path('DOM/HTML'); |
|
371 |
+ # "http://example.com/perldoc/DOM/HTML" |
|
372 |
+ Mojo::URL->new('http://example.com/perldoc/Mojo')->path('DOM/HTML'); |
|
379 | 373 |
|
380 |
- # "http://mojolicio.us/perldoc/Mojo/DOM/HTML" |
|
381 |
- Mojo::URL->new('http://mojolicio.us/perldoc/Mojo/')->path('DOM/HTML'); |
|
374 |
+ # "http://example.com/perldoc/Mojo/DOM/HTML" |
|
375 |
+ Mojo::URL->new('http://example.com/perldoc/Mojo/')->path('DOM/HTML'); |
|
382 | 376 |
|
383 | 377 |
=head2 protocol |
384 | 378 |
|
... | ... |
@@ -387,7 +381,7 @@ defaults to a L<Mojo::Path> object. |
387 | 381 |
Normalized version of C<scheme>. |
388 | 382 |
|
389 | 383 |
# "http" |
390 |
- Mojo::URL->new('HtTp://mojolicio.us')->protocol; |
|
384 |
+ Mojo::URL->new('HtTp://example.com')->protocol; |
|
391 | 385 |
|
392 | 386 |
=head2 query |
393 | 387 |
|
... | ... |
@@ -397,44 +391,45 @@ Normalized version of C<scheme>. |
397 | 391 |
$url = $url->query({append => 'to'}); |
398 | 392 |
$url = $url->query(Mojo::Parameters->new); |
399 | 393 |
|
400 |
-Query part of this URL, defaults to a L<Mojo::Parameters> object. |
|
394 |
+Query part of this URL, pairs in an array will be merged and pairs in a hash |
|
395 |
+appended, defaults to a L<Mojo::Parameters> object. |
|
401 | 396 |
|
402 | 397 |
# "2" |
403 |
- Mojo::URL->new('http://mojolicio.us?a=1&b=2')->query->param('b'); |
|
398 |
+ Mojo::URL->new('http://example.com?a=1&b=2')->query->param('b'); |
|
404 | 399 |
|
405 |
- # "http://mojolicio.us?a=2&c=3" |
|
406 |
- Mojo::URL->new('http://mojolicio.us?a=1&b=2')->query(a => 2, c => 3); |
|
400 |
+ # "http://example.com?a=2&c=3" |
|
401 |
+ Mojo::URL->new('http://example.com?a=1&b=2')->query(a => 2, c => 3); |
|
407 | 402 |
|
408 |
- # "http://mojolicio.us?a=2&a=3" |
|
409 |
- Mojo::URL->new('http://mojolicio.us?a=1&b=2')->query(a => [2, 3]); |
|
403 |
+ # "http://example.com?a=2&a=3" |
|
404 |
+ Mojo::URL->new('http://example.com?a=1&b=2')->query(a => [2, 3]); |
|
410 | 405 |
|
411 |
- # "http://mojolicio.us?a=2&b=2&c=3" |
|
412 |
- Mojo::URL->new('http://mojolicio.us?a=1&b=2')->query([a => 2, c => 3]); |
|
406 |
+ # "http://example.com?a=2&b=2&c=3" |
|
407 |
+ Mojo::URL->new('http://example.com?a=1&b=2')->query([a => 2, c => 3]); |
|
413 | 408 |
|
414 |
- # "http://mojolicio.us?b=2" |
|
415 |
- Mojo::URL->new('http://mojolicio.us?a=1&b=2')->query([a => undef]); |
|
409 |
+ # "http://example.com?b=2" |
|
410 |
+ Mojo::URL->new('http://example.com?a=1&b=2')->query([a => undef]); |
|
416 | 411 |
|
417 |
- # "http://mojolicio.us?a=1&b=2&a=2&c=3" |
|
418 |
- Mojo::URL->new('http://mojolicio.us?a=1&b=2')->query({a => 2, c => 3}); |
|
412 |
+ # "http://example.com?a=1&b=2&a=2&c=3" |
|
413 |
+ Mojo::URL->new('http://example.com?a=1&b=2')->query({a => 2, c => 3}); |
|
419 | 414 |
|
420 | 415 |
=head2 to_abs |
421 | 416 |
|
422 | 417 |
my $abs = $url->to_abs; |
423 |
- my $abs = $url->to_abs(Mojo::URL->new('http://kraih.com/foo')); |
|
418 |
+ my $abs = $url->to_abs(Mojo::URL->new('http://example.com/foo')); |
|
424 | 419 |
|
425 | 420 |
Clone relative URL and turn it into an absolute one. |
426 | 421 |
|
427 | 422 |
=head2 to_rel |
428 | 423 |
|
429 | 424 |
my $rel = $url->to_rel; |
430 |
- my $rel = $url->to_rel(Mojo::URL->new('http://kraih.com/foo')); |
|
425 |
+ my $rel = $url->to_rel(Mojo::URL->new('http://example.com/foo')); |
|
431 | 426 |
|
432 | 427 |
Clone absolute URL and turn it into a relative one. |
433 | 428 |
|
434 | 429 |
=head2 to_string |
435 | 430 |
|
436 |
- my $string = $url->to_string; |
|
437 |
- my $string = "$url"; |
|
431 |
+ my $str = $url->to_string; |
|
432 |
+ my $str = "$url"; |
|
438 | 433 |
|
439 | 434 |
Turn URL into a string. |
440 | 435 |
|
... | ... |
@@ -1,7 +1,6 @@ |
1 | 1 |
package Mojo::Upload; |
2 | 2 |
use Mojo::Base -base; |
3 | 3 |
|
4 |
-use Carp 'croak'; |
|
5 | 4 |
use Mojo::Asset::File; |
6 | 5 |
use Mojo::Headers; |
7 | 6 |
|
... | ... |
@@ -45,7 +44,8 @@ L<Mojo::Upload> implements the following attributes. |
45 | 44 |
my $asset = $upload->asset; |
46 | 45 |
$upload = $upload->asset(Mojo::Asset::File->new); |
47 | 46 |
|
48 |
-Asset containing the uploaded data, defaults to a L<Mojo::Asset::File> object. |
|
47 |
+Asset containing the uploaded data, usually a L<Mojo::Asset::File> or |
|
48 |
+L<Mojo::Asset::Memory> object. |
|
49 | 49 |
|
50 | 50 |
=head2 filename |
51 | 51 |
|
... | ... |
@@ -59,8 +59,6 @@ sub app_url { |
59 | 59 |
return Mojo::URL->new("$self->{proto}://localhost:$self->{port}/"); |
60 | 60 |
} |
61 | 61 |
|
62 |
-sub build_form_tx { shift->transactor->form(@_) } |
|
63 |
-sub build_json_tx { shift->transactor->json(@_) } |
|
64 | 62 |
sub build_tx { shift->transactor->tx(@_) } |
65 | 63 |
sub build_websocket_tx { shift->transactor->websocket(@_) } |
66 | 64 |
|
... | ... |
@@ -76,25 +74,11 @@ sub need_proxy { |
76 | 74 |
return !first { $host =~ /\Q$_\E$/ } @{$self->no_proxy || []}; |
77 | 75 |
} |
78 | 76 |
|
79 |
-sub post_form { |
|
80 |
- my $self = shift; |
|
81 |
- my $cb = ref $_[-1] eq 'CODE' ? pop : undef; |
|
82 |
- return $self->start($self->build_form_tx(@_), $cb); |
|
83 |
-} |
|
84 |
- |
|
85 |
-sub post_json { |
|
86 |
- my $self = shift; |
|
87 |
- my $cb = ref $_[-1] eq 'CODE' ? pop : undef; |
|
88 |
- return $self->start($self->build_json_tx(@_), $cb); |
|
89 |
-} |
|
90 |
- |
|
91 | 77 |
sub start { |
92 | 78 |
my ($self, $tx, $cb) = @_; |
93 | 79 |
|
94 | 80 |
# Non-blocking |
95 | 81 |
if ($cb) { |
96 |
- |
|
97 |
- # Start non-blocking |
|
98 | 82 |
warn "-- Non-blocking request (@{[$tx->req->url->to_abs]})\n" if DEBUG; |
99 | 83 |
unless ($self->{nb}) { |
100 | 84 |
croak 'Blocking request in progress' if keys %{$self->{connections}}; |
... | ... |
@@ -105,7 +89,7 @@ sub start { |
105 | 89 |
return $self->_start($tx, $cb); |
106 | 90 |
} |
107 | 91 |
|
108 |
- # Start blocking |
|
92 |
+ # Blocking |
|
109 | 93 |
warn "-- Blocking request (@{[$tx->req->url->to_abs]})\n" if DEBUG; |
110 | 94 |
if ($self->{nb}) { |
111 | 95 |
croak 'Non-blocking requests in progress' if keys %{$self->{connections}}; |
... | ... |
@@ -113,9 +97,7 @@ sub start { |
113 | 97 |
$self->_cleanup; |
114 | 98 |
delete $self->{nb}; |
115 | 99 |
} |
116 |
- $self->_start($tx => sub { $tx = pop }); |
|
117 |
- |
|
118 |
- # Start event loop |
|
100 |
+ $self->_start($tx => sub { shift->ioloop->stop; $tx = shift }); |
|
119 | 101 |
$self->ioloop->start; |
120 | 102 |
|
121 | 103 |
return $tx; |
... | ... |
@@ -166,7 +148,7 @@ sub _cleanup { |
166 | 148 |
# Clean up active connections (by closing them) |
167 | 149 |
$self->_handle($_ => 1) for keys %{$self->{connections} || {}}; |
168 | 150 |
|
169 |
- # Clean up keep alive connections |
|
151 |
+ # Clean up keep-alive connections |
|
170 | 152 |
$loop->remove($_->[1]) for @{delete $self->{cache} || []}; |
171 | 153 |
|
172 | 154 |
# Stop server |
... | ... |
@@ -311,17 +293,16 @@ sub _finish { |
311 | 293 |
} |
312 | 294 |
} |
313 | 295 |
|
314 |
- # Stop event loop if necessary |
|
315 | 296 |
$self->$cb($tx); |
316 |
- $self->ioloop->stop unless $self->{nb}; |
|
317 | 297 |
} |
318 | 298 |
|
319 | 299 |
sub _handle { |
320 | 300 |
my ($self, $id, $close) = @_; |
321 | 301 |
|
322 | 302 |
# Remove request timeout |
303 |
+ return unless my $loop = $self->_loop; |
|
323 | 304 |
my $c = $self->{connections}{$id}; |
324 |
- $self->_loop->remove($c->{timeout}) if $c->{timeout}; |
|
305 |
+ $loop->remove($c->{timeout}) if $c->{timeout}; |
|
325 | 306 |
|
326 | 307 |
# Finish WebSocket |
327 | 308 |
my $old = $c->{tx}; |
... | ... |
@@ -336,7 +317,7 @@ sub _handle { |
336 | 317 |
if (my $jar = $self->cookie_jar) { $jar->extract($old) } |
337 | 318 |
$old->client_close; |
338 | 319 |
$self->_finish($new, $c->{cb}); |
339 |
- $new->client_read($old->res->leftovers); |
|
320 |
+ $new->client_read($old->res->content->leftovers); |
|
340 | 321 |
} |
341 | 322 |
|
342 | 323 |
# Finish normal connection |
... | ... |
@@ -352,7 +333,7 @@ sub _handle { |
352 | 333 |
} |
353 | 334 |
} |
354 | 335 |
|
355 |
-sub _loop { $_[0]->{nb} ? Mojo::IOLoop->singleton : $_[0]->ioloop } |
|
336 |
+sub _loop { $_[0]{nb} ? Mojo::IOLoop->singleton : $_[0]->ioloop } |
|
356 | 337 |
|
357 | 338 |
sub _read { |
358 | 339 |
my ($self, $id, $chunk) = @_; |
... | ... |
@@ -380,7 +361,7 @@ sub _remove { |
380 | 361 |
|
381 | 362 |
# Keep connection alive |
382 | 363 |
$self->_cache(join(':', $self->transactor->endpoint($tx)), $id) |
383 |
- unless $tx->req->method eq 'CONNECT' && (defined $tx->res->code ? $tx->res->code : '') eq '200'; |
|
364 |
+ unless uc $tx->req->method eq 'CONNECT' && (defined $tx->res->code ? $tx->res->code : '') eq '200'; |
|
384 | 365 |
} |
385 | 366 |
|
386 | 367 |
sub _redirect { |
... | ... |
@@ -421,7 +402,8 @@ sub _start { |
421 | 402 |
my $url = $req->url; |
422 | 403 |
if ($self->{port} || !$url->is_abs) { |
423 | 404 |
if (my $app = $self->app) { $self->_server->app($app) } |
424 |
- $url = $req->url($url->base($self->app_url)->to_abs)->url |
|
405 |
+ my $base = $self->app_url; |
|
406 |
+ $url->scheme($base->scheme)->authority($base->authority) |
|
425 | 407 |
unless $url->is_abs; |
426 | 408 |
} |
427 | 409 |
|
... | ... |
@@ -447,10 +429,10 @@ sub _start { |
447 | 429 |
|
448 | 430 |
# Connect and add request timeout if necessary |
449 | 431 |
my $id = $self->emit(start => $tx)->_connection($tx, $cb); |
450 |
- if (my $t = $self->request_timeout) { |
|
432 |
+ if (my $timeout = $self->request_timeout) { |
|
451 | 433 |
weaken $self; |
452 | 434 |
$self->{connections}{$id}{timeout} = $self->_loop->timer( |
453 |
- $t => sub { $self->_error($id => 'Request timeout') }); |
|
435 |
+ $timeout => sub { $self->_error($id => 'Request timeout') }); |
|
454 | 436 |
} |
455 | 437 |
|
456 | 438 |
return $id; |
... | ... |
@@ -504,7 +486,7 @@ Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent |
504 | 486 |
say $ua->get('www.☃.net?hello=there' => {DNT => 1})->res->body; |
505 | 487 |
|
506 | 488 |
# Form POST with exception handling |
507 |
- my $tx = $ua->post_form('search.cpan.org/search' => {q => 'mojo'}); |
|
489 |
+ my $tx = $ua->post('https://metacpan.org/search' => form => {q => 'mojo'}); |
|
508 | 490 |
if (my $res = $tx->success) { say $res->body } |
509 | 491 |
else { |
510 | 492 |
my ($err, $code) = $tx->error; |
... | ... |
@@ -532,20 +514,15 @@ Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent |
532 | 514 |
|
533 | 515 |
# TLS certificate authentication and JSON POST |
534 | 516 |
my $tx = $ua->cert('tls.crt')->key('tls.key') |
535 |
- ->post_json('https://mojolicio.us' => {top => 'secret'}); |
|
536 |
- |
|
537 |
- # Custom JSON PUT request |
|
538 |
- my $tx = $ua->build_json_tx('http://mojolicious/foo' => {hi => 'there'}); |
|
539 |
- $tx->req->method('PUT'); |
|
540 |
- say $ua->start($tx)->res->body; |
|
517 |
+ ->post('https://mojolicio.us' => json => {top => 'secret'}); |
|
541 | 518 |
|
542 | 519 |
# Blocking parallel requests (does not work inside a running event loop) |
543 | 520 |
my $delay = Mojo::IOLoop->delay; |
544 | 521 |
for my $url ('mojolicio.us', 'cpan.org') { |
545 |
- $delay->begin; |
|
522 |
+ my $end = $delay->begin(0); |
|
546 | 523 |
$ua->get($url => sub { |
547 | 524 |
my ($ua, $tx) = @_; |
548 |
- $delay->end($tx->res->dom->at('title')->text); |
|
525 |
+ $end->($tx->res->dom->at('title')->text); |
|
549 | 526 |
}); |
550 | 527 |
} |
551 | 528 |
my @titles = $delay->wait; |
... | ... |
@@ -556,37 +533,38 @@ Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent |
556 | 533 |
... |
557 | 534 |
}); |
558 | 535 |
for my $url ('mojolicio.us', 'cpan.org') { |
559 |
- $delay->begin; |
|
536 |
+ my $end = $delay->begin(0); |
|
560 | 537 |
$ua->get($url => sub { |
561 | 538 |
my ($ua, $tx) = @_; |
562 |
- $delay->end($tx->res->dom->at('title')->text); |
|
539 |
+ $end->($tx->res->dom->at('title')->text); |
|
563 | 540 |
}); |
564 | 541 |
} |
565 | 542 |
$delay->wait unless Mojo::IOLoop->is_running; |
566 | 543 |
|
567 |
- # Non-blocking WebSocket connection |
|
568 |
- $ua->websocket('ws://websockets.org:8787' => sub { |
|
544 |
+ # Non-blocking WebSocket connection sending and receiving JSON messages |
|
545 |
+ $ua->websocket('ws://example.com/echo.json' => sub { |
|
569 | 546 |
my ($ua, $tx) = @_; |
570 |
- $tx->on(finish => sub { say 'WebSocket closed.' }); |
|
571 |
- $tx->on(message => sub { |
|
572 |
- my ($tx, $msg) = @_; |
|
573 |
- say "WebSocket message: $msg"; |
|
547 |
+ say 'WebSocket handshake failed!' and return unless $tx->is_websocket; |
|
548 |
+ $tx->on(json => sub { |
|
549 |
+ my ($tx, $hash) = @_; |
|
550 |
+ say "WebSocket message via JSON: $hash->{msg}"; |
|
574 | 551 |
$tx->finish; |
575 | 552 |
}); |
576 |
- $tx->send('hi there!'); |
|
553 |
+ $tx->send({json => {msg => 'Hello World!'}}); |
|
577 | 554 |
}); |
578 | 555 |
Mojo::IOLoop->start unless Mojo::IOLoop->is_running; |
579 | 556 |
|
580 | 557 |
=head1 DESCRIPTION |
581 | 558 |
|
582 | 559 |
L<Mojo::UserAgent> is a full featured non-blocking I/O HTTP and WebSocket user |
583 |
-agent, with C<IPv6>, C<TLS>, C<SNI>, C<IDNA>, C<Comet> (long polling), C<gzip> |
|
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> |
|
584 | 562 |
compression and multiple event loop support. |
585 | 563 |
|
586 | 564 |
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
587 | 565 |
L<IO::Socket::SSL> (1.75+) are supported transparently through |
588 | 566 |
L<Mojo::IOLoop>, and used if installed. Individual features can also be |
589 |
-disabled with the C<MOJO_NO_IPV6> and C<MOJO_NO_TLS> environment variables. |
|
567 |
+disabled with the MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
590 | 568 |
|
591 | 569 |
See L<Mojolicious::Guides::Cookbook> for more. |
592 | 570 |
|
... | ... |
@@ -634,7 +612,7 @@ L<Mojo::UserAgent> implements the following attributes. |
634 | 612 |
$ua = $ua->ca('/etc/tls/ca.crt'); |
635 | 613 |
|
636 | 614 |
Path to TLS certificate authority file, defaults to the value of the |
637 |
-C<MOJO_CA_FILE> environment variable. Also activates hostname verification. |
|
615 |
+MOJO_CA_FILE environment variable. Also activates hostname verification. |
|
638 | 616 |
|
639 | 617 |
# Show certificate authorities for debugging |
640 | 618 |
IO::Socket::SSL::set_defaults( |
... | ... |
@@ -645,7 +623,7 @@ C<MOJO_CA_FILE> environment variable. Also activates hostname verification. |
645 | 623 |
my $cert = $ua->cert; |
646 | 624 |
$ua = $ua->cert('/etc/tls/client.crt'); |
647 | 625 |
|
648 |
-Path to TLS certificate file, defaults to the value of the C<MOJO_CERT_FILE> |
|
626 |
+Path to TLS certificate file, defaults to the value of the MOJO_CERT_FILE |
|
649 | 627 |
environment variable. |
650 | 628 |
|
651 | 629 |
=head2 connect_timeout |
... | ... |
@@ -654,7 +632,7 @@ environment variable. |
654 | 632 |
$ua = $ua->connect_timeout(5); |
655 | 633 |
|
656 | 634 |
Maximum amount of time in seconds establishing a connection may take before |
657 |
-getting canceled, defaults to the value of the C<MOJO_CONNECT_TIMEOUT> |
|
635 |
+getting canceled, defaults to the value of the MOJO_CONNECT_TIMEOUT |
|
658 | 636 |
environment variable or C<10>. |
659 | 637 |
|
660 | 638 |
=head2 cookie_jar |
... | ... |
@@ -688,7 +666,7 @@ Proxy server to use for HTTPS and WebSocket requests. |
688 | 666 |
$ua = $ua->inactivity_timeout(15); |
689 | 667 |
|
690 | 668 |
Maximum amount of time in seconds a connection can be inactive before getting |
691 |
-closed, defaults to the value of the C<MOJO_INACTIVITY_TIMEOUT> environment |
|
669 |
+closed, defaults to the value of the MOJO_INACTIVITY_TIMEOUT environment |
|
692 | 670 |
variable or C<20>. Setting the value to C<0> will allow connections to be |
693 | 671 |
inactive indefinitely. |
694 | 672 |
|
... | ... |
@@ -705,8 +683,8 @@ L<Mojo::IOLoop> object. |
705 | 683 |
my $key = $ua->key; |
706 | 684 |
$ua = $ua->key('/etc/tls/client.crt'); |
707 | 685 |
|
708 |
-Path to TLS key file, defaults to the value of the C<MOJO_KEY_FILE> |
|
709 |
-environment variable. |
|
686 |
+Path to TLS key file, defaults to the value of the MOJO_KEY_FILE environment |
|
687 |
+variable. |
|
710 | 688 |
|
711 | 689 |
=head2 local_address |
712 | 690 |
|
... | ... |
@@ -720,7 +698,7 @@ Local address to bind to. |
720 | 698 |
my $max = $ua->max_connections; |
721 | 699 |
$ua = $ua->max_connections(5); |
722 | 700 |
|
723 |
-Maximum number of keep alive connections that the user agent will retain |
|
701 |
+Maximum number of keep-alive connections that the user agent will retain |
|
724 | 702 |
before it starts closing the oldest cached ones, defaults to C<5>. |
725 | 703 |
|
726 | 704 |
=head2 max_redirects |
... | ... |
@@ -729,8 +707,7 @@ before it starts closing the oldest cached ones, defaults to C<5>. |
729 | 707 |
$ua = $ua->max_redirects(3); |
730 | 708 |
|
731 | 709 |
Maximum number of redirects the user agent will follow before it fails, |
732 |
-defaults to the value of the C<MOJO_MAX_REDIRECTS> environment variable or |
|
733 |
-C<0>. |
|
710 |
+defaults to the value of the MOJO_MAX_REDIRECTS environment variable or C<0>. |
|
734 | 711 |
|
735 | 712 |
=head2 name |
736 | 713 |
|
... | ... |
@@ -753,7 +730,7 @@ Domains that don't require a proxy server to be used. |
753 | 730 |
|
754 | 731 |
Maximum amount of time in seconds establishing a connection, sending the |
755 | 732 |
request and receiving a whole response may take before getting canceled, |
756 |
-defaults to the value of the C<MOJO_REQUEST_TIMEOUT> environment variable or |
|
733 |
+defaults to the value of the MOJO_REQUEST_TIMEOUT environment variable or |
|
757 | 734 |
C<0>. Setting the value to C<0> will allow the user agent to wait |
758 | 735 |
indefinitely. The timeout will reset for every followed redirect. |
759 | 736 |
|
... | ... |
@@ -802,54 +779,47 @@ Get absolute L<Mojo::URL> object for C<app> and switch protocol if necessary. |
802 | 779 |
# Port currently used for processing relative URLs |
803 | 780 |
say $ua->app_url->port; |
804 | 781 |
|
805 |
-=head2 build_form_tx |
|
806 |
- |
|
807 |
- my $tx = $ua->build_form_tx('http://kraih.com' => {a => 'b'}); |
|
808 |
- my $tx = $ua->build_form_tx('kraih.com', 'UTF-8', {a => 'b'}, {DNT => 1}); |
|
809 |
- |
|
810 |
-Generate L<Mojo::Transaction::HTTP> object with |
|
811 |
-L<Mojo::UserAgent::Transactor/"form">. |
|
812 |
- |
|
813 |
-=head2 build_json_tx |
|
814 |
- |
|
815 |
- my $tx = $ua->build_json_tx('http://kraih.com' => {a => 'b'}); |
|
816 |
- my $tx = $ua->build_json_tx('kraih.com' => {a => 'b'} => {DNT => 1}); |
|
817 |
- |
|
818 |
-Generate L<Mojo::Transaction::HTTP> object with |
|
819 |
-L<Mojo::UserAgent::Transactor/"json">. |
|
820 |
- |
|
821 | 782 |
=head2 build_tx |
822 | 783 |
|
823 |
- my $tx = $ua->build_tx(GET => 'kraih.com'); |
|
824 |
- my $tx = $ua->build_tx(PUT => 'http://kraih.com' => {DNT => 1} => 'Hi!'); |
|
784 |
+ my $tx = $ua->build_tx(GET => 'example.com'); |
|
785 |
+ my $tx = $ua->build_tx(PUT => 'http://example.com' => {DNT => 1} => 'Hi!'); |
|
786 |
+ my $tx = $ua->build_tx( |
|
787 |
+ PUT => 'http://example.com' => {DNT => 1} => form => {a => 'b'}); |
|
788 |
+ my $tx = $ua->build_tx( |
|
789 |
+ PUT => 'http://example.com' => {DNT => 1} => json => {a => 'b'}); |
|
825 | 790 |
|
826 | 791 |
Generate L<Mojo::Transaction::HTTP> object with |
827 | 792 |
L<Mojo::UserAgent::Transactor/"tx">. |
828 | 793 |
|
829 | 794 |
# Request with cookie |
830 |
- my $tx = $ua->build_tx(GET => 'kraih.com'); |
|
795 |
+ my $tx = $ua->build_tx(GET => 'example.com'); |
|
831 | 796 |
$tx->req->cookies({name => 'foo', value => 'bar'}); |
832 | 797 |
$ua->start($tx); |
833 | 798 |
|
834 | 799 |
=head2 build_websocket_tx |
835 | 800 |
|
836 |
- my $tx = $ua->build_websocket_tx('ws://localhost:3000'); |
|
837 |
- my $tx = $ua->build_websocket_tx('ws://localhost:3000' => {DNT => 1}); |
|
801 |
+ my $tx = $ua->build_websocket_tx('ws://example.com'); |
|
802 |
+ my $tx = |
|
803 |
+ $ua->build_websocket_tx('ws://example.com' => {DNT => 1} => ['v1.proto']); |
|
838 | 804 |
|
839 | 805 |
Generate L<Mojo::Transaction::HTTP> object with |
840 | 806 |
L<Mojo::UserAgent::Transactor/"websocket">. |
841 | 807 |
|
842 | 808 |
=head2 delete |
843 | 809 |
|
844 |
- my $tx = $ua->delete('kraih.com'); |
|
845 |
- my $tx = $ua->delete('http://kraih.com' => {DNT => 1} => 'Hi!'); |
|
810 |
+ my $tx = $ua->delete('example.com'); |
|
811 |
+ my $tx = $ua->delete('http://example.com' => {DNT => 1} => 'Hi!'); |
|
812 |
+ my $tx = $ua->delete( |
|
813 |
+ 'http://example.com' => {DNT => 1} => form => {a => 'b'}); |
|
814 |
+ my $tx = $ua->delete( |
|
815 |
+ 'http://example.com' => {DNT => 1} => json => {a => 'b'}); |
|
846 | 816 |
|
847 | 817 |
Perform blocking HTTP C<DELETE> request and return resulting |
848 | 818 |
L<Mojo::Transaction::HTTP> object, takes the same arguments as |
849 | 819 |
L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also |
850 | 820 |
append a callback to perform requests non-blocking. |
851 | 821 |
|
852 |
- $ua->delete('http://kraih.com' => sub { |
|
822 |
+ $ua->delete('http://example.com' => sub { |
|
853 | 823 |
my ($ua, $tx) = @_; |
854 | 824 |
say $tx->res->body; |
855 | 825 |
}); |
... | ... |
@@ -859,21 +829,23 @@ append a callback to perform requests non-blocking. |
859 | 829 |
|
860 | 830 |
$ua = $ua->detect_proxy; |
861 | 831 |
|
862 |
-Check environment variables C<HTTP_PROXY>, C<http_proxy>, C<HTTPS_PROXY>, |
|
863 |
-C<https_proxy>, C<NO_PROXY> and C<no_proxy> for proxy information. Automatic |
|
864 |
-proxy detection can be enabled with the C<MOJO_PROXY> environment variable. |
|
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. |
|
865 | 835 |
|
866 | 836 |
=head2 get |
867 | 837 |
|
868 |
- my $tx = $ua->get('kraih.com'); |
|
869 |
- my $tx = $ua->get('http://kraih.com' => {DNT => 1} => 'Hi!'); |
|
838 |
+ my $tx = $ua->get('example.com'); |
|
839 |
+ my $tx = $ua->get('http://example.com' => {DNT => 1} => 'Hi!'); |
|
840 |
+ my $tx = $ua->get('http://example.com' => {DNT => 1} => form => {a => 'b'}); |
|
841 |
+ my $tx = $ua->get('http://example.com' => {DNT => 1} => json => {a => 'b'}); |
|
870 | 842 |
|
871 | 843 |
Perform blocking HTTP C<GET> request and return resulting |
872 | 844 |
L<Mojo::Transaction::HTTP> object, takes the same arguments as |
873 | 845 |
L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also |
874 | 846 |
append a callback to perform requests non-blocking. |
875 | 847 |
|
876 |
- $ua->get('http://kraih.com' => sub { |
|
848 |
+ $ua->get('http://example.com' => sub { |
|
877 | 849 |
my ($ua, $tx) = @_; |
878 | 850 |
say $tx->res->body; |
879 | 851 |
}); |
... | ... |
@@ -881,15 +853,19 @@ append a callback to perform requests non-blocking. |
881 | 853 |
|
882 | 854 |
=head2 head |
883 | 855 |
|
884 |
- my $tx = $ua->head('kraih.com'); |
|
885 |
- my $tx = $ua->head('http://kraih.com' => {DNT => 1} => 'Hi!'); |
|
856 |
+ my $tx = $ua->head('example.com'); |
|
857 |
+ my $tx = $ua->head('http://example.com' => {DNT => 1} => 'Hi!'); |
|
858 |
+ my $tx = $ua->head( |
|
859 |
+ 'http://example.com' => {DNT => 1} => form => {a => 'b'}); |
|
860 |
+ my $tx = $ua->head( |
|
861 |
+ 'http://example.com' => {DNT => 1} => json => {a => 'b'}); |
|
886 | 862 |
|
887 | 863 |
Perform blocking HTTP C<HEAD> request and return resulting |
888 | 864 |
L<Mojo::Transaction::HTTP> object, takes the same arguments as |
889 | 865 |
L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also |
890 | 866 |
append a callback to perform requests non-blocking. |
891 | 867 |
|
892 |
- $ua->head('http://kraih.com' => sub { |
|
868 |
+ $ua->head('http://example.com' => sub { |
|
893 | 869 |
my ($ua, $tx) = @_; |
894 | 870 |
say $tx->res->body; |
895 | 871 |
}); |
... | ... |
@@ -897,21 +873,25 @@ append a callback to perform requests non-blocking. |
897 | 873 |
|
898 | 874 |
=head2 need_proxy |
899 | 875 |
|
900 |
- my $success = $ua->need_proxy('intranet.mojolicio.us'); |
|
876 |
+ my $success = $ua->need_proxy('intranet.example.com'); |
|
901 | 877 |
|
902 | 878 |
Check if request for domain would use a proxy server. |
903 | 879 |
|
904 | 880 |
=head2 options |
905 | 881 |
|
906 |
- my $tx = $ua->options('kraih.com'); |
|
907 |
- my $tx = $ua->options('http://kraih.com' => {DNT => 1} => 'Hi!'); |
|
882 |
+ my $tx = $ua->options('example.com'); |
|
883 |
+ my $tx = $ua->options('http://example.com' => {DNT => 1} => 'Hi!'); |
|
884 |
+ my $tx = $ua->options( |
|
885 |
+ 'http://example.com' => {DNT => 1} => form => {a => 'b'}); |
|
886 |
+ my $tx = $ua->options( |
|
887 |
+ 'http://example.com' => {DNT => 1} => json => {a => 'b'}); |
|
908 | 888 |
|
909 | 889 |
Perform blocking HTTP C<OPTIONS> request and return resulting |
910 | 890 |
L<Mojo::Transaction::HTTP> object, takes the same arguments as |
911 | 891 |
L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also |
912 | 892 |
append a callback to perform requests non-blocking. |
913 | 893 |
|
914 |
- $ua->options('http://kraih.com' => sub { |
|
894 |
+ $ua->options('http://example.com' => sub { |
|
915 | 895 |
my ($ua, $tx) = @_; |
916 | 896 |
say $tx->res->body; |
917 | 897 |
}); |
... | ... |
@@ -919,15 +899,19 @@ append a callback to perform requests non-blocking. |
919 | 899 |
|
920 | 900 |
=head2 patch |
921 | 901 |
|
922 |
- my $tx = $ua->patch('kraih.com'); |
|
923 |
- my $tx = $ua->patch('http://kraih.com' => {DNT => 1} => 'Hi!'); |
|
902 |
+ my $tx = $ua->patch('example.com'); |
|
903 |
+ my $tx = $ua->patch('http://example.com' => {DNT => 1} => 'Hi!'); |
|
904 |
+ my $tx = $ua->patch( |
|
905 |
+ 'http://example.com' => {DNT => 1} => form => {a => 'b'}); |
|
906 |
+ my $tx = $ua->patch( |
|
907 |
+ 'http://example.com' => {DNT => 1} => json => {a => 'b'}); |
|
924 | 908 |
|
925 | 909 |
Perform blocking HTTP C<PATCH> request and return resulting |
926 | 910 |
L<Mojo::Transaction::HTTP> object, takes the same arguments as |
927 | 911 |
L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also |
928 | 912 |
append a callback to perform requests non-blocking. |
929 | 913 |
|
930 |
- $ua->patch('http://kraih.com' => sub { |
|
914 |
+ $ua->patch('http://example.com' => sub { |
|
931 | 915 |
my ($ua, $tx) = @_; |
932 | 916 |
say $tx->res->body; |
933 | 917 |
}); |
... | ... |
@@ -935,47 +919,19 @@ append a callback to perform requests non-blocking. |
935 | 919 |
|
936 | 920 |
=head2 post |
937 | 921 |
|
938 |
- my $tx = $ua->post('kraih.com'); |
|
939 |
- my $tx = $ua->post('http://kraih.com' => {DNT => 1} => 'Hi!'); |
|
922 |
+ my $tx = $ua->post('example.com'); |
|
923 |
+ my $tx = $ua->post('http://example.com' => {DNT => 1} => 'Hi!'); |
|
924 |
+ my $tx = $ua->post( |
|
925 |
+ 'http://example.com' => {DNT => 1} => form => {a => 'b'}); |
|
926 |
+ my $tx = $ua->post( |
|
927 |
+ 'http://example.com' => {DNT => 1} => json => {a => 'b'}); |
|
940 | 928 |
|
941 | 929 |
Perform blocking HTTP C<POST> request and return resulting |
942 | 930 |
L<Mojo::Transaction::HTTP> object, takes the same arguments as |
943 | 931 |
L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also |
944 | 932 |
append a callback to perform requests non-blocking. |
945 | 933 |
|
946 |
- $ua->post('http://kraih.com' => sub { |
|
947 |
- my ($ua, $tx) = @_; |
|
948 |
- say $tx->res->body; |
|
949 |
- }); |
|
950 |
- Mojo::IOLoop->start unless Mojo::IOLoop->is_running; |
|
951 |
- |
|
952 |
-=head2 post_form |
|
953 |
- |
|
954 |
- my $tx = $ua->post_form('http://kraih.com' => {a => 'b'}); |
|
955 |
- my $tx = $ua->post_form('kraih.com', 'UTF-8', {a => 'b'}, {DNT => 1}); |
|
956 |
- |
|
957 |
-Perform blocking HTTP C<POST> request with form data and return resulting |
|
958 |
-L<Mojo::Transaction::HTTP> object, takes the same arguments as |
|
959 |
-L<Mojo::UserAgent::Transactor/"form">. You can also append a callback to |
|
960 |
-perform requests non-blocking. |
|
961 |
- |
|
962 |
- $ua->post_form('http://kraih.com' => {q => 'test'} => sub { |
|
963 |
- my ($ua, $tx) = @_; |
|
964 |
- say $tx->res->body; |
|
965 |
- }); |
|
966 |
- Mojo::IOLoop->start unless Mojo::IOLoop->is_running; |
|
967 |
- |
|
968 |
-=head2 post_json |
|
969 |
- |
|
970 |
- my $tx = $ua->post_json('http://kraih.com' => {a => 'b'}); |
|
971 |
- my $tx = $ua->post_json('kraih.com' => {a => 'b'} => {DNT => 1}); |
|
972 |
- |
|
973 |
-Perform blocking HTTP C<POST> request with JSON data and return resulting |
|
974 |
-L<Mojo::Transaction::HTTP> object, takes the same arguments as |
|
975 |
-L<Mojo::UserAgent::Transactor/"json">. You can also append a callback to |
|
976 |
-perform requests non-blocking. |
|
977 |
- |
|
978 |
- $ua->post_json('http://kraih.com' => {q => 'test'} => sub { |
|
934 |
+ $ua->post('http://example.com' => sub { |
|
979 | 935 |
my ($ua, $tx) = @_; |
980 | 936 |
say $tx->res->body; |
981 | 937 |
}); |
... | ... |
@@ -983,15 +939,17 @@ perform requests non-blocking. |
983 | 939 |
|
984 | 940 |
=head2 put |
985 | 941 |
|
986 |
- my $tx = $ua->put('kraih.com'); |
|
987 |
- my $tx = $ua->put('http://kraih.com' => {DNT => 1} => 'Hi!'); |
|
942 |
+ my $tx = $ua->put('example.com'); |
|
943 |
+ my $tx = $ua->put('http://example.com' => {DNT => 1} => 'Hi!'); |
|
944 |
+ my $tx = $ua->put('http://example.com' => {DNT => 1} => form => {a => 'b'}); |
|
945 |
+ my $tx = $ua->put('http://example.com' => {DNT => 1} => json => {a => 'b'}); |
|
988 | 946 |
|
989 | 947 |
Perform blocking HTTP C<PUT> request and return resulting |
990 | 948 |
L<Mojo::Transaction::HTTP> object, takes the same arguments as |
991 | 949 |
L<Mojo::UserAgent::Transactor/"tx"> (except for the method). You can also |
992 | 950 |
append a callback to perform requests non-blocking. |
993 | 951 |
|
994 |
- $ua->put('http://kraih.com' => sub { |
|
952 |
+ $ua->put('http://example.com' => sub { |
|
995 | 953 |
my ($ua, $tx) = @_; |
996 | 954 |
say $tx->res->body; |
997 | 955 |
}); |
... | ... |
@@ -1004,7 +962,7 @@ append a callback to perform requests non-blocking. |
1004 | 962 |
Perform blocking request. You can also append a callback to perform requests |
1005 | 963 |
non-blocking. |
1006 | 964 |
|
1007 |
- my $tx = $ua->build_tx(GET => 'http://kraih.com'); |
|
965 |
+ my $tx = $ua->build_tx(GET => 'http://example.com'); |
|
1008 | 966 |
$ua->start($tx => sub { |
1009 | 967 |
my ($ua, $tx) = @_; |
1010 | 968 |
say $tx->res->body; |
... | ... |
@@ -1013,17 +971,26 @@ non-blocking. |
1013 | 971 |
|
1014 | 972 |
=head2 websocket |
1015 | 973 |
|
1016 |
- $ua->websocket('ws://localhost:3000' => sub {...}); |
|
1017 |
- $ua->websocket('ws://localhost:3000' => {DNT => 1} => sub {...}); |
|
974 |
+ $ua->websocket('ws://example.com' => sub {...}); |
|
975 |
+ $ua->websocket( |
|
976 |
+ 'ws://example.com' => {DNT => 1} => ['v1.proto'] => sub {...}); |
|
1018 | 977 |
|
1019 | 978 |
Open a non-blocking WebSocket connection with transparent handshake, takes the |
1020 |
-same arguments as L<Mojo::UserAgent::Transactor/"websocket">. |
|
979 |
+same arguments as L<Mojo::UserAgent::Transactor/"websocket">. The callback |
|
980 |
+will receive either a L<Mojo::Transaction::WebSocket> or |
|
981 |
+L<Mojo::Transaction::HTTP> object. |
|
1021 | 982 |
|
1022 |
- $ua->websocket('ws://localhost:3000/echo' => sub { |
|
983 |
+ $ua->websocket('ws://example.com/echo' => sub { |
|
1023 | 984 |
my ($ua, $tx) = @_; |
985 |
+ say 'WebSocket handshake failed!' and return unless $tx->is_websocket; |
|
986 |
+ $tx->on(finish => sub { |
|
987 |
+ my ($tx, $code, $reason) = @_; |
|
988 |
+ say "WebSocket closed with status $code."; |
|
989 |
+ }); |
|
1024 | 990 |
$tx->on(message => sub { |
1025 | 991 |
my ($tx, $msg) = @_; |
1026 |
- say $msg; |
|
992 |
+ say "WebSocket message: $msg"; |
|
993 |
+ $tx->finish; |
|
1027 | 994 |
}); |
1028 | 995 |
$tx->send('Hi!'); |
1029 | 996 |
}); |
... | ... |
@@ -1031,8 +998,8 @@ same arguments as L<Mojo::UserAgent::Transactor/"websocket">. |
1031 | 998 |
|
1032 | 999 |
=head1 DEBUGGING |
1033 | 1000 |
|
1034 |
-You can set the C<MOJO_USERAGENT_DEBUG> environment variable to get some |
|
1035 |
-advanced diagnostics information printed to C<STDERR>. |
|
1001 |
+You can set the MOJO_USERAGENT_DEBUG environment variable to get some advanced |
|
1002 |
+diagnostics information printed to C<STDERR>. |
|
1036 | 1003 |
|
1037 | 1004 |
MOJO_USERAGENT_DEBUG=1 |
1038 | 1005 |
|
... | ... |
@@ -43,11 +43,11 @@ sub extract { |
43 | 43 |
for my $cookie (@{$tx->res->cookies}) { |
44 | 44 |
|
45 | 45 |
# Validate domain |
46 |
- my $host = lc $url->ihost; |
|
46 |
+ my $host = $url->ihost; |
|
47 | 47 |
my $domain = lc(defined $cookie->domain ? $cookie->domain : $host); |
48 | 48 |
$domain =~ s/^\.//; |
49 |
- next unless $host eq $domain || $host =~ /\Q.$domain\E$/; |
|
50 |
- next if $host =~ /\.\d+$/; |
|
49 |
+ next |
|
50 |
+ if $host ne $domain && ($host !~ /\Q.$domain\E$/ || $host =~ /\.\d+$/); |
|
51 | 51 |
$cookie->domain($domain); |
52 | 52 |
|
53 | 53 |
# Validate path |
... | ... |
@@ -61,7 +61,7 @@ sub extract { |
61 | 61 |
sub find { |
62 | 62 |
my ($self, $url) = @_; |
63 | 63 |
|
64 |
- return unless my $domain = lc(defined $url->ihost ? $url->ihost : ''); |
|
64 |
+ return unless my $domain = $url->ihost; |
|
65 | 65 |
my $path = $url->path->to_abs_string; |
66 | 66 |
my @found; |
67 | 67 |
while ($domain =~ /[^.]+\.[^.]+|localhost$/) { |
... | ... |
@@ -129,8 +129,8 @@ Mojo::UserAgent::CookieJar - Cookie jar for HTTP user agents |
129 | 129 |
|
130 | 130 |
=head1 DESCRIPTION |
131 | 131 |
|
132 |
-L<Mojo::UserAgent::CookieJar> is a minimalistic and relaxed cookie jar used by |
|
133 |
-L<Mojo::UserAgent>. |
|
132 |
+L<Mojo::UserAgent::CookieJar> is a minimalistic and relaxed cookie jar based |
|
133 |
+on RFC 6265 for L<Mojo::UserAgent>. |
|
134 | 134 |
|
135 | 135 |
=head1 ATTRIBUTES |
136 | 136 |
|
... | ... |
@@ -13,6 +13,19 @@ 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 |
+} |
|
22 |
+ |
|
23 |
+sub add_generator { |
|
24 |
+ my ($self, $name, $cb) = @_; |
|
25 |
+ $self->generators->{$name} = $cb; |
|
26 |
+ return $self; |
|
27 |
+} |
|
28 |
+ |
|
16 | 29 |
sub endpoint { |
17 | 30 |
my ($self, $tx) = @_; |
18 | 31 |
|
... | ... |
@@ -25,54 +38,11 @@ sub endpoint { |
25 | 38 |
|
26 | 39 |
# Proxy for normal HTTP requests |
27 | 40 |
return $self->_proxy($tx, $proto, $host, $port) |
28 |
- if $proto eq 'http' && lc($req->headers->upgrade || '') ne 'websocket'; |
|
41 |
+ if $proto eq 'http' && lc(defined $req->headers->upgrade ? $req->headers->upgrade : '') ne 'websocket'; |
|
29 | 42 |
|
30 | 43 |
return $proto, $host, $port; |
31 | 44 |
} |
32 | 45 |
|
33 |
-sub form { |
|
34 |
- my ($self, $url, $encoding) = (shift, shift, shift); |
|
35 |
- my $form = ref $encoding ? $encoding : shift; |
|
36 |
- $encoding = undef if ref $encoding; |
|
37 |
- |
|
38 |
- # Start with normal POST transaction |
|
39 |
- my $tx = $self->tx(POST => $url, @_); |
|
40 |
- |
|
41 |
- # Check for uploads and force multipart if necessary |
|
42 |
- my $multipart; |
|
43 |
- for my $value (map { ref $_ eq 'ARRAY' ? @$_ : $_ } values %$form) { |
|
44 |
- ++$multipart and last if ref $value eq 'HASH'; |
|
45 |
- } |
|
46 |
- my $req = $tx->req; |
|
47 |
- my $headers = $req->headers; |
|
48 |
- $headers->content_type('multipart/form-data') if $multipart; |
|
49 |
- |
|
50 |
- # Multipart |
|
51 |
- if ((defined $headers->content_type ? $headers->content_type : '') eq 'multipart/form-data') { |
|
52 |
- my $parts = $self->_multipart($encoding, $form); |
|
53 |
- $req->content( |
|
54 |
- Mojo::Content::MultiPart->new(headers => $headers, parts => $parts)); |
|
55 |
- } |
|
56 |
- |
|
57 |
- # Urlencoded |
|
58 |
- else { |
|
59 |
- $headers->content_type('application/x-www-form-urlencoded'); |
|
60 |
- my $p = Mojo::Parameters->new(map { $_ => $form->{$_} } sort keys %$form); |
|
61 |
- $p->charset($encoding) if defined $encoding; |
|
62 |
- $req->body($p->to_string); |
|
63 |
- } |
|
64 |
- |
|
65 |
- return $tx; |
|
66 |
-} |
|
67 |
- |
|
68 |
-sub json { |
|
69 |
- my ($self, $url, $data) = (shift, shift, shift); |
|
70 |
- my $tx = $self->tx(POST => $url, @_, Mojo::JSON->new->encode($data)); |
|
71 |
- my $headers = $tx->req->headers; |
|
72 |
- $headers->content_type('application/json') unless $headers->content_type; |
|
73 |
- return $tx; |
|
74 |
-} |
|
75 |
- |
|
76 | 46 |
sub peer { |
77 | 47 |
my ($self, $tx) = @_; |
78 | 48 |
return $self->_proxy($tx, $self->endpoint($tx)); |
... | ... |
@@ -83,14 +53,14 @@ sub proxy_connect { |
83 | 53 |
|
84 | 54 |
# Already a CONNECT request |
85 | 55 |
my $req = $old->req; |
86 |
- return undef if $req->method eq 'CONNECT'; |
|
56 |
+ return undef if uc $req->method eq 'CONNECT'; |
|
87 | 57 |
|
88 | 58 |
# No proxy |
89 | 59 |
return undef unless my $proxy = $req->proxy; |
90 | 60 |
|
91 | 61 |
# WebSocket and/or HTTPS |
92 | 62 |
my $url = $req->url; |
93 |
- my $upgrade = lc($req->headers->upgrade || ''); |
|
63 |
+ my $upgrade = lc(defined $req->headers->upgrade ? $req->headers->upgrade : ''); |
|
94 | 64 |
return undef unless $upgrade eq 'websocket' || $url->protocol eq 'https'; |
95 | 65 |
|
96 | 66 |
# CONNECT request |
... | ... |
@@ -116,8 +86,8 @@ sub redirect { |
116 | 86 |
# Clone request if necessary |
117 | 87 |
my $new = Mojo::Transaction::HTTP->new; |
118 | 88 |
my $req = $old->req; |
119 |
- my $method = $req->method; |
|
120 |
- if (grep { $_ eq $code } 301, 307, 308) { |
|
89 |
+ my $method = uc $req->method; |
|
90 |
+ if ($code eq 301 || $code eq 307 || $code eq 308) { |
|
121 | 91 |
return undef unless my $req = $req->clone; |
122 | 92 |
$new->req($req); |
123 | 93 |
$req->headers->remove('Host')->remove('Cookie')->remove('Referer'); |
... | ... |
@@ -140,8 +110,14 @@ sub tx { |
140 | 110 |
# Headers |
141 | 111 |
$req->headers->from_hash(shift) if ref $_[0] eq 'HASH'; |
142 | 112 |
|
113 |
+ # Generator |
|
114 |
+ if (@_ > 1) { |
|
115 |
+ return $tx unless my $generator = $self->generators->{shift()}; |
|
116 |
+ $self->$generator($tx, @_); |
|
117 |
+ } |
|
118 |
+ |
|
143 | 119 |
# Body |
144 |
- $req->body(shift) if @_; |
|
120 |
+ elsif (@_) { $req->body(shift) } |
|
145 | 121 |
|
146 | 122 |
return $tx; |
147 | 123 |
} |
... | ... |
@@ -158,11 +134,13 @@ sub websocket { |
158 | 134 |
my $self = shift; |
159 | 135 |
|
160 | 136 |
# New WebSocket transaction |
161 |
- my $tx = $self->tx(GET => @_); |
|
162 |
- my $req = $tx->req; |
|
163 |
- my $abs = $req->url->to_abs; |
|
164 |
- my $proto = $abs->protocol; |
|
165 |
- $req->url($abs->scheme($proto eq 'wss' ? 'https' : 'http')) if $proto; |
|
137 |
+ my $sub = ref $_[-1] eq 'ARRAY' ? pop : []; |
|
138 |
+ my $tx = $self->tx(GET => @_); |
|
139 |
+ my $req = $tx->req; |
|
140 |
+ $req->headers->sec_websocket_protocol(join ', ', @$sub) if @$sub; |
|
141 |
+ my $url = $req->url; |
|
142 |
+ my $proto = $url->protocol; |
|
143 |
+ $url->scheme($proto eq 'wss' ? 'https' : 'http') if $proto; |
|
166 | 144 |
|
167 | 145 |
# Handshake |
168 | 146 |
Mojo::Transaction::WebSocket->new(handshake => $tx)->client_handshake; |
... | ... |
@@ -170,8 +148,48 @@ sub websocket { |
170 | 148 |
return $tx; |
171 | 149 |
} |
172 | 150 |
|
151 |
+sub _form { |
|
152 |
+ my ($self, $tx, $form, %options) = @_; |
|
153 |
+ |
|
154 |
+ # Check for uploads and force multipart if necessary |
|
155 |
+ my $multipart; |
|
156 |
+ for my $value (map { ref $_ eq 'ARRAY' ? @$_ : $_ } values %$form) { |
|
157 |
+ ++$multipart and last if ref $value eq 'HASH'; |
|
158 |
+ } |
|
159 |
+ my $req = $tx->req; |
|
160 |
+ my $headers = $req->headers; |
|
161 |
+ $headers->content_type('multipart/form-data') if $multipart; |
|
162 |
+ |
|
163 |
+ # Multipart |
|
164 |
+ if ((defined $headers->content_type ? $headers->content_type : '') eq 'multipart/form-data') { |
|
165 |
+ my $parts = $self->_multipart($options{charset}, $form); |
|
166 |
+ $req->content( |
|
167 |
+ Mojo::Content::MultiPart->new(headers => $headers, parts => $parts)); |
|
168 |
+ return $tx; |
|
169 |
+ } |
|
170 |
+ |
|
171 |
+ # Query parameters or urlencoded |
|
172 |
+ my $p = Mojo::Parameters->new(map { $_ => $form->{$_} } sort keys %$form); |
|
173 |
+ $p->charset($options{charset}) if defined $options{charset}; |
|
174 |
+ my $method = uc $req->method; |
|
175 |
+ if ($method eq 'GET' || $method eq 'HEAD') { $req->url->query->merge($p) } |
|
176 |
+ else { |
|
177 |
+ $req->body($p->to_string); |
|
178 |
+ $headers->content_type('application/x-www-form-urlencoded'); |
|
179 |
+ } |
|
180 |
+ return $tx; |
|
181 |
+} |
|
182 |
+ |
|
183 |
+sub _json { |
|
184 |
+ my ($self, $tx, $data) = @_; |
|
185 |
+ $tx->req->body(Mojo::JSON->new->encode($data)); |
|
186 |
+ my $headers = $tx->req->headers; |
|
187 |
+ $headers->content_type('application/json') unless $headers->content_type; |
|
188 |
+ return $tx; |
|
189 |
+} |
|
190 |
+ |
|
173 | 191 |
sub _multipart { |
174 |
- my ($self, $encoding, $form) = @_; |
|
192 |
+ my ($self, $charset, $form) = @_; |
|
175 | 193 |
|
176 | 194 |
my @parts; |
177 | 195 |
for my $name (sort keys %$form) { |
... | ... |
@@ -199,18 +217,18 @@ sub _multipart { |
199 | 217 |
|
200 | 218 |
# Filename and headers |
201 | 219 |
$filename = delete $value->{filename} || $name; |
202 |
- $filename = encode $encoding, $filename if $encoding; |
|
220 |
+ $filename = encode $charset, $filename if $charset; |
|
203 | 221 |
$headers->from_hash($value); |
204 | 222 |
} |
205 | 223 |
|
206 | 224 |
# Field |
207 | 225 |
else { |
208 |
- $value = encode $encoding, $value if $encoding; |
|
226 |
+ $value = encode $charset, $value if $charset; |
|
209 | 227 |
$part->asset(Mojo::Asset::Memory->new->add_chunk($value)); |
210 | 228 |
} |
211 | 229 |
|
212 | 230 |
# Content-Disposition |
213 |
- $name = encode $encoding, $name if $encoding; |
|
231 |
+ $name = encode $charset, $name if $charset; |
|
214 | 232 |
my $disposition = qq{form-data; name="$name"}; |
215 | 233 |
$disposition .= qq{; filename="$filename"} if $filename; |
216 | 234 |
$headers->content_disposition($disposition); |
... | ... |
@@ -245,87 +263,56 @@ Mojo::UserAgent::Transactor - User agent transactor |
245 | 263 |
|
246 | 264 |
# Simple GET request |
247 | 265 |
my $t = Mojo::UserAgent::Transactor->new; |
248 |
- say $t->tx(GET => 'http://mojolicio.us')->req->to_string; |
|
266 |
+ say $t->tx(GET => 'http://example.com')->req->to_string; |
|
249 | 267 |
|
250 | 268 |
# PATCH request with "Do Not Track" header and content |
251 |
- say $t->tx(PATCH => 'mojolicio.us' => {DNT => 1} => 'Hi!')->req->to_string; |
|
269 |
+ say $t->tx(PATCH => 'example.com' => {DNT => 1} => 'Hi!')->req->to_string; |
|
252 | 270 |
|
253 | 271 |
# POST request with form data |
254 |
- say $t->form('http://kraih.com' => {a => [1, 2], b => 3})->req->to_string; |
|
272 |
+ say $t->tx(POST => 'example.com' => form => {a => 'b'})->req->to_string; |
|
255 | 273 |
|
256 |
- # POST request with JSON data |
|
257 |
- say $t->json('http://kraih.com' => {a => [1, 2], b => 3})->req->to_string; |
|
274 |
+ # PUT request with JSON data |
|
275 |
+ say $t->tx(PUT => 'example.com' => json => {a => 'b'})->req->to_string; |
|
258 | 276 |
|
259 | 277 |
=head1 DESCRIPTION |
260 | 278 |
|
261 | 279 |
L<Mojo::UserAgent::Transactor> is the transaction building and manipulation |
262 | 280 |
framework used by L<Mojo::UserAgent>. |
263 | 281 |
|
264 |
-=head1 METHODS |
|
282 |
+=head1 ATTRIBUTES |
|
265 | 283 |
|
266 |
-L<Mojo::UserAgent::Transactor> inherits all methods from L<Mojo::Base> and |
|
267 |
-implements the following new ones. |
|
284 |
+L<Mojo::UserAgent::Transactor> implements the following attributes. |
|
268 | 285 |
|
269 |
-=head2 endpoint |
|
286 |
+=head2 generators |
|
270 | 287 |
|
271 |
- my ($proto, $host, $port) = $t->endpoint(Mojo::Transaction::HTTP->new); |
|
288 |
+ my $generators = $t->generators; |
|
289 |
+ $t = $t->generators({foo => sub {...}}); |
|
272 | 290 |
|
273 |
-Actual endpoint for transaction. |
|
291 |
+Registered content generators. |
|
274 | 292 |
|
275 |
-=head2 form |
|
293 |
+=head1 METHODS |
|
276 | 294 |
|
277 |
- my $tx = $t->form('kraih.com' => {a => 'b'}); |
|
278 |
- my $tx = $t->form('http://kraih.com' => {a => 'b'}); |
|
279 |
- my $tx = $t->form('http://kraih.com' => {a => [qw(b c d)]}); |
|
280 |
- my $tx = $t->form('http://kraih.com' => {mytext => {file => '/foo.txt'}}); |
|
281 |
- my $tx = $t->form('http://kraih.com' => {mytext => {content => 'lalala'}}); |
|
282 |
- my $tx = $t->form('http://kraih.com' => |
|
283 |
- {mytexts => [{content => 'first'}, {content => 'second'}]}); |
|
284 |
- my $tx = $t->form('http://kraih.com' => { |
|
285 |
- myzip => { |
|
286 |
- file => Mojo::Asset::Memory->new->add_chunk('lalala'), |
|
287 |
- filename => 'foo.zip', |
|
288 |
- DNT => 1 |
|
289 |
- } |
|
290 |
- }); |
|
291 |
- my $tx = $t->form('http://kraih.com' => 'UTF-8' => {a => 'b'}); |
|
292 |
- my $tx = $t->form('http://kraih.com' => {a => 'b'} => {DNT => 1}); |
|
293 |
- my $tx = $t->form('http://kraih.com', 'UTF-8', {a => 'b'}, {DNT => 1}); |
|
295 |
+L<Mojo::UserAgent::Transactor> inherits all methods from L<Mojo::Base> and |
|
296 |
+implements the following new ones. |
|
294 | 297 |
|
295 |
-Versatile L<Mojo::Transaction::HTTP> transaction builder for C<POST> requests |
|
296 |
-with form data. |
|
298 |
+=head2 new |
|
297 | 299 |
|
298 |
- # Multipart upload with filename |
|
299 |
- my $tx = $t->form( |
|
300 |
- 'mojolicio.us' => {fun => {content => 'Hello!', filename => 'test.txt'}}); |
|
300 |
+ my $t = Mojo::UserAgent::Transactor->new; |
|
301 | 301 |
|
302 |
- # Multipart upload streamed from file |
|
303 |
- my $tx = $t->form('mojolicio.us' => {fun => {file => '/etc/passwd'}}); |
|
302 |
+Construct a new transactor and register C<form> and C<json> content |
|
303 |
+generators. |
|
304 | 304 |
|
305 |
-While the "multipart/form-data" content type will be automatically used |
|
306 |
-instead of "application/x-www-form-urlencoded" when necessary, you can also |
|
307 |
-enforce it by setting the header manually. |
|
305 |
+=head2 add_generator |
|
308 | 306 |
|
309 |
- # Force multipart |
|
310 |
- my $tx = $t->form( |
|
311 |
- 'http://kraih.com/foo', |
|
312 |
- {a => 'b'}, |
|
313 |
- {'Content-Type' => 'multipart/form-data'} |
|
314 |
- ); |
|
307 |
+ $t = $t->add_generator(foo => sub {...}); |
|
315 | 308 |
|
316 |
-=head2 json |
|
309 |
+Register a new content generator. |
|
317 | 310 |
|
318 |
- my $tx = $t->json('kraih.com' => {a => 'b'}); |
|
319 |
- my $tx = $t->json('http://kraih.com' => [1, 2, 3]); |
|
320 |
- my $tx = $t->json('http://kraih.com' => {a => 'b'} => {DNT => 1}); |
|
321 |
- my $tx = $t->json('http://kraih.com' => [1, 2, 3] => {DNT => 1}); |
|
311 |
+=head2 endpoint |
|
322 | 312 |
|
323 |
-Versatile L<Mojo::Transaction::HTTP> transaction builder for C<POST> requests |
|
324 |
-with JSON data. |
|
313 |
+ my ($proto, $host, $port) = $t->endpoint(Mojo::Transaction::HTTP->new); |
|
325 | 314 |
|
326 |
- # Change method |
|
327 |
- my $tx = $t->json('mojolicio.us/hello', {hello => 'world'}); |
|
328 |
- $tx->req->method('PATCH'); |
|
315 |
+Actual endpoint for transaction. |
|
329 | 316 |
|
330 | 317 |
=head2 peer |
331 | 318 |
|
... | ... |
@@ -349,26 +336,73 @@ C<307> or C<308> redirect response if possible. |
349 | 336 |
|
350 | 337 |
=head2 tx |
351 | 338 |
|
352 |
- my $tx = $t->tx(GET => 'kraih.com'); |
|
353 |
- my $tx = $t->tx(POST => 'http://kraih.com'); |
|
354 |
- my $tx = $t->tx(GET => 'http://kraih.com' => {DNT => 1}); |
|
355 |
- my $tx = $t->tx(PUT => 'http://kraih.com' => 'Hi!'); |
|
356 |
- my $tx = $t->tx(POST => 'http://kraih.com' => {DNT => 1} => 'Hi!'); |
|
339 |
+ my $tx = $t->tx(GET => 'example.com'); |
|
340 |
+ my $tx = $t->tx(POST => 'http://example.com'); |
|
341 |
+ my $tx = $t->tx(GET => 'http://example.com' => {DNT => 1}); |
|
342 |
+ my $tx = $t->tx(PUT => 'http://example.com' => 'Hi!'); |
|
343 |
+ my $tx = $t->tx(PUT => 'http://example.com' => form => {a => 'b'}); |
|
344 |
+ my $tx = $t->tx(PUT => 'http://example.com' => json => {a => 'b'}); |
|
345 |
+ my $tx = $t->tx(POST => 'http://example.com' => {DNT => 1} => 'Hi!'); |
|
346 |
+ my $tx = $t->tx( |
|
347 |
+ PUT => 'http://example.com' => {DNT => 1} => form => {a => 'b'}); |
|
348 |
+ my $tx = $t->tx( |
|
349 |
+ PUT => 'http://example.com' => {DNT => 1} => json => {a => 'b'}); |
|
357 | 350 |
|
358 | 351 |
Versatile general purpose L<Mojo::Transaction::HTTP> transaction builder for |
359 |
-requests. |
|
352 |
+requests, with support for content generators. |
|
360 | 353 |
|
361 | 354 |
# Inspect generated request |
362 |
- say $t->tx(GET => 'mojolicio.us' => {DNT => 1} => 'Bye!')->req->to_string; |
|
355 |
+ say $t->tx(GET => 'example.com' => {DNT => 1} => 'Bye!')->req->to_string; |
|
363 | 356 |
|
364 | 357 |
# Streaming response |
365 |
- my $tx = $t->tx(GET => 'http://mojolicio.us'); |
|
366 |
- $tx->res->body(sub { say $_[1] }); |
|
358 |
+ my $tx = $t->tx(GET => 'http://example.com'); |
|
359 |
+ $tx->res->content->unsubscribe('read')->on(read => sub { say $_[1] }); |
|
367 | 360 |
|
368 | 361 |
# Custom socket |
369 |
- my $tx = $t->tx(GET => 'http://mojolicio.us'); |
|
362 |
+ my $tx = $t->tx(GET => 'http://example.com'); |
|
370 | 363 |
$tx->connection($sock); |
371 | 364 |
|
365 |
+ # Generate query parameters |
|
366 |
+ my $tx = $t->tx(GET => 'http://example.com' => form => {a => 'b'}); |
|
367 |
+ |
|
368 |
+ # Use form generator with custom charset |
|
369 |
+ my $tx = $t->tx( |
|
370 |
+ PUT => 'http://example.com' => form => {a => 'b'} => charset => 'UTF-8'); |
|
371 |
+ |
|
372 |
+ # Multiple form values with the same name |
|
373 |
+ my $tx = $t->tx(PUT => 'http://example.com' => form => {a => [qw(b c d)]}); |
|
374 |
+ |
|
375 |
+ # Multipart upload streamed from file |
|
376 |
+ my $tx = $t->tx( |
|
377 |
+ PUT => 'http://example.com' => form => {mytext => {file => '/foo.txt'}}); |
|
378 |
+ |
|
379 |
+ # Multipart upload with in-memory content |
|
380 |
+ my $tx = $t->tx( |
|
381 |
+ POST => 'http://example.com' => form => {mytext => {content => 'lala'}}); |
|
382 |
+ |
|
383 |
+ # Upload multiple files with same name |
|
384 |
+ my $tx = $t->tx(POST => 'http://example.com' => |
|
385 |
+ form => {mytext => [{content => 'first'}, {content => 'second'}]}); |
|
386 |
+ |
|
387 |
+ # Customized upload with filename and header |
|
388 |
+ my $tx = $t->tx(POST => 'http://example.com' => form => { |
|
389 |
+ myzip => { |
|
390 |
+ file => Mojo::Asset::Memory->new->add_chunk('lalala'), |
|
391 |
+ filename => 'foo.zip', |
|
392 |
+ 'Content-Type' => 'text/plain' |
|
393 |
+ } |
|
394 |
+ }); |
|
395 |
+ |
|
396 |
+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 |
|
399 |
+"multipart/form-data" content type when necessary or when the header has been |
|
400 |
+set manually. |
|
401 |
+ |
|
402 |
+ # Force "multipart/form-data" |
|
403 |
+ my $headers = {'Content-Type' => 'multipart/form-data'}; |
|
404 |
+ my $tx = $t->tx(POST => 'example.com' => $headers => form => {a => 'b'}); |
|
405 |
+ |
|
372 | 406 |
=head2 upgrade |
373 | 407 |
|
374 | 408 |
my $tx = $t->upgrade(Mojo::Transaction::HTTP->new); |
... | ... |
@@ -378,8 +412,8 @@ handshake if possible. |
378 | 412 |
|
379 | 413 |
=head2 websocket |
380 | 414 |
|
381 |
- my $tx = $t->websocket('ws://localhost:3000'); |
|
382 |
- my $tx = $t->websocket('ws://localhost:3000' => {DNT => 1}); |
|
415 |
+ my $tx = $t->websocket('ws://example.com'); |
|
416 |
+ my $tx = $t->websocket('ws://example.com' => {DNT => 1} => ['v1.proto']); |
|
383 | 417 |
|
384 | 418 |
Versatile L<Mojo::Transaction::HTTP> transaction builder for WebSocket |
385 | 419 |
handshake requests. |
... | ... |
@@ -1,13 +1,18 @@ |
1 | 1 |
package Mojo::Util; |
2 | 2 |
use Mojo::Base 'Exporter'; |
3 | 3 |
|
4 |
-use Carp 'croak'; |
|
4 |
+use Carp qw(carp croak); |
|
5 | 5 |
use Digest::MD5 qw(md5 md5_hex); |
6 | 6 |
BEGIN {eval {require Digest::SHA; import Digest::SHA qw(sha1 sha1_hex)}} |
7 | 7 |
use Encode 'find_encoding'; |
8 | 8 |
use File::Basename 'dirname'; |
9 | 9 |
use File::Spec::Functions 'catfile'; |
10 | 10 |
use MIME::Base64 qw(decode_base64 encode_base64); |
11 |
+use Time::HiRes (); |
|
12 |
+ |
|
13 |
+# Check for monotonic clock support |
|
14 |
+use constant MONOTONIC => eval |
|
15 |
+ '!!Time::HiRes::clock_gettime(Time::HiRes::CLOCK_MONOTONIC())'; |
|
11 | 16 |
|
12 | 17 |
# Punycode bootstring parameters |
13 | 18 |
use constant { |
... | ... |
@@ -23,46 +28,33 @@ use constant { |
23 | 28 |
# To update HTML5 entities run this command |
24 | 29 |
# perl examples/entities.pl > lib/Mojo/entities.txt |
25 | 30 |
my %ENTITIES; |
26 |
-{ |
|
27 |
- open my $entities, '<', catfile(dirname(__FILE__), 'entities.txt'); |
|
28 |
- for my $entity (<$entities>) { |
|
29 |
- next unless $entity =~ /^(\S+)\s+U\+(\S+)(?:\s+U\+(\S+))?/; |
|
30 |
- $ENTITIES{$1} = defined $3 ? (chr(hex $2) . chr(hex $3)) : chr(hex $2); |
|
31 |
- } |
|
31 |
+for my $line (split "\x0a", slurp(catfile dirname(__FILE__), 'entities.txt')) { |
|
32 |
+ next unless $line =~ /^(\S+)\s+U\+(\S+)(?:\s+U\+(\S+))?/; |
|
33 |
+ $ENTITIES{$1} = defined $3 ? (chr(hex $2) . chr(hex $3)) : chr(hex $2); |
|
32 | 34 |
} |
33 | 35 |
|
34 |
-# DEPRECATED in Rainbow! |
|
35 |
-my %REVERSE = ("\x{0027}" => '#39;'); |
|
36 |
-$REVERSE{$ENTITIES{$_}} = defined $REVERSE{$ENTITIES{$_}} ? $REVERSE{$ENTITIES{$_}} : $_ |
|
37 |
- for sort { @{[$a =~ /[A-Z]/g]} <=> @{[$b =~ /[A-Z]/g]} } |
|
38 |
- sort grep {/;/} keys %ENTITIES; |
|
39 |
- |
|
40 | 36 |
# Encoding cache |
41 | 37 |
my %CACHE; |
42 | 38 |
|
43 | 39 |
our @EXPORT_OK = ( |
44 | 40 |
qw(b64_decode b64_encode camelize class_to_file class_to_path decamelize), |
45 |
- qw(decode encode get_line hmac_md5_sum hmac_sha1_sum html_unescape), |
|
46 |
- qw(md5_bytes md5_sum monkey_patch punycode_decode punycode_encode quote), |
|
47 |
- qw(secure_compare sha1_bytes sha1_sum slurp spurt squish trim unquote), |
|
48 |
- qw(url_escape url_unescape xml_escape xor_encode) |
|
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) |
|
49 | 45 |
); |
50 | 46 |
|
51 |
-# DEPRECATED in Rainbow! |
|
52 |
-push @EXPORT_OK, 'html_escape'; |
|
53 |
- |
|
54 | 47 |
sub b64_decode { decode_base64($_[0]) } |
55 |
- |
|
56 | 48 |
sub b64_encode { encode_base64($_[0], $_[1]) } |
57 | 49 |
|
58 | 50 |
sub camelize { |
59 |
- my $string = shift; |
|
60 |
- return $string if $string =~ /^[A-Z]/; |
|
51 |
+ my $str = shift; |
|
52 |
+ return $str if $str =~ /^[A-Z]/; |
|
61 | 53 |
|
62 | 54 |
# Camel case words |
63 | 55 |
return join '::', map { |
64 | 56 |
join '', map { ucfirst lc } split /_/, $_ |
65 |
- } split /-/, $string; |
|
57 |
+ } split /-/, $str; |
|
66 | 58 |
} |
67 | 59 |
|
68 | 60 |
sub class_to_file { |
... | ... |
@@ -75,12 +67,12 @@ sub class_to_file { |
75 | 67 |
sub class_to_path { join '.', join('/', split /::|'/, shift), 'pm' } |
76 | 68 |
|
77 | 69 |
sub decamelize { |
78 |
- my $string = shift; |
|
79 |
- return $string if $string !~ /^[A-Z]/; |
|
70 |
+ my $str = shift; |
|
71 |
+ return $str if $str !~ /^[A-Z]/; |
|
80 | 72 |
|
81 | 73 |
# Module parts |
82 | 74 |
my @parts; |
83 |
- for my $part (split /::/, $string) { |
|
75 |
+ for my $part (split /::/, $str) { |
|
84 | 76 |
|
85 | 77 |
# Snake case words |
86 | 78 |
my @words; |
... | ... |
@@ -98,6 +90,11 @@ sub decode { |
98 | 90 |
return $bytes; |
99 | 91 |
} |
100 | 92 |
|
93 |
+sub deprecated { |
|
94 |
+ local $Carp::CarpLevel = 1; |
|
95 |
+ $ENV{MOJO_FATAL_DEPRECATIONS} ? croak(@_) : carp(@_); |
|
96 |
+} |
|
97 |
+ |
|
101 | 98 |
sub encode { _encoding($_[0])->encode("$_[1]") } |
102 | 99 |
|
103 | 100 |
sub get_line { |
... | ... |
@@ -112,26 +109,22 @@ sub get_line { |
112 | 109 |
return $line; |
113 | 110 |
} |
114 | 111 |
|
115 |
-sub hmac_md5_sum { _hmac(\&md5, @_) } |
|
116 |
-sub hmac_sha1_sum { _hmac(\&sha1, @_) } |
|
117 |
- |
|
118 |
-# DEPRECATED in Rainbow! |
|
119 |
-sub html_escape { |
|
120 |
- warn <<EOF; |
|
121 |
-Mojo::Util->html_escape is DEPRECATED in favor of Mojo::Util->xml_escape!!! |
|
122 |
-EOF |
|
123 |
- my ($string, $pattern) = @_; |
|
124 |
- $pattern ||= '^\n\r\t !#$%(-;=?-~'; |
|
125 |
- return $string unless $string =~ /[^$pattern]/; |
|
126 |
- $string =~ s/([$pattern])/_encode($1)/ge; |
|
127 |
- return $string; |
|
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)); |
|
128 | 120 |
} |
129 | 121 |
|
130 | 122 |
sub html_unescape { |
131 |
- my $string = shift; |
|
132 |
- $string |
|
123 |
+ my $str = shift; |
|
124 |
+ return $str if index($str, '&') == -1; |
|
125 |
+ $str |
|
133 | 126 |
=~ s/&(?:\#((?:\d{1,7}|x[[:xdigit:]]{1,6}));|(\w+;?))/_decode($1, $2)/ge; |
134 |
- return $string; |
|
127 |
+ return $str; |
|
135 | 128 |
} |
136 | 129 |
|
137 | 130 |
sub md5_bytes { md5(@_) } |
... | ... |
@@ -195,7 +188,7 @@ sub punycode_encode { |
195 | 188 |
my @input = map {ord} split //, $output; |
196 | 189 |
my @chars = sort grep { $_ >= PC_INITIAL_N } @input; |
197 | 190 |
|
198 |
- # Handle non basic characters |
|
191 |
+ # Handle non-basic characters |
|
199 | 192 |
$output =~ s/[^\x00-\x7f]+//gs; |
200 | 193 |
my $h = my $b = length $output; |
201 | 194 |
$output .= "\x2d" if $b > 0; |
... | ... |
@@ -252,9 +245,9 @@ sub punycode_encode { |
252 | 245 |
} |
253 | 246 |
|
254 | 247 |
sub quote { |
255 |
- my $string = shift; |
|
256 |
- $string =~ s/(["\\])/\\$1/g; |
|
257 |
- return qq{"$string"}; |
|
248 |
+ my $str = shift; |
|
249 |
+ $str =~ s/(["\\])/\\$1/g; |
|
250 |
+ return qq{"$str"}; |
|
258 | 251 |
} |
259 | 252 |
|
260 | 253 |
sub secure_compare { |
... | ... |
@@ -285,49 +278,55 @@ sub spurt { |
285 | 278 |
} |
286 | 279 |
|
287 | 280 |
sub squish { |
288 |
- my $string = trim(@_); |
|
289 |
- $string =~ s/\s+/ /g; |
|
290 |
- return $string; |
|
281 |
+ my $str = trim(@_); |
|
282 |
+ $str =~ s/\s+/ /g; |
|
283 |
+ return $str; |
|
284 |
+} |
|
285 |
+ |
|
286 |
+sub steady_time () { |
|
287 |
+ MONOTONIC |
|
288 |
+ ? Time::HiRes::clock_gettime(Time::HiRes::CLOCK_MONOTONIC()) |
|
289 |
+ : Time::HiRes::time; |
|
291 | 290 |
} |
292 | 291 |
|
293 | 292 |
sub trim { |
294 |
- my $string = shift; |
|
295 |
- $string =~ s/^\s+|\s+$//g; |
|
296 |
- return $string; |
|
293 |
+ my $str = shift; |
|
294 |
+ $str =~ s/^\s+|\s+$//g; |
|
295 |
+ return $str; |
|
297 | 296 |
} |
298 | 297 |
|
299 | 298 |
sub unquote { |
300 |
- my $string = shift; |
|
301 |
- return $string unless $string =~ s/^"(.*)"$/$1/g; |
|
302 |
- $string =~ s/\\\\/\\/g; |
|
303 |
- $string =~ s/\\"/"/g; |
|
304 |
- return $string; |
|
299 |
+ my $str = shift; |
|
300 |
+ return $str unless $str =~ s/^"(.*)"$/$1/g; |
|
301 |
+ $str =~ s/\\\\/\\/g; |
|
302 |
+ $str =~ s/\\"/"/g; |
|
303 |
+ return $str; |
|
305 | 304 |
} |
306 | 305 |
|
307 | 306 |
sub url_escape { |
308 |
- my ($string, $pattern) = @_; |
|
307 |
+ my ($str, $pattern) = @_; |
|
309 | 308 |
$pattern ||= '^A-Za-z0-9\-._~'; |
310 |
- $string =~ s/([$pattern])/sprintf('%%%02X',ord($1))/ge; |
|
311 |
- return $string; |
|
309 |
+ $str =~ s/([$pattern])/sprintf('%%%02X',ord($1))/ge; |
|
310 |
+ return $str; |
|
312 | 311 |
} |
313 | 312 |
|
314 | 313 |
sub url_unescape { |
315 |
- my $string = shift; |
|
316 |
- return $string if index($string, '%') == -1; |
|
317 |
- $string =~ s/%([[:xdigit:]]{2})/chr(hex($1))/ge; |
|
318 |
- return $string; |
|
314 |
+ my $str = shift; |
|
315 |
+ return $str if index($str, '%') == -1; |
|
316 |
+ $str =~ s/%([[:xdigit:]]{2})/chr(hex($1))/ge; |
|
317 |
+ return $str; |
|
319 | 318 |
} |
320 | 319 |
|
321 | 320 |
sub xml_escape { |
322 |
- my $string = shift; |
|
321 |
+ my $str = shift; |
|
323 | 322 |
|
324 |
- $string =~ s/&/&/g; |
|
325 |
- $string =~ s/</</g; |
|
326 |
- $string =~ s/>/>/g; |
|
327 |
- $string =~ s/"/"/g; |
|
328 |
- $string =~ s/'/'/g; |
|
323 |
+ $str =~ s/&/&/g; |
|
324 |
+ $str =~ s/</</g; |
|
325 |
+ $str =~ s/>/>/g; |
|
326 |
+ $str =~ s/"/"/g; |
|
327 |
+ $str =~ s/'/'/g; |
|
329 | 328 |
|
330 |
- return $string; |
|
329 |
+ return $str; |
|
331 | 330 |
} |
332 | 331 |
|
333 | 332 |
sub xor_encode { |
... | ... |
@@ -357,42 +356,24 @@ sub _adapt { |
357 | 356 |
} |
358 | 357 |
|
359 | 358 |
sub _decode { |
359 |
+ my ($point, $name) = @_; |
|
360 | 360 |
|
361 |
- # Numeric |
|
362 |
- return substr($_[0], 0, 1) eq 'x' ? chr(hex $_[0]) : chr($_[0]) unless $_[1]; |
|
361 |
+ # Code point |
|
362 |
+ return chr($point !~ /^x/ ? $point : hex $point) unless defined $name; |
|
363 | 363 |
|
364 | 364 |
# Find entity name |
365 |
- my $rest = ''; |
|
366 |
- my $entity = $_[1]; |
|
367 |
- while (length $entity) { |
|
368 |
- return "$ENTITIES{$entity}$rest" if exists $ENTITIES{$entity}; |
|
369 |
- $rest = chop($entity) . $rest; |
|
365 |
+ my $rest = ''; |
|
366 |
+ while (length $name) { |
|
367 |
+ return "$ENTITIES{$name}$rest" if exists $ENTITIES{$name}; |
|
368 |
+ $rest = chop($name) . $rest; |
|
370 | 369 |
} |
371 |
- return "&$_[1]"; |
|
372 |
-} |
|
373 |
- |
|
374 |
-# DEPRECATED in Rainbow! |
|
375 |
-sub _encode { |
|
376 |
- return exists $REVERSE{$_[0]} ? "&$REVERSE{$_[0]}" : "&#@{[ord($_[0])]};"; |
|
370 |
+ return "&$rest"; |
|
377 | 371 |
} |
378 | 372 |
|
379 | 373 |
sub _encoding { |
380 | 374 |
$CACHE{$_[0]} = defined $CACHE{$_[0]} ? $CACHE{$_[0]} : defined find_encoding($_[0]) ? find_encoding($_[0]) : croak "Unknown encoding '$_[0]'"; |
381 | 375 |
} |
382 | 376 |
|
383 |
-sub _hmac { |
|
384 |
- my ($hash, $string, $secret) = @_; |
|
385 |
- |
|
386 |
- # Secret |
|
387 |
- $secret = $secret ? "$secret" : 'Very insecure!'; |
|
388 |
- $secret = $hash->($secret) if length $secret > 64; |
|
389 |
- |
|
390 |
- # HMAC |
|
391 |
- my $ipad = $secret ^ (chr(0x36) x 64); |
|
392 |
- my $opad = $secret ^ (chr(0x5c) x 64); |
|
393 |
- return unpack 'H*', $hash->($opad . $hash->($ipad . $string)); |
|
394 |
-} |
|
395 |
- |
|
396 | 377 |
1; |
397 | 378 |
|
398 | 379 |
=head1 NAME |
... | ... |
@@ -403,8 +384,8 @@ Mojo::Util - Portable utility functions |
403 | 384 |
|
404 | 385 |
use Mojo::Util qw(b64_encode url_escape url_unescape); |
405 | 386 |
|
406 |
- my $string = 'test=23'; |
|
407 |
- my $escaped = url_escape $string; |
|
387 |
+ my $str = 'test=23'; |
|
388 |
+ my $escaped = url_escape $str; |
|
408 | 389 |
say url_unescape $escaped; |
409 | 390 |
say b64_encode $escaped, ''; |
410 | 391 |
|
... | ... |
@@ -418,14 +399,14 @@ L<Mojo::Util> implements the following functions. |
418 | 399 |
|
419 | 400 |
=head2 b64_decode |
420 | 401 |
|
421 |
- my $string = b64_decode $b64; |
|
402 |
+ my $str = b64_decode $b64; |
|
422 | 403 |
|
423 | 404 |
Base64 decode string. |
424 | 405 |
|
425 | 406 |
=head2 b64_encode |
426 | 407 |
|
427 |
- my $b64 = b64_encode $string; |
|
428 |
- my $b64 = b64_encode $string, "\n"; |
|
408 |
+ my $b64 = b64_encode $str; |
|
409 |
+ my $b64 = b64_encode $str, "\n"; |
|
429 | 410 |
|
430 | 411 |
Base64 encode string, the line ending defaults to a newline. |
431 | 412 |
|
... | ... |
@@ -485,6 +466,13 @@ Convert camel case string to snake case and replace C<::> with C<->. |
485 | 466 |
|
486 | 467 |
Decode bytes to characters and return C<undef> if decoding failed. |
487 | 468 |
|
469 |
+=head2 deprecated |
|
470 |
+ |
|
471 |
+ deprecated 'foo is DEPRECATED in favor of bar'; |
|
472 |
+ |
|
473 |
+Warn about deprecated feature from perspective of caller. You can also set the |
|
474 |
+MOJO_FATAL_DEPRECATIONS environment variable to make them die instead. |
|
475 |
+ |
|
488 | 476 |
=head2 encode |
489 | 477 |
|
490 | 478 |
my $bytes = encode 'UTF-8', $chars; |
... | ... |
@@ -493,38 +481,32 @@ Encode characters to bytes. |
493 | 481 |
|
494 | 482 |
=head2 get_line |
495 | 483 |
|
496 |
- my $line = get_line \$string; |
|
484 |
+ my $line = get_line \$str; |
|
497 | 485 |
|
498 | 486 |
Extract whole line from string or return C<undef>. Lines are expected to end |
499 | 487 |
with C<0x0d 0x0a> or C<0x0a>. |
500 | 488 |
|
501 |
-=head2 hmac_md5_sum |
|
502 |
- |
|
503 |
- my $checksum = hmac_md5_sum $string, 'passw0rd'; |
|
504 |
- |
|
505 |
-Generate HMAC-MD5 checksum for string. |
|
506 |
- |
|
507 | 489 |
=head2 hmac_sha1_sum |
508 | 490 |
|
509 |
- my $checksum = hmac_sha1_sum $string, 'passw0rd'; |
|
491 |
+ my $checksum = hmac_sha1_sum $str, 'passw0rd'; |
|
510 | 492 |
|
511 | 493 |
Generate HMAC-SHA1 checksum for string. |
512 | 494 |
|
513 | 495 |
=head2 html_unescape |
514 | 496 |
|
515 |
- my $string = html_unescape $escaped; |
|
497 |
+ my $str = html_unescape $escaped; |
|
516 | 498 |
|
517 | 499 |
Unescape all HTML entities in string. |
518 | 500 |
|
519 | 501 |
=head2 md5_bytes |
520 | 502 |
|
521 |
- my $checksum = md5_bytes $string; |
|
503 |
+ my $checksum = md5_bytes $str; |
|
522 | 504 |
|
523 | 505 |
Generate binary MD5 checksum for string. |
524 | 506 |
|
525 | 507 |
=head2 md5_sum |
526 | 508 |
|
527 |
- my $checksum = md5_sum $string; |
|
509 |
+ my $checksum = md5_sum $str; |
|
528 | 510 |
|
529 | 511 |
Generate MD5 checksum for string. |
530 | 512 |
|
... | ... |
@@ -542,37 +524,37 @@ Monkey patch functions into package. |
542 | 524 |
|
543 | 525 |
=head2 punycode_decode |
544 | 526 |
|
545 |
- my $string = punycode_decode $punycode; |
|
527 |
+ my $str = punycode_decode $punycode; |
|
546 | 528 |
|
547 | 529 |
Punycode decode string. |
548 | 530 |
|
549 | 531 |
=head2 punycode_encode |
550 | 532 |
|
551 |
- my $punycode = punycode_encode $string; |
|
533 |
+ my $punycode = punycode_encode $str; |
|
552 | 534 |
|
553 | 535 |
Punycode encode string. |
554 | 536 |
|
555 | 537 |
=head2 quote |
556 | 538 |
|
557 |
- my $quoted = quote $string; |
|
539 |
+ my $quoted = quote $str; |
|
558 | 540 |
|
559 | 541 |
Quote string. |
560 | 542 |
|
561 | 543 |
=head2 secure_compare |
562 | 544 |
|
563 |
- my $success = secure_compare $string1, $string2; |
|
545 |
+ my $success = secure_compare $str1, $str2; |
|
564 | 546 |
|
565 | 547 |
Constant time comparison algorithm to prevent timing attacks. |
566 | 548 |
|
567 | 549 |
=head2 sha1_bytes |
568 | 550 |
|
569 |
- my $checksum = sha1_bytes $string; |
|
551 |
+ my $checksum = sha1_bytes $str; |
|
570 | 552 |
|
571 | 553 |
Generate binary SHA1 checksum for string. |
572 | 554 |
|
573 | 555 |
=head2 sha1_sum |
574 | 556 |
|
575 |
- my $checksum = sha1_sum $string; |
|
557 |
+ my $checksum = sha1_sum $str; |
|
576 | 558 |
|
577 | 559 |
Generate SHA1 checksum for string. |
578 | 560 |
|
... | ... |
@@ -590,46 +572,53 @@ Write all data at once to file. |
590 | 572 |
|
591 | 573 |
=head2 squish |
592 | 574 |
|
593 |
- my $squished = squish $string; |
|
575 |
+ my $squished = squish $str; |
|
594 | 576 |
|
595 | 577 |
Trim whitespace characters from both ends of string and then change all |
596 | 578 |
consecutive groups of whitespace into one space each. |
597 | 579 |
|
580 |
+=head2 steady_time |
|
581 |
+ |
|
582 |
+ my $time = steady_time; |
|
583 |
+ |
|
584 |
+High resolution time, resilient to time jumps if a monotonic clock is |
|
585 |
+available through L<Time::HiRes>. |
|
586 |
+ |
|
598 | 587 |
=head2 trim |
599 | 588 |
|
600 |
- my $trimmed = trim $string; |
|
589 |
+ my $trimmed = trim $str; |
|
601 | 590 |
|
602 | 591 |
Trim whitespace characters from both ends of string. |
603 | 592 |
|
604 | 593 |
=head2 unquote |
605 | 594 |
|
606 |
- my $string = unquote $quoted; |
|
595 |
+ my $str = unquote $quoted; |
|
607 | 596 |
|
608 | 597 |
Unquote string. |
609 | 598 |
|
610 | 599 |
=head2 url_escape |
611 | 600 |
|
612 |
- my $escaped = url_escape $string; |
|
613 |
- my $escaped = url_escape $string, '^A-Za-z0-9\-._~'; |
|
601 |
+ my $escaped = url_escape $str; |
|
602 |
+ my $escaped = url_escape $str, '^A-Za-z0-9\-._~'; |
|
614 | 603 |
|
615 | 604 |
Percent encode unsafe characters in string, the pattern used defaults to |
616 | 605 |
C<^A-Za-z0-9\-._~>. |
617 | 606 |
|
618 | 607 |
=head2 url_unescape |
619 | 608 |
|
620 |
- my $string = url_unescape $escaped; |
|
609 |
+ my $str = url_unescape $escaped; |
|
621 | 610 |
|
622 | 611 |
Decode percent encoded characters in string. |
623 | 612 |
|
624 | 613 |
=head2 xml_escape |
625 | 614 |
|
626 |
- my $escaped = xml_escape $string; |
|
615 |
+ my $escaped = xml_escape $str; |
|
627 | 616 |
|
628 | 617 |
Escape unsafe characters C<&>, C<E<lt>>, C<E<gt>>, C<"> and C<'> in string. |
629 | 618 |
|
630 | 619 |
=head2 xor_encode |
631 | 620 |
|
632 |
- my $encoded = xor_encode $string, $key; |
|
621 |
+ my $encoded = xor_encode $str, $key; |
|
633 | 622 |
|
634 | 623 |
XOR encode string with variable length key. |
635 | 624 |
|
... | ... |
@@ -14,6 +14,7 @@ use Mojolicious::Sessions; |
14 | 14 |
use Mojolicious::Static; |
15 | 15 |
use Mojolicious::Types; |
16 | 16 |
use Scalar::Util qw(blessed weaken); |
17 |
+use Time::HiRes 'gettimeofday'; |
|
17 | 18 |
|
18 | 19 |
has commands => sub { |
19 | 20 |
my $commands = Mojolicious::Commands->new(app => shift); |
... | ... |
@@ -21,7 +22,7 @@ has commands => sub { |
21 | 22 |
return $commands; |
22 | 23 |
}; |
23 | 24 |
has controller_class => 'Mojolicious::Controller'; |
24 |
-has mode => sub { $ENV{MOJO_MODE} || 'development' }; |
|
25 |
+has mode => sub { $ENV{MOJO_MODE} || $ENV{PLACK_ENV} || 'development' }; |
|
25 | 26 |
has moniker => sub { decamelize ref shift }; |
26 | 27 |
has plugins => sub { Mojolicious::Plugins->new }; |
27 | 28 |
has renderer => sub { Mojolicious::Renderer->new }; |
... | ... |
@@ -32,29 +33,26 @@ has secret => sub { |
32 | 33 |
# Warn developers about insecure default |
33 | 34 |
$self->log->debug('Your secret passphrase needs to be changed!!!'); |
34 | 35 |
|
35 |
- # Default to application name |
|
36 |
- return ref $self; |
|
36 |
+ # Default to moniker |
|
37 |
+ return $self->moniker; |
|
37 | 38 |
}; |
38 | 39 |
has sessions => sub { Mojolicious::Sessions->new }; |
39 | 40 |
has static => sub { Mojolicious::Static->new }; |
40 | 41 |
has types => sub { Mojolicious::Types->new }; |
41 | 42 |
|
42 |
-our $CODENAME = 'Rainbow'; |
|
43 |
-our $VERSION = '3.84'; |
|
43 |
+our $CODENAME = 'Top Hat'; |
|
44 |
+our $VERSION = '4.07'; |
|
44 | 45 |
|
45 | 46 |
sub AUTOLOAD { |
46 | 47 |
my $self = shift; |
47 | 48 |
|
48 |
- # Method |
|
49 | 49 |
my ($package, $method) = our $AUTOLOAD =~ /^([\w:]+)::(\w+)$/; |
50 | 50 |
croak "Undefined subroutine &${package}::$method called" |
51 | 51 |
unless blessed $self && $self->isa(__PACKAGE__); |
52 | 52 |
|
53 |
- # Check for helper |
|
53 |
+ # Call helper with fresh controller |
|
54 | 54 |
croak qq{Can't locate object method "$method" via package "$package"} |
55 | 55 |
unless my $helper = $self->renderer->helpers->{$method}; |
56 |
- |
|
57 |
- # Call helper with fresh controller |
|
58 | 56 |
return $self->controller_class->new(app => $self)->$helper(@_); |
59 | 57 |
} |
60 | 58 |
|
... | ... |
@@ -72,18 +70,18 @@ sub new { |
72 | 70 |
|
73 | 71 |
# Hide controller attributes/methods and "handler" |
74 | 72 |
$r->hide(qw(AUTOLOAD DESTROY app cookie finish flash handler on param)); |
75 |
- $r->hide(qw(redirect_to render render_data render_exception render_json)); |
|
76 |
- $r->hide(qw(render_not_found render_partial render_static render_text)); |
|
77 |
- $r->hide(qw(rendered req res respond_to send session signed_cookie stash)); |
|
78 |
- $r->hide(qw(tx ua url_for write write_chunk)); |
|
73 |
+ $r->hide(qw(redirect_to render render_exception render_maybe)); |
|
74 |
+ $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'); |
|
79 | 77 |
|
80 | 78 |
# Check if we have a log directory |
81 | 79 |
my $mode = $self->mode; |
82 | 80 |
$self->log->path($home->rel_file("log/$mode.log")) |
83 | 81 |
if -w $home->rel_file('log'); |
84 | 82 |
|
85 |
- $self->plugin($_) for qw(HeaderCondition DefaultHelpers TagHelpers); |
|
86 |
- $self->plugin($_) for qw(EPLRenderer EPRenderer RequestTimer PoweredBy); |
|
83 |
+ $self->plugin($_) |
|
84 |
+ for qw(HeaderCondition DefaultHelpers TagHelpers EPLRenderer EPRenderer); |
|
87 | 85 |
|
88 | 86 |
# Exception handling should be first in chain |
89 | 87 |
$self->hook(around_dispatch => \&_exception); |
... | ... |
@@ -112,7 +110,7 @@ sub dispatch { |
112 | 110 |
|
113 | 111 |
# Prepare transaction |
114 | 112 |
my $tx = $c->tx; |
115 |
- $c->res->code(undef) if $tx->is_websocket; |
|
113 |
+ $tx->res->code(undef) if $tx->is_websocket; |
|
116 | 114 |
$self->sessions->load($c); |
117 | 115 |
my $plugins = $self->plugins->emit_hook(before_dispatch => $c); |
118 | 116 |
|
... | ... |
@@ -120,11 +118,14 @@ sub dispatch { |
120 | 118 |
$self->static->dispatch($c) and $plugins->emit_hook(after_static => $c) |
121 | 119 |
unless $tx->res->code; |
122 | 120 |
|
123 |
- # DEPRECATED in Rainbow! |
|
124 |
- if ($plugins->has_subscribers('after_static_dispatch')) { |
|
125 |
- warn <<EOF and $plugins->emit_hook_reverse(after_static_dispatch => $c); |
|
126 |
-after_static_dispatch hook is DEPRECATED in favor of before_routes!!! |
|
127 |
-EOF |
|
121 |
+ # Start timer (ignore static files) |
|
122 |
+ my $stash = $c->stash; |
|
123 |
+ unless ($stash->{'mojo.static'} || $stash->{'mojo.started'}) { |
|
124 |
+ my $req = $c->req; |
|
125 |
+ my $method = $req->method; |
|
126 |
+ my $path = $req->url->path->to_abs_string; |
|
127 |
+ $self->log->debug(qq{$method "$path".}); |
|
128 |
+ $stash->{'mojo.started'} = [gettimeofday]; |
|
128 | 129 |
} |
129 | 130 |
|
130 | 131 |
# Routes |
... | ... |
@@ -157,11 +158,7 @@ sub handler { |
157 | 158 |
unless $self->{dispatch}; |
158 | 159 |
|
159 | 160 |
# Process with chain |
160 |
- unless (eval { $self->plugins->emit_chain(around_dispatch => $c) }) { |
|
161 |
- $self->log->fatal("Processing request failed: $@"); |
|
162 |
- $tx->res->code(500); |
|
163 |
- $tx->resume; |
|
164 |
- } |
|
161 |
+ $self->plugins->emit_chain(around_dispatch => $c); |
|
165 | 162 |
|
166 | 163 |
# Delayed response |
167 | 164 |
$self->log->debug('Nothing has been rendered, expecting delayed response.') |
... | ... |
@@ -255,10 +252,11 @@ L<Mojolicious::Controller>. |
255 | 252 |
my $mode = $app->mode; |
256 | 253 |
$app = $app->mode('production'); |
257 | 254 |
|
258 |
-The operating mode for your application, defaults to the value of the |
|
259 |
-C<MOJO_MODE> environment variable or C<development>. You can also add per |
|
260 |
-mode logic to your application by defining methods named C<${mode}_mode> in |
|
261 |
-the application class, which will be called right before C<startup>. |
|
255 |
+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>. |
|
262 | 260 |
|
263 | 261 |
sub development_mode { |
264 | 262 |
my $self = shift; |
... | ... |
@@ -332,9 +330,9 @@ startup method to define the url endpoints for your application. |
332 | 330 |
$app = $app->secret('passw0rd'); |
333 | 331 |
|
334 | 332 |
A secret passphrase used for signed cookies and the like, defaults to the |
335 |
-application name which is not very secure, so you should change it!!! As long |
|
336 |
-as you are using the insecure default there will be debug messages in the log |
|
337 |
-file reminding you to change your passphrase. |
|
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 |
|
335 |
+messages in the log file reminding you to change your passphrase. |
|
338 | 336 |
|
339 | 337 |
=head2 sessions |
340 | 338 |
|
... | ... |
@@ -346,6 +344,9 @@ object. You can usually leave this alone, see |
346 | 344 |
L<Mojolicious::Controller/"session"> for more information about working with |
347 | 345 |
session data. |
348 | 346 |
|
347 |
+ # Change name of cookie used for all sessions |
|
348 |
+ $app->sessions->cookie_name('mysession'); |
|
349 |
+ |
|
349 | 350 |
=head2 static |
350 | 351 |
|
351 | 352 |
my $static = $app->static; |
... | ... |
@@ -368,6 +369,7 @@ L<Mojolicious::Static> object. |
368 | 369 |
Responsible for connecting file extensions with MIME types, defaults to a |
369 | 370 |
L<Mojolicious::Types> object. |
370 | 371 |
|
372 |
+ # Add custom MIME type |
|
371 | 373 |
$app->types->type(twt => 'text/tweet'); |
372 | 374 |
|
373 | 375 |
=head1 METHODS |
... | ... |
@@ -382,7 +384,8 @@ new ones. |
382 | 384 |
Construct a new L<Mojolicious> application, calling C<${mode}_mode> and |
383 | 385 |
C<startup> in the process. Will automatically detect your home directory and |
384 | 386 |
set up logging based on your current operating mode. Also sets up the |
385 |
-renderer, static file server and a default set of plugins. |
|
387 |
+renderer, static file server, a default set of plugins and an |
|
388 |
+C<around_dispatch> hook with the default exception handling. |
|
386 | 389 |
|
387 | 390 |
=head2 build_tx |
388 | 391 |
|
... | ... |
@@ -393,18 +396,16 @@ object. |
393 | 396 |
|
394 | 397 |
=head2 defaults |
395 | 398 |
|
396 |
- my $defaults = $app->defaults; |
|
397 |
- my $foo = $app->defaults('foo'); |
|
398 |
- $app = $app->defaults({foo => 'bar'}); |
|
399 |
- $app = $app->defaults(foo => 'bar'); |
|
399 |
+ my $hash = $app->defaults; |
|
400 |
+ my $foo = $app->defaults('foo'); |
|
401 |
+ $app = $app->defaults({foo => 'bar'}); |
|
402 |
+ $app = $app->defaults(foo => 'bar'); |
|
400 | 403 |
|
401 | 404 |
Default values for L<Mojolicious::Controller/"stash">, assigned for every new |
402 | 405 |
request. |
403 | 406 |
|
404 |
- # Manipulate defaults |
|
405 |
- $app->defaults->{foo} = 'bar'; |
|
406 |
- my $foo = $app->defaults->{foo}; |
|
407 |
- delete $app->defaults->{foo}; |
|
407 |
+ # Remove value |
|
408 |
+ my $foo = delete $app->defaults->{foo}; |
|
408 | 409 |
|
409 | 410 |
=head2 dispatch |
410 | 411 |
|
... | ... |
@@ -449,7 +450,7 @@ requests indiscriminately. |
449 | 450 |
# Dispatchers will not run if there's already a response code defined |
450 | 451 |
$app->hook(before_dispatch => sub { |
451 | 452 |
my $c = shift; |
452 |
- $c->render(text => 'Skipped dispatchers!') |
|
453 |
+ $c->render(text => 'Skipped static file server and router!') |
|
453 | 454 |
if $c->req->url->path->to_route =~ /do_not_dispatch/; |
454 | 455 |
}); |
455 | 456 |
|
... | ... |
@@ -486,7 +487,8 @@ Very useful for rewriting incoming requests and other preprocessing tasks. |
486 | 487 |
|
487 | 488 |
=item after_static |
488 | 489 |
|
489 |
-Emitted after the static file server decided to serve a static file. |
|
490 |
+Emitted after a static file response has been generated by the static file |
|
491 |
+server. |
|
490 | 492 |
|
491 | 493 |
$app->hook(after_static => sub { |
492 | 494 |
my $c = shift; |
... | ... |
@@ -498,8 +500,8 @@ controller object) |
498 | 500 |
|
499 | 501 |
=item before_routes |
500 | 502 |
|
501 |
-Emitted after the static file server decided if a static file should be served |
|
502 |
-and before the router starts its work. |
|
503 |
+Emitted after the static file server determined if a static file should be |
|
504 |
+served and before the router starts its work. |
|
503 | 505 |
|
504 | 506 |
$app->hook(before_routes => sub { |
505 | 507 |
my $c = shift; |
... | ... |
@@ -623,13 +625,13 @@ L<http://creativecommons.org/licenses/by-sa/3.0>. |
623 | 625 |
|
624 | 626 |
=head2 jQuery |
625 | 627 |
|
626 |
- Copyright (C) 2005, 2012 jQuery Foundation, Inc. |
|
628 |
+ Copyright (C) 2005, 2013 jQuery Foundation, Inc. |
|
627 | 629 |
|
628 | 630 |
Licensed under the MIT License, L<http://creativecommons.org/licenses/MIT>. |
629 | 631 |
|
630 | 632 |
=head2 prettify.js |
631 | 633 |
|
632 |
- Copyright (C) 2006, Google Inc. |
|
634 |
+ Copyright (C) 2006, 2013 Google Inc. |
|
633 | 635 |
|
634 | 636 |
Licensed under the Apache License, Version 2.0 |
635 | 637 |
L<http://www.apache.org/licenses/LICENSE-2.0>. |
... | ... |
@@ -639,6 +641,8 @@ L<http://www.apache.org/licenses/LICENSE-2.0>. |
639 | 641 |
Every major release of L<Mojolicious> has a code name, these are the ones that |
640 | 642 |
have been used in the past. |
641 | 643 |
|
644 |
+4.0, C<Top Hat> (u1F3A9) |
|
645 |
+ |
|
642 | 646 |
3.0, C<Rainbow> (u1F308) |
643 | 647 |
|
644 | 648 |
2.0, C<Leaf Fluttering In Wind> (u1F343) |
... | ... |
@@ -657,6 +661,11 @@ have been used in the past. |
657 | 661 |
|
658 | 662 |
0.999920, C<Snowman> (u2603) |
659 | 663 |
|
664 |
+=head1 SPONSORS |
|
665 |
+ |
|
666 |
+Some of the work on this distribution has been sponsored by |
|
667 |
+L<The Perl Foundation|http://www.perlfoundation.org>, thank you! |
|
668 |
+ |
|
660 | 669 |
=head1 PROJECT FOUNDER |
661 | 670 |
|
662 | 671 |
Sebastian Riedel, C<sri@cpan.org> |
... | ... |
@@ -671,6 +680,8 @@ Abhijit Menon-Sen, C<ams@cpan.org> |
671 | 680 |
|
672 | 681 |
Glen Hinkle, C<tempire@cpan.org> |
673 | 682 |
|
683 |
+Joel Berger, C<jberger@cpan.org> |
|
684 |
+ |
|
674 | 685 |
Marcus Ramberg, C<mramberg@cpan.org> |
675 | 686 |
|
676 | 687 |
=back |
... | ... |
@@ -753,6 +764,8 @@ Dmitriy Shalashov |
753 | 764 |
|
754 | 765 |
Dmitry Konstantinov |
755 | 766 |
|
767 |
+Dominik Jarmulowicz |
|
768 |
+ |
|
756 | 769 |
Dominique Dumont |
757 | 770 |
|
758 | 771 |
Douglas Christopher Wilson |
... | ... |
@@ -779,8 +792,6 @@ Jaroslav Muhin |
779 | 792 |
|
780 | 793 |
Jesse Vincent |
781 | 794 |
|
782 |
-Joel Berger |
|
783 |
- |
|
784 | 795 |
Johannes Plunien |
785 | 796 |
|
786 | 797 |
John Kingsley |
... | ... |
@@ -877,8 +888,6 @@ Tatsuhiko Miyagawa |
877 | 888 |
|
878 | 889 |
Terrence Brannon |
879 | 890 |
|
880 |
-The Perl Foundation |
|
881 |
- |
|
882 | 891 |
Tomas Znamenacek |
883 | 892 |
|
884 | 893 |
Ulrich Habel |
... | ... |
@@ -24,8 +24,8 @@ 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_form( |
|
28 |
- "https://$user:$password\@pause.perl.org/pause/authenquery" => { |
|
27 |
+ my $tx = Mojo::UserAgent->new->detect_proxy->post( |
|
28 |
+ "https://$user:$password\@pause.perl.org/pause/authenquery" => form => { |
|
29 | 29 |
HIDDENNAME => $user, |
30 | 30 |
CAN_MULTIPART => 1, |
31 | 31 |
pause99_add_uri_upload => basename($file), |
... | ... |
@@ -57,7 +57,7 @@ use strict; |
57 | 57 |
use warnings; |
58 | 58 |
|
59 | 59 |
use FindBin; |
60 |
-use lib "$FindBin::Bin/../lib"; |
|
60 |
+BEGIN { unshift @INC, "$FindBin::Bin/../lib" } |
|
61 | 61 |
|
62 | 62 |
# Start command line interface for application |
63 | 63 |
require Mojolicious::Commands; |
... | ... |
@@ -86,7 +86,7 @@ plugin '<%= $name %>'; |
86 | 86 |
|
87 | 87 |
get '/' => sub { |
88 | 88 |
my $self = shift; |
89 |
- $self->render_text('Hello Mojo!'); |
|
89 |
+ $self->render(text => 'Hello Mojo!'); |
|
90 | 90 |
}; |
91 | 91 |
|
92 | 92 |
my $t = Test::Mojo->new; |
... | ... |
@@ -8,6 +8,7 @@ use Mojo::JSON; |
8 | 8 |
use Mojo::JSON::Pointer; |
9 | 9 |
use Mojo::UserAgent; |
10 | 10 |
use Mojo::Util qw(decode encode); |
11 |
+use Scalar::Util 'weaken'; |
|
11 | 12 |
|
12 | 13 |
has description => "Perform HTTP request.\n"; |
13 | 14 |
has usage => <<"EOF"; |
... | ... |
@@ -27,7 +28,7 @@ usage: $0 get [OPTIONS] URL [SELECTOR|JSON-POINTER] [COMMANDS] |
27 | 28 |
|
28 | 29 |
These options are available: |
29 | 30 |
-C, --charset <charset> Charset of HTML/XML content, defaults to auto |
30 |
- detection or "UTF-8". |
|
31 |
+ detection. |
|
31 | 32 |
-c, --content <content> Content to send with request. |
32 | 33 |
-H, --header <name:value> Additional HTTP header. |
33 | 34 |
-M, --method <method> HTTP method to use, defaults to "GET". |
... | ... |
@@ -46,59 +47,38 @@ sub run { |
46 | 47 |
'r|redirect' => \my $redirect, |
47 | 48 |
'v|verbose' => \my $verbose; |
48 | 49 |
|
49 |
- die $self->usage unless my $url = decode 'UTF-8', do {my $tmp = shift @args; defined $tmp ? $tmp : ''}; |
|
50 |
+ @args = map { decode 'UTF-8', $_ } @args; |
|
51 |
+ die $self->usage unless my $url = shift @args; |
|
50 | 52 |
my $selector = shift @args; |
51 | 53 |
|
52 | 54 |
# Parse header pairs |
53 | 55 |
my %headers; |
54 | 56 |
/^\s*([^:]+)\s*:\s*(.+)$/ and $headers{$1} = $2 for @headers; |
55 | 57 |
|
56 |
- # Use global event loop singleton |
|
58 |
+ # Detect proxy for absolute URLs |
|
57 | 59 |
my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton); |
60 |
+ $url !~ m!^/! ? $ua->detect_proxy : $ua->app($self->app); |
|
58 | 61 |
$ua->max_redirects(10) if $redirect; |
59 | 62 |
|
60 |
- # Detect proxy for absolute URLs |
|
61 |
- if ($url !~ m!/!) { $ua->detect_proxy } |
|
62 |
- else { $ua->app($self->app) } |
|
63 |
- |
|
64 |
- # Do the real work with "start" event |
|
65 |
- my $v = my $buffer = ''; |
|
63 |
+ my $buffer = ''; |
|
66 | 64 |
$ua->on( |
67 | 65 |
start => sub { |
68 |
- my $tx = pop; |
|
69 |
- |
|
70 |
- # Verbose callback |
|
71 |
- my $v = $verbose; |
|
72 |
- my $cb = sub { |
|
73 |
- my $res = shift; |
|
74 |
- |
|
75 |
- # Wait for headers |
|
76 |
- return unless $v && $res->headers->is_finished; |
|
77 |
- $v = undef; |
|
78 |
- |
|
79 |
- # Show request |
|
80 |
- my $req = $tx->req; |
|
81 |
- my $startline = $req->build_start_line; |
|
82 |
- my $req_headers = $req->build_headers; |
|
83 |
- warn "$startline$req_headers"; |
|
84 |
- |
|
85 |
- # Show response |
|
86 |
- my $version = $res->version; |
|
87 |
- my $code = $res->code; |
|
88 |
- my $msg = $res->message; |
|
89 |
- my $res_headers = $res->headers->to_string; |
|
90 |
- warn "HTTP/$version $code $msg\n$res_headers\n\n"; |
|
91 |
- }; |
|
92 |
- $tx->res->on(progress => $cb); |
|
93 |
- |
|
94 |
- # Stream content |
|
95 |
- $tx->res->body( |
|
96 |
- sub { |
|
97 |
- $cb->(my $res = shift); |
|
98 |
- |
|
99 |
- # Ignore intermediate content |
|
100 |
- return if $redirect && $res->is_status_class(300); |
|
101 |
- $selector ? ($buffer .= pop) : print(pop); |
|
66 |
+ my ($ua, $tx) = @_; |
|
67 |
+ |
|
68 |
+ # Verbose |
|
69 |
+ weaken $tx; |
|
70 |
+ $tx->res->content->on( |
|
71 |
+ body => sub { |
|
72 |
+ warn $tx->req->$_ for qw(build_start_line build_headers); |
|
73 |
+ warn $tx->res->$_ for qw(build_start_line build_headers); |
|
74 |
+ } |
|
75 |
+ ) if $verbose; |
|
76 |
+ |
|
77 |
+ # Stream content (ignore redirects) |
|
78 |
+ $tx->res->content->unsubscribe('read')->on( |
|
79 |
+ read => sub { |
|
80 |
+ return if $redirect && $tx->res->is_status_class(300); |
|
81 |
+ defined $selector ? ($buffer .= pop) : print pop; |
|
102 | 82 |
} |
103 | 83 |
); |
104 | 84 |
} |
... | ... |
@@ -113,8 +93,8 @@ sub run { |
113 | 93 |
warn qq{Problem loading URL "$url". ($err)\n} if $err && !$code; |
114 | 94 |
|
115 | 95 |
# JSON Pointer |
116 |
- return unless $selector; |
|
117 |
- my $type = $tx->res->headers->content_type || ''; |
|
96 |
+ return unless defined $selector; |
|
97 |
+ my $type = defined $tx->res->headers->content_type ? $tx->res->headers->content_type : ''; |
|
118 | 98 |
return _json($buffer, $selector) if $type =~ /json/i; |
119 | 99 |
|
120 | 100 |
# Selector |
... | ... |
@@ -126,19 +106,16 @@ sub _json { |
126 | 106 |
return unless my $data = $json->decode(shift); |
127 | 107 |
return unless defined($data = Mojo::JSON::Pointer->new->get($data, shift)); |
128 | 108 |
return _say($data) unless ref $data eq 'HASH' || ref $data eq 'ARRAY'; |
129 |
- say($json->encode($data)); |
|
109 |
+ say $json->encode($data); |
|
130 | 110 |
} |
131 | 111 |
|
132 |
-sub _say { |
|
133 |
- return unless length(my $value = shift); |
|
134 |
- say encode('UTF-8', $value); |
|
135 |
-} |
|
112 |
+sub _say { say encode('UTF-8', $_[0]) if length $_[0] } |
|
136 | 113 |
|
137 | 114 |
sub _select { |
138 | 115 |
my ($buffer, $selector, $charset, @args) = @_; |
139 | 116 |
|
140 |
- my $dom = Mojo::DOM->new->charset($charset)->parse($buffer); |
|
141 |
- my $results = $dom->find($selector); |
|
117 |
+ $buffer = do {my $tmp = decode($charset, $buffer); defined $tmp ? $tmp : $buffer} if $charset; |
|
118 |
+ my $results = Mojo::DOM->new($buffer)->find($selector); |
|
142 | 119 |
|
143 | 120 |
my $finished; |
144 | 121 |
while (defined(my $command = shift @args)) { |
... | ... |
@@ -25,7 +25,7 @@ These options are available: |
25 | 25 |
value of MOJO_INACTIVITY_TIMEOUT or 15. |
26 | 26 |
--lock-file <path> Path to lock file, defaults to a random |
27 | 27 |
file. |
28 |
- -L, --lock-timeout <seconds> Lock timeout, defaults to 0.5. |
|
28 |
+ -L, --lock-timeout <seconds> Lock timeout, defaults to 1. |
|
29 | 29 |
-l, --listen <location> One or more locations you want to |
30 | 30 |
listen on, defaults to the value of |
31 | 31 |
MOJO_LISTEN or "http://*:3000". |
... | ... |
@@ -48,7 +48,7 @@ sub run { |
48 | 48 |
my $prefork = Mojo::Server::Prefork->new(app => $self->app); |
49 | 49 |
GetOptionsFromArray \@args, |
50 | 50 |
'A|accepts=i' => sub { $prefork->accepts($_[1]) }, |
51 |
- 'a|accept-interval=i' => sub { $prefork->accept_interval($_[1]) }, |
|
51 |
+ 'a|accept-interval=f' => sub { $prefork->accept_interval($_[1]) }, |
|
52 | 52 |
'b|backlog=i' => sub { $prefork->backlog($_[1]) }, |
53 | 53 |
'c|clients=i' => sub { $prefork->max_clients($_[1]) }, |
54 | 54 |
'G|graceful-timeout=i' => sub { $prefork->graceful_timeout($_[1]) }, |
... | ... |
@@ -57,7 +57,7 @@ sub run { |
57 | 57 |
'H|heartbeat-timeout=i' => sub { $prefork->heartbeat_timeout($_[1]) }, |
58 | 58 |
'i|inactivity=i' => sub { $prefork->inactivity_timeout($_[1]) }, |
59 | 59 |
'lock-file=s' => sub { $prefork->lock_file($_[1]) }, |
60 |
- 'L|lock-timeout=i' => sub { $prefork->lock_timeout($_[1]) }, |
|
60 |
+ 'L|lock-timeout=f' => sub { $prefork->lock_timeout($_[1]) }, |
|
61 | 61 |
'l|listen=s' => \my @listen, |
62 | 62 |
'multi-accept=i' => sub { $prefork->multi_accept($_[1]) }, |
63 | 63 |
'P|pid-file=s' => sub { $prefork->pid_file($_[1]) }, |
... | ... |
@@ -3,6 +3,7 @@ use Mojo::Base 'Mojolicious::Command'; |
3 | 3 |
|
4 | 4 |
use MojoLegacy::re 'regexp_pattern'; |
5 | 5 |
use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case); |
6 |
+use Mojo::Util 'encode'; |
|
6 | 7 |
|
7 | 8 |
has description => "Show available routes.\n"; |
8 | 9 |
has usage => <<"EOF"; |
... | ... |
@@ -25,67 +26,51 @@ sub run { |
25 | 26 |
sub _draw { |
26 | 27 |
my ($self, $routes, $verbose) = @_; |
27 | 28 |
|
28 |
- # Calculate column widths |
|
29 |
- my @length = (0, 0, 0); |
|
29 |
+ my @table = (0, 0, 0); |
|
30 | 30 |
for my $node (@$routes) { |
31 | 31 |
|
32 |
- # Pattern |
|
33 |
- my $len = length $node->[0]; |
|
34 |
- $length[0] = $len if $len > $length[0]; |
|
35 |
- |
|
36 | 32 |
# Methods |
37 |
- unless (defined $node->[1]->via) { $len = length '*' } |
|
38 |
- else { $len = length(join ',', @{$node->[1]->via}) } |
|
39 |
- $length[1] = $len if $len > $length[1]; |
|
33 |
+ my $via = $node->[0]->via; |
|
34 |
+ $node->[2] = !$via ? '*' : uc join ',', @$via; |
|
40 | 35 |
|
41 | 36 |
# Name |
42 |
- $len = length $node->[1]->name; |
|
43 |
- $len += 2 if $node->[1]->has_custom_name; |
|
44 |
- $length[2] = $len if $len > $length[2]; |
|
37 |
+ my $name = $node->[0]->name; |
|
38 |
+ $node->[3] = $node->[0]->has_custom_name ? qq{"$name"} : $name; |
|
39 |
+ |
|
40 |
+ # Check column width |
|
41 |
+ $table[$_] = _max($table[$_], length $node->[$_ + 1]) for 0 .. 2; |
|
45 | 42 |
} |
46 | 43 |
|
47 |
- # Draw all routes |
|
48 | 44 |
for my $node (@$routes) { |
49 |
- my @parts; |
|
50 |
- |
|
51 |
- # Pattern |
|
52 |
- push @parts, $node->[0]; |
|
53 |
- $parts[-1] .= ' ' x ($length[0] - length $parts[-1]); |
|
54 |
- |
|
55 |
- # Methods |
|
56 |
- my $methods; |
|
57 |
- unless (defined $node->[1]->via) { $methods = '*' } |
|
58 |
- else { $methods = uc join ',', @{$node->[1]->via} } |
|
59 |
- push @parts, $methods . ' ' x ($length[1] - length $methods); |
|
45 |
+ my @parts = map { _padding($node->[$_ + 1], $table[$_]) } 0 .. 2; |
|
60 | 46 |
|
61 |
- # Name |
|
62 |
- my $name = $node->[1]->name; |
|
63 |
- $name = qq{"$name"} if $node->[1]->has_custom_name; |
|
64 |
- push @parts, $name . ' ' x ($length[2] - length $name); |
|
65 |
- |
|
66 |
- # Regex |
|
67 |
- my $pattern = $node->[1]->pattern; |
|
68 |
- $pattern->match('/', $node->[1]->is_endpoint); |
|
47 |
+ # Regex (verbose) |
|
48 |
+ my $pattern = $node->[0]->pattern; |
|
49 |
+ $pattern->match('/', $node->[0]->is_endpoint); |
|
69 | 50 |
my $regex = (regexp_pattern $pattern->regex)[0]; |
70 |
- my $format = (regexp_pattern $pattern->format_regex || '')[0]; |
|
51 |
+ my $format = (regexp_pattern($pattern->format_regex || ''))[0]; |
|
71 | 52 |
my $optional |
72 | 53 |
= !$pattern->constraints->{format} || $pattern->defaults->{format}; |
73 |
- $format .= '?' if $format && $optional; |
|
74 |
- push @parts, $format ? "$regex$format" : $regex if $verbose; |
|
54 |
+ $regex .= $optional ? "(?:$format)?" : $format if $format; |
|
55 |
+ push @parts, $regex if $verbose; |
|
75 | 56 |
|
76 |
- say join(' ', @parts); |
|
57 |
+ say encode('UTF-8', join(' ', @parts)); |
|
77 | 58 |
} |
78 | 59 |
} |
79 | 60 |
|
61 |
+sub _max { $_[1] > $_[0] ? $_[1] : $_[0] } |
|
62 |
+ |
|
63 |
+sub _padding { $_[0] . ' ' x ($_[1] - length $_[0]) } |
|
64 |
+ |
|
80 | 65 |
sub _walk { |
81 |
- my ($self, $node, $depth, $routes) = @_; |
|
66 |
+ my ($self, $route, $depth, $routes) = @_; |
|
82 | 67 |
|
83 | 68 |
my $prefix = ''; |
84 | 69 |
if (my $i = $depth * 2) { $prefix .= ' ' x $i . '+' } |
85 |
- push @$routes, [$prefix . ($node->pattern->pattern || '/'), $node]; |
|
70 |
+ push @$routes, [$route, $prefix . ($route->pattern->pattern || '/')]; |
|
86 | 71 |
|
87 | 72 |
$depth++; |
88 |
- $self->_walk($_, $depth, $routes) for @{$node->children}; |
|
73 |
+ $self->_walk($_, $depth, $routes) for @{$route->children}; |
|
89 | 74 |
$depth--; |
90 | 75 |
} |
91 | 76 |
|
... | ... |
@@ -31,7 +31,7 @@ sub run { |
31 | 31 |
die "Can't find test directory.\n" unless -d $path; |
32 | 32 |
|
33 | 33 |
my $home = Mojo::Home->new($path); |
34 |
- /\.t$/ and push(@args, $home->rel_file($_)) for @{$home->list_files}; |
|
34 |
+ /\.t$/ and push @args, $home->rel_file($_) for @{$home->list_files}; |
|
35 | 35 |
say "Running tests from '", realpath($path), "'."; |
36 | 36 |
} |
37 | 37 |
|
... | ... |
@@ -23,9 +23,9 @@ CORE |
23 | 23 |
Mojolicious ($Mojolicious::VERSION, $Mojolicious::CODENAME) |
24 | 24 |
|
25 | 25 |
OPTIONAL |
26 |
- EV ($ev) |
|
27 |
- IO::Socket::IP ($ipv6) |
|
28 |
- IO::Socket::SSL ($tls) |
|
26 |
+ EV 4.0+ ($ev) |
|
27 |
+ IO::Socket::IP 0.16+ ($ipv6) |
|
28 |
+ IO::Socket::SSL 1.75+ ($tls) |
|
29 | 29 |
|
30 | 30 |
EOF |
31 | 31 |
|
... | ... |
@@ -11,8 +11,8 @@ 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 |
|
15 |
- of MOJO_MODE or "development". |
|
14 |
+ -m, --mode <name> Run mode of your application, defaults to the value of |
|
15 |
+ MOJO_MODE/PLACK_ENV or "development". |
|
16 | 16 |
|
17 | 17 |
See '$0 help COMMAND' for more information on a specific command. |
18 | 18 |
EOF |
... | ... |
@@ -103,17 +103,6 @@ sub run { |
103 | 103 |
return print $self->hint; |
104 | 104 |
} |
105 | 105 |
|
106 |
-# DEPRECATED in Rainbow! |
|
107 |
-sub start { |
|
108 |
- warn <<EOF; |
|
109 |
-Mojolicious::Commands->start is DEPRECATED in favor of |
|
110 |
-Mojolicious::Commands->start_app!!! |
|
111 |
-EOF |
|
112 |
- my $self = shift; |
|
113 |
- return $self->start_app($ENV{MOJO_APP} => @_) if $ENV{MOJO_APP}; |
|
114 |
- return $self->new->app->start(@_); |
|
115 |
-} |
|
116 |
- |
|
117 | 106 |
sub start_app { |
118 | 107 |
my $self = shift; |
119 | 108 |
return Mojo::Server->new->build_app(shift)->start(@_); |
... | ... |
@@ -179,7 +168,7 @@ Upload files to CPAN. |
179 | 168 |
|
180 | 169 |
$ ./myapp.pl daemon |
181 | 170 |
|
182 |
-Start application with standalone HTTP and WebSocket server server. |
|
171 |
+Start application with standalone HTTP and WebSocket server. |
|
183 | 172 |
|
184 | 173 |
=head2 eval |
185 | 174 |
|
... | ... |
@@ -320,7 +309,7 @@ Try to detect environment. |
320 | 309 |
$commands->run(@ARGV); |
321 | 310 |
|
322 | 311 |
Load and run commands. Automatic deployment environment detection can be |
323 |
-disabled with the C<MOJO_NO_DETECT> environment variable. |
|
312 |
+disabled with the MOJO_NO_DETECT environment variable. |
|
324 | 313 |
|
325 | 314 |
=head2 start_app |
326 | 315 |
|
... | ... |
@@ -4,7 +4,6 @@ use Mojo::Base -base; |
4 | 4 |
# No imports, for security reasons! |
5 | 5 |
use Carp (); |
6 | 6 |
use Mojo::ByteStream; |
7 |
-use Mojo::Cookie::Response; |
|
8 | 7 |
use Mojo::Exception; |
9 | 8 |
use Mojo::Transaction::HTTP; |
10 | 9 |
use Mojo::URL; |
... | ... |
@@ -12,11 +11,11 @@ use Mojo::Util; |
12 | 11 |
use Mojolicious; |
13 | 12 |
use Mojolicious::Routes::Match; |
14 | 13 |
use Scalar::Util (); |
14 |
+use Time::HiRes (); |
|
15 | 15 |
|
16 | 16 |
has app => sub { Mojolicious->new }; |
17 |
-has match => sub { |
|
18 |
- Mojolicious::Routes::Match->new(GET => '/')->root(shift->app->routes); |
|
19 |
-}; |
|
17 |
+has match => |
|
18 |
+ sub { Mojolicious::Routes::Match->new(root => shift->app->routes) }; |
|
20 | 19 |
has tx => sub { Mojo::Transaction::HTTP->new }; |
21 | 20 |
|
22 | 21 |
# Reserved stash values |
... | ... |
@@ -28,12 +27,11 @@ my %RESERVED = map { $_ => 1 } ( |
28 | 27 |
sub AUTOLOAD { |
29 | 28 |
my $self = shift; |
30 | 29 |
|
31 |
- # Method |
|
32 | 30 |
my ($package, $method) = our $AUTOLOAD =~ /^([\w:]+)::(\w+)$/; |
33 | 31 |
Carp::croak "Undefined subroutine &${package}::$method called" |
34 | 32 |
unless Scalar::Util::blessed $self && $self->isa(__PACKAGE__); |
35 | 33 |
|
36 |
- # Call helper |
|
34 |
+ # Call helper with current controller |
|
37 | 35 |
Carp::croak qq{Can't locate object method "$method" via package "$package"} |
38 | 36 |
unless my $helper = $self->app->renderer->helpers->{$method}; |
39 | 37 |
return $self->$helper(@_); |
... | ... |
@@ -42,19 +40,17 @@ sub AUTOLOAD { |
42 | 40 |
sub DESTROY { } |
43 | 41 |
|
44 | 42 |
sub cookie { |
45 |
- my ($self, $name, $value, $options) = @_; |
|
46 |
- $options ||= {}; |
|
43 |
+ my ($self, $name) = (shift, shift); |
|
47 | 44 |
|
48 | 45 |
# Response cookie |
49 |
- if (defined $value) { |
|
46 |
+ if (@_) { |
|
50 | 47 |
|
51 | 48 |
# Cookie too big |
49 |
+ my $cookie = {name => $name, value => shift, %{shift || {}}}; |
|
52 | 50 |
$self->app->log->error(qq{Cookie "$name" is bigger than 4096 bytes.}) |
53 |
- if length $value > 4096; |
|
51 |
+ if length $cookie->{value} > 4096; |
|
54 | 52 |
|
55 |
- # Create new cookie |
|
56 |
- $self->res->cookies( |
|
57 |
- Mojo::Cookie::Response->new(name => $name, value => $value, %$options)); |
|
53 |
+ $self->res->cookies($cookie); |
|
58 | 54 |
return $self; |
59 | 55 |
} |
60 | 56 |
|
... | ... |
@@ -65,20 +61,20 @@ sub cookie { |
65 | 61 |
} |
66 | 62 |
|
67 | 63 |
sub finish { |
68 |
- my ($self, $chunk) = @_; |
|
64 |
+ my $self = shift; |
|
69 | 65 |
|
70 | 66 |
# WebSocket |
71 | 67 |
my $tx = $self->tx; |
72 |
- $tx->finish and return $self if $tx->is_websocket; |
|
68 |
+ $tx->finish(@_) and return $self if $tx->is_websocket; |
|
73 | 69 |
|
74 | 70 |
# Chunked stream |
75 |
- if ($tx->res->is_chunked) { |
|
76 |
- $self->write_chunk($chunk) if defined $chunk; |
|
71 |
+ if ($tx->res->content->is_chunked) { |
|
72 |
+ $self->write_chunk(@_) if @_; |
|
77 | 73 |
return $self->write_chunk(''); |
78 | 74 |
} |
79 | 75 |
|
80 | 76 |
# Normal stream |
81 |
- $self->write($chunk) if defined $chunk; |
|
77 |
+ $self->write(@_) if @_; |
|
82 | 78 |
return $self->write(''); |
83 | 79 |
} |
84 | 80 |
|
... | ... |
@@ -152,44 +148,27 @@ sub render { |
152 | 148 |
my $self = shift; |
153 | 149 |
|
154 | 150 |
# Template may be first argument |
155 |
- my $template = @_ % 2 && !ref $_[0] ? shift : undef; |
|
156 |
- my $args = ref $_[0] ? $_[0] : {@_}; |
|
151 |
+ my ($template, $args) = (@_ % 2 ? shift : undef, {@_}); |
|
157 | 152 |
$args->{template} = $template if $template; |
158 |
- |
|
159 |
- # Detect template name |
|
160 |
- my $stash = $self->stash; |
|
161 |
- unless ($args->{template} || $stash->{template}) { |
|
162 |
- |
|
163 |
- # Normal default template |
|
164 |
- my $controller = $args->{controller} || $stash->{controller}; |
|
165 |
- my $action = $args->{action} || $stash->{action}; |
|
166 |
- if ($controller && $action) { |
|
167 |
- $stash->{template} = join '/', |
|
168 |
- split(/-/, Mojo::Util::decamelize($controller)), $action; |
|
169 |
- } |
|
170 |
- |
|
171 |
- # Try the route name if we don't have controller and action |
|
172 |
- elsif (my $endpoint = $self->match->endpoint) { |
|
173 |
- $stash->{template} = $endpoint->name; |
|
174 |
- } |
|
175 |
- } |
|
153 |
+ my $maybe = delete $args->{'mojo.maybe'}; |
|
176 | 154 |
|
177 | 155 |
# Render |
178 | 156 |
my $app = $self->app; |
179 | 157 |
my ($output, $format) = $app->renderer->render($self, $args); |
180 |
- return undef unless defined $output; |
|
181 |
- return Mojo::ByteStream->new($output) if $args->{partial}; |
|
158 |
+ return defined $output ? Mojo::ByteStream->new($output) : undef |
|
159 |
+ if $args->{partial}; |
|
160 |
+ |
|
161 |
+ # Maybe |
|
162 |
+ return $maybe ? undef : !$self->render_not_found unless defined $output; |
|
182 | 163 |
|
183 | 164 |
# Prepare response |
184 | 165 |
$app->plugins->emit_hook(after_render => $self, \$output, $format); |
185 | 166 |
my $headers = $self->res->body($output)->headers; |
186 | 167 |
$headers->content_type($app->types->type($format) || 'text/plain') |
187 | 168 |
unless $headers->content_type; |
188 |
- return !!$self->rendered($stash->{status}); |
|
169 |
+ return !!$self->rendered($self->stash->{status}); |
|
189 | 170 |
} |
190 | 171 |
|
191 |
-sub render_data { shift->render(data => @_) } |
|
192 |
- |
|
193 | 172 |
sub render_exception { |
194 | 173 |
my ($self, $e) = @_; |
195 | 174 |
|
... | ... |
@@ -214,14 +193,15 @@ sub render_exception { |
214 | 193 |
}; |
215 | 194 |
my $inline = $renderer->_bundled( |
216 | 195 |
$mode eq 'development' ? 'exception.development' : 'exception'); |
217 |
- return if $self->_fallbacks($options, 'exception', $inline); |
|
196 |
+ return $self if $self->_fallbacks($options, 'exception', $inline); |
|
218 | 197 |
$self->_fallbacks({%$options, format => 'html'}, 'exception', $inline); |
198 |
+ return $self; |
|
219 | 199 |
} |
220 | 200 |
|
221 |
-sub render_json { shift->render(json => @_) } |
|
222 |
- |
|
223 | 201 |
sub render_later { shift->stash('mojo.rendered' => 1) } |
224 | 202 |
|
203 |
+sub render_maybe { shift->render(@_, 'mojo.maybe' => 1) } |
|
204 |
+ |
|
225 | 205 |
sub render_not_found { |
226 | 206 |
my $self = shift; |
227 | 207 |
|
... | ... |
@@ -234,15 +214,9 @@ sub render_not_found { |
234 | 214 |
= {template => "not_found.$mode", format => $format, status => 404}; |
235 | 215 |
my $inline = $renderer->_bundled( |
236 | 216 |
$mode eq 'development' ? 'not_found.development' : 'not_found'); |
237 |
- return if $self->_fallbacks($options, 'not_found', $inline); |
|
217 |
+ return $self if $self->_fallbacks($options, 'not_found', $inline); |
|
238 | 218 |
$self->_fallbacks({%$options, format => 'html'}, 'not_found', $inline); |
239 |
-} |
|
240 |
- |
|
241 |
-sub render_partial { |
|
242 |
- my $self = shift; |
|
243 |
- my $template = @_ % 2 ? shift : undef; |
|
244 |
- return $self->render( |
|
245 |
- {@_, partial => 1, defined $template ? (template => $template) : ()}); |
|
219 |
+ return $self; |
|
246 | 220 |
} |
247 | 221 |
|
248 | 222 |
sub render_static { |
... | ... |
@@ -250,11 +224,9 @@ sub render_static { |
250 | 224 |
my $app = $self->app; |
251 | 225 |
return !!$self->rendered if $app->static->serve($self, $file); |
252 | 226 |
$app->log->debug(qq{File "$file" not found, public directory missing?}); |
253 |
- return undef; |
|
227 |
+ return !$self->render_not_found; |
|
254 | 228 |
} |
255 | 229 |
|
256 |
-sub render_text { shift->render(text => @_) } |
|
257 |
- |
|
258 | 230 |
sub rendered { |
259 | 231 |
my ($self, $status) = @_; |
260 | 232 |
|
... | ... |
@@ -263,8 +235,20 @@ sub rendered { |
263 | 235 |
$res->code($status || 200) if $status || !$res->code; |
264 | 236 |
|
265 | 237 |
# Finish transaction |
266 |
- unless ($self->stash->{'mojo.finished'}++) { |
|
238 |
+ my $stash = $self->stash; |
|
239 |
+ unless ($stash->{'mojo.finished'}++) { |
|
240 |
+ |
|
241 |
+ # Stop timer |
|
267 | 242 |
my $app = $self->app; |
243 |
+ if (my $started = delete $stash->{'mojo.started'}) { |
|
244 |
+ my $elapsed = sprintf '%f', |
|
245 |
+ Time::HiRes::tv_interval($started, [Time::HiRes::gettimeofday()]); |
|
246 |
+ my $rps = $elapsed == 0 ? '??' : sprintf '%.3f', 1 / $elapsed; |
|
247 |
+ my $code = $res->code; |
|
248 |
+ my $msg = $res->message || $res->default_message($code); |
|
249 |
+ $app->log->debug("$code $msg (${elapsed}s, $rps/s)."); |
|
250 |
+ } |
|
251 |
+ |
|
268 | 252 |
$app->plugins->emit_hook_reverse(after_dispatch => $self); |
269 | 253 |
$app->sessions->store($self); |
270 | 254 |
} |
... | ... |
@@ -304,7 +288,9 @@ sub respond_to { |
304 | 288 |
} |
305 | 289 |
|
306 | 290 |
# Dispatch |
307 |
- ref $target eq 'CODE' ? $target->($self) : $self->render($target); |
|
291 |
+ ref $target eq 'CODE' ? $target->($self) : $self->render(%$target); |
|
292 |
+ |
|
293 |
+ return $self; |
|
308 | 294 |
} |
309 | 295 |
|
310 | 296 |
sub send { |
... | ... |
@@ -438,14 +424,16 @@ sub url_for { |
438 | 424 |
sub write { |
439 | 425 |
my ($self, $chunk, $cb) = @_; |
440 | 426 |
($cb, $chunk) = ($chunk, undef) if ref $chunk eq 'CODE'; |
441 |
- $self->res->write($chunk => sub { shift and $self->$cb(@_) if $cb }); |
|
427 |
+ my $content = $self->res->content; |
|
428 |
+ $content->write($chunk => sub { shift and $self->$cb(@_) if $cb }); |
|
442 | 429 |
return $self->rendered; |
443 | 430 |
} |
444 | 431 |
|
445 | 432 |
sub write_chunk { |
446 | 433 |
my ($self, $chunk, $cb) = @_; |
447 | 434 |
($cb, $chunk) = ($chunk, undef) if ref $chunk eq 'CODE'; |
448 |
- $self->res->write_chunk($chunk => sub { shift and $self->$cb(@_) if $cb }); |
|
435 |
+ my $content = $self->res->content; |
|
436 |
+ $content->write_chunk($chunk => sub { shift and $self->$cb(@_) if $cb }); |
|
449 | 437 |
return $self->rendered; |
450 | 438 |
} |
451 | 439 |
|
... | ... |
@@ -453,18 +441,16 @@ sub _fallbacks { |
453 | 441 |
my ($self, $options, $template, $inline) = @_; |
454 | 442 |
|
455 | 443 |
# Mode specific template |
456 |
- return 1 if $self->render($options); |
|
444 |
+ return 1 if $self->render_maybe(%$options); |
|
457 | 445 |
|
458 | 446 |
# Normal template |
459 |
- $options->{template} = $template; |
|
460 |
- return 1 if $self->render($options); |
|
447 |
+ return 1 if $self->render_maybe(%$options, template => $template); |
|
461 | 448 |
|
462 | 449 |
# Inline template |
463 | 450 |
my $stash = $self->stash; |
464 | 451 |
return undef unless $stash->{format} eq 'html'; |
465 | 452 |
delete $stash->{$_} for qw(extends layout); |
466 |
- delete $options->{template}; |
|
467 |
- return $self->render(%$options, inline => $inline, handler => 'ep'); |
|
453 |
+ return $self->render_maybe(%$options, inline => $inline, handler => 'ep'); |
|
468 | 454 |
} |
469 | 455 |
|
470 | 456 |
1; |
... | ... |
@@ -548,14 +534,16 @@ implements the following new ones. |
548 | 534 |
Access request cookie values and create new response cookies. |
549 | 535 |
|
550 | 536 |
# Create response cookie with domain and expiration date |
551 |
- $c->cookie(user => 'sri', {domain => 'mojolicio.us', expires => time + 60}); |
|
537 |
+ $c->cookie(user => 'sri', {domain => 'example.com', expires => time + 60}); |
|
552 | 538 |
|
553 | 539 |
=head2 finish |
554 | 540 |
|
555 | 541 |
$c = $c->finish; |
542 |
+ $c = $c->finish(1000); |
|
543 |
+ $c = $c->finish(1003 => 'Cannot accept data!'); |
|
556 | 544 |
$c = $c->finish('Bye!'); |
557 | 545 |
|
558 |
-Gracefully end WebSocket connection or long poll stream. |
|
546 |
+Close WebSocket connection or long poll stream gracefully. |
|
559 | 547 |
|
560 | 548 |
=head2 flash |
561 | 549 |
|
... | ... |
@@ -588,20 +576,17 @@ L<Mojo::Transaction::WebSocket> object. |
588 | 576 |
$c->app->log->debug("Message: $msg"); |
589 | 577 |
}); |
590 | 578 |
|
591 |
- # Receive JSON object via WebSocket "Text" message |
|
592 |
- use Mojo::JSON 'j'; |
|
593 |
- $c->on(text => sub { |
|
594 |
- my ($c, $bytes) = @_; |
|
595 |
- my $test = j($bytes)->{test}; |
|
596 |
- $c->app->log->debug("Test: $test"); |
|
579 |
+ # Receive JSON object via WebSocket message |
|
580 |
+ $c->on(json => sub { |
|
581 |
+ my ($c, $hash) = @_; |
|
582 |
+ $c->app->log->debug("Test: $hash->{test}"); |
|
597 | 583 |
}); |
598 | 584 |
|
599 |
- # Receive JSON object via WebSocket "Binary" message |
|
600 |
- use Mojo::JSON 'j'; |
|
585 |
+ # Receive WebSocket "Binary" message |
|
601 | 586 |
$c->on(binary => sub { |
602 | 587 |
my ($c, $bytes) = @_; |
603 |
- my $test = j($bytes)->{test}; |
|
604 |
- $c->app->log->debug("Test: $test"); |
|
588 |
+ my $len = length $bytes; |
|
589 |
+ $c->app->log->debug("Received $len bytes."); |
|
605 | 590 |
}); |
606 | 591 |
|
607 | 592 |
=head2 param |
... | ... |
@@ -615,8 +600,8 @@ L<Mojo::Transaction::WebSocket> object. |
615 | 600 |
|
616 | 601 |
Access GET/POST parameters, file uploads and route placeholder values that are |
617 | 602 |
not reserved stash values. Note that this method is context sensitive in some |
618 |
-cases and therefore needs to be used with care, every GET/POST parameter can |
|
619 |
-have multiple values, which might have unexpected consequences. |
|
603 |
+cases and therefore needs to be used with care, there can always be multiple |
|
604 |
+values, which might have unexpected consequences. |
|
620 | 605 |
|
621 | 606 |
# List context is ambiguous and should be avoided |
622 | 607 |
my $hash = {foo => $self->param('foo')}; |
... | ... |
@@ -658,7 +643,6 @@ Prepare a C<302> redirect response, takes the same arguments as C<url_for>. |
658 | 643 |
|
659 | 644 |
my $success = $c->render; |
660 | 645 |
my $success = $c->render(controller => 'foo', action => 'bar'); |
661 |
- my $success = $c->render({controller => 'foo', action => 'bar'}); |
|
662 | 646 |
my $success = $c->render(template => 'foo/index'); |
663 | 647 |
my $success = $c->render(template => 'index', format => 'html'); |
664 | 648 |
my $success = $c->render(data => $bytes); |
... | ... |
@@ -673,42 +657,22 @@ C<after_render> hook unless the result is C<partial>. If no template is |
673 | 657 |
provided a default one based on controller and action or route name will be |
674 | 658 |
generated, all additional values get merged into the C<stash>. |
675 | 659 |
|
676 |
-=head2 render_data |
|
677 |
- |
|
678 |
- $c->render_data($bytes); |
|
679 |
- $c->render_data($bytes, format => 'png'); |
|
680 |
- |
|
681 |
-Render the given content as raw bytes, similar to C<render_text> but data will |
|
682 |
-not be encoded. All additional values get merged into the C<stash>. |
|
683 |
- |
|
684 |
- # Longer version |
|
685 |
- $c->render(data => $bytes); |
|
686 |
- |
|
687 | 660 |
=head2 render_exception |
688 | 661 |
|
689 |
- $c->render_exception('Oops!'); |
|
690 |
- $c->render_exception(Mojo::Exception->new('Oops!')); |
|
662 |
+ $c = $c->render_exception('Oops!'); |
|
663 |
+ $c = $c->render_exception(Mojo::Exception->new('Oops!')); |
|
691 | 664 |
|
692 | 665 |
Render the exception template C<exception.$mode.$format.*> or |
693 |
-C<exception.$format.*> and set the response status code to C<500>. |
|
694 |
- |
|
695 |
-=head2 render_json |
|
696 |
- |
|
697 |
- $c->render_json({foo => 'bar'}); |
|
698 |
- $c->render_json([1, 2, -3], status => 201); |
|
699 |
- |
|
700 |
-Render a data structure as JSON. All additional values get merged into the |
|
701 |
-C<stash>. |
|
702 |
- |
|
703 |
- # Longer version |
|
704 |
- $c->render(json => {foo => 'bar'}); |
|
666 |
+C<exception.$format.*> and set the response status code to C<500>. Also sets |
|
667 |
+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. |
|
705 | 669 |
|
706 | 670 |
=head2 render_later |
707 | 671 |
|
708 | 672 |
$c = $c->render_later; |
709 | 673 |
|
710 | 674 |
Disable automatic rendering to delay response generation, only necessary if |
711 |
-automatic rendring would result in a response. |
|
675 |
+automatic rendering would result in a response. |
|
712 | 676 |
|
713 | 677 |
# Delayed rendering |
714 | 678 |
$c->render_later; |
... | ... |
@@ -716,23 +680,24 @@ automatic rendring would result in a response. |
716 | 680 |
$c->render(text => 'Delayed by 2 seconds!'); |
717 | 681 |
}); |
718 | 682 |
|
719 |
-=head2 render_not_found |
|
683 |
+=head2 render_maybe |
|
720 | 684 |
|
721 |
- $c->render_not_found; |
|
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'); |
|
722 | 688 |
|
723 |
-Render the not found template C<not_found.$mode.$format.*> or |
|
724 |
-C<not_found.$format.*> and set the response status code to C<404>. |
|
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>. |
|
725 | 691 |
|
726 |
-=head2 render_partial |
|
692 |
+ # Render template "index_local" only if it exists |
|
693 |
+ $self->render_maybe('index_local') or $self->render('index'); |
|
727 | 694 |
|
728 |
- my $output = $c->render_partial('menubar'); |
|
729 |
- my $output = $c->render_partial('menubar', format => 'txt'); |
|
730 |
- my $output = $c->render_partial(template => 'menubar'); |
|
695 |
+=head2 render_not_found |
|
731 | 696 |
|
732 |
-Same as C<render> but returns the rendered result. |
|
697 |
+ $c = $c->render_not_found; |
|
733 | 698 |
|
734 |
- # Longer version |
|
735 |
- my $output = $c->render('menubar', partial => 1); |
|
699 |
+Render the not found template C<not_found.$mode.$format.*> or |
|
700 |
+C<not_found.$format.*> and set the response status code to C<404>. |
|
736 | 701 |
|
737 | 702 |
=head2 render_static |
738 | 703 |
|
... | ... |
@@ -743,22 +708,6 @@ Render a static file using L<Mojolicious::Static/"serve">, usually from the |
743 | 708 |
C<public> directories or C<DATA> sections of your application. Note that this |
744 | 709 |
method does not protect from traversing to parent directories. |
745 | 710 |
|
746 |
-=head2 render_text |
|
747 |
- |
|
748 |
- $c->render_text('Hello World!'); |
|
749 |
- $c->render_text('Hello World!', layout => 'green'); |
|
750 |
- |
|
751 |
-Render the given content as Perl characters, which will be encoded to bytes. |
|
752 |
-All additional values get merged into the C<stash>. See C<render_data> for an |
|
753 |
-alternative without encoding. Note that this does not change the content type |
|
754 |
-of the response, which is C<text/html;charset=UTF-8> by default. |
|
755 |
- |
|
756 |
- # Longer version |
|
757 |
- $c->render(text => 'Hello World!'); |
|
758 |
- |
|
759 |
- # Render "text/plain" response |
|
760 |
- $c->render_text('Hello World!', format => 'txt'); |
|
761 |
- |
|
762 | 711 |
=head2 rendered |
763 | 712 |
|
764 | 713 |
$c = $c->rendered; |
... | ... |
@@ -777,7 +726,9 @@ Get L<Mojo::Message::Request> object from L<Mojo::Transaction/"req">. |
777 | 726 |
my $req = $c->tx->req; |
778 | 727 |
|
779 | 728 |
# Extract request information |
780 |
- my $userinfo = $c->req->url->userinfo; |
|
729 |
+ my $url = $c->req->url->to_abs; |
|
730 |
+ my $userinfo = $c->req->url->to_abs->userinfo; |
|
731 |
+ my $host = $c->req->url->to_abs->host; |
|
781 | 732 |
my $agent = $c->req->headers->user_agent; |
782 | 733 |
my $body = $c->req->body; |
783 | 734 |
my $foo = $c->req->json('/23/foo'); |
... | ... |
@@ -797,7 +748,7 @@ Get L<Mojo::Message::Response> object from L<Mojo::Transaction/"res">. |
797 | 748 |
|
798 | 749 |
=head2 respond_to |
799 | 750 |
|
800 |
- $c->respond_to( |
|
751 |
+ $c = $c->respond_to( |
|
801 | 752 |
json => {json => {message => 'Welcome!'}}, |
802 | 753 |
html => {template => 'welcome'}, |
803 | 754 |
any => sub {...} |
... | ... |
@@ -811,7 +762,7 @@ more than one MIME type will be ignored, unless the C<X-Requested-With> header |
811 | 762 |
is set to the value C<XMLHttpRequest>. |
812 | 763 |
|
813 | 764 |
$c->respond_to( |
814 |
- json => sub { $c->render_json({just => 'works'}) }, |
|
765 |
+ json => sub { $c->render(json => {just => 'works'}) }, |
|
815 | 766 |
xml => {text => '<just>works</just>'}, |
816 | 767 |
any => {data => '', status => 204} |
817 | 768 |
); |
... | ... |
@@ -820,7 +771,9 @@ is set to the value C<XMLHttpRequest>. |
820 | 771 |
|
821 | 772 |
$c = $c->send({binary => $bytes}); |
822 | 773 |
$c = $c->send({text => $bytes}); |
774 |
+ $c = $c->send({json => {test => [1, 2, 3]}}); |
|
823 | 775 |
$c = $c->send([$fin, $rsv1, $rsv2, $rsv3, $op, $bytes]); |
776 |
+ $c = $c->send(Mojo::ByteStream->new($chars)); |
|
824 | 777 |
$c = $c->send($chars); |
825 | 778 |
$c = $c->send($chars => sub {...}); |
826 | 779 |
|
... | ... |
@@ -831,8 +784,7 @@ will be invoked once all data has been written. |
831 | 784 |
$c->send('I ♥ Mojolicious!'); |
832 | 785 |
|
833 | 786 |
# Send JSON object as "Text" message |
834 |
- use Mojo::JSON 'j'; |
|
835 |
- $c->send({text => j({test => 'I ♥ Mojolicious!'})}); |
|
787 |
+ $c->send({json => {test => 'I ♥ Mojolicious!'}}); |
|
836 | 788 |
|
837 | 789 |
# Send JSON object as "Binary" message |
838 | 790 |
use Mojo::JSON 'j'; |
... | ... |
@@ -885,10 +837,10 @@ discarded. |
885 | 837 |
|
886 | 838 |
=head2 stash |
887 | 839 |
|
888 |
- my $stash = $c->stash; |
|
889 |
- my $foo = $c->stash('foo'); |
|
890 |
- $c = $c->stash({foo => 'bar'}); |
|
891 |
- $c = $c->stash(foo => 'bar'); |
|
840 |
+ my $hash = $c->stash; |
|
841 |
+ my $foo = $c->stash('foo'); |
|
842 |
+ $c = $c->stash({foo => 'bar'}); |
|
843 |
+ $c = $c->stash(foo => 'bar'); |
|
892 | 844 |
|
893 | 845 |
Non persistent data storage and exchange, application wide default values can |
894 | 846 |
be set with L<Mojolicious/"defaults">. Many stash values have a special |
... | ... |
@@ -897,10 +849,8 @@ C<controller>, C<data>, C<extends>, C<format>, C<handler>, C<json>, C<layout>, |
897 | 849 |
C<namespace>, C<partial>, C<path>, C<status>, C<template> and C<text>. Note |
898 | 850 |
that all stash values with a C<mojo.*> prefix are reserved for internal use. |
899 | 851 |
|
900 |
- # Manipulate stash |
|
901 |
- $c->stash->{foo} = 'bar'; |
|
902 |
- my $foo = $c->stash->{foo}; |
|
903 |
- delete $c->stash->{foo}; |
|
852 |
+ # Remove value |
|
853 |
+ my $foo = delete $c->stash->{foo}; |
|
904 | 854 |
|
905 | 855 |
=head2 ua |
906 | 856 |
|
... | ... |
@@ -912,25 +862,25 @@ Get L<Mojo::UserAgent> object from L<Mojo/"ua">. |
912 | 862 |
my $ua = $c->app->ua; |
913 | 863 |
|
914 | 864 |
# Blocking |
915 |
- my $tx = $c->ua->get('http://mojolicio.us'); |
|
916 |
- my $tx = $c->ua->post_form('http://kraih.com/login' => {user => 'mojo'}); |
|
865 |
+ my $tx = $c->ua->get('http://example.com'); |
|
866 |
+ my $tx = $c->ua->post('example.com/login' => form => {user => 'mojo'}); |
|
917 | 867 |
|
918 | 868 |
# Non-blocking |
919 |
- $c->ua->get('http://mojolicio.us' => sub { |
|
869 |
+ $c->ua->get('http://example.com' => sub { |
|
920 | 870 |
my ($ua, $tx) = @_; |
921 |
- $c->render_data($tx->res->body); |
|
871 |
+ $c->render(data => $tx->res->body); |
|
922 | 872 |
}); |
923 | 873 |
|
924 | 874 |
# Parallel non-blocking |
925 | 875 |
my $delay = Mojo::IOLoop->delay(sub { |
926 | 876 |
my ($delay, @titles) = @_; |
927 |
- $c->render_json(\@titles); |
|
877 |
+ $c->render(json => \@titles); |
|
928 | 878 |
}); |
929 | 879 |
for my $url ('http://mojolicio.us', 'https://metacpan.org') { |
930 |
- $delay->begin; |
|
880 |
+ my $end = $delay->begin(0); |
|
931 | 881 |
$c->ua->get($url => sub { |
932 | 882 |
my ($ua, $tx) = @_; |
933 |
- $delay->end($tx->res->dom->html->head->title->text); |
|
883 |
+ $end->($tx->res->dom->html->head->title->text); |
|
934 | 884 |
}); |
935 | 885 |
} |
936 | 886 |
|
... | ... |
@@ -938,7 +888,9 @@ Get L<Mojo::UserAgent> object from L<Mojo/"ua">. |
938 | 888 |
|
939 | 889 |
my $url = $c->url_for; |
940 | 890 |
my $url = $c->url_for(name => 'sebastian'); |
891 |
+ my $url = $c->url_for({name => 'sebastian'}); |
|
941 | 892 |
my $url = $c->url_for('test', name => 'sebastian'); |
893 |
+ my $url = $c->url_for('test', {name => 'sebastian'}); |
|
942 | 894 |
my $url = $c->url_for('/perldoc'); |
943 | 895 |
my $url = $c->url_for('http://mojolicio.us/perldoc'); |
944 | 896 |
|
... | ... |
@@ -32,10 +32,12 @@ overview of what L<Mojolicious> is all about. |
32 | 32 |
|
33 | 33 |
=item L<Mojolicious::Lite> |
34 | 34 |
|
35 |
-A really fast and fun way to get started developing web applications with |
|
36 |
-Mojolicious is the L<Mojolicious::Lite> tutorial. Almost everything you learn |
|
37 |
-there can also be applied to normal L<Mojolicious> applications and is |
|
38 |
-considered a prerequisite for the guides. You should definitely take a look! |
|
35 |
+A fast and fun way to get started developing web applications with Mojolicious |
|
36 |
+is the L<Mojolicious::Lite> tutorial. This micro web framework is only a thin |
|
37 |
+wrapper around the normal web framework, so almost everything you learn here |
|
38 |
+also applies to full L<Mojolicious> applications. The simplified notation |
|
39 |
+introduced in the tutorial is commonly used throughout the guides and is |
|
40 |
+therefore considered a prerequisite, you should definitely take a look! |
|
39 | 41 |
|
40 | 42 |
=back |
41 | 43 |
|
... | ... |
@@ -130,7 +130,7 @@ The pumpking has veto rights and may select his successor. |
130 | 130 |
|
131 | 131 |
It's not a feature without a test and documentation. |
132 | 132 |
|
133 |
-A feature is only needed when the majority of the userbase benefits from it. |
|
133 |
+A feature is only needed when the majority of the user base benefits from it. |
|
134 | 134 |
|
135 | 135 |
Features may only be changed in a major release or after being deprecated for |
136 | 136 |
at least 3 months. |
... | ... |
@@ -42,6 +42,15 @@ On UNIX platforms you can also add preforking with L<Mojo::Server::Prefork>. |
42 | 42 |
$ ./script/myapp prefork |
43 | 43 |
Server available at http://127.0.0.1:3000. |
44 | 44 |
|
45 |
+Since all built-in web servers are based on the L<Mojo::IOLoop> event loop, |
|
46 |
+they scale best with non-blocking operations. But if your application for some |
|
47 |
+reason needs to perform many blocking operations, you can improve performance |
|
48 |
+by increasing the number of worker processes and decreasing the number of |
|
49 |
+concurrent connections each worker is allowed to handle. |
|
50 |
+ |
|
51 |
+ $ ./script/myapp prefork -m production -w 10 -c 1 |
|
52 |
+ Server available at http://127.0.0.1:3000. |
|
53 |
+ |
|
45 | 54 |
=head2 Morbo |
46 | 55 |
|
47 | 56 |
After reading the L<Mojolicious::Lite> tutorial, you should already be |
... | ... |
@@ -129,8 +138,8 @@ timers. |
129 | 138 |
|
130 | 139 |
=head2 Nginx |
131 | 140 |
|
132 |
-One of the most popular setups these days is the built-in web server behind a |
|
133 |
-Nginx reverse proxy. |
|
141 |
+One of the most popular setups these days is Hypnotoad behind an Nginx reverse |
|
142 |
+proxy, which even supports WebSockets in newer versions. |
|
134 | 143 |
|
135 | 144 |
upstream myapp { |
136 | 145 |
server 127.0.0.1:8080; |
... | ... |
@@ -139,8 +148,10 @@ Nginx reverse proxy. |
139 | 148 |
listen 80; |
140 | 149 |
server_name localhost; |
141 | 150 |
location / { |
142 |
- proxy_read_timeout 300; |
|
143 | 151 |
proxy_pass http://myapp; |
152 |
+ proxy_http_version 1.1; |
|
153 |
+ proxy_set_header Upgrade $http_upgrade; |
|
154 |
+ proxy_set_header Connection "upgrade"; |
|
144 | 155 |
proxy_set_header Host $host; |
145 | 156 |
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
146 | 157 |
proxy_set_header X-Forwarded-HTTPS 0; |
... | ... |
@@ -150,7 +161,7 @@ Nginx reverse proxy. |
150 | 161 |
=head2 Apache/mod_proxy |
151 | 162 |
|
152 | 163 |
Another good reverse proxy is Apache with C<mod_proxy>, the configuration |
153 |
-looks very similar to the Nginx one above. |
|
164 |
+looks quite similar to the Nginx one above. |
|
154 | 165 |
|
155 | 166 |
<VirtualHost *:80> |
156 | 167 |
ServerName localhost |
... | ... |
@@ -184,12 +195,12 @@ simple to deploy with L<Plack>. |
184 | 195 |
HTTP::Server::PSGI: Accepting connections at http://0:5000/ |
185 | 196 |
|
186 | 197 |
L<Plack> provides many server and protocol adapters for you to choose from, |
187 |
-such as C<FCGI>, C<SCGI> and C<mod_perl>. |
|
198 |
+such as C<FCGI>, C<uWSGI> and C<mod_perl>. |
|
188 | 199 |
|
189 | 200 |
$ plackup ./script/myapp -s FCGI -l /tmp/myapp.sock |
190 | 201 |
|
191 | 202 |
If an older server adapter is not be able to correctly detect the application |
192 |
-home directory, you can simply use the C<MOJO_HOME> environment variable. |
|
203 |
+home directory, you can simply use the MOJO_HOME environment variable. |
|
193 | 204 |
|
194 | 205 |
$ MOJO_HOME=/home/sri/myapp plackup ./script/myapp |
195 | 206 |
HTTP::Server::PSGI: Accepting connections at http://0:5000/ |
... | ... |
@@ -247,8 +258,7 @@ incoming requests is also quite common. |
247 | 258 |
# Move first part from path to base path in production mode |
248 | 259 |
app->hook(before_dispatch => sub { |
249 | 260 |
my $self = shift; |
250 |
- push @{$self->req->url->base->path->parts}, |
|
251 |
- shift @{$self->req->url->path->parts}; |
|
261 |
+ push @{$self->req->url->base->path}, shift @{$self->req->url->path}; |
|
252 | 262 |
}) if app->mode eq 'production'; |
253 | 263 |
|
254 | 264 |
=head2 Application embedding |
... | ... |
@@ -440,9 +450,10 @@ shouldn't block for too long. |
440 | 450 |
=head2 WebSocket web service |
441 | 451 |
|
442 | 452 |
The WebSocket protocol offers full bi-directional low-latency communication |
443 |
-channels between clients and servers. Receiving messages is as easy as |
|
444 |
-subscribing to the event L<Mojo::Transaction::WebSocket/"message"> with the |
|
445 |
-method L<Mojolicious::Controller/"on">. |
|
453 |
+channels between clients and servers. Receive messages just by subscribing to |
|
454 |
+events such as L<Mojo::Transaction::WebSocket/"message"> with the method |
|
455 |
+L<Mojolicious::Controller/"on"> and return them with |
|
456 |
+L<Mojolicious::Controller/"send">. |
|
446 | 457 |
|
447 | 458 |
use Mojolicious::Lite; |
448 | 459 |
use Mojo::IOLoop; |
... | ... |
@@ -454,8 +465,8 @@ method L<Mojolicious::Controller/"on">. |
454 | 465 |
websocket '/echo' => sub { |
455 | 466 |
my $self = shift; |
456 | 467 |
|
457 |
- # Connected |
|
458 |
- $self->app->log->debug('WebSocket connected.'); |
|
468 |
+ # Opened |
|
469 |
+ $self->app->log->debug('WebSocket opened.'); |
|
459 | 470 |
|
460 | 471 |
# Increase inactivity timeout for connection a bit |
461 | 472 |
Mojo::IOLoop->stream($self->tx->connection)->timeout(300); |
... | ... |
@@ -466,10 +477,10 @@ method L<Mojolicious::Controller/"on">. |
466 | 477 |
$self->send("echo: $msg"); |
467 | 478 |
}); |
468 | 479 |
|
469 |
- # Disconnected |
|
480 |
+ # Closed |
|
470 | 481 |
$self->on(finish => sub { |
471 |
- my $self = shift; |
|
472 |
- $self->app->log->debug('WebSocket disconnected.'); |
|
482 |
+ my ($self, $code, $reason) = @_; |
|
483 |
+ $self->app->log->debug("WebSocket closed with status $code."); |
|
473 | 484 |
}); |
474 | 485 |
}; |
475 | 486 |
|
... | ... |
@@ -521,6 +532,13 @@ L<Test::Mojo> API to be used. |
521 | 532 |
->message_is('echo: Hello Mojo!') |
522 | 533 |
->finish_ok; |
523 | 534 |
|
535 |
+ # Test JSON web service |
|
536 |
+ $t->websocket_ok('/echo.json') |
|
537 |
+ ->send_ok({json => {test => [1, 2, 3]}}) |
|
538 |
+ ->message_ok |
|
539 |
+ ->json_message_is('/test', [1, 2, 3]) |
|
540 |
+ ->finish_ok; |
|
541 |
+ |
|
524 | 542 |
done_testing(); |
525 | 543 |
|
526 | 544 |
=head2 EventSource web service |
... | ... |
@@ -794,7 +812,7 @@ You can just add username and password to the URL. |
794 | 812 |
use Mojo::UserAgent; |
795 | 813 |
|
796 | 814 |
my $ua = Mojo::UserAgent->new; |
797 |
- say $ua->get('https://sri:secret@mojolicio.us/hideout')->res->body; |
|
815 |
+ say $ua->get('https://sri:secret@example.com/hideout')->res->body; |
|
798 | 816 |
|
799 | 817 |
=head2 Decorating followup requests |
800 | 818 |
|
... | ... |
@@ -820,6 +838,42 @@ them. |
820 | 838 |
|
821 | 839 |
This even works for proxy C<CONNECT> requests. |
822 | 840 |
|
841 |
+=head2 Content generators |
|
842 |
+ |
|
843 |
+Generate the same type of content repeatedly for multiple requests. |
|
844 |
+ |
|
845 |
+ use Mojo::UserAgent; |
|
846 |
+ use Mojo::Asset::File; |
|
847 |
+ |
|
848 |
+ # Add "stream" generator |
|
849 |
+ my $ua = Mojo::UserAgent->new; |
|
850 |
+ $ua->transactor->add_generator(stream => sub { |
|
851 |
+ my ($transactor, $tx, $path) = @_; |
|
852 |
+ $tx->req->content->asset(Mojo::Asset::File->new(path => $path)); |
|
853 |
+ }); |
|
854 |
+ |
|
855 |
+ # Send multiple files streaming via PUT and POST |
|
856 |
+ $ua->put('http://example.com/upload' => stream => '/home/sri/mojo.png'); |
|
857 |
+ $ua->post('http://example.com/upload' => stream => '/home/sri/mango.png'); |
|
858 |
+ |
|
859 |
+The C<json> and C<form> content generators are always available. |
|
860 |
+ |
|
861 |
+ use Mojo::UserAgent; |
|
862 |
+ |
|
863 |
+ # Send JSON content via PATCH |
|
864 |
+ my $ua = Mojo::UserAgent->new; |
|
865 |
+ my $tx = $ua->patch('http://api.example.com' => json => {foo => 'bar'}); |
|
866 |
+ |
|
867 |
+ # Send "application/x-www-form-urlencoded" content via POST |
|
868 |
+ my $tx2 = $ua->post('http://search.example.com' => form => {q => 'test'}); |
|
869 |
+ |
|
870 |
+ # Send "multipart/form-data" content via PUT |
|
871 |
+ my $tx3 = $ua->put('http://upload.example.com' => |
|
872 |
+ form => {test => {content => 'Hello World!'}}); |
|
873 |
+ |
|
874 |
+For more information about available content generators see also |
|
875 |
+L<Mojo::UserAgent::Transactor/"tx">. |
|
876 |
+ |
|
823 | 877 |
=head2 Streaming response |
824 | 878 |
|
825 | 879 |
Receiving a streaming response can be really tricky in most HTTP clients, but |
... | ... |
@@ -829,7 +883,7 @@ L<Mojo::UserAgent> makes it actually easy. |
829 | 883 |
|
830 | 884 |
# Build a normal transaction |
831 | 885 |
my $ua = Mojo::UserAgent->new; |
832 |
- my $tx = $ua->build_tx(GET => 'http://mojolicio.us'); |
|
886 |
+ my $tx = $ua->build_tx(GET => 'http://example.com'); |
|
833 | 887 |
|
834 | 888 |
# Replace "read" events to disable default content parser |
835 | 889 |
$tx->res->content->unsubscribe('read')->on(read => sub { |
... | ... |
@@ -852,7 +906,7 @@ Sending a streaming request is almost just as easy. |
852 | 906 |
|
853 | 907 |
# Build a normal transaction |
854 | 908 |
my $ua = Mojo::UserAgent->new; |
855 |
- my $tx = $ua->build_tx(GET => 'http://mojolicio.us'); |
|
909 |
+ my $tx = $ua->build_tx(GET => 'http://example.com'); |
|
856 | 910 |
|
857 | 911 |
# Prepare content |
858 | 912 |
my $content = 'Hello world!'; |
... | ... |
@@ -888,7 +942,7 @@ above C<250KB> into a temporary file. |
888 | 942 |
$tx->res->content->asset->move_to('mojo.tar.gz'); |
889 | 943 |
|
890 | 944 |
To protect you from excessively large files there is also a limit of C<5MB> by |
891 |
-default, which you can tweak with the C<MOJO_MAX_MESSAGE_SIZE> environment |
|
945 |
+default, which you can tweak with the MOJO_MAX_MESSAGE_SIZE environment |
|
892 | 946 |
variable. |
893 | 947 |
|
894 | 948 |
# Increase limit to 1GB |
... | ... |
@@ -902,21 +956,12 @@ Uploading a large file is even easier. |
902 | 956 |
|
903 | 957 |
# Upload file via POST and "multipart/form-data" |
904 | 958 |
my $ua = Mojo::UserAgent->new; |
905 |
- $ua->post_form('mojolicio.us/upload', |
|
906 |
- {image => {file => '/home/sri/hello.png'}}); |
|
959 |
+ $ua->post('example.com/upload' => |
|
960 |
+ form => {image => {file => '/home/sri/hello.png'}}); |
|
907 | 961 |
|
908 | 962 |
And once again you don't have to worry about memory usage, all data will be |
909 | 963 |
streamed directly from the file. |
910 | 964 |
|
911 |
- use Mojo::UserAgent; |
|
912 |
- |
|
913 |
- # Upload file via PUT |
|
914 |
- my $ua = Mojo::UserAgent->new; |
|
915 |
- my $asset = Mojo::Asset::File->new(path => '/home/sri/hello.png'); |
|
916 |
- my $tx = $ua->build_tx(PUT => 'mojolicio.us/upload'); |
|
917 |
- $tx->req->content->asset($asset); |
|
918 |
- $ua->start($tx); |
|
919 |
- |
|
920 | 965 |
=head2 Non-blocking |
921 | 966 |
|
922 | 967 |
L<Mojo::UserAgent> has been designed from the ground up to be non-blocking, |
... | ... |
@@ -1030,7 +1075,7 @@ Fun hacks you might not use very often but that might come in handy some day. |
1030 | 1075 |
|
1031 | 1076 |
=head2 Adding commands to Mojolicious |
1032 | 1077 |
|
1033 |
-By now you've propably used many of the built-in commands described in |
|
1078 |
+By now you've probably used many of the built-in commands described in |
|
1034 | 1079 |
L<Mojolicious::Commands>, but did you know that you can just add new ones and |
1035 | 1080 |
that they will be picked up automatically by the command line interface? |
1036 | 1081 |
|
... | ... |
@@ -1056,7 +1101,7 @@ There are many more useful methods and attributes in L<Mojolicious::Command> |
1056 | 1101 |
that you can use or overload. |
1057 | 1102 |
|
1058 | 1103 |
$ mojo spy secret |
1059 |
- The secret of this application is "Mojolicious::Lite". |
|
1104 |
+ The secret of this application is "HelloWorld". |
|
1060 | 1105 |
|
1061 | 1106 |
$ ./myapp.pl spy secret |
1062 | 1107 |
The secret of this application is "secr3t". |
... | ... |
@@ -62,7 +62,7 @@ which we already have done in the past. |
62 | 62 |
To protect your applications from excessively large requests and responses, |
63 | 63 |
our HTTP parser has a cap after which it will automatically stop accepting new |
64 | 64 |
data, and in most cases force the connection to be closed. This limit is |
65 |
-around C<5MB> by default, you can use the C<MOJO_MAX_MESSAGE_SIZE> environment |
|
65 |
+around C<5MB> by default, you can use the MOJO_MAX_MESSAGE_SIZE environment |
|
66 | 66 |
variable to change this value. |
67 | 67 |
|
68 | 68 |
=head2 What does the error "Maximum line size exceeded" mean? |
... | ... |
@@ -71,15 +71,15 @@ This is a very similar protection mechanism to the one described in the |
71 | 71 |
previous answer, but a little more specific. It limits the maximum length of |
72 | 72 |
any C<\x0d\x0a> terminated part of a HTTP message, such as request line, |
73 | 73 |
status line and headers. This limit is around C<10KB> by default, you can use |
74 |
-the C<MOJO_MAX_LINE_SIZE> environment variable to change this value. |
|
74 |
+the MOJO_MAX_LINE_SIZE environment variable to change this value. |
|
75 | 75 |
|
76 | 76 |
=head2 What does the error "Maximum buffer size exceeded" mean? |
77 | 77 |
|
78 | 78 |
This protection mechanism is very similar to those mentioned in the two |
79 | 79 |
previous answers. It limits how much content the HTTP parser is allowed to |
80 | 80 |
buffer when parsing chunked, compressed and multipart messages. This limit is |
81 |
-around C<256KB> by default, you can use the C<MOJO_MAX_BUFFER_SIZE> |
|
82 |
-environment variable to change this value. |
|
81 |
+around C<256KB> by default, you can use the MOJO_MAX_BUFFER_SIZE environment |
|
82 |
+variable to change this value. |
|
83 | 83 |
|
84 | 84 |
=head2 What does the error "EV does not work with ithreads" mean? |
85 | 85 |
|
... | ... |
@@ -87,15 +87,14 @@ The L<Mojolicious> user agent and web servers are based on an event loop that |
87 | 87 |
supports multiple reactor backends. One of these backends is L<EV>, it is very |
88 | 88 |
fast and will be automatically used if installed. On Windows however, the |
89 | 89 |
C<ithreads> based C<fork()> emulation can interfere with it, and you may have |
90 |
-to use the C<MOJO_REACTOR> environment variable to enforce a more portable |
|
91 |
-one. |
|
90 |
+to use the MOJO_REACTOR environment variable to enforce a more portable one. |
|
92 | 91 |
|
93 | 92 |
MOJO_REACTOR=Mojo::Reactor::Poll |
94 | 93 |
|
95 | 94 |
=head2 What does "Your secret passphrase needs to be changed" mean? |
96 | 95 |
|
97 | 96 |
L<Mojolicious> uses a secret passphrase for security features such as signed |
98 |
-cookies. It defaults to the name of your application, which is not very |
|
97 |
+cookies. It defaults to the moniker of your application, which is not very |
|
99 | 98 |
secure, so we added this log message as a reminder. You can change the |
100 | 99 |
passphrase with the attribute L<Mojolicious/"secret">. |
101 | 100 |
|
... | ... |
@@ -300,7 +300,7 @@ on L<Mojo::DOM>. |
300 | 300 |
->element_exists('form input[type="submit"]'); |
301 | 301 |
|
302 | 302 |
# Test login with valid credentials |
303 |
- $t->post_form_ok('/' => {user => 'sri', pass => 'secr3t'}) |
|
303 |
+ $t->post_ok('/' => form => {user => 'sri', pass => 'secr3t'}) |
|
304 | 304 |
->status_is(200)->text_like('html body' => qr/Welcome sri/); |
305 | 305 |
|
306 | 306 |
# Test accessing a protected page |
... | ... |
@@ -319,20 +319,23 @@ against your application. |
319 | 319 |
|
320 | 320 |
$ ./myapp.pl test |
321 | 321 |
$ ./myapp.pl test t/login.t |
322 |
+ $ ./myapp.pl test -v t/login.t |
|
322 | 323 |
|
323 |
-To make the tests less noisy and limit log output to just C<error> messages |
|
324 |
-you can also add a line like this. |
|
325 |
- |
|
326 |
- $t->app->log->level('error'); |
|
327 |
- |
|
328 |
-Quick C<GET> requests can be performed right from the command line. |
|
324 |
+Or perform quick requests right from the command line. |
|
329 | 325 |
|
330 | 326 |
$ ./myapp.pl get / |
331 | 327 |
Wrong username or password. |
332 | 328 |
|
333 | 329 |
$ ./myapp.pl get -v '/?user=sri&pass=secr3t' |
330 |
+ GET /?user=sri&pass=secr3t HTTP/1.1 |
|
331 |
+ User-Agent: Mojolicious (Perl) |
|
332 |
+ Connection: keep-alive |
|
333 |
+ Accept-Encoding: gzip |
|
334 |
+ Content-Length: 0 |
|
335 |
+ Host: localhost:59472 |
|
336 |
+ |
|
334 | 337 |
HTTP/1.1 200 OK |
335 |
- Connection: Keep-Alive |
|
338 |
+ Connection: keep-alive |
|
336 | 339 |
Date: Sun, 18 Jul 2010 13:09:58 GMT |
337 | 340 |
Server: Mojolicious (Perl) |
338 | 341 |
Content-Length: 12 |
... | ... |
@@ -413,12 +416,17 @@ like this. |
413 | 416 |
$self->redirect_to('protected'); |
414 | 417 |
} => 'index'; |
415 | 418 |
|
416 |
- # A protected page auto rendering "protected.html.ep" |
|
417 |
- get '/protected' => sub { |
|
418 |
- my $self = shift; |
|
419 |
+ # Make sure user is logged in for actions in this group |
|
420 |
+ group { |
|
421 |
+ under sub { |
|
422 |
+ my $self = shift; |
|
423 |
+ |
|
424 |
+ # Redirect to main page with a 302 response if user is not logged in |
|
425 |
+ return $self->session('user') || !$self->redirect_to('index'); |
|
426 |
+ }; |
|
419 | 427 |
|
420 |
- # Redirect to main page with a 302 response if user is not logged in |
|
421 |
- return $self->redirect_to('index') unless $self->session('user'); |
|
428 |
+ # A protected page auto rendering "protected.html.ep" |
|
429 |
+ get '/protected'; |
|
422 | 430 |
}; |
423 | 431 |
|
424 | 432 |
# Logout action |
... | ... |
@@ -521,10 +529,11 @@ actual action code needs to be changed. |
521 | 529 |
$self->redirect_to('protected'); |
522 | 530 |
} => 'index'); |
523 | 531 |
|
524 |
- $r->get('/protected' => sub { |
|
532 |
+ my $logged_in = $r->under(sub { |
|
525 | 533 |
my $self = shift; |
526 |
- return $self->redirect_to('index') unless $self->session('user'); |
|
534 |
+ return $self->session('user') || !$self->redirect_to('index'); |
|
527 | 535 |
}); |
536 |
+ $logged_in->get('/protected'); |
|
528 | 537 |
|
529 | 538 |
$r->get('/logout' => sub { |
530 | 539 |
my $self = shift; |
... | ... |
@@ -536,7 +545,8 @@ actual action code needs to be changed. |
536 | 545 |
1; |
537 | 546 |
|
538 | 547 |
The C<startup> method gets called right after instantiation and is the place |
539 |
-where the whole application gets set up. |
|
548 |
+where the whole application gets set up. Since full L<Mojolicious> |
|
549 |
+applications can use nested routes they have no need for C<group> blocks. |
|
540 | 550 |
|
541 | 551 |
=head2 Simplified application script |
542 | 552 |
|
... | ... |
@@ -580,9 +590,9 @@ Once again the actual action code does not change at all. |
580 | 590 |
$self->redirect_to('protected'); |
581 | 591 |
} |
582 | 592 |
|
583 |
- sub protected { |
|
593 |
+ sub logged_in { |
|
584 | 594 |
my $self = shift; |
585 |
- return $self->redirect_to('index') unless $self->session('user'); |
|
595 |
+ return $self->session('user') || !$self->redirect_to('index'); |
|
586 | 596 |
} |
587 | 597 |
|
588 | 598 |
sub logout { |
... | ... |
@@ -614,8 +624,9 @@ information. |
614 | 624 |
|
615 | 625 |
my $r = $self->routes; |
616 | 626 |
$r->any('/')->to('login#index')->name('index'); |
617 |
- $r->get('/protected')->to('login#protected')->name('protected'); |
|
618 |
- $r->get('/logout')->to('login#logout')->name('logout'); |
|
627 |
+ my $logged_in = $r->under->to('login#logged_in'); |
|
628 |
+ $logged_in->get('/protected')->to('login#protected'); |
|
629 |
+ $r->get('/logout')->to('login#logout'); |
|
619 | 630 |
} |
620 | 631 |
|
621 | 632 |
1; |
... | ... |
@@ -641,7 +652,8 @@ Finally C<myapp.pl> can be replaced with a proper L<Mojolicious> script. |
641 | 652 |
$ touch script/myapp |
642 | 653 |
$ chmod 744 script/myapp |
643 | 654 |
|
644 |
-Only a few small details change. |
|
655 |
+Only a few small details change, since installable scripts can't use L<lib> |
|
656 |
+without breaking updated dual-life modules. |
|
645 | 657 |
|
646 | 658 |
#!/usr/bin/env perl |
647 | 659 |
|
... | ... |
@@ -649,7 +661,7 @@ Only a few small details change. |
649 | 661 |
use warnings; |
650 | 662 |
|
651 | 663 |
use FindBin; |
652 |
- use lib "$FindBin::Bin/../lib"; |
|
664 |
+ BEGIN { unshift @INC, "$FindBin::Bin/../lib" } |
|
653 | 665 |
|
654 | 666 |
# Start command line interface for application |
655 | 667 |
require Mojolicious::Commands; |
... | ... |
@@ -673,7 +685,7 @@ C<t/login.t> can be simplified. |
673 | 685 |
->element_exists('form input[name="pass"]') |
674 | 686 |
->element_exists('form input[type="submit"]'); |
675 | 687 |
|
676 |
- $t->post_form_ok('/' => {user => 'sri', pass => 'secr3t'}) |
|
688 |
+ $t->post_ok('/' => form => {user => 'sri', pass => 'secr3t'}) |
|
677 | 689 |
->status_is(200)->text_like('html body' => qr/Welcome sri/); |
678 | 690 |
|
679 | 691 |
$t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/); |
... | ... |
@@ -66,7 +66,7 @@ start characters. |
66 | 66 |
% Perl code line, treated as "<% line =%>" |
67 | 67 |
%= Perl expression line, treated as "<%= line %>" |
68 | 68 |
%== Perl expression line, treated as "<%== line %>" |
69 |
- %# Comment line, treated as "<%# line =%>" |
|
69 |
+ %# Comment line, useful for debugging |
|
70 | 70 |
%% Replaced with "%", useful for generating templates |
71 | 71 |
|
72 | 72 |
Tags and lines work pretty much the same, but depending on context one will |
... | ... |
@@ -196,17 +196,17 @@ too. |
196 | 196 |
|
197 | 197 |
=head2 Rendering text |
198 | 198 |
|
199 |
-Perl characters can be rendered with the C<text> stash value, the given |
|
199 |
+Characters can be rendered to bytes with the C<text> stash value, the given |
|
200 | 200 |
content will be automatically encoded to bytes. |
201 | 201 |
|
202 | 202 |
$self->render(text => 'Hello Wörld!'); |
203 | 203 |
|
204 | 204 |
=head2 Rendering data |
205 | 205 |
|
206 |
-Raw bytes can be rendered with the C<data> stash value, no encoding will be |
|
206 |
+Bytes can be rendered with the C<data> stash value, no encoding will be |
|
207 | 207 |
performed. |
208 | 208 |
|
209 |
- $self->render(data => $octets); |
|
209 |
+ $self->render(data => $bytes); |
|
210 | 210 |
|
211 | 211 |
=head2 Rendering JSON |
212 | 212 |
|
... | ... |
@@ -217,11 +217,17 @@ renderer which get directly encoded to JSON. |
217 | 217 |
|
218 | 218 |
=head2 Partial rendering |
219 | 219 |
|
220 |
-Sometimes you might want to access the rendered result, for example to |
|
221 |
-generate emails, this can be done using the C<partial> stash value. |
|
220 |
+Sometimes you might want to use the rendered result directly instead of |
|
221 |
+generating a response, for example to send emails, this can be done using the |
|
222 |
+C<partial> stash value. |
|
222 | 223 |
|
223 | 224 |
my $html = $self->render('mail', partial => 1); |
224 | 225 |
|
226 |
+No encoding will be performed, making it easy to reuse the result in other |
|
227 |
+templates or to generate binary data. |
|
228 |
+ |
|
229 |
+ $self->render(data => $self->render('pdf_invoice', partial => 1)); |
|
230 |
+ |
|
225 | 231 |
=head2 Status code |
226 | 232 |
|
227 | 233 |
Response status codes can be changed with the C<status> stash value. |
... | ... |
@@ -328,10 +334,10 @@ used or an empty C<204> response rendered automatically. |
328 | 334 |
|
329 | 335 |
By now you've probably already encountered the built-in 404 (Not Found) and |
330 | 336 |
500 (Server Error) pages, that get rendered automatically when you make a |
331 |
-mistake. Especially during development they can be a great help, the methods |
|
337 |
+mistake. Especially during development they can be a great help, you can |
|
338 |
+render them manually with the methods |
|
332 | 339 |
L<Mojolicious::Controller/"render_exception"> and |
333 |
-L<Mojolicious::Controller/"render_not_found"> can be used to render them |
|
334 |
-manually. |
|
340 |
+L<Mojolicious::Controller/"render_not_found">. |
|
335 | 341 |
|
336 | 342 |
use Mojolicious::Lite; |
337 | 343 |
use Scalar::Util 'looks_like_number'; |
... | ... |
@@ -348,7 +354,7 @@ manually. |
348 | 354 |
return $self->render_exception('Division by zero!') if $divisor == 0; |
349 | 355 |
|
350 | 356 |
# 200 |
351 |
- $self->render_text($dividend / $divisor); |
|
357 |
+ $self->render(text => $dividend / $divisor); |
|
352 | 358 |
}; |
353 | 359 |
|
354 | 360 |
app->start; |
... | ... |
@@ -363,7 +369,12 @@ templates. |
363 | 369 |
<!DOCTYPE html> |
364 | 370 |
<html> |
365 | 371 |
<head><title>Server error</title></head> |
366 |
- <body><%= $exception %></body> |
|
372 |
+ <body> |
|
373 |
+ <h1>Exception</h1> |
|
374 |
+ <p><%= $exception->message %></p> |
|
375 |
+ <h1>Stash</h1> |
|
376 |
+ <pre><%= dumper $snapshot %></pre> |
|
377 |
+ </body> |
|
367 | 378 |
</html> |
368 | 379 |
|
369 | 380 |
=head2 Helpers |
... | ... |
@@ -580,18 +591,6 @@ template with named blocks that child templates can override. |
580 | 591 |
|
581 | 592 |
This chain could go on and on to allow a very high level of template reuse. |
582 | 593 |
|
583 |
-=head2 Memorizing template blocks |
|
584 |
- |
|
585 |
-Compiled templates are always cached in memory, but with the helper |
|
586 |
-L<Mojolicious::Plugin::DefaultHelpers/"memorize"> you can go one step further |
|
587 |
-and prevent template blocks from getting executed more than once. |
|
588 |
- |
|
589 |
- @@ cached.html.ep |
|
590 |
- % use Time::Piece; |
|
591 |
- %= memorize begin |
|
592 |
- This template was compiled at <%= localtime->hms %>. |
|
593 |
- % end |
|
594 |
- |
|
595 | 594 |
=head2 Adding helpers |
596 | 595 |
|
597 | 596 |
Adding and redefining helpers is very easy, you can use them to do pretty much |
... | ... |
@@ -600,8 +599,8 @@ everything. |
600 | 599 |
use Mojolicious::Lite; |
601 | 600 |
|
602 | 601 |
helper debug => sub { |
603 |
- my ($self, $string) = @_; |
|
604 |
- $self->app->log->debug($string); |
|
602 |
+ my ($self, $str) = @_; |
|
603 |
+ $self->app->log->debug($str); |
|
605 | 604 |
}; |
606 | 605 |
|
607 | 606 |
get '/' => sub { |
... | ... |
@@ -654,8 +653,8 @@ applications, plugins make that very simple. |
654 | 653 |
sub register { |
655 | 654 |
my ($self, $app) = @_; |
656 | 655 |
$app->helper(debug => sub { |
657 |
- my ($self, $string) = @_; |
|
658 |
- $self->app->log->debug($string); |
|
656 |
+ my ($self, $str) = @_; |
|
657 |
+ $self->app->log->debug($str); |
|
659 | 658 |
}); |
660 | 659 |
} |
661 | 660 |
|
... | ... |
@@ -670,7 +669,7 @@ The C<register> method will be called when you load the plugin. |
670 | 669 |
get '/' => sub { |
671 | 670 |
my $self = shift; |
672 | 671 |
$self->debug('It works.'); |
673 |
- $self->render_text('Hello.'); |
|
672 |
+ $self->render(text => 'Hello.'); |
|
674 | 673 |
}; |
675 | 674 |
|
676 | 675 |
app->start; |
... | ... |
@@ -681,7 +680,7 @@ generated. |
681 | 680 |
$ mojo generate plugin DebugHelper |
682 | 681 |
|
683 | 682 |
And if you have a C<PAUSE> account (which can be requested at |
684 |
-L<http://pause.perl.org>), you are only a few commands away from relasing it |
|
683 |
+L<http://pause.perl.org>), you are only a few commands away from releasing it |
|
685 | 684 |
to CPAN. |
686 | 685 |
|
687 | 686 |
$ perl Makefile.PL |
... | ... |
@@ -99,7 +99,7 @@ characters except C</> and C<.>. |
99 | 99 |
/sebastian23/hello -> /:name/hello -> {name => 'sebastian23'} |
100 | 100 |
/sebastian 23/hello -> /:name/hello -> {name => 'sebastian 23'} |
101 | 101 |
|
102 |
-A generic placeholder can be surrounded by parentheses to separate it from the |
|
102 |
+All placeholders can be surrounded by parentheses to separate them from the |
|
103 | 103 |
surrounding text. |
104 | 104 |
|
105 | 105 |
/hello -> /(:name)hello -> undef |
... | ... |
@@ -221,7 +221,7 @@ L<Mojolicious::Controller/"stash">. |
221 | 221 |
=head2 Nested routes |
222 | 222 |
|
223 | 223 |
It is also possible to build tree structures from routes to remove repetitive |
224 |
-code. A route with children can't match on it's own though, only the actual |
|
224 |
+code. A route with children can't match on its own though, only the actual |
|
225 | 225 |
endpoints of these nested routes can. |
226 | 226 |
|
227 | 227 |
# /foo -> undef |
... | ... |
@@ -301,7 +301,7 @@ dispatching to it. |
301 | 301 |
You can use the C<namespace> stash value to change the namespace of a whole |
302 | 302 |
route with all its children. |
303 | 303 |
|
304 |
- # /bye -> MyApp::Controller::Foo->bye |
|
304 |
+ # /bye -> MyApp::Controller::Foo::Bar->bye |
|
305 | 305 |
$r->route('/bye') |
306 | 306 |
->to(namespace => 'MyApp::Controller::Foo::Bar', action => 'bye'); |
307 | 307 |
|
... | ... |
@@ -425,12 +425,12 @@ routes and allows selective re-enabling. |
425 | 425 |
# /foo -> {controller => 'foo', action => 'bar'} |
426 | 426 |
# /foo.html -> undef |
427 | 427 |
# /baz -> undef |
428 |
- # /baz.txt -> {controller => 'bar', action => 'baz', format => 'txt'} |
|
429 |
- # /baz.html -> {controller => 'bar', action => 'baz', format => 'html'} |
|
428 |
+ # /baz.txt -> {controller => 'baz', action => 'yada', format => 'txt'} |
|
429 |
+ # /baz.html -> {controller => 'baz', action => 'yada', format => 'html'} |
|
430 | 430 |
# /baz.xml -> undef |
431 | 431 |
my $inactive = $r->route(format => 0); |
432 |
- $inactive->route('/foo')->to('foo#none'); |
|
433 |
- $inactive->route('/baz', format => [qw(txt html)])->to('bar#baz'); |
|
432 |
+ $inactive->route('/foo')->to('foo#bar'); |
|
433 |
+ $inactive->route('/baz', format => [qw(txt html)])->to('baz#yada'); |
|
434 | 434 |
|
435 | 435 |
=head2 Named routes |
436 | 436 |
|
... | ... |
@@ -489,6 +489,14 @@ methods to pass. |
489 | 489 |
$r->route('/bye')->via('GET', 'POST') |
490 | 490 |
->to(controller => 'foo', action => 'bye'); |
491 | 491 |
|
492 |
+With one small exception, C<HEAD> requests are considered equal to C<GET> and |
|
493 |
+content will not be sent with the response. |
|
494 |
+ |
|
495 |
+ # GET /test -> {controller => 'bar', action => 'test'} |
|
496 |
+ # HEAD /test -> {controller => 'bar', action => 'test'} |
|
497 |
+ # PUT /test -> undef |
|
498 |
+ $r->route('/test')->via('GET')->to(controller => 'bar', action => 'test'); |
|
499 |
+ |
|
492 | 500 |
=head2 WebSockets |
493 | 501 |
|
494 | 502 |
With the method L<Mojolicious::Routes::Route/"websocket"> you can restrict |
... | ... |
@@ -634,7 +642,7 @@ Same for monitoring tasks. |
634 | 642 |
$self->hook(after_dispatch => sub { |
635 | 643 |
my $self = shift; |
636 | 644 |
return unless my $e = $self->stash('exception'); |
637 |
- $self->ua->post_form('https://kraih.com/bugs' => {exception => $e}); |
|
645 |
+ $self->ua->post('https://example.com/bugs' => form => {exception => $e}); |
|
638 | 646 |
}); |
639 | 647 |
|
640 | 648 |
For a full list of available hooks see L<Mojolicious/"hook">. |
... | ... |
@@ -682,7 +690,7 @@ Less commonly used and more powerful features. |
682 | 690 |
=head2 IRIs |
683 | 691 |
|
684 | 692 |
IRIs are handled transparently, that means paths are guaranteed to be |
685 |
-unescaped and decoded to Perl characters. |
|
693 |
+unescaped and decoded from bytes to characters. |
|
686 | 694 |
|
687 | 695 |
# GET /☃ (unicode snowman) -> {controller => 'foo', action => 'snowman'} |
688 | 696 |
$r->get('/☃')->to('foo#snowman'); |
... | ... |
@@ -829,7 +837,7 @@ self-contained applications under a prefix. |
829 | 837 |
plugin Mount => {'/prefix' => '/home/sri/myapp.pl'}; |
830 | 838 |
|
831 | 839 |
# Normal route |
832 |
- get '/' => sub { shift->render_text('Hello World!') }; |
|
840 |
+ get '/' => sub { shift->render(text => 'Hello World!') }; |
|
833 | 841 |
|
834 | 842 |
app->start; |
835 | 843 |
|
... | ... |
@@ -61,6 +61,8 @@ sub import { |
61 | 61 |
|
62 | 62 |
1; |
63 | 63 |
|
64 |
+=encoding utf8 |
|
65 |
+ |
|
64 | 66 |
=head1 NAME |
65 | 67 |
|
66 | 68 |
Mojolicious::Lite - Real-time micro web framework |
... | ... |
@@ -108,8 +110,6 @@ featured web application. |
108 | 110 |
|
109 | 111 |
app->start; |
110 | 112 |
|
111 |
-=head2 Generator |
|
112 |
- |
|
113 | 113 |
There is also a helper command to generate a small example application. |
114 | 114 |
|
115 | 115 |
$ mojo generate lite_app |
... | ... |
@@ -132,9 +132,8 @@ just work without commands. |
132 | 132 |
$ ./myapp.pl |
133 | 133 |
...List of available commands (or automatically detected environment)... |
134 | 134 |
|
135 |
-=head2 Start |
|
136 |
- |
|
137 |
-The app->start call that starts the L<Mojolicious> command system can be |
|
135 |
+The C<app-E<gt>start> call that starts the L<Mojolicious> command system |
|
136 |
+should usually be the last expression in your application and can be |
|
138 | 137 |
customized to override normal C<@ARGV> use. |
139 | 138 |
|
140 | 139 |
app->start('cgi'); |
... | ... |
@@ -151,12 +150,13 @@ every change. |
151 | 150 |
=head2 Routes |
152 | 151 |
|
153 | 152 |
Routes are basically just fancy paths that can contain different kinds of |
154 |
-placeholders. C<$self> is a L<Mojolicious::Controller> object containing both, |
|
155 |
-the HTTP request and response. |
|
153 |
+placeholders and usually lead to an action. The first argument passed to all |
|
154 |
+actions (the invocant C<$self>) is a L<Mojolicious::Controller> object |
|
155 |
+containing both the HTTP request and response. |
|
156 | 156 |
|
157 | 157 |
use Mojolicious::Lite; |
158 | 158 |
|
159 |
- # /foo |
|
159 |
+ # Route leading to an action |
|
160 | 160 |
get '/foo' => sub { |
161 | 161 |
my $self = shift; |
162 | 162 |
$self->render(text => 'Hello World!'); |
... | ... |
@@ -164,9 +164,12 @@ the HTTP request and response. |
164 | 164 |
|
165 | 165 |
app->start; |
166 | 166 |
|
167 |
+Response content is often generated by actions with |
|
168 |
+L<Mojolicious::Controller/"render">, but more about that later. |
|
169 |
+ |
|
167 | 170 |
=head2 GET/POST parameters |
168 | 171 |
|
169 |
-All C<GET> and C<POST> parameters are accessible via |
|
172 |
+All C<GET> and C<POST> parameters sent with the request are accessible via |
|
170 | 173 |
L<Mojolicious::Controller/"param">. |
171 | 174 |
|
172 | 175 |
use Mojolicious::Lite; |
... | ... |
@@ -187,7 +190,7 @@ which can be inlined in the C<DATA> section. |
187 | 190 |
|
188 | 191 |
use Mojolicious::Lite; |
189 | 192 |
|
190 |
- # /bar |
|
193 |
+ # Route leading to an action that renders a template |
|
191 | 194 |
get '/bar' => sub { |
192 | 195 |
my $self = shift; |
193 | 196 |
$self->stash(one => 23); |
... | ... |
@@ -210,11 +213,13 @@ full access to all HTTP features and information. |
210 | 213 |
|
211 | 214 |
use Mojolicious::Lite; |
212 | 215 |
|
213 |
- # /agent |
|
216 |
+ # Access request and reponse information |
|
214 | 217 |
get '/agent' => sub { |
215 | 218 |
my $self = shift; |
219 |
+ my $host = $self->req->url->to_abs->host; |
|
220 |
+ my $ua = $self->req->headers->user_agent; |
|
216 | 221 |
$self->res->headers->header('X-Bender' => 'Bite my shiny metal ass!'); |
217 |
- $self->render(text => $self->req->headers->user_agent); |
|
222 |
+ $self->render(text => "Request by $ua reached $host."); |
|
218 | 223 |
}; |
219 | 224 |
|
220 | 225 |
app->start; |
... | ... |
@@ -230,13 +235,13 @@ without non-word characters. |
230 | 235 |
|
231 | 236 |
use Mojolicious::Lite; |
232 | 237 |
|
233 |
- # / |
|
238 |
+ # Render the template "index.html.ep" |
|
234 | 239 |
get '/' => sub { |
235 | 240 |
my $self = shift; |
236 | 241 |
$self->render; |
237 | 242 |
} => 'index'; |
238 | 243 |
|
239 |
- # /hello |
|
244 |
+ # Render the template "hello.html.ep" |
|
240 | 245 |
get '/hello'; |
241 | 246 |
|
242 | 247 |
app->start; |
... | ... |
@@ -258,11 +263,7 @@ L<Mojolicious::Plugin::DefaultHelpers/"content">. |
258 | 263 |
|
259 | 264 |
use Mojolicious::Lite; |
260 | 265 |
|
261 |
- # /with_layout |
|
262 |
- get '/with_layout' => sub { |
|
263 |
- my $self = shift; |
|
264 |
- $self->render('with_layout'); |
|
265 |
- }; |
|
266 |
+ get '/with_layout'; |
|
266 | 267 |
|
267 | 268 |
app->start; |
268 | 269 |
__DATA__ |
... | ... |
@@ -286,7 +287,6 @@ delimited by the C<begin> and C<end> keywords. |
286 | 287 |
|
287 | 288 |
use Mojolicious::Lite; |
288 | 289 |
|
289 |
- # /with_block |
|
290 | 290 |
get '/with_block' => 'block'; |
291 | 291 |
|
292 | 292 |
app->start; |
... | ... |
@@ -313,11 +313,7 @@ pass around blocks of captured content. |
313 | 313 |
|
314 | 314 |
use Mojolicious::Lite; |
315 | 315 |
|
316 |
- # /captured |
|
317 |
- get '/captured' => sub { |
|
318 |
- my $self = shift; |
|
319 |
- $self->render('captured'); |
|
320 |
- }; |
|
316 |
+ get '/captured'; |
|
321 | 317 |
|
322 | 318 |
app->start; |
323 | 319 |
__DATA__ |
... | ... |
@@ -350,7 +346,7 @@ L<Mojolicious::Plugin::TagHelpers>. |
350 | 346 |
|
351 | 347 |
use Mojolicious::Lite; |
352 | 348 |
|
353 |
- # "whois" helper |
|
349 |
+ # A helper to identify visitors |
|
354 | 350 |
helper whois => sub { |
355 | 351 |
my $self = shift; |
356 | 352 |
my $agent = $self->req->headers->user_agent || 'Anonymous'; |
... | ... |
@@ -358,7 +354,7 @@ L<Mojolicious::Plugin::TagHelpers>. |
358 | 354 |
return "$agent ($ip)"; |
359 | 355 |
}; |
360 | 356 |
|
361 |
- # /secret |
|
357 |
+ # Use helper in action and template |
|
362 | 358 |
get '/secret' => sub { |
363 | 359 |
my $self = shift; |
364 | 360 |
my $user = $self->whois; |
... | ... |
@@ -434,7 +430,7 @@ C<.>. |
434 | 430 |
|
435 | 431 |
=head2 HTTP methods |
436 | 432 |
|
437 |
-Routes can be restricted to specific request methods. |
|
433 |
+Routes can be restricted to specific request methods with different keywords. |
|
438 | 434 |
|
439 | 435 |
use Mojolicious::Lite; |
440 | 436 |
|
... | ... |
@@ -542,11 +538,11 @@ are only evaluated if the callback returned a true value. |
542 | 538 |
return undef; |
543 | 539 |
}; |
544 | 540 |
|
545 |
- # / (with authentication) |
|
541 |
+ # Only reached when authenticated |
|
546 | 542 |
get '/' => 'index'; |
547 | 543 |
|
548 | 544 |
app->start; |
549 |
- __DATA__; |
|
545 |
+ __DATA__ |
|
550 | 546 |
|
551 | 547 |
@@ denied.html.ep |
552 | 548 |
You are not Bender, permission denied. |
... | ... |
@@ -567,7 +563,7 @@ Prefixing multiple routes is another good use for C<under>. |
567 | 563 |
# /foo/baz |
568 | 564 |
get '/baz' => {text => 'foo baz'}; |
569 | 565 |
|
570 |
- # / |
|
566 |
+ # / (reset) |
|
571 | 567 |
under '/' => {message => 'whatever'}; |
572 | 568 |
|
573 | 569 |
# /bar |
... | ... |
@@ -594,7 +590,7 @@ C<under> statements. |
594 | 590 |
# Local logic shared only by routes in this group |
595 | 591 |
under '/admin' => sub { |
596 | 592 |
my $self = shift; |
597 |
- return 1 if $self->req->heaers->header('X-Awesome'); |
|
593 |
+ return 1 if $self->req->headers->header('X-Awesome'); |
|
598 | 594 |
$self->render(text => "You're not awesome enough."); |
599 | 595 |
return undef; |
600 | 596 |
}; |
... | ... |
@@ -621,7 +617,6 @@ Formats can be automatically detected by looking at file extensions. |
621 | 617 |
$self->render('detected'); |
622 | 618 |
}; |
623 | 619 |
|
624 |
- |
|
625 | 620 |
app->start; |
626 | 621 |
__DATA__ |
627 | 622 |
|
... | ... |
@@ -643,9 +638,9 @@ Restrictive placeholders can also be used. |
643 | 638 |
# /hello.txt |
644 | 639 |
get '/hello' => [format => [qw(json txt)]] => sub { |
645 | 640 |
my $self = shift; |
646 |
- return $self->render_json({hello => 'world'}) |
|
641 |
+ return $self->render(json => {hello => 'world'}) |
|
647 | 642 |
if $self->stash('format') eq 'json'; |
648 |
- $self->render_text('hello world'); |
|
643 |
+ $self->render(text => 'hello world'); |
|
649 | 644 |
}; |
650 | 645 |
|
651 | 646 |
app->start; |
... | ... |
@@ -697,6 +692,47 @@ L<Mojolicious/"types">. |
697 | 692 |
|
698 | 693 |
app->types->type(rdf => 'application/rdf+xml'); |
699 | 694 |
|
695 |
+=head2 Static files |
|
696 |
+ |
|
697 |
+Similar to templates, but with only a single file extension and optional |
|
698 |
+Base64 encoding, static files can be inlined in the C<DATA> section and are |
|
699 |
+served automatically. |
|
700 |
+ |
|
701 |
+ use Mojolicious::Lite; |
|
702 |
+ |
|
703 |
+ app->start; |
|
704 |
+ __DATA__ |
|
705 |
+ |
|
706 |
+ @@ something.js |
|
707 |
+ alert('hello!'); |
|
708 |
+ |
|
709 |
+ @@ test.txt (base64) |
|
710 |
+ dGVzdCAxMjMKbGFsYWxh |
|
711 |
+ |
|
712 |
+External static files are not limited to a single file extension and will be |
|
713 |
+served automatically from a C<public> directory if it exists. |
|
714 |
+ |
|
715 |
+ $ mkdir public |
|
716 |
+ $ mv something.js public/something.js |
|
717 |
+ $ mv mojolicious.tar.gz public/mojolicious.tar.gz |
|
718 |
+ |
|
719 |
+Both have a higher precedence than routes. |
|
720 |
+ |
|
721 |
+=head2 External templates |
|
722 |
+ |
|
723 |
+External templates will be searched by the renderer in a C<templates> |
|
724 |
+directory if it exists. |
|
725 |
+ |
|
726 |
+ use Mojolicious::Lite; |
|
727 |
+ |
|
728 |
+ # Render template "templates/foo/bar.html.ep" |
|
729 |
+ any '/external' => sub { |
|
730 |
+ my $self = shift; |
|
731 |
+ $self->render('foo/bar'); |
|
732 |
+ }; |
|
733 |
+ |
|
734 |
+ app->start; |
|
735 |
+ |
|
700 | 736 |
=head2 Conditions |
701 | 737 |
|
702 | 738 |
Conditions such as C<agent> and C<host> from |
... | ... |
@@ -705,13 +741,13 @@ constructs. |
705 | 741 |
|
706 | 742 |
use Mojolicious::Lite; |
707 | 743 |
|
708 |
- # /foo (Firefox) |
|
744 |
+ # Firefox |
|
709 | 745 |
get '/foo' => (agent => qr/Firefox/) => sub { |
710 | 746 |
my $self = shift; |
711 | 747 |
$self->render(text => 'Congratulations, you are using a cool browser.'); |
712 | 748 |
}; |
713 | 749 |
|
714 |
- # /foo (Internet Explorer) |
|
750 |
+ # Internet Explorer |
|
715 | 751 |
get '/foo' => (agent => qr/Internet Explorer/) => sub { |
716 | 752 |
my $self = shift; |
717 | 753 |
$self->render(text => 'Dude, you really need to upgrade to Firefox.'); |
... | ... |
@@ -729,10 +765,12 @@ constructs. |
729 | 765 |
|
730 | 766 |
Signed cookie based sessions just work out of the box as soon as you start |
731 | 767 |
using them through the helper |
732 |
-L<Mojolicious::Plugin::DefaultHelpers/"session">. |
|
768 |
+L<Mojolicious::Plugin::DefaultHelpers/"session">, just be aware that all |
|
769 |
+session data gets serialized with L<Mojo::JSON>. |
|
733 | 770 |
|
734 | 771 |
use Mojolicious::Lite; |
735 | 772 |
|
773 |
+ # Access session data in action and template |
|
736 | 774 |
get '/counter' => sub { |
737 | 775 |
my $self = shift; |
738 | 776 |
$self->session->{counter}++; |
... | ... |
@@ -744,10 +782,6 @@ L<Mojolicious::Plugin::DefaultHelpers/"session">. |
744 | 782 |
@@ counter.html.ep |
745 | 783 |
Counter: <%= session 'counter' %> |
746 | 784 |
|
747 |
-Just be aware that all session data gets serialized with L<Mojo::JSON>. |
|
748 |
- |
|
749 |
-=head2 Secret |
|
750 |
- |
|
751 | 785 |
Note that you should use a custom L<Mojolicious/"secret"> to make signed |
752 | 786 |
cookies really secure. |
753 | 787 |
|
... | ... |
@@ -797,7 +831,7 @@ temporary file. |
797 | 831 |
</html> |
798 | 832 |
|
799 | 833 |
To protect you from excessively large files there is also a limit of C<5MB> by |
800 |
-default, which you can tweak with the C<MOJO_MAX_MESSAGE_SIZE> environment |
|
834 |
+default, which you can tweak with the MOJO_MAX_MESSAGE_SIZE environment |
|
801 | 835 |
variable. |
802 | 836 |
|
803 | 837 |
# Increase limit to 1GB |
... | ... |
@@ -811,68 +845,95 @@ L<Mojo::JSON> and L<Mojo::DOM> this can be a very powerful tool. |
811 | 845 |
|
812 | 846 |
use Mojolicious::Lite; |
813 | 847 |
|
814 |
- get '/test' => sub { |
|
848 |
+ get '/headers' => sub { |
|
815 | 849 |
my $self = shift; |
816 |
- $self->render(data => $self->ua->get('http://mojolicio.us')->res->body); |
|
850 |
+ my $url = $self->param('url') || 'http://mojolicio.us'; |
|
851 |
+ my $dom = $self->ua->get($url)->res->dom; |
|
852 |
+ $self->render(json => [$dom->find('h1, h2, h3')->pluck('text')->each]); |
|
817 | 853 |
}; |
818 | 854 |
|
819 | 855 |
app->start; |
820 | 856 |
|
821 | 857 |
=head2 WebSockets |
822 | 858 |
|
823 |
-WebSocket applications have never been this easy before. |
|
859 |
+WebSocket applications have never been this simple before. Just receive |
|
860 |
+messages by subscribing to events such as |
|
861 |
+L<Mojo::Transaction::WebSocket/"json"> with L<Mojolicious::Controller/"on"> |
|
862 |
+and return them with L<Mojolicious::Controller/"send">. |
|
824 | 863 |
|
825 | 864 |
use Mojolicious::Lite; |
826 | 865 |
|
827 | 866 |
websocket '/echo' => sub { |
828 | 867 |
my $self = shift; |
829 |
- $self->on(message => sub { |
|
830 |
- my ($self, $msg) = @_; |
|
831 |
- $self->send("echo: $msg"); |
|
868 |
+ $self->on(json => sub { |
|
869 |
+ my ($self, $hash) = @_; |
|
870 |
+ $hash->{msg} = "echo: $hash->{msg}"; |
|
871 |
+ $self->send({json => $hash}); |
|
832 | 872 |
}); |
833 | 873 |
}; |
834 | 874 |
|
875 |
+ get '/' => 'index'; |
|
876 |
+ |
|
835 | 877 |
app->start; |
878 |
+ __DATA__ |
|
836 | 879 |
|
837 |
-The event L<Mojo::Transaction::WebSocket/"message">, which you can subscribe |
|
838 |
-to with L<Mojolicious::Controller/"on">, will be emitted for every new |
|
839 |
-WebSocket message that is received. |
|
880 |
+ @@ index.html.ep |
|
881 |
+ <!DOCTYPE html> |
|
882 |
+ <html> |
|
883 |
+ <head> |
|
884 |
+ <title>Echo</title> |
|
885 |
+ %= javascript begin |
|
886 |
+ var ws = new WebSocket('<%= url_for('echo')->to_abs %>'); |
|
887 |
+ ws.onmessage = function (event) { |
|
888 |
+ document.body.innerHTML += JSON.parse(event.data).msg; |
|
889 |
+ }; |
|
890 |
+ ws.onopen = function (event) { |
|
891 |
+ ws.send(JSON.stringify({msg: 'I ♥ Mojolicious!'})); |
|
892 |
+ }; |
|
893 |
+ % end |
|
894 |
+ </head> |
|
895 |
+ </html> |
|
840 | 896 |
|
841 |
-=head2 External templates |
|
897 |
+For more information about real-time web features see also |
|
898 |
+L<Mojolicious::Guides::Cookbook/"REAL-TIME WEB">. |
|
842 | 899 |
|
843 |
-External templates will be searched by the renderer in a C<templates> |
|
844 |
-directory. |
|
900 |
+=head2 Mode |
|
901 |
+ |
|
902 |
+You can use the L<Mojo::Log> object from L<Mojo/"log"> to portably collect |
|
903 |
+debug messages and automatically disable them later in a production setup by |
|
904 |
+changing the L<Mojolicious> operating mode. |
|
845 | 905 |
|
846 | 906 |
use Mojolicious::Lite; |
847 | 907 |
|
848 |
- # /external |
|
849 |
- any '/external' => sub { |
|
908 |
+ get '/' => sub { |
|
850 | 909 |
my $self = shift; |
851 |
- |
|
852 |
- # templates/foo/bar.html.ep |
|
853 |
- $self->render('foo/bar'); |
|
910 |
+ $self->app->log->debug('Rendering "Hello World!" message.'); |
|
911 |
+ $self->render(text => 'Hello World!'); |
|
854 | 912 |
}; |
855 | 913 |
|
914 |
+ app->log->debug('Starting application.'); |
|
856 | 915 |
app->start; |
857 | 916 |
|
858 |
-=head2 Static files |
|
917 |
+The default operating mode will usually be C<development> and can be changed |
|
918 |
+with command line options or the MOJO_MODE and PLACK_ENV environment |
|
919 |
+variables. A mode other than C<development> will raise the log level from |
|
920 |
+C<debug> to C<info>. |
|
859 | 921 |
|
860 |
-Static files will be automatically served from the C<DATA> section (even |
|
861 |
-Base64 encoded) or a C<public> directory if it exists. |
|
922 |
+ $ ./myapp.pl daemon -m production |
|
862 | 923 |
|
863 |
- @@ something.js |
|
864 |
- alert('hello!'); |
|
924 |
+All messages will be written to C<STDERR> or a C<log/$mode.log> file if a |
|
925 |
+C<log> directory exists. |
|
865 | 926 |
|
866 |
- @@ test.txt (base64) |
|
867 |
- dGVzdCAxMjMKbGFsYWxh |
|
927 |
+ $ mkdir log |
|
868 | 928 |
|
869 |
- $ mkdir public |
|
870 |
- $ mv something.js public/something.js |
|
929 |
+Mode changes also affects a few other aspects of the framework, such as mode |
|
930 |
+specific C<exception> and C<not_found> templates. |
|
871 | 931 |
|
872 | 932 |
=head2 Testing |
873 | 933 |
|
874 | 934 |
Testing your application is as easy as creating a C<t> directory and filling |
875 |
-it with normal Perl unit tests. |
|
935 |
+it with normal Perl unit tests, which can be a lot of fun thanks to |
|
936 |
+L<Test::Mojo>. |
|
876 | 937 |
|
877 | 938 |
use Test::More; |
878 | 939 |
use Test::Mojo; |
... | ... |
@@ -888,42 +949,7 @@ it with normal Perl unit tests. |
888 | 949 |
Run all unit tests with the C<test> command. |
889 | 950 |
|
890 | 951 |
$ ./myapp.pl test |
891 |
- |
|
892 |
-To make your tests more noisy and show you all log messages you can also |
|
893 |
-change the application log level directly in your test files. |
|
894 |
- |
|
895 |
- $t->app->log->level('debug'); |
|
896 |
- |
|
897 |
-=head2 Mode |
|
898 |
- |
|
899 |
-To disable debug messages later in a production setup, you can change the |
|
900 |
-L<Mojolicious> operating mode with command line options or the C<MOJO_MODE> |
|
901 |
-environment variable, the default will usually be C<development>. |
|
902 |
- |
|
903 |
- $ ./myapp.pl daemon -m production |
|
904 |
- |
|
905 |
-This also affects many other aspects of the framework, such as mode specific |
|
906 |
-C<exception> and C<not_found> templates. |
|
907 |
- |
|
908 |
-=head2 Logging |
|
909 |
- |
|
910 |
-L<Mojo::Log> messages will be automatically written to C<STDERR> or a |
|
911 |
-C<log/$mode.log> file if a C<log> directory exists. |
|
912 |
- |
|
913 |
- $ mkdir log |
|
914 |
- |
|
915 |
-For more control the L<Mojolicious> object can be accessed directly. |
|
916 |
- |
|
917 |
- use Mojolicious::Lite; |
|
918 |
- |
|
919 |
- app->log->level('error'); |
|
920 |
- app->routes->get('/foo/:bar' => sub { |
|
921 |
- my $self = shift; |
|
922 |
- $self->app->log->debug('Got a request for "Hello Mojo!".'); |
|
923 |
- $self->render(text => 'Hello Mojo!'); |
|
924 |
- }); |
|
925 |
- |
|
926 |
- app->start; |
|
952 |
+ $ ./myapp.pl test -v |
|
927 | 953 |
|
928 | 954 |
=head2 More |
929 | 955 |
|
... | ... |
@@ -1030,7 +1056,7 @@ more argument variations. |
1030 | 1056 |
my $route = websocket '/:foo' => sub {...}; |
1031 | 1057 |
|
1032 | 1058 |
Generate route with L<Mojolicious::Routes::Route/"websocket">, matching only |
1033 |
-C<WebSocket> handshakes. See also the tutorial above for more argument |
|
1059 |
+WebSocket handshakes. See also the tutorial above for more argument |
|
1034 | 1060 |
variations. |
1035 | 1061 |
|
1036 | 1062 |
=head1 ATTRIBUTES |
... | ... |
@@ -53,7 +53,8 @@ L<Mojolicious::Plugin> and implements the following new ones. |
53 | 53 |
|
54 | 54 |
$plugin->register(Mojolicious->new, {charset => 'Shift_JIS'}); |
55 | 55 |
|
56 |
-Register hooks in L<Mojolicious> application. |
|
56 |
+Register C<before_dispatch> hook in L<Mojolicious> application and change a |
|
57 |
+few defaults. |
|
57 | 58 |
|
58 | 59 |
=head1 SEE ALSO |
59 | 60 |
|
... | ... |
@@ -64,7 +64,7 @@ Mojolicious::Plugin::Config - Perl-ish configuration plugin |
64 | 64 |
|
65 | 65 |
=head1 SYNOPSIS |
66 | 66 |
|
67 |
- # myapp.conf |
|
67 |
+ # myapp.conf (it's just Perl returning a hash) |
|
68 | 68 |
{ |
69 | 69 |
foo => "bar", |
70 | 70 |
music_dir => app->home->rel_dir('music') |
... | ... |
@@ -72,15 +72,18 @@ Mojolicious::Plugin::Config - Perl-ish configuration plugin |
72 | 72 |
|
73 | 73 |
# Mojolicious |
74 | 74 |
my $config = $self->plugin('Config'); |
75 |
+ say $config->{foo}; |
|
75 | 76 |
|
76 | 77 |
# Mojolicious::Lite |
77 | 78 |
my $config = plugin 'Config'; |
79 |
+ say $config->{foo}; |
|
78 | 80 |
|
79 | 81 |
# foo.html.ep |
80 | 82 |
%= $config->{foo} |
81 | 83 |
|
82 | 84 |
# The configuration is available application wide |
83 | 85 |
my $config = app->config; |
86 |
+ say $config->{foo}; |
|
84 | 87 |
|
85 | 88 |
# Everything can be customized with options |
86 | 89 |
my $config = plugin Config => {file => '/etc/myapp.stuff'}; |
... | ... |
@@ -122,7 +125,7 @@ File extension for generated configuration filenames, defaults to C<conf>. |
122 | 125 |
plugin Config => {file => 'myapp.conf'}; |
123 | 126 |
plugin Config => {file => '/etc/foo.stuff'}; |
124 | 127 |
|
125 |
-Full path to configuration file, defaults to the value of the C<MOJO_CONFIG> |
|
128 |
+Full path to configuration file, defaults to the value of the MOJO_CONFIG |
|
126 | 129 |
environment variable or C<myapp.conf> in the application home directory. |
127 | 130 |
|
128 | 131 |
=head1 METHODS |
... | ... |
@@ -159,7 +162,7 @@ Parse configuration file. |
159 | 162 |
my $config = $plugin->register(Mojolicious->new); |
160 | 163 |
my $config = $plugin->register(Mojolicious->new, {file => '/etc/app.conf'}); |
161 | 164 |
|
162 |
-Register plugin in L<Mojolicious> application. |
|
165 |
+Register plugin in L<Mojolicious> application and merge configuration. |
|
163 | 166 |
|
164 | 167 |
=head1 SEE ALSO |
165 | 168 |
|
... | ... |
@@ -12,7 +12,7 @@ sub register { |
12 | 12 |
$app->helper($name => sub { shift->$name(@_) }); |
13 | 13 |
} |
14 | 14 |
|
15 |
- # Stash key shortcuts |
|
15 |
+ # Stash key shortcuts (should not generate log messages) |
|
16 | 16 |
for my $name (qw(extends layout title)) { |
17 | 17 |
$app->helper( |
18 | 18 |
$name => sub { |
... | ... |
@@ -26,46 +26,13 @@ sub register { |
26 | 26 |
} |
27 | 27 |
|
28 | 28 |
$app->helper(config => sub { shift->app->config(@_) }); |
29 |
+ |
|
29 | 30 |
$app->helper(content => \&_content); |
30 | 31 |
$app->helper(content_for => \&_content_for); |
31 | 32 |
$app->helper(current_route => \&_current_route); |
32 | 33 |
$app->helper(dumper => \&_dumper); |
33 | 34 |
$app->helper(include => \&_include); |
34 |
- |
|
35 |
- my %mem; |
|
36 |
- $app->helper( |
|
37 |
- memorize => sub { |
|
38 |
- my $self = shift; |
|
39 |
- return '' unless ref(my $cb = pop) eq 'CODE'; |
|
40 |
- my ($name, $args) |
|
41 |
- = ref $_[0] eq 'HASH' ? (undef, shift) : (shift, shift || {}); |
|
42 |
- |
|
43 |
- # Default name |
|
44 |
- $name ||= join '', map { $_ || '' } (caller(1))[0 .. 3]; |
|
45 |
- |
|
46 |
- # Expire old results |
|
47 |
- my $expires = $args->{expires} || 0; |
|
48 |
- delete $mem{$name} |
|
49 |
- if exists $mem{$name} && $expires > 0 && $mem{$name}{expires} < time; |
|
50 |
- |
|
51 |
- # Memorized result |
|
52 |
- return $mem{$name}{content} if exists $mem{$name}; |
|
53 |
- |
|
54 |
- # Memorize new result |
|
55 |
- $mem{$name}{expires} = $expires; |
|
56 |
- return $mem{$name}{content} = $cb->(); |
|
57 |
- } |
|
58 |
- ); |
|
59 |
- |
|
60 |
- # DEPRECATED in Rainbow! |
|
61 |
- $app->helper( |
|
62 |
- render_content => sub { |
|
63 |
- warn "Mojolicious::Controller->render_content is DEPRECATED!\n"; |
|
64 |
- shift->content(@_); |
|
65 |
- } |
|
66 |
- ); |
|
67 |
- |
|
68 |
- $app->helper(url_with => \&_url_with); |
|
35 |
+ $app->helper(url_with => \&_url_with); |
|
69 | 36 |
} |
70 | 37 |
|
71 | 38 |
sub _content { |
... | ... |
@@ -94,7 +61,10 @@ sub _current_route { |
94 | 61 |
return $endpoint->name eq shift; |
95 | 62 |
} |
96 | 63 |
|
97 |
-sub _dumper { shift; Data::Dumper->new([@_])->Indent(1)->Terse(1)->Dump } |
|
64 |
+sub _dumper { |
|
65 |
+ my $self = shift; |
|
66 |
+ return Data::Dumper->new([@_])->Indent(1)->Sortkeys(1)->Terse(1)->Dump; |
|
67 |
+} |
|
98 | 68 |
|
99 | 69 |
sub _include { |
100 | 70 |
my $self = shift; |
... | ... |
@@ -110,7 +80,7 @@ sub _include { |
110 | 80 |
my @keys = keys %$args; |
111 | 81 |
local @{$self->stash}{@keys} = @{$args}{@keys}; |
112 | 82 |
|
113 |
- return $self->render_partial(layout => $layout, extend => $extends); |
|
83 |
+ return $self->render(partial => 1, layout => $layout, extend => $extends); |
|
114 | 84 |
} |
115 | 85 |
|
116 | 86 |
sub _url_with { |
... | ... |
@@ -229,23 +199,6 @@ only available in the partial template. |
229 | 199 |
Render this template with a layout. All additional values get merged into the |
230 | 200 |
C<stash>. |
231 | 201 |
|
232 |
-=head2 memorize |
|
233 |
- |
|
234 |
- %= memorize begin |
|
235 |
- %= time |
|
236 |
- % end |
|
237 |
- %= memorize {expires => time + 1} => begin |
|
238 |
- %= time |
|
239 |
- % end |
|
240 |
- %= memorize foo => begin |
|
241 |
- %= time |
|
242 |
- % end |
|
243 |
- %= memorize foo => {expires => time + 1} => begin |
|
244 |
- %= time |
|
245 |
- % end |
|
246 |
- |
|
247 |
-Memorize block result in memory and prevent future execution. |
|
248 |
- |
|
249 | 202 |
=head2 param |
250 | 203 |
|
251 | 204 |
%= param 'foo' |
... | ... |
@@ -16,11 +16,13 @@ sub _epl { |
16 | 16 |
return undef unless defined $path; |
17 | 17 |
|
18 | 18 |
# Cached |
19 |
- my $cache = $renderer->cache; |
|
20 | 19 |
my $key = delete $options->{cache} || $path; |
21 |
- my $mt = $cache->get($key) || Mojo::Template->new; |
|
20 |
+ my $cache = $renderer->cache; |
|
21 |
+ my $mt = $cache->get($key); |
|
22 |
+ $mt ||= $cache->set($key => Mojo::Template->new)->get($key); |
|
23 |
+ my $log = $c->app->log; |
|
22 | 24 |
if ($mt->compiled) { |
23 |
- $c->app->log->debug("Rendering cached @{[$mt->name]}."); |
|
25 |
+ $log->debug("Rendering cached @{[$mt->name]}."); |
|
24 | 26 |
$$output = $mt->interpret($c); |
25 | 27 |
} |
26 | 28 |
|
... | ... |
@@ -29,7 +31,7 @@ sub _epl { |
29 | 31 |
|
30 | 32 |
# Inline |
31 | 33 |
if (defined $inline) { |
32 |
- $c->app->log->debug('Rendering inline template.'); |
|
34 |
+ $log->debug('Rendering inline template.'); |
|
33 | 35 |
$$output = $mt->name('inline template')->render($inline, $c); |
34 | 36 |
} |
35 | 37 |
|
... | ... |
@@ -40,24 +42,20 @@ sub _epl { |
40 | 42 |
|
41 | 43 |
# Try template |
42 | 44 |
if (-r $path) { |
43 |
- $c->app->log->debug(qq{Rendering template "$t".}); |
|
44 |
- $$output = $mt->name("template $t")->render_file($path, $c); |
|
45 |
+ $log->debug(qq{Rendering template "$t".}); |
|
46 |
+ $$output = $mt->name(qq{template "$t"})->render_file($path, $c); |
|
45 | 47 |
} |
46 | 48 |
|
47 | 49 |
# Try DATA section |
48 | 50 |
elsif (my $d = $renderer->get_data_template($options)) { |
49 |
- $c->app->log->debug(qq{Rendering template "$t" from DATA section.}); |
|
50 |
- $$output = $mt->name("template $t from DATA section")->render($d, $c); |
|
51 |
+ $log->debug(qq{Rendering template "$t" from DATA section.}); |
|
52 |
+ $$output |
|
53 |
+ = $mt->name(qq{template "$t" from DATA section})->render($d, $c); |
|
51 | 54 |
} |
52 | 55 |
|
53 | 56 |
# No template |
54 |
- else { |
|
55 |
- $c->app->log->debug(qq{Template "$t" not found.}) and return undef; |
|
56 |
- } |
|
57 |
+ else { $log->debug(qq{Template "$t" not found.}) and return undef } |
|
57 | 58 |
} |
58 |
- |
|
59 |
- # Cache |
|
60 |
- $cache->set($key => $mt); |
|
61 | 59 |
} |
62 | 60 |
|
63 | 61 |
# Exception or success |
... | ... |
@@ -76,7 +76,7 @@ L<Mojolicious::Plugin> and implements the following new ones. |
76 | 76 |
|
77 | 77 |
$plugin->register(Mojolicious->new); |
78 | 78 |
|
79 |
-Register condition in L<Mojolicious> application. |
|
79 |
+Register conditions in L<Mojolicious> application. |
|
80 | 80 |
|
81 | 81 |
=head1 SEE ALSO |
82 | 82 |
|
... | ... |
@@ -40,7 +40,7 @@ Mojolicious::Plugin::JSONConfig - JSON configuration plugin |
40 | 40 |
|
41 | 41 |
=head1 SYNOPSIS |
42 | 42 |
|
43 |
- # myapp.json |
|
43 |
+ # myapp.json (it's just JSON with embedded Perl) |
|
44 | 44 |
{ |
45 | 45 |
"foo" : "bar", |
46 | 46 |
"music_dir" : "<%= app->home->rel_dir('music') %>" |
... | ... |
@@ -48,15 +48,18 @@ Mojolicious::Plugin::JSONConfig - JSON configuration plugin |
48 | 48 |
|
49 | 49 |
# Mojolicious |
50 | 50 |
my $config = $self->plugin('JSONConfig'); |
51 |
+ say $config->{foo}; |
|
51 | 52 |
|
52 | 53 |
# Mojolicious::Lite |
53 | 54 |
my $config = plugin 'JSONConfig'; |
55 |
+ say $config->{foo}; |
|
54 | 56 |
|
55 | 57 |
# foo.html.ep |
56 | 58 |
%= $config->{foo} |
57 | 59 |
|
58 | 60 |
# The configuration is available application wide |
59 | 61 |
my $config = app->config; |
62 |
+ say $config->{foo}; |
|
60 | 63 |
|
61 | 64 |
# Everything can be customized with options |
62 | 65 |
my $config = plugin JSONConfig => {file => '/etc/myapp.conf'}; |
... | ... |
@@ -111,7 +114,7 @@ Process content with C<render> and parse it with L<Mojo::JSON>. |
111 | 114 |
my $config = $plugin->register(Mojolicious->new); |
112 | 115 |
my $config = $plugin->register(Mojolicious->new, {file => '/etc/foo.conf'}); |
113 | 116 |
|
114 |
-Register plugin in L<Mojolicious> application. |
|
117 |
+Register plugin in L<Mojolicious> application and merge configuration. |
|
115 | 118 |
|
116 | 119 |
=head2 render |
117 | 120 |
|
... | ... |
@@ -41,13 +41,13 @@ Mojolicious::Plugin::Mount - Application mount plugin |
41 | 41 |
$example->to(message => 'It works great!'); |
42 | 42 |
|
43 | 43 |
# Mount application with host |
44 |
- plugin Mount => {'mojolicio.us' => '/home/sri/myapp.pl'}; |
|
44 |
+ plugin Mount => {'example.com' => '/home/sri/myapp.pl'}; |
|
45 | 45 |
|
46 | 46 |
# Host and path |
47 |
- plugin Mount => {'mojolicio.us/myapp' => '/home/sri/myapp.pl'}; |
|
47 |
+ plugin Mount => {'example.com/myapp' => '/home/sri/myapp.pl'}; |
|
48 | 48 |
|
49 | 49 |
# Or even hosts with wildcard subdomains |
50 |
- plugin Mount => {'*.mojolicio.us/myapp' => '/home/sri/myapp.pl'}; |
|
50 |
+ plugin Mount => {'*.example.com/myapp' => '/home/sri/myapp.pl'}; |
|
51 | 51 |
|
52 | 52 |
=head1 DESCRIPTION |
53 | 53 |
|
... | ... |
@@ -4,7 +4,7 @@ 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 'url_escape'; |
|
7 |
+use Mojo::Util qw'slurp url_escape'; |
|
8 | 8 |
BEGIN {eval {require Pod::Simple::HTML; import Pod::Simple::HTML}} |
9 | 9 |
BEGIN {eval {require Pod::Simple::Search; import Pod::Simple::Search}} |
10 | 10 |
|
... | ... |
@@ -44,10 +44,7 @@ sub _perldoc { |
44 | 44 |
my $path = Pod::Simple::Search->new->find($module, @PATHS); |
45 | 45 |
return $self->redirect_to("http://metacpan.org/module/$module") |
46 | 46 |
unless $path && -r $path; |
47 |
- |
|
48 |
- # Turn POD into HTML |
|
49 |
- open my $file, '<', $path; |
|
50 |
- my $html = _pod_to_html(join '', <$file>); |
|
47 |
+ my $html = _pod_to_html(slurp $path); |
|
51 | 48 |
|
52 | 49 |
# Rewrite links |
53 | 50 |
my $dom = Mojo::DOM->new("$html"); |
... | ... |
@@ -81,7 +78,7 @@ sub _perldoc { |
81 | 78 |
# Anchor and text |
82 | 79 |
my $name = my $text = $e->all_text; |
83 | 80 |
$name =~ s/\s+/_/g; |
84 |
- $name =~ s/\W//g; |
|
81 |
+ $name =~ s/[^\w\-]//g; |
|
85 | 82 |
my $anchor = $name; |
86 | 83 |
my $i = 1; |
87 | 84 |
$anchor = $name . $i++ while $anchors{$anchor}++; |
... | ... |
@@ -206,7 +203,7 @@ L<Mojolicious::Plugin> and implements the following new ones. |
206 | 203 |
my $route = $plugin->register(Mojolicious->new); |
207 | 204 |
my $route = $plugin->register(Mojolicious->new, {name => 'foo'}); |
208 | 205 |
|
209 |
-Register renderer in L<Mojolicious> application. |
|
206 |
+Register renderer and helper in L<Mojolicious> application. |
|
210 | 207 |
|
211 | 208 |
=head1 SEE ALSO |
212 | 209 |
|
... | ... |
@@ -1,7 +1,7 @@ |
1 | 1 |
package Mojolicious::Plugin::TagHelpers; |
2 | 2 |
use Mojo::Base 'Mojolicious::Plugin'; |
3 | 3 |
|
4 |
-use Mojo::ByteStream 'b'; |
|
4 |
+use Mojo::ByteStream; |
|
5 | 5 |
use Mojo::Util 'xml_escape'; |
6 | 6 |
|
7 | 7 |
sub register { |
... | ... |
@@ -13,14 +13,6 @@ sub register { |
13 | 13 |
$app->helper("${name}_field" => sub { _input(@_, type => $name) }); |
14 | 14 |
} |
15 | 15 |
|
16 |
- # DEPRECATED in Rainbow! |
|
17 |
- $app->helper( |
|
18 |
- base_tag => sub { |
|
19 |
- warn "base_tag is DEPRECATED!!!\n"; |
|
20 |
- _tag('base', href => shift->req->url->base, @_); |
|
21 |
- } |
|
22 |
- ); |
|
23 |
- |
|
24 | 16 |
$app->helper(check_box => |
25 | 17 |
sub { _input(shift, shift, value => shift, @_, type => 'checkbox') }); |
26 | 18 |
$app->helper(file_field => |
... | ... |
@@ -220,7 +212,7 @@ sub _tag { |
220 | 212 |
else { $tag .= ' />' } |
221 | 213 |
|
222 | 214 |
# Prevent escaping |
223 |
- return b($tag); |
|
215 |
+ return Mojo::ByteStream->new($tag); |
|
224 | 216 |
} |
225 | 217 |
|
226 | 218 |
sub _text_area { |
... | ... |
@@ -361,7 +353,7 @@ Generate file input element. |
361 | 353 |
%= text_field 'first_name' |
362 | 354 |
%= submit_button |
363 | 355 |
% end |
364 |
- %= form_for 'http://kraih.com/login' => (method => 'POST') => begin |
|
356 |
+ %= form_for 'http://example.com/login' => (method => 'POST') => begin |
|
365 | 357 |
%= text_field 'first_name' |
366 | 358 |
%= submit_button |
367 | 359 |
% end |
... | ... |
@@ -381,7 +373,7 @@ but not C<GET>, a C<method> attribute will be automatically added. |
381 | 373 |
<input name="first_name" /> |
382 | 374 |
<input value="Ok" type="submit" /> |
383 | 375 |
</form> |
384 |
- <form action="http://kraih.com/login" method="POST"> |
|
376 |
+ <form action="http://example.com/login" method="POST"> |
|
385 | 377 |
<input name="first_name" /> |
386 | 378 |
<input value="Ok" type="submit" /> |
387 | 379 |
</form> |
... | ... |
@@ -440,6 +432,7 @@ Generate portable script tag for C<Javascript> asset. |
440 | 432 |
%= link_to index => {format => 'txt'} => (class => 'links') => begin |
441 | 433 |
Home |
442 | 434 |
% end |
435 |
+ %= link_to Contact => Mojo::URL->new('mailto:sri@example.com') |
|
443 | 436 |
<%= link_to index => begin %>Home<% end %> |
444 | 437 |
<%= link_to '/path/to/file' => begin %>File<% end %> |
445 | 438 |
<%= link_to 'http://mojolicio.us' => begin %>Mojolicious<% end %> |
... | ... |
@@ -453,6 +446,7 @@ capitalized link target as content. |
453 | 446 |
<a class="links" href="/path/to/index.txt"> |
454 | 447 |
Home |
455 | 448 |
</a> |
449 |
+ <a href="mailto:sri@example.com">Contact</a> |
|
456 | 450 |
<a href="/path/to/index">Home</a> |
457 | 451 |
<a href="/path/to/file">File</a> |
458 | 452 |
<a href="http://mojolicio.us">Mojolicious</a> |
... | ... |
@@ -101,8 +101,7 @@ Renderer for plain embedded Perl templates, loaded automatically. |
101 | 101 |
|
102 | 102 |
=item L<Mojolicious::Plugin::EPRenderer> |
103 | 103 |
|
104 |
-Renderer for more sophisiticated embedded Perl templates, loaded |
|
105 |
-automatically. |
|
104 |
+Renderer for more sophisticated embedded Perl templates, loaded automatically. |
|
106 | 105 |
|
107 | 106 |
=item L<Mojolicious::Plugin::HeaderCondition> |
108 | 107 |
|
... | ... |
@@ -121,14 +120,6 @@ Mount whole L<Mojolicious> applications. |
121 | 120 |
Renderer for turning POD into HTML and documentation browser for |
122 | 121 |
L<Mojolicious::Guides>. |
123 | 122 |
|
124 |
-=item L<Mojolicious::Plugin::PoweredBy> |
|
125 |
- |
|
126 |
-Add an C<X-Powered-By> header to outgoing responses, loaded automatically. |
|
127 |
- |
|
128 |
-=item L<Mojolicious::Plugin::RequestTimer> |
|
129 |
- |
|
130 |
-Log timing information, loaded automatically. |
|
131 |
- |
|
132 | 123 |
=item L<Mojolicious::Plugin::TagHelpers> |
133 | 124 |
|
134 | 125 |
Template specific helper collection, loaded automatically. |
... | ... |
@@ -6,7 +6,7 @@ use Mojo::Cache; |
6 | 6 |
use Mojo::JSON; |
7 | 7 |
use Mojo::Home; |
8 | 8 |
use Mojo::Loader; |
9 |
-use Mojo::Util qw(encode slurp); |
|
9 |
+use Mojo::Util qw(decamelize encode slurp); |
|
10 | 10 |
|
11 | 11 |
has cache => sub { Mojo::Cache->new }; |
12 | 12 |
has classes => sub { ['main'] }; |
... | ... |
@@ -24,13 +24,10 @@ my %TEMPLATES = map { $_ => slurp $HOME->rel_file($_) } @{$HOME->list_files}; |
24 | 24 |
|
25 | 25 |
sub new { |
26 | 26 |
my $self = shift->SUPER::new(@_); |
27 |
- |
|
28 |
- $self->add_handler( |
|
29 |
- json => sub { ${$_[2]} = Mojo::JSON->new->encode($_[3]->{json}) }); |
|
30 |
- $self->add_handler(data => sub { ${$_[2]} = $_[3]->{data} }); |
|
31 |
- $self->add_handler(text => sub { ${$_[2]} = $_[3]->{text} }); |
|
32 |
- |
|
33 |
- return $self; |
|
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}) }); |
|
34 | 31 |
} |
35 | 32 |
|
36 | 33 |
sub add_handler { shift->_add(handlers => @_) } |
... | ... |
@@ -66,69 +63,56 @@ sub render { |
66 | 63 |
# Merge stash and arguments |
67 | 64 |
@{$stash}{keys %$args} = values %$args; |
68 | 65 |
|
69 |
- # Extract important stash values |
|
70 | 66 |
my $options = { |
71 | 67 |
encoding => $self->encoding, |
72 | 68 |
handler => $stash->{handler}, |
73 | 69 |
template => delete $stash->{template} |
74 | 70 |
}; |
75 |
- my $data = delete $stash->{data}; |
|
76 |
- my $format = $options->{format} = $stash->{format} || $self->default_format; |
|
77 | 71 |
my $inline = $options->{inline} = delete $stash->{inline}; |
78 |
- my $json = delete $stash->{json}; |
|
79 |
- my $text = delete $stash->{text}; |
|
80 | 72 |
$options->{handler} = defined $options->{handler} ? $options->{handler} : $self->default_handler if defined $inline; |
81 |
- |
|
82 |
- # Text |
|
83 |
- my $output; |
|
84 |
- my $content = $stash->{'mojo.content'} ||= {}; |
|
85 |
- if (defined $text) { |
|
86 |
- $self->handlers->{text}->($self, $c, \$output, {text => $text}); |
|
87 |
- $content->{content} = $output |
|
88 |
- if ($c->stash->{extends} || $c->stash->{layout}); |
|
89 |
- } |
|
73 |
+ $options->{format} = $stash->{format} || $self->default_format; |
|
90 | 74 |
|
91 | 75 |
# Data |
92 |
- elsif (defined $data) { |
|
76 |
+ my $output; |
|
77 |
+ if (defined(my $data = delete $stash->{data})) { |
|
93 | 78 |
$self->handlers->{data}->($self, $c, \$output, {data => $data}); |
94 |
- $content->{content} = $output |
|
95 |
- if ($c->stash->{extends} || $c->stash->{layout}); |
|
79 |
+ return $output, $options->{format}; |
|
96 | 80 |
} |
97 | 81 |
|
98 | 82 |
# JSON |
99 |
- elsif (defined $json) { |
|
83 |
+ elsif (my $json = delete $stash->{json}) { |
|
100 | 84 |
$self->handlers->{json}->($self, $c, \$output, {json => $json}); |
101 |
- $format = 'json'; |
|
102 |
- $content->{content} = $output |
|
103 |
- if ($c->stash->{extends} || $c->stash->{layout}); |
|
85 |
+ return $output, 'json'; |
|
86 |
+ } |
|
87 |
+ |
|
88 |
+ # Text |
|
89 |
+ elsif (defined(my $text = delete $stash->{text})) { |
|
90 |
+ $self->handlers->{text}->($self, $c, \$output, {text => $text}); |
|
104 | 91 |
} |
105 | 92 |
|
106 | 93 |
# Template or templateless handler |
107 | 94 |
else { |
108 |
- return undef unless $self->_render_template($c, \$output, $options); |
|
109 |
- $content->{content} = $output |
|
110 |
- if ($c->stash->{extends} || $c->stash->{layout}); |
|
95 |
+ $options->{template} ||= $self->_generate_template($c); |
|
96 |
+ return unless $self->_render_template($c, \$output, $options); |
|
111 | 97 |
} |
112 | 98 |
|
113 |
- # Extendable content |
|
114 |
- if (!$json && !defined $data) { |
|
115 |
- |
|
116 |
- # Extends |
|
117 |
- while ((my $extends = $self->_extends($c)) && !defined $inline) { |
|
118 |
- $options->{handler} = $stash->{handler}; |
|
119 |
- $options->{format} = $stash->{format} || $self->default_format; |
|
120 |
- $options->{template} = $extends; |
|
121 |
- $self->_render_template($c, \$output, $options); |
|
122 |
- $content->{content} = $output |
|
123 |
- if $content->{content} !~ /\S/ && $output =~ /\S/; |
|
124 |
- } |
|
125 |
- |
|
126 |
- # Encoding |
|
127 |
- $output = encode $options->{encoding}, $output |
|
128 |
- if !$partial && $options->{encoding} && $output; |
|
99 |
+ # Extends |
|
100 |
+ my $content = $stash->{'mojo.content'} ||= {}; |
|
101 |
+ local $content->{content} = $output if $stash->{extends} || $stash->{layout}; |
|
102 |
+ while ((my $extends = $self->_extends($stash)) && !defined $inline) { |
|
103 |
+ $options->{handler} = $stash->{handler}; |
|
104 |
+ $options->{format} = $stash->{format} || $self->default_format; |
|
105 |
+ $options->{template} = $extends; |
|
106 |
+ $self->_render_template($c, \$output, $options); |
|
107 |
+ $content->{content} = $output |
|
108 |
+ if $content->{content} !~ /\S/ && $output =~ /\S/; |
|
129 | 109 |
} |
130 | 110 |
|
131 |
- return $output, $format; |
|
111 |
+ # Encoding |
|
112 |
+ $output = encode $options->{encoding}, $output |
|
113 |
+ if !$partial && $options->{encoding} && $output; |
|
114 |
+ |
|
115 |
+ return $output, $options->{format}; |
|
132 | 116 |
} |
133 | 117 |
|
134 | 118 |
sub template_name { |
... | ... |
@@ -187,13 +171,27 @@ sub _detect_handler { |
187 | 171 |
} |
188 | 172 |
|
189 | 173 |
sub _extends { |
190 |
- my ($self, $c) = @_; |
|
191 |
- my $stash = $c->stash; |
|
174 |
+ my ($self, $stash) = @_; |
|
192 | 175 |
my $layout = delete $stash->{layout}; |
193 | 176 |
$stash->{extends} ||= join('/', 'layouts', $layout) if $layout; |
194 | 177 |
return delete $stash->{extends}; |
195 | 178 |
} |
196 | 179 |
|
180 |
+sub _generate_template { |
|
181 |
+ my ($self, $c) = @_; |
|
182 |
+ |
|
183 |
+ # Normal default template |
|
184 |
+ my $stash = $c->stash; |
|
185 |
+ my $controller = $stash->{controller}; |
|
186 |
+ my $action = $stash->{action}; |
|
187 |
+ return join '/', split(/-/, decamelize($controller)), $action |
|
188 |
+ if $controller && $action; |
|
189 |
+ |
|
190 |
+ # Try the route name if we don't have controller and action |
|
191 |
+ return undef unless my $endpoint = $c->match->endpoint; |
|
192 |
+ return $endpoint->name; |
|
193 |
+} |
|
194 |
+ |
|
197 | 195 |
sub _render_template { |
198 | 196 |
my ($self, $c, $output, $options) = @_; |
199 | 197 |
|
... | ... |
@@ -20,57 +20,59 @@ sub add_shortcut { shift->_add(shortcuts => @_) } |
20 | 20 |
sub auto_render { |
21 | 21 |
my ($self, $c) = @_; |
22 | 22 |
my $stash = $c->stash; |
23 |
- return undef if $stash->{'mojo.rendered'} || $c->tx->is_websocket; |
|
24 |
- $c->render or ($stash->{'mojo.routed'} or $c->render_not_found); |
|
23 |
+ return if $stash->{'mojo.rendered'}; |
|
24 |
+ $c->render_maybe or $stash->{'mojo.routed'} or $c->render_not_found; |
|
25 | 25 |
} |
26 | 26 |
|
27 | 27 |
sub dispatch { |
28 | 28 |
my ($self, $c) = @_; |
29 | 29 |
|
30 |
- # Prepare path |
|
30 |
+ # Path (partial path gets priority) |
|
31 | 31 |
my $req = $c->req; |
32 | 32 |
my $path = $c->stash->{path}; |
33 | 33 |
if (defined $path) { $path = "/$path" if $path !~ m!^/! } |
34 | 34 |
else { $path = $req->url->path->to_route } |
35 | 35 |
|
36 |
- # Prepare match |
|
37 |
- my $method = $req->method; |
|
38 |
- my $websocket = $c->tx->is_websocket ? 1 : 0; |
|
39 |
- my $m = Mojolicious::Routes::Match->new($method => $path, $websocket); |
|
40 |
- $c->match($m); |
|
36 |
+ # Method (HEAD will be treated as GET) |
|
37 |
+ my $method = uc $req->method; |
|
38 |
+ $method = 'GET' if $method eq 'HEAD'; |
|
41 | 39 |
|
42 | 40 |
# Check cache |
43 | 41 |
my $cache = $self->cache; |
44 |
- if ($cache && (my $cached = $cache->get("$method:$path:$websocket"))) { |
|
45 |
- $m->root($self)->endpoint($cached->{endpoint}); |
|
46 |
- $m->stack($cached->{stack})->captures($cached->{captures}); |
|
42 |
+ my $ws = $c->tx->is_websocket ? 1 : 0; |
|
43 |
+ my $match = Mojolicious::Routes::Match->new(root => $self); |
|
44 |
+ $c->match($match); |
|
45 |
+ if ($cache && (my $cached = $cache->get("$method:$path:$ws"))) { |
|
46 |
+ $match->endpoint($cached->{endpoint})->stack($cached->{stack}); |
|
47 | 47 |
} |
48 | 48 |
|
49 | 49 |
# Check routes |
50 | 50 |
else { |
51 |
- $m->match($self, $c); |
|
51 |
+ my $options = {method => $method, path => $path, websocket => $ws}; |
|
52 |
+ $match->match($c => $options); |
|
52 | 53 |
|
53 | 54 |
# Cache routes without conditions |
54 |
- if ($cache && (my $endpoint = $m->endpoint)) { |
|
55 |
- $cache->set( |
|
56 |
- "$method:$path:$websocket" => { |
|
57 |
- endpoint => $endpoint, |
|
58 |
- stack => $m->stack, |
|
59 |
- captures => $m->captures |
|
60 |
- } |
|
61 |
- ) unless $endpoint->has_conditions; |
|
55 |
+ if ($cache && (my $endpoint = $match->endpoint)) { |
|
56 |
+ my $result = {endpoint => $endpoint, stack => $match->stack}; |
|
57 |
+ $cache->set("$method:$path:$ws" => $result) |
|
58 |
+ unless $endpoint->has_conditions; |
|
62 | 59 |
} |
63 | 60 |
} |
64 | 61 |
|
65 | 62 |
# Dispatch |
66 |
- return undef unless $m && @{$m->stack}; |
|
67 |
- return undef if $self->_walk($c); |
|
63 |
+ return undef unless $self->_walk($c); |
|
68 | 64 |
$self->auto_render($c); |
69 | 65 |
return 1; |
70 | 66 |
} |
71 | 67 |
|
72 | 68 |
sub hide { push @{shift->hidden}, @_ } |
73 | 69 |
|
70 |
+sub is_hidden { |
|
71 |
+ my ($self, $method) = @_; |
|
72 |
+ my $hiding = $self->{hiding} ||= {map { $_ => 1 } @{$self->hidden}}; |
|
73 |
+ return !!($hiding->{$method} || index($method, '_') == 0); |
|
74 |
+} |
|
75 |
+ |
|
74 | 76 |
sub lookup { |
75 | 77 |
my ($self, $name) = @_; |
76 | 78 |
my $reverse = $self->{reverse} ||= {}; |
... | ... |
@@ -79,18 +81,6 @@ sub lookup { |
79 | 81 |
return $reverse->{$name} = $route; |
80 | 82 |
} |
81 | 83 |
|
82 |
-# DEPRECATED in Rainbow! |
|
83 |
-sub namespace { |
|
84 |
- warn <<EOF; |
|
85 |
-Mojolicious::Routes->namespace is DEPRECATED in favor of |
|
86 |
-Mojolicious::Routes->namespaces! |
|
87 |
-EOF |
|
88 |
- my $self = shift; |
|
89 |
- return $self->namespaces->[0] unless @_; |
|
90 |
- $self->namespaces->[0] = shift; |
|
91 |
- return $self; |
|
92 |
-} |
|
93 |
- |
|
94 | 84 |
sub route { |
95 | 85 |
shift->add_child(Mojolicious::Routes::Route->new(@_))->children->[-1]; |
96 | 86 |
} |
... | ... |
@@ -102,11 +92,11 @@ sub _add { |
102 | 92 |
} |
103 | 93 |
|
104 | 94 |
sub _callback { |
105 |
- my ($self, $c, $field, $staging) = @_; |
|
95 |
+ my ($self, $c, $field, $nested) = @_; |
|
106 | 96 |
$c->stash->{'mojo.routed'}++; |
107 | 97 |
$c->app->log->debug('Routing to a callback.'); |
108 | 98 |
my $continue = $field->{cb}->($c); |
109 |
- return !$staging || $continue ? 1 : undef; |
|
99 |
+ return !$nested || $continue ? 1 : undef; |
|
110 | 100 |
} |
111 | 101 |
|
112 | 102 |
sub _class { |
... | ... |
@@ -117,7 +107,7 @@ sub _class { |
117 | 107 |
|
118 | 108 |
# Application class |
119 | 109 |
my @classes; |
120 |
- my $class = camelize $field->{controller} || ''; |
|
110 |
+ my $class = $field->{controller} ? camelize($field->{controller}) : ''; |
|
121 | 111 |
if ($field->{app}) { push @classes, $field->{app} } |
122 | 112 |
|
123 | 113 |
# Specific namespace |
... | ... |
@@ -150,7 +140,7 @@ sub _class { |
150 | 140 |
} |
151 | 141 |
|
152 | 142 |
sub _controller { |
153 |
- my ($self, $c, $field, $staging) = @_; |
|
143 |
+ my ($self, $c, $field, $nested) = @_; |
|
154 | 144 |
|
155 | 145 |
# Load and instantiate controller/application |
156 | 146 |
my $app; |
... | ... |
@@ -178,7 +168,7 @@ sub _controller { |
178 | 168 |
|
179 | 169 |
# Try to call action |
180 | 170 |
if (my $sub = $app->can($method)) { |
181 |
- $c->stash->{'mojo.routed'}++ unless $staging; |
|
171 |
+ $c->stash->{'mojo.routed'}++ unless $nested; |
|
182 | 172 |
$continue = $app->$sub; |
183 | 173 |
} |
184 | 174 |
|
... | ... |
@@ -186,7 +176,7 @@ sub _controller { |
186 | 176 |
else { $log->debug('Action not found in controller.') } |
187 | 177 |
} |
188 | 178 |
|
189 |
- return !$staging || $continue ? 1 : undef; |
|
179 |
+ return !$nested || $continue ? 1 : undef; |
|
190 | 180 |
} |
191 | 181 |
|
192 | 182 |
sub _load { |
... | ... |
@@ -205,10 +195,9 @@ sub _method { |
205 | 195 |
my ($self, $c, $field) = @_; |
206 | 196 |
|
207 | 197 |
# Hidden |
208 |
- $self->{hiding} = {map { $_ => 1 } @{$self->hidden}} unless $self->{hiding}; |
|
209 | 198 |
return undef unless my $method = $field->{action}; |
210 | 199 |
$c->app->log->debug(qq{Action "$method" is not allowed.}) and return undef |
211 |
- if $self->{hiding}{$method} || index($method, '_') == 0; |
|
200 |
+ if $self->is_hidden($method); |
|
212 | 201 |
|
213 | 202 |
# Invalid |
214 | 203 |
$c->app->log->debug(qq{Action "$method" is invalid.}) and return undef |
... | ... |
@@ -220,28 +209,28 @@ sub _method { |
220 | 209 |
sub _walk { |
221 | 210 |
my ($self, $c) = @_; |
222 | 211 |
|
223 |
- my $stack = $c->match->stack; |
|
224 |
- my $stash = $c->stash; |
|
225 |
- my $staging = @$stack; |
|
212 |
+ my $stack = $c->match->stack; |
|
213 |
+ return undef unless my $nested = @$stack; |
|
214 |
+ my $stash = $c->stash; |
|
226 | 215 |
$stash->{'mojo.captures'} ||= {}; |
227 | 216 |
for my $field (@$stack) { |
228 |
- $staging--; |
|
217 |
+ $nested--; |
|
229 | 218 |
|
230 |
- # Merge in captures |
|
219 |
+ # Merge captures into stash |
|
231 | 220 |
my @keys = keys %$field; |
232 | 221 |
@{$stash}{@keys} = @{$stash->{'mojo.captures'}}{@keys} = values %$field; |
233 | 222 |
|
234 | 223 |
# Dispatch |
235 | 224 |
my $continue |
236 | 225 |
= $field->{cb} |
237 |
- ? $self->_callback($c, $field, $staging) |
|
238 |
- : $self->_controller($c, $field, $staging); |
|
226 |
+ ? $self->_callback($c, $field, $nested) |
|
227 |
+ : $self->_controller($c, $field, $nested); |
|
239 | 228 |
|
240 | 229 |
# Break the chain |
241 |
- return 1 if $staging && !$continue; |
|
230 |
+ return undef if $nested && !$continue; |
|
242 | 231 |
} |
243 | 232 |
|
244 |
- return undef; |
|
233 |
+ return 1; |
|
245 | 234 |
} |
246 | 235 |
|
247 | 236 |
1; |
... | ... |
@@ -254,38 +243,15 @@ Mojolicious::Routes - Always find your destination with routes! |
254 | 243 |
|
255 | 244 |
use Mojolicious::Routes; |
256 | 245 |
|
257 |
- # New route tree |
|
246 |
+ # Simple route |
|
258 | 247 |
my $r = Mojolicious::Routes->new; |
248 |
+ $r->route('/')->to(controller => 'blog', action => 'welcome'); |
|
259 | 249 |
|
260 |
- # Normal route matching "/articles" with parameters "controller" and |
|
261 |
- # "action" |
|
262 |
- $r->route('/articles')->to(controller => 'article', action => 'list'); |
|
263 |
- |
|
264 |
- # Route with a placeholder matching everything but "/" and "." |
|
265 |
- $r->route('/:controller')->to(action => 'list'); |
|
266 |
- |
|
267 |
- # Route with a placeholder and regex constraint |
|
268 |
- $r->route('/articles/:id', id => qr/\d+/) |
|
269 |
- ->to(controller => 'article', action => 'view'); |
|
270 |
- |
|
271 |
- # Route with an optional parameter "year" |
|
272 |
- $r->route('/archive/:year') |
|
273 |
- ->to(controller => 'archive', action => 'list', year => undef); |
|
274 |
- |
|
275 |
- # Nested route for two actions sharing the same "controller" parameter |
|
276 |
- my $books = $r->route('/books/:id')->to(controller => 'book'); |
|
277 |
- $books->route('/edit')->to(action => 'edit'); |
|
278 |
- $books->route('/delete')->to(action => 'delete'); |
|
279 |
- |
|
280 |
- # Bridges can be used to chain multiple routes |
|
281 |
- $r->bridge->to(controller => 'foo', action =>'auth') |
|
282 |
- ->route('/blog')->to(action => 'list'); |
|
283 |
- |
|
284 |
- # Simplified Mojolicious::Lite style route generation is also possible |
|
285 |
- $r->get('/')->to(controller => 'blog', action => 'welcome'); |
|
250 |
+ # More advanced routes |
|
286 | 251 |
my $blog = $r->under('/blog'); |
287 |
- $blog->post('/list')->to('blog#list'); |
|
288 |
- $blog->get(sub { shift->render(text => 'Go away!') }); |
|
252 |
+ $blog->get('/list')->to('blog#list'); |
|
253 |
+ $blog->get('/:id' => [id => qr/\d+/])->to('blog#show', id => 23); |
|
254 |
+ $blog->patch(sub { shift->render(text => 'Go away!', status => 405) }); |
|
289 | 255 |
|
290 | 256 |
=head1 DESCRIPTION |
291 | 257 |
|
... | ... |
@@ -328,7 +294,7 @@ Contains all available conditions. |
328 | 294 |
my $hidden = $r->hidden; |
329 | 295 |
$r = $r->hidden([qw(attr has new)]); |
330 | 296 |
|
331 |
-Controller methods and attributes that are hidden from routes, defaults to |
|
297 |
+Controller methods and attributes that are hidden from router, defaults to |
|
332 | 298 |
C<attr>, C<has>, C<new> and C<tap>. |
333 | 299 |
|
334 | 300 |
=head2 namespaces |
... | ... |
@@ -381,7 +347,13 @@ Match routes with L<Mojolicious::Routes::Match> and dispatch. |
381 | 347 |
|
382 | 348 |
$r = $r->hide(qw(foo bar)); |
383 | 349 |
|
384 |
-Hide controller methods and attributes from routes. |
|
350 |
+Hide controller methods and attributes from router. |
|
351 |
+ |
|
352 |
+=head2 is_hidden |
|
353 |
+ |
|
354 |
+ my $success = $r->is_hidden('foo'); |
|
355 |
+ |
|
356 |
+Check if controller method or attribute is hidden from router. |
|
385 | 357 |
|
386 | 358 |
=head2 lookup |
387 | 359 |
|
... | ... |
@@ -1,34 +1,50 @@ |
1 | 1 |
package Mojolicious::Routes::Match; |
2 | 2 |
use Mojo::Base -base; |
3 | 3 |
|
4 |
-has captures => sub { {} }; |
|
5 | 4 |
has [qw(endpoint root)]; |
6 | 5 |
has stack => sub { [] }; |
7 | 6 |
|
8 |
-sub new { |
|
9 |
- my $self = shift->SUPER::new; |
|
10 |
- $self->{method} = uc shift; |
|
11 |
- $self->{path} = shift; |
|
12 |
- $self->{websocket} = shift; |
|
13 |
- return $self; |
|
7 |
+sub match { $_[0]->_match($_[0]->root, $_[1], $_[2]) } |
|
8 |
+ |
|
9 |
+sub path_for { |
|
10 |
+ my ($self, $name, %values) = (shift, _values(@_)); |
|
11 |
+ |
|
12 |
+ # Current route |
|
13 |
+ my $endpoint; |
|
14 |
+ if ($name && $name eq 'current' || !$name) { |
|
15 |
+ return unless $endpoint = $self->endpoint; |
|
16 |
+ } |
|
17 |
+ |
|
18 |
+ # Find endpoint |
|
19 |
+ else { return $name unless $endpoint = $self->root->lookup($name) } |
|
20 |
+ |
|
21 |
+ # Merge values (clear format) |
|
22 |
+ my $captures = $self->stack->[-1] || {}; |
|
23 |
+ %values = (%$captures, format => undef, %values); |
|
24 |
+ my $pattern = $endpoint->pattern; |
|
25 |
+ $values{format} |
|
26 |
+ = defined $captures->{format} |
|
27 |
+ ? $captures->{format} |
|
28 |
+ : $pattern->defaults->{format} |
|
29 |
+ if $pattern->constraints->{format}; |
|
30 |
+ |
|
31 |
+ my $path = $endpoint->render('', \%values); |
|
32 |
+ return wantarray ? ($path, $endpoint->has_websocket) : $path; |
|
14 | 33 |
} |
15 | 34 |
|
16 |
-sub match { |
|
17 |
- my ($self, $r, $c) = @_; |
|
35 |
+sub _match { |
|
36 |
+ my ($self, $r, $c, $options) = @_; |
|
18 | 37 |
|
19 | 38 |
# Pattern |
20 |
- $self->root($r) unless $self->root; |
|
21 |
- my $path = $self->{path}; |
|
22 |
- my $pattern = $r->pattern; |
|
23 |
- return unless my $captures = $pattern->shape_match(\$path, $r->is_endpoint); |
|
24 |
- $self->{path} = $path; |
|
25 |
- $captures = {%{$self->captures}, %$captures}; |
|
39 |
+ my $path = $options->{path}; |
|
40 |
+ return |
|
41 |
+ unless my $captures = $r->pattern->match_partial(\$path, $r->is_endpoint); |
|
42 |
+ local $options->{path} = $path; |
|
43 |
+ $captures = $self->{captures} = {%{$self->{captures} || {}}, %$captures}; |
|
26 | 44 |
|
27 | 45 |
# Method |
28 |
- if (my $methods = $r->via) { |
|
29 |
- my $method = $self->{method} eq 'HEAD' ? 'GET' : $self->{method}; |
|
30 |
- return unless grep { $_ eq $method } @$methods; |
|
31 |
- } |
|
46 |
+ my $methods = $r->via; |
|
47 |
+ return if $methods && !grep { $_ eq $options->{method} } @$methods; |
|
32 | 48 |
|
33 | 49 |
# Conditions |
34 | 50 |
if (my $over = $r->over) { |
... | ... |
@@ -40,7 +56,7 @@ sub match { |
40 | 56 |
} |
41 | 57 |
|
42 | 58 |
# WebSocket |
43 |
- return if $r->is_websocket && !$self->{websocket}; |
|
59 |
+ return if $r->is_websocket && !$options->{websocket}; |
|
44 | 60 |
|
45 | 61 |
# Partial |
46 | 62 |
my $empty = !length $path || $path eq '/'; |
... | ... |
@@ -50,93 +66,45 @@ sub match { |
50 | 66 |
$empty = 1; |
51 | 67 |
} |
52 | 68 |
|
53 |
- # Update stack |
|
54 |
- $self->captures($captures); |
|
69 |
+ # Endpoint (or bridge) |
|
55 | 70 |
my $endpoint = $r->is_endpoint; |
56 |
- if ($r->inline || ($endpoint && $empty)) { |
|
71 |
+ if (($endpoint && $empty) || $r->inline) { |
|
57 | 72 |
push @{$self->stack}, {%$captures}; |
73 |
+ return $self->endpoint($r) if $endpoint && $empty; |
|
58 | 74 |
delete $captures->{$_} for qw(app cb); |
59 | 75 |
} |
60 | 76 |
|
61 |
- # Endpoint |
|
62 |
- return $self->endpoint($r) if $endpoint && $empty; |
|
63 |
- |
|
64 | 77 |
# Match children |
65 | 78 |
my $snapshot = [@{$self->stack}]; |
66 | 79 |
for my $child (@{$r->children}) { |
67 |
- $self->match($child, $c); |
|
80 |
+ $self->_match($child, $c, $options); |
|
68 | 81 |
|
69 | 82 |
# Endpoint found |
70 | 83 |
return if $self->endpoint; |
71 | 84 |
|
72 | 85 |
# Reset |
73 |
- $self->{path} = $path; |
|
74 |
- if ($r->parent) { $self->captures($captures)->stack([@$snapshot]) } |
|
75 |
- else { $self->captures({})->stack([]) } |
|
86 |
+ if ($r->parent) { $self->stack([@$snapshot])->{captures} = $captures } |
|
87 |
+ else { $self->stack([])->{captures} = {} } |
|
76 | 88 |
} |
77 | 89 |
} |
78 | 90 |
|
79 |
-sub path_for { |
|
80 |
- my $self = shift; |
|
81 |
- |
|
82 |
- # Single argument |
|
83 |
- my (%values, $name); |
|
84 |
- if (@_ == 1) { |
|
85 |
- |
|
86 |
- # Hash |
|
87 |
- %values = %{shift()} if ref $_[0] eq 'HASH'; |
|
88 |
- |
|
89 |
- # Name |
|
90 |
- $name = $_[0] if $_[0]; |
|
91 |
- } |
|
92 |
- |
|
93 |
- # Multiple arguments |
|
94 |
- elsif (@_ > 1) { |
|
95 |
- |
|
96 |
- # Odd |
|
97 |
- if (@_ % 2) { ($name, %values) = (shift, @_) } |
|
98 |
- |
|
99 |
- # Even |
|
100 |
- else { |
|
91 |
+sub _values { |
|
101 | 92 |
|
102 |
- # Name and hash |
|
103 |
- if (ref $_[1] eq 'HASH') { ($name, %values) = (shift, %{shift()}) } |
|
93 |
+ # Hash or name (one) |
|
94 |
+ return ref $_[0] eq 'HASH' ? (undef, %{shift()}) : @_ if @_ == 1; |
|
104 | 95 |
|
105 |
- # Just values |
|
106 |
- else { %values = @_ } |
|
96 |
+ # Name and values (odd) |
|
97 |
+ return shift, @_ if @_ % 2; |
|
107 | 98 |
|
108 |
- } |
|
109 |
- } |
|
110 |
- |
|
111 |
- # Current route |
|
112 |
- my $endpoint; |
|
113 |
- if ($name && $name eq 'current' || !$name) { |
|
114 |
- return unless $endpoint = $self->endpoint; |
|
115 |
- } |
|
116 |
- |
|
117 |
- # Find endpoint |
|
118 |
- else { return $name unless $endpoint = $self->root->lookup($name) } |
|
119 |
- |
|
120 |
- # Merge values |
|
121 |
- my $captures = $self->captures; |
|
122 |
- %values = (%$captures, format => undef, %values); |
|
123 |
- my $pattern = $endpoint->pattern; |
|
124 |
- $values{format} |
|
125 |
- = defined $captures->{format} |
|
126 |
- ? $captures->{format} |
|
127 |
- : $pattern->defaults->{format} |
|
128 |
- if $pattern->constraints->{format}; |
|
129 |
- |
|
130 |
- # Render |
|
131 |
- my $path = $endpoint->render('', \%values); |
|
132 |
- return wantarray ? ($path, $endpoint->has_websocket) : $path; |
|
99 |
+ # Name and hash or just values (even) |
|
100 |
+ return ref $_[1] eq 'HASH' ? (shift, %{shift()}) : (undef, @_); |
|
133 | 101 |
} |
134 | 102 |
|
135 | 103 |
1; |
136 | 104 |
|
137 | 105 |
=head1 NAME |
138 | 106 |
|
139 |
-Mojolicious::Routes::Match - Routes visitor |
|
107 |
+Mojolicious::Routes::Match - Find routes |
|
140 | 108 |
|
141 | 109 |
=head1 SYNOPSIS |
142 | 110 |
|
... | ... |
@@ -146,49 +114,47 @@ Mojolicious::Routes::Match - Routes visitor |
146 | 114 |
|
147 | 115 |
# Routes |
148 | 116 |
my $r = Mojolicious::Routes->new; |
149 |
- $r->get('/foo')->to(action => 'foo'); |
|
150 |
- $r->put('/bar')->to(action => 'bar'); |
|
117 |
+ $r->get('/:controller/:action'); |
|
118 |
+ $r->put('/:controller/:action'); |
|
151 | 119 |
|
152 | 120 |
# Match |
153 | 121 |
my $c = Mojolicious::Controller->new; |
154 |
- my $m = Mojolicious::Routes::Match->new(PUT => '/bar'); |
|
155 |
- $m->match($r, $c); |
|
156 |
- say $m->captures->{action}; |
|
122 |
+ my $match = Mojolicious::Routes::Match->new(root => $r); |
|
123 |
+ $match->match($c => {method => 'PUT', path => '/foo/bar'}); |
|
124 |
+ say $match->stack->[0]{controller}; |
|
125 |
+ say $match->stack->[0]{action}; |
|
126 |
+ |
|
127 |
+ # Render |
|
128 |
+ say $match->path_for; |
|
129 |
+ say $match->path_for(action => 'baz'); |
|
157 | 130 |
|
158 | 131 |
=head1 DESCRIPTION |
159 | 132 |
|
160 |
-L<Mojolicious::Routes::Match> is a visitor for L<Mojolicious::Routes> |
|
133 |
+L<Mojolicious::Routes::Match> finds routes in L<Mojolicious::Routes> |
|
161 | 134 |
structures. |
162 | 135 |
|
163 | 136 |
=head1 ATTRIBUTES |
164 | 137 |
|
165 | 138 |
L<Mojolicious::Routes::Match> implements the following attributes. |
166 | 139 |
|
167 |
-=head2 captures |
|
168 |
- |
|
169 |
- my $captures = $m->captures; |
|
170 |
- $m = $m->captures({foo => 'bar'}); |
|
171 |
- |
|
172 |
-Captured parameters. |
|
173 |
- |
|
174 | 140 |
=head2 endpoint |
175 | 141 |
|
176 |
- my $endpoint = $m->endpoint; |
|
177 |
- $m = $m->endpoint(Mojolicious::Routes->new); |
|
142 |
+ my $endpoint = $match->endpoint; |
|
143 |
+ $match = $match->endpoint(Mojolicious::Routes::Route->new); |
|
178 | 144 |
|
179 |
-The route endpoint that actually matched. |
|
145 |
+The route endpoint that matched. |
|
180 | 146 |
|
181 | 147 |
=head2 root |
182 | 148 |
|
183 |
- my $root = $m->root; |
|
184 |
- $m = $m->root($routes); |
|
149 |
+ my $root = $match->root; |
|
150 |
+ $match = $match->root(Mojolicious::Routes->new); |
|
185 | 151 |
|
186 |
-The root of the route tree. |
|
152 |
+The root of the route structure. |
|
187 | 153 |
|
188 | 154 |
=head2 stack |
189 | 155 |
|
190 |
- my $stack = $m->stack; |
|
191 |
- $m = $m->stack([{foo => 'bar'}]); |
|
156 |
+ my $stack = $match->stack; |
|
157 |
+ $match = $match->stack([{foo => 'bar'}]); |
|
192 | 158 |
|
193 | 159 |
Captured parameters with nesting history. |
194 | 160 |
|
... | ... |
@@ -197,33 +163,26 @@ Captured parameters with nesting history. |
197 | 163 |
L<Mojolicious::Routes::Match> inherits all methods from L<Mojo::Base> and |
198 | 164 |
implements the following new ones. |
199 | 165 |
|
200 |
-=head2 new |
|
201 |
- |
|
202 |
- my $m = Mojolicious::Routes::Match->new(GET => '/foo'); |
|
203 |
- my $m = Mojolicious::Routes::Match->new(GET => '/foo', $ws); |
|
204 |
- |
|
205 |
-Construct a new L<Mojolicious::Routes::Match> object. |
|
206 |
- |
|
207 | 166 |
=head2 match |
208 | 167 |
|
209 |
- $m->match(Mojolicious::Routes->new, Mojolicious::Controller->new); |
|
168 |
+ $match->match(Mojolicious::Controller->new, {method => 'GET', path => '/'}); |
|
210 | 169 |
|
211 |
-Match against a route tree. |
|
170 |
+Match controller and options against C<root> to find appropriate C<endpoint>. |
|
212 | 171 |
|
213 | 172 |
=head2 path_for |
214 | 173 |
|
215 |
- my $path = $m->path_for; |
|
216 |
- my $path = $m->path_for(foo => 'bar'); |
|
217 |
- my $path = $m->path_for({foo => 'bar'}); |
|
218 |
- my $path = $m->path_for('named'); |
|
219 |
- my $path = $m->path_for('named', foo => 'bar'); |
|
220 |
- my $path = $m->path_for('named', {foo => 'bar'}); |
|
221 |
- my ($path, $ws) = $m->path_for; |
|
222 |
- my ($path, $ws) = $m->path_for(foo => 'bar'); |
|
223 |
- my ($path, $ws) = $m->path_for({foo => 'bar'}); |
|
224 |
- my ($path, $ws) = $m->path_for('named'); |
|
225 |
- my ($path, $ws) = $m->path_for('named', foo => 'bar'); |
|
226 |
- my ($path, $ws) = $m->path_for('named', {foo => 'bar'}); |
|
174 |
+ my $path = $match->path_for; |
|
175 |
+ my $path = $match->path_for(foo => 'bar'); |
|
176 |
+ my $path = $match->path_for({foo => 'bar'}); |
|
177 |
+ my $path = $match->path_for('named'); |
|
178 |
+ my $path = $match->path_for('named', foo => 'bar'); |
|
179 |
+ my $path = $match->path_for('named', {foo => 'bar'}); |
|
180 |
+ my ($path, $ws) = $match->path_for; |
|
181 |
+ my ($path, $ws) = $match->path_for(foo => 'bar'); |
|
182 |
+ my ($path, $ws) = $match->path_for({foo => 'bar'}); |
|
183 |
+ my ($path, $ws) = $match->path_for('named'); |
|
184 |
+ my ($path, $ws) = $match->path_for('named', foo => 'bar'); |
|
185 |
+ my ($path, $ws) = $match->path_for('named', {foo => 'bar'}); |
|
227 | 186 |
|
228 | 187 |
Render matching route with parameters into path. |
229 | 188 |
|
... | ... |
@@ -14,8 +14,37 @@ sub new { shift->SUPER::new->parse(@_) } |
14 | 14 |
|
15 | 15 |
sub match { |
16 | 16 |
my ($self, $path, $detect) = @_; |
17 |
- my $result = $self->shape_match(\$path, $detect); |
|
18 |
- return !$path || $path eq '/' ? $result : undef; |
|
17 |
+ my $captures = $self->match_partial(\$path, $detect); |
|
18 |
+ return !$path || $path eq '/' ? $captures : undef; |
|
19 |
+} |
|
20 |
+ |
|
21 |
+sub match_partial { |
|
22 |
+ my ($self, $pathref, $detect) = @_; |
|
23 |
+ |
|
24 |
+ # Compile on demand |
|
25 |
+ my $regex = $self->regex || $self->_compile; |
|
26 |
+ my $format |
|
27 |
+ = $detect ? ($self->format_regex || $self->_compile_format) : undef; |
|
28 |
+ |
|
29 |
+ # Match |
|
30 |
+ return undef unless my @captures = $$pathref =~ $regex; |
|
31 |
+ $$pathref =~ s/$regex//; |
|
32 |
+ |
|
33 |
+ # Merge captures |
|
34 |
+ my $captures = {%{$self->defaults}}; |
|
35 |
+ for my $placeholder (@{$self->placeholders}) { |
|
36 |
+ last unless @captures; |
|
37 |
+ my $capture = shift @captures; |
|
38 |
+ $captures->{$placeholder} = $capture if defined $capture; |
|
39 |
+ } |
|
40 |
+ |
|
41 |
+ # Format |
|
42 |
+ my $constraint = $self->constraints->{format}; |
|
43 |
+ return $captures if !$detect || defined $constraint && !$constraint; |
|
44 |
+ if ($$pathref =~ s!^/?$format!!) { $captures->{format} = $1 } |
|
45 |
+ elsif ($constraint) { return undef unless $captures->{format} } |
|
46 |
+ |
|
47 |
+ return $captures; |
|
19 | 48 |
} |
20 | 49 |
|
21 | 50 |
sub parse { |
... | ... |
@@ -36,8 +65,10 @@ sub render { |
36 | 65 |
my $format = ($values ||= {})->{format}; |
37 | 66 |
$values = {%{$self->defaults}, %$values}; |
38 | 67 |
|
39 |
- my $string = ''; |
|
40 |
- my $optional = 1; |
|
68 |
+ # Placeholders can only be optional without a format |
|
69 |
+ my $optional = !$format; |
|
70 |
+ |
|
71 |
+ my $str = ''; |
|
41 | 72 |
for my $token (reverse @{$self->tree}) { |
42 | 73 |
my $op = $token->[0]; |
43 | 74 |
my $rendered = ''; |
... | ... |
@@ -52,7 +83,7 @@ sub render { |
52 | 83 |
} |
53 | 84 |
|
54 | 85 |
# Placeholder, relaxed or wildcard |
55 |
- elsif (grep { $_ eq $op } qw(placeholder relaxed wildcard)) { |
|
86 |
+ elsif ($op eq 'placeholder' || $op eq 'relaxed' || $op eq 'wildcard') { |
|
56 | 87 |
my $name = $token->[1]; |
57 | 88 |
$rendered = defined $values->{$name} ? $values->{$name} : ''; |
58 | 89 |
my $default = $self->defaults->{$name}; |
... | ... |
@@ -60,49 +91,20 @@ sub render { |
60 | 91 |
elsif ($optional) { $rendered = '' } |
61 | 92 |
} |
62 | 93 |
|
63 |
- $string = "$rendered$string"; |
|
94 |
+ $str = "$rendered$str"; |
|
64 | 95 |
} |
65 | 96 |
|
66 | 97 |
# Format is optional |
67 |
- $string ||= '/'; |
|
68 |
- return $render && $format ? "$string.$format" : $string; |
|
69 |
-} |
|
70 |
- |
|
71 |
-sub shape_match { |
|
72 |
- my ($self, $pathref, $detect) = @_; |
|
73 |
- |
|
74 |
- # Compile on demand |
|
75 |
- my $regex = $self->regex || $self->_compile; |
|
76 |
- my $format |
|
77 |
- = $detect ? ($self->format_regex || $self->_compile_format) : undef; |
|
78 |
- |
|
79 |
- # Match |
|
80 |
- return undef unless my @captures = $$pathref =~ $regex; |
|
81 |
- $$pathref =~ s/($regex)//; |
|
82 |
- |
|
83 |
- # Merge captures |
|
84 |
- my $result = {%{$self->defaults}}; |
|
85 |
- for my $placeholder (@{$self->placeholders}) { |
|
86 |
- last unless @captures; |
|
87 |
- my $capture = shift @captures; |
|
88 |
- $result->{$placeholder} = $capture if defined $capture; |
|
89 |
- } |
|
90 |
- |
|
91 |
- # Format |
|
92 |
- my $constraint = $self->constraints->{format}; |
|
93 |
- return $result if !$detect || defined $constraint && !$constraint; |
|
94 |
- if ($$pathref =~ s!^/?$format!!) { $result->{format} = $1 } |
|
95 |
- elsif ($constraint) { return undef unless $result->{format} } |
|
96 |
- |
|
97 |
- return $result; |
|
98 |
+ $str ||= '/'; |
|
99 |
+ return $render && $format ? "$str.$format" : $str; |
|
98 | 100 |
} |
99 | 101 |
|
100 | 102 |
sub _compile { |
101 | 103 |
my $self = shift; |
102 | 104 |
|
103 | 105 |
my $block = my $regex = ''; |
104 |
- my $constraints = $self->constraints; |
|
105 | 106 |
my $optional = 1; |
107 |
+ my $constraints = $self->constraints; |
|
106 | 108 |
my $defaults = $self->defaults; |
107 | 109 |
for my $token (reverse @{$self->tree}) { |
108 | 110 |
my $op = $token->[0]; |
... | ... |
@@ -110,10 +112,7 @@ sub _compile { |
110 | 112 |
|
111 | 113 |
# Slash |
112 | 114 |
if ($op eq 'slash') { |
113 |
- |
|
114 |
- # Full block |
|
115 |
- $block = $optional ? "(?:/$block)?" : "/$block"; |
|
116 |
- $regex = "$block$regex"; |
|
115 |
+ $regex = ($optional ? "(?:/$block)?" : "/$block") . $regex; |
|
117 | 116 |
$block = ''; |
118 | 117 |
next; |
119 | 118 |
} |
... | ... |
@@ -125,7 +124,7 @@ sub _compile { |
125 | 124 |
} |
126 | 125 |
|
127 | 126 |
# Placeholder |
128 |
- elsif (grep { $_ eq $op } qw(placeholder relaxed wildcard)) { |
|
127 |
+ elsif ($op eq 'placeholder' || $op eq 'relaxed' || $op eq 'wildcard') { |
|
129 | 128 |
my $name = $token->[1]; |
130 | 129 |
unshift @{$self->placeholders}, $name; |
131 | 130 |
|
... | ... |
@@ -153,7 +152,6 @@ sub _compile { |
153 | 152 |
# Not rooted with a slash |
154 | 153 |
$regex = "$block$regex" if $block; |
155 | 154 |
|
156 |
- # Compile |
|
157 | 155 |
return $self->regex(qr/^$regex/s)->regex; |
158 | 156 |
} |
159 | 157 |
|
... | ... |
@@ -210,7 +208,7 @@ sub _tokenize { |
210 | 208 |
} |
211 | 209 |
|
212 | 210 |
# Relaxed or wildcard start (upgrade when quoted) |
213 |
- elsif (grep { $_ eq $char } $relaxed, $wildcard) { |
|
211 |
+ elsif ($char eq $relaxed || $char eq $wildcard) { |
|
214 | 212 |
push @tree, ['placeholder', ''] unless $quoted; |
215 | 213 |
$tree[-1][0] = $state = $char eq $relaxed ? 'relaxed' : 'wildcard'; |
216 | 214 |
} |
... | ... |
@@ -259,8 +257,8 @@ Mojolicious::Routes::Pattern - Routes pattern engine |
259 | 257 |
my $pattern = Mojolicious::Routes::Pattern->new('/test/:name'); |
260 | 258 |
|
261 | 259 |
# Match routes |
262 |
- my $result = $pattern->match('/test/sebastian'); |
|
263 |
- say $result->{name}; |
|
260 |
+ my $captures = $pattern->match('/test/sebastian'); |
|
261 |
+ say $captures->{name}; |
|
264 | 262 |
|
265 | 263 |
=head1 DESCRIPTION |
266 | 264 |
|
... | ... |
@@ -343,9 +341,10 @@ Character indicating a relaxed placeholder, defaults to C<#>. |
343 | 341 |
=head2 tree |
344 | 342 |
|
345 | 343 |
my $tree = $pattern->tree; |
346 |
- $pattern = $pattern->tree([ ... ]); |
|
344 |
+ $pattern = $pattern->tree([['slash'], ['text', 'foo']]); |
|
347 | 345 |
|
348 |
-Pattern in parsed form. |
|
346 |
+Pattern in parsed form. Note that this structure should only be used very |
|
347 |
+carefully since it is very dynamic. |
|
349 | 348 |
|
350 | 349 |
=head2 wildcard_start |
351 | 350 |
|
... | ... |
@@ -366,22 +365,31 @@ implements the following new ones. |
366 | 365 |
= Mojolicious::Routes::Pattern->new('/:action', action => qr/\w+/); |
367 | 366 |
my $pattern = Mojolicious::Routes::Pattern->new(format => 0); |
368 | 367 |
|
369 |
-Construct a new L<Mojolicious::Routes::Pattern> object. |
|
368 |
+Construct a new L<Mojolicious::Routes::Pattern> object and C<parse> pattern if |
|
369 |
+necessary. |
|
370 | 370 |
|
371 | 371 |
=head2 match |
372 | 372 |
|
373 |
- my $result = $pattern->match('/foo/bar'); |
|
374 |
- my $result = $pattern->match('/foo/bar', 1); |
|
373 |
+ my $captures = $pattern->match('/foo/bar'); |
|
374 |
+ my $captures = $pattern->match('/foo/bar', 1); |
|
375 | 375 |
|
376 | 376 |
Match pattern against entire path, format detection is disabled by default. |
377 | 377 |
|
378 |
+=head2 match_partial |
|
379 |
+ |
|
380 |
+ my $captures = $pattern->match_partial(\$path); |
|
381 |
+ my $captures = $pattern->match_partial(\$path, 1); |
|
382 |
+ |
|
383 |
+Match pattern against path and remove matching parts, format detection is |
|
384 |
+disabled by default. |
|
385 |
+ |
|
378 | 386 |
=head2 parse |
379 | 387 |
|
380 | 388 |
$pattern = $pattern->parse('/:action'); |
381 | 389 |
$pattern = $pattern->parse('/:action', action => qr/\w+/); |
382 | 390 |
$pattern = $pattern->parse(format => 0); |
383 | 391 |
|
384 |
-Parse a raw pattern. |
|
392 |
+Parse pattern. |
|
385 | 393 |
|
386 | 394 |
=head2 render |
387 | 395 |
|
... | ... |
@@ -391,14 +399,6 @@ Parse a raw pattern. |
391 | 399 |
Render pattern into a path with parameters, format rendering is disabled by |
392 | 400 |
default. |
393 | 401 |
|
394 |
-=head2 shape_match |
|
395 |
- |
|
396 |
- my $result = $pattern->shape_match(\$path); |
|
397 |
- my $result = $pattern->shape_match(\$path, 1); |
|
398 |
- |
|
399 |
-Match pattern against path and remove matching parts, format detection is |
|
400 |
-disabled by default. |
|
401 |
- |
|
402 | 402 |
=head1 SEE ALSO |
403 | 403 |
|
404 | 404 |
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
... | ... |
@@ -12,12 +12,11 @@ has pattern => sub { Mojolicious::Routes::Pattern->new }; |
12 | 12 |
sub AUTOLOAD { |
13 | 13 |
my $self = shift; |
14 | 14 |
|
15 |
- # Method |
|
16 | 15 |
my ($package, $method) = our $AUTOLOAD =~ /^([\w:]+)::(\w+)$/; |
17 | 16 |
croak "Undefined subroutine &${package}::$method called" |
18 | 17 |
unless blessed $self && $self->isa(__PACKAGE__); |
19 | 18 |
|
20 |
- # Call shortcut |
|
19 |
+ # Call shortcut with current route |
|
21 | 20 |
croak qq{Can't locate object method "$method" via package "$package"} |
22 | 21 |
unless my $shortcut = $self->root->shortcuts->{$method}; |
23 | 22 |
return $self->$shortcut(@_); |
... | ... |
@@ -152,51 +151,25 @@ sub route { |
152 | 151 |
sub to { |
153 | 152 |
my $self = shift; |
154 | 153 |
|
155 |
- # No argument |
|
156 | 154 |
my $pattern = $self->pattern; |
157 | 155 |
return $pattern->defaults unless @_; |
156 |
+ my ($shortcut, %defaults) = _defaults(@_); |
|
158 | 157 |
|
159 |
- # Single argument |
|
160 |
- my ($shortcut, $defaults); |
|
161 |
- if (@_ == 1) { |
|
162 |
- $defaults = shift if ref $_[0] eq 'HASH'; |
|
163 |
- $shortcut = shift if $_[0]; |
|
164 |
- } |
|
165 |
- |
|
166 |
- # Multiple arguments |
|
167 |
- else { |
|
168 |
- |
|
169 |
- # Odd |
|
170 |
- if (@_ % 2) { ($shortcut, $defaults) = (shift, {@_}) } |
|
171 |
- |
|
172 |
- # Even |
|
173 |
- else { |
|
174 |
- |
|
175 |
- # Shortcut and defaults |
|
176 |
- if (ref $_[1] eq 'HASH') { ($shortcut, $defaults) = (shift, shift) } |
|
177 |
- |
|
178 |
- # Just defaults |
|
179 |
- else { $defaults = {@_} } |
|
180 |
- } |
|
181 |
- } |
|
182 |
- |
|
183 |
- # Shortcut |
|
184 | 158 |
if ($shortcut) { |
185 | 159 |
|
186 |
- # App |
|
160 |
+ # Application |
|
187 | 161 |
if (ref $shortcut || $shortcut =~ /^[\w:]+$/) { |
188 |
- $defaults->{app} = $shortcut; |
|
162 |
+ $defaults{app} = $shortcut; |
|
189 | 163 |
} |
190 | 164 |
|
191 | 165 |
# Controller and action |
192 | 166 |
elsif ($shortcut =~ /^([\w\-:]+)?\#(\w+)?$/) { |
193 |
- $defaults->{controller} = $1 if defined $1; |
|
194 |
- $defaults->{action} = $2 if defined $2; |
|
167 |
+ $defaults{controller} = $1 if defined $1; |
|
168 |
+ $defaults{action} = $2 if defined $2; |
|
195 | 169 |
} |
196 | 170 |
} |
197 | 171 |
|
198 |
- # Merge defaults |
|
199 |
- $pattern->defaults({%{$pattern->defaults}, %$defaults}) if $defaults; |
|
172 |
+ $pattern->defaults({%{$pattern->defaults}, %defaults}); |
|
200 | 173 |
|
201 | 174 |
return $self; |
202 | 175 |
} |
... | ... |
@@ -224,6 +197,18 @@ sub websocket { |
224 | 197 |
return $route; |
225 | 198 |
} |
226 | 199 |
|
200 |
+sub _defaults { |
|
201 |
+ |
|
202 |
+ # Hash or shortcut (one) |
|
203 |
+ return ref $_[0] eq 'HASH' ? (undef, %{shift()}) : @_ if @_ == 1; |
|
204 |
+ |
|
205 |
+ # Shortcut and values (odd) |
|
206 |
+ return shift, @_ if @_ % 2; |
|
207 |
+ |
|
208 |
+ # Shortcut and hash or just values (even) |
|
209 |
+ return ref $_[1] eq 'HASH' ? (shift, %{shift()}) : (undef, @_); |
|
210 |
+} |
|
211 |
+ |
|
227 | 212 |
sub _generate_route { |
228 | 213 |
my ($self, $methods, @args) = @_; |
229 | 214 |
|
... | ... |
@@ -328,11 +313,12 @@ implements the following new ones. |
328 | 313 |
my $r = Mojolicious::Routes::Route->new; |
329 | 314 |
my $r = Mojolicious::Routes::Route->new('/:controller/:action'); |
330 | 315 |
|
331 |
-Construct a new L<Mojolicious::Routes::Route> object. |
|
316 |
+Construct a new L<Mojolicious::Routes::Route> object and <parse> pattern if |
|
317 |
+necessary. |
|
332 | 318 |
|
333 | 319 |
=head2 add_child |
334 | 320 |
|
335 |
- $r = $r->add_child(Mojolicious::Route->new); |
|
321 |
+ $r = $r->add_child(Mojolicious::Routes::Route->new); |
|
336 | 322 |
|
337 | 323 |
Add a new child to this route, it will be automatically removed from its |
338 | 324 |
current parent if necessary. |
... | ... |
@@ -375,19 +361,12 @@ L<Mojolicious::Lite> tutorial for more argument variations. |
375 | 361 |
=head2 detour |
376 | 362 |
|
377 | 363 |
$r = $r->detour(action => 'foo'); |
378 |
- $r = $r->detour({action => 'foo'}); |
|
379 | 364 |
$r = $r->detour('controller#action'); |
380 |
- $r = $r->detour('controller#action', foo => 'bar'); |
|
381 |
- $r = $r->detour('controller#action', {foo => 'bar'}); |
|
382 |
- $r = $r->detour(Mojolicious->new); |
|
383 | 365 |
$r = $r->detour(Mojolicious->new, foo => 'bar'); |
384 |
- $r = $r->detour(Mojolicious->new, {foo => 'bar'}); |
|
385 |
- $r = $r->detour('MyApp'); |
|
386 |
- $r = $r->detour('MyApp', foo => 'bar'); |
|
387 | 366 |
$r = $r->detour('MyApp', {foo => 'bar'}); |
388 | 367 |
|
389 | 368 |
Set default parameters for this route and allow partial matching to simplify |
390 |
-application embedding. |
|
369 |
+application embedding, takes the same arguments as C<to>. |
|
391 | 370 |
|
392 | 371 |
=head2 find |
393 | 372 |
|
... | ... |
@@ -475,7 +454,7 @@ routing cache, since conditions are too complex for caching. |
475 | 454 |
$r = $r->parse('/:action', action => qr/\w+/); |
476 | 455 |
$r = $r->parse(format => 0); |
477 | 456 |
|
478 |
-Parse a pattern. |
|
457 |
+Parse pattern. |
|
479 | 458 |
|
480 | 459 |
=head2 patch |
481 | 460 |
|
... | ... |
@@ -559,7 +538,7 @@ Set default parameters for this route. |
559 | 538 |
|
560 | 539 |
=head2 to_string |
561 | 540 |
|
562 |
- my $string = $r->to_string; |
|
541 |
+ my $str = $r->to_string; |
|
563 | 542 |
|
564 | 543 |
Stringify the whole route. |
565 | 544 |
|
... | ... |
@@ -589,9 +568,9 @@ restrictions. |
589 | 568 |
|
590 | 569 |
=head2 websocket |
591 | 570 |
|
592 |
- my $websocket = $r->websocket('/:foo' => sub {...}); |
|
571 |
+ my $ws = $r->websocket('/:foo' => sub {...}); |
|
593 | 572 |
|
594 |
-Generate route matching only C<WebSocket> handshakes. See also the |
|
573 |
+Generate route matching only WebSocket handshakes. See also the |
|
595 | 574 |
L<Mojolicious::Lite> tutorial for more argument variations. |
596 | 575 |
|
597 | 576 |
$r->websocket('/echo')->to('example#echo'); |
... | ... |
@@ -6,7 +6,6 @@ use Mojo::Asset::File; |
6 | 6 |
use Mojo::Asset::Memory; |
7 | 7 |
use Mojo::Home; |
8 | 8 |
use Mojo::Loader; |
9 |
-use Mojo::Path; |
|
10 | 9 |
|
11 | 10 |
has classes => sub { ['main'] }; |
12 | 11 |
has paths => sub { [] }; |
... | ... |
@@ -23,8 +22,9 @@ sub dispatch { |
23 | 22 |
|
24 | 23 |
# Canonical path |
25 | 24 |
my $stash = $c->stash; |
26 |
- my $path = $stash->{path} || $c->req->url->path->clone->canonicalize; |
|
27 |
- return undef unless my @parts = @{Mojo::Path->new("$path")->parts}; |
|
25 |
+ my $path = $c->req->url->path; |
|
26 |
+ $path = $stash->{path} ? $path->new($stash->{path}) : $path->clone; |
|
27 |
+ return undef unless my @parts = @{$path->canonicalize->parts}; |
|
28 | 28 |
|
29 | 29 |
# Serve static file and prevent directory traversal |
30 | 30 |
return undef if $parts[0] eq '..' || !$self->serve($c, join('/', @parts)); |
... | ... |
@@ -3,32 +3,32 @@ use Mojo::Base -base; |
3 | 3 |
|
4 | 4 |
has types => sub { |
5 | 5 |
{ |
6 |
- appcache => 'text/cache-manifest', |
|
7 |
- atom => 'application/atom+xml', |
|
8 |
- bin => 'application/octet-stream', |
|
9 |
- css => 'text/css', |
|
10 |
- gif => 'image/gif', |
|
11 |
- gz => 'application/x-gzip', |
|
12 |
- htm => 'text/html', |
|
13 |
- html => 'text/html;charset=UTF-8', |
|
14 |
- ico => 'image/x-icon', |
|
15 |
- jpeg => 'image/jpeg', |
|
16 |
- jpg => 'image/jpeg', |
|
17 |
- js => 'application/javascript', |
|
18 |
- json => 'application/json', |
|
19 |
- mp3 => 'audio/mpeg', |
|
20 |
- mp4 => 'video/mp4', |
|
21 |
- ogg => 'audio/ogg', |
|
22 |
- ogv => 'video/ogg', |
|
23 |
- pdf => 'application/pdf', |
|
24 |
- png => 'image/png', |
|
25 |
- rss => 'application/rss+xml', |
|
26 |
- svg => 'image/svg+xml', |
|
27 |
- txt => 'text/plain', |
|
28 |
- webm => 'video/webm', |
|
29 |
- woff => 'application/font-woff', |
|
6 |
+ appcache => ['text/cache-manifest'], |
|
7 |
+ atom => ['application/atom+xml'], |
|
8 |
+ bin => ['application/octet-stream'], |
|
9 |
+ css => ['text/css'], |
|
10 |
+ gif => ['image/gif'], |
|
11 |
+ gz => ['application/x-gzip'], |
|
12 |
+ htm => ['text/html'], |
|
13 |
+ html => ['text/html;charset=UTF-8'], |
|
14 |
+ ico => ['image/x-icon'], |
|
15 |
+ jpeg => ['image/jpeg'], |
|
16 |
+ jpg => ['image/jpeg'], |
|
17 |
+ js => ['application/javascript'], |
|
18 |
+ json => ['application/json'], |
|
19 |
+ mp3 => ['audio/mpeg'], |
|
20 |
+ mp4 => ['video/mp4'], |
|
21 |
+ ogg => ['audio/ogg'], |
|
22 |
+ ogv => ['video/ogg'], |
|
23 |
+ pdf => ['application/pdf'], |
|
24 |
+ png => ['image/png'], |
|
25 |
+ rss => ['application/rss+xml'], |
|
26 |
+ svg => ['image/svg+xml'], |
|
27 |
+ txt => ['text/plain'], |
|
28 |
+ webm => ['video/webm'], |
|
29 |
+ woff => ['application/font-woff'], |
|
30 | 30 |
xml => ['application/xml', 'text/xml'], |
31 |
- zip => 'application/zip' |
|
31 |
+ zip => ['application/zip'] |
|
32 | 32 |
}; |
33 | 33 |
}; |
34 | 34 |
|
... | ... |
@@ -40,24 +40,23 @@ sub detect { |
40 | 40 |
/^\s*([^,; ]+)(?:\s*\;\s*q=(\d+(?:\.\d+)?))?\s*$/i |
41 | 41 |
and $types{lc $1} = defined $2 ? $2 : 1 |
42 | 42 |
for split /,/, defined $accept ? $accept : ''; |
43 |
- my @types = sort { $types{$b} <=> $types{$a} } sort keys %types; |
|
44 |
- return [] if !$prioritize && @types > 1; |
|
43 |
+ my @detected = sort { $types{$b} <=> $types{$a} } sort keys %types; |
|
44 |
+ return [] if !$prioritize && @detected > 1; |
|
45 | 45 |
|
46 | 46 |
# Detect extensions from MIME types |
47 | 47 |
my %reverse; |
48 | 48 |
my $types = $self->types; |
49 | 49 |
for my $ext (sort keys %$types) { |
50 |
- my @types = ref $types->{$ext} ? @{$types->{$ext}} : ($types->{$ext}); |
|
50 |
+ my @types = @{$types->{$ext}}; |
|
51 | 51 |
push @{$reverse{$_}}, $ext for map { s/\;.*$//; lc $_ } @types; |
52 | 52 |
} |
53 |
- return [map { @{defined $reverse{$_} ? $reverse{$_} : []} } @types]; |
|
53 |
+ return [map { @{defined $reverse{$_} ? $reverse{$_} : []} } @detected]; |
|
54 | 54 |
} |
55 | 55 |
|
56 | 56 |
sub type { |
57 | 57 |
my ($self, $ext, $type) = @_; |
58 |
- my $types = $self->types; |
|
59 |
- return ref $types->{$ext} ? $types->{$ext}[0] : $types->{$ext} unless $type; |
|
60 |
- $types->{$ext} = $type; |
|
58 |
+ return $self->types->{$ext}[0] unless $type; |
|
59 |
+ $self->types->{$ext} = ref $type ? $type : [$type]; |
|
61 | 60 |
return $self; |
62 | 61 |
} |
63 | 62 |
|
... | ... |
@@ -115,7 +114,7 @@ L<Mojolicious::Types> implements the following attributes. |
115 | 114 |
=head2 types |
116 | 115 |
|
117 | 116 |
my $map = $types->types; |
118 |
- $types = $types->types({png => 'image/png'}); |
|
117 |
+ $types = $types->types({png => ['image/png']}); |
|
119 | 118 |
|
120 | 119 |
List of MIME types. |
121 | 120 |
|
... | ... |
@@ -1,4 +1,6 @@ |
1 |
-/*! jQuery v1.9.0 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license */(function(e,t){"use strict";function n(e){var t=e.length,n=st.type(e);return st.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}function r(e){var t=Tt[e]={};return st.each(e.match(lt)||[],function(e,n){t[n]=!0}),t}function i(e,n,r,i){if(st.acceptData(e)){var o,a,s=st.expando,u="string"==typeof n,l=e.nodeType,c=l?st.cache:e,f=l?e[s]:e[s]&&s;if(f&&c[f]&&(i||c[f].data)||!u||r!==t)return f||(l?e[s]=f=K.pop()||st.guid++:f=s),c[f]||(c[f]={},l||(c[f].toJSON=st.noop)),("object"==typeof n||"function"==typeof n)&&(i?c[f]=st.extend(c[f],n):c[f].data=st.extend(c[f].data,n)),o=c[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[st.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[st.camelCase(n)])):a=o,a}}function o(e,t,n){if(st.acceptData(e)){var r,i,o,a=e.nodeType,u=a?st.cache:e,l=a?e[st.expando]:st.expando;if(u[l]){if(t&&(r=n?u[l]:u[l].data)){st.isArray(t)?t=t.concat(st.map(t,st.camelCase)):t in r?t=[t]:(t=st.camelCase(t),t=t in r?[t]:t.split(" "));for(i=0,o=t.length;o>i;i++)delete r[t[i]];if(!(n?s:st.isEmptyObject)(r))return}(n||(delete u[l].data,s(u[l])))&&(a?st.cleanData([e],!0):st.support.deleteExpando||u!=u.window?delete u[l]:u[l]=null)}}}function a(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(Nt,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:wt.test(r)?st.parseJSON(r):r}catch(o){}st.data(e,n,r)}else r=t}return r}function s(e){var t;for(t in e)if(("data"!==t||!st.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function u(){return!0}function l(){return!1}function c(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function f(e,t,n){if(t=t||0,st.isFunction(t))return st.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return st.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=st.grep(e,function(e){return 1===e.nodeType});if(Wt.test(t))return st.filter(t,r,!n);t=st.filter(t,r)}return st.grep(e,function(e){return st.inArray(e,t)>=0===n})}function p(e){var t=zt.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function d(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function h(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function g(e){var t=nn.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function m(e,t){for(var n,r=0;null!=(n=e[r]);r++)st._data(n,"globalEval",!t||st._data(t[r],"globalEval"))}function y(e,t){if(1===t.nodeType&&st.hasData(e)){var n,r,i,o=st._data(e),a=st._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)st.event.add(t,n,s[n][r])}a.data&&(a.data=st.extend({},a.data))}}function v(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!st.support.noCloneEvent&&t[st.expando]){r=st._data(t);for(i in r.events)st.removeEvent(t,i,r.handle);t.removeAttribute(st.expando)}"script"===n&&t.text!==e.text?(h(t).text=e.text,g(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),st.support.html5Clone&&e.innerHTML&&!st.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Zt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}function b(e,n){var r,i,o=0,a=e.getElementsByTagName!==t?e.getElementsByTagName(n||"*"):e.querySelectorAll!==t?e.querySelectorAll(n||"*"):t;if(!a)for(a=[],r=e.childNodes||e;null!=(i=r[o]);o++)!n||st.nodeName(i,n)?a.push(i):st.merge(a,b(i,n));return n===t||n&&st.nodeName(e,n)?st.merge([e],a):a}function x(e){Zt.test(e.type)&&(e.defaultChecked=e.checked)}function T(e,t){if(t in e)return t;for(var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=Nn.length;i--;)if(t=Nn[i]+n,t in e)return t;return r}function w(e,t){return e=t||e,"none"===st.css(e,"display")||!st.contains(e.ownerDocument,e)}function N(e,t){for(var n,r=[],i=0,o=e.length;o>i;i++)n=e[i],n.style&&(r[i]=st._data(n,"olddisplay"),t?(r[i]||"none"!==n.style.display||(n.style.display=""),""===n.style.display&&w(n)&&(r[i]=st._data(n,"olddisplay",S(n.nodeName)))):r[i]||w(n)||st._data(n,"olddisplay",st.css(n,"display")));for(i=0;o>i;i++)n=e[i],n.style&&(t&&"none"!==n.style.display&&""!==n.style.display||(n.style.display=t?r[i]||"":"none"));return e}function C(e,t,n){var r=mn.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function k(e,t,n,r,i){for(var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;4>o;o+=2)"margin"===n&&(a+=st.css(e,n+wn[o],!0,i)),r?("content"===n&&(a-=st.css(e,"padding"+wn[o],!0,i)),"margin"!==n&&(a-=st.css(e,"border"+wn[o]+"Width",!0,i))):(a+=st.css(e,"padding"+wn[o],!0,i),"padding"!==n&&(a+=st.css(e,"border"+wn[o]+"Width",!0,i)));return a}function E(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=ln(e),a=st.support.boxSizing&&"border-box"===st.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=un(e,t,o),(0>i||null==i)&&(i=e.style[t]),yn.test(i))return i;r=a&&(st.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+k(e,t,n||(a?"border":"content"),r,o)+"px"}function S(e){var t=V,n=bn[e];return n||(n=A(e,t),"none"!==n&&n||(cn=(cn||st("<iframe frameborder='0' width='0' height='0'/>").css("cssText","display:block !important")).appendTo(t.documentElement),t=(cn[0].contentWindow||cn[0].contentDocument).document,t.write("<!doctype html><html><body>"),t.close(),n=A(e,t),cn.detach()),bn[e]=n),n}function A(e,t){var n=st(t.createElement(e)).appendTo(t.body),r=st.css(n[0],"display");return n.remove(),r}function j(e,t,n,r){var i;if(st.isArray(t))st.each(t,function(t,i){n||kn.test(e)?r(e,i):j(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==st.type(t))r(e,t);else for(i in t)j(e+"["+i+"]",t[i],n,r)}function D(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(lt)||[];if(st.isFunction(n))for(;r=o[i++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function L(e,n,r,i){function o(u){var l;return a[u]=!0,st.each(e[u]||[],function(e,u){var c=u(n,r,i);return"string"!=typeof c||s||a[c]?s?!(l=c):t:(n.dataTypes.unshift(c),o(c),!1)}),l}var a={},s=e===$n;return o(n.dataTypes[0])||!a["*"]&&o("*")}function H(e,n){var r,i,o=st.ajaxSettings.flatOptions||{};for(r in n)n[r]!==t&&((o[r]?e:i||(i={}))[r]=n[r]);return i&&st.extend(!0,e,i),e}function M(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(o in c)o in r&&(n[c[o]]=r[o]);for(;"*"===l[0];)l.shift(),i===t&&(i=e.mimeType||n.getResponseHeader("Content-Type"));if(i)for(o in u)if(u[o]&&u[o].test(i)){l.unshift(o);break}if(l[0]in r)a=l[0];else{for(o in r){if(!l[0]||e.converters[o+" "+l[0]]){a=o;break}s||(s=o)}a=a||s}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function q(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(n in e.converters)a[n.toLowerCase()]=e.converters[n];for(;i=u[++s];)if("*"!==i){if("*"!==l&&l!==i){if(n=a[l+" "+i]||a["* "+i],!n)for(r in a)if(o=r.split(" "),o[1]===i&&(n=a[l+" "+o[0]]||a["* "+o[0]])){n===!0?n=a[r]:a[r]!==!0&&(i=o[0],u.splice(s--,0,i));break}if(n!==!0)if(n&&e["throws"])t=n(t);else try{t=n(t)}catch(c){return{state:"parsererror",error:n?c:"No conversion from "+l+" to "+i}}}l=i}return{state:"success",data:t}}function _(){try{return new e.XMLHttpRequest}catch(t){}}function F(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}function O(){return setTimeout(function(){Qn=t}),Qn=st.now()}function B(e,t){st.each(t,function(t,n){for(var r=(rr[t]||[]).concat(rr["*"]),i=0,o=r.length;o>i;i++)if(r[i].call(e,t,n))return})}function P(e,t,n){var r,i,o=0,a=nr.length,s=st.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;for(var t=Qn||O(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;u>a;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),1>o&&u?n:(s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:st.extend({},t),opts:st.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Qn||O(),duration:n.duration,tweens:[],createTween:function(t,n){var r=st.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?s.resolveWith(e,[l,t]):s.rejectWith(e,[l,t]),this}}),c=l.props;for(R(c,l.opts.specialEasing);a>o;o++)if(r=nr[o].call(l,e,c,l.opts))return r;return B(l,c),st.isFunction(l.opts.start)&&l.opts.start.call(e,l),st.fx.timer(st.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 R(e,t){var n,r,i,o,a;for(n in e)if(r=st.camelCase(n),i=t[r],o=e[n],st.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=st.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}function W(e,t,n){var r,i,o,a,s,u,l,c,f,p=this,d=e.style,h={},g=[],m=e.nodeType&&w(e);n.queue||(c=st._queueHooks(e,"fx"),null==c.unqueued&&(c.unqueued=0,f=c.empty.fire,c.empty.fire=function(){c.unqueued||f()}),c.unqueued++,p.always(function(){p.always(function(){c.unqueued--,st.queue(e,"fx").length||c.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],"inline"===st.css(e,"display")&&"none"===st.css(e,"float")&&(st.support.inlineBlockNeedsLayout&&"inline"!==S(e.nodeName)?d.zoom=1:d.display="inline-block")),n.overflow&&(d.overflow="hidden",st.support.shrinkWrapBlocks||p.done(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(r in t)if(o=t[r],Zn.exec(o)){if(delete t[r],u=u||"toggle"===o,o===(m?"hide":"show"))continue;g.push(r)}if(a=g.length){s=st._data(e,"fxshow")||st._data(e,"fxshow",{}),"hidden"in s&&(m=s.hidden),u&&(s.hidden=!m),m?st(e).show():p.done(function(){st(e).hide()}),p.done(function(){var t;st._removeData(e,"fxshow");for(t in h)st.style(e,t,h[t])});for(r=0;a>r;r++)i=g[r],l=p.createTween(i,m?s[i]:0),h[i]=s[i]||st.style(e,i),i in s||(s[i]=l.start,m&&(l.end=l.start,l.start="width"===i||"height"===i?1:0))}}function $(e,t,n,r,i){return new $.prototype.init(e,t,n,r,i)}function I(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=wn[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}function z(e){return st.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}var X,U,V=e.document,Y=e.location,J=e.jQuery,G=e.$,Q={},K=[],Z="1.9.0",et=K.concat,tt=K.push,nt=K.slice,rt=K.indexOf,it=Q.toString,ot=Q.hasOwnProperty,at=Z.trim,st=function(e,t){return new st.fn.init(e,t,X)},ut=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,lt=/\S+/g,ct=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,ft=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,pt=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,dt=/^[\],:{}\s]*$/,ht=/(?:^|:|,)(?:\s*\[)+/g,gt=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,mt=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,yt=/^-ms-/,vt=/-([\da-z])/gi,bt=function(e,t){return t.toUpperCase()},xt=function(){V.addEventListener?(V.removeEventListener("DOMContentLoaded",xt,!1),st.ready()):"complete"===V.readyState&&(V.detachEvent("onreadystatechange",xt),st.ready())};st.fn=st.prototype={jquery:Z,constructor:st,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:ft.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof st?n[0]:n,st.merge(this,st.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:V,!0)),pt.test(i[1])&&st.isPlainObject(n))for(i in n)st.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=V.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=V,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):st.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),st.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return nt.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=st.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return st.each(this,e,t)},ready:function(e){return st.ready.promise().done(e),this},slice:function(){return this.pushStack(nt.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(st.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:tt,sort:[].sort,splice:[].splice},st.fn.init.prototype=st.fn,st.extend=st.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||st.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(e=arguments[u]))for(n in e)r=s[n],i=e[n],s!==i&&(c&&i&&(st.isPlainObject(i)||(o=st.isArray(i)))?(o?(o=!1,a=r&&st.isArray(r)?r:[]):a=r&&st.isPlainObject(r)?r:{},s[n]=st.extend(c,a,i)):i!==t&&(s[n]=i));return s},st.extend({noConflict:function(t){return e.$===st&&(e.$=G),t&&e.jQuery===st&&(e.jQuery=J),st},isReady:!1,readyWait:1,holdReady:function(e){e?st.readyWait++:st.ready(!0)},ready:function(e){if(e===!0?!--st.readyWait:!st.isReady){if(!V.body)return setTimeout(st.ready);st.isReady=!0,e!==!0&&--st.readyWait>0||(U.resolveWith(V,[st]),st.fn.trigger&&st(V).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===st.type(e)},isArray:Array.isArray||function(e){return"array"===st.type(e)},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?Q[it.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==st.type(e)||e.nodeType||st.isWindow(e))return!1;try{if(e.constructor&&!ot.call(e,"constructor")&&!ot.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||ot.call(e,r)},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||V;var r=pt.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=st.buildFragment([e],t,i),i&&st(i).remove(),st.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=st.trim(n),n&&dt.test(n.replace(gt,"@").replace(mt,"]").replace(ht,"")))?Function("return "+n)():(st.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||st.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&st.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(yt,"ms-").replace(vt,bt)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,r){var i,o=0,a=e.length,s=n(e);if(r){if(s)for(;a>o&&(i=t.apply(e[o],r),i!==!1);o++);else for(o in e)if(i=t.apply(e[o],r),i===!1)break}else if(s)for(;a>o&&(i=t.call(e[o],o,e[o]),i!==!1);o++);else for(o in e)if(i=t.call(e[o],o,e[o]),i===!1)break;return e},trim:at&&!at.call("\ufeff\u00a0")?function(e){return null==e?"":at.call(e)}:function(e){return null==e?"":(e+"").replace(ct,"")},makeArray:function(e,t){var r=t||[];return null!=e&&(n(Object(e))?st.merge(r,"string"==typeof e?[e]:e):tt.call(r,e)),r},inArray:function(e,t,n){var r;if(t){if(rt)return rt.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else for(;n[o]!==t;)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,r){var i,o=0,a=e.length,s=n(e),u=[];if(s)for(;a>o;o++)i=t(e[o],o,r),null!=i&&(u[u.length]=i);else for(o in e)i=t(e[o],o,r),null!=i&&(u[u.length]=i);return et.apply([],u)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(r=e[n],n=e,e=r),st.isFunction(e)?(i=nt.call(arguments,2),o=function(){return e.apply(n||this,i.concat(nt.call(arguments)))},o.guid=e.guid=e.guid||st.guid++,o):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===st.type(r)){o=!0;for(u in r)st.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,st.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(st(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),st.ready.promise=function(t){if(!U)if(U=st.Deferred(),"complete"===V.readyState)setTimeout(st.ready);else if(V.addEventListener)V.addEventListener("DOMContentLoaded",xt,!1),e.addEventListener("load",st.ready,!1);else{V.attachEvent("onreadystatechange",xt),e.attachEvent("onload",st.ready);var n=!1;try{n=null==e.frameElement&&V.documentElement}catch(r){}n&&n.doScroll&&function i(){if(!st.isReady){try{n.doScroll("left")}catch(e){return setTimeout(i,50)}st.ready()}}()}return U.promise(t)},st.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){Q["[object "+t+"]"]=t.toLowerCase()}),X=st(V);var Tt={};st.Callbacks=function(e){e="string"==typeof e?Tt[e]||r(e):st.extend({},e);var n,i,o,a,s,u,l=[],c=!e.once&&[],f=function(t){for(n=e.memory&&t,i=!0,u=a||0,a=0,s=l.length,o=!0;l&&s>u;u++)if(l[u].apply(t[0],t[1])===!1&&e.stopOnFalse){n=!1;break}o=!1,l&&(c?c.length&&f(c.shift()):n?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function r(t){st.each(t,function(t,n){var i=st.type(n);"function"===i?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==i&&r(n)})})(arguments),o?s=l.length:n&&(a=t,f(n))}return this},remove:function(){return l&&st.each(arguments,function(e,t){for(var n;(n=st.inArray(t,l,n))>-1;)l.splice(n,1),o&&(s>=n&&s--,u>=n&&u--)}),this},has:function(e){return st.inArray(e,l)>-1},empty:function(){return l=[],this},disable:function(){return l=c=n=t,this},disabled:function(){return!l},lock:function(){return c=t,n||p.disable(),this},locked:function(){return!c},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!l||i&&!c||(o?c.push(t):f(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},st.extend({Deferred:function(e){var t=[["resolve","done",st.Callbacks("once memory"),"resolved"],["reject","fail",st.Callbacks("once memory"),"rejected"],["notify","progress",st.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return st.Deferred(function(n){st.each(t,function(t,o){var a=o[0],s=st.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&st.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?st.extend(e,r):r}},i={};return r.pipe=r.then,st.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=nt.call(arguments),a=o.length,s=1!==a||e&&st.isFunction(e.promise)?a:0,u=1===s?e:st.Deferred(),l=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?nt.call(arguments):i,r===t?u.notifyWith(n,r):--s||u.resolveWith(n,r)}};if(a>1)for(t=Array(a),n=Array(a),r=Array(a);a>i;i++)o[i]&&st.isFunction(o[i].promise)?o[i].promise().done(l(i,r,o)).fail(u.reject).progress(l(i,n,t)):--s;return s||u.resolveWith(r,o),u.promise()}}),st.support=function(){var n,r,i,o,a,s,u,l,c,f,p=V.createElement("div");if(p.setAttribute("className","t"),p.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",r=p.getElementsByTagName("*"),i=p.getElementsByTagName("a")[0],!r||!i||!r.length)return{};o=V.createElement("select"),a=o.appendChild(V.createElement("option")),s=p.getElementsByTagName("input")[0],i.style.cssText="top:1px;float:left;opacity:.5",n={getSetAttribute:"t"!==p.className,leadingWhitespace:3===p.firstChild.nodeType,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(i.getAttribute("style")),hrefNormalized:"/a"===i.getAttribute("href"),opacity:/^0.5/.test(i.style.opacity),cssFloat:!!i.style.cssFloat,checkOn:!!s.value,optSelected:a.selected,enctype:!!V.createElement("form").enctype,html5Clone:"<:nav></:nav>"!==V.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===V.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},s.checked=!0,n.noCloneChecked=s.cloneNode(!0).checked,o.disabled=!0,n.optDisabled=!a.disabled;try{delete p.test}catch(d){n.deleteExpando=!1}s=V.createElement("input"),s.setAttribute("value",""),n.input=""===s.getAttribute("value"),s.value="t",s.setAttribute("type","radio"),n.radioValue="t"===s.value,s.setAttribute("checked","t"),s.setAttribute("name","t"),u=V.createDocumentFragment(),u.appendChild(s),n.appendChecked=s.checked,n.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,p.attachEvent&&(p.attachEvent("onclick",function(){n.noCloneEvent=!1}),p.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})p.setAttribute(l="on"+f,"t"),n[f+"Bubbles"]=l in e||p.attributes[l].expando===!1;return p.style.backgroundClip="content-box",p.cloneNode(!0).style.backgroundClip="",n.clearCloneStyle="content-box"===p.style.backgroundClip,st(function(){var r,i,o,a="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",s=V.getElementsByTagName("body")[0];s&&(r=V.createElement("div"),r.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",s.appendChild(r).appendChild(p),p.innerHTML="<table><tr><td></td><td>t</td></tr></table>",o=p.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",c=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",n.reliableHiddenOffsets=c&&0===o[0].offsetHeight,p.innerHTML="",p.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",n.boxSizing=4===p.offsetWidth,n.doesNotIncludeMarginInBodyOffset=1!==s.offsetTop,e.getComputedStyle&&(n.pixelPosition="1%"!==(e.getComputedStyle(p,null)||{}).top,n.boxSizingReliable="4px"===(e.getComputedStyle(p,null)||{width:"4px"}).width,i=p.appendChild(V.createElement("div")),i.style.cssText=p.style.cssText=a,i.style.marginRight=i.style.width="0",p.style.width="1px",n.reliableMarginRight=!parseFloat((e.getComputedStyle(i,null)||{}).marginRight)),p.style.zoom!==t&&(p.innerHTML="",p.style.cssText=a+"width:1px;padding:1px;display:inline;zoom:1",n.inlineBlockNeedsLayout=3===p.offsetWidth,p.style.display="block",p.innerHTML="<div></div>",p.firstChild.style.width="5px",n.shrinkWrapBlocks=3!==p.offsetWidth,s.style.zoom=1),s.removeChild(r),r=p=o=i=null)}),r=o=u=a=i=s=null,n}();var wt=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,Nt=/([A-Z])/g;st.extend({cache:{},expando:"jQuery"+(Z+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?st.cache[e[st.expando]]:e[st.expando],!!e&&!s(e)},data:function(e,t,n){return i(e,t,n,!1)},removeData:function(e,t){return o(e,t,!1)},_data:function(e,t,n){return i(e,t,n,!0)},_removeData:function(e,t){return o(e,t,!0)},acceptData:function(e){var t=e.nodeName&&st.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),st.fn.extend({data:function(e,n){var r,i,o=this[0],s=0,u=null;if(e===t){if(this.length&&(u=st.data(o),1===o.nodeType&&!st._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>s;s++)i=r[s].name,i.indexOf("data-")||(i=st.camelCase(i.substring(5)),a(o,i,u[i]));st._data(o,"parsedAttrs",!0)}return u}return"object"==typeof e?this.each(function(){st.data(this,e)}):st.access(this,function(n){return n===t?o?a(o,e,st.data(o,e)):null:(this.each(function(){st.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){st.removeData(this,e)})}}),st.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=st._data(e,n),r&&(!i||st.isArray(r)?i=st._data(e,n,st.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=st.queue(e,t),r=n.length,i=n.shift(),o=st._queueHooks(e,t),a=function(){st.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return st._data(e,n)||st._data(e,n,{empty:st.Callbacks("once memory").add(function(){st._removeData(e,t+"queue"),st._removeData(e,n)})})}}),st.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?st.queue(this[0],e):n===t?this:this.each(function(){var t=st.queue(this,e,n);st._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&st.dequeue(this,e)})},dequeue:function(e){return this.each(function(){st.dequeue(this,e)})},delay:function(e,t){return e=st.fx?st.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,n){var r,i=1,o=st.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};for("string"!=typeof e&&(n=e,e=t),e=e||"fx";s--;)r=st._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var Ct,kt,Et=/[\t\r\n]/g,St=/\r/g,At=/^(?:input|select|textarea|button|object)$/i,jt=/^(?:a|area)$/i,Dt=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,Lt=/^(?:checked|selected)$/i,Ht=st.support.getSetAttribute,Mt=st.support.input;st.fn.extend({attr:function(e,t){return st.access(this,st.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){st.removeAttr(this,e)})},prop:function(e,t){return st.access(this,st.prop,e,t,arguments.length>1)},removeProp:function(e){return e=st.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(st.isFunction(e))return this.each(function(t){st(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(lt)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(Et," "):" ")){for(o=0;i=t[o++];)0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=st.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(st.isFunction(e))return this.each(function(t){st(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(lt)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(Et," "):"")){for(o=0;i=t[o++];)for(;r.indexOf(" "+i+" ")>=0;)r=r.replace(" "+i+" "," ");n.className=e?st.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return st.isFunction(e)?this.each(function(n){st(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n)for(var i,o=0,a=st(this),s=t,u=e.match(lt)||[];i=u[o++];)s=r?s:!a.hasClass(i),a[s?"addClass":"removeClass"](i);else("undefined"===n||"boolean"===n)&&(this.className&&st._data(this,"__className__",this.className),this.className=this.className||e===!1?"":st._data(this,"__className__")||"")})},hasClass:function(e){for(var t=" "+e+" ",n=0,r=this.length;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(Et," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=st.isFunction(e),this.each(function(r){var o,a=st(this);1===this.nodeType&&(o=i?e.call(this,r,a.val()):e,null==o?o="":"number"==typeof o?o+="":st.isArray(o)&&(o=st.map(o,function(e){return null==e?"":e+""})),n=st.valHooks[this.type]||st.valHooks[this.nodeName.toLowerCase()],n&&"set"in n&&n.set(this,o,"value")!==t||(this.value=o))});if(o)return n=st.valHooks[o.type]||st.valHooks[o.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(o,"value"))!==t?r:(r=o.value,"string"==typeof r?r.replace(St,""):null==r?"":r)}}}),st.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(st.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&st.nodeName(n.parentNode,"optgroup"))){if(t=st(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=st.makeArray(t);return st(e).find("option").each(function(){this.selected=st.inArray(st(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return e.getAttribute===t?st.prop(e,n,r):(a=1!==s||!st.isXMLDoc(e),a&&(n=n.toLowerCase(),o=st.attrHooks[n]||(Dt.test(n)?kt:Ct)),r===t?o&&a&&"get"in o&&null!==(i=o.get(e,n))?i:(e.getAttribute!==t&&(i=e.getAttribute(n)),null==i?t:i):null!==r?o&&a&&"set"in o&&(i=o.set(e,r,n))!==t?i:(e.setAttribute(n,r+""),r):(st.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(lt);if(o&&1===e.nodeType)for(;n=o[i++];)r=st.propFix[n]||n,Dt.test(n)?!Ht&&Lt.test(n)?e[st.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:st.attr(e,n,""),e.removeAttribute(Ht?n:r)},attrHooks:{type:{set:function(e,t){if(!st.support.radioValue&&"radio"===t&&st.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!st.isXMLDoc(e),a&&(n=st.propFix[n]||n,o=st.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):At.test(e.nodeName)||jt.test(e.nodeName)&&e.href?0:t}}}}),kt={get:function(e,n){var r=st.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?Mt&&Ht?null!=i:Lt.test(n)?e[st.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?st.removeAttr(e,n):Mt&&Ht||!Lt.test(n)?e.setAttribute(!Ht&&st.propFix[n]||n,n):e[st.camelCase("default-"+n)]=e[n]=!0,n}},Mt&&Ht||(st.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return st.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t |
|
2 |
-},set:function(e,n,r){return st.nodeName(e,"input")?(e.defaultValue=n,t):Ct&&Ct.set(e,n,r)}}),Ht||(Ct=st.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},st.attrHooks.contenteditable={get:Ct.get,set:function(e,t,n){Ct.set(e,""===t?!1:t,n)}},st.each(["width","height"],function(e,n){st.attrHooks[n]=st.extend(st.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),st.support.hrefNormalized||(st.each(["href","src","width","height"],function(e,n){st.attrHooks[n]=st.extend(st.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),st.each(["href","src"],function(e,t){st.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),st.support.style||(st.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),st.support.optSelected||(st.propHooks.selected=st.extend(st.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),st.support.enctype||(st.propFix.enctype="encoding"),st.support.checkOn||st.each(["radio","checkbox"],function(){st.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),st.each(["radio","checkbox"],function(){st.valHooks[this]=st.extend(st.valHooks[this],{set:function(e,n){return st.isArray(n)?e.checked=st.inArray(st(e).val(),n)>=0:t}})});var qt=/^(?:input|select|textarea)$/i,_t=/^key/,Ft=/^(?:mouse|contextmenu)|click/,Ot=/^(?:focusinfocus|focusoutblur)$/,Bt=/^([^.]*)(?:\.(.+)|)$/;st.event={global:{},add:function(e,n,r,i,o){var a,s,u,l,c,f,p,d,h,g,m,y=3!==e.nodeType&&8!==e.nodeType&&st._data(e);if(y){for(r.handler&&(a=r,r=a.handler,o=a.selector),r.guid||(r.guid=st.guid++),(l=y.events)||(l=y.events={}),(s=y.handle)||(s=y.handle=function(e){return st===t||e&&st.event.triggered===e.type?t:st.event.dispatch.apply(s.elem,arguments)},s.elem=e),n=(n||"").match(lt)||[""],c=n.length;c--;)u=Bt.exec(n[c])||[],h=m=u[1],g=(u[2]||"").split(".").sort(),p=st.event.special[h]||{},h=(o?p.delegateType:p.bindType)||h,p=st.event.special[h]||{},f=st.extend({type:h,origType:m,data:i,handler:r,guid:r.guid,selector:o,needsContext:o&&st.expr.match.needsContext.test(o),namespace:g.join(".")},a),(d=l[h])||(d=l[h]=[],d.delegateCount=0,p.setup&&p.setup.call(e,i,g,s)!==!1||(e.addEventListener?e.addEventListener(h,s,!1):e.attachEvent&&e.attachEvent("on"+h,s))),p.add&&(p.add.call(e,f),f.handler.guid||(f.handler.guid=r.guid)),o?d.splice(d.delegateCount++,0,f):d.push(f),st.event.global[h]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,m=st.hasData(e)&&st._data(e);if(m&&(u=m.events)){for(t=(t||"").match(lt)||[""],l=t.length;l--;)if(s=Bt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){for(f=st.event.special[d]||{},d=(r?f.delegateType:f.bindType)||d,p=u[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;o--;)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.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));a&&!p.length&&(f.teardown&&f.teardown.call(e,h,m.handle)!==!1||st.removeEvent(e,d,m.handle),delete u[d])}else for(d in u)st.event.remove(e,d+t[l],n,r,!0);st.isEmptyObject(u)&&(delete m.handle,st._removeData(e,"events"))}},trigger:function(n,r,i,o){var a,s,u,l,c,f,p,d=[i||V],h=n.type||n,g=n.namespace?n.namespace.split("."):[];if(s=u=i=i||V,3!==i.nodeType&&8!==i.nodeType&&!Ot.test(h+st.event.triggered)&&(h.indexOf(".")>=0&&(g=h.split("."),h=g.shift(),g.sort()),c=0>h.indexOf(":")&&"on"+h,n=n[st.expando]?n:new st.Event(h,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=g.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+g.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:st.makeArray(r,[n]),p=st.event.special[h]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!st.isWindow(i)){for(l=p.delegateType||h,Ot.test(l+h)||(s=s.parentNode);s;s=s.parentNode)d.push(s),u=s;u===(i.ownerDocument||V)&&d.push(u.defaultView||u.parentWindow||e)}for(a=0;(s=d[a++])&&!n.isPropagationStopped();)n.type=a>1?l:p.bindType||h,f=(st._data(s,"events")||{})[n.type]&&st._data(s,"handle"),f&&f.apply(s,r),f=c&&s[c],f&&st.acceptData(s)&&f.apply&&f.apply(s,r)===!1&&n.preventDefault();if(n.type=h,!(o||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===h&&st.nodeName(i,"a")||!st.acceptData(i)||!c||!i[h]||st.isWindow(i))){u=i[c],u&&(i[c]=null),st.event.triggered=h;try{i[h]()}catch(m){}st.event.triggered=t,u&&(i[c]=u)}return n.result}},dispatch:function(e){e=st.event.fix(e);var n,r,i,o,a,s=[],u=nt.call(arguments),l=(st._data(this,"events")||{})[e.type]||[],c=st.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){for(s=st.event.handlers.call(this,e,l),n=0;(o=s[n++])&&!e.isPropagationStopped();)for(e.currentTarget=o.elem,r=0;(a=o.handlers[r++])&&!e.isImmediatePropagationStopped();)(!e.namespace_re||e.namespace_re.test(a.namespace))&&(e.handleObj=a,e.data=a.data,i=((st.event.special[a.origType]||{}).handle||a.handler).apply(o.elem,u),i!==t&&(e.result=i)===!1&&(e.preventDefault(),e.stopPropagation()));return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(l.disabled!==!0||"click"!==e.type){for(i=[],r=0;u>r;r++)a=n[r],o=a.selector+" ",i[o]===t&&(i[o]=a.needsContext?st(o,this).index(l)>=0:st.find(o,this,null,[l]).length),i[o]&&i.push(a);i.length&&s.push({elem:l,handlers:i})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[st.expando])return e;var t,n,r=e,i=st.event.fixHooks[e.type]||{},o=i.props?this.props.concat(i.props):this.props;for(e=new st.Event(r),t=o.length;t--;)n=o[t],e[n]=r[n];return e.target||(e.target=r.srcElement||V),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,i.filter?i.filter(e,r):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,a=n.button,s=n.fromElement;return null==e.pageX&&null!=n.clientX&&(r=e.target.ownerDocument||V,i=r.documentElement,o=r.body,e.pageX=n.clientX+(i&&i.scrollLeft||o&&o.scrollLeft||0)-(i&&i.clientLeft||o&&o.clientLeft||0),e.pageY=n.clientY+(i&&i.scrollTop||o&&o.scrollTop||0)-(i&&i.clientTop||o&&o.clientTop||0)),!e.relatedTarget&&s&&(e.relatedTarget=s===e.target?n.toElement:s),e.which||a===t||(e.which=1&a?1:2&a?3:4&a?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return st.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==V.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===V.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=st.extend(new st.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?st.event.trigger(i,null,t):st.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},st.removeEvent=V.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,n,r){var i="on"+n;e.detachEvent&&(e[i]===t&&(e[i]=null),e.detachEvent(i,r))},st.Event=function(e,n){return this instanceof st.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?u:l):this.type=e,n&&st.extend(this,n),this.timeStamp=e&&e.timeStamp||st.now(),this[st.expando]=!0,t):new st.Event(e,n)},st.Event.prototype={isDefaultPrevented:l,isPropagationStopped:l,isImmediatePropagationStopped:l,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=u,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=u,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u,this.stopPropagation()}},st.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){st.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!st.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),st.support.submitBubbles||(st.event.special.submit={setup:function(){return st.nodeName(this,"form")?!1:(st.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=st.nodeName(n,"input")||st.nodeName(n,"button")?n.form:t;r&&!st._data(r,"submitBubbles")&&(st.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),st._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&st.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return st.nodeName(this,"form")?!1:(st.event.remove(this,"._submit"),t)}}),st.support.changeBubbles||(st.event.special.change={setup:function(){return qt.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(st.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),st.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),st.event.simulate("change",this,e,!0)})),!1):(st.event.add(this,"beforeactivate._change",function(e){var t=e.target;qt.test(t.nodeName)&&!st._data(t,"changeBubbles")&&(st.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||st.event.simulate("change",this.parentNode,e,!0)}),st._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return st.event.remove(this,"._change"),!qt.test(this.nodeName)}}),st.support.focusinBubbles||st.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){st.event.simulate(t,e.target,st.event.fix(e),!0)};st.event.special[t]={setup:function(){0===n++&&V.addEventListener(e,r,!0)},teardown:function(){0===--n&&V.removeEventListener(e,r,!0)}}}),st.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(s in e)this.on(s,n,r,e[s],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=l;else if(!i)return this;return 1===o&&(a=i,i=function(e){return st().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=st.guid++)),this.each(function(){st.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,st(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=l),this.each(function(){st.event.remove(this,e,r,n)})},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)},trigger:function(e,t){return this.each(function(){st.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?st.event.trigger(e,n,r,!0):t},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),st.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){st.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)},_t.test(t)&&(st.event.fixHooks[t]=st.event.keyHooks),Ft.test(t)&&(st.event.fixHooks[t]=st.event.mouseHooks)}),function(e,t){function n(e){return ht.test(e+"")}function r(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>C.cacheLength&&delete e[t.shift()],e[n]=r}}function i(e){return e[P]=!0,e}function o(e){var t=L.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function a(e,t,n,r){var i,o,a,s,u,l,c,d,h,g;if((t?t.ownerDocument||t:R)!==L&&D(t),t=t||L,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!M&&!r){if(i=gt.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&O(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return Q.apply(n,K.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&W.getByClassName&&t.getElementsByClassName)return Q.apply(n,K.call(t.getElementsByClassName(a),0)),n}if(W.qsa&&!q.test(e)){if(c=!0,d=P,h=t,g=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){for(l=f(e),(c=t.getAttribute("id"))?d=c.replace(vt,"\\$&"):t.setAttribute("id",d),d="[id='"+d+"'] ",u=l.length;u--;)l[u]=d+p(l[u]);h=dt.test(e)&&t.parentNode||t,g=l.join(",")}if(g)try{return Q.apply(n,K.call(h.querySelectorAll(g),0)),n}catch(m){}finally{c||t.removeAttribute("id")}}}return x(e.replace(at,"$1"),t,n,r)}function s(e,t){for(var n=e&&t&&e.nextSibling;n;n=n.nextSibling)if(n===t)return-1;return e?1:-1}function u(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function l(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function c(e){return i(function(t){return t=+t,i(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function f(e,t){var n,r,i,o,s,u,l,c=X[e+" "];if(c)return t?0:c.slice(0);for(s=e,u=[],l=C.preFilter;s;){(!n||(r=ut.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(i=[])),n=!1,(r=lt.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(at," ")}),s=s.slice(n.length));for(o in C.filter)!(r=pt[o].exec(s))||l[o]&&!(r=l[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?a.error(e):X(e,u).slice(0)}function p(e){for(var t=0,n=e.length,r="";n>t;t++)r+=e[t].value;return r}function d(e,t,n){var r=t.dir,i=n&&"parentNode"===t.dir,o=I++;return t.first?function(t,n,o){for(;t=t[r];)if(1===t.nodeType||i)return e(t,n,o)}:function(t,n,a){var s,u,l,c=$+" "+o;if(a){for(;t=t[r];)if((1===t.nodeType||i)&&e(t,n,a))return!0}else for(;t=t[r];)if(1===t.nodeType||i)if(l=t[P]||(t[P]={}),(u=l[r])&&u[0]===c){if((s=u[1])===!0||s===N)return s===!0}else if(u=l[r]=[c],u[1]=e(t,n,a)||N,u[1]===!0)return!0}}function h(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function g(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function m(e,t,n,r,o,a){return r&&!r[P]&&(r=m(r)),o&&!o[P]&&(o=m(o,a)),i(function(i,a,s,u){var l,c,f,p=[],d=[],h=a.length,m=i||b(t||"*",s.nodeType?[s]:s,[]),y=!e||!i&&t?m:g(m,p,e,s,u),v=n?o||(i?e:h||r)?[]:a:y;if(n&&n(y,v,s,u),r)for(l=g(v,d),r(l,[],s,u),c=l.length;c--;)(f=l[c])&&(v[d[c]]=!(y[d[c]]=f));if(i){if(o||e){if(o){for(l=[],c=v.length;c--;)(f=v[c])&&l.push(y[c]=f);o(null,v=[],l,u)}for(c=v.length;c--;)(f=v[c])&&(l=o?Z.call(i,f):p[c])>-1&&(i[l]=!(a[l]=f))}}else v=g(v===a?v.splice(h,v.length):v),o?o(null,a,v,u):Q.apply(a,v)})}function y(e){for(var t,n,r,i=e.length,o=C.relative[e[0].type],a=o||C.relative[" "],s=o?1:0,u=d(function(e){return e===t},a,!0),l=d(function(e){return Z.call(t,e)>-1},a,!0),c=[function(e,n,r){return!o&&(r||n!==j)||((t=n).nodeType?u(e,n,r):l(e,n,r))}];i>s;s++)if(n=C.relative[e[s].type])c=[d(h(c),n)];else{if(n=C.filter[e[s].type].apply(null,e[s].matches),n[P]){for(r=++s;i>r&&!C.relative[e[r].type];r++);return m(s>1&&h(c),s>1&&p(e.slice(0,s-1)).replace(at,"$1"),n,r>s&&y(e.slice(s,r)),i>r&&y(e=e.slice(r)),i>r&&p(e))}c.push(n)}return h(c)}function v(e,t){var n=0,r=t.length>0,o=e.length>0,s=function(i,s,u,l,c){var f,p,d,h=[],m=0,y="0",v=i&&[],b=null!=c,x=j,T=i||o&&C.find.TAG("*",c&&s.parentNode||s),w=$+=null==x?1:Math.E;for(b&&(j=s!==L&&s,N=n);null!=(f=T[y]);y++){if(o&&f){for(p=0;d=e[p];p++)if(d(f,s,u)){l.push(f);break}b&&($=w,N=++n)}r&&((f=!d&&f)&&m--,i&&v.push(f))}if(m+=y,r&&y!==m){for(p=0;d=t[p];p++)d(v,h,s,u);if(i){if(m>0)for(;y--;)v[y]||h[y]||(h[y]=G.call(l));h=g(h)}Q.apply(l,h),b&&!i&&h.length>0&&m+t.length>1&&a.uniqueSort(l)}return b&&($=w,j=x),v};return r?i(s):s}function b(e,t,n){for(var r=0,i=t.length;i>r;r++)a(e,t[r],n);return n}function x(e,t,n,r){var i,o,a,s,u,l=f(e);if(!r&&1===l.length){if(o=l[0]=l[0].slice(0),o.length>2&&"ID"===(a=o[0]).type&&9===t.nodeType&&!M&&C.relative[o[1].type]){if(t=C.find.ID(a.matches[0].replace(xt,Tt),t)[0],!t)return n;e=e.slice(o.shift().value.length)}for(i=pt.needsContext.test(e)?-1:o.length-1;i>=0&&(a=o[i],!C.relative[s=a.type]);i--)if((u=C.find[s])&&(r=u(a.matches[0].replace(xt,Tt),dt.test(o[0].type)&&t.parentNode||t))){if(o.splice(i,1),e=r.length&&p(o),!e)return Q.apply(n,K.call(r,0)),n;break}}return S(e,l)(r,t,M,n,dt.test(e)),n}function T(){}var w,N,C,k,E,S,A,j,D,L,H,M,q,_,F,O,B,P="sizzle"+-new Date,R=e.document,W={},$=0,I=0,z=r(),X=r(),U=r(),V=typeof t,Y=1<<31,J=[],G=J.pop,Q=J.push,K=J.slice,Z=J.indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(this[t]===e)return t;return-1},et="[\\x20\\t\\r\\n\\f]",tt="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",nt=tt.replace("w","w#"),rt="([*^$|!~]?=)",it="\\["+et+"*("+tt+")"+et+"*(?:"+rt+et+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+nt+")|)|)"+et+"*\\]",ot=":("+tt+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+it.replace(3,8)+")*)|.*)\\)|)",at=RegExp("^"+et+"+|((?:^|[^\\\\])(?:\\\\.)*)"+et+"+$","g"),ut=RegExp("^"+et+"*,"+et+"*"),lt=RegExp("^"+et+"*([\\x20\\t\\r\\n\\f>+~])"+et+"*"),ct=RegExp(ot),ft=RegExp("^"+nt+"$"),pt={ID:RegExp("^#("+tt+")"),CLASS:RegExp("^\\.("+tt+")"),NAME:RegExp("^\\[name=['\"]?("+tt+")['\"]?\\]"),TAG:RegExp("^("+tt.replace("w","w*")+")"),ATTR:RegExp("^"+it),PSEUDO:RegExp("^"+ot),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+et+"*(even|odd|(([+-]|)(\\d*)n|)"+et+"*(?:([+-]|)"+et+"*(\\d+)|))"+et+"*\\)|)","i"),needsContext:RegExp("^"+et+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+et+"*((?:-\\d)?\\d*)"+et+"*\\)|)(?=[^-]|$)","i")},dt=/[\x20\t\r\n\f]*[+~]/,ht=/\{\s*\[native code\]\s*\}/,gt=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,mt=/^(?:input|select|textarea|button)$/i,yt=/^h\d$/i,vt=/'|\\/g,bt=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,xt=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,Tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{K.call(H.childNodes,0)[0].nodeType}catch(wt){K=function(e){for(var t,n=[];t=this[e];e++)n.push(t);return n}}E=a.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},D=a.setDocument=function(e){var r=e?e.ownerDocument||e:R;return r!==L&&9===r.nodeType&&r.documentElement?(L=r,H=r.documentElement,M=E(r),W.tagNameNoComments=o(function(e){return e.appendChild(r.createComment("")),!e.getElementsByTagName("*").length}),W.attributes=o(function(e){e.innerHTML="<select></select>";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),W.getByClassName=o(function(e){return e.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),W.getByName=o(function(e){e.id=P+0,e.innerHTML="<a name='"+P+"'></a><div name='"+P+"'></div>",H.insertBefore(e,H.firstChild);var t=r.getElementsByName&&r.getElementsByName(P).length===2+r.getElementsByName(P+0).length;return W.getIdNotName=!r.getElementById(P),H.removeChild(e),t}),C.attrHandle=o(function(e){return e.innerHTML="<a href='#'></a>",e.firstChild&&typeof e.firstChild.getAttribute!==V&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},W.getIdNotName?(C.find.ID=function(e,t){if(typeof t.getElementById!==V&&!M){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},C.filter.ID=function(e){var t=e.replace(xt,Tt);return function(e){return e.getAttribute("id")===t}}):(C.find.ID=function(e,n){if(typeof n.getElementById!==V&&!M){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==V&&r.getAttributeNode("id").value===e?[r]:t:[]}},C.filter.ID=function(e){var t=e.replace(xt,Tt);return function(e){var n=typeof e.getAttributeNode!==V&&e.getAttributeNode("id");return n&&n.value===t}}),C.find.TAG=W.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==V?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i];i++)1===n.nodeType&&r.push(n);return r}return o},C.find.NAME=W.getByName&&function(e,n){return typeof n.getElementsByName!==V?n.getElementsByName(name):t},C.find.CLASS=W.getByClassName&&function(e,n){return typeof n.getElementsByClassName===V||M?t:n.getElementsByClassName(e)},_=[],q=[":focus"],(W.qsa=n(r.querySelectorAll))&&(o(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||q.push("\\["+et+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||q.push(":checked")}),o(function(e){e.innerHTML="<input type='hidden' i=''/>",e.querySelectorAll("[i^='']").length&&q.push("[*^$]="+et+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),q.push(",.*:")})),(W.matchesSelector=n(F=H.matchesSelector||H.mozMatchesSelector||H.webkitMatchesSelector||H.oMatchesSelector||H.msMatchesSelector))&&o(function(e){W.disconnectedMatch=F.call(e,"div"),F.call(e,"[s!='']:x"),_.push("!=",ot)}),q=RegExp(q.join("|")),_=RegExp(_.join("|")),O=n(H.contains)||H.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)for(;t=t.parentNode;)if(t===e)return!0;return!1},B=H.compareDocumentPosition?function(e,t){var n;return e===t?(A=!0,0):(n=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&n||e.parentNode&&11===e.parentNode.nodeType?e===r||O(R,e)?-1:t===r||O(R,t)?1:0:4&n?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var n,i=0,o=e.parentNode,a=t.parentNode,u=[e],l=[t];if(e===t)return A=!0,0;if(e.sourceIndex&&t.sourceIndex)return(~t.sourceIndex||Y)-(O(R,e)&&~e.sourceIndex||Y);if(!o||!a)return e===r?-1:t===r?1:o?-1:a?1:0;if(o===a)return s(e,t);for(n=e;n=n.parentNode;)u.unshift(n);for(n=t;n=n.parentNode;)l.unshift(n);for(;u[i]===l[i];)i++;return i?s(u[i],l[i]):u[i]===R?-1:l[i]===R?1:0},A=!1,[0,0].sort(B),W.detectDuplicates=A,L):L},a.matches=function(e,t){return a(e,null,null,t)},a.matchesSelector=function(e,t){if((e.ownerDocument||e)!==L&&D(e),t=t.replace(bt,"='$1']"),!(!W.matchesSelector||M||_&&_.test(t)||q.test(t)))try{var n=F.call(e,t);if(n||W.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return a(t,L,null,[e]).length>0},a.contains=function(e,t){return(e.ownerDocument||e)!==L&&D(e),O(e,t)},a.attr=function(e,t){var n;return(e.ownerDocument||e)!==L&&D(e),M||(t=t.toLowerCase()),(n=C.attrHandle[t])?n(e):M||W.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},a.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},a.uniqueSort=function(e){var t,n=[],r=1,i=0;if(A=!W.detectDuplicates,e.sort(B),A){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));for(;i--;)e.splice(n[i],1)}return e},k=a.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+=k(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=k(t);return n},C=a.selectors={cacheLength:50,createPseudo:i,match:pt,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(xt,Tt),e[3]=(e[4]||e[5]||"").replace(xt,Tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||a.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]&&a.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return pt.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&ct.test(n)&&(t=f(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){return"*"===e?function(){return!0}:(e=e.replace(xt,Tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=z[e+" "];return t||(t=RegExp("(^|"+et+")"+e+"("+et+"|$)"))&&z(e,function(e){return t.test(e.className||typeof e.getAttribute!==V&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=a.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.substr(i.length-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){for(;g;){for(f=t;f=f[g];)if(s?f.nodeName.toLowerCase()===y:1===f.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){for(c=m[P]||(m[P]={}),l=c[e]||[],d=l[0]===$&&l[1],p=l[0]===$&&l[2],f=d&&m.childNodes[d];f=++d&&f&&f[g]||(p=d=0)||h.pop();)if(1===f.nodeType&&++p&&f===t){c[e]=[$,d,p];break}}else if(v&&(l=(t[P]||(t[P]={}))[e])&&l[0]===$)p=l[1];else for(;(f=++d&&f&&f[g]||(p=d=0)||h.pop())&&((s?f.nodeName.toLowerCase()!==y:1!==f.nodeType)||!++p||(v&&((f[P]||(f[P]={}))[e]=[$,p]),f!==t)););return p-=i,p===r||0===p%r&&p/r>=0}}},PSEUDO:function(e,t){var n,r=C.pseudos[e]||C.setFilters[e.toLowerCase()]||a.error("unsupported pseudo: "+e);return r[P]?r(t):r.length>1?(n=[e,e,"",t],C.setFilters.hasOwnProperty(e.toLowerCase())?i(function(e,n){for(var i,o=r(e,t),a=o.length;a--;)i=Z.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:i(function(e){var t=[],n=[],r=S(e.replace(at,"$1"));return r[P]?i(function(e,t,n,i){for(var o,a=r(e,null,i,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:i(function(e){return function(t){return a(e,t).length>0}}),contains:i(function(e){return function(t){return(t.textContent||t.innerText||k(t)).indexOf(e)>-1}}),lang:i(function(e){return ft.test(e||"")||a.error("unsupported lang: "+e),e=e.replace(xt,Tt).toLowerCase(),function(t){var n;do if(n=M?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.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===H},focus:function(e){return e===L.activeElement&&(!L.hasFocus||L.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!C.pseudos.empty(e)},header:function(e){return yt.test(e.nodeName)},input:function(e){return mt.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:c(function(){return[0]}),last:c(function(e,t){return[t-1]}),eq:c(function(e,t,n){return[0>n?n+t:n]}),even:c(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:c(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:c(function(e,t,n){for(var r=0>n?n+t:n;--r>=0;)e.push(r);return e}),gt:c(function(e,t,n){for(var r=0>n?n+t:n;t>++r;)e.push(r);return e})}};for(w in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})C.pseudos[w]=u(w);for(w in{submit:!0,reset:!0})C.pseudos[w]=l(w);S=a.compile=function(e,t){var n,r=[],i=[],o=U[e+" "];if(!o){for(t||(t=f(e)),n=t.length;n--;)o=y(t[n]),o[P]?r.push(o):i.push(o);o=U(e,v(i,r))}return o},C.pseudos.nth=C.pseudos.eq,C.filters=T.prototype=C.pseudos,C.setFilters=new T,D(),a.attr=st.attr,st.find=a,st.expr=a.selectors,st.expr[":"]=st.expr.pseudos,st.unique=a.uniqueSort,st.text=a.getText,st.isXMLDoc=a.isXML,st.contains=a.contains}(e);var Pt=/Until$/,Rt=/^(?:parents|prev(?:Until|All))/,Wt=/^.[^:#\[\.,]*$/,$t=st.expr.match.needsContext,It={children:!0,contents:!0,next:!0,prev:!0};st.fn.extend({find:function(e){var t,n,r;if("string"!=typeof e)return r=this,this.pushStack(st(e).filter(function(){for(t=0;r.length>t;t++)if(st.contains(r[t],this))return!0}));for(n=[],t=0;this.length>t;t++)st.find(e,this[t],n);return n=this.pushStack(st.unique(n)),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=st(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(st.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(f(this,e,!1))},filter:function(e){return this.pushStack(f(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?$t.test(e)?st(e,this.context).index(this[0])>=0:st.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){for(var n,r=0,i=this.length,o=[],a=$t.test(e)||"string"!=typeof e?st(e,t||this.context):0;i>r;r++)for(n=this[r];n&&n.ownerDocument&&n!==t&&11!==n.nodeType;){if(a?a.index(n)>-1:st.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}return this.pushStack(o.length>1?st.unique(o):o)},index:function(e){return e?"string"==typeof e?st.inArray(this[0],st(e)):st.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?st(e,t):st.makeArray(e&&e.nodeType?[e]:e),r=st.merge(this.get(),n);return this.pushStack(st.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),st.fn.andSelf=st.fn.addBack,st.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return st.dir(e,"parentNode")},parentsUntil:function(e,t,n){return st.dir(e,"parentNode",n)},next:function(e){return c(e,"nextSibling")},prev:function(e){return c(e,"previousSibling") |
|
3 |
-},nextAll:function(e){return st.dir(e,"nextSibling")},prevAll:function(e){return st.dir(e,"previousSibling")},nextUntil:function(e,t,n){return st.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return st.dir(e,"previousSibling",n)},siblings:function(e){return st.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return st.sibling(e.firstChild)},contents:function(e){return st.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:st.merge([],e.childNodes)}},function(e,t){st.fn[e]=function(n,r){var i=st.map(this,t,n);return Pt.test(e)||(r=n),r&&"string"==typeof r&&(i=st.filter(r,i)),i=this.length>1&&!It[e]?st.unique(i):i,this.length>1&&Rt.test(e)&&(i=i.reverse()),this.pushStack(i)}}),st.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?st.find.matchesSelector(t[0],e)?[t[0]]:[]:st.find.matches(e,t)},dir:function(e,n,r){for(var i=[],o=e[n];o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!st(o).is(r));)1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});var zt="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",Xt=/ jQuery\d+="(?:null|\d+)"/g,Ut=RegExp("<(?:"+zt+")[\\s/>]","i"),Vt=/^\s+/,Yt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,Jt=/<([\w:]+)/,Gt=/<tbody/i,Qt=/<|&#?\w+;/,Kt=/<(?:script|style|link)/i,Zt=/^(?:checkbox|radio)$/i,en=/checked\s*(?:[^=]|=\s*.checked.)/i,tn=/^$|\/(?:java|ecma)script/i,nn=/^true\/(.*)/,rn=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,on={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:st.support.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},an=p(V),sn=an.appendChild(V.createElement("div"));on.optgroup=on.option,on.tbody=on.tfoot=on.colgroup=on.caption=on.thead,on.th=on.td,st.fn.extend({text:function(e){return st.access(this,function(e){return e===t?st.text(this):this.empty().append((this[0]&&this[0].ownerDocument||V).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(st.isFunction(e))return this.each(function(t){st(this).wrapAll(e.call(this,t))});if(this[0]){var t=st(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return st.isFunction(e)?this.each(function(t){st(this).wrapInner(e.call(this,t))}):this.each(function(){var t=st(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=st.isFunction(e);return this.each(function(n){st(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){st.nodeName(this,"body")||st(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){for(var n,r=0;null!=(n=this[r]);r++)(!e||st.filter(e,[n]).length>0)&&(t||1!==n.nodeType||st.cleanData(b(n)),n.parentNode&&(t&&st.contains(n.ownerDocument,n)&&m(b(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&st.cleanData(b(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&st.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return st.clone(this,e,t)})},html:function(e){return st.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(Xt,""):t;if(!("string"!=typeof e||Kt.test(e)||!st.support.htmlSerialize&&Ut.test(e)||!st.support.leadingWhitespace&&Vt.test(e)||on[(Jt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(Yt,"<$1></$2>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(st.cleanData(b(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=st.isFunction(e);return t||"string"==typeof e||(e=st(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;(n&&1===this.nodeType||11===this.nodeType)&&(st(this).remove(),t?t.parentNode.insertBefore(e,t):n.appendChild(e))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=et.apply([],e);var i,o,a,s,u,l,c=0,f=this.length,p=this,m=f-1,y=e[0],v=st.isFunction(y);if(v||!(1>=f||"string"!=typeof y||st.support.checkClone)&&en.test(y))return this.each(function(i){var o=p.eq(i);v&&(e[0]=y.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(f&&(i=st.buildFragment(e,this[0].ownerDocument,!1,this),o=i.firstChild,1===i.childNodes.length&&(i=o),o)){for(n=n&&st.nodeName(o,"tr"),a=st.map(b(i,"script"),h),s=a.length;f>c;c++)u=i,c!==m&&(u=st.clone(u,!0,!0),s&&st.merge(a,b(u,"script"))),r.call(n&&st.nodeName(this[c],"table")?d(this[c],"tbody"):this[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,st.map(a,g),c=0;s>c;c++)u=a[c],tn.test(u.type||"")&&!st._data(u,"globalEval")&&st.contains(l,u)&&(u.src?st.ajax({url:u.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):st.globalEval((u.text||u.textContent||u.innerHTML||"").replace(rn,"")));i=o=null}return this}}),st.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){st.fn[e]=function(e){for(var n,r=0,i=[],o=st(e),a=o.length-1;a>=r;r++)n=r===a?this:this.clone(!0),st(o[r])[t](n),tt.apply(i,n.get());return this.pushStack(i)}}),st.extend({clone:function(e,t,n){var r,i,o,a,s,u=st.contains(e.ownerDocument,e);if(st.support.html5Clone||st.isXMLDoc(e)||!Ut.test("<"+e.nodeName+">")?s=e.cloneNode(!0):(sn.innerHTML=e.outerHTML,sn.removeChild(s=sn.firstChild)),!(st.support.noCloneEvent&&st.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||st.isXMLDoc(e)))for(r=b(s),i=b(e),a=0;null!=(o=i[a]);++a)r[a]&&v(o,r[a]);if(t)if(n)for(i=i||b(e),r=r||b(s),a=0;null!=(o=i[a]);a++)y(o,r[a]);else y(e,s);return r=b(s,"script"),r.length>0&&m(r,!u&&b(e,"script")),r=i=o=null,s},buildFragment:function(e,t,n,r){for(var i,o,a,s,u,l,c,f=e.length,d=p(t),h=[],g=0;f>g;g++)if(o=e[g],o||0===o)if("object"===st.type(o))st.merge(h,o.nodeType?[o]:o);else if(Qt.test(o)){for(s=s||d.appendChild(t.createElement("div")),a=(Jt.exec(o)||["",""])[1].toLowerCase(),u=on[a]||on._default,s.innerHTML=u[1]+o.replace(Yt,"<$1></$2>")+u[2],c=u[0];c--;)s=s.lastChild;if(!st.support.leadingWhitespace&&Vt.test(o)&&h.push(t.createTextNode(Vt.exec(o)[0])),!st.support.tbody)for(o="table"!==a||Gt.test(o)?"<table>"!==u[1]||Gt.test(o)?0:s:s.firstChild,c=o&&o.childNodes.length;c--;)st.nodeName(l=o.childNodes[c],"tbody")&&!l.childNodes.length&&o.removeChild(l);for(st.merge(h,s.childNodes),s.textContent="";s.firstChild;)s.removeChild(s.firstChild);s=d.lastChild}else h.push(t.createTextNode(o));for(s&&d.removeChild(s),st.support.appendChecked||st.grep(b(h,"input"),x),g=0;o=h[g++];)if((!r||-1===st.inArray(o,r))&&(i=st.contains(o.ownerDocument,o),s=b(d.appendChild(o),"script"),i&&m(s),n))for(c=0;o=s[c++];)tn.test(o.type||"")&&n.push(o);return s=null,d},cleanData:function(e,n){for(var r,i,o,a,s=0,u=st.expando,l=st.cache,c=st.support.deleteExpando,f=st.event.special;null!=(o=e[s]);s++)if((n||st.acceptData(o))&&(i=o[u],r=i&&l[i])){if(r.events)for(a in r.events)f[a]?st.event.remove(o,a):st.removeEvent(o,a,r.handle);l[i]&&(delete l[i],c?delete o[u]:o.removeAttribute!==t?o.removeAttribute(u):o[u]=null,K.push(i))}}});var un,ln,cn,fn=/alpha\([^)]*\)/i,pn=/opacity\s*=\s*([^)]*)/,dn=/^(top|right|bottom|left)$/,hn=/^(none|table(?!-c[ea]).+)/,gn=/^margin/,mn=RegExp("^("+ut+")(.*)$","i"),yn=RegExp("^("+ut+")(?!px)[a-z%]+$","i"),vn=RegExp("^([+-])=("+ut+")","i"),bn={BODY:"block"},xn={position:"absolute",visibility:"hidden",display:"block"},Tn={letterSpacing:0,fontWeight:400},wn=["Top","Right","Bottom","Left"],Nn=["Webkit","O","Moz","ms"];st.fn.extend({css:function(e,n){return st.access(this,function(e,n,r){var i,o,a={},s=0;if(st.isArray(n)){for(i=ln(e),o=n.length;o>s;s++)a[n[s]]=st.css(e,n[s],!1,i);return a}return r!==t?st.style(e,n,r):st.css(e,n)},e,n,arguments.length>1)},show:function(){return N(this,!0)},hide:function(){return N(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:w(this))?st(this).show():st(this).hide()})}}),st.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=un(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":st.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=st.camelCase(n),l=e.style;if(n=st.cssProps[u]||(st.cssProps[u]=T(l,u)),s=st.cssHooks[n]||st.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=vn.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(st.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||st.cssNumber[u]||(r+="px"),st.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=st.camelCase(n);return n=st.cssProps[u]||(st.cssProps[u]=T(e.style,u)),s=st.cssHooks[n]||st.cssHooks[u],s&&"get"in s&&(o=s.get(e,!0,r)),o===t&&(o=un(e,n,i)),"normal"===o&&n in Tn&&(o=Tn[n]),r?(a=parseFloat(o),r===!0||st.isNumeric(a)?a||0:o):o},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(ln=function(t){return e.getComputedStyle(t,null)},un=function(e,n,r){var i,o,a,s=r||ln(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||st.contains(e.ownerDocument,e)||(u=st.style(e,n)),yn.test(u)&&gn.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):V.documentElement.currentStyle&&(ln=function(e){return e.currentStyle},un=function(e,n,r){var i,o,a,s=r||ln(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),yn.test(u)&&!dn.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u}),st.each(["height","width"],function(e,n){st.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&hn.test(st.css(e,"display"))?st.swap(e,xn,function(){return E(e,n,i)}):E(e,n,i):t},set:function(e,t,r){var i=r&&ln(e);return C(e,t,r?k(e,n,r,st.support.boxSizing&&"border-box"===st.css(e,"boxSizing",!1,i),i):0)}}}),st.support.opacity||(st.cssHooks.opacity={get:function(e,t){return pn.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=st.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===st.trim(o.replace(fn,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=fn.test(o)?o.replace(fn,i):o+" "+i)}}),st(function(){st.support.reliableMarginRight||(st.cssHooks.marginRight={get:function(e,n){return n?st.swap(e,{display:"inline-block"},un,[e,"marginRight"]):t}}),!st.support.pixelPosition&&st.fn.position&&st.each(["top","left"],function(e,n){st.cssHooks[n]={get:function(e,r){return r?(r=un(e,n),yn.test(r)?st(e).position()[n]+"px":r):t}}})}),st.expr&&st.expr.filters&&(st.expr.filters.hidden=function(e){return 0===e.offsetWidth&&0===e.offsetHeight||!st.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||st.css(e,"display"))},st.expr.filters.visible=function(e){return!st.expr.filters.hidden(e)}),st.each({margin:"",padding:"",border:"Width"},function(e,t){st.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];4>r;r++)i[e+wn[r]+t]=o[r]||o[r-2]||o[0];return i}},gn.test(e)||(st.cssHooks[e+t].set=C)});var Cn=/%20/g,kn=/\[\]$/,En=/\r?\n/g,Sn=/^(?:submit|button|image|reset)$/i,An=/^(?:input|select|textarea|keygen)/i;st.fn.extend({serialize:function(){return st.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=st.prop(this,"elements");return e?st.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!st(this).is(":disabled")&&An.test(this.nodeName)&&!Sn.test(e)&&(this.checked||!Zt.test(e))}).map(function(e,t){var n=st(this).val();return null==n?null:st.isArray(n)?st.map(n,function(e){return{name:t.name,value:e.replace(En,"\r\n")}}):{name:t.name,value:n.replace(En,"\r\n")}}).get()}}),st.param=function(e,n){var r,i=[],o=function(e,t){t=st.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=st.ajaxSettings&&st.ajaxSettings.traditional),st.isArray(e)||e.jquery&&!st.isPlainObject(e))st.each(e,function(){o(this.name,this.value)});else for(r in e)j(r,e[r],n,o);return i.join("&").replace(Cn,"+")};var jn,Dn,Ln=st.now(),Hn=/\?/,Mn=/#.*$/,qn=/([?&])_=[^&]*/,_n=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Fn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,On=/^(?:GET|HEAD)$/,Bn=/^\/\//,Pn=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Rn=st.fn.load,Wn={},$n={},In="*/".concat("*");try{Dn=Y.href}catch(zn){Dn=V.createElement("a"),Dn.href="",Dn=Dn.href}jn=Pn.exec(Dn.toLowerCase())||[],st.fn.load=function(e,n,r){if("string"!=typeof e&&Rn)return Rn.apply(this,arguments);var i,o,a,s=this,u=e.indexOf(" ");return u>=0&&(i=e.slice(u,e.length),e=e.slice(0,u)),st.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(o="POST"),s.length>0&&st.ajax({url:e,type:o,dataType:"html",data:n}).done(function(e){a=arguments,s.html(i?st("<div>").append(st.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,a||[e.responseText,t,e])}),this},st.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){st.fn[t]=function(e){return this.on(t,e)}}),st.each(["get","post"],function(e,n){st[n]=function(e,r,i,o){return st.isFunction(r)&&(o=o||i,i=r,r=t),st.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),st.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Dn,type:"GET",isLocal:Fn.test(jn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":In,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"},converters:{"* text":e.String,"text html":!0,"text json":st.parseJSON,"text xml":st.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?H(H(e,st.ajaxSettings),t):H(st.ajaxSettings,e)},ajaxPrefilter:D(Wn),ajaxTransport:D($n),ajax:function(e,n){function r(e,n,r,s){var l,f,v,b,T,N=n;2!==x&&(x=2,u&&clearTimeout(u),i=t,a=s||"",w.readyState=e>0?4:0,r&&(b=M(p,w,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=w.getResponseHeader("Last-Modified"),T&&(st.lastModified[o]=T),T=w.getResponseHeader("etag"),T&&(st.etag[o]=T)),304===e?(l=!0,N="notmodified"):(l=q(p,b),N=l.state,f=l.data,v=l.error,l=!v)):(v=N,(e||!N)&&(N="error",0>e&&(e=0))),w.status=e,w.statusText=(n||N)+"",l?g.resolveWith(d,[f,N,w]):g.rejectWith(d,[w,N,v]),w.statusCode(y),y=t,c&&h.trigger(l?"ajaxSuccess":"ajaxError",[w,p,l?f:v]),m.fireWith(d,[w,N]),c&&(h.trigger("ajaxComplete",[w,p]),--st.active||st.event.trigger("ajaxStop")))}"object"==typeof e&&(n=e,e=t),n=n||{};var i,o,a,s,u,l,c,f,p=st.ajaxSetup({},n),d=p.context||p,h=p.context&&(d.nodeType||d.jquery)?st(d):st.event,g=st.Deferred(),m=st.Callbacks("once memory"),y=p.statusCode||{},v={},b={},x=0,T="canceled",w={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!s)for(s={};t=_n.exec(a);)s[t[1].toLowerCase()]=t[2];t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=b[n]=b[n]||e,v[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)y[t]=[y[t],e[t]];else w.always(e[w.status]);return this},abort:function(e){var t=e||T;return i&&i.abort(t),r(0,t),this}};if(g.promise(w).complete=m.add,w.success=w.done,w.error=w.fail,p.url=((e||p.url||Dn)+"").replace(Mn,"").replace(Bn,jn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=st.trim(p.dataType||"*").toLowerCase().match(lt)||[""],null==p.crossDomain&&(l=Pn.exec(p.url.toLowerCase()),p.crossDomain=!(!l||l[1]===jn[1]&&l[2]===jn[2]&&(l[3]||("http:"===l[1]?80:443))==(jn[3]||("http:"===jn[1]?80:443)))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=st.param(p.data,p.traditional)),L(Wn,p,n,w),2===x)return w;c=p.global,c&&0===st.active++&&st.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!On.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(Hn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=qn.test(o)?o.replace(qn,"$1_="+Ln++):o+(Hn.test(o)?"&":"?")+"_="+Ln++)),p.ifModified&&(st.lastModified[o]&&w.setRequestHeader("If-Modified-Since",st.lastModified[o]),st.etag[o]&&w.setRequestHeader("If-None-Match",st.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&w.setRequestHeader("Content-Type",p.contentType),w.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+In+"; q=0.01":""):p.accepts["*"]);for(f in p.headers)w.setRequestHeader(f,p.headers[f]);if(p.beforeSend&&(p.beforeSend.call(d,w,p)===!1||2===x))return w.abort();T="abort";for(f in{success:1,error:1,complete:1})w[f](p[f]);if(i=L($n,p,n,w)){w.readyState=1,c&&h.trigger("ajaxSend",[w,p]),p.async&&p.timeout>0&&(u=setTimeout(function(){w.abort("timeout")},p.timeout));try{x=1,i.send(v,r)}catch(N){if(!(2>x))throw N;r(-1,N)}}else r(-1,"No Transport");return w},getScript:function(e,n){return st.get(e,t,n,"script")},getJSON:function(e,t,n){return st.get(e,t,n,"json")}}),st.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return st.globalEval(e),e}}}),st.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),st.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=V.head||st("head")[0]||V.documentElement;return{send:function(t,i){n=V.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var Xn=[],Un=/(=)\?(?=&|$)|\?\?/;st.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xn.pop()||st.expando+"_"+Ln++;return this[e]=!0,e}}),st.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Un.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Un.test(n.data)&&"data");return u||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=st.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Un,"$1"+o):n.jsonp!==!1&&(n.url+=(Hn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||st.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,Xn.push(o)),s&&st.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Vn,Yn,Jn=0,Gn=e.ActiveXObject&&function(){var e;for(e in Vn)Vn[e](t,!0)};st.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&_()||F()}:_,Yn=st.ajaxSettings.xhr(),st.support.cors=!!Yn&&"withCredentials"in Yn,Yn=st.support.ajax=!!Yn,Yn&&st.ajaxTransport(function(n){if(!n.crossDomain||st.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,f,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=st.noop,Gn&&delete Vn[a]),i)4!==u.readyState&&u.abort();else{f={},s=u.status,p=u.responseXML,c=u.getAllResponseHeaders(),p&&p.documentElement&&(f.xml=p),"string"==typeof u.responseText&&(f.text=u.responseText);try{l=u.statusText}catch(d){l=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=f.text?200:404}}catch(h){i||o(-1,h)}f&&o(s,l,f,c)},n.async?4===u.readyState?setTimeout(r):(a=++Jn,Gn&&(Vn||(Vn={},st(e).unload(Gn)),Vn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Qn,Kn,Zn=/^(?:toggle|show|hide)$/,er=RegExp("^(?:([+-])=|)("+ut+")([a-z%]*)$","i"),tr=/queueHooks$/,nr=[W],rr={"*":[function(e,t){var n,r,i=this.createTween(e,t),o=er.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(st.cssNumber[e]?"":"px"),"px"!==r&&s){s=st.css(i.elem,e,!0)||n||1;do u=u||".5",s/=u,st.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};st.Animation=st.extend(P,{tweener:function(e,t){st.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");for(var n,r=0,i=e.length;i>r;r++)n=e[r],rr[n]=rr[n]||[],rr[n].unshift(t)},prefilter:function(e,t){t?nr.unshift(e):nr.push(e)}}),st.Tween=$,$.prototype={constructor:$,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||(st.cssNumber[n]?"":"px")},cur:function(){var e=$.propHooks[this.prop];return e&&e.get?e.get(this):$.propHooks._default.get(this)},run:function(e){var t,n=$.propHooks[this.prop];return this.pos=t=this.options.duration?st.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):$.propHooks._default.set(this),this}},$.prototype.init.prototype=$.prototype,$.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=st.css(e.elem,e.prop,"auto"),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){st.fx.step[e.prop]?st.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[st.cssProps[e.prop]]||st.cssHooks[e.prop])?st.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},$.propHooks.scrollTop=$.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},st.each(["toggle","show","hide"],function(e,t){var n=st.fn[t];st.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(I(t,!0),e,r,i)}}),st.fn.extend({fadeTo:function(e,t,n,r){return this.filter(w).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=st.isEmptyObject(e),o=st.speed(t,n,r),a=function(){var t=P(this,st.extend({},e),o);a.finish=function(){t.stop(!0)},(i||st._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=st.timers,a=st._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&tr.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&st.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=st._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=st.timers,a=r?r.length:0;for(n.finish=!0,st.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;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),st.each({slideDown:I("show"),slideUp:I("hide"),slideToggle:I("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){st.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),st.speed=function(e,t,n){var r=e&&"object"==typeof e?st.extend({},e):{complete:n||!n&&t||st.isFunction(e)&&e,duration:e,easing:n&&t||t&&!st.isFunction(t)&&t};return r.duration=st.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in st.fx.speeds?st.fx.speeds[r.duration]:st.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){st.isFunction(r.old)&&r.old.call(this),r.queue&&st.dequeue(this,r.queue)},r},st.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},st.timers=[],st.fx=$.prototype.init,st.fx.tick=function(){var e,n=st.timers,r=0;for(Qn=st.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||st.fx.stop(),Qn=t},st.fx.timer=function(e){e()&&st.timers.push(e)&&st.fx.start()},st.fx.interval=13,st.fx.start=function(){Kn||(Kn=setInterval(st.fx.tick,st.fx.interval))},st.fx.stop=function(){clearInterval(Kn),Kn=null},st.fx.speeds={slow:600,fast:200,_default:400},st.fx.step={},st.expr&&st.expr.filters&&(st.expr.filters.animated=function(e){return st.grep(st.timers,function(t){return e===t.elem}).length}),st.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){st.offset.setOffset(this,e,t)});var n,r,i={top:0,left:0},o=this[0],a=o&&o.ownerDocument;if(a)return n=a.documentElement,st.contains(n,o)?(o.getBoundingClientRect!==t&&(i=o.getBoundingClientRect()),r=z(a),{top:i.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:i.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):i},st.offset={setOffset:function(e,t,n){var r=st.css(e,"position");"static"===r&&(e.style.position="relative");var i,o,a=st(e),s=a.offset(),u=st.css(e,"top"),l=st.css(e,"left"),c=("absolute"===r||"fixed"===r)&&st.inArray("auto",[u,l])>-1,f={},p={};c?(p=a.position(),i=p.top,o=p.left):(i=parseFloat(u)||0,o=parseFloat(l)||0),st.isFunction(t)&&(t=t.call(e,n,s)),null!=t.top&&(f.top=t.top-s.top+i),null!=t.left&&(f.left=t.left-s.left+o),"using"in t?t.using.call(e,f):a.css(f)}},st.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===st.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),st.nodeName(e[0],"html")||(n=e.offset()),n.top+=st.css(e[0],"borderTopWidth",!0),n.left+=st.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-st.css(r,"marginTop",!0),left:t.left-n.left-st.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent||V.documentElement;e&&!st.nodeName(e,"html")&&"static"===st.css(e,"position");)e=e.offsetParent;return e||V.documentElement})}}),st.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);st.fn[e]=function(i){return st.access(this,function(e,i,o){var a=z(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?st(a).scrollLeft():o,r?o:st(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}}),st.each({Height:"height",Width:"width"},function(e,n){st.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){st.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return st.access(this,function(n,r,i){var o;return st.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?st.css(n,r,s):st.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=st,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return st})})(window); |
|
4 |
-//@ sourceMappingURL=jquery.min.map |
|
1 |
+/*! jQuery v2.0.1 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license |
|
2 |
+//@ sourceMappingURL=jquery-2.0.1.min.map |
|
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); |
... | ... |
@@ -1,2 +1,2 @@ |
1 |
-PR.registerLangHandler(PR.createSimpleLexer([["com",/^#[^\n\r]*/,null,"#"],["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r �\xa0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,null,'"']],[["kwd",/^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\s/, |
|
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["com",/^#[^\n\r]*/,null,"#"],["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,null,'"']],[["kwd",/^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\s/, |
|
2 | 2 |
null],["typ",/^(?:-?GENADR|=MINUS|2BCADR|VN|BOF|MM|-?2CADR|-?[1-6]DNADR|ADRES|BBCON|[ES]?BANK=?|BLOCK|BNKSUM|E?CADR|COUNT\*?|2?DEC\*?|-?DNCHAN|-?DNPTR|EQUALS|ERASE|MEMORY|2?OCT|REMADR|SETLOC|SUBRO|ORG|BSS|BES|SYN|EQU|DEFINE|END)\s/,null],["lit",/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[!-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["apollo","agc","aea"]); |
... | ... |
@@ -0,0 +1,3 @@ |
1 |
+var a=null; |
|
2 |
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^"(?:[^\n\r"\\]|\\.)*(?:"|$)/,a,'"'],["pln",/^\s+/,a," \r\n\t\u00a0"]],[["com",/^REM[^\n\r]*/,a],["kwd",/^\b(?:AND|CLOSE|CLR|CMD|CONT|DATA|DEF ?FN|DIM|END|FOR|GET|GOSUB|GOTO|IF|INPUT|LET|LIST|LOAD|NEW|NEXT|NOT|ON|OPEN|OR|POKE|PRINT|READ|RESTORE|RETURN|RUN|SAVE|STEP|STOP|SYS|THEN|TO|VERIFY|WAIT)\b/,a],["pln",/^[a-z][^\W_]?(?:\$|%)?/i,a],["lit",/^(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?/i,a,"0123456789"],["pun", |
|
3 |
+/^.[^\s\w"$%.]*/,a]]),["basic","cbm"]); |
... | ... |
@@ -14,5 +14,5 @@ |
14 | 14 |
limitations under the License. |
15 | 15 |
*/ |
16 | 16 |
var a=null; |
17 |
-PR.registerLangHandler(PR.createSimpleLexer([["opn",/^[([{]+/,a,"([{"],["clo",/^[)\]}]+/,a,")]}"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \xa0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:def|if|do|let|quote|var|fn|loop|recur|throw|try|monitor-enter|monitor-exit|defmacro|defn|defn-|macroexpand|macroexpand-1|for|doseq|dosync|dotimes|and|or|when|not|assert|doto|proxy|defstruct|first|rest|cons|defprotocol|deftype|defrecord|reify|defmulti|defmethod|meta|with-meta|ns|in-ns|create-ns|import|intern|refer|alias|namespace|resolve|ref|deref|refset|new|set!|memfn|to-array|into-array|aset|gen-class|reduce|map|filter|find|nil?|empty?|hash-map|hash-set|vec|vector|seq|flatten|reverse|assoc|dissoc|list|list?|disj|get|union|difference|intersection|extend|extend-type|extend-protocol|prn)\b/,a], |
|
17 |
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^[([{]+/,a,"([{"],["clo",/^[)\]}]+/,a,")]}"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \u00a0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:def|if|do|let|quote|var|fn|loop|recur|throw|try|monitor-enter|monitor-exit|defmacro|defn|defn-|macroexpand|macroexpand-1|for|doseq|dosync|dotimes|and|or|when|not|assert|doto|proxy|defstruct|first|rest|cons|defprotocol|deftype|defrecord|reify|defmulti|defmethod|meta|with-meta|ns|in-ns|create-ns|import|intern|refer|alias|namespace|resolve|ref|deref|refset|new|set!|memfn|to-array|into-array|aset|gen-class|reduce|map|filter|find|nil?|empty?|hash-map|hash-set|vec|vector|seq|flatten|reverse|assoc|dissoc|list|list?|disj|get|union|difference|intersection|extend|extend-type|extend-protocol|prn)\b/,a], |
|
18 | 18 |
["typ",/^:[\dA-Za-z-]+/]]),["clj"]); |
... | ... |
@@ -1,2 +1,2 @@ |
1 |
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", |
|
2 |
-/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); |
|
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n\u000c"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]+)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//], |
|
2 |
+["com",/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}\b/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); |
... | ... |
@@ -0,0 +1,3 @@ |
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"]],[["com",/^#!.*/],["kwd",/^\b(?:import|library|part of|part|as|show|hide)\b/i],["com",/^\/\/.*/],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["kwd",/^\b(?:class|interface)\b/i],["kwd",/^\b(?:assert|break|case|catch|continue|default|do|else|finally|for|if|in|is|new|return|super|switch|this|throw|try|while)\b/i],["kwd",/^\b(?:abstract|const|extends|factory|final|get|implements|native|operator|set|static|typedef|var)\b/i], |
|
2 |
+["typ",/^\b(?:bool|double|dynamic|int|num|object|string|void)\b/i],["kwd",/^\b(?:false|null|true)\b/i],["str",/^r?'''[\S\s]*?[^\\]'''/],["str",/^r?"""[\S\s]*?[^\\]"""/],["str",/^r?'('|[^\n\f\r]*?[^\\]')/],["str",/^r?"("|[^\n\f\r]*?[^\\]")/],["pln",/^[$_a-z]\w*/i],["pun",/^[!%&*+/:<-?^|~-]/],["lit",/^\b0x[\da-f]+/i],["lit",/^\b\d+(?:\.\d*)?(?:e[+-]?\d+)?/i],["lit",/^\b\.\d+(?:e[+-]?\d+)?/i],["pun",/^[(),.;[\]{}]/]]), |
|
3 |
+["dart"]); |
... | ... |
@@ -0,0 +1,2 @@ |
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n\u000b\u000c\r "],["str",/^"(?:[^\n\f\r"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["lit",/^[a-z]\w*/],["lit",/^'(?:[^\n\f\r'\\]|\\[^&])+'?/,null,"'"],["lit",/^\?[^\t\n ({]+/,null,"?"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)/i,null,"0123456789"]],[["com",/^%[^\n]*/],["kwd",/^(?:module|attributes|do|let|in|letrec|apply|call|primop|case|of|end|when|fun|try|catch|receive|after|char|integer|float,atom,string,var)\b/], |
|
2 |
+["kwd",/^-[_a-z]+/],["typ",/^[A-Z_]\w*/],["pun",/^[,.;]/]]),["erlang","erl"]); |
... | ... |
@@ -1 +1 @@ |
1 |
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r �\xa0"],["pln",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])+(?:'|$)|`[^`]*(?:`|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\/\*[\S\s]*?\*\/)/],["pln",/^(?:[^"'/`]|\/(?![*/]))+/]]),["go"]); |
|
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["pln",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])+(?:'|$)|`[^`]*(?:`|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\/\*[\S\s]*?\*\/)/],["pln",/^(?:[^"'/`]|\/(?![*/]))+/]]),["go"]); |
... | ... |
@@ -1,2 +1,2 @@ |
1 |
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n\r "],["str",/^"(?:[^\n\f\r"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["str",/^'(?:[^\n\f\r'\\]|\\[^&])'?/,null,"'"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)/i,null,"0123456789"]],[["com",/^(?:--+[^\n\f\r]*|{-(?:[^-]|-+[^}-])*-})/],["kwd",/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^\d'A-Za-z]|$)/, |
|
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n\u000b\u000c\r "],["str",/^"(?:[^\n\f\r"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["str",/^'(?:[^\n\f\r'\\]|\\[^&])'?/,null,"'"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)/i,null,"0123456789"]],[["com",/^(?:--+[^\n\f\r]*|{-(?:[^-]|-+[^}-])*-})/],["kwd",/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^\d'A-Za-z]|$)/, |
|
2 | 2 |
null],["pln",/^(?:[A-Z][\w']*\.)*[A-Za-z][\w']*/],["pun",/^[^\d\t-\r "'A-Za-z]+/]]),["hs"]); |
... | ... |
@@ -1,3 +1,3 @@ |
1 | 1 |
var a=null; |
2 |
-PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,a,"("],["clo",/^\)+/,a,")"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \xa0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,a], |
|
3 |
-["lit",/^[+-]?(?:[#0]x[\da-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[de][+-]?\d+)?)/i],["lit",/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[_a-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/i],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["cl","el","lisp","scm"]); |
|
2 |
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,a,"("],["clo",/^\)+/,a,")"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \u00a0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,a], |
|
3 |
+["lit",/^[+-]?(?:[#0]x[\da-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[de][+-]?\d+)?)/i],["lit",/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[_a-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/i],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["cl","el","lisp","lsp","scm","ss","rkt"]); |
... | ... |
@@ -0,0 +1 @@ |
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^!?"(?:[^"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["com",/^;[^\n\r]*/,null,";"]],[["pln",/^[!%@](?:[$\-.A-Z_a-z][\w$\-.]*|\d+)/],["kwd",/^[^\W\d]\w*/,null],["lit",/^\d+\.\d+/],["lit",/^(?:\d+|0[Xx][\dA-Fa-f]+)/],["pun",/^[(-*,:<->[\]{}]|\.\.\.$/]]),["llvm","ll"]); |
... | ... |
@@ -1,2 +1,2 @@ |
1 |
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r �\xa0"],["str",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$))/,null,"\"'"]],[["com",/^--(?:\[(=*)\[[\S\s]*?(?:]\1]|$)|[^\n\r]*)/],["str",/^\[(=*)\[[\S\s]*?(?:]\1]|$)/],["kwd",/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i], |
|
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$))/,null,"\"'"]],[["com",/^--(?:\[(=*)\[[\S\s]*?(?:]\1]|$)|[^\n\r]*)/],["str",/^\[(=*)\[[\S\s]*?(?:]\1]|$)/],["kwd",/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i], |
|
2 | 2 |
["pln",/^[_a-z]\w*/i],["pun",/^[^\w\t\n\r \xa0][^\w\t\n\r "'+=\xa0-]*/]]),["lua"]); |
... | ... |
@@ -0,0 +1,6 @@ |
1 |
+var a=null,b=window.PR,c=[[b.PR_PLAIN,/^[\t-\r \xa0]+/,a," \t\r\n\u000b\u000c\u00a0"],[b.PR_COMMENT,/^%{[^%]*%+(?:[^%}][^%]*%+)*}/,a],[b.PR_COMMENT,/^%[^\n\r]*/,a,"%"],["syscmd",/^![^\n\r]*/,a,"!"]],d=[["linecont",/^\.\.\.\s*[\n\r]/,a],["err",/^\?\?\? [^\n\r]*/,a],["wrn",/^Warning: [^\n\r]*/,a],["codeoutput",/^>>\s+/,a],["codeoutput",/^octave:\d+>\s+/,a],["lang-matlab-operators",/^((?:[A-Za-z]\w*(?:\.[A-Za-z]\w*)*|[).\]}])')/,a],["lang-matlab-identifiers",/^([A-Za-z]\w*(?:\.[A-Za-z]\w*)*)(?!')/,a], |
|
2 |
+[b.PR_STRING,/^'(?:[^']|'')*'/,a],[b.PR_LITERAL,/^[+-]?\.?\d+(?:\.\d*)?(?:[Ee][+-]?\d+)?[ij]?/,a],[b.PR_TAG,/^[()[\]{}]/,a],[b.PR_PUNCTUATION,/^[!&*-/:->@\\^|~]/,a]],e=[["lang-matlab-identifiers",/^([A-Za-z]\w*(?:\.[A-Za-z]\w*)*)/,a],[b.PR_TAG,/^[()[\]{}]/,a],[b.PR_PUNCTUATION,/^[!&*-/:->@\\^|~]/,a],["transpose",/^'/,a]]; |
|
3 |
+b.registerLangHandler(b.createSimpleLexer([],[[b.PR_KEYWORD,/^\b(?:break|case|catch|classdef|continue|else|elseif|end|for|function|global|if|otherwise|parfor|persistent|return|spmd|switch|try|while)\b/,a],["const",/^\b(?:true|false|inf|Inf|nan|NaN|eps|pi|ans|nargin|nargout|varargin|varargout)\b/,a],[b.PR_TYPE,/^\b(?:cell|struct|char|double|single|logical|u?int(?:8|16|32|64)|sparse)\b/,a],["fun",/^\b(?:abs|accumarray|acos(?:d|h)?|acot(?:d|h)?|acsc(?:d|h)?|actxcontrol(?:list|select)?|actxGetRunningServer|actxserver|addlistener|addpath|addpref|addtodate|airy|align|alim|all|allchild|alpha|alphamap|amd|ancestor|and|angle|annotation|any|area|arrayfun|asec(?:d|h)?|asin(?:d|h)?|assert|assignin|atan[2dh]?|audiodevinfo|audioplayer|audiorecorder|aufinfo|auread|autumn|auwrite|avifile|aviinfo|aviread|axes|axis|balance|bar(?:3|3h|h)?|base2dec|beep|BeginInvoke|bench|bessel[h-ky]|beta|betainc|betaincinv|betaln|bicg|bicgstab|bicgstabl|bin2dec|bitand|bitcmp|bitget|bitmax|bitnot|bitor|bitset|bitshift|bitxor|blanks|blkdiag|bone|box|brighten|brush|bsxfun|builddocsearchdb|builtin|bvp4c|bvp5c|bvpget|bvpinit|bvpset|bvpxtend|calendar|calllib|callSoapService|camdolly|cameratoolbar|camlight|camlookat|camorbit|campan|campos|camproj|camroll|camtarget|camup|camva|camzoom|cart2pol|cart2sph|cast|cat|caxis|cd|cdf2rdf|cdfepoch|cdfinfo|cdflib(?:.(?:close|closeVar|computeEpoch|computeEpoch16|create|createAttr|createVar|delete|deleteAttr|deleteAttrEntry|deleteAttrgEntry|deleteVar|deleteVarRecords|epoch16Breakdown|epochBreakdown|getAttrEntry|getAttrgEntry|getAttrMaxEntry|getAttrMaxgEntry|getAttrName|getAttrNum|getAttrScope|getCacheSize|getChecksum|getCompression|getCompressionCacheSize|getConstantNames|getConstantValue|getCopyright|getFileBackward|getFormat|getLibraryCopyright|getLibraryVersion|getMajority|getName|getNumAttrEntries|getNumAttrgEntries|getNumAttributes|getNumgAttributes|getReadOnlyMode|getStageCacheSize|getValidate|getVarAllocRecords|getVarBlockingFactor|getVarCacheSize|getVarCompression|getVarData|getVarMaxAllocRecNum|getVarMaxWrittenRecNum|getVarName|getVarNum|getVarNumRecsWritten|getVarPadValue|getVarRecordData|getVarReservePercent|getVarsMaxWrittenRecNum|getVarSparseRecords|getVersion|hyperGetVarData|hyperPutVarData|inquire|inquireAttr|inquireAttrEntry|inquireAttrgEntry|inquireVar|open|putAttrEntry|putAttrgEntry|putVarData|putVarRecordData|renameAttr|renameVar|setCacheSize|setChecksum|setCompression|setCompressionCacheSize|setFileBackward|setFormat|setMajority|setReadOnlyMode|setStageCacheSize|setValidate|setVarAllocBlockRecords|setVarBlockingFactor|setVarCacheSize|setVarCompression|setVarInitialRecs|setVarPadValue|SetVarReservePercent|setVarsCacheSize|setVarSparseRecords))?|cdfread|cdfwrite|ceil|cell2mat|cell2struct|celldisp|cellfun|cellplot|cellstr|cgs|checkcode|checkin|checkout|chol|cholinc|cholupdate|circshift|cla|clabel|class|clc|clear|clearvars|clf|clipboard|clock|close|closereq|cmopts|cmpermute|cmunique|colamd|colon|colorbar|colordef|colormap|colormapeditor|colperm|Combine|comet|comet3|commandhistory|commandwindow|compan|compass|complex|computer|cond|condeig|condest|coneplot|conj|containers.Map|contour(?:[3cf]|slice)?|contrast|conv|conv2|convhull|convhulln|convn|cool|copper|copyfile|copyobj|corrcoef|cos(?:d|h)?|cot(?:d|h)?|cov|cplxpair|cputime|createClassFromWsdl|createSoapMessage|cross|csc(?:d|h)?|csvread|csvwrite|ctranspose|cumprod|cumsum|cumtrapz|curl|customverctrl|cylinder|daqread|daspect|datacursormode|datatipinfo|date|datenum|datestr|datetick|datevec|dbclear|dbcont|dbdown|dblquad|dbmex|dbquit|dbstack|dbstatus|dbstep|dbstop|dbtype|dbup|dde23|ddeget|ddesd|ddeset|deal|deblank|dec2base|dec2bin|dec2hex|decic|deconv|del2|delaunay|delaunay3|delaunayn|DelaunayTri|delete|demo|depdir|depfun|det|detrend|deval|diag|dialog|diary|diff|diffuse|dir|disp|display|dither|divergence|dlmread|dlmwrite|dmperm|doc|docsearch|dos|dot|dragrect|drawnow|dsearch|dsearchn|dynamicprops|echo|echodemo|edit|eig|eigs|ellipj|ellipke|ellipsoid|empty|enableNETfromNetworkDrive|enableservice|EndInvoke|enumeration|eomday|eq|erf|erfc|erfcinv|erfcx|erfinv|error|errorbar|errordlg|etime|etree|etreeplot|eval|evalc|evalin|event.(?:EventData|listener|PropertyEvent|proplistener)|exifread|exist|exit|exp|expint|expm|expm1|export2wsdlg|eye|ezcontour|ezcontourf|ezmesh|ezmeshc|ezplot|ezplot3|ezpolar|ezsurf|ezsurfc|factor|factorial|fclose|feather|feature|feof|ferror|feval|fft|fft2|fftn|fftshift|fftw|fgetl|fgets|fieldnames|figure|figurepalette|fileattrib|filebrowser|filemarker|fileparts|fileread|filesep|fill|fill3|filter|filter2|find|findall|findfigs|findobj|findstr|finish|fitsdisp|fitsinfo|fitsread|fitswrite|fix|flag|flipdim|fliplr|flipud|floor|flow|fminbnd|fminsearch|fopen|format|fplot|fprintf|frame2im|fread|freqspace|frewind|fscanf|fseek|ftell|FTP|full|fullfile|func2str|functions|funm|fwrite|fzero|gallery|gamma|gammainc|gammaincinv|gammaln|gca|gcbf|gcbo|gcd|gcf|gco|ge|genpath|genvarname|get|getappdata|getenv|getfield|getframe|getpixelposition|getpref|ginput|gmres|gplot|grabcode|gradient|gray|graymon|grid|griddata(?:3|n)?|griddedInterpolant|gsvd|gt|gtext|guidata|guide|guihandles|gunzip|gzip|h5create|h5disp|h5info|h5read|h5readatt|h5write|h5writeatt|hadamard|handle|hankel|hdf|hdf5|hdf5info|hdf5read|hdf5write|hdfinfo|hdfread|hdftool|help|helpbrowser|helpdesk|helpdlg|helpwin|hess|hex2dec|hex2num|hgexport|hggroup|hgload|hgsave|hgsetget|hgtransform|hidden|hilb|hist|histc|hold|home|horzcat|hostid|hot|hsv|hsv2rgb|hypot|ichol|idivide|ifft|ifft2|ifftn|ifftshift|ilu|im2frame|im2java|imag|image|imagesc|imapprox|imfinfo|imformats|import|importdata|imread|imwrite|ind2rgb|ind2sub|inferiorto|info|inline|inmem|inpolygon|input|inputdlg|inputname|inputParser|inspect|instrcallback|instrfind|instrfindall|int2str|integral(?:2|3)?|interp(?:1|1q|2|3|ft|n)|interpstreamspeed|intersect|intmax|intmin|inv|invhilb|ipermute|isa|isappdata|iscell|iscellstr|ischar|iscolumn|isdir|isempty|isequal|isequaln|isequalwithequalnans|isfield|isfinite|isfloat|isglobal|ishandle|ishghandle|ishold|isinf|isinteger|isjava|iskeyword|isletter|islogical|ismac|ismatrix|ismember|ismethod|isnan|isnumeric|isobject|isocaps|isocolors|isonormals|isosurface|ispc|ispref|isprime|isprop|isreal|isrow|isscalar|issorted|isspace|issparse|isstr|isstrprop|isstruct|isstudent|isunix|isvarname|isvector|javaaddpath|javaArray|javachk|javaclasspath|javacomponent|javaMethod|javaMethodEDT|javaObject|javaObjectEDT|javarmpath|jet|keyboard|kron|lasterr|lasterror|lastwarn|lcm|ldivide|ldl|le|legend|legendre|length|libfunctions|libfunctionsview|libisloaded|libpointer|libstruct|license|light|lightangle|lighting|lin2mu|line|lines|linkaxes|linkdata|linkprop|linsolve|linspace|listdlg|listfonts|load|loadlibrary|loadobj|log|log10|log1p|log2|loglog|logm|logspace|lookfor|lower|ls|lscov|lsqnonneg|lsqr|lt|lu|luinc|magic|makehgtform|mat2cell|mat2str|material|matfile|matlab.io.MatFile|matlab.mixin.(?:Copyable|Heterogeneous(?:.getDefaultScalarElement)?)|matlabrc|matlabroot|max|maxNumCompThreads|mean|median|membrane|memmapfile|memory|menu|mesh|meshc|meshgrid|meshz|meta.(?:class(?:.fromName)?|DynamicProperty|EnumeratedValue|event|MetaData|method|package(?:.(?:fromName|getAllPackages))?|property)|metaclass|methods|methodsview|mex(?:.getCompilerConfigurations)?|MException|mexext|mfilename|min|minres|minus|mislocked|mkdir|mkpp|mldivide|mlint|mlintrpt|mlock|mmfileinfo|mmreader|mod|mode|more|move|movefile|movegui|movie|movie2avi|mpower|mrdivide|msgbox|mtimes|mu2lin|multibandread|multibandwrite|munlock|namelengthmax|nargchk|narginchk|nargoutchk|native2unicode|nccreate|ncdisp|nchoosek|ncinfo|ncread|ncreadatt|ncwrite|ncwriteatt|ncwriteschema|ndgrid|ndims|ne|NET(?:.(?:addAssembly|Assembly|convertArray|createArray|createGeneric|disableAutoRelease|enableAutoRelease|GenericClass|invokeGenericMethod|NetException|setStaticProperty))?|netcdf.(?:abort|close|copyAtt|create|defDim|defGrp|defVar|defVarChunking|defVarDeflate|defVarFill|defVarFletcher32|delAtt|endDef|getAtt|getChunkCache|getConstant|getConstantNames|getVar|inq|inqAtt|inqAttID|inqAttName|inqDim|inqDimID|inqDimIDs|inqFormat|inqGrpName|inqGrpNameFull|inqGrpParent|inqGrps|inqLibVers|inqNcid|inqUnlimDims|inqVar|inqVarChunking|inqVarDeflate|inqVarFill|inqVarFletcher32|inqVarID|inqVarIDs|open|putAtt|putVar|reDef|renameAtt|renameDim|renameVar|setChunkCache|setDefaultFormat|setFill|sync)|newplot|nextpow2|nnz|noanimate|nonzeros|norm|normest|not|notebook|now|nthroot|null|num2cell|num2hex|num2str|numel|nzmax|ode(?:113|15i|15s|23|23s|23t|23tb|45)|odeget|odeset|odextend|onCleanup|ones|open|openfig|opengl|openvar|optimget|optimset|or|ordeig|orderfields|ordqz|ordschur|orient|orth|pack|padecoef|pagesetupdlg|pan|pareto|parseSoapResponse|pascal|patch|path|path2rc|pathsep|pathtool|pause|pbaspect|pcg|pchip|pcode|pcolor|pdepe|pdeval|peaks|perl|perms|permute|pie|pink|pinv|planerot|playshow|plot|plot3|plotbrowser|plotedit|plotmatrix|plottools|plotyy|plus|pol2cart|polar|poly|polyarea|polyder|polyeig|polyfit|polyint|polyval|polyvalm|pow2|power|ppval|prefdir|preferences|primes|print|printdlg|printopt|printpreview|prod|profile|profsave|propedit|propertyeditor|psi|publish|PutCharArray|PutFullMatrix|PutWorkspaceData|pwd|qhull|qmr|qr|qrdelete|qrinsert|qrupdate|quad|quad2d|quadgk|quadl|quadv|questdlg|quit|quiver|quiver3|qz|rand|randi|randn|randperm|RandStream(?:.(?:create|getDefaultStream|getGlobalStream|list|setDefaultStream|setGlobalStream))?|rank|rat|rats|rbbox|rcond|rdivide|readasync|real|reallog|realmax|realmin|realpow|realsqrt|record|rectangle|rectint|recycle|reducepatch|reducevolume|refresh|refreshdata|regexp|regexpi|regexprep|regexptranslate|rehash|rem|Remove|RemoveAll|repmat|reset|reshape|residue|restoredefaultpath|rethrow|rgb2hsv|rgb2ind|rgbplot|ribbon|rmappdata|rmdir|rmfield|rmpath|rmpref|rng|roots|rose|rosser|rot90|rotate|rotate3d|round|rref|rsf2csf|run|save|saveas|saveobj|savepath|scatter|scatter3|schur|sec|secd|sech|selectmoveresize|semilogx|semilogy|sendmail|serial|set|setappdata|setdiff|setenv|setfield|setpixelposition|setpref|setstr|setxor|shading|shg|shiftdim|showplottool|shrinkfaces|sign|sin(?:d|h)?|size|slice|smooth3|snapnow|sort|sortrows|sound|soundsc|spalloc|spaugment|spconvert|spdiags|specular|speye|spfun|sph2cart|sphere|spinmap|spline|spones|spparms|sprand|sprandn|sprandsym|sprank|spring|sprintf|spy|sqrt|sqrtm|squeeze|ss2tf|sscanf|stairs|startup|std|stem|stem3|stopasync|str2double|str2func|str2mat|str2num|strcat|strcmp|strcmpi|stream2|stream3|streamline|streamparticles|streamribbon|streamslice|streamtube|strfind|strjust|strmatch|strncmp|strncmpi|strread|strrep|strtok|strtrim|struct2cell|structfun|strvcat|sub2ind|subplot|subsasgn|subsindex|subspace|subsref|substruct|subvolume|sum|summer|superclasses|superiorto|support|surf|surf2patch|surface|surfc|surfl|surfnorm|svd|svds|swapbytes|symamd|symbfact|symmlq|symrcm|symvar|system|tan(?:d|h)?|tar|tempdir|tempname|tetramesh|texlabel|text|textread|textscan|textwrap|tfqmr|throw|tic|Tiff(?:.(?:getTagNames|getVersion))?|timer|timerfind|timerfindall|times|timeseries|title|toc|todatenum|toeplitz|toolboxdir|trace|transpose|trapz|treelayout|treeplot|tril|trimesh|triplequad|triplot|TriRep|TriScatteredInterp|trisurf|triu|tscollection|tsearch|tsearchn|tstool|type|typecast|uibuttongroup|uicontextmenu|uicontrol|uigetdir|uigetfile|uigetpref|uiimport|uimenu|uiopen|uipanel|uipushtool|uiputfile|uiresume|uisave|uisetcolor|uisetfont|uisetpref|uistack|uitable|uitoggletool|uitoolbar|uiwait|uminus|undocheckout|unicode2native|union|unique|unix|unloadlibrary|unmesh|unmkpp|untar|unwrap|unzip|uplus|upper|urlread|urlwrite|usejava|userpath|validateattributes|validatestring|vander|var|vectorize|ver|verctrl|verLessThan|version|vertcat|VideoReader(?:.isPlatformSupported)?|VideoWriter(?:.getProfiles)?|view|viewmtx|visdiff|volumebounds|voronoi|voronoin|wait|waitbar|waitfor|waitforbuttonpress|warndlg|warning|waterfall|wavfinfo|wavplay|wavread|wavrecord|wavwrite|web|weekday|what|whatsnew|which|whitebg|who|whos|wilkinson|winopen|winqueryreg|winter|wk1finfo|wk1read|wk1write|workspace|xlabel|xlim|xlsfinfo|xlsread|xlswrite|xmlread|xmlwrite|xor|xslt|ylabel|ylim|zeros|zip|zlabel|zlim|zoom)\b/, |
|
4 |
+a],["fun_tbx",/^\b(?:addedvarplot|andrewsplot|anova[12n]|ansaribradley|aoctool|barttest|bbdesign|beta(?:cdf|fit|inv|like|pdf|rnd|stat)|bino(?:cdf|fit|inv|pdf|rnd|stat)|biplot|bootci|bootstrp|boxplot|candexch|candgen|canoncorr|capability|capaplot|caseread|casewrite|categorical|ccdesign|cdfplot|chi2(?:cdf|gof|inv|pdf|rnd|stat)|cholcov|Classification(?:BaggedEnsemble|Discriminant(?:.(?:fit|make|template))?|Ensemble|KNN(?:.(?:fit|template))?|PartitionedEnsemble|PartitionedModel|Tree(?:.(?:fit|template))?)|classify|classregtree|cluster|clusterdata|cmdscale|combnk|Compact(?:Classification(?:Discriminant|Ensemble|Tree)|Regression(?:Ensemble|Tree)|TreeBagger)|confusionmat|controlchart|controlrules|cophenet|copula(?:cdf|fit|param|pdf|rnd|stat)|cordexch|corr|corrcov|coxphfit|createns|crosstab|crossval|cvpartition|datasample|dataset|daugment|dcovary|dendrogram|dfittool|disttool|dummyvar|dwtest|ecdf|ecdfhist|ev(?:cdf|fit|inv|like|pdf|rnd|stat)|ExhaustiveSearcher|exp(?:cdf|fit|inv|like|pdf|rnd|stat)|factoran|fcdf|ff2n|finv|fitdist|fitensemble|fpdf|fracfact|fracfactgen|friedman|frnd|fstat|fsurfht|fullfact|gagerr|gam(?:cdf|fit|inv|like|pdf|rnd|stat)|GeneralizedLinearModel(?:.fit)?|geo(?:cdf|inv|mean|pdf|rnd|stat)|gev(?:cdf|fit|inv|like|pdf|rnd|stat)|gline|glmfit|glmval|glyphplot|gmdistribution(?:.fit)?|gname|gp(?:cdf|fit|inv|like|pdf|rnd|stat)|gplotmatrix|grp2idx|grpstats|gscatter|haltonset|harmmean|hist3|histfit|hmm(?:decode|estimate|generate|train|viterbi)|hougen|hyge(?:cdf|inv|pdf|rnd|stat)|icdf|inconsistent|interactionplot|invpred|iqr|iwishrnd|jackknife|jbtest|johnsrnd|KDTreeSearcher|kmeans|knnsearch|kruskalwallis|ksdensity|kstest|kstest2|kurtosis|lasso|lassoglm|lassoPlot|leverage|lhsdesign|lhsnorm|lillietest|LinearModel(?:.fit)?|linhyptest|linkage|logn(?:cdf|fit|inv|like|pdf|rnd|stat)|lsline|mad|mahal|maineffectsplot|manova1|manovacluster|mdscale|mhsample|mle|mlecov|mnpdf|mnrfit|mnrnd|mnrval|moment|multcompare|multivarichart|mvn(?:cdf|pdf|rnd)|mvregress|mvregresslike|mvt(?:cdf|pdf|rnd)|NaiveBayes(?:.fit)?|nan(?:cov|max|mean|median|min|std|sum|var)|nbin(?:cdf|fit|inv|pdf|rnd|stat)|ncf(?:cdf|inv|pdf|rnd|stat)|nct(?:cdf|inv|pdf|rnd|stat)|ncx2(?:cdf|inv|pdf|rnd|stat)|NeighborSearcher|nlinfit|nlintool|nlmefit|nlmefitsa|nlparci|nlpredci|nnmf|nominal|NonLinearModel(?:.fit)?|norm(?:cdf|fit|inv|like|pdf|rnd|stat)|normplot|normspec|ordinal|outlierMeasure|parallelcoords|paretotails|partialcorr|pcacov|pcares|pdf|pdist|pdist2|pearsrnd|perfcurve|perms|piecewisedistribution|plsregress|poiss(?:cdf|fit|inv|pdf|rnd|tat)|polyconf|polytool|prctile|princomp|ProbDist(?:Kernel|Parametric|UnivKernel|UnivParam)?|probplot|procrustes|qqplot|qrandset|qrandstream|quantile|randg|random|randsample|randtool|range|rangesearch|ranksum|rayl(?:cdf|fit|inv|pdf|rnd|stat)|rcoplot|refcurve|refline|regress|Regression(?:BaggedEnsemble|Ensemble|PartitionedEnsemble|PartitionedModel|Tree(?:.(?:fit|template))?)|regstats|relieff|ridge|robustdemo|robustfit|rotatefactors|rowexch|rsmdemo|rstool|runstest|sampsizepwr|scatterhist|sequentialfs|signrank|signtest|silhouette|skewness|slicesample|sobolset|squareform|statget|statset|stepwise|stepwisefit|surfht|tabulate|tblread|tblwrite|tcdf|tdfread|tiedrank|tinv|tpdf|TreeBagger|treedisp|treefit|treeprune|treetest|treeval|trimmean|trnd|tstat|ttest|ttest2|unid(?:cdf|inv|pdf|rnd|stat)|unif(?:cdf|inv|it|pdf|rnd|stat)|vartest(?:2|n)?|wbl(?:cdf|fit|inv|like|pdf|rnd|stat)|wblplot|wishrnd|x2fx|xptread|zscore|ztest)\b/, |
|
5 |
+a],["fun_tbx",/^\b(?:adapthisteq|analyze75info|analyze75read|applycform|applylut|axes2pix|bestblk|blockproc|bwarea|bwareaopen|bwboundaries|bwconncomp|bwconvhull|bwdist|bwdistgeodesic|bweuler|bwhitmiss|bwlabel|bwlabeln|bwmorph|bwpack|bwperim|bwselect|bwtraceboundary|bwulterode|bwunpack|checkerboard|col2im|colfilt|conndef|convmtx2|corner|cornermetric|corr2|cp2tform|cpcorr|cpselect|cpstruct2pairs|dct2|dctmtx|deconvblind|deconvlucy|deconvreg|deconvwnr|decorrstretch|demosaic|dicom(?:anon|dict|info|lookup|read|uid|write)|edge|edgetaper|entropy|entropyfilt|fan2para|fanbeam|findbounds|fliptform|freqz2|fsamp2|fspecial|ftrans2|fwind1|fwind2|getheight|getimage|getimagemodel|getline|getneighbors|getnhood|getpts|getrangefromclass|getrect|getsequence|gray2ind|graycomatrix|graycoprops|graydist|grayslice|graythresh|hdrread|hdrwrite|histeq|hough|houghlines|houghpeaks|iccfind|iccread|iccroot|iccwrite|idct2|ifanbeam|im2bw|im2col|im2double|im2int16|im2java2d|im2single|im2uint16|im2uint8|imabsdiff|imadd|imadjust|ImageAdapter|imageinfo|imagemodel|imapplymatrix|imattributes|imbothat|imclearborder|imclose|imcolormaptool|imcomplement|imcontour|imcontrast|imcrop|imdilate|imdisplayrange|imdistline|imdivide|imellipse|imerode|imextendedmax|imextendedmin|imfill|imfilter|imfindcircles|imfreehand|imfuse|imgca|imgcf|imgetfile|imhandles|imhist|imhmax|imhmin|imimposemin|imlincomb|imline|immagbox|immovie|immultiply|imnoise|imopen|imoverview|imoverviewpanel|impixel|impixelinfo|impixelinfoval|impixelregion|impixelregionpanel|implay|impoint|impoly|impositionrect|improfile|imputfile|impyramid|imreconstruct|imrect|imregconfig|imregionalmax|imregionalmin|imregister|imresize|imroi|imrotate|imsave|imscrollpanel|imshow|imshowpair|imsubtract|imtool|imtophat|imtransform|imview|ind2gray|ind2rgb|interfileinfo|interfileread|intlut|ippl|iptaddcallback|iptcheckconn|iptcheckhandle|iptcheckinput|iptcheckmap|iptchecknargin|iptcheckstrs|iptdemos|iptgetapi|iptGetPointerBehavior|iptgetpref|ipticondir|iptnum2ordinal|iptPointerManager|iptprefs|iptremovecallback|iptSetPointerBehavior|iptsetpref|iptwindowalign|iradon|isbw|isflat|isgray|isicc|isind|isnitf|isrgb|isrset|lab2double|lab2uint16|lab2uint8|label2rgb|labelmatrix|makecform|makeConstrainToRectFcn|makehdr|makelut|makeresampler|maketform|mat2gray|mean2|medfilt2|montage|nitfinfo|nitfread|nlfilter|normxcorr2|ntsc2rgb|openrset|ordfilt2|otf2psf|padarray|para2fan|phantom|poly2mask|psf2otf|qtdecomp|qtgetblk|qtsetblk|radon|rangefilt|reflect|regionprops|registration.metric.(?:MattesMutualInformation|MeanSquares)|registration.optimizer.(?:OnePlusOneEvolutionary|RegularStepGradientDescent)|rgb2gray|rgb2ntsc|rgb2ycbcr|roicolor|roifill|roifilt2|roipoly|rsetwrite|std2|stdfilt|strel|stretchlim|subimage|tformarray|tformfwd|tforminv|tonemap|translate|truesize|uintlut|viscircles|warp|watershed|whitepoint|wiener2|xyz2double|xyz2uint16|ycbcr2rgb)\b/, |
|
6 |
+a],["fun_tbx",/^\b(?:bintprog|color|fgoalattain|fminbnd|fmincon|fminimax|fminsearch|fminunc|fseminf|fsolve|fzero|fzmult|gangstr|ktrlink|linprog|lsqcurvefit|lsqlin|lsqnonlin|lsqnonneg|optimget|optimset|optimtool|quadprog)\b/,a],["ident",/^[A-Za-z]\w*(?:\.[A-Za-z]\w*)*/,a]]),["matlab-identifiers"]);b.registerLangHandler(b.createSimpleLexer([],e),["matlab-operators"]);b.registerLangHandler(b.createSimpleLexer(c,d),["matlab"]); |
... | ... |
@@ -1,2 +1,2 @@ |
1 |
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r �\xa0"],["com",/^#(?:if[\t\n\r \xa0]+(?:[$_a-z][\w']*|``[^\t\n\r`]*(?:``|$))|else|endif|light)/i,null,"#"],["str",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])(?:'|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\(\*[\S\s]*?\*\))/],["kwd",/^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/], |
|
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["com",/^#(?:if[\t\n\r \xa0]+(?:[$_a-z][\w']*|``[^\t\n\r`]*(?:``|$))|else|endif|light)/i,null,"#"],["str",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])(?:'|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\(\*[\S\s]*?\*\))/],["kwd",/^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/], |
|
2 | 2 |
["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],["pln",/^(?:[_a-z][\w']*[!#?]?|``[^\t\n\r`]*(?:``|$))/i],["pun",/^[^\w\t\n\r "'\xa0]+/]]),["fs","ml"]); |
... | ... |
@@ -0,0 +1,2 @@ |
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^"(?:[^"]|\\.)*"/,null,'"']],[["com",/^;[^\n\r]*/,null,";"],["dec",/^\$(?:d|device|ec|ecode|es|estack|et|etrap|h|horolog|i|io|j|job|k|key|p|principal|q|quit|st|stack|s|storage|sy|system|t|test|tl|tlevel|tr|trestart|x|y|z[a-z]*|a|ascii|c|char|d|data|e|extract|f|find|fn|fnumber|g|get|j|justify|l|length|na|name|o|order|p|piece|ql|qlength|qs|qsubscript|q|query|r|random|re|reverse|s|select|st|stack|t|text|tr|translate|nan)\b/i, |
|
2 |
+null],["kwd",/^(?:[^$]b|break|c|close|d|do|e|else|f|for|g|goto|h|halt|h|hang|i|if|j|job|k|kill|l|lock|m|merge|n|new|o|open|q|quit|r|read|s|set|tc|tcommit|tre|trestart|tro|trollback|ts|tstart|u|use|v|view|w|write|x|xecute)\b/i,null],["lit",/^[+-]?(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?/i],["pln",/^[a-z][^\W_]*/i],["pun",/^[^\w\t\n\r"$%;^\xa0]|_/]]),["mumps"]); |
... | ... |
@@ -1,4 +1,4 @@ |
1 | 1 |
var a=null; |
2 |
-PR.registerLangHandler(PR.createSimpleLexer([["str",/^(?:'(?:[^\n\r'\\]|\\.)*'|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,a,'"'],["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,a,"#"],["pln",/^\s+/,a," \r\n\t\xa0"]],[["str",/^@"(?:[^"]|"")*(?:"|$)/,a],["str",/^<#[^#>]*(?:#>|$)/,a],["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,a],["com",/^\/\/[^\n\r]*/,a],["com",/^\/\*[\S\s]*?(?:\*\/|$)/, |
|
2 |
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^(?:'(?:[^\n\r'\\]|\\.)*'|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,a,'"'],["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,a,"#"],["pln",/^\s+/,a," \r\n\t\u00a0"]],[["str",/^@"(?:[^"]|"")*(?:"|$)/,a],["str",/^<#[^#>]*(?:#>|$)/,a],["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,a],["com",/^\/\/[^\n\r]*/,a],["com",/^\/\*[\S\s]*?(?:\*\/|$)/, |
|
3 | 3 |
a],["kwd",/^(?:abstract|and|as|base|catch|class|def|delegate|enum|event|extern|false|finally|fun|implements|interface|internal|is|macro|match|matches|module|mutable|namespace|new|null|out|override|params|partial|private|protected|public|ref|sealed|static|struct|syntax|this|throw|true|try|type|typeof|using|variant|virtual|volatile|when|where|with|assert|assert2|async|break|checked|continue|do|else|ensures|for|foreach|if|late|lock|new|nolate|otherwise|regexp|repeat|requires|return|surroundwith|unchecked|unless|using|while|yield)\b/, |
4 | 4 |
a],["typ",/^(?:array|bool|byte|char|decimal|double|float|int|list|long|object|sbyte|short|string|ulong|uint|ufloat|ulong|ushort|void)\b/,a],["lit",/^@[$_a-z][\w$@]*/i,a],["typ",/^@[A-Z]+[a-z][\w$@]*/,a],["pln",/^'?[$_a-z][\w$@]*/i,a],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,a,"0123456789"],["pun",/^.[^\s\w"-$'./@`]*/,a]]),["n","nemerle"]); |
... | ... |
@@ -0,0 +1,3 @@ |
1 |
+var a=null; |
|
2 |
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^'(?:[^\n\r'\\]|\\.)*(?:'|$)/,a,"'"],["pln",/^\s+/,a," \r\n\t\u00a0"]],[["com",/^\(\*[\S\s]*?(?:\*\)|$)|^{[\S\s]*?(?:}|$)/,a],["kwd",/^(?:absolute|and|array|asm|assembler|begin|case|const|constructor|destructor|div|do|downto|else|end|external|for|forward|function|goto|if|implementation|in|inline|interface|interrupt|label|mod|not|object|of|or|packed|procedure|program|record|repeat|set|shl|shr|then|to|type|unit|until|uses|var|virtual|while|with|xor)\b/i,a], |
|
3 |
+["lit",/^(?:true|false|self|nil)/i,a],["pln",/^[a-z][^\W_]*/i,a],["lit",/^(?:\$[\da-f]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?)/i,a,"0123456789"],["pun",/^.[^\s\w$'./@]*/,a]]),["pascal"]); |
... | ... |
@@ -0,0 +1,2 @@ |
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["str",/^'(?:[^'\\]|\\[\S\s])*(?:'|$)/,null,"'"]],[["com",/^#.*/],["kwd",/^(?:if|else|for|while|repeat|in|next|break|return|switch|function)(?![\w.])/],["lit",/^0[Xx][\dA-Fa-f]+([Pp]\d+)?[Li]?/],["lit",/^[+-]?(\d+(\.\d+)?|\.\d+)([Ee][+-]?\d+)?[Li]?/],["lit",/^(?:NULL|NA(?:_(?:integer|real|complex|character)_)?|Inf|TRUE|FALSE|NaN|\.\.(?:\.|\d+))(?![\w.])/], |
|
2 |
+["pun",/^(?:<<?-|->>?|-|==|<=|>=|<|>|&&?|!=|\|\|?|[!*+/^]|%.*?%|[$=@~]|:{1,3}|[(),;?[\]{}])/],["pln",/^(?:[A-Za-z]+[\w.]*|\.[^\W\d][\w.]*)(?![\w.])/],["str",/^`.+`/]]),["r","s","R","S","Splus"]); |
... | ... |
@@ -0,0 +1 @@ |
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["com",/^%[^\n\r]*/,null,"%"]],[["lit",/^\\(?:cr|l?dots|R|tab)\b/],["kwd",/^\\[@-Za-z]+/],["kwd",/^#(?:ifn?def|endif)/],["pln",/^\\[{}]/],["pun",/^[()[\]{}]+/]]),["Rd","rd"]); |
... | ... |
@@ -1,2 +1,2 @@ |
1 |
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r �\xa0"],["str",/^"(?:""(?:""?(?!")|[^"\\]|\\.)*"{0,3}|(?:[^\n\r"\\]|\\.)*"?)/,null,'"'],["lit",/^`(?:[^\n\r\\`]|\\.)*`?/,null,"`"],["pun",/^[!#%&(--:-@[-^{-~]+/,null,"!#%&()*+,-:;<=>?@[\\]^{|}~"]],[["str",/^'(?:[^\n\r'\\]|\\(?:'|[^\n\r']+))'/],["lit",/^'[$A-Z_a-z][\w$]*(?![\w$'])/],["kwd",/^(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|object|override|package|private|protected|requires|return|sealed|super|throw|trait|try|type|val|var|while|with|yield)\b/], |
|
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^"(?:""(?:""?(?!")|[^"\\]|\\.)*"{0,3}|(?:[^\n\r"\\]|\\.)*"?)/,null,'"'],["lit",/^`(?:[^\n\r\\`]|\\.)*`?/,null,"`"],["pun",/^[!#%&(--:-@[-^{-~]+/,null,"!#%&()*+,-:;<=>?@[\\]^{|}~"]],[["str",/^'(?:[^\n\r'\\]|\\(?:'|[^\n\r']+))'/],["lit",/^'[$A-Z_a-z][\w$]*(?![\w$'])/],["kwd",/^(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|object|override|package|private|protected|requires|return|sealed|super|throw|trait|try|type|val|var|while|with|yield)\b/], |
|
2 | 2 |
["lit",/^(?:true|false|null|this)\b/],["lit",/^(?:0(?:[0-7]+|x[\da-f]+)l?|(?:0|[1-9]\d*)(?:(?:\.\d+)?(?:e[+-]?\d+)?f?|l?)|\\.\d+(?:e[+-]?\d+)?f?)/i],["typ",/^[$_]*[A-Z][\d$A-Z_]*[a-z][\w$]*/],["pln",/^[$A-Z_a-z][\w$]*/],["com",/^\/(?:\/.*|\*(?:\/|\**[^*/])*(?:\*+\/?)?)/],["pun",/^(?:\.+|\/)/]]),["scala"]); |
... | ... |
@@ -1,2 +1,2 @@ |
1 |
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r �\xa0"],["str",/^(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/,null,"\"'"]],[["com",/^(?:--[^\n\r]*|\/\*[\S\s]*?(?:\*\/|$))/],["kwd",/^(?:add|all|alter|and|any|as|asc|authorization|backup|begin|between|break|browse|bulk|by|cascade|case|check|checkpoint|close|clustered|coalesce|collate|column|commit|compute|constraint|contains|containstable|continue|convert|create|cross|current|current_date|current_time|current_timestamp|current_user|cursor|database|dbcc|deallocate|declare|default|delete|deny|desc|disk|distinct|distributed|double|drop|dummy|dump|else|end|errlvl|escape|except|exec|execute|exists|exit|fetch|file|fillfactor|for|foreign|freetext|freetexttable|from|full|function|goto|grant|group|having|holdlock|identity|identitycol|identity_insert|if|in|index|inner|insert|intersect|into|is|join|key|kill|left|like|lineno|load|match|merge|national|nocheck|nonclustered|not|null|nullif|of|off|offsets|on|open|opendatasource|openquery|openrowset|openxml|option|or|order|outer|over|percent|plan|precision|primary|print|proc|procedure|public|raiserror|read|readtext|reconfigure|references|replication|restore|restrict|return|revoke|right|rollback|rowcount|rowguidcol|rule|save|schema|select|session_user|set|setuser|shutdown|some|statistics|system_user|table|textsize|then|to|top|tran|transaction|trigger|truncate|tsequal|union|unique|update|updatetext|use|user|using|values|varying|view|waitfor|when|where|while|with|writetext)(?=[^\w-]|$)/i, |
|
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/,null,"\"'"]],[["com",/^(?:--[^\n\r]*|\/\*[\S\s]*?(?:\*\/|$))/],["kwd",/^(?:add|all|alter|and|any|apply|as|asc|authorization|backup|begin|between|break|browse|bulk|by|cascade|case|check|checkpoint|close|clustered|coalesce|collate|column|commit|compute|connect|constraint|contains|containstable|continue|convert|create|cross|current|current_date|current_time|current_timestamp|current_user|cursor|database|dbcc|deallocate|declare|default|delete|deny|desc|disk|distinct|distributed|double|drop|dummy|dump|else|end|errlvl|escape|except|exec|execute|exists|exit|fetch|file|fillfactor|following|for|foreign|freetext|freetexttable|from|full|function|goto|grant|group|having|holdlock|identity|identitycol|identity_insert|if|in|index|inner|insert|intersect|into|is|join|key|kill|left|like|lineno|load|match|matched|merge|natural|national|nocheck|nonclustered|nocycle|not|null|nullif|of|off|offsets|on|open|opendatasource|openquery|openrowset|openxml|option|or|order|outer|over|partition|percent|pivot|plan|preceding|precision|primary|print|proc|procedure|public|raiserror|read|readtext|reconfigure|references|replication|restore|restrict|return|revoke|right|rollback|rowcount|rowguidcol|rows?|rule|save|schema|select|session_user|set|setuser|shutdown|some|start|statistics|system_user|table|textsize|then|to|top|tran|transaction|trigger|truncate|tsequal|unbounded|union|unique|unpivot|update|updatetext|use|user|using|values|varying|view|waitfor|when|where|while|with|within|writetext|xml)(?=[^\w-]|$)/i, |
|
2 | 2 |
null],["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],["pln",/^[_a-z][\w-]*/i],["pun",/^[^\w\t\n\r "'\xa0][^\w\t\n\r "'+\xa0-]*/]]),["sql"]); |
... | ... |
@@ -0,0 +1,3 @@ |
1 |
+var a=null; |
|
2 |
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^{+/,a,"{"],["clo",/^}+/,a,"}"],["com",/^#[^\n\r]*/,a,"#"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \u00a0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:after|append|apply|array|break|case|catch|continue|error|eval|exec|exit|expr|for|foreach|if|incr|info|proc|return|set|switch|trace|uplevel|upvar|while)\b/,a],["lit",/^[+-]?(?:[#0]x[\da-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[de][+-]?\d+)?)/i],["lit", |
|
3 |
+/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[_a-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/i],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["tcl"]); |
... | ... |
@@ -1 +1 @@ |
1 |
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r �\xa0"],["com",/^%[^\n\r]*/,null,"%"]],[["kwd",/^\\[@-Za-z]+/],["kwd",/^\\./],["typ",/^[$&]/],["lit",/[+-]?(?:\.\d+|\d+(?:\.\d*)?)(cm|em|ex|in|pc|pt|bp|mm)/i],["pun",/^[()=[\]{}]+/]]),["latex","tex"]); |
|
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["com",/^%[^\n\r]*/,null,"%"]],[["kwd",/^\\[@-Za-z]+/],["kwd",/^\\./],["typ",/^[$&]/],["lit",/[+-]?(?:\.\d+|\d+(?:\.\d*)?)(cm|em|ex|in|pc|pt|bp|mm)/i],["pun",/^[()=[\]{}]+/]]),["latex","tex"]); |
... | ... |
@@ -1,2 +1,2 @@ |
1 |
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0\u2028\u2029]+/,null,"\t\n\r �\xa0 "],["str",/^(?:["\u201c\u201d](?:[^"\u201c\u201d]|["\u201c\u201d]{2})(?:["\u201c\u201d]c|$)|["\u201c\u201d](?:[^"\u201c\u201d]|["\u201c\u201d]{2})*(?:["\u201c\u201d]|$))/i,null,'"“”'],["com",/^['\u2018\u2019].*/,null,"'‘’"]],[["kwd",/^(?:addhandler|addressof|alias|and|andalso|ansi|as|assembly|auto|boolean|byref|byte|byval|call|case|catch|cbool|cbyte|cchar|cdate|cdbl|cdec|char|cint|class|clng|cobj|const|cshort|csng|cstr|ctype|date|decimal|declare|default|delegate|dim|directcast|do|double|each|else|elseif|end|endif|enum|erase|error|event|exit|finally|for|friend|function|get|gettype|gosub|goto|handles|if|implements|imports|in|inherits|integer|interface|is|let|lib|like|long|loop|me|mod|module|mustinherit|mustoverride|mybase|myclass|namespace|new|next|not|notinheritable|notoverridable|object|on|option|optional|or|orelse|overloads|overridable|overrides|paramarray|preserve|private|property|protected|public|raiseevent|readonly|redim|removehandler|resume|return|select|set|shadows|shared|short|single|static|step|stop|string|structure|sub|synclock|then|throw|to|try|typeof|unicode|until|variant|wend|when|while|with|withevents|writeonly|xor|endif|gosub|let|variant|wend)\b/i, |
|
2 |
-null],["com",/^rem.*/i],["lit",/^(?:true\b|false\b|nothing\b|\d+(?:e[+-]?\d+[dfr]?|[dfilrs])?|(?:&h[\da-f]+|&o[0-7]+)[ils]?|\d*\.\d+(?:e[+-]?\d+)?[dfr]?|#\s+(?:\d+[/-]\d+[/-]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:am|pm))?)?|\d+:\d+(?::\d+)?(\s*(?:am|pm))?)\s+#)/i],["pln",/^(?:(?:[a-z]|_\w)\w*|\[(?:[a-z]|_\w)\w*])/i],["pun",/^[^\w\t\n\r "'[\]\xa0\u2018\u2019\u201c\u201d\u2028\u2029]+/],["pun",/^(?:\[|])/]]),["vb","vbs"]); |
|
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0\u2028\u2029]+/,null,"\t\n\r \u00a0\u2028\u2029"],["str",/^(?:["\u201c\u201d](?:[^"\u201c\u201d]|["\u201c\u201d]{2})(?:["\u201c\u201d]c|$)|["\u201c\u201d](?:[^"\u201c\u201d]|["\u201c\u201d]{2})*(?:["\u201c\u201d]|$))/i,null,'"\u201c\u201d'],["com",/^['\u2018\u2019](?:_(?:\r\n?|[^\r]?)|[^\n\r_\u2028\u2029])*/,null,"'\u2018\u2019"]],[["kwd",/^(?:addhandler|addressof|alias|and|andalso|ansi|as|assembly|auto|boolean|byref|byte|byval|call|case|catch|cbool|cbyte|cchar|cdate|cdbl|cdec|char|cint|class|clng|cobj|const|cshort|csng|cstr|ctype|date|decimal|declare|default|delegate|dim|directcast|do|double|each|else|elseif|end|endif|enum|erase|error|event|exit|finally|for|friend|function|get|gettype|gosub|goto|handles|if|implements|imports|in|inherits|integer|interface|is|let|lib|like|long|loop|me|mod|module|mustinherit|mustoverride|mybase|myclass|namespace|new|next|not|notinheritable|notoverridable|object|on|option|optional|or|orelse|overloads|overridable|overrides|paramarray|preserve|private|property|protected|public|raiseevent|readonly|redim|removehandler|resume|return|select|set|shadows|shared|short|single|static|step|stop|string|structure|sub|synclock|then|throw|to|try|typeof|unicode|until|variant|wend|when|while|with|withevents|writeonly|xor|endif|gosub|let|variant|wend)\b/i, |
|
2 |
+null],["com",/^rem\b.*/i],["lit",/^(?:true\b|false\b|nothing\b|\d+(?:e[+-]?\d+[dfr]?|[dfilrs])?|(?:&h[\da-f]+|&o[0-7]+)[ils]?|\d*\.\d+(?:e[+-]?\d+)?[dfr]?|#\s+(?:\d+[/-]\d+[/-]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:am|pm))?)?|\d+:\d+(?::\d+)?(\s*(?:am|pm))?)\s+#)/i],["pln",/^(?:(?:[a-z]|_\w)\w*(?:\[[!#%&@]+])?|\[(?:[a-z]|_\w)\w*])/i],["pun",/^[^\w\t\n\r "'[\]\xa0\u2018\u2019\u201c\u201d\u2028\u2029]+/],["pun",/^(?:\[|])/]]),["vb","vbs"]); |
... | ... |
@@ -1,3 +1,3 @@ |
1 |
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r �\xa0"]],[["str",/^(?:[box]?"(?:[^"]|"")*"|'.')/i],["com",/^--[^\n\r]*/],["kwd",/^(?:abs|access|after|alias|all|and|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|mod|nand|new|next|nor|not|null|of|on|open|or|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|rem|report|return|rol|ror|select|severity|shared|signal|sla|sll|sra|srl|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with|xnor|xor)(?=[^\w-]|$)/i, |
|
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"]],[["str",/^(?:[box]?"(?:[^"]|"")*"|'.')/i],["com",/^--[^\n\r]*/],["kwd",/^(?:abs|access|after|alias|all|and|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|mod|nand|new|next|nor|not|null|of|on|open|or|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|rem|report|return|rol|ror|select|severity|shared|signal|sla|sll|sra|srl|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with|xnor|xor)(?=[^\w-]|$)/i, |
|
2 | 2 |
null],["typ",/^(?:bit|bit_vector|character|boolean|integer|real|time|string|severity_level|positive|natural|signed|unsigned|line|text|std_u?logic(?:_vector)?)(?=[^\w-]|$)/i,null],["typ",/^'(?:active|ascending|base|delayed|driving|driving_value|event|high|image|instance_name|last_active|last_event|last_value|left|leftof|length|low|path_name|pos|pred|quiet|range|reverse_range|right|rightof|simple_name|stable|succ|transaction|val|value)(?=[^\w-]|$)/i,null],["lit",/^\d+(?:_\d+)*(?:#[\w.\\]+#(?:[+-]?\d+(?:_\d+)*)?|(?:\.\d+(?:_\d+)*)?(?:e[+-]?\d+(?:_\d+)*)?)/i], |
3 | 3 |
["pln",/^(?:[a-z]\w*|\\[^\\]*\\)/i],["pun",/^[^\w\t\n\r "'\xa0][^\w\t\n\r "'\xa0-]*/]]),["vhdl","vhd"]); |
... | ... |
@@ -1,2 +1,2 @@ |
1 |
-PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\d\t a-gi-z\xa0]+/,null,"\t �\xa0abcdefgijklmnopqrstuvwxyz0123456789"],["pun",/^[*=[\]^~]+/,null,"=*~^[]"]],[["lang-wiki.meta",/(?:^^|\r\n?|\n)(#[a-z]+)\b/],["lit",/^[A-Z][a-z][\da-z]+[A-Z][a-z][^\W_]+\b/],["lang-",/^{{{([\S\s]+?)}}}/],["lang-",/^`([^\n\r`]+)`/],["str",/^https?:\/\/[^\s#/?]*(?:\/[^\s#?]*)?(?:\?[^\s#]*)?(?:#\S*)?/i],["pln",/^(?:\r\n|[\S\s])[^\n\r#*=A-[^`h{~]*/]]),["wiki"]); |
|
1 |
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\d\t a-gi-z\xa0]+/,null,"\t \u00a0abcdefgijklmnopqrstuvwxyz0123456789"],["pun",/^[*=[\]^~]+/,null,"=*~^[]"]],[["lang-wiki.meta",/(?:^^|\r\n?|\n)(#[a-z]+)\b/],["lit",/^[A-Z][a-z][\da-z]+[A-Z][a-z][^\W_]+\b/],["lang-",/^{{{([\S\s]+?)}}}/],["lang-",/^`([^\n\r`]+)`/],["str",/^https?:\/\/[^\s#/?]*(?:\/[^\s#?]*)?(?:\?[^\s#]*)?(?:#\S*)?/i],["pln",/^(?:\r\n|[\S\s])[^\n\r#*=A-[^`h{~]*/]]),["wiki"]); |
|
2 | 2 |
PR.registerLangHandler(PR.createSimpleLexer([["kwd",/^#[a-z]+/i,null,"#"]],[]),["wiki.meta"]); |
... | ... |
@@ -1 +1 @@ |
1 |
-.str{color:#9daa7e}.kwd{color:#d5b57c}.com{color:#726d73}.typ{color:#dd7e5e}.lit{color:#fcf0a4}.pun,.opn,.clo{color:#a78353}.pln{color:#889dbc}.tag{color:#d5b57c}.atn{color:#dd7e5e}.atv{color:#9daa7e}.dec{color:#dd7e5e} |
|
1 |
+.str{color:#9daa7e}.kwd{color:#d5b57c}.com{color:#726d73}.typ{color:#dd7e5e}.lit{color:#fcf0a4}.pun,.opn,.clo{color:#a78353}.pln{color:#889dbc}.tag{color:#d5b57c}.atn{color:#dd7e5e}.atv{color:#9daa7e}.dec{color:#dd7e5e}pre.prettyprint{border:0;padding-bottom:1.5em;padding-top:1.5em} |
... | ... |
@@ -1,28 +1,30 @@ |
1 |
-var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; |
|
2 |
-(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= |
|
3 |
-[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c< |
|
4 |
-f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&& |
|
5 |
-(j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r= |
|
6 |
-{b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length, |
|
7 |
-t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b=== |
|
8 |
-"string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), |
|
9 |
-l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, |
|
10 |
-q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, |
|
11 |
-q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, |
|
12 |
-"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), |
|
13 |
-a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} |
|
14 |
-for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value", |
|
15 |
-m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m= |
|
16 |
-a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue= |
|
17 |
-j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], |
|
18 |
-"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], |
|
19 |
-H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], |
|
20 |
-J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ |
|
21 |
-I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), |
|
22 |
-["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", |
|
23 |
-/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), |
|
24 |
-["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", |
|
25 |
-hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b= |
|
26 |
-!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m, |
|
27 |
-250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit", |
|
28 |
-PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})(); |
|
1 |
+!function(){var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; |
|
2 |
+(function(){function S(a){function d(e){var b=e.charCodeAt(0);if(b!==92)return b;var a=e.charAt(1);return(b=r[a])?b:"0"<=a&&a<="7"?parseInt(e.substring(1),8):a==="u"||a==="x"?parseInt(e.substring(2),16):e.charCodeAt(1)}function g(e){if(e<32)return(e<16?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return e==="\\"||e==="-"||e==="]"||e==="^"?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),e=[],a= |
|
3 |
+b[0]==="^",c=["["];a&&c.push("^");for(var a=a?1:0,f=b.length;a<f;++a){var h=b[a];if(/\\[bdsw]/i.test(h))c.push(h);else{var h=d(h),l;a+2<f&&"-"===b[a+1]?(l=d(b[a+2]),a+=2):l=h;e.push([h,l]);l<65||h>122||(l<65||h>90||e.push([Math.max(65,h)|32,Math.min(l,90)|32]),l<97||h>122||e.push([Math.max(97,h)&-33,Math.min(l,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});b=[];f=[];for(a=0;a<e.length;++a)h=e[a],h[0]<=f[1]+1?f[1]=Math.max(f[1],h[1]):b.push(f=h);for(a=0;a<b.length;++a)h=b[a],c.push(g(h[0])), |
|
4 |
+h[1]>h[0]&&(h[1]+1>h[0]&&c.push("-"),c.push(g(h[1])));c.push("]");return c.join("")}function s(e){for(var a=e.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),c=a.length,d=[],f=0,h=0;f<c;++f){var l=a[f];l==="("?++h:"\\"===l.charAt(0)&&(l=+l.substring(1))&&(l<=h?d[l]=-1:a[f]=g(l))}for(f=1;f<d.length;++f)-1===d[f]&&(d[f]=++x);for(h=f=0;f<c;++f)l=a[f],l==="("?(++h,d[h]||(a[f]="(?:")):"\\"===l.charAt(0)&&(l=+l.substring(1))&&l<=h&& |
|
5 |
+(a[f]="\\"+d[l]);for(f=0;f<c;++f)"^"===a[f]&&"^"!==a[f+1]&&(a[f]="");if(e.ignoreCase&&m)for(f=0;f<c;++f)l=a[f],e=l.charAt(0),l.length>=2&&e==="["?a[f]=b(l):e!=="\\"&&(a[f]=l.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var x=0,m=!1,j=!1,k=0,c=a.length;k<c;++k){var i=a[k];if(i.ignoreCase)j=!0;else if(/[a-z]/i.test(i.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){m=!0;j=!1;break}}for(var r={b:8,t:9,n:10,v:11, |
|
6 |
+f:12,r:13},n=[],k=0,c=a.length;k<c;++k){i=a[k];if(i.global||i.multiline)throw Error(""+i);n.push("(?:"+s(i)+")")}return RegExp(n.join("|"),j?"gi":"g")}function T(a,d){function g(a){var c=a.nodeType;if(c==1){if(!b.test(a.className)){for(c=a.firstChild;c;c=c.nextSibling)g(c);c=a.nodeName.toLowerCase();if("br"===c||"li"===c)s[j]="\n",m[j<<1]=x++,m[j++<<1|1]=a}}else if(c==3||c==4)c=a.nodeValue,c.length&&(c=d?c.replace(/\r\n?/g,"\n"):c.replace(/[\t\n\r ]+/g," "),s[j]=c,m[j<<1]=x,x+=c.length,m[j++<<1|1]= |
|
7 |
+a)}var b=/(?:^|\s)nocode(?:\s|$)/,s=[],x=0,m=[],j=0;g(a);return{a:s.join("").replace(/\n$/,""),d:m}}function H(a,d,g,b){d&&(a={a:d,e:a},g(a),b.push.apply(b,a.g))}function U(a){for(var d=void 0,g=a.firstChild;g;g=g.nextSibling)var b=g.nodeType,d=b===1?d?a:g:b===3?V.test(g.nodeValue)?a:d:d;return d===a?void 0:d}function C(a,d){function g(a){for(var j=a.e,k=[j,"pln"],c=0,i=a.a.match(s)||[],r={},n=0,e=i.length;n<e;++n){var z=i[n],w=r[z],t=void 0,f;if(typeof w==="string")f=!1;else{var h=b[z.charAt(0)]; |
|
8 |
+if(h)t=z.match(h[1]),w=h[0];else{for(f=0;f<x;++f)if(h=d[f],t=z.match(h[1])){w=h[0];break}t||(w="pln")}if((f=w.length>=5&&"lang-"===w.substring(0,5))&&!(t&&typeof t[1]==="string"))f=!1,w="src";f||(r[z]=w)}h=c;c+=z.length;if(f){f=t[1];var l=z.indexOf(f),B=l+f.length;t[2]&&(B=z.length-t[2].length,l=B-f.length);w=w.substring(5);H(j+h,z.substring(0,l),g,k);H(j+h+l,f,I(w,f),k);H(j+h+B,z.substring(B),g,k)}else k.push(j+h,w)}a.g=k}var b={},s;(function(){for(var g=a.concat(d),j=[],k={},c=0,i=g.length;c<i;++c){var r= |
|
9 |
+g[c],n=r[3];if(n)for(var e=n.length;--e>=0;)b[n.charAt(e)]=r;r=r[1];n=""+r;k.hasOwnProperty(n)||(j.push(r),k[n]=q)}j.push(/[\S\s]/);s=S(j)})();var x=d.length;return g}function v(a){var d=[],g=[];a.tripleQuotedStrings?d.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?d.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, |
|
10 |
+q,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&g.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var b=a.hashComments;b&&(a.cStyleComments?(b>1?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),g.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,q])):d.push(["com", |
|
11 |
+/^#[^\n\r]*/,q,"#"]));a.cStyleComments&&(g.push(["com",/^\/\/[^\n\r]*/,q]),g.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));if(b=a.regexLiterals){var s=(b=b>1?"":"\n\r")?".":"[\\S\\s]";g.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+s+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+ |
|
12 |
+s+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&g.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),q]);d.push(["pln",/^\s+/,q," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");g.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/, |
|
13 |
+q],["pun",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if("br"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b(a,c){var d= |
|
14 |
+c?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=a.ownerDocument,k=j.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i<c.length;++i)b(c[i]);d===(d|0)&&c[0].setAttribute("value",d);var r=j.createElement("ol"); |
|
15 |
+r.className="linenums";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.className="L"+(i+d)%10,k.firstChild||k.appendChild(j.createTextNode("\u00a0")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return F[a]}function K(a){var d=a.h;try{var g=T(a.c,a.i),b=g.a; |
|
16 |
+a.a=b;a.d=g.d;a.e=0;I(d,b)(a);var s=/\bMSIE\s(\d+)/.exec(navigator.userAgent),s=s&&+s[1]<=8,d=/\n/g,x=a.a,m=x.length,g=0,j=a.d,k=j.length,b=0,c=a.g,i=c.length,r=0;c[i]=m;var n,e;for(e=n=0;e<i;)c[e]!==c[e+2]?(c[n++]=c[e++],c[n++]=c[e++]):e+=2;i=n;for(e=n=0;e<i;){for(var p=c[e],w=c[e+1],t=e+2;t+2<=i&&c[t+1]===w;)t+=2;c[n++]=p;c[n++]=w;e=t}c.length=n;var f=a.c,h;if(f)h=f.style.display,f.style.display="none";try{for(;b<k;){var l=j[b+2]||m,B=c[r+2]||m,t=Math.min(l,B),A=j[b+1],G;if(A.nodeType!==1&&(G=x.substring(g, |
|
17 |
+t))){s&&(G=G.replace(d,"\r"));A.nodeValue=G;var L=A.ownerDocument,o=L.createElement("span");o.className=c[r+1];var v=A.parentNode;v.replaceChild(o,A);o.appendChild(A);g<l&&(j[b+1]=A=L.createTextNode(x.substring(t,l)),v.insertBefore(A,o.nextSibling))}g=t;g>=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], |
|
18 |
+"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"], |
|
19 |
+O=[N,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],E=[E,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],P=[y,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], |
|
20 |
+Q=[y,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],W=[y,"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"],y=[y,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],R=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/, |
|
21 |
+V=/\S/,X=v({keywords:[M,O,E,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",P,Q,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),F={};p(X,["default-code"]);p(C([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-", |
|
22 |
+/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);p(C([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/], |
|
23 |
+["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);p(C([],[["atv",/^[\S\s]+/]]),["uq.val"]);p(v({keywords:M,hashComments:!0,cStyleComments:!0,types:R}),["c","cc","cpp","cxx","cyc","m"]);p(v({keywords:"null,true,false"}),["json"]);p(v({keywords:O,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:R}), |
|
24 |
+["cs"]);p(v({keywords:N,cStyleComments:!0}),["java"]);p(v({keywords:y,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);p(v({keywords:P,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);p(v({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl","pm"]);p(v({keywords:Q, |
|
25 |
+hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);p(v({keywords:E,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);p(v({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);p(v({keywords:W,cStyleComments:!0,multilineStrings:!0}),["rc","rs","rust"]); |
|
26 |
+p(C([],[["str",/^[\S\s]+/]]),["regex"]);var Y=D.PR={createSimpleLexer:C,registerLangHandler:p,sourceDecorator:v,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:D.prettyPrintOne=function(a,d,g){var b=document.createElement("div");b.innerHTML="<pre>"+a+"</pre>";b=b.firstChild;g&&J(b,g,!0);K({h:d,j:g,c:b,i:1}); |
|
27 |
+return b.innerHTML},prettyPrint:D.prettyPrint=function(a,d){function g(){for(var b=D.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;i<p.length&&c.now()<b;i++){for(var d=p[i],j=h,k=d;k=k.previousSibling;){var m=k.nodeType,o=(m===7||m===8)&&k.nodeValue;if(o?!/^\??prettify\b/.test(o):m!==3||/\S/.test(k.nodeValue))break;if(o){j={};o.replace(/\b(\w+)=([\w%+\-.:]+)/g,function(a,b,c){j[b]=c});break}}k=d.className;if((j!==h||e.test(k))&&!v.test(k)){m=!1;for(o=d.parentNode;o;o=o.parentNode)if(f.test(o.tagName)&& |
|
28 |
+o.className&&e.test(o.className)){m=!0;break}if(!m){d.className+=" prettyprinted";m=j.lang;if(!m){var m=k.match(n),y;if(!m&&(y=U(d))&&t.test(y.tagName))m=y.className.match(n);m&&(m=m[1])}if(w.test(d.tagName))o=1;else var o=d.currentStyle,u=s.defaultView,o=(o=o?o.whiteSpace:u&&u.getComputedStyle?u.getComputedStyle(d,q).getPropertyValue("white-space"):0)&&"pre"===o.substring(0,3);u=j.linenums;if(!(u=u==="true"||+u))u=(u=k.match(/\blinenums\b(?::(\d+))?/))?u[1]&&u[1].length?+u[1]:!0:!1;u&&J(d,u,o);r= |
|
29 |
+{h:m,c:d,j:u,i:o};K(r)}}}i<p.length?setTimeout(g,250):"function"===typeof a&&a()}for(var b=d||document.body,s=b.ownerDocument||document,b=[b.getElementsByTagName("pre"),b.getElementsByTagName("code"),b.getElementsByTagName("xmp")],p=[],m=0;m<b.length;++m)for(var j=0,k=b[m].length;j<k;++j)p.push(b[m][j]);var b=q,c=Date;c.now||(c={now:function(){return+new Date}});var i=0,r,n=/\blang(?:uage)?-([\w.]+)(?!\S)/,e=/\bprettyprint\b/,v=/\bprettyprinted\b/,w=/pre|xmp/i,t=/^code$/i,f=/^(?:pre|code|xmp)$/i, |
|
30 |
+h={};g()}};typeof define==="function"&&define.amd&&define("google-code-prettify",[],function(){return Y})})();}() |
... | ... |
@@ -0,0 +1,34 @@ |
1 |
+!function(){var r=null; |
|
2 |
+(function(){function X(e){function j(){try{J.doScroll("left")}catch(e){P(j,50);return}w("poll")}function w(j){if(!(j.type=="readystatechange"&&x.readyState!="complete")&&((j.type=="load"?n:x)[z](i+j.type,w,!1),!m&&(m=!0)))e.call(n,j.type||j)}var Y=x.addEventListener,m=!1,C=!0,t=Y?"addEventListener":"attachEvent",z=Y?"removeEventListener":"detachEvent",i=Y?"":"on";if(x.readyState=="complete")e.call(n,"lazy");else{if(x.createEventObject&&J.doScroll){try{C=!n.frameElement}catch(A){}C&&j()}x[t](i+"DOMContentLoaded", |
|
3 |
+w,!1);x[t](i+"readystatechange",w,!1);n[t](i+"load",w,!1)}}function Q(){S&&X(function(){var e=K.length;$(e?function(){for(var j=0;j<e;++j)(function(e){P(function(){n.exports[K[e]].apply(n,arguments)},0)})(j)}:void 0)})}for(var n=window,P=n.setTimeout,x=document,J=x.documentElement,L=x.head||x.getElementsByTagName("head")[0]||J,z="",A=x.scripts,m=A.length;--m>=0;){var M=A[m],T=M.src.match(/^[^#?]*\/run_prettify\.js(\?[^#]*)?(?:#.*)?$/);if(T){z=T[1]||"";M.parentNode.removeChild(M);break}}var S=!0,D= |
|
4 |
+[],N=[],K=[];z.replace(/[&?]([^&=]+)=([^&]+)/g,function(e,j,w){w=decodeURIComponent(w);j=decodeURIComponent(j);j=="autorun"?S=!/^[0fn]/i.test(w):j=="lang"?D.push(w):j=="skin"?N.push(w):j=="callback"&&K.push(w)});m=0;for(z=D.length;m<z;++m)(function(){var e=x.createElement("script");e.onload=e.onerror=e.onreadystatechange=function(){if(e&&(!e.readyState||/loaded|complete/.test(e.readyState)))e.onerror=e.onload=e.onreadystatechange=r,--R,R||P(Q,0),e.parentNode&&e.parentNode.removeChild(e),e=r};e.type= |
|
5 |
+"text/javascript";e.src="https://google-code-prettify.googlecode.com/svn/loader/lang-"+encodeURIComponent(D[m])+".js";L.insertBefore(e,L.firstChild)})(D[m]);for(var R=D.length,A=[],m=0,z=N.length;m<z;++m)A.push("https://google-code-prettify.googlecode.com/svn/loader/skins/"+encodeURIComponent(N[m])+".css");A.push("https://google-code-prettify.googlecode.com/svn/loader/prettify.css");(function(e){function j(m){if(m!==w){var n=x.createElement("link");n.rel="stylesheet";n.type="text/css";if(m+1<w)n.error= |
|
6 |
+n.onerror=function(){j(m+1)};n.href=e[m];L.appendChild(n)}}var w=e.length;j(0)})(A);var $=function(){window.PR_SHOULD_USE_CONTINUATION=!0;var e;(function(){function j(a){function d(f){var b=f.charCodeAt(0);if(b!==92)return b;var a=f.charAt(1);return(b=i[a])?b:"0"<=a&&a<="7"?parseInt(f.substring(1),8):a==="u"||a==="x"?parseInt(f.substring(2),16):f.charCodeAt(1)}function h(f){if(f<32)return(f<16?"\\x0":"\\x")+f.toString(16);f=String.fromCharCode(f);return f==="\\"||f==="-"||f==="]"||f==="^"?"\\"+f: |
|
7 |
+f}function b(f){var b=f.substring(1,f.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),f=[],a=b[0]==="^",c=["["];a&&c.push("^");for(var a=a?1:0,g=b.length;a<g;++a){var k=b[a];if(/\\[bdsw]/i.test(k))c.push(k);else{var k=d(k),o;a+2<g&&"-"===b[a+1]?(o=d(b[a+2]),a+=2):o=k;f.push([k,o]);o<65||k>122||(o<65||k>90||f.push([Math.max(65,k)|32,Math.min(o,90)|32]),o<97||k>122||f.push([Math.max(97,k)&-33,Math.min(o,122)&-33]))}}f.sort(function(f,a){return f[0]- |
|
8 |
+a[0]||a[1]-f[1]});b=[];g=[];for(a=0;a<f.length;++a)k=f[a],k[0]<=g[1]+1?g[1]=Math.max(g[1],k[1]):b.push(g=k);for(a=0;a<b.length;++a)k=b[a],c.push(h(k[0])),k[1]>k[0]&&(k[1]+1>k[0]&&c.push("-"),c.push(h(k[1])));c.push("]");return c.join("")}function e(f){for(var a=f.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),c=a.length,d=[],g=0,k=0;g<c;++g){var o=a[g];o==="("?++k:"\\"===o.charAt(0)&&(o=+o.substring(1))&&(o<=k?d[o]=-1:a[g]=h(o))}for(g= |
|
9 |
+1;g<d.length;++g)-1===d[g]&&(d[g]=++j);for(k=g=0;g<c;++g)o=a[g],o==="("?(++k,d[k]||(a[g]="(?:")):"\\"===o.charAt(0)&&(o=+o.substring(1))&&o<=k&&(a[g]="\\"+d[o]);for(g=0;g<c;++g)"^"===a[g]&&"^"!==a[g+1]&&(a[g]="");if(f.ignoreCase&&F)for(g=0;g<c;++g)o=a[g],f=o.charAt(0),o.length>=2&&f==="["?a[g]=b(o):f!=="\\"&&(a[g]=o.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var j=0,F=!1,l=!1,I=0,c=a.length;I<c;++I){var p=a[I];if(p.ignoreCase)l= |
|
10 |
+!0;else if(/[a-z]/i.test(p.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){F=!0;l=!1;break}}for(var i={b:8,t:9,n:10,v:11,f:12,r:13},q=[],I=0,c=a.length;I<c;++I){p=a[I];if(p.global||p.multiline)throw Error(""+p);q.push("(?:"+e(p)+")")}return RegExp(q.join("|"),l?"gi":"g")}function m(a,d){function h(a){var c=a.nodeType;if(c==1){if(!b.test(a.className)){for(c=a.firstChild;c;c=c.nextSibling)h(c);c=a.nodeName.toLowerCase();if("br"===c||"li"===c)e[l]="\n",F[l<<1]=j++,F[l++<<1|1]=a}}else if(c== |
|
11 |
+3||c==4)c=a.nodeValue,c.length&&(c=d?c.replace(/\r\n?/g,"\n"):c.replace(/[\t\n\r ]+/g," "),e[l]=c,F[l<<1]=j,j+=c.length,F[l++<<1|1]=a)}var b=/(?:^|\s)nocode(?:\s|$)/,e=[],j=0,F=[],l=0;h(a);return{a:e.join("").replace(/\n$/,""),d:F}}function n(a,d,h,b){d&&(a={a:d,e:a},h(a),b.push.apply(b,a.g))}function x(a){for(var d=void 0,h=a.firstChild;h;h=h.nextSibling)var b=h.nodeType,d=b===1?d?a:h:b===3?S.test(h.nodeValue)?a:d:d;return d===a?void 0:d}function C(a,d){function h(a){for(var l=a.e,j=[l,"pln"],c= |
|
12 |
+0,p=a.a.match(e)||[],m={},q=0,f=p.length;q<f;++q){var B=p[q],y=m[B],u=void 0,g;if(typeof y==="string")g=!1;else{var k=b[B.charAt(0)];if(k)u=B.match(k[1]),y=k[0];else{for(g=0;g<i;++g)if(k=d[g],u=B.match(k[1])){y=k[0];break}u||(y="pln")}if((g=y.length>=5&&"lang-"===y.substring(0,5))&&!(u&&typeof u[1]==="string"))g=!1,y="src";g||(m[B]=y)}k=c;c+=B.length;if(g){g=u[1];var o=B.indexOf(g),H=o+g.length;u[2]&&(H=B.length-u[2].length,o=H-g.length);y=y.substring(5);n(l+k,B.substring(0,o),h,j);n(l+k+o,g,A(y, |
|
13 |
+g),j);n(l+k+H,B.substring(H),h,j)}else j.push(l+k,y)}a.g=j}var b={},e;(function(){for(var h=a.concat(d),l=[],i={},c=0,p=h.length;c<p;++c){var m=h[c],q=m[3];if(q)for(var f=q.length;--f>=0;)b[q.charAt(f)]=m;m=m[1];q=""+m;i.hasOwnProperty(q)||(l.push(m),i[q]=r)}l.push(/[\S\s]/);e=j(l)})();var i=d.length;return h}function t(a){var d=[],h=[];a.tripleQuotedStrings?d.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/, |
|
14 |
+r,"'\""]):a.multiLineStrings?d.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,r,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,r,"\"'"]);a.verbatimStrings&&h.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,r]);var b=a.hashComments;b&&(a.cStyleComments?(b>1?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,r,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/, |
|
15 |
+r,"#"]),h.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,r])):d.push(["com",/^#[^\n\r]*/,r,"#"]));a.cStyleComments&&(h.push(["com",/^\/\/[^\n\r]*/,r]),h.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,r]));if(b=a.regexLiterals){var e=(b=b>1?"":"\n\r")?".":"[\\S\\s]";h.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+ |
|
16 |
+("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+e+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+e+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&h.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&h.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),r]);d.push(["pln",/^\s+/,r," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");h.push(["lit",/^@[$_a-z][\w$@]*/i,r],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,r],["pln",/^[$_a-z][\w$@]*/i,r],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i, |
|
17 |
+r,"0123456789"],["pln",/^\\[\S\s]?/,r],["pun",RegExp(b),r]);return C(d,h)}function z(a,d,h){function b(a){var c=a.nodeType;if(c==1&&!j.test(a.className))if("br"===a.nodeName)e(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&h){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(l.createTextNode(d),a.nextSibling),e(a),c||a.parentNode.removeChild(a)}} |
|
18 |
+function e(a){function b(a,c){var d=c?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),h=a.nextSibling;f.appendChild(d);for(var e=h;e;e=h)h=e.nextSibling,f.appendChild(e)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var j=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,l=a.ownerDocument,i=l.createElement("li");a.firstChild;)i.appendChild(a.firstChild);for(var c=[i],p=0;p<c.length;++p)b(c[p]);d===(d|0)&&c[0].setAttribute("value", |
|
19 |
+d);var n=l.createElement("ol");n.className="linenums";for(var d=Math.max(0,d-1|0)||0,p=0,q=c.length;p<q;++p)i=c[p],i.className="L"+(p+d)%10,i.firstChild||i.appendChild(l.createTextNode("\u00a0")),n.appendChild(i);a.appendChild(n)}function i(a,d){for(var h=d.length;--h>=0;){var b=d[h];U.hasOwnProperty(b)?V.console&&console.warn("cannot override language handler %s",b):U[b]=a}}function A(a,d){if(!a||!U.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return U[a]}function D(a){var d= |
|
20 |
+a.h;try{var h=m(a.c,a.i),b=h.a;a.a=b;a.d=h.d;a.e=0;A(d,b)(a);var e=/\bMSIE\s(\d+)/.exec(navigator.userAgent),e=e&&+e[1]<=8,d=/\n/g,i=a.a,j=i.length,h=0,l=a.d,n=l.length,b=0,c=a.g,p=c.length,t=0;c[p]=j;var q,f;for(f=q=0;f<p;)c[f]!==c[f+2]?(c[q++]=c[f++],c[q++]=c[f++]):f+=2;p=q;for(f=q=0;f<p;){for(var x=c[f],y=c[f+1],u=f+2;u+2<=p&&c[u+1]===y;)u+=2;c[q++]=x;c[q++]=y;f=u}c.length=q;var g=a.c,k;if(g)k=g.style.display,g.style.display="none";try{for(;b<n;){var o=l[b+2]||j,H=c[t+2]||j,u=Math.min(o,H),E=l[b+ |
|
21 |
+1],W;if(E.nodeType!==1&&(W=i.substring(h,u))){e&&(W=W.replace(d,"\r"));E.nodeValue=W;var Z=E.ownerDocument,s=Z.createElement("span");s.className=c[t+1];var z=E.parentNode;z.replaceChild(s,E);s.appendChild(E);h<o&&(l[b+1]=E=Z.createTextNode(i.substring(u,o)),z.insertBefore(E,s.nextSibling))}h=u;h>=o&&(b+=2);h>=H&&(t+=2)}}finally{if(g)g.style.display=k}}catch(v){V.console&&console.log(v&&v.stack||v)}}var V=window,G=["break,continue,do,else,for,if,return,while"],O=[[G,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], |
|
22 |
+"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],J=[O,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],K=[O,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"], |
|
23 |
+L=[K,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],O=[O,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],M=[G,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], |
|
24 |
+N=[G,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],R=[G,"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"],G=[G,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],Q=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/, |
|
25 |
+S=/\S/,T=t({keywords:[J,L,O,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",M,N,G],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),U={};i(T,["default-code"]);i(C([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-", |
|
26 |
+/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);i(C([["pln",/^\s+/,r," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,r,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/], |
|
27 |
+["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);i(C([],[["atv",/^[\S\s]+/]]),["uq.val"]);i(t({keywords:J,hashComments:!0,cStyleComments:!0,types:Q}),["c","cc","cpp","cxx","cyc","m"]);i(t({keywords:"null,true,false"}),["json"]);i(t({keywords:L,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:Q}), |
|
28 |
+["cs"]);i(t({keywords:K,cStyleComments:!0}),["java"]);i(t({keywords:G,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);i(t({keywords:M,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);i(t({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl","pm"]);i(t({keywords:N, |
|
29 |
+hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);i(t({keywords:O,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);i(t({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);i(t({keywords:R,cStyleComments:!0,multilineStrings:!0}),["rc","rs","rust"]); |
|
30 |
+i(C([],[["str",/^[\S\s]+/]]),["regex"]);var X=V.PR={createSimpleLexer:C,registerLangHandler:i,sourceDecorator:t,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:function(a,d,e){var b=document.createElement("div");b.innerHTML="<pre>"+a+"</pre>";b=b.firstChild;e&&z(b,e,!0);D({h:d,j:e,c:b,i:1});return b.innerHTML}, |
|
31 |
+prettyPrint:e=e=function(a,d){function e(){for(var b=V.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;p<j.length&&c.now()<b;p++){for(var d=j[p],m=k,l=d;l=l.previousSibling;){var n=l.nodeType,s=(n===7||n===8)&&l.nodeValue;if(s?!/^\??prettify\b/.test(s):n!==3||/\S/.test(l.nodeValue))break;if(s){m={};s.replace(/\b(\w+)=([\w%+\-.:]+)/g,function(a,b,c){m[b]=c});break}}l=d.className;if((m!==k||f.test(l))&&!w.test(l)){n=!1;for(s=d.parentNode;s;s=s.parentNode)if(g.test(s.tagName)&&s.className&&f.test(s.className)){n= |
|
32 |
+!0;break}if(!n){d.className+=" prettyprinted";n=m.lang;if(!n){var n=l.match(q),A;if(!n&&(A=x(d))&&u.test(A.tagName))n=A.className.match(q);n&&(n=n[1])}if(y.test(d.tagName))s=1;else var s=d.currentStyle,v=i.defaultView,s=(s=s?s.whiteSpace:v&&v.getComputedStyle?v.getComputedStyle(d,r).getPropertyValue("white-space"):0)&&"pre"===s.substring(0,3);v=m.linenums;if(!(v=v==="true"||+v))v=(v=l.match(/\blinenums\b(?::(\d+))?/))?v[1]&&v[1].length?+v[1]:!0:!1;v&&z(d,v,s);t={h:n,c:d,j:v,i:s};D(t)}}}p<j.length? |
|
33 |
+P(e,250):"function"===typeof a&&a()}for(var b=d||document.body,i=b.ownerDocument||document,b=[b.getElementsByTagName("pre"),b.getElementsByTagName("code"),b.getElementsByTagName("xmp")],j=[],m=0;m<b.length;++m)for(var l=0,n=b[m].length;l<n;++l)j.push(b[m][l]);var b=r,c=Date;c.now||(c={now:function(){return+new Date}});var p=0,t,q=/\blang(?:uage)?-([\w.]+)(?!\S)/,f=/\bprettyprint\b/,w=/\bprettyprinted\b/,y=/pre|xmp/i,u=/^code$/i,g=/^(?:pre|code|xmp)$/i,k={};e()}};typeof define==="function"&&define.amd&& |
|
34 |
+define("google-code-prettify",[],function(){return X})})();return e}();R||P(Q,0)})();}() |
... | ... |
@@ -5,8 +5,8 @@ |
5 | 5 |
<meta http-equiv="Pragma" content="no-cache"> |
6 | 6 |
<meta http-equiv="Expires" content="-1"> |
7 | 7 |
%= javascript '/mojo/jquery/jquery.js' |
8 |
+ %= javascript '/mojo/prettify/run_prettify.js' |
|
8 | 9 |
%= stylesheet '/mojo/prettify/prettify-mojo.css' |
9 |
- %= javascript '/mojo/prettify/prettify.js' |
|
10 | 10 |
%= stylesheet begin |
11 | 11 |
a img { border: 0 } |
12 | 12 |
body { |
... | ... |
@@ -97,7 +97,7 @@ |
97 | 97 |
} |
98 | 98 |
% end |
99 | 99 |
</head> |
100 |
- <body onload="prettyPrint()"> |
|
100 |
+ <body> |
|
101 | 101 |
%= include inline => app->renderer->_bundled('mojobar') |
102 | 102 |
<div id="wrapperlicious"> |
103 | 103 |
<div id="nothing" class="box spaced"></div> |
... | ... |
@@ -2,8 +2,8 @@ |
2 | 2 |
<html> |
3 | 3 |
<head> |
4 | 4 |
<title>Page not found</title> |
5 |
+ %= javascript '/mojo/prettify/run_prettify.js' |
|
5 | 6 |
%= stylesheet '/mojo/prettify/prettify-mojo.css' |
6 |
- %= javascript '/mojo/prettify/prettify.js' |
|
7 | 7 |
%= stylesheet begin |
8 | 8 |
body { |
9 | 9 |
background-color: #f5f6f8; |
... | ... |
@@ -73,7 +73,7 @@ |
73 | 73 |
} |
74 | 74 |
% end |
75 | 75 |
</head> |
76 |
- <body onload="prettyPrint()"> |
|
76 |
+ <body> |
|
77 | 77 |
%= include inline => app->renderer->_bundled('mojobar') |
78 | 78 |
<div id="wrapperlicious"> |
79 | 79 |
<div id="routes"> |
... | ... |
@@ -84,25 +84,24 @@ |
84 | 84 |
<code><%= $self->req->url->path %></code>, maybe you need to add a |
85 | 85 |
new one? |
86 | 86 |
</p> |
87 |
- % my $walk; |
|
88 |
- % $walk = begin |
|
89 |
- % my ($node, $depth) = @_; |
|
87 |
+ % my $walk = begin |
|
88 |
+ % my ($walk, $route, $depth) = @_; |
|
90 | 89 |
<tr> |
91 | 90 |
<td> |
92 |
- % my $pattern = $node->pattern->pattern || '/'; |
|
91 |
+ % my $pattern = $route->pattern->pattern || '/'; |
|
93 | 92 |
% $pattern = "+$pattern" if $depth; |
94 | 93 |
<pre><%= ' ' x $depth %><%= $pattern %></pre> |
95 | 94 |
</td> |
96 | 95 |
<td> |
97 |
- <pre><%= uc(join ',', @{$node->via || []}) || '*' %></pre> |
|
96 |
+ <pre><%= uc(join ',', @{$route->via || []}) || '*' %></pre> |
|
98 | 97 |
</td> |
99 | 98 |
<td> |
100 |
- % my $name = $node->name; |
|
101 |
- <pre><%= $node->has_custom_name ? qq{"$name"} : $name %></pre> |
|
99 |
+ % my $name = $route->name; |
|
100 |
+ <pre><%= $route->has_custom_name ? qq{"$name"} : $name %></pre> |
|
102 | 101 |
</td> |
103 | 102 |
</tr> |
104 | 103 |
% $depth++; |
105 |
- %= $walk->($_, $depth) for @{$node->children}; |
|
104 |
+ %= $walk->($walk, $_, $depth) for @{$route->children}; |
|
106 | 105 |
% $depth--; |
107 | 106 |
% end |
108 | 107 |
<table> |
... | ... |
@@ -111,7 +110,7 @@ |
111 | 110 |
<th>Methods</th> |
112 | 111 |
<th>Name</th> |
113 | 112 |
</tr> |
114 |
- %= $walk->($_, 0) for @{app->routes->children}; |
|
113 |
+ %= $walk->($walk, $_, 0) for @{app->routes->children}; |
|
115 | 114 |
</table> |
116 | 115 |
</div> |
117 | 116 |
</div> |
... | ... |
@@ -2,8 +2,8 @@ |
2 | 2 |
<html> |
3 | 3 |
<head> |
4 | 4 |
<title><%= $title %></title> |
5 |
+ %= javascript '/mojo/prettify/run_prettify.js' |
|
5 | 6 |
%= stylesheet '/mojo/prettify/prettify-mojo.css' |
6 |
- %= javascript '/mojo/prettify/prettify.js' |
|
7 | 7 |
%= stylesheet begin |
8 | 8 |
a { color: inherit } |
9 | 9 |
a:hover { color: #2a2a2a } |
... | ... |
@@ -61,7 +61,7 @@ |
61 | 61 |
} |
62 | 62 |
% end |
63 | 63 |
</head> |
64 |
- <body onload="prettyPrint()"> |
|
64 |
+ <body> |
|
65 | 65 |
%= include inline => app->renderer->_bundled('mojobar') |
66 | 66 |
% my $link = begin |
67 | 67 |
%= link_to shift, shift, class => "mojoscroll" |
... | ... |
@@ -102,10 +102,18 @@ sub element_exists_not { |
102 | 102 |
} |
103 | 103 |
|
104 | 104 |
sub finish_ok { |
105 |
- my ($self, $desc) = @_; |
|
106 |
- $self->tx->finish; |
|
105 |
+ my $self = shift; |
|
106 |
+ $self->tx->finish(@_); |
|
107 |
+ Mojo::IOLoop->one_tick while !$self->{finished}; |
|
108 |
+ return $self->_test('ok', 1, 'closed WebSocket'); |
|
109 |
+} |
|
110 |
+ |
|
111 |
+sub finished_ok { |
|
112 |
+ my ($self, $code) = @_; |
|
107 | 113 |
Mojo::IOLoop->one_tick while !$self->{finished}; |
108 |
- return $self->_test('ok', 1, $desc || 'finished websocket'); |
|
114 |
+ Test::More::diag "WebSocket closed with status $self->{finished}[0]" |
|
115 |
+ unless my $ok = grep { $self->{finished}[0] == $_ } $code, 1006; |
|
116 |
+ return $self->_test('ok', $ok, "WebSocket closed with status $code"); |
|
109 | 117 |
} |
110 | 118 |
|
111 | 119 |
sub get_ok { shift->_request_ok(get => @_) } |
... | ... |
@@ -138,12 +146,6 @@ sub header_unlike { |
138 | 146 |
$regex, $desc || "$name is not similar"); |
139 | 147 |
} |
140 | 148 |
|
141 |
-sub json_content_is { |
|
142 |
- my ($self, $data, $desc) = @_; |
|
143 |
- $desc ||= 'exact match for JSON structure'; |
|
144 |
- return $self->_test('is_deeply', $self->tx->res->json, $data, $desc); |
|
145 |
-} |
|
146 |
- |
|
147 | 149 |
sub json_has { |
148 | 150 |
my ($self, $p, $desc) = @_; |
149 | 151 |
$desc ||= qq{has value for JSON Pointer "$p"}; |
... | ... |
@@ -159,8 +161,9 @@ sub json_hasnt { |
159 | 161 |
} |
160 | 162 |
|
161 | 163 |
sub json_is { |
162 |
- my ($self, $p, $data, $desc) = @_; |
|
163 |
- $desc ||= qq{exact match for JSON Pointer "$p"}; |
|
164 |
+ my $self = shift; |
|
165 |
+ my ($p, $data) = ref $_[0] ? ('', shift) : (shift, shift); |
|
166 |
+ my $desc = shift || qq{exact match for JSON Pointer "$p"}; |
|
164 | 167 |
return $self->_test('is_deeply', $self->tx->res->json($p), $data, $desc); |
165 | 168 |
} |
166 | 169 |
|
... | ... |
@@ -177,8 +180,9 @@ sub json_message_hasnt { |
177 | 180 |
} |
178 | 181 |
|
179 | 182 |
sub json_message_is { |
180 |
- my ($self, $p, $data, $desc) = @_; |
|
181 |
- $desc ||= qq{exact match for JSON Pointer "$p"}; |
|
183 |
+ my $self = shift; |
|
184 |
+ my ($p, $data) = ref $_[0] ? ('', shift) : (shift, shift); |
|
185 |
+ my $desc = shift || qq{exact match for JSON Pointer "$p"}; |
|
182 | 186 |
return $self->_test('is_deeply', $self->_json(get => $p), $data, $desc); |
183 | 187 |
} |
184 | 188 |
|
... | ... |
@@ -199,7 +203,7 @@ sub message_like { |
199 | 203 |
|
200 | 204 |
sub message_ok { |
201 | 205 |
my ($self, $desc) = @_; |
202 |
- return $self->_test('ok', !!$self->_wait(1), $desc, 'message received'); |
|
206 |
+ return $self->_test('ok', !!$self->_wait, $desc || 'message received'); |
|
203 | 207 |
} |
204 | 208 |
|
205 | 209 |
sub message_unlike { |
... | ... |
@@ -217,20 +221,7 @@ sub or { |
217 | 221 |
|
218 | 222 |
sub patch_ok { shift->_request_ok(patch => @_) } |
219 | 223 |
sub post_ok { shift->_request_ok(post => @_) } |
220 |
- |
|
221 |
-sub post_form_ok { |
|
222 |
- my ($self, $url) = (shift, shift); |
|
223 |
- my $tx = $self->tx($self->ua->post_form($url, @_))->tx; |
|
224 |
- return $self->_test('ok', $tx->is_finished, encode('UTF-8', "post $url")); |
|
225 |
-} |
|
226 |
- |
|
227 |
-sub post_json_ok { |
|
228 |
- my ($self, $url) = (shift, shift); |
|
229 |
- my $tx = $self->tx($self->ua->post_json($url, @_))->tx; |
|
230 |
- return $self->_test('ok', $tx->is_finished, encode('UTF-8', "post $url")); |
|
231 |
-} |
|
232 |
- |
|
233 |
-sub put_ok { shift->_request_ok(put => @_) } |
|
224 |
+sub put_ok { shift->_request_ok(put => @_) } |
|
234 | 225 |
|
235 | 226 |
sub request_ok { |
236 | 227 |
my $self = shift; |
... | ... |
@@ -293,12 +284,12 @@ sub websocket_ok { |
293 | 284 |
|
294 | 285 |
# Establish WebSocket connection |
295 | 286 |
$self->{messages} = []; |
296 |
- $self->{finished} = 0; |
|
287 |
+ $self->{finished} = undef; |
|
297 | 288 |
$self->ua->websocket( |
298 | 289 |
$url => @_ => sub { |
299 |
- my $tx = pop; |
|
290 |
+ my ($ua, $tx) = @_; |
|
300 | 291 |
$self->tx($tx); |
301 |
- $tx->on(finish => sub { $self->{finished} = 1 }); |
|
292 |
+ $tx->on(finish => sub { shift; $self->{finished} = [@_] }); |
|
302 | 293 |
$tx->on(binary => sub { push @{$self->{messages}}, [binary => pop] }); |
303 | 294 |
$tx->on(text => sub { push @{$self->{messages}}, [text => pop] }); |
304 | 295 |
Mojo::IOLoop->stop; |
... | ... |
@@ -306,7 +297,7 @@ sub websocket_ok { |
306 | 297 |
); |
307 | 298 |
Mojo::IOLoop->start; |
308 | 299 |
|
309 |
- my $desc = encode 'UTF-8', "websocket $url"; |
|
300 |
+ my $desc = encode 'UTF-8', "WebSocket $url"; |
|
310 | 301 |
return $self->_test('ok', $self->tx->res->code eq 101, $desc); |
311 | 302 |
} |
312 | 303 |
|
... | ... |
@@ -320,13 +311,13 @@ sub _get_content { |
320 | 311 |
sub _json { |
321 | 312 |
my ($self, $method, $p) = @_; |
322 | 313 |
return Mojo::JSON::Pointer->new->$method( |
323 |
- Mojo::JSON->new->decode(@{$self->_wait || []}[1]), $p); |
|
314 |
+ Mojo::JSON->new->decode(@{$self->message}[1]), $p); |
|
324 | 315 |
} |
325 | 316 |
|
326 | 317 |
sub _message { |
327 | 318 |
my ($self, $name, $value, $desc) = @_; |
328 | 319 |
local $Test::Builder::Level = $Test::Builder::Level + 1; |
329 |
- my ($type, $msg) = @{$self->_wait || ['']}; |
|
320 |
+ my ($type, $msg) = @{$self->message}; |
|
330 | 321 |
|
331 | 322 |
# Type check |
332 | 323 |
if (ref $value eq 'HASH') { |
... | ... |
@@ -342,16 +333,14 @@ sub _message { |
342 | 333 |
} |
343 | 334 |
|
344 | 335 |
sub _request_ok { |
345 |
- my ($self, $method, $url, $headers, $body) = @_; |
|
346 |
- $body = $headers if !ref $headers && @_ > 3; |
|
347 |
- $headers = {} if !ref $headers; |
|
336 |
+ my ($self, $method, $url) = (shift, shift, shift); |
|
348 | 337 |
|
349 | 338 |
# Perform request against application |
350 |
- $self->tx($self->ua->$method($url, $headers, $body)); |
|
339 |
+ $self->tx($self->ua->$method($url, @_)); |
|
351 | 340 |
local $Test::Builder::Level = $Test::Builder::Level + 1; |
352 | 341 |
my ($err, $code) = $self->tx->error; |
353 | 342 |
Test::More::diag $err if !(my $ok = !$err || $code) && $err; |
354 |
- return $self->_test('ok', $ok, encode('UTF-8', "$method $url")); |
|
343 |
+ return $self->_test('ok', $ok, encode('UTF-8', "@{[uc $method]} $url")); |
|
355 | 344 |
} |
356 | 345 |
|
357 | 346 |
sub _test { |
... | ... |
@@ -367,22 +356,15 @@ sub _text { |
367 | 356 |
} |
368 | 357 |
|
369 | 358 |
sub _wait { |
370 |
- my ($self, $wait) = @_; |
|
371 |
- |
|
372 |
- # DEPRECATED in Rainbow! |
|
373 |
- my $new = $self->{new} = defined $self->{new} ? $self->{new} : $wait; |
|
374 |
- warn <<EOF unless $new; |
|
375 |
-Testing WebSocket messages without Test::Mojo->message_ok is DEPRECATED!!! |
|
376 |
-EOF |
|
377 |
- return $self->message if $new && !$wait; |
|
378 |
- |
|
379 |
- # Wait for message |
|
359 |
+ my $self = shift; |
|
380 | 360 |
Mojo::IOLoop->one_tick while !$self->{finished} && !@{$self->{messages}}; |
381 | 361 |
return $self->message(shift @{$self->{messages}})->message; |
382 | 362 |
} |
383 | 363 |
|
384 | 364 |
1; |
385 | 365 |
|
366 |
+=encoding utf8 |
|
367 |
+ |
|
386 | 368 |
=head1 NAME |
387 | 369 |
|
388 | 370 |
Test::Mojo - Testing Mojo! |
... | ... |
@@ -398,10 +380,10 @@ Test::Mojo - Testing Mojo! |
398 | 380 |
$t->get_ok('/welcome')->status_is(200)->text_is('div#message' => 'Hello!'); |
399 | 381 |
|
400 | 382 |
# JSON |
401 |
- $t->post_form_ok('/search.json' => {q => 'Perl'}) |
|
383 |
+ $t->post_ok('/search.json' => form => {q => 'Perl'}) |
|
402 | 384 |
->status_is(200) |
403 |
- ->header_is('X-Powered-By' => 'Mojolicious (Perl)') |
|
404 |
- ->header_isnt('X-Bender' => 'Bite my shiny metal ass!'); |
|
385 |
+ ->header_is('Server' => 'Mojolicious (Perl)') |
|
386 |
+ ->header_isnt('X-Bender' => 'Bite my shiny metal ass!') |
|
405 | 387 |
->json_is('/results/4/title' => 'Perl rocks!'); |
406 | 388 |
|
407 | 389 |
# WebSocket |
... | ... |
@@ -444,7 +426,7 @@ Current transaction, usually a L<Mojo::Transaction::HTTP> object. |
444 | 426 |
|
445 | 427 |
# More specific tests |
446 | 428 |
is $t->tx->res->json->{foo}, 'bar', 'right value'; |
447 |
- ok $t->tx->res->is_multipart, 'multipart content'; |
|
429 |
+ ok $t->tx->res->content->is_multipart, 'multipart content'; |
|
448 | 430 |
|
449 | 431 |
# Test custom transactions |
450 | 432 |
$t->tx($t->tx->previous)->status_is(302)->header_like(Location => qr/foo/); |
... | ... |
@@ -459,15 +441,18 @@ User agent used for testing, defaults to a L<Mojo::UserAgent> object. |
459 | 441 |
# Allow redirects |
460 | 442 |
$t->ua->max_redirects(10); |
461 | 443 |
|
444 |
+ # Use absolute URL for request with Basic authentication |
|
445 |
+ my $url = $t->ua->app_url->userinfo('sri:secr3t')->path('/secrets.json'); |
|
446 |
+ $t->post_ok($url => json => {limit => 10}) |
|
447 |
+ ->status_is(200) |
|
448 |
+ ->json_is('/1/content', 'Mojo rocks!'); |
|
449 |
+ |
|
462 | 450 |
# Customize all transactions (including followed redirects) |
463 | 451 |
$t->ua->on(start => sub { |
464 | 452 |
my ($ua, $tx) = @_; |
465 | 453 |
$tx->req->headers->accept_language('en-US'); |
466 | 454 |
}); |
467 | 455 |
|
468 |
- # Request with Basic authentication |
|
469 |
- $t->get_ok($t->ua->app_url->userinfo('sri:secr3t')->path('/secrets')); |
|
470 |
- |
|
471 | 456 |
=head1 METHODS |
472 | 457 |
|
473 | 458 |
L<Test::Mojo> inherits all methods from L<Mojo::Base> and implements the |
... | ... |
@@ -566,9 +551,11 @@ Opposite of C<content_type_like>. |
566 | 551 |
|
567 | 552 |
$t = $t->delete_ok('/foo'); |
568 | 553 |
$t = $t->delete_ok('/foo' => {DNT => 1} => 'Hi!'); |
554 |
+ $t = $t->delete_ok('/foo' => {DNT => 1} => form => {a => 'b'}); |
|
555 |
+ $t = $t->delete_ok('/foo' => {DNT => 1} => json => {a => 'b'}); |
|
569 | 556 |
|
570 | 557 |
Perform a C<DELETE> request and check for transport errors, takes the same |
571 |
-arguments as L<Mojo::UserAgent/"delete">. |
|
558 |
+arguments as L<Mojo::UserAgent/"delete">, except for the callback. |
|
572 | 559 |
|
573 | 560 |
=head2 element_exists |
574 | 561 |
|
... | ... |
@@ -588,25 +575,36 @@ Opposite of C<element_exists>. |
588 | 575 |
=head2 finish_ok |
589 | 576 |
|
590 | 577 |
$t = $t->finish_ok; |
591 |
- $t = $t->finish_ok('finished successfully'); |
|
578 |
+ $t = $t->finish_ok(1000); |
|
579 |
+ $t = $t->finish_ok(1003 => 'Cannot accept data!'); |
|
580 |
+ |
|
581 |
+Close WebSocket connection gracefully. |
|
582 |
+ |
|
583 |
+=head2 finished_ok |
|
592 | 584 |
|
593 |
-Finish C<WebSocket> connection. |
|
585 |
+ $t = $t->finished_ok(1000); |
|
586 |
+ |
|
587 |
+Wait for WebSocket connection to be closed gracefully and check status. |
|
594 | 588 |
|
595 | 589 |
=head2 get_ok |
596 | 590 |
|
597 | 591 |
$t = $t->get_ok('/foo'); |
598 | 592 |
$t = $t->get_ok('/foo' => {DNT => 1} => 'Hi!'); |
593 |
+ $t = $t->get_ok('/foo' => {DNT => 1} => form => {a => 'b'}); |
|
594 |
+ $t = $t->get_ok('/foo' => {DNT => 1} => json => {a => 'b'}); |
|
599 | 595 |
|
600 | 596 |
Perform a C<GET> request and check for transport errors, takes the same |
601 |
-arguments as L<Mojo::UserAgent/"get">. |
|
597 |
+arguments as L<Mojo::UserAgent/"get">, except for the callback. |
|
602 | 598 |
|
603 | 599 |
=head2 head_ok |
604 | 600 |
|
605 | 601 |
$t = $t->head_ok('/foo'); |
606 | 602 |
$t = $t->head_ok('/foo' => {DNT => 1} => 'Hi!'); |
603 |
+ $t = $t->head_ok('/foo' => {DNT => 1} => form => {a => 'b'}); |
|
604 |
+ $t = $t->head_ok('/foo' => {DNT => 1} => json => {a => 'b'}); |
|
607 | 605 |
|
608 | 606 |
Perform a C<HEAD> request and check for transport errors, takes the same |
609 |
-arguments as L<Mojo::UserAgent/"head">. |
|
607 |
+arguments as L<Mojo::UserAgent/"head">, except for the callback. |
|
610 | 608 |
|
611 | 609 |
=head2 header_is |
612 | 610 |
|
... | ... |
@@ -636,14 +634,6 @@ Check response header for similar match. |
636 | 634 |
|
637 | 635 |
Opposite of C<header_like>. |
638 | 636 |
|
639 |
-=head2 json_content_is |
|
640 |
- |
|
641 |
- $t = $t->json_content_is([1, 2, 3]); |
|
642 |
- $t = $t->json_content_is([1, 2, 3], 'right content'); |
|
643 |
- $t = $t->json_content_is({foo => 'bar', baz => 23}, 'right content'); |
|
644 |
- |
|
645 |
-Check response content for JSON data. |
|
646 |
- |
|
647 | 637 |
=head2 json_has |
648 | 638 |
|
649 | 639 |
$t = $t->json_has('/foo'); |
... | ... |
@@ -661,12 +651,13 @@ Opposite of C<json_has>. |
661 | 651 |
|
662 | 652 |
=head2 json_is |
663 | 653 |
|
664 |
- $t = $t->json_is('/' => {foo => [1, 2, 3]}); |
|
654 |
+ $t = $t->json_is({foo => [1, 2, 3]}); |
|
655 |
+ $t = $t->json_is({foo => [1, 2, 3]}, 'right content'); |
|
665 | 656 |
$t = $t->json_is('/foo' => [1, 2, 3]); |
666 | 657 |
$t = $t->json_is('/foo/1' => 2, 'right value'); |
667 | 658 |
|
668 | 659 |
Check the value extracted from JSON response using the given JSON Pointer with |
669 |
-L<Mojo::JSON::Pointer>. |
|
660 |
+L<Mojo::JSON::Pointer>, which defaults to the root value if it is omitted. |
|
670 | 661 |
|
671 | 662 |
=head2 json_message_has |
672 | 663 |
|
... | ... |
@@ -685,12 +676,14 @@ Opposite of C<json_message_has>. |
685 | 676 |
|
686 | 677 |
=head2 json_message_is |
687 | 678 |
|
688 |
- $t = $t->json_message_is('/' => {foo => [1, 2, 3]}); |
|
679 |
+ $t = $t->json_message_is({foo => [1, 2, 3]}); |
|
680 |
+ $t = $t->json_message_is({foo => [1, 2, 3]}, 'right content'); |
|
689 | 681 |
$t = $t->json_message_is('/foo' => [1, 2, 3]); |
690 | 682 |
$t = $t->json_message_is('/foo/1' => 2, 'right value'); |
691 | 683 |
|
692 | 684 |
Check the value extracted from JSON WebSocket message using the given JSON |
693 |
-Pointer with L<Mojo::JSON::Pointer>. |
|
685 |
+Pointer with L<Mojo::JSON::Pointer>, which defaults to the root value if it is |
|
686 |
+omitted. |
|
694 | 687 |
|
695 | 688 |
=head2 message_is |
696 | 689 |
|
... | ... |
@@ -746,9 +739,11 @@ Opposite of C<message_like>. |
746 | 739 |
|
747 | 740 |
$t = $t->options_ok('/foo'); |
748 | 741 |
$t = $t->options_ok('/foo' => {DNT => 1} => 'Hi!'); |
742 |
+ $t = $t->options_ok('/foo' => {DNT => 1} => form => {a => 'b'}); |
|
743 |
+ $t = $t->options_ok('/foo' => {DNT => 1} => json => {a => 'b'}); |
|
749 | 744 |
|
750 | 745 |
Perform a C<OPTIONS> request and check for transport errors, takes the same |
751 |
-arguments as L<Mojo::UserAgent/"options">. |
|
746 |
+arguments as L<Mojo::UserAgent/"options">, except for the callback. |
|
752 | 747 |
|
753 | 748 |
=head2 or |
754 | 749 |
|
... | ... |
@@ -764,49 +759,40 @@ Invoke callback if previous test failed. |
764 | 759 |
|
765 | 760 |
$t = $t->patch_ok('/foo'); |
766 | 761 |
$t = $t->patch_ok('/foo' => {DNT => 1} => 'Hi!'); |
762 |
+ $t = $t->patch_ok('/foo' => {DNT => 1} => form => {a => 'b'}); |
|
763 |
+ $t = $t->patch_ok('/foo' => {DNT => 1} => json => {a => 'b'}); |
|
767 | 764 |
|
768 | 765 |
Perform a C<PATCH> request and check for transport errors, takes the same |
769 |
-arguments as L<Mojo::UserAgent/"patch">. |
|
766 |
+arguments as L<Mojo::UserAgent/"patch">, except for the callback. |
|
770 | 767 |
|
771 | 768 |
=head2 post_ok |
772 | 769 |
|
773 | 770 |
$t = $t->post_ok('/foo'); |
774 | 771 |
$t = $t->post_ok('/foo' => {DNT => 1} => 'Hi!'); |
772 |
+ $t = $t->post_ok('/foo' => {DNT => 1} => form => {a => 'b'}); |
|
773 |
+ $t = $t->post_ok('/foo' => {DNT => 1} => json => {a => 'b'}); |
|
775 | 774 |
|
776 | 775 |
Perform a C<POST> request and check for transport errors, takes the same |
777 |
-arguments as L<Mojo::UserAgent/"post">. |
|
778 |
- |
|
779 |
-=head2 post_form_ok |
|
780 |
- |
|
781 |
- $t = $t->post_form_ok('/foo' => {a => 'b'}); |
|
782 |
- $t = $t->post_form_ok('/foo' => 'UTF-8' => {a => 'b'} => {DNT => 1}); |
|
783 |
- |
|
784 |
-Perform a C<POST> request with form data and check for transport errors, takes |
|
785 |
-the same arguments as L<Mojo::UserAgent/"post_form">. |
|
776 |
+arguments as L<Mojo::UserAgent/"post">, except for the callback. |
|
786 | 777 |
|
787 | 778 |
# Test file upload |
788 |
- $t->post_form_ok('/upload' => {foo => {content => 'bar'}})->status_is(200); |
|
789 |
- |
|
790 |
-=head2 post_json_ok |
|
791 |
- |
|
792 |
- $t = $t->post_json_ok('/foo' => {a => 'b'}); |
|
793 |
- $t = $t->post_json_ok('/foo' => {a => 'b'} => {DNT => 1}); |
|
794 |
- |
|
795 |
-Perform a C<POST> request with JSON data and check for transport errors, takes |
|
796 |
-the same arguments as L<Mojo::UserAgent/"post_json">. |
|
779 |
+ $t->post_ok('/upload' => form => {foo => {content => 'bar'}}) |
|
780 |
+ ->status_is(200); |
|
797 | 781 |
|
798 | 782 |
# Test JSON API |
799 |
- $t->post_json_ok('/hello.json' => {hello => 'world'}) |
|
783 |
+ $t->post_json_ok('/hello.json' => json => {hello => 'world'}) |
|
800 | 784 |
->status_is(200) |
801 |
- ->json_content_is({bye => 'world'}); |
|
785 |
+ ->json_is({bye => 'world'}); |
|
802 | 786 |
|
803 | 787 |
=head2 put_ok |
804 | 788 |
|
805 | 789 |
$t = $t->put_ok('/foo'); |
806 | 790 |
$t = $t->put_ok('/foo' => {DNT => 1} => 'Hi!'); |
791 |
+ $t = $t->put_ok('/foo' => {DNT => 1} => form => {a => 'b'}); |
|
792 |
+ $t = $t->put_ok('/foo' => {DNT => 1} => json => {a => 'b'}); |
|
807 | 793 |
|
808 | 794 |
Perform a C<PUT> request and check for transport errors, takes the same |
809 |
-arguments as L<Mojo::UserAgent/"put">. |
|
795 |
+arguments as L<Mojo::UserAgent/"put">, except for the callback. |
|
810 | 796 |
|
811 | 797 |
=head2 request_ok |
812 | 798 |
|
... | ... |
@@ -815,12 +801,9 @@ arguments as L<Mojo::UserAgent/"put">. |
815 | 801 |
|
816 | 802 |
Perform request and check for transport errors. |
817 | 803 |
|
818 |
- # Customize transaction |
|
819 |
- my $tx = $t->ua->build_json_tx('/user/99' => {name => 'sri'}); |
|
820 |
- $tx->req->method('PUT'); |
|
821 |
- $t->request_ok($tx) |
|
822 |
- ->status_is(200) |
|
823 |
- ->json_is('/message' => 'User has been replaced.'); |
|
804 |
+ # Request with custom method |
|
805 |
+ my $tx = $t->ua->build_tx(FOO => '/test.json' => json => {foo => 1}); |
|
806 |
+ $t->request_ok($tx)->status_is(200)->json_is({success => 1}); |
|
824 | 807 |
|
825 | 808 |
=head2 reset_session |
826 | 809 |
|
... | ... |
@@ -832,12 +815,20 @@ Reset user agent session. |
832 | 815 |
|
833 | 816 |
$t = $t->send_ok({binary => $bytes}); |
834 | 817 |
$t = $t->send_ok({text => $bytes}); |
818 |
+ $t = $t->send_ok({json => {test => [1, 2, 3]}}); |
|
835 | 819 |
$t = $t->send_ok([$fin, $rsv1, $rsv2, $rsv3, $op, $payload]); |
836 | 820 |
$t = $t->send_ok($chars); |
837 | 821 |
$t = $t->send_ok($chars, 'sent successfully'); |
838 | 822 |
|
839 | 823 |
Send message or frame via WebSocket. |
840 | 824 |
|
825 |
+ # Send JSON object as "Text" message |
|
826 |
+ $t->websocket_ok('/echo.json') |
|
827 |
+ ->send_ok({json => {test => 'I ♥ Mojolicious!'}}) |
|
828 |
+ ->message_ok |
|
829 |
+ ->json_message_is('/test' => 'I ♥ Mojolicious!') |
|
830 |
+ ->finish_ok; |
|
831 |
+ |
|
841 | 832 |
=head2 status_is |
842 | 833 |
|
843 | 834 |
$t = $t->status_is(200); |
... | ... |
@@ -885,10 +876,10 @@ Opposite of C<text_like>. |
885 | 876 |
=head2 websocket_ok |
886 | 877 |
|
887 | 878 |
$t = $t->websocket_ok('/echo'); |
888 |
- $t = $t->websocket_ok('/echo' => {DNT => 1}); |
|
879 |
+ $t = $t->websocket_ok('/echo' => {DNT => 1} => ['v1.proto']); |
|
889 | 880 |
|
890 |
-Open a C<WebSocket> connection with transparent handshake, takes the same |
|
891 |
-arguments as L<Mojo::UserAgent/"websocket">. |
|
881 |
+Open a WebSocket connection with transparent handshake, takes the same |
|
882 |
+arguments as L<Mojo::UserAgent/"websocket">, except for the callback. |
|
892 | 883 |
|
893 | 884 |
=head1 SEE ALSO |
894 | 885 |
|
... | ... |
@@ -29,14 +29,12 @@ sub import { |
29 | 29 |
a => sub { $caller->can('any')->(@_) and return $UA->app }, |
30 | 30 |
b => \&b, |
31 | 31 |
c => \&c, |
32 |
- d => sub { _request($UA->build_tx(DELETE => @_)) }, |
|
33 |
- f => sub { _request($UA->build_form_tx(@_)) }, |
|
34 |
- g => sub { _request($UA->build_tx(GET => @_)) }, |
|
35 |
- h => sub { _request($UA->build_tx(HEAD => @_)) }, |
|
32 |
+ d => sub { _request($UA->build_tx(DELETE => @_)) }, |
|
33 |
+ g => sub { _request($UA->build_tx(GET => @_)) }, |
|
34 |
+ h => sub { _request($UA->build_tx(HEAD => @_)) }, |
|
36 | 35 |
j => \&j, |
37 |
- n => sub { _request($UA->build_json_tx(@_)) }, |
|
38 | 36 |
o => sub { _request($UA->build_tx(OPTIONS => @_)) }, |
39 |
- p => sub { _request($UA->build_tx(POST => @_)) }, |
|
37 |
+ p => sub { _request($UA->build_tx(POST => @_)) }, |
|
40 | 38 |
r => sub { $UA->app->dumper(@_) }, |
41 | 39 |
t => sub { _request($UA->build_tx(PATCH => @_)) }, |
42 | 40 |
u => sub { _request($UA->build_tx(PUT => @_)) }, |
... | ... |
@@ -65,14 +63,14 @@ ojo - Fun oneliners with Mojo! |
65 | 63 |
|
66 | 64 |
A collection of automatically exported functions for fun Perl oneliners. Ten |
67 | 65 |
redirects will be followed by default, you can change this behavior with the |
68 |
-C<MOJO_MAX_REDIRECTS> environment variable. |
|
66 |
+MOJO_MAX_REDIRECTS environment variable. |
|
69 | 67 |
|
70 |
- $ MOJO_MAX_REDIRECTS=0 perl -Mojo -E 'say g("mojolicio.us")->code' |
|
68 |
+ $ MOJO_MAX_REDIRECTS=0 perl -Mojo -E 'say g("example.com")->code' |
|
71 | 69 |
|
72 | 70 |
Proxy detection is enabled by default, but you can disable it with the |
73 |
-C<MOJO_PROXY> environment variable. |
|
71 |
+MOJO_PROXY environment variable. |
|
74 | 72 |
|
75 |
- $ MOJO_PROXY=0 perl -Mojo -E 'say g("mojolicio.us")->body' |
|
73 |
+ $ MOJO_PROXY=0 perl -Mojo -E 'say g("example.com")->body' |
|
76 | 74 |
|
77 | 75 |
=head1 FUNCTIONS |
78 | 76 |
|
... | ... |
@@ -104,24 +102,16 @@ Turn list into a L<Mojo::Collection> object. |
104 | 102 |
|
105 | 103 |
=head2 d |
106 | 104 |
|
107 |
- my $res = d('mojolicio.us'); |
|
108 |
- my $res = d('http://mojolicio.us' => {DNT => 1} => 'Hi!'); |
|
105 |
+ my $res = d('example.com'); |
|
106 |
+ my $res = d('http://example.com' => {DNT => 1} => 'Hi!'); |
|
109 | 107 |
|
110 | 108 |
Perform C<DELETE> request with L<Mojo::UserAgent/"delete"> and return |
111 | 109 |
resulting L<Mojo::Message::Response> object. |
112 | 110 |
|
113 |
-=head2 f |
|
114 |
- |
|
115 |
- my $res = f('http://kraih.com' => {a => 'b'}); |
|
116 |
- my $res = f('kraih.com' => 'UTF-8' => {a => 'b'} => {DNT => 1}); |
|
117 |
- |
|
118 |
-Perform C<POST> request with L<Mojo::UserAgent/"post_form"> and return |
|
119 |
-resulting L<Mojo::Message::Response> object. |
|
120 |
- |
|
121 | 111 |
=head2 g |
122 | 112 |
|
123 |
- my $res = g('mojolicio.us'); |
|
124 |
- my $res = g('http://mojolicio.us' => {DNT => 1} => 'Hi!'); |
|
113 |
+ my $res = g('example.com'); |
|
114 |
+ my $res = g('http://example.com' => {DNT => 1} => 'Hi!'); |
|
125 | 115 |
|
126 | 116 |
Perform C<GET> request with L<Mojo::UserAgent/"get"> and return resulting |
127 | 117 |
L<Mojo::Message::Response> object. |
... | ... |
@@ -130,8 +120,8 @@ L<Mojo::Message::Response> object. |
130 | 120 |
|
131 | 121 |
=head2 h |
132 | 122 |
|
133 |
- my $res = h('mojolicio.us'); |
|
134 |
- my $res = h('http://mojolicio.us' => {DNT => 1} => 'Hi!'); |
|
123 |
+ my $res = h('example.com'); |
|
124 |
+ my $res = h('http://example.com' => {DNT => 1} => 'Hi!'); |
|
135 | 125 |
|
136 | 126 |
Perform C<HEAD> request with L<Mojo::UserAgent/"head"> and return resulting |
137 | 127 |
L<Mojo::Message::Response> object. |
... | ... |
@@ -146,26 +136,18 @@ Encode Perl data structure or decode JSON with L<Mojo::JSON>. |
146 | 136 |
|
147 | 137 |
$ perl -Mojo -E 'b(j({hello => "world!"}))->spurt("hello.json")' |
148 | 138 |
|
149 |
-=head2 n |
|
150 |
- |
|
151 |
- my $res = n('http://kraih.com' => {a => 'b'}); |
|
152 |
- my $res = n('kraih.com' => {a => 'b'} => {DNT => 1}); |
|
153 |
- |
|
154 |
-Perform C<POST> request with L<Mojo::UserAgent/"post_json"> and return |
|
155 |
-resulting L<Mojo::Message::Response> object. |
|
156 |
- |
|
157 | 139 |
=head2 o |
158 | 140 |
|
159 |
- my $res = o('mojolicio.us'); |
|
160 |
- my $res = o('http://mojolicio.us' => {DNT => 1} => 'Hi!'); |
|
141 |
+ my $res = o('example.com'); |
|
142 |
+ my $res = o('http://example.com' => {DNT => 1} => 'Hi!'); |
|
161 | 143 |
|
162 | 144 |
Perform C<OPTIONS> request with L<Mojo::UserAgent/"options"> and return |
163 | 145 |
resulting L<Mojo::Message::Response> object. |
164 | 146 |
|
165 | 147 |
=head2 p |
166 | 148 |
|
167 |
- my $res = p('mojolicio.us'); |
|
168 |
- my $res = p('http://mojolicio.us' => {DNT => 1} => 'Hi!'); |
|
149 |
+ my $res = p('example.com'); |
|
150 |
+ my $res = p('http://example.com' => {DNT => 1} => 'Hi!'); |
|
169 | 151 |
|
170 | 152 |
Perform C<POST> request with L<Mojo::UserAgent/"post"> and return resulting |
171 | 153 |
L<Mojo::Message::Response> object. |
... | ... |
@@ -176,20 +158,20 @@ L<Mojo::Message::Response> object. |
176 | 158 |
|
177 | 159 |
Dump a Perl data structure with L<Data::Dumper>. |
178 | 160 |
|
179 |
- perl -Mojo -E 'say r(g("mojolicio.us")->headers->to_hash)' |
|
161 |
+ perl -Mojo -E 'say r(g("example.com")->headers->to_hash)' |
|
180 | 162 |
|
181 | 163 |
=head2 t |
182 | 164 |
|
183 |
- my $res = t('mojolicio.us'); |
|
184 |
- my $res = t('http://mojolicio.us' => {DNT => 1} => 'Hi!'); |
|
165 |
+ my $res = t('example.com'); |
|
166 |
+ my $res = t('http://example.com' => {DNT => 1} => 'Hi!'); |
|
185 | 167 |
|
186 | 168 |
Perform C<PATCH> request with L<Mojo::UserAgent/"patch"> and return resulting |
187 | 169 |
L<Mojo::Message::Response> object. |
188 | 170 |
|
189 | 171 |
=head2 u |
190 | 172 |
|
191 |
- my $res = u('mojolicio.us'); |
|
192 |
- my $res = u('http://mojolicio.us' => {DNT => 1} => 'Hi!'); |
|
173 |
+ my $res = u('example.com'); |
|
174 |
+ my $res = u('http://example.com' => {DNT => 1} => 'Hi!'); |
|
193 | 175 |
|
194 | 176 |
Perform C<PUT> request with L<Mojo::UserAgent/"put"> and return resulting |
195 | 177 |
L<Mojo::Message::Response> object. |
... | ... |
@@ -4,7 +4,7 @@ use strict; |
4 | 4 |
use warnings; |
5 | 5 |
|
6 | 6 |
use FindBin; |
7 |
-use lib "$FindBin::Bin/../lib"; |
|
7 |
+BEGIN { unshift @INC, "$FindBin::Bin/../lib" } |
|
8 | 8 |
|
9 | 9 |
use Getopt::Long qw(GetOptions :config no_auto_abbrev no_ignore_case); |
10 | 10 |
|
... | ... |
@@ -4,7 +4,7 @@ use strict; |
4 | 4 |
use warnings; |
5 | 5 |
|
6 | 6 |
use FindBin; |
7 |
-use lib "$FindBin::Bin/../lib"; |
|
7 |
+BEGIN { unshift @INC, "$FindBin::Bin/../lib" } |
|
8 | 8 |
|
9 | 9 |
require Mojolicious::Commands; |
10 | 10 |
Mojolicious::Commands->start_app('Mojo::HelloWorld'); |
... | ... |
@@ -4,7 +4,7 @@ use strict; |
4 | 4 |
use warnings; |
5 | 5 |
|
6 | 6 |
use FindBin; |
7 |
-use lib "$FindBin::Bin/../lib"; |
|
7 |
+BEGIN { unshift @INC, "$FindBin::Bin/../lib" } |
|
8 | 8 |
|
9 | 9 |
use Getopt::Long qw(GetOptions :config no_auto_abbrev no_ignore_case); |
10 | 10 |
|