/usr/local/CPAN/Mozilla-DOM/EventListeners.pm


# NB: to use EventListeners, you have to specifically enable them
# when building Mozilla::DOM (this example won't work otherwise).
# See README for how to enable experimental features.
#
# This example shows how to create event listeners.
# (Click on the button.)
# Refer also to the Minilla, Signals, Elements, and Events examples.
#
# $CVSHeader: Mozilla-DOM/examples/EventListeners.pm,v 1.4 2007-06-06 21:46:56 slanning Exp $


package EventListeners;

use strict;
use warnings;

use Cwd 'getcwd';
use Glib qw(TRUE FALSE);
use Gtk2;
use Gtk2::MozEmbed '0.04';
use Mozilla::DOM '0.12';   # for EventListener

use Glib::Object::Subclass Gtk2::Window::;

sub INIT_INSTANCE {
    my $self = shift;

    my $embed = Gtk2::MozEmbed->new();

    $embed->signal_connect(net_stop => \&net_stop_cb);

    $self->add($embed);

    my $cwd = getcwd();
    $embed->load_url("file://$cwd/index.html");

    $self->{_embed} = $embed;

    # See the perldoc for Mozilla::DOM::EventListener for why
    # this is considered experimental. Briefly, you must make
    # sure that your EventListener doesn't go out of scope
    # while HandleEvent can be called on it; otherwise, a segfault
    # will occur. (You could also put the listener in a global variable.)
    $self->{_embed}{listener} = Mozilla::DOM::EventListener->new(\&click_listener);
}


sub net_stop_cb {
    my $embed = shift;

    # Get <input type="button">
    my $button = _get_input($embed, 'button');

    # Usual interface switching, from Node to EventTarget
    my $iid = Mozilla::DOM::EventTarget->GetIID;
    my $target = $button->QueryInterface($iid);

    # Add the event listener created in INIT_INSTANCE above
    $target->AddEventListener('click', $embed->{listener}, 0);

    return TRUE;
}

# This is invoked when you click on the button.
# It only fires after you close the popup alert box, however.
# I think the reason is that the popup window is also
# called as a result of a click-event handler, and that
# handler only returns after the alert box is gone
# (i.e., the `alert' function has returned). But I thought
# only one handler could be added per event type. Maybe this
# means one handler in addition to any handler added by JavaScript
# as an onClick handler, but I'm not sure.
sub click_listener {
    my $event = shift;

    my $type = $event->GetType;
    print "handling event type=$type\n";

    # You could call StopPropagation or PreventDefault here,
    # or maybe QueryInterface to MouseEvent call one of those
    # methods. You could also call RemoveEventListener if you
    # had the EventTarget.

    return TRUE;
}

## Helper functions

# (Same method as in Events.pm, copied out of laziness.)
# If we wanted the 3rd button (<input type="button">),
# $type eq 'button' and $num == 2.
# Returns the input element node.
sub _get_input {
    my ($embed, $type, $num) = @_;
    $num = 0 unless defined $num;

    my @nodes = ();

    # Get all <input> elements
    my $browser = $embed->get_nsIWebBrowser;
    my $window = $browser->GetContentDOMWindow;
    my $doc = $window->GetDocument;
    my $docelem = $doc->GetDocumentElement;
    my $inputs = $docelem->GetElementsByTagName('input');

    # Find inputs whose 'type' attribute eq $type
    # and put them in @nodes
    INPUT: foreach my $i (0 .. $inputs->GetLength - 1) {
        my $input = $inputs->Item($i);
        my $attrs = $input->GetAttributes;

        ATTR: foreach my $a (0 .. $attrs->GetLength - 1) {
            my $attr = $attrs->Item($a);
            next ATTR unless $attr->GetNodeName =~ /^type$/i;

            if ($attr->GetNodeValue =~ /^$type$/i) {
                push @nodes, $input;
                next INPUT;
            }
        }
    }

    # This could easily be made to return multiple inputs, instead
    if (@nodes) {
        if (exists $nodes[$num]) {
            return $nodes[$num];
        } else {
            die "not enough $type inputs!\n";
        }
    } else {
        die "no inputs of type=$type\n";
    }
}


1;