File Coverage

blib/lib/JavaScript/Packer.pm
Criterion Covered Total %
statement 338 359 94.1
branch 104 130 80.0
condition 29 33 87.8
subroutine 25 25 100.0
pod 1 3 33.3
total 497 550 90.3


line stmt bran cond sub pod time code
1             package JavaScript::Packer;
2              
3 4     4   204246 use 5.008009;
  4         35  
4 4     4   20 use warnings;
  4         8  
  4         116  
5 4     4   23 use strict;
  4         6  
  4         95  
6 4     4   19 use Carp;
  4         7  
  4         249  
7 4     4   1944 use Regexp::RegGrp;
  4         16547  
  4         5616  
8              
9             # =========================================================================== #
10              
11             our $VERSION = "2.11";
12              
13             our @BOOLEAN_ACCESSORS = ( 'no_compress_comment', 'remove_copyright' );
14              
15             our @COPYRIGHT_ACCESSORS = ( 'copyright', 'copyright_comment' );
16              
17             our @COMPRESS_OPTS = ( 'clean', 'obfuscate', 'shrink', 'best' );
18             our $DEFAULT_COMPRESS = 'clean';
19              
20             our $PACKER_COMMENT = '\/\*\s*JavaScript::Packer\s*(\w+)\s*\*\/';
21             our $COPYRIGHT_COMMENT = '\/\*((?>[^\*]|\*[^\/])*copyright(?>[^\*]|\*[^\/])*)\*\/';
22              
23             our $RESTORE_PATTERN = qr~\x01(\d+)\x01~;
24             our $RESTORE_REPLACEMENT = "\x01%d\x01";
25              
26             our $SHRINK_VARS = {
27             BLOCK => qr/(((catch|do|if|while|with|function)\b[^~{};]*(\(\s*[^{};]*\s*\))\s*)?(\{[^{}]*\}))/, # function ( arg ) { ... }
28             ENCODED_BLOCK => qr/~#?(\d+)~/,
29             CALLER => qr/((?>[a-zA-Z0-9_\x24\.]+)\s*\([^\(\)]*\))(?=[,\)])/, # do_something( arg1, arg2 ) as argument of another function call
30             BRACKETS => qr/\{[^{}]*\}|\[[^\[\]]*\]|\([^\(\)]*\)|~[^~]+~/,
31             IDENTIFIER => qr~[a-zA-Z_\x24][a-zA-Z_0-9\\x24]*~,
32             SCOPED => qr/~#(\d+)~/,
33             VARS => qr~\b(?:var|function)\s+((?>[a-zA-Z0-9_\x24]+))~, # var x, funktion blah
34             PREFIX => qr~\x02~,
35             SHRUNK => qr~\x02\d+\b~
36             };
37              
38             our $BASE62_VARS = {
39             WORDS => qr~(\b[0-9a-zA-Z]\b|(?>[a-zA-Z0-9_]{2,}))~,
40             ENCODE10 => 'String',
41             ENCODE36 => 'function(c){return c.toString(36)}',
42             ENCODE62 => q~function(c){return(c<62?'':e(parseInt(c/62)))+((c=c%62)>35?String.fromCharCode(c+29):c.toString(36))}~,
43             UNPACK =>
44             q~eval(function(p,a,c,k,e,r){e=%s;if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'%s'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('%s',%s,%d,'%s'.split('|'),0,{}))~
45             };
46              
47             our $DICTIONARY = {
48             STRING1 => qr~"(?>(?:(?>[^"\\]+)|\\.|\\")*)"~,
49             STRING2 => qr~'(?>(?:(?>[^'\\]+)|\\.|\\')*)'~,
50             STRING3 => qr~`(?>(?:(?>[^`\\]+)|\\.|\\`)*)`~,
51             REGEXP => qr~\/(\\[\/\\]|[^*\/])(\\.|[^\/\n\\])*\/[gim]*~,
52             OPERATOR => qr'return|typeof|[\[(\^=,{}:;&|!*?]',
53             CONDITIONAL => qr~\/\*\@\w*|\w*\@\*\/|\/\/\@\w*|\@(?>\w+)~,
54              
55             # single line comments
56             COMMENT1 => qr~(:?)\/\/([\@#])?([^\n]*)?\n~,
57              
58             # multline comments
59             COMMENT2 => qr~\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/~
60             };
61              
62             our $DATA = [
63             { regexp => $DICTIONARY->{STRING1} },
64             { regexp => $DICTIONARY->{STRING2} },
65             { regexp => $DICTIONARY->{STRING3} },
66             { regexp => $DICTIONARY->{CONDITIONAL} },
67             {
68             regexp => '(' . $DICTIONARY->{OPERATOR} . ')\s*(' . $DICTIONARY->{REGEXP} . ')',
69             replacement => sub {
70             return sprintf( "%s%s", $_[0]->{submatches}->[0], $_[0]->{submatches}->[1] );
71             },
72             },
73             ];
74              
75             our $COMMENTS = [
76             {
77             regexp => ';;;[^\n]*\n',
78             replacement => ''
79             },
80             { regexp => $DICTIONARY->{COMMENT1} . '\s*(' . $DICTIONARY->{REGEXP} . ')?', },
81             { regexp => '(' . $DICTIONARY->{COMMENT2} . ')\s*(' . $DICTIONARY->{REGEXP} . ')?' }
82             ];
83              
84             our $CLEAN = [
85             {
86             regexp => '\(\s*([^;)]*)\s*;\s*([^;)]*)\s*;\s*([^;)]*)\)',
87             replacement => sub { return sprintf( "(%s;%s;%s)", @{ $_[0]->{submatches} } ); }
88             },
89             { regexp => 'throw[^};]+[};]' },
90             {
91             regexp => ';+\s*([};])',
92             replacement => sub { return $_[0]->{submatches}->[0]; }
93             }
94             ];
95              
96             our $WHITESPACE = [
97             { regexp => '\/\/@[^\n]*\n' },
98             {
99             regexp => '@\s+\b',
100             replacement => '@ '
101             },
102             {
103             regexp => '\b\s+@',
104             replacement => ' @'
105             },
106             {
107             regexp => '(\d)\s+(\.\s*[a-z\x24_\[(])',
108             replacement => sub { return sprintf( "%s %s", @{ $_[0]->{submatches} } ); }
109             },
110             {
111             regexp => '([+-])\s+([+-])',
112             replacement => sub { return sprintf( "%s %s", @{ $_[0]->{submatches} } ); }
113             },
114             {
115             regexp => '(?>\s+)(\x24)(?>\s+)',
116             replacement => sub { return sprintf( " %s ", $_[0]->{submatches}->[0] ); }
117             },
118             {
119             regexp => '(\x24)(?>\s+)(?!=)',
120             replacement => sub { return sprintf( "%s ", $_[0]->{submatches}->[0] ); }
121             },
122             {
123             regexp => '(?\s+)(\x24)',
124             replacement => sub { return sprintf( " %s", $_[0]->{submatches}->[0] ); }
125             },
126             {
127             regexp => '\b\s+\b',
128             replacement => ' '
129             },
130             {
131             regexp => '\s+',
132             replacement => ''
133             }
134             ];
135              
136             our $TRIM = [
137             {
138             regexp => '(\d)(?:\|\d)+\|(\d)',
139             replacement => sub { return sprintf( "%d-%d", $_[0]->{submatches}->[0] || 0, $_[0]->{submatches}->[1] || 0 ); }
140             },
141             {
142             regexp => '([a-z])(?:\|[a-z])+\|([a-z])',
143             replacement => sub { return sprintf( "%s-%s", $_[0]->{submatches}->[0], $_[0]->{submatches}->[1] ); }
144             },
145             {
146             regexp => '([A-Z])(?:\|[A-Z])+\|([A-Z])',
147             replacement => sub { return sprintf( "%s-%s", $_[0]->{submatches}->[0], $_[0]->{submatches}->[1] ); }
148             },
149             {
150             regexp => '\|',
151             replacement => ''
152             }
153             ];
154              
155             our @REGGRPS = ( 'comments', 'clean', 'whitespace', 'concat', 'trim', 'data_store', 'concat_store' );
156              
157             # --------------------------------------------------------------------------- #
158              
159             {
160 4     4   33 no strict 'refs';
  4         12  
  4         19498  
161              
162             foreach my $field ( @BOOLEAN_ACCESSORS ) {
163             next if defined *{ __PACKAGE__ . '::' . $field }{CODE};
164              
165             *{ __PACKAGE__ . '::' . $field } = sub {
166 99     99   197 my ( $self, $value ) = @_;
167              
168 99 100       249 $self->{ '_' . $field } = $value ? 1 : undef if ( defined( $value ) );
    100          
169              
170 99         375 return $self->{ '_' . $field };
171             };
172             }
173              
174             foreach my $field ( @COPYRIGHT_ACCESSORS ) {
175             $field = '_' . $field if ( $field eq 'copyright_comment' );
176             next if defined *{ __PACKAGE__ . '::' . $field }{CODE};
177              
178             *{ __PACKAGE__ . '::' . $field } = sub {
179 126     126   829 my ( $self, $value ) = @_;
180              
181 126 100 66     394 if ( defined( $value ) and not ref( $value ) ) {
182 46         419 $value =~ s/^\s*|\s*$//gs;
183 46         192 $self->{ '_' . $field } = $value;
184             }
185              
186 126         198 my $ret = '';
187              
188 126 100       308 if ( $self->{ '_' . $field } ) {
189 14         39 $ret = '/* ' . $self->{ '_' . $field } . ' */' . "\n";
190             }
191              
192 126         321 return $ret;
193             };
194             }
195              
196             foreach my $reggrp ( @REGGRPS ) {
197             next if defined *{ __PACKAGE__ . '::reggrp_' . $reggrp }{CODE};
198              
199             *{ __PACKAGE__ . '::reggrp_' . $reggrp } = sub {
200 413     413   776 my ( $self ) = shift;
201              
202 413         2587 return $self->{ '_reggrp_' . $reggrp };
203             };
204             }
205             }
206              
207             sub compress {
208 163     163 1 2054 my ( $self, $value ) = @_;
209              
210 163 100       351 if ( defined( $value ) ) {
211 29 100       151 if ( grep( $value eq $_, @COMPRESS_OPTS ) ) {
    50          
212 21         90 $self->{_compress} = $value;
213             }
214             elsif ( !$value ) {
215 0         0 $self->{_compress} = undef;
216             }
217             }
218              
219 163   66     400 $self->{_compress} ||= $DEFAULT_COMPRESS;
220              
221 163         562 return $self->{_compress};
222             }
223              
224             # these variables are used in the closures defined in the init function
225             # below - we have to use globals as using $self within the closures leads
226             # to a reference cycle and thus memory leak, and we can't scope them to
227             # the init method as they may change. they are set by the minify sub
228             our $reggrp_comments;
229             our $reggrp_clean;
230             our $reggrp_whitespace;
231              
232             sub init {
233 31     31 0 1734882 my $class = shift;
234 31         63 my $self = {};
235              
236 31         68 bless( $self, $class );
237              
238 31         75 @{ $self->{clean}->{reggrp_data} } = ( @$DATA, @$CLEAN );
  31         159  
239 31         103 @{ $self->{whitespace}->{reggrp_data} } = ( @$DATA[ 0, 1, 2, 4 ], @$WHITESPACE );
  31         103  
240 31         86 $self->{trim}->{reggrp_data} = $TRIM;
241              
242 31         114 @{ $self->{data_store}->{reggrp_data} } = map {
243 31         77 {
244             regexp => $_->{regexp},
245 792     792   11053 store => sub { return sprintf( "%s", $_[0]->{match} ); },
246             replacement => sub {
247 792     792   82850 return sprintf( $RESTORE_REPLACEMENT, $_[0]->{store_index} );
248             },
249             }
250 155         819 } @$DATA;
251              
252             $self->{data_store}->{reggrp_data}->[-1]->{replacement} = sub {
253 9     9   2219 return sprintf( "%s$RESTORE_REPLACEMENT", $_[0]->{submatches}->[0], $_[0]->{store_index} );
254 31         195 };
255              
256             $self->{data_store}->{reggrp_data}->[-1]->{store} = sub {
257 9     9   143 return $_[0]->{submatches}->[1];
258 31         128 };
259              
260 31         127 @{ $self->{concat_store}->{reggrp_data} } = map {
261 31         87 my $data = $_;
  93         162  
262             {
263             regexp => $data->{regexp},
264             store => sub {
265 3211     3211   51194 my ( $quote, $string, $rest ) = $_[0]->{match} =~ /^(['"`])(.*)(['"`])$/;
266 3211 100       7198 return $_[0]->{match} if ! $rest;
267              
268 3210         6984 return $string;
269             },
270             replacement => sub {
271 3211     3211   248232 my ( $quote, $string, $rest ) = $_[0]->{match} =~ /^(['"`])(.*)(['"`])$/;
272 3211 100       7805 return $_[0]->{match} if ! $rest;
273              
274 3210         12949 return sprintf( "%s$RESTORE_REPLACEMENT%s", $quote, $_[0]->{store_index}, $quote );
275             },
276              
277 93         511 };
278             } @$DATA[ 0, 1, 2 ];
279              
280 31         108 @{ $self->{concat}->{reggrp_data} } = map {
281 31         75 my $quote = $_;
  93         173  
282 93         143 my $restore_pattern = $RESTORE_PATTERN;
283 93         427 $restore_pattern =~ s/\(\\d\+\)/\\d\+/g;
284              
285 93         267 my $regexp = '(' . $quote . $restore_pattern . $quote . ')((?:\+' . $quote . $restore_pattern . $quote . ')+)';
286 93         1707 $regexp = qr/$regexp/;
287              
288             {
289             regexp => $regexp,
290             replacement => sub {
291 16     16   14153 my $submatches = $_[0]->{submatches};
292 16         36 my $ret = $submatches->[0];
293              
294 16         45 my $next_str = '^\+(' . $quote . $restore_pattern . $quote . ')';
295              
296 16         184 while ( my ( $next ) = $submatches->[1] =~ /$next_str/ ) {
297 20         44 chop( $ret );
298 20         45 $ret .= substr( $next, 1 );
299 20         163 $submatches->[1] =~ s/$next_str//;
300             }
301              
302 16         49 return $ret;
303             },
304 93         588 };
305             } ( '"', '\'', '\`' );
306              
307 31         93 @{ $self->{comments}->{reggrp_data} } = ( @$DATA[ 0, 1, 2, 4 ], @$COMMENTS );
  31         102  
308              
309             # single line comment
310             $self->{comments}->{reggrp_data}->[-2]->{replacement} = sub {
311 1836     1836   531685 my $submatches = $_[0]->{submatches};
312              
313 1836 100       5726 if ( $submatches->[0] eq ':' ) {
    100          
    100          
314 1         7 return sprintf( "://%s", $submatches->[2] );
315             }
316             elsif ( $submatches->[1] eq '#' ) {
317 6         12 my $cmnt = sprintf( "%s//%s%s__NEW_LINE__", @{$submatches}[0 .. 2] );
  6         30  
318 6         23 return $cmnt;
319             }
320             elsif ( $submatches->[1] eq '@' ) {
321 4         14 $reggrp_comments->exec( \$submatches->[2] );
322 4         112 $reggrp_clean->exec( \$submatches->[2] );
323 4         1028 $reggrp_whitespace->exec( \$submatches->[2] );
324              
325 4         991 return sprintf( "%s//%s%s\n%s", @{$submatches}[0 .. 3] );
  4         24  
326             }
327 1825         6184 return sprintf( "\n%s", $submatches->[3] );
328 31         164 };
329              
330             # multi line comments
331             $self->{comments}->{reggrp_data}->[-1]->{replacement} = sub {
332 178     178   109968 my $submatches = $_[0]->{submatches};
333 178 100       605 if ( $submatches->[0] =~ /^\/\*\@(.*)\@\*\/$/sm ) {
334 7         20 my $cmnt = $1;
335              
336 7         22 $reggrp_comments->exec( \$cmnt );
337 7         236 $reggrp_clean->exec( \$cmnt );
338 7         1392 $reggrp_whitespace->exec( \$cmnt );
339              
340 7         3387 return sprintf( '/*@%s@*/ %s', $cmnt, $submatches->[1] );
341             }
342 171         665 return sprintf( " %s", $submatches->[1] );
343 31         136 };
344              
345 31         95 foreach my $reggrp ( @REGGRPS ) {
346 217         127678 my $reggrp_args = { reggrp => $self->{$reggrp}->{reggrp_data} };
347              
348 217 100 100     933 $reggrp_args->{restore_pattern} = $RESTORE_PATTERN if ( $reggrp eq 'data_store' or $reggrp eq 'concat_store' );
349              
350 217         662 $self->{ '_reggrp_' . $reggrp } = Regexp::RegGrp->new( $reggrp_args );
351             }
352              
353 31         9662 $self->{block_data} = [];
354              
355 31         197 return $self;
356             }
357              
358             sub minify {
359 42     42 0 8804 my ( $self, $input, $opts );
360              
361 42 100 66     220 unless (ref( $_[0] )
362             and ref( $_[0] ) eq __PACKAGE__ )
363             {
364 9         29 $self = __PACKAGE__->init();
365              
366 9 50       32 shift( @_ ) unless ( ref( $_[0] ) );
367              
368 9         21 ( $input, $opts ) = @_;
369             }
370             else {
371 33         81 ( $self, $input, $opts ) = @_;
372             }
373              
374 42 50       133 if ( ref( $input ) ne 'SCALAR' ) {
375 0         0 carp( 'First argument must be a scalarref!' );
376 0         0 return undef;
377             }
378              
379 42         70 my $javascript = \'';
380 42         70 my $cont = 'void';
381              
382 42 50       90 if ( defined( wantarray ) ) {
383 0 0       0 my $tmp_input = ref( $input ) ? ${$input} : $input;
  0         0  
384              
385 0         0 $javascript = \$tmp_input;
386 0         0 $cont = 'scalar';
387             }
388             else {
389 42 50       99 $javascript = ref( $input ) ? $input : \$input;
390             }
391              
392 42 100       102 if ( ref( $opts ) eq 'HASH' ) {
393 27         55 foreach my $field ( @BOOLEAN_ACCESSORS ) {
394 54 100       175 $self->$field( $opts->{$field} ) if ( defined( $opts->{$field} ) );
395             }
396              
397 27         56 foreach my $field ( 'compress', 'copyright' ) {
398 54 100       205 $self->$field( $opts->{$field} ) if ( defined( $opts->{$field} ) );
399             }
400             }
401              
402             # (re)initialize variables used in the closures
403 42         110 $reggrp_comments = $self->reggrp_comments;
404 42         115 $reggrp_clean = $self->reggrp_clean;
405 42         97 $reggrp_whitespace = $self->reggrp_whitespace;
406              
407 42         72 my $copyright_comment = '';
408              
409 42 100       63 if ( ${$javascript} =~ /$COPYRIGHT_COMMENT/ism ) {
  42         2298  
410 3         23 $copyright_comment = $1;
411             }
412              
413             # Resets copyright_comment() if there is no copyright comment
414 42         165 $self->_copyright_comment( $copyright_comment );
415              
416 42 100 100     113 if ( not $self->no_compress_comment() and ${$javascript} =~ /$PACKER_COMMENT/ ) {
  37         729  
417 1         5 my $compress = $1;
418 1 50       6 if ( $compress eq '_no_compress_' ) {
419 1 50       4 return ${$javascript} if ( $cont eq 'scalar' );
  0         0  
420 1         4 return;
421             }
422              
423 0         0 $self->compress( $compress );
424             }
425              
426 41         78 ${$javascript} =~ s/\r//gsm;
  41         144  
427 41         78 ${$javascript} .= "\n";
  41         847  
428 41         121 $self->reggrp_comments()->exec( $javascript );
429 41         12491 $self->reggrp_clean()->exec( $javascript );
430 41         13277 $self->reggrp_whitespace()->exec( $javascript );
431 41         430003 $self->reggrp_concat_store()->exec( $javascript );
432 41         3880 $self->reggrp_concat()->exec( $javascript );
433 41         21092 $self->reggrp_concat_store()->restore_stored( $javascript );
434              
435 41 100       24821 if ( $self->compress() ne 'clean' ) {
436 17         55 $self->reggrp_data_store()->exec( $javascript );
437              
438 17         1653 while ( ${$javascript} =~ /$SHRINK_VARS->{BLOCK}/ ) {
  44         3893  
439 27         64 ${$javascript} =~ s/$SHRINK_VARS->{BLOCK}/$self->_store_block_data( $1 )/egsm;
  27         4062  
  511         1196  
440             }
441              
442 17         69 $self->_restore_data( $javascript, 'block_data', $SHRINK_VARS->{ENCODED_BLOCK} );
443              
444 17         40 my %shrunk_vars = map { $_ => 1 } ( ${$javascript} =~ /$SHRINK_VARS->{SHRUNK}/g );
  1657         2861  
  17         1267  
445              
446 17         225 my $cnt = 0;
447 17         89 foreach my $shrunk_var ( sort keys( %shrunk_vars ) ) {
448 58         116 my $short_id;
449             do {
450 70         172 $short_id = $self->_encode52( $cnt++ );
451 58         78 } while ( ${$javascript} =~ /[^a-zA-Z0-9_\\x24\.]\Q$short_id\E[^a-zA-Z0-9_\\x24:]/ );
  70         3334  
452              
453 58         142 ${$javascript} =~ s/$shrunk_var/$short_id/g;
  58         1878  
454             }
455              
456 17         53 $self->reggrp_data_store()->restore_stored( $javascript );
457              
458 17         6258 $self->{block_data} = [];
459             }
460              
461 41 100 100     128 if ( $self->compress() eq 'obfuscate' or $self->compress() eq 'best' ) {
462 7         20 my $words = {};
463              
464 7         9 my @words = ${$javascript} =~ /$BASE62_VARS->{WORDS}/g;
  7         11627  
465              
466 7         74 my $idx = 0;
467              
468 7         25 foreach ( @words ) {
469 10898         19209 $words->{$_}->{count}++;
470             }
471              
472 7         25 WORD: foreach my $word ( sort { $words->{$b}->{count} <=> $words->{$a}->{count} } sort keys( %{$words} ) ) {
  10419         15076  
  7         1664  
473              
474 3122 100 66     6658 if ( exists( $words->{$word}->{encoded} ) and $words->{$word}->{encoded} eq $word ) {
475 151         241 next WORD;
476             }
477              
478 2971         4935 my $encoded = $self->_encode62( $idx );
479              
480 2971 100       5499 if ( exists( $words->{$encoded} ) ) {
481 209         268 my $next = 0;
482 209 100       388 if ( exists( $words->{$encoded}->{encoded} ) ) {
483 58         137 $words->{$word}->{encoded} = $words->{$encoded}->{encoded};
484 58         111 $words->{$word}->{index} = $words->{$encoded}->{index};
485 58         106 $words->{$word}->{minus} = length( $word ) - length( $words->{$word}->{encoded} );
486 58         69 $next = 1;
487             }
488 209         348 $words->{$encoded}->{encoded} = $encoded;
489 209         315 $words->{$encoded}->{index} = $idx;
490 209         335 $words->{$encoded}->{minus} = 0;
491 209         264 $idx++;
492 209 100       378 next WORD if ( $next );
493 151         240 redo WORD;
494             }
495              
496 2762         4718 $words->{$word}->{encoded} = $encoded;
497 2762         4324 $words->{$word}->{index} = $idx;
498 2762         4324 $words->{$word}->{minus} = length( $word ) - length( $encoded );
499              
500 2762         4131 $idx++;
501             }
502              
503 7         136 my $packed_length = length( ${$javascript} );
  7         38  
504              
505 7         21 my ( @pk, @pattern ) = ( (), () );
506              
507 7         13 foreach ( sort { $words->{$a}->{index} <=> $words->{$b}->{index} } sort keys( %{$words} ) ) {
  12076         18102  
  7         1683  
508 2971         5611 $packed_length -= ( $words->{$_}->{count} * $words->{$_}->{minus} );
509              
510 2971 100       5107 if ( $words->{$_}->{encoded} ne $_ ) {
511 2762         4228 push( @pk, $_ );
512 2762         5617 push( @pattern, $words->{$_}->{encoded} );
513             }
514             else {
515 209         300 push( @pk, '' );
516 209         359 push( @pattern, '' );
517             }
518             }
519              
520 7         130 my $size = scalar( @pattern );
521              
522 7 100       146 splice( @pattern, 62 ) if ( scalar( @pattern ) > 62 );
523              
524 7         36 my $pd = join( '|', @pattern );
525              
526 7         30 $self->reggrp_trim()->exec( \$pd );
527              
528 7 50       1108 unless ( $pd ) {
529 0         0 $pd = '^$';
530             }
531             else {
532 7         22 $pd = '[' . $pd . ']';
533              
534 7 100       20 if ( $size > 62 ) {
535 3         9 $pd = '(' . $pd . '|';
536              
537 3         13 my $enc = $self->_encode62( $size );
538              
539 3         31 my ( $c ) = $enc =~ /(^.)/;
540 3         11 my $ord = ord( $c );
541              
542 3         8 my $mul = length( $enc ) - 1;
543              
544 3         4 my $is62 = 0;
545              
546 3 100       20 if ( $ord >= 65 ) {
    100          
    50          
    50          
547 1 50       7 if ( $c eq 'Z' ) {
548 0         0 $mul += 1;
549 0         0 $is62 = 1;
550             }
551             else {
552 1         3 $pd .= '[0-9a';
553 1 50       9 if ( $ord > 97 ) {
    50          
    50          
554 0         0 $pd .= '-' . $c;
555             }
556             elsif ( $ord > 65 ) {
557 0         0 $pd .= '-zA-' . $c;
558             }
559             elsif ( $ord == 65 ) {
560 1         5 $pd .= '-zA';
561             }
562 1         3 $pd .= ']';
563             }
564             }
565             elsif ( $ord == 57 ) {
566 1         3 $pd .= '[0-9]';
567             }
568             elsif ( $ord == 50 ) {
569 0         0 $pd .= '[12]';
570             }
571             elsif ( $ord == 49 ) {
572 1         3 $pd .= '1';
573             }
574             else {
575 0         0 $pd .= '[0-' . ( $ord - 48 ) . ']';
576             }
577              
578 3 50       19 $pd .= '[0-9a-zA-Z]' . ( ( $mul > 1 ) ? '{' . $mul . '}' : '' );
579              
580 3 50       9 $mul-- if ( $is62 );
581              
582 3 50       10 if ( $mul > 1 ) {
583 0         0 for ( my $i = $mul; $i >= 2; $i-- ) {
584 0         0 $pd .= '|[0-9a-zA-Z]{' . $i . '}';
585             }
586             }
587              
588 3         9 $pd .= ')';
589             }
590             }
591 7         20 $packed_length += length( $pd );
592              
593 7         300 my $pk = join( '|', @pk );
594 7         412 $pk =~ s/(?>\|+)$//;
595 7         21 $packed_length += length( $pk );
596              
597 7 50       61 my $pc = length( $pk ) ? ( ( $pk =~ tr/|/|/ ) + 1 ) : 0;
598 7         22 $packed_length += length( $pc );
599              
600 7         13 my $pa = '[]';
601 7         14 $packed_length += length( $pa );
602              
603 7 50       35 my $pe = $BASE62_VARS->{ 'ENCODE' . ( $pc > 10 ? $pc > 36 ? 62 : 36 : 10 ) };
    100          
604 7         16 $packed_length += length( $pe );
605              
606 7         20 $packed_length += length( $BASE62_VARS->{UNPACK} );
607 7         112 $packed_length -= ( $BASE62_VARS->{UNPACK} =~ s/(%s|%d)/$1/g ) * 2;
608              
609 7         22 my ( @length_matches ) = ${$javascript} =~ s/((?>[\r\n]+))/$1/g;
  7         265  
610 7         21 foreach ( @length_matches ) {
611 7         24 $packed_length -= length( $_ ) - 3;
612             }
613              
614 7         13 $packed_length += ${$javascript} =~ tr/\\\'/\\\'/;
  7         90  
615              
616 7 100 100     32 if ( $self->compress() eq 'obfuscate' or $packed_length <= length( ${$javascript} ) ) {
  5         50  
617              
618 5         11 ${$javascript} =~ s/$BASE62_VARS->{WORDS}/sprintf( "%s", $words->{$1}->{encoded} )/eg;
  5         50  
  10865         36095  
619              
620 5         27 ${$javascript} =~ s/([\\'])/\\$1/g;
  5         527  
621 5         13 ${$javascript} =~ s/[\r\n]+/\\n/g;
  5         139  
622              
623 5         16 my $pp = ${$javascript};
  5         33  
624              
625 5         126 ${$javascript} = sprintf( $BASE62_VARS->{UNPACK}, $pe, $pd, $pp, $pa, $pc, $pk );
  5         2443  
626             }
627              
628             }
629              
630 41 100       143 if ( not $self->remove_copyright() ) {
631 40   100     103 ${$javascript} = ( $self->copyright() || $self->_copyright_comment() ) . ${$javascript};
  40         129  
  40         1030  
632             }
633              
634             # GH #9 bodge for sourceMappingURL
635 41         84 ${$javascript} =~ s/__NEW_LINE__/\n/xsmg;
  41         343  
636 41         74 ${$javascript} =~ s!//#sourceMappingURL!//# sourceMappingURL!g;
  41         558  
637 41         59 chomp( ${$javascript} );
  41         115  
638              
639 41 50       696 return ${$javascript} if ( $cont eq 'scalar' );
  0         0  
640             }
641              
642             sub _restore_data {
643 379     379   695 my ( $self, $string_ref, $data_name, $pattern ) = @_;
644              
645 379         526 while ( ${$string_ref} =~ /$pattern/ ) {
  533         2267  
646 154         280 ${$string_ref} =~ s/$pattern/$self->{$data_name}->[$1]/egsm;
  154         710  
  511         2467  
647             }
648             }
649              
650             sub _store_block_data {
651 511     511   1436 my ( $self, $match ) = @_;
652              
653 511         4104 my ( undef, $prefix, $blocktype, $args, $block ) = $match =~ /$SHRINK_VARS->{BLOCK}/;
654              
655 511   100     1485 $prefix ||= '';
656 511   100     1021 $blocktype ||= '';
657 511   100     981 $args ||= '';
658 511         722 my $replacement = '';
659 511 100       932 if ( $blocktype eq 'function' ) {
660              
661 181         570 $self->_restore_data( \$block, 'block_data', $SHRINK_VARS->{SCOPED} );
662              
663 181         1894 $args =~ s/\s*//g;
664              
665 181         473 $block = $args . $block;
666 181         1065 $prefix =~ s/$SHRINK_VARS->{BRACKETS}//;
667              
668 181         858 $args =~ s/^\(|\)$//g;
669              
670 181         674 while ( $args =~ /$SHRINK_VARS->{CALLER}/ ) {
671 0         0 $args =~ s/$SHRINK_VARS->{CALLER}//gsm;
672             }
673              
674 181         790 my @vars = grep( $_, split( /\s*,\s*/, $args ) );
675 181 100       499 my $do_shrink = grep( $_ eq '_no_shrink_', @vars ) ? 0 : 1;
676              
677 181         314 my %block_vars = ();
678 181 100       335 if ( $do_shrink ) {
679 180         2245 %block_vars = map { $_ => 1 } ( $block =~ /$SHRINK_VARS->{VARS}/g ), grep( $_ ne '$super', @vars );
  475         1531  
680             }
681              
682 181         637 $self->_restore_data( \$block, 'block_data', $SHRINK_VARS->{ENCODED_BLOCK} );
683              
684 181 100       385 if ( $do_shrink ) {
685              
686 180         269 my $cnt = 0;
687 180         723 foreach my $block_var ( sort keys( %block_vars ) ) {
688 468 50       952 if ( length( $block_var ) ) {
689 468         4670 while ( $block =~ /$SHRINK_VARS->{PREFIX}\Q$cnt\E\b/ ) {
690 16         163 $cnt++;
691             }
692              
693 468         10852 while ( $block =~ /[^a-zA-Z0-9_\\x24\.]\Q$block_var\E[^a-zA-Z0-9_\\x24:]/ ) {
694 491         10989 $block =~ s/([^a-zA-Z0-9_\\x24\.])\Q$block_var\E([^a-zA-Z0-9_\\x24:])/sprintf( "%s\x02%d%s", $1, $cnt, $2 )/eg;
  1656         10572  
695             }
696              
697 468         6129 $block =~ s/([^{,a-zA-Z0-9_\\x24\.])\Q$block_var\E:/sprintf( "%s\x02%d:", $1, $cnt )/eg;
  1         6  
698              
699 468         1354 $cnt++;
700             }
701             }
702             }
703 181         372 $replacement = sprintf( "%s~%d~", $prefix, scalar( @{ $self->{block_data} } ) );
  181         667  
704              
705 181         303 push( @{ $self->{block_data} }, $block );
  181         614  
706             }
707             else {
708 330         433 $replacement = sprintf( "~#%d~", scalar( @{ $self->{block_data} } ) );
  330         847  
709              
710 330         538 push( @{ $self->{block_data} }, $prefix . $block );
  330         825  
711             }
712              
713 511         23784 return $replacement;
714             }
715              
716             sub _encode52 {
717 70     70   125 my ( $self, $c ) = @_;
718              
719 70         134 my $m = $c % 52;
720              
721 70 50       177 my $ret = $m > 25 ? chr( $m + 39 ) : chr( $m + 97 );
722              
723 70 50       144 if ( $c >= 52 ) {
724 0         0 $ret = $self->_encode52( int( $c / 52 ) ) . $ret;
725             }
726              
727 70 50       126 $ret = substr( $ret, 1 ) . '0' if ( $ret =~ /^(do|if|in)$/ );
728              
729 70         172 return $ret;
730             }
731              
732             sub _encode62 {
733 5725     5725   8292 my ( $self, $c ) = @_;
734              
735 5725         7784 my $m = $c % 62;
736              
737 5725 100       10316 my $ret = $m > 35 ? chr( $m + 29 ) : $m > 9 ? chr( $m + 87 ) : $m;
    100          
738              
739 5725 100       9528 if ( $c >= 62 ) {
740 2751         5363 $ret = $self->_encode62( int( $c / 62 ) ) . $ret;
741             }
742              
743 5725         11681 return $ret;
744             }
745              
746             1;
747              
748             __END__