File Coverage

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


line stmt bran cond sub pod time code
1             package HTML::DOM::EventTarget;
2              
3             our $VERSION = '0.057';
4              
5              
6 28     28   91 use strict;
  28         37  
  28         905  
7 28     28   86 use warnings;
  28         29  
  28         864  
8 28     28   122 no warnings qw ' utf8 parenthesis ';
  28         25  
  28         1379  
9              
10 28     28   86 use Carp 'croak';
  28         32  
  28         1091  
11 28     28   105 use HTML::DOM::Event;
  28         295  
  28         821  
12 28     28   94 use HTML::DOM::Exception qw 'UNSPECIFIED_EVENT_TYPE_ERR';
  28         24  
  28         984  
13 28     28   120 use Scalar::Util qw'refaddr blessed';
  28         30  
  28         1601  
14 28     28   10557 use HTML::DOM::_FieldHash;
  28         80  
  28         35570  
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.057
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 4047 my ($self,$name,$listener, $capture) = @_;
110             (\(%cevh, %evh))[!$capture]->{$self}
111 885         3315 {lc $name}{refaddr $listener} = $listener;
112 885         1244 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 217 my ($self,$name,$listener, $capture) = @_;
125 99         95 $name = lc $name;
126 99         107 my $h = (\(%cevh, %evh))[!$capture];
127             exists $h->{$self}
128             and exists $$h{$self}{$name}
129 99 100 33     469 and delete $$h{$self}{$name}{refaddr $listener};
130 99         192 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 3486     3486   14663 my($pack,$meth) = our $AUTOLOAD =~ /(.*)::(.*)/s;
147 3486 100       26933 $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         30 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 50 my ($self,$name) = (shift,shift);
172 25         30 $name = lc $name;
173             my $old = exists $aevh{$self} && exists $aevh{$self}{$name}
174 25   66     146 && $aevh{$self}{$name};
175 25 100       86 @_ and $aevh{$self}{$name} = shift;
176 25 100       108 $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 6502     6502 1 9499 my($self,$name,$capture) = @_;
204 6502         6064 $name = lc $name;
205 6502         9771 my $h = (\(%cevh, %evh))[!$capture]->{$self};
206             my @ret = $h && exists $$h{$name}
207 6502 100 66     13719 ? values %{$$h{$name}}
  1087         2250  
208             : ();
209 6502 100 66     16045 if(!$capture && exists $aevh{$self} && exists $aevh{$self}{$name}
      66        
      66        
210             and defined (my $aevh = $aevh{$self}{$name})) {
211             @ret, sub {
212 10 100 66 10   68 my $ret =
213             defined blessed $aevh && $aevh->can('call_with')
214             ? call_with $aevh $_[0]->currentTarget, $_[0]
215             : &$aevh($_[0]);
216 10 100 66     65 defined $ret
    100          
217             && ($name eq 'mouseover' ? $ret : !$ret)
218             && $_[0]->preventDefault;
219             }
220 13         58 }
221 6489         8970 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 1336     1336 1 2022 _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 1921     1921   1889 my ($target, $event) = (shift,shift);
258 1921 100 66     7325 $event &&= shift or my ($cat, $args, %args) = @_;;
259 1921 100       4169 my $name = $event ? $event->type : $args{type};
260              
261 1921 100 100     6445 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 1915 100 100     4401 $event->_set_target($target) if $event && !$event->target;
266              
267 1915         3312 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 1915         1487 my $doc;
  1915         1410  
