File Coverage

blib/lib/HTML/DOM/EventTarget.pm
Criterion Covered Total %
statement 127 133 95.4
branch 67 78 85.9
condition 68 107 63.5
subroutine 18 19 94.7
pod 6 7 85.7
total 286 344 83.1


line stmt bran cond sub pod time code
1             package HTML::DOM::EventTarget;
2              
3             our $VERSION = '0.058';
4              
5              
6 28     28   152 use strict;
  28         40  
  28         642  
7 28     28   115 use warnings;
  28         55  
  28         647  
8 28     28   115 no warnings qw ' utf8 parenthesis ';
  28         45  
  28         779  
9              
10 28     28   149 use Carp 'croak';
  28         56  
  28         1212  
11 28     28   141 use HTML::DOM::Event;
  28         41  
  28         929  
12 28     28   153 use HTML::DOM::Exception qw 'UNSPECIFIED_EVENT_TYPE_ERR';
  28         46  
  28         947  
13 28     28   145 use Scalar::Util qw'refaddr blessed';
  28         53  
  28         1183  
14 28     28   10806 use HTML::DOM::_FieldHash;
  28         60  
  28         38062  
15              
16             fieldhashes \my(
17             %evh, # event handlers
18             %cevh, # capturing event handlers
19             %aevh, # attribute event handlers
20             );
21              
22             =head1 NAME
23              
24             HTML::DOM::EventTarget - Perl implementation of the DOM EventTarget interface
25              
26             =head1 VERSION
27              
28             Version 0.058
29              
30             =head1 SYNOPSIS
31              
32             use HTML::DOM;
33             $doc = HTML::DOM->new;
34             $doc->isa('HTML::DOM::EventTarget'); # true
35              
36             $event = $doc->createEvent('MouseEvents');
37             $event->initEvent('click',1,1);
38              
39             $doc->trigger_event('click');
40             $doc->dispatchEvent($event);
41             # etc
42              
43             =head1 DESCRIPTION
44              
45             This class provides the W3C's EventTarget DOM interface. It serves as a
46             base class for L and L, but any class you
47             write can inherit from it.
48              
49             This class provides the methods listed under L, but will also use
50             a few
51             others
52             defined by subclasses, if they are present:
53              
54             =over
55              
56             =item parentNode
57              
58             =item event_parent
59              
60             These are used to determine the 'ancestry' of the event target, through
61             which the event will be dispatched. For each object, starting with the
62             target, the C method is called; if it doesn't exist or returns
63             false, the C method is tried. If that fails, then the object
64             is taken to be the topmost object.
65              
66             =item error_handler
67              
68             The return value of this method, if it exists and returns one, is presumed
69             to be a code ref, and is called whenever an event handler (listener) dies.
70             If there is no C method that returns true, then
71             C<< $target->ownerDocument->error_handler >> is used instead. If that
72             fails, then errors are ignored.
73              
74             =item event_listeners_enabled
75              
76             If this method exists and returns false, then event handlers are not
77             called.
78             If there is no C method,
79             then
80             C<< $target->ownerDocument->event_listeners_enabled >> is used instead.
81              
82             =item ownerDocument
83              
84             See C and C.
85              
86             =back
87              
88             =head1 METHODS
89              
90             If a subclass needs to store event handlers and listeners elsewhere (e.g.,
91             associating them with another object), it can override C,
92             C, C and C.
93              
94             =over
95              
96             =item addEventListener($event_name, $listener, $capture)
97              
98             The C<$listener> should be either a coderef or an object with a
99             C method. (HTML::DOM does not implement any such object since
100             it would just be a wrapper around a coderef anyway, but has support for
101             them.) An object with C<&{}> overloading will also do.
102              
103             C<$capture> is a boolean indicating whether this is to be triggered during
104             the 'capture' phase.
105              
106             =cut
107              
108             sub addEventListener {
109 885     885 1 4735 my ($self,$name,$listener, $capture) = @_;
110             (\(%cevh, %evh))[!$capture]->{$self}
111 885         3795 {lc $name}{refaddr $listener} = $listener;
112 885         1497 return;
113             }
114              
115              
116             =item removeEventListener($event_name, $listener, $capture)
117              
118             The C<$listener> should be the same reference passed to
119             C.
120              
121             =cut
122              
123             sub removeEventListener {
124 99     99 1 232 my ($self,$name,$listener, $capture) = @_;
125 99         132 $name = lc $name;
126 99         153 my $h = (\(%cevh, %evh))[!$capture];
127             exists $h->{$self}
128             and exists $$h{$self}{$name}
129 99 100 66     518 and delete $$h{$self}{$name}{refaddr $listener};
130 99         169 return;
131             }
132              
133              
134             =item on* (onthis, onthat, onclick, onfoo, etc.)
135              
136             This applies to any all-lowercase method beginning with C. Basically,
137             C<< $target->onclick(\&sub) >> is equivalent to
138             C<< $target->addEventListener('click', \&sub, 0) >>, except that it
139             replaces any event handler already assigned via C, returning it.
140             C<< $target->onclick >> (without arguments) returns the event handler
141             previously assigned to C if there is one.
142              
143             =cut
144              
145             sub AUTOLOAD {
146 3489     3489   16403 my($pack,$meth) = our $AUTOLOAD =~ /(.*)::(.*)/s;
147 3489 100       24727 $meth =~ /^on([a-z]+)\z/
148             or die "Can't locate object method \"$meth\" via package "
149             . qq'"$pack" at '.join' line ',(caller)[1,2]
150             ,. "\n";
151 13         41 shift->event_handler($1, @_);
152             }
153       0     sub DESTROY{}
154              
155             =item event_handler ( $name )
156              
157             =item event_handler ( $name, $new_value )
158              
159             This is an accessor method for event listeners created by HTML or DOM
160             attributes beginning with 'on'. This is used internally by the C
161             methods. You can use it directly for efficiency's sake.
162              
163             This method used to be called C, but that was a
164             mistake, as there is a distinction between handlers and listeners. The old
165             name is still available but will be removed in a future release. It simply
166             calls C.
167              
168             =cut
169              
170             sub event_handler {
171 25     25 1 64 my ($self,$name) = (shift,shift);
172 25         41 $name = lc $name;
173             my $old = exists $aevh{$self} && exists $aevh{$self}{$name}
174 25   66     137 && $aevh{$self}{$name};
175 25 100       90 @_ and $aevh{$self}{$name} = shift;
176 25 100       102 $old ||();
177             }
178 2     2 0 6 sub attr_event_listener { shift->event_handler(@_) }
179              
180              
181             =item get_event_listeners($event_name, $capture)
182              
183             This is not a DOM method (hence the underscores in the name). It returns a
184             list of all event listeners for the given event name. C<$capture> is a
185             boolean that indicates which list to return, either 'capture' listeners or
186             normal ones.
187              
188             If there is an event handler for this event (and C<$capture> is false),
189             then C tacks a wrapper for the event handler on to the
190             end of the list it returns.
191              
192             =for comment
193             This is no longer true. But we may need a similar warning in case other packages install listeners that must not be removed.
194             B This method is intended mostly for internal use, but you can
195             go ahead and use it if you like. Just beware that some of the event
196             handlers returned may have been installed automatically by HTML::DOM, and
197             are necessary for its internal workings, so don't go passing those to
198             C and expect all to go well.
199              
200             =cut
201              
202             sub get_event_listeners { # uses underscores because it is not a DOM method
203 6535     6535 1 14010 my($self,$name,$capture) = @_;
204 6535         8271 $name = lc $name;
205 6535         12981 my $h = (\(%cevh, %evh))[!$capture]->{$self};
206             my @ret = $h && exists $$h{$name}
207 6535 100 100     13953 ? values %{$$h{$name}}
  1087         2651  
208             : ();
209 6535 100 100     16442 if(!$capture && exists $aevh{$self} && exists $aevh{$self}{$name}
      100        
      66        
210             and defined (my $aevh = $aevh{$self}{$name})) {
211             @ret, sub {
212 10 100 66 10   67 my $ret =
213             defined blessed $aevh && $aevh->can('call_with')
214             ? call_with $aevh $_[0]->currentTarget, $_[0]
215             : &$aevh($_[0]);
216 10 100 100     96 defined $ret
    100          
217             && ($name eq 'mouseover' ? $ret : !$ret)
218             && $_[0]->preventDefault;
219             }
220 13         62 }
221 6522         9949 else { @ret }
222             }
223              
224             =item dispatchEvent($event_object)
225              
226             $event_object is an object returned by HTML::DOM's C method,
227             or any object that implements the interface documented in
228             L.
229              
230             C does not automatically call the handler passed to the
231             document's C. It is expected that the code that
232             calls this method will do that (see also L).
233              
234             The return value is a boolean indicating whether the default action
235             should be taken (i.e., whether preventDefault was I called).
236              
237             =for comment
238             Actually, it's the event object itself (unless it was called in
239             auto-vivacious mode and the event was never auto-vivved); but that’s an
240             implementation detail that’s subject to change willy-nilly.
241              
242             =cut
243              
244             sub dispatchEvent {
245 1339     1339 1 2423 _dispatch_event(shift, 1, shift);
246             }
247              
248             sub _dispatch_event { # This is where all the work is.
249             # We accept two different types of arg lists:
250             # 1) $target->...($yes_it_is_an_event_object, $event_obj)
251             # 2) $target->...($no_it's_not_an_event_object,
252             # $event_category, \&arg_maker, %more_args)
253             # The second is for autovivving the event object, as we do with
254             # attr modifications, to avoid creating an attr node unnecessarily.
255             # We init an event with (%more_args, &arg_maker).
256              
257 1924     1924   2969 my ($target, $event) = (shift,shift);
258 1924 100 66     6605 $event &&= shift or my ($cat, $args, %args) = @_;;
259 1924 100       4151 my $name = $event ? $event->type : $args{type};
260              
261 1924 100 100     5788 die HTML::DOM::Exception->new(UNSPECIFIED_EVENT_TYPE_ERR,
262             'The type of event has not been specified')
263             unless defined $name and length $name;
264              
265 1918 100 100     6420 $event->_set_target($target) if $event && !$event->target;
266              
267 1918         3748 local *@;
268              
269             # Check to see whether we are supposed to skip event handlers, and
270             # short-circuit if that’s the case:
271             Foo: {
272 1918         2309 my $doc;
  1918         2031  
273             my $sub = $target->can('event_listeners_enabled')
274 1918   50     7647 || (eval{$doc = $target->ownerDocument}||next Foo)
275             ->can('event_listeners_enabled')
276             || last Foo;
277 1898 100 50     5045 &$sub($doc||$target) or return $event||1
      66        
278             }
279            
280             # Basic event flow is as follows:
281             # 1. The 'capturing' phase: Go through the node's ancestors,
282             # starting from the top of the tree. For each one, trigger any
283             # capture events it might have.
284             # 2. Trigger events on the $target.
285             # 3. 'Bubble-blowing' phase: Trigger events on the target's ances-
286             # tors in reverse order (top last).
287              
288             my $eh = eval{$target->error_handler}
289 1914   100     2443 ||eval{$target->ownerDocument->error_handler};
290              
291 1914         4515 my @lineage = $target;
292             {
293 1914         2249 push @lineage, eval{$lineage[-1]->parentNode}
294 4895   100     5359 ||eval{$lineage[-1]->event_parent}
295             ||last;
296             redo
297 2981         3728 }
298 1914         3146 shift @lineage; # shouldn’t include the target
299             # $lineage[-1] is the root, by the way
300              
301 1914         2255 my $initted;
302              
303 1914         3219 for (reverse @lineage) { # root first
304 2981         4573 my @l = $_->get_event_listeners($name, 1);
305 2981 100 66     4601 if(@l and !$initted++) {
306             # ~~~ This occurs three times; it probably ought to
307             # go it its own sub
308 10   33     18 $event ||= do {
309 0   0     0 (my $e =
310             ($target->ownerDocument||$target)
311             ->createEvent($cat)
312             )->init(
313             %args, &$args
314             );
315 0 0       0 $e->_set_target($target) unless $e->target;
316 0         0 $e;
317             };
318 10         41 $event->_set_eventPhase(
319             HTML::DOM::Event::CAPTURING_PHASE);
320             }
321 2981 100       4573 $event-> _set_currentTarget($_) if @l;
322 2981         4353 for(@l) {
323 18 50 0     22 eval {
324 18 50 33     73 defined blessed $_ && $_->can('handleEvent') ?
325             $_->handleEvent($event) : &$_($event);
326 18         1126 1
327             } or $eh and &$eh();
328             }
329 2981 100 100     6048 return !cancelled $event if
330             ($event||next)->propagation_stopped;
331             }
332              
333 1912         3619 my @l = $target->get_event_listeners($name);
334 1912 100       3134 if(@l) {
335 768 100       1674 unless ($initted++) {
336 760   66     1349 $event ||= do {
337 229   33     492 (my $e =
338             ($target->ownerDocument||$target)
339             ->createEvent($cat)
340             )->init(
341             %args, &$args
342             );
343 229 50       618 $e->_set_target($target) unless $e->target;
344 229         531 $e;
345             };
346             };
347 768         1977 $event->_set_eventPhase(HTML::DOM::Event::AT_TARGET);
348 768         1376 $event->_set_currentTarget($target);
349             }
350             eval {
351 780 50 33     2860 defined blessed $_ && $_->can('handleEvent') ?
352             $_->handleEvent($event) : &$_($event);
353 775         3285 1
354 1912   66     3012 } or $eh and &$eh() for @l;
      66        
355             return +($event) x !cancelled $event if
356             $event
357             ? $event->propagation_stopped || !$event->bubbles
358 1912 100 100     4310 : !$args{propagates_up};
    100          
359              
360 1512         1824 my $initted2;
361 1512         2105 for (@lineage) { # root last
362 1304         1881 my @l = $_->get_event_listeners($name);
363 1304 100       1851 if(@l){
364 217 100       333 unless($initted++) {
365 19   33     33 $event ||= do {
366 0   0     0 (my $e =
367             ($target->ownerDocument||$target)
368             ->createEvent($cat)
369             )->init(
370             %args, &$args
371             );
372 0 0       0 $e->_set_target($target)
373             unless $e->target;
374 0         0 $e;
375             };
376             }
377 217 100       356 unless ($initted2++) {
378 121         215 $event->_set_eventPhase(
379             HTML::DOM::Event::BUBBLING_PHASE);
380             }
381             }
382 1304 100       2159 $event-> _set_currentTarget($_) if @l;
383             eval {
384 223 50 33     750 defined blessed $_ && $_->can('handleEvent') ?
385             $_->handleEvent($event) : &$_($event);
386 223         1657 1
387 1304   0     1761 } or $eh and &$eh() for(@l);
      33        
388 1304 100 100     2535 return +($event) x !cancelled $event
389             if ($event||next)->propagation_stopped;
390             }
391 1510   100     4094 return +($event) x !($event||return 1)->cancelled ;
392             }
393              
394             =item trigger_event($event, ...)
395              
396             Here is another non-DOM method. C<$event> can be an event object or simply
397             an event name. This method triggers an
398             event for real, first calling C and then running the default
399             action for the event unless an event listener cancels it.
400              
401             It can take named args following the C<$event> arg. These are passed to the
402             event object's C method. Any
403             omitted args will be filled in with reasonable defaults. These are
404             completely ignored if C<$event> is an event object.
405              
406             Also, you can use the C arg to provide a coderef that will be
407             called as the default event handler. L overrides it to do
408             just that, so you shouldn't need to use this arg except on a custom
409             subclass of EventTarget.
410              
411             When C<$event> is an event name, C automatically chooses the
412             right event class and a set of default args for that event name, so you can
413             supply just a few. E.g.,
414              
415             $elem->trigger_event('click', shift => 1, button => 1);
416              
417             =begin comment
418              
419             Internal-only features:
420              
421             The interface for this is very clunky, so I’m keeping it private for now.
422             It only exists for the sake of the implementation, anyway.
423              
424             The named args can contain DOMActivate_default => \&sub to specify a
425             default handler for an event type. We don't use default =>
426             { DOMActivate => \&sub } as I originally intended, because that would make
427             it harder for multiple classes
428             to say SUPER::trigger_event($evnt, ..._default => ) without clobbering each
429             other.
430              
431             And there's the 'create event object on demand' interface, which is as
432             follows:
433              
434             $thing->trigger_event('DOMAttrModified', auto_viv => \&arg_maker);
435              
436             This does not automatically supply the view.
437              
438             =end comment
439              
440             =cut
441              
442             sub trigger_event { # non-DOM method
443 1897     1897 1 10263 my ($target, $event, %args) = @_;
444 1897 100       3680 if($args{auto_viv}) {
445             # For efficiency’s sake, we skip creating the event object
446             # here, and have _dispatch_event create the object on
447             # demand, using the code ref that we pass to it.
448 585         1212 my ($cat, @init_args) = HTML'DOM'Event'defaults($event);
449 585         1339 unshift @init_args, type => $event;
450 585 50       1258 if(my $rv = _dispatch_event(
451             $target, 0, $cat, $args{auto_viv},
452             @init_args
453             )) {
454             my $def =
455             $args{"$event\_default"} ||
456 585   100     4004 $args{"default"}
457             || return;
458 3 50       9 unless (ref $rv) {
459             ($rv =
460             HTML'DOM'Event'create_event($cat)
461             )->init(my @args =
462 3         9 @init_args, &{$args{auto_viv}}
  3         11  
463             );
464 3         16 $rv->_set_target($target);
465             }
466 3         8 &$def($rv);
467             }
468 3         84 return;
469             }
470 1312         1505 my $type;
471             defined blessed $event && $event->isa('HTML::DOM::Event')
472             ? $type = $event->type
473 1312 100 66     3491 : do {
474 1308         1630 $type = $event;
475 1308         2641 $event = HTML'DOM'Event'create_event((
476             my (undef, @init_args) =
477             HTML'DOM'Event'defaults($type)
478             )[0]);
479 1308         4566 $event->init(
480             type=>$type,
481             @init_args,
482             %args
483             );
484             };
485              
486             $target->dispatchEvent($event) and &{
487 1312 100       3561 $args{"$type\_default"} ||
488             $args{default}
489             || return
490 1308 100 100     10529 }($event);
491 128         22693 return;
492             }
493              
494              
495             =back
496              
497             =cut
498              
499             1;
500             __END__