Skip to content

Commit

Permalink
added val method to Mojo::DOM
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Jul 23, 2014
1 parent da5c7f7 commit 3564dd1
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 0 deletions.
1 change: 1 addition & 0 deletions Changes
@@ -1,6 +1,7 @@

5.17 2014-07-23
- Welcome to the Mojolicious core team Jan Henning Thorsen.
- Added val method to Mojo::DOM. (sri, batman)

5.16 2014-07-21
- Improved Mojo::Asset::File to allow appending data to existing files.
Expand Down
67 changes: 67 additions & 0 deletions lib/Mojo/DOM.pm
Expand Up @@ -176,6 +176,23 @@ sub type {
return $self;
}

sub val {
my $self = shift;

# Element
return $self->_val unless $self->type eq 'form';

# Form
my $form = {};
_pair($form, $_->{name}, $_->_val) for $self->find('select, textarea')->each;
_input($form, $_)
for $self->find('input:not([type=button], [type="submit"])')->each;
my $e = $self->at('button, input[type=button], input[type=submit]');
_input($form, $e) if $e;

return $form;
}

sub wrap { shift->_wrap(0, @_) }
sub wrap_content { shift->_wrap(1, @_) }

Expand Down Expand Up @@ -253,6 +270,13 @@ sub _delegate {
return $self;
}

sub _input {
my ($form, $e) = @_;
my $type = $e->{type} // '';
_pair($form, $e->{name}, $e->_val)
if ($type ne 'radio' && $type ne 'checkbox') || defined $e->{checked};
}

sub _link {
my ($children, $parent) = @_;

Expand Down Expand Up @@ -280,6 +304,8 @@ sub _offset {
return $i;
}

sub _pair { $_[0]->{$_[1]} = $_[2] if defined $_[1] && defined $_[2] }

sub _parent { $_[0]->tree->[$_[0]->node eq 'tag' ? 3 : 2] }

sub _parse { Mojo::DOM::HTML->new(xml => shift->xml)->parse(shift)->tree }
Expand Down Expand Up @@ -353,6 +379,26 @@ sub _text {
return $text;
}

sub _val {
my $self = shift;

# "option"
my $type = $self->type;
return $self->{value} // $self->text if $type eq 'option';

# "select"
if ($type eq 'select') {
my @values = $self->find('option[selected]')->val->each;
return @values ? @values > 1 ? \@values : $values[0] : undef;
}

# "textarea"
return $self->text if $type eq 'textarea';

# "input" or "button"
return $type eq 'input' || $type eq 'button' ? $self->{value} : undef;
}

sub _wrap {
my ($self, $content, $new) = @_;

Expand Down Expand Up @@ -824,6 +870,27 @@ This element's type.
# List types of child elements
say $dom->children->type;
=head2 val
my $value = $dom->val;
my $array = $dom->val;
my $hash = $dom->val;
Extract values from C<button>, C<form>, C<input>, C<option>, C<select> and
C<textarea> elements or return C<undef> if this element has no value. In the
case of C<select>, find an C<option> element with C<selected> attribute and
return its value or an array reference with all values if there are more than
one. In the case of C<form>, find all elements mentioned before that would be
submitted by pressing the first button and return a hash reference with their
names and values.
# "b"
$dom->parse('<form><input name="a" value="b"></form>')->at('input')->val;
# "b"
$dom->parse('<form action="/"><input name="a" value="b"></form>')
->at('form')->val->{a};
=head2 wrap
$dom = $dom->wrap('<div></div>');
Expand Down
46 changes: 46 additions & 0 deletions t/mojo/dom.t
Expand Up @@ -2303,6 +2303,52 @@ is $dom->find('div > ul li')->[2], undef, 'no result';
is $dom->find('div > ul ul')->[0]->text, 'C', 'right text';
is $dom->find('div > ul ul')->[1], undef, 'no result';

# Form values
$dom = Mojo::DOM->new(<<EOF);
<form action="/foo">
<p>Test</p>
<input type="text" name="a" value="A" />
<input type="checkbox" checked name="b" value="B">
<input type="checkbox" name="c" value="C">
<input type="radio" name="d" value="D">
<input type="radio" checked name="e" value="E">
<select name="f">
<option value="F">G</option>
<optgroup>
<option>H</option>
<option selected>I</option>
</optgroup>
<option value="J" selected>K</option>
</select>
<select name="n"><option>N</option></select>
<select name="l"><option selected>L</option></select>
<textarea name="m">M</textarea>
<button name="o" value="O">No!</button>
<input type="button" name="s" value="S" />
<input type="submit" name="p" value="P" />
</form>
<form><input type="submit" name="q" value="Q"></form>
<form><input type="button" name="r" value="R"></form>
<form></form>
EOF
is $dom->at('p')->val, undef, 'no value';
is_deeply $dom->at('form')->val,
{a => 'A', f => ['I', 'J'], l => 'L', m => 'M', o => 'O'}, 'right values';
is $dom->at('input')->val, 'A', 'right value';
is $dom->find('input')->[2]->val, 'C', 'right value';
is $dom->find('input')->[3]->val, 'D', 'right value';
is_deeply $dom->find('select')->first->val, ['I', 'J'], 'right values';
is $dom->at('select option')->val, 'F', 'right value';
is $dom->find('select')->[1]->val, undef, 'no value';
is $dom->find('select')->[1]->at('option')->val, 'N', 'right value';
is $dom->find('select')->last->val, 'L', 'right value';
is $dom->at('textarea')->val, 'M', 'right value';
is $dom->at('form')->find('input')->[-2]->val, 'S', 'right value';
is $dom->at('form')->find('input')->last->val, 'P', 'right value';
is_deeply $dom->find('form')->[1]->val, {q => 'Q'}, 'right values';
is_deeply $dom->find('form')->[2]->val, {r => 'R'}, 'right values';
is_deeply $dom->find('form')->last->val, {}, 'no values';

# Slash between attributes
$dom = Mojo::DOM->new('<input /type=checkbox / value="/a/" checked/><br/>');
is_deeply $dom->at('input')->attr,
Expand Down

0 comments on commit 3564dd1

Please sign in to comment.