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.056';
4              
5              
6 28     28   95 use strict;
  28         34  
  28         599  
7 28     28   263 use warnings;
  28         26  
  28         742  
8 28     28   100 no warnings qw ' utf8 parenthesis ';
  28         20  
  28         724  
9              
10 28     28   88 use Carp 'croak';
  28         253  
  28         1037  
11 28     28   94 use HTML::DOM::Event;
  28         30  
  28         777  
12 28     28   88 use HTML::DOM::Exception qw 'UNSPECIFIED_EVENT_TYPE_ERR';
  28         27  
  28         1148  
13 28     28   106 use Scalar::Util qw'refaddr blessed';
  28         296  
  28         1054  
14 28     28   10278 use HTML::DOM::_FieldHash;
  28         336  
  28         35332  
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.056
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 3478 my ($self,$name,$listener, $capture) = @_;
110             (\(%cevh, %evh))[!$capture]->{$self}
111 885         2916 {lc $name}{refaddr $listener} = $listener;
112 885         1023 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 200 my ($self,$name,$listener, $capture) = @_;
125 99         88 $name = lc $name;
126 99         101 my $h = (\(%cevh, %evh))[!$capture];
127             exists $h->{$self}
128             and exists $$h{$self}{$name}
129 99 100 33     464 and delete $$h{$self}{$name}{refaddr $listener};
130 99         131 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   13300 my($pack,$meth) = our $AUTOLOAD =~ /(.*)::(.*)/s;
147 3486 100       25034 $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         28 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 36 my ($self,$name) = (shift,shift);
172 25         26 $name = lc $name;
173             my $old = exists $aevh{$self} && exists $aevh{$self}{$name}
174 25   66     119 && $aevh{$self}{$name};
175 25 100       78 @_ and $aevh{$self}{$name} = shift;
176 25 100       92 $old ||();
177             }
178 2     2 0 5 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 8312 my($self,$name,$capture) = @_;
204 6502         5558 $name = lc $name;
205 6502         9221 my $h = (\(%cevh, %evh))[!$capture]->{$self};
206             my @ret = $h && exists $$h{$name}
207 6502 100 66     12495 ? values %{$$h{$name}}
  1087         2081  
208             : ();
209 6502 100 66     13925 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   52 my $ret =
213             defined blessed $aevh && $aevh->can('call_with')
214             ? call_with $aevh $_[0]->currentTarget, $_[0]
215             : &$aevh($_[0]);
216 10 100 66     58 defined $ret
    100          
217             && ($name eq 'mouseover' ? $ret : !$ret)
218             && $_[0]->preventDefault;
219             }
220 13         55 }
221 6489         7515 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 1931 _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   1748 my ($target, $event) = (shift,shift);
258 1921 100 66     6709 $event &&= shift or my ($cat, $args, %args) = @_;;
259 1921 100       3712 my $name = $event ? $event->type : $args{type};
260              
261 1921 100 100     6067 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     4058 $event->_set_target($target) if $event && !$event->target;
266              
267 1915         3098 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         1338 my $doc;
  1915         1265  
273             my $sub = $target->can('event_listeners_enabled')
274 1915   50     7061 || (eval{$doc = $target->ownerDocument}||next Foo)
275             ->can('event_listeners_enabled')
276             || last Foo;
277 1895 100 50     4853 &$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     1579 ||eval{$target->ownerDocument->error_handler};
290              
291 1911         3541 my @lineage = $target;
292             {
293 1911         1314 push @lineage, eval{$lineage[-1]->parentNode}
294 4877   100     3506 ||eval{$lineage[-1]->event_parent}
295             ||last;
296             redo
297 2966         2226 }
298 1911         2186 shift @lineage; # shouldn’t include the target
299             # $lineage[-1] is the root, by the way
300              
301 1911         1558 my $initted;
302              
303 1911         2663 for (reverse @lineage) { # root first
304 2966         3448 my @l = $_->get_event_listeners($name, 1);
305 2966 100 66     4329 if(@l and !$initted++) {
306             # ~~~ This occurs three times; it probably ought to
307             # go it its own sub
308 10   33     16 $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         27 $event->_set_eventPhase(
319             HTML::DOM::Event::CAPTURING_PHASE);
320             }
321 2966 100       3528 $event-> _set_currentTarget($_) if @l;
322 2966         2985 for(@l) {
323 18 50 0     15 eval {
324 18 50 33     56 defined blessed $_ && $_->can('handleEvent') ?
325             $_->handleEvent($event) : &$_($event);
326 18         541 1
327             } or $eh and &$eh();
328             }
329 2966 100 100     5926 return !cancelled $event if
330             ($event||next)->propagation_stopped;
331             }
332              
333 1909         2850 my @l = $target->get_event_listeners($name);
334 1909 100       2664 if(@l) {
335 768 100       1246 unless ($initted++) {
336 760   66     1070 $event ||= do {
337 229   33     397 (my $e =
338             ($target->ownerDocument||$target)
339             ->createEvent($cat)
340             )->init(
341             %args, &$args
342             );
343 229 50       465 $e->_set_target($target) unless $e->target;
344 229         453 $e;
345             };
346             };
347 768         1327 $event->_set_eventPhase(HTML::DOM::Event::AT_TARGET);
348 768         1187 $event->_set_currentTarget($target);
349             }
350             eval {
351 780 50 33     2837 defined blessed $_ && $_->can('handleEvent') ?
352             $_->handleEvent($event) : &$_($event);
353 775         2593 1
354 1909   66     2410 } 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     4097 : !$args{propagates_up};
    100          
359              
360 1509         1177 my $initted2;
361 1509         1571 for (@lineage) { # root last
362 1289         1407 my @l = $_->get_event_listeners($name);
363 1289 100       1566 if(@l){
364 217 100       269 unless($initted++) {
365 19   33     30 $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       282 unless ($initted2++) {
378 121         188 $event->_set_eventPhase(
379             HTML::DOM::Event::BUBBLING_PHASE);
380             }
381             }
382 1289 100       1746 $event-> _set_currentTarget($_) if @l;
383             eval {
384 223 50 33     601 defined blessed $_ && $_->can('handleEvent') ?
385             $_->handleEvent($event) : &$_($event);
386 223         1084 1
387 1289   0     1330 } or $eh and &$eh() for(@l);
      33        
388 1289 100 100     2437 return +($event) x !cancelled $event
389             if ($event||next)->propagation_stopped;
390             }
391 1507   100     3928 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 7384 my ($target, $event, %args) = @_;
444 1894 100       2959 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         999 my ($cat, @init_args) = HTML'DOM'Event'defaults($event);
449 585         1131 unshift @init_args, type => $event;
450 585 50       934 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     3630 $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         36  
463             );
464 3         11 $rv->_set_target($target);
465             }
466 3         8 &$def($rv);
467             }
468 3         80 return;
469             }
470 1309         930 my $type;
471             defined blessed $event && $event->isa('HTML::DOM::Event')
472             ? $type = $event->type
473 1309 100 66     3073 : do {
474 1305         1082 $type = $event;
475 1305         2251 $event = HTML'DOM'Event'create_event((
476             my (undef, @init_args) =
477             HTML'DOM'Event'defaults($type)
478             )[0]);
479 1305         4453 $event->init(
480             type=>$type,
481             @init_args,
482             %args
483             );
484             };
485              
486             $target->dispatchEvent($event) and &{
487 1309 100       2798 $args{"$type\_default"} ||
488             $args{default}
489             || return
490 1305 100 66     9875 }($event);
491 125         14767 return;
492             }
493              
494              
495             =back
496              
497             =cut
498              
499             1;
500             __END__