File Coverage

blib/lib/WWW/Scripter/Plugin/JavaScript/JE.pm
Criterion Covered Total %
statement 145 160 90.6
branch 52 58 89.6
condition 23 26 88.4
subroutine 29 34 85.2
pod 3 9 33.3
total 252 287 87.8


line stmt bran cond sub pod time code
1             package WWW::Scripter::Plugin::JavaScript::JE;
2              
3 3     3   18 use strict; # :-(
  3         7  
  3         139  
4 3     3   17 use warnings; # :-(
  3         5  
  3         196  
5              
6 3     3   17 use Carp 'croak';
  3         6  
  3         285  
7 3     3   17 use Hash::Util::FieldHash::Compat 'fieldhash';
  3         10  
  3         45  
8 3     3   245 use HTML::DOM::Interface 0.032 ':all'; # for the constants (0.032
  3         179  
  3         890  
9 3     3   2591 use JE'Destroyer; # for UTF16)
  3         2016  
  3         96  
10 3     3   3219 use JE 0.038; # call_with
  3         431521  
  3         138  
11 3     3   34 use Scalar::Util 1.09 qw'weaken refaddr';
  3         60  
  3         8823  
12              
13             our $VERSION = '0.009';
14             our @ISA = 'JE';
15              
16             fieldhash my %parathia;
17              
18             # No need to implement eval since JE's method
19             # is sufficient
20              
21             my @types;
22             $types[BOOL] = Boolean =>;
23             $types[STR ] = DOMString =>;
24             $types[NUM ] = Number =>;
25             $types[OBJ ] = null =>;
26              
27             sub new {
28 25     25 1 61 my $self = SUPER::new{shift} html_mode => 1;
  25         206  
29 25         1158 weaken( $parathia{$self} = my $parathi = shift );
30            
31 25         84 my $i = \%WWW'Scripter'WindowInterface;
32 25   100     4724 for(grep !/^_/ && $$i{$_} & METHOD, =>=> keys %$i) {
33 450         12721 my $method = $_;
34 450         923 my $type = $$i{$_};
35             $self->new_method($_ => sub {
36 6     6   267 my $parathi = $parathia{my $self = shift};
37             # undocumented JE methods:
38 6         52 $self->_cast(
39             scalar
40             $parathi->$method($self->_unwrap(@_)),
41             $types[$type&TYPE]
42             );
43 450         2762 });
44             }
45 25   100     5555 for(grep !/^_/ && !($$i{$_}&METHOD) =>=> keys %$i) {
46 1150         60651 my $name = $_;
47 1150 100       3814 next if $name =~ /^(?:window|self)\z/; # for efficiency
48 1100         1812 my $type = $$i{$_};
49 1100 100       2340 if ($type & READONLY) {
50             $self->prop({
51             name => $_,
52             readonly => 1,
53             fetch => sub {
54 34     34   903 my $self = shift;
55 34         281 $parathia{$self}->$name;
56 34         516 $self->_cast(
57             scalar
58             $parathia{$self}->$name,
59             $types[$type&TYPE]
60             );
61             },
62 200         3008 });
63             }
64             else {
65             $self->prop({
66             name => $_,
67             fetch => sub {
68 2     2   31 my $self = shift;
69 2         15 $self->_cast(
70             scalar
71             $parathia{$self}->$name,
72             $types[$type&TYPE]
73             );
74             },
75             store => sub {
76 0     0   0 my $self = shift;
77 0         0 $self->_cast(
78             scalar
79             $parathia{$self}
80             ->$name(shift),
81             $types[$type&TYPE]
82             );
83             },
84 900         8124 });
85             }
86             }
87              
88             # ~~~ This is *such* a hack! If anyone wants to help me fix JE’s
89             # screwed up type-conversion system (which makes my head hurt),
90             # please let me know.
91             $self->new_function("DOMString", sub {
92 10 100   10   594 if(ref($_[0]) =~ /^JE::(?:Null|Undefined)\z/) {
93 2         14 return $_[0]->global->null;
94             }
95 8         42 return $_[0]->to_string;
96 25         695 });
97              
98             $self->bind_class(
99             package => 'WWW::Scripter',
100             wrapper => sub {
101 9     9   4725 my ($self, $window) = @_;
102              
103             # ~~~ This needs to be modified to create a special
104             # restrictive wrapper if the $window has a
105             # different origin.
106             # Fetch the cached JavaScript environment corres-
107             # ponding to the WWW::Scripter object.
108             #warn $window, " ", $window->response, " ", $window->uri;
109 9 100       55 refaddr
110             $window->plugin("JavaScript")->back_end($window)
111             == refaddr $self
112             and return $self;
113 7         112 (__PACKAGE__."::Proxy")->new($window)
114             },
115 25         2038 );
116             $self->bind_class(
117             package => 'WWW::Scripter::Frames',
118             wrapper => sub {
119 1     1   53 my($self,$frames) = @_;
120 1         7 return $self->upgrade($frames->window);
121             },
122 25         13598 );
123             # ~~~ We also need a 'JE' wrapper, that will create a special
124             # objcet that delegates to the JS environment currently
125             # belonging to the window.
126              
127 25         499 for my $p($self->{String}{prototype}) {
128 25         745 for my $v(
129             ['anchor','a','name'],
130             ['fontcolor','font','color'],
131             ['fontsize','font','size'],
132             ['link','a','href'],
133             ) {
134             $p->new_method(
135             $$v[0],
136             sub{
137 8 100   8   8928 qq'<$$v[1] $$v[2]="'
138             . (defined $_[1] ? $_[1] : 'undefined')
139             . qq'">$_[0]'
140             }
141             )
142 100         26441 }
143 25         8206 for my $v('big', 'blink', 'small','strike','sub','sup') {
144 6     6   4610 $p->new_method( $v, sub{"<$v>$_[0]"} )
145 150         41976 }
146 25         7799 for my $v(['bold','b'],['fixed','tt'],['italics','i'],) {
147 3     3   2562 $p->new_method(
148             $$v[0],
149             sub{"<$$v[1]>$_[0]"}
150 75         16342 );
151             }
152             }
153              
154             # for speed:
155 25         8900 $self->prop('frames' => $self);
156 25         79 $self->prop('window' => $self);
157 25         127 $self->prop('self' => $self);
158             }
159              
160             sub prop {
161 10955     10955 1 1739180 my $self = shift;
162 10955 100       49892 return $self->SUPER::prop(@_) if ref $_[0] eq 'HASH';
163            
164 6306         26337 my $val = $self->SUPER::prop(@_);
165 6306 100       466404 return $val if defined $val;
166              
167 2342         4790 my $name = shift;
168 2342 50       6554 return $_[0] if @_;
169              
170 2342         5977 my $window = $parathia{$self};
171              
172 2342 100 66     19072 my $ret =
173             $name =~ /^(?:0|[1-9]\d*)\z/ && $name < 4294967295
174             ? $window->frames->[$name]
175             : $window->frames->{$name};
176 2342 100       153569 defined $ret ? $self->upgrade($ret) : $ret;
177             }
178              
179             sub set {
180 3     3 0 34 my $obj = shift;
181 3         6 my $val = pop;
182 3 50       14 croak "Not enough arguments for W:M:P:JS:JE->set" unless @_;
183 3         6 my $prop = pop;
184 3         11 for (@_) {
185 1         6 my $next_obj = $obj->{$_};
186 1 50       7 defined $next_obj or
187             $obj->{$_} = {},
188             $obj = $obj->{$_}, next;
189 0         0 $obj = $next_obj;
190             }
191 3         13 $obj->{$prop} = $val;
192 3         103 return;
193             }
194              
195             sub bind_classes {
196 119     119 0 437 my($self, $classes) = @_;
197 119         245 my @defer;
198 119         4341 for (grep /::/, keys %$classes) {
199 2419         35007 my $i = $$classes{$$classes{$_}}; # interface info
200 3118         4941 my @args = (
201             unwrap => 1,
202             package => $_,
203             name => $$classes{$_},
204             methods => { map {
205 14818         20503 my $perlname = $_;
206 3118 100       8205 $perlname .= 16 if $$i{$_} & UTF16;
207 3118 100       66922 $_ => $$i{$_} & VOID
208             ? $perlname
209             : "$perlname:$types[$$i{$_} & TYPE]"
210             } grep !/^_/ && $$i{$_} & METHOD, keys %$i },
211             props => { map {
212 2419 100 100     99336 my $perlname = $_;
    100 100        
213 14818 100       36036 $perlname .= 16 if $$i{$_} & UTF16;
214 14818 100       96234 $$i{$_} & READONLY
215             ? ($_ =>{
216             fetch=>
217             "$perlname:$types[$$i{$_} & TYPE]"
218             })
219             : ($_ => "$perlname:$types[$$i{$_} & TYPE]")
220             } grep !/^_/ && !($$i{$_} & METHOD), keys %$i },
221             hash => $$i{_hash},
222             array => $$i{_array},
223             exists $$i{_isa} ? (isa => $$i{_isa}) : (),
224             exists $$i{_constructor}
225             ? (constructor => $$i{_constructor})
226             : (),
227             );
228 2419         9591 my $make_constants;
229 2419 100       6714 if(exists $$i{_constants}){
230 175         351 my $p = $_;
231 175     175   333 $make_constants = sub { for(@{$$i{_constants}}){
  175         732  
232 1775         188714 /([^:]+\z)/;
233 1775         139511 $self->{$$classes{$p}}{$1} =
234             # ~~~ to be replaced simply with 'eval' when JE's
235             # upgrading is improved:
236             $self->upgrade(eval)->to_number;
237 175         987 }}}
238 2419 100 100     35774 if (exists $$i{_isa} and !exists $self->{$$i{_isa}}) {
239 1585         91111 push @defer, [\@args, $$i{_isa}, $make_constants]
240             } else {
241             # use Data::Dumper; print Dumper \@args if $_ !~ /HTML|CSS/;
242 834         6162 $self->bind_class(@args);
243 834 100       919290 defined $make_constants and &$make_constants;
244             }
245             }
246 119         1345 while(@defer) {
247 78         676 my @copy = @defer;
248 78         259 @defer = ();
249 78         219 for (@copy) {
250 2117 100       22026 if(exists $self->{$$_[1]}) { # $$_[1] == superclass
251 1585         15900 $self->bind_class(@{$$_[0]});
  1585         8424  
252 1585 100       1511260 &{$$_[2] or next}
  1585         20217  
253             }
254             else {
255 532         24426 push @defer, $_;
256             }
257             }
258             }
259 119         1482 return;
260             }
261              
262             sub event2sub {
263 6     6 0 41 my ($w, $code, $elem, $url, $line) = @_;
264              
265             # ~~~ JE's interface needs to be improved. This is a mess:
266             # We need the line break after $code, because there may be a sin-
267             # gle-line comment at the end, and no line break. ("foo //bar"
268             # would fail without this, because the }) would be com-
269             # mented out too.)
270             # We have to check whether the $elem is a form before calling it’s
271             #‘form’ method, because forms *do* have such a method, but it
272             # returns a list of form element names and values, which is *not*
273             # what we want. (We want the element’s parent form where
274             # applicable.)
275 6 100 100     67 ($w->compile("(function(){ $code\n })",$url,$line)||return)
      100        
276             ->execute($w, bless [
277             $w,
278             $w->upgrade($elem->ownerDocument),
279             $elem->tag ne 'form' && $elem->can('form')
280             ? $w->upgrade($elem->form) : (),
281             my $wrapper=($w->upgrade($elem))
282             ], 'JE::Scope');
283             }
284              
285             sub define_setter {
286 0     0 0 0 my $obj = shift;
287 0         0 my $cref = pop;
288 0         0 my $prop = pop;
289 0         0 for (@_) {
290 0         0 my $next_obj = $obj->{$_};
291 0 0       0 defined $next_obj or
292             $obj->{$_} = {},
293             $obj = $obj->{$_}, next;
294 0         0 $obj = $next_obj;
295             }
296 0     0   0 $obj->prop({name=>$prop, store=>sub{$cref->($_[1])}});
  0         0  
297 0         0 return;
298             }
299              
300             sub new_function {
301 95     95 1 323 my($self, $name, $sub, $type) = @_;
302 95 100       259 if(defined $type) {
303             $self->new_function(
304 2     2   88 $name => sub { $self->{$type}->($sub->(@_)) }
305 18         134 );
306 18         507 weaken $self;
307             } else {
308 77         360 shift->SUPER::new_function(@_);
309             }
310             }
311              
312             sub exists {
313 3988     3988 0 384366 my $self = shift;
314             SUPER::exists $self @_
315 3988 100       21375 or do {
316 2130         15977 my($name) = @_;
317 2130         5203 my $window = $parathia{$self};
318 2130 50 33     14078 $name =~ /^(?:0|[1-9]\d*)\z/ && $name < 4294967295
319             ? defined $window->frames->[$name]
320             : defined $window->frames->{$name}
321             }
322             }
323              
324             sub destroy {
325 26 100   26 0 134280 return SUPER::destroy{@_} if caller eq 'JE::Destroyer';
  13         125  
326 13         217 JE'Destroyer'destroy $_[0]
327             }
328              
329              
330             package WWW::Scripter::Plugin::JavaScript::JE::Proxy;
331              
332             # ~~~ This package needs to override any method used internally by
333             # JavaScript that returns a JE datatype to return a wrapper instead.
334              
335             sub new {
336             # If a method is called on another window, as in frames[0].alert(), an
337             # lvalue is created with the proxy object returned by frames[0] as the
338             # the base and ‘alert’ as the property name. The lvalue then fetches the
339             # value of frames[0].alert and runs it with frames[0] as the invocant.
340             # This means that it is the proxy that gets passed to the closures
341             # in WSPJSJE:new above. Since those expect to find a window in
342             # $parathia{$self}, and $self might be a proxy, we have to add ourselves
343             # to the %parathi field hash and store the window there. For the sake of
344             # speed in autoload, we still keep a direct reference to the window.
345 7     7   27 my $proxy = bless \(my $w = pop);
346 7         103 Scalar'Util'weaken( $parathia{$proxy} = $w );
347 7         61 $proxy
348             }
349              
350             sub AUTOLOAD {
351 22     22   1128 my $window = ${;shift};
  22         58  
352             (
353             # We have to use this roundabout method
354             # rather than __PACKAGE__->new($window),
355             # because the JS plugin needs to do its
356             # stuff (binding classes, etc.).
357 22         321 $window->plugin("JavaScript")->back_end($window)
358 22         399 )->${\(our $AUTOLOAD =~ /.*::(.+)\z/)[0]}(@_)
359             }
360              
361 0     0     sub DESTROY{}
362             sub isa {
363 0     0     goto &UNIVERSAL'isa;
364             }
365              
366              
367             =cut
368              
369             # ------------------ DOCS --------------------#
370              
371             =head1 NAME
372              
373             WWW::Scripter::Plugin::JavaScript::JE - JE backend for WSPJS
374              
375             =head1 VERSION
376              
377             0.008 (alpha)
378              
379             =head1 DESCRIPTION
380              
381             This little module is a bit of duct tape to connect the JavaScript plugin
382             for L to the JE JavaScript engine. Don't use this module
383             directly. For usage, see
384             L.
385              
386             =head1 REQUIREMENTS
387              
388             Hash::Util::FieldHash::Compat
389              
390             HTML::DOM 0.032 or later
391              
392             JE 0.056 or higher
393              
394             WWW::Scripter 0.016 or higher
395              
396             =head1 SEE ALSO
397              
398             =over 4
399              
400             =item -
401              
402             L
403              
404             =item -
405              
406             L
407              
408             =item -
409              
410             L
411              
412             =item -
413              
414             L (the original version of this
415             module)
416              
417             =cut