The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Serengeti::Backend::Native::Elements::Forms;

use strict;
use warnings;

use Serengeti::Backend::Native::HTMLElementProperties;

my %VALID_PROPERTY = make_property_map(
    qw(name acceptCharset action method target)
);

sub register {
    return (
        form    => \&get_formelement_property,
        option  => \&get_optionelement_property,
        input   => \&get_inputelement_property,
    );
}

sub get_formelement_property {
    my ($element, $property) = @_;

    return to_DOMString($element->attr($property)) if exists $VALID_PROPERTY{$property};
    
    if ($property eq "elements" || $property eq "length") {
        # Now, find all input, select and textarea inputs
        my @inputs = $element->findnodes(".//input | .//textarea | .//select");
        return scalar @inputs if $property eq "length";
        return Serengeti::Backend::Native::HTMLCollection->new(@inputs);
    }
    
    if ($property eq "enctype") {
        my $enctype = $element->attr("enctype");
        return to_DOMString($enctype) if defined $enctype;
        
        # TODO: Check if we have a file input and 
        # set it to mutlipart/form-data instead.
        
        return "application/x-www-form-urlencoded";
    }
    
    return sub { submit($element, @_) } if $property eq "submit";
    
    if ($property eq "reset") {
        return sub {
            
        };
    }
    
    return;
}

sub submit {
    my ($element, $override) = @_;
    
    my $action = $element->attr("action");
    my $method = uc $element->attr("method");
    
    my $browser = $element->root->owner_document->browser;
    
    # Gather input, textarea and hm.. other form elements
    my %form_data = _gather_form_data($element);
    
    $override = {} unless ref $override eq "HASH";
    for (keys %form_data) {
        $form_data{$_} = $override->{$_} if exists $override->{$_};
    }
    
    my $url = $element->root->owner_document->make_url($action);
    
    if ($method eq "POST") {
        $browser->post($url, \%form_data, {});        
    }
    else {
        # Assume GET
        $browser->get($url, \%form_data, {});        
    }
}

sub _gather_form_data {
    my $element = shift;
    
    my %form_data;
    my @inputs = $element->findnodes(".//input | .//textarea | .//select");
    for my $input (@inputs) {
        my $name = $input->attr("name");
        next unless defined $name;
        
        my $tag = $input->tag;
        if ($tag eq "textarea") {
            $form_data{$name} = $element->as_trimmed_text();
        }
        elsif ($tag eq "select") {
            my @selected = $element->look_down("_tag", "option", "selected", undef);
            if (@selected) {
                $form_data{$name} = $selected[0]->attr("value");
            }
            elsif (my $first = $element->find("option")) {
                $form_data{$name} = $first->attr("value");
            }
        }
        else {
            $form_data{$name} = $input->attr("value");
        }
    }
    
    return %form_data;
}

{
    my %VALID_PROPERTY = make_property_map(qw(value));

    sub get_optionelement_property {
        my ($element, $property) = @_;
        
        return to_DOMString($element->attr($property)) if exists $VALID_PROPERTY{$property};
        
        return;
    }
}

{
    sub get_inputelement_property {
        my ($element, $property) = @_;
        
        return to_DOMString($element->attr("value")) if $property eq "value";

        return $element->look_up("_tag", "form") if $property eq "form";
        
        return;
    }
}

1;
__END__

=head1 NAME

Serengeti::Backend::Native::HTMLFormElement - Deals with properties and methods 
of forms

=head1 INTERFACE

=head2 METHODS

=over 4

=item get_property ($element : HTML::Element, $property : scalar) : scalar

Returns the property value from the element when requested from JavaScript.

B<Object properties:>

=over 4

=item elements

Scans the element for any descendants which are input-, textarea- or select-
elements and wraps these in a HTMLCollection element.

=item length

Returns the number of inputs that are descendants of the form.

=item name

Returns the name of the form.

=item enctype

Returns the encodning type of the form when sent - normally this is 
C<application/x-www-form-urlencoded> but if the form contains a file-input 
the type will be C<mulitpart/form-data>.

=item action

Returns the URL which the request will be made to when submitting.

=item target

Returns the name of the frame which will be used to do the submit in.

=item method

Returns the method the submit will be done in. When actually submitting the 
only values permitted are POST and GET. If it's neither a GET request will be 
assumed.

=item submit

Returns a function that can be called to perform the actual submit. The 
function optionally takes a C<object>-instance whose key/value paris can 
override values set on the forms inputs.

=item reset

Returns a function which resets the form to its initial state.

=back

=item submit ( $element : HTML::Element : $override : hash ) : Serengeti::Backend::Native::Document

Gathers the values of the inputs in the form and performs the request. 
Returns a new document instance corresponding to the newly requested page.

=back

=cut