273             my $sub = $target->can('event_listeners_enabled')
274 1915   50     7879 || (eval{$doc = $target->ownerDocument}||next Foo)
275             ->can('event_listeners_enabled')
276             || last Foo;
277 1895 100 50     5418 &$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 1911   100     1844 ||eval{$target->ownerDocument->error_handler};
290              
291 1911         3846 my @lineage = $target;
292             {
293 1911         1508 push @lineage, eval{$lineage[-1]->parentNode}
294 4877   100     3856 ||eval{$lineage[-1]->event_parent}
295             ||last;
296             redo
297 2966         2577 }
298 1911         2228 shift @lineage; # shouldn’t include the target
299             # $lineage[-1] is the root, by the way
300              
301 1911         1597 my $initted;
302              
303 1911         3198 for (reverse @lineage) { # root first
304 2966         3790 my @l = $_->get_event_listeners($name, 1);
305 2966 100 66     4752 if(@l and !$initted++) {
306             # ~~~ This occurs three times; it probably ought to
307             # go it its own sub
308 10   33     20 $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         23 $event->_set_eventPhase(
319             HTML::DOM::Event::CAPTURING_PHASE);
320             }
321 2966 100       3889 $event-> _set_currentTarget($_) if @l;
322 2966         3445 for(@l) {
323 18 50 0     16 eval {
324 18 50 33     92 defined blessed $_ && $_->can('handleEvent') ?
325             $_->handleEvent($event) : &$_($event);
326 18         685 1
327             } or $eh and &$eh();
328             }
329 2966 100 100     6847 return !cancelled $event if
330             ($event||next)->propagation_stopped;
331             }
332              
333 1909         3193 my @l = $target->get_event_listeners($name);
334 1909 100       2990 if(@l) {
335 768 100       1324 unless ($initted++) {
336 760   66     1220 $event ||= do {
337 229   33     456 (my $e =
338             ($target->ownerDocument||$target)
339             ->createEvent($cat)
340             )->init(
341             %args, &$args
342             );
343 229 50       534 $e->_set_target($target) unless $e->target;
344 229         562 $e;
345             };
346             };
347 768         1761 $event->_set_eventPhase(HTML::DOM::Event::AT_TARGET);
348 768         1313 $event->_set_currentTarget($target);
349             }
350             eval {
351 780 50 33     3096 defined blessed $_ && $_->can('handleEvent') ?
352             $_->handleEvent($event) : &$_($event);
353 775         3067 1
354 1909   66     2853 } or $eh and &$eh() for @l;
      66        
355             return +($event) x !cancelled $event if
356             $event
357             ? $event->propagation_stopped || !$event->bubbles
358 1909 100 100     4374 : !$args{propagates_up};
    100          
359              
360 1509         1394 my $initted2;
361 1509         1895 for (@lineage) { # root last
362 1289         1549 my @l = $_->get_event_listeners($name);
363 1289 100       1820 if(@l){
364 217 100       323 unless($initted++) {
365 19   33     38 $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       314 unless ($initted2++) {
378 121         226 $event->_set_eventPhase(
379             HTML::DOM::Event::BUBBLING_PHASE);
380             }
381             }
382 1289 100       1930 $event-> _set_currentTarget($_) if @l;
383             eval {
384 223 50 33     763 defined blessed $_ && $_->can('handleEvent') ?
385             $_->handleEvent($event) : &$_($event);
386 223         1286 1
387 1289   0     1565 } or $eh and &$eh() for(@l);
      33        
388 1289 100 100     2758 return +($event) x !cancelled $event
389             if ($event||next)->propagation_stopped;
390             }
391 1507   100     4575 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 1894     1894 1 7913 my ($target, $event, %args) = @_;
444 1894 100       3475 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         1146 my ($cat, @init_args) = HTML'DOM'Event'defaults($event);
449 585         1170 unshift @init_args, type => $event;
450 585 50       1010 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     4114 $args{"default"}
457             || return;
458 3 50       8 unless (ref $rv) {
459             ($rv =
460             HTML'DOM'Event'create_event($cat)
461             )->init(my @args =
462 3         10 @init_args, &{$args{auto_viv}}
  3         36  
463             );
464 3         10 $rv->_set_target($target);
465             }
466 3         8 &$def($rv);
467             }
468 3         74 return;
469             }
470 1309         1031 my $type;
471             defined blessed $event && $event->isa('HTML::DOM::Event')
472             ? $type = $event->type
473 1309 100 66     3469 : do {
474 1305         1110 $type = $event;
475 1305         2490 $event = HTML'DOM'Event'create_event((
476             my (undef, @init_args) =
477             HTML'DOM'Event'defaults($type)
478             )[0]);
479 1305         4556 $event->init(
480             type=>$type,
481             @init_args,
482             %args
483             );
484             };
485              
486             $target->dispatchEvent($event) and &{
487 1309 100       2974 $args{"$type\_default"} ||
488             $args{default}
489             || return
490 1305 100 66     11031 }($event);
491 125         16760 return;
492             }
493              
494              
495             =back
496              
497             =cut
498              
499             1;
500             __END__