File Coverage

blib/lib/CSS/Orientation.pm
Criterion Covered Total %
statement 135 142 95.0
branch 27 42 64.2
condition 1 3 33.3
subroutine 25 26 96.1
pod 2 18 11.1
total 190 231 82.2


line stmt bran cond sub pod time code
1             package CSS::Orientation;
2              
3 12     12   297194 use strict;
  12         29  
  12         457  
4 12     12   58 use warnings;
  12         22  
  12         592  
5              
6             our $VERSION = '0.01';
7              
8 12     12   64 use base qw( Exporter );
  12         29  
  12         66591  
9              
10             our %EXPORT_TAGS = ( 'all' => [ qw(
11             ChangeLeftToRightToLeft
12             ) ] );
13              
14             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
15              
16             # h [0-9a-f] ; a hexadecimal digit
17             our $HEX = q'[0-9a-f]';
18              
19             # nonascii [\200-\377]
20             our $NON_ASCII = q'[\200-\377]';
21              
22             # unicode \\{h}{1,6}(\r\n|[ \t\r\n\f])?
23             our $UNICODE = q'(?:(?:\\' . $HEX . q'{1,6})(?:\r\n|[ \t\r\n\f])?)';
24              
25             # escape {unicode}|\\[^\r\n\f0-9a-f]
26             our $ESCAPE = q'(?:' . $UNICODE . q'|\\[^\r\n\f0-9a-f])';
27              
28             # nmstart [_a-z]|{nonascii}|{escape}
29             our $NMSTART = q'(?:[_a-z]|' . $NON_ASCII . q'|' . $ESCAPE . q')';
30              
31             # nmchar [_a-z0-9-]|{nonascii}|{escape}
32             our $NMCHAR = q'(?:[_a-z0-9-]|' . $NON_ASCII . q'|' . $ESCAPE . q')';
33              
34             # ident -?{nmstart}{nmchar}*
35             our $IDENT = q'-?' . $NMSTART . $NMCHAR . '*';
36              
37             # name {nmchar}+
38             our $NAME = $NMCHAR . q'+';
39              
40             # hash
41             our $HASH = q'#' . $NAME;
42              
43             # num [0-9]+|[0-9]*"."[0-9]+
44             our $NUM = q'(?:[0-9]*\.[0-9]+|[0-9]+)';
45              
46             # s [ \t\r\n\f]
47             our $SPACE = q'[ \t\r\n\f]';
48              
49             # w {s}*
50             our $WHITESPACE = '(?:' . $SPACE . q'*)';
51              
52             # url special chars
53             our $URL_SPECIAL_CHARS = q'[!#$%&*-~]';
54              
55             # url chars ({url_special_chars}|{nonascii}|{escape})*
56             our $URL_CHARS = sprintf( q'(?:%s|%s|%s)*', $URL_SPECIAL_CHARS, $NON_ASCII, $ESCAPE );
57              
58             # comments
59             # see http://www.w3.org/TR/CSS21/grammar.html
60             our $COMMENT = q'/\*[^*]*\*+([^/*][^*]*\*+)*/';
61              
62             # {E}{M} {return EMS;}
63             # {E}{X} {return EXS;}
64             # {P}{X} {return LENGTH;}
65             # {C}{M} {return LENGTH;}
66             # {M}{M} {return LENGTH;}
67             # {I}{N} {return LENGTH;}
68             # {P}{T} {return LENGTH;}
69             # {P}{C} {return LENGTH;}
70             # {D}{E}{G} {return ANGLE;}
71             # {R}{A}{D} {return ANGLE;}
72             # {G}{R}{A}{D} {return ANGLE;}
73             # {M}{S} {return TIME;}
74             # {S} {return TIME;}
75             # {H}{Z} {return FREQ;}
76             # {K}{H}{Z} {return FREQ;}
77             # % {return PERCENTAGE;}
78             our $UNIT = q'(?:em|ex|px|cm|mm|in|pt|pc|deg|rad|grad|ms|s|hz|khz|%)';
79              
80             # {num}{UNIT|IDENT} {return NUMBER;}
81             our $QUANTITY = sprintf( '%s(?:%s%s|%s)?', $NUM, $WHITESPACE, $UNIT, $IDENT );
82              
83              
84              
85              
86             # Generic token delimiter character.
87             our $TOKEN_DELIMITER = '~';
88              
89             # This is a temporary match token we use when swapping strings.
90             our $TMP_TOKEN = sprintf( '%sTMP%s', $TOKEN_DELIMITER, $TOKEN_DELIMITER );
91              
92             # Token to be used for joining lines.
93             our $TOKEN_LINES = sprintf( '%sJ%s', $TOKEN_DELIMITER, $TOKEN_DELIMITER );
94              
95             # Global constant text strings for CSS value matches.
96             our $LTR = 'ltr';
97             our $RTL = 'rtl';
98             our $LEFT = 'left';
99             our $RIGHT = 'right';
100              
101             # This is a lookbehind match to ensure that we don't replace instances
102             # of our string token (left, rtl, etc...) if there's a letter in front of it.
103             # Specifically, this prevents replacements like 'background: url(bright.png)'.
104             our $LOOKBEHIND_NOT_LETTER = q'(?
105              
106             # This is a lookahead match to make sure we don't replace left and right
107             # in actual classnames, so that we don't break the HTML/CSS dependencies.
108             # Read literally, it says ignore cases where the word left, for instance, is
109             # directly followed by valid classname characters and a curly brace.
110             # ex: .column-left {float: left} will become .column-left {float: right}
111             our $LOOKAHEAD_NOT_OPEN_BRACE = sprintf( q'(?!(?:%s|%s|%s|#|\:|\.|\,|\+|>)*?{)',
112             $NMCHAR, $TOKEN_LINES, $SPACE );
113              
114             # These two lookaheads are to test whether or not we are within a
115             # background: url(HERE) situation.
116             # Ref: http://www.w3.org/TR/CSS21/syndata.html#uri
117             our $VALID_AFTER_URI_CHARS = sprintf( q'[\'\"]?%s', $WHITESPACE );
118             our $LOOKAHEAD_NOT_CLOSING_PAREN = sprintf( q'(?!%s?%s\))', $URL_CHARS,
119             $VALID_AFTER_URI_CHARS );
120             our $LOOKAHEAD_FOR_CLOSING_PAREN = sprintf( q'(?=%s?%s\))', $URL_CHARS,
121             $VALID_AFTER_URI_CHARS );
122              
123             # Compile a regex to swap left and right values in 4 part notations.
124             # We need to match negatives and decimal numeric values.
125             # The case of border-radius is extra complex, so we handle it separately below.
126             # ex. 'margin: .25em -2px 3px 0' becomes 'margin: .25em 0 3px -2px'.
127              
128             our $POSSIBLY_NEGATIVE_QUANTITY = sprintf( q'((?:-?%s)|(?:inherit|auto))', $QUANTITY );
129             our $POSSIBLY_NEGATIVE_QUANTITY_SPACE = sprintf( q'%s%s%s', $POSSIBLY_NEGATIVE_QUANTITY,
130             $SPACE,
131             $WHITESPACE );
132              
133             our $FOUR_NOTATION_QUANTITY_RE = risprintf( q'%s%s%s%s',
134             $POSSIBLY_NEGATIVE_QUANTITY_SPACE,
135             $POSSIBLY_NEGATIVE_QUANTITY_SPACE,
136             $POSSIBLY_NEGATIVE_QUANTITY_SPACE,
137             $POSSIBLY_NEGATIVE_QUANTITY );
138             our $COLOR = sprintf( q'(%s|%s)', $NAME, $HASH );
139             our $COLOR_SPACE = sprintf( q'%s%s', $COLOR, $SPACE );
140             our $FOUR_NOTATION_COLOR_RE = risprintf( q'(-color%s:%s)%s%s%s(%s)',
141             $WHITESPACE,
142             $WHITESPACE,
143             $COLOR_SPACE,
144             $COLOR_SPACE,
145             $COLOR_SPACE,
146             $COLOR );
147              
148             # border-radius is very different from usual 4 part notation: ABCD should
149             # change to BADC (while it would be ADCB in normal 4 part notation), ABC
150             # should change to BABC, and AB should change to BA
151             our $BORDER_RADIUS_RE = risprintf( q'((?:%s)?)border-radius(%s:%s)' .
152             '(?:%s)?(?:%s)?(?:%s)?(?:%s)' .
153             '(?:%s/%s(?:%s)?(?:%s)?(?:%s)?(?:%s))?', $IDENT,
154             $WHITESPACE,
155             $WHITESPACE,
156             $POSSIBLY_NEGATIVE_QUANTITY_SPACE,
157             $POSSIBLY_NEGATIVE_QUANTITY_SPACE,
158             $POSSIBLY_NEGATIVE_QUANTITY_SPACE,
159             $POSSIBLY_NEGATIVE_QUANTITY,
160             $WHITESPACE,
161             $WHITESPACE,
162             $POSSIBLY_NEGATIVE_QUANTITY_SPACE,
163             $POSSIBLY_NEGATIVE_QUANTITY_SPACE,
164             $POSSIBLY_NEGATIVE_QUANTITY_SPACE,
165             $POSSIBLY_NEGATIVE_QUANTITY );
166              
167              
168              
169             # Compile the cursor resize regexes
170             our $CURSOR_EAST_RE = resprintf( $LOOKBEHIND_NOT_LETTER . '([ns]?)e-resize' );
171             our $CURSOR_WEST_RE = resprintf( $LOOKBEHIND_NOT_LETTER . '([ns]?)w-resize' );
172              
173             # Matches the condition where we need to replace the horizontal component
174             # of a background-position value when expressed in horizontal percentage.
175             # Had to make two regexes because in the case of position-x there is only
176             # one quantity, and otherwise we don't want to match and change cases with only
177             # one quantity.
178             our $BG_HORIZONTAL_PERCENTAGE_RE = resprintf( q'background(-position)?(%s:%s)' .
179             q'([^%%]*?)(%s)%%' .
180             q'(%s(?:%s|top|center|bottom))', $WHITESPACE,
181             $WHITESPACE,
182             $NUM,
183             $WHITESPACE,
184             $POSSIBLY_NEGATIVE_QUANTITY );
185              
186             our $BG_HORIZONTAL_PERCENTAGE_X_RE = resprintf( q'background-position-x(%s:%s)' .
187             q'(%s)%%', $WHITESPACE,
188             $WHITESPACE,
189             $NUM );
190             # Non-percentage units used for CSS lengths
191             our $LENGTH_UNIT = q'(?:em|ex|px|cm|mm|in|pt|pc)';
192             # To make sure the lone 0 is not just starting a number (like "02") or a percentage like ("0 %")
193             our $LOOKAHEAD_END_OF_ZERO = sprintf( '(?![0-9]|%s%%)', $WHITESPACE );
194             # A length with a unit specified. Matches "0" too, as it's a length, not a percentage.
195             our $LENGTH = sprintf( '(?:-?%s(?:%s%s)|0+%s)', $NUM,
196             $WHITESPACE,
197             $LENGTH_UNIT,
198             $LOOKAHEAD_END_OF_ZERO );
199              
200             # Zero length. Used in the replacement functions.
201             our $ZERO_LENGTH = resprintf( q'(?:-?0+(?:%s%s)|0+%s)$', $WHITESPACE,
202             $LENGTH_UNIT,
203             $LOOKAHEAD_END_OF_ZERO );
204              
205             # Matches background, background-position, and background-position-x
206             # properties when using a CSS length for its horizontal positioning.
207             our $BG_HORIZONTAL_LENGTH_RE = resprintf( q'background(-position)?(%s:%s)' .
208             q'((?:.+?%s+)??)(%s)' .
209             q'((?:%s+)(?:%s|top|center|bottom))', $WHITESPACE,
210             $WHITESPACE,
211             $SPACE,
212             $LENGTH,
213             $SPACE,
214             $POSSIBLY_NEGATIVE_QUANTITY );
215              
216             our $BG_HORIZONTAL_LENGTH_X_RE = resprintf( q'background-position-x(%s:%s)' .
217             q'(%s)', $WHITESPACE,
218             $WHITESPACE,
219             $LENGTH );
220              
221             # Matches the opening of a body selector.
222             our $BODY_SELECTOR = sprintf( q'body%s{%s', $WHITESPACE, $WHITESPACE );
223              
224             # Matches anything up until the closing of a selector.
225             our $CHARS_WITHIN_SELECTOR = q'[^\}]*?';
226              
227             # Matches the direction property in a selector.
228             our $DIRECTION_RE = sprintf( q'direction%s:%s', $WHITESPACE, $WHITESPACE );
229              
230              
231              
232             sub resprintf {
233 120     120 0 182 my $fmt = shift;
234 120         345 my $ret = sprintf( $fmt, @_ );
235              
236 120         6513 return qr/$ret/;
237             }
238              
239             sub risprintf {
240 180     180 0 261 my $fmt = shift;
241 180         712 my $ret = sprintf( $fmt, @_ );
242              
243 180         61699 return qr/$ret/i;
244             }
245              
246             # These allow us to swap "ltr" with "rtl" and vice versa ONLY within the
247             # body selector and on the same line.
248             our $BODY_DIRECTION_LTR_RE = risprintf( q'(%s)(%s)(%s)(ltr)',
249             $BODY_SELECTOR, $CHARS_WITHIN_SELECTOR,
250             $DIRECTION_RE );
251             our $BODY_DIRECTION_RTL_RE = risprintf( q'(%s)(%s)(%s)(rtl)',
252             $BODY_SELECTOR, $CHARS_WITHIN_SELECTOR,
253             $DIRECTION_RE );
254              
255             # Allows us to swap "direction:ltr" with "direction:rtl" and
256             # vice versa anywhere in a line.
257             our $DIRECTION_LTR_RE = resprintf( q'%s(ltr)', $DIRECTION_RE );
258             our $DIRECTION_RTL_RE = resprintf( q'%s(rtl)', $DIRECTION_RE );
259              
260             # We want to be able to switch left with right and vice versa anywhere
261             # we encounter left/right strings, EXCEPT inside the background:url(). The next
262             # two regexes are for that purpose. We have alternate IN_URL versions of the
263             # regexes compiled in case the user passes the flag that they do
264             # actually want to have left and right swapped inside of background:urls.
265             our $LEFT_RE = risprintf( '%s((?:top|bottom)?)(%s)%s%s', $LOOKBEHIND_NOT_LETTER,
266             $LEFT,
267             $LOOKAHEAD_NOT_CLOSING_PAREN,
268             $LOOKAHEAD_NOT_OPEN_BRACE );
269             our $RIGHT_RE = risprintf( '%s((?:top|bottom)?)(%s)%s%s', $LOOKBEHIND_NOT_LETTER,
270             $RIGHT,
271             $LOOKAHEAD_NOT_CLOSING_PAREN,
272             $LOOKAHEAD_NOT_OPEN_BRACE );
273             our $LEFT_IN_URL_RE = risprintf( '%s(%s)%s', $LOOKBEHIND_NOT_LETTER,
274             $LEFT,
275             $LOOKAHEAD_FOR_CLOSING_PAREN );
276             our $RIGHT_IN_URL_RE = risprintf( '%s(%s)%s', $LOOKBEHIND_NOT_LETTER,
277             $RIGHT,
278             $LOOKAHEAD_FOR_CLOSING_PAREN );
279             our $LTR_IN_URL_RE = risprintf( '%s(%s)%s', $LOOKBEHIND_NOT_LETTER,
280             $LTR,
281             $LOOKAHEAD_FOR_CLOSING_PAREN );
282             our $RTL_IN_URL_RE = risprintf( '%s(%s)%s', $LOOKBEHIND_NOT_LETTER,
283             $RTL,
284             $LOOKAHEAD_FOR_CLOSING_PAREN );
285              
286             our $COMMENT_RE = risprintf( '(%s)', $COMMENT );
287              
288             our $NOFLIP_TOKEN = q'@noflip';
289             # The NOFLIP_TOKEN inside of a comment. For now, this requires that comments
290             # be in the input, which means users of a css compiler would have to run
291             # this script first if they want this functionality.
292             our $NOFLIP_ANNOTATION = resprintf( q'/\*!?%s%s%s\*/', $WHITESPACE,
293             $NOFLIP_TOKEN,
294             $WHITESPACE );
295              
296             # After a NOFLIP_ANNOTATION, and within a class selector, we want to be able
297             # to set aside a single rule not to be flipped. We can do this by matching
298             # our NOFLIP annotation and then using a lookahead to make sure there is not
299             # an opening brace before the match.
300             our $NOFLIP_SINGLE_RE = risprintf( q'(%s%s[^;}]+;?)', $NOFLIP_ANNOTATION,
301             $LOOKAHEAD_NOT_OPEN_BRACE );
302              
303             # After a NOFLIP_ANNOTATION, we want to grab anything up until the next } which
304             # means the entire following class block. This will prevent all of its
305             # declarations from being flipped.
306             our $NOFLIP_CLASS_RE = risprintf( q'(%s%s})', $NOFLIP_ANNOTATION,
307             $CHARS_WITHIN_SELECTOR );
308              
309             # border-radis properties and their values
310             our $BORDER_RADIUS_TOKENIZER_RE = risprintf( q'((?:%s)?border-radius%s:[^;}]+;?)', $IDENT,
311             $WHITESPACE );
312              
313             our $GRADIENT_RE = qr/ (
314             $CSS::Orientation::IDENT
315             [\.-] gradient
316             $CSS::Orientation::WHITESPACE
317             \( (?: (?>[^()]+) | \([^()]*\) )+ \)
318             ) /ix;
319              
320             sub FixBodyDirectionLtrAndRtl {
321 103     103 0 121 my ( $line ) = @_;
322              
323 103         323 $line =~ s!$BODY_DIRECTION_LTR_RE!$1$2$3$TMP_TOKEN!gms;
324 103         292 $line =~ s!$BODY_DIRECTION_RTL_RE!$1$2$3$LTR!gms;
325 103         209 $line =~ s!$TMP_TOKEN!$RTL!gms;
326              
327 103         174 return $line;
328             }
329              
330             sub FixLeftAndRight {
331 103     103 0 125 my ( $line ) = @_;
332              
333 103         1590 $line =~ s!$LEFT_RE!$1$TMP_TOKEN!gms;
334 103         1781 $line =~ s!$RIGHT_RE!$1$LEFT!gms;
335 103         302 $line =~ s!$TMP_TOKEN!$RIGHT!gms;
336              
337 103         275 return $line;
338             }
339              
340             sub FixLeftAndRightInUrl {
341 12     12 0 23 my ( $line ) = @_;
342              
343 12         80 $line =~ s!$LEFT_IN_URL_RE!$TMP_TOKEN!gms;
344 12         63 $line =~ s!$RIGHT_IN_URL_RE!$LEFT!gms;
345 12         45 $line =~ s!$TMP_TOKEN!$RIGHT!gms;
346              
347 12         35 return $line;
348             }
349              
350             sub FixLtrAndRtlInUrl {
351 12     12 0 26 my ( $line ) = @_;
352              
353 12         74 $line =~ s!$LTR_IN_URL_RE!$TMP_TOKEN!gms;
354 12         75 $line =~ s!$RTL_IN_URL_RE!$LTR!gms;
355 12         55 $line =~ s!$TMP_TOKEN!$RTL!gms;
356              
357 12         28 return $line;
358             }
359              
360             sub FixCursorProperties {
361 103     103 0 121 my ( $line ) = @_;
362              
363 103         396 $line =~ s!$CURSOR_EAST_RE!$1$TMP_TOKEN!gms;
364 103         263 $line =~ s!$CURSOR_WEST_RE!${1}e-resize!gms;
365 103         241 $line =~ s!$TMP_TOKEN!w-resize!gms;
366              
367 103         248 return $line;
368             }
369              
370             sub FixBackgroundPosition {
371 110     110 0 155 my ( $line, $ignore_bad_bgp ) = @_;
372              
373             # leave full match undef where not needed
374 110         728 $line =~ s!$BG_HORIZONTAL_PERCENTAGE_RE!CalculateNewBackgroundPosition(undef,$1,$2,$3,$4,$5,$6)!egms;
  13         66  
375 110         381 $line =~ s!$BG_HORIZONTAL_PERCENTAGE_X_RE!CalculateNewBackgroundPositionX(undef,$1,$2)!egms;
  2         9  
376              
377 110         7074 $line =~ s!($BG_HORIZONTAL_LENGTH_RE)!
378 1 50       6 defined( $_ = CalculateNewBackgroundLengthPosition( $1, $2, $3, $4, $5, $6, $7, $ignore_bad_bgp ) )
379             ? $_
380             : return undef
381             !egmsx;
382              
383 110         447 $line =~ s!($BG_HORIZONTAL_LENGTH_X_RE)!
384 1 50       6 defined( $_ = CalculateNewBackgroundLengthPositionX( $1, $2, $3, $ignore_bad_bgp ) )
385             ? $_
386             : return undef
387             !egmsx;
388              
389 110         314 return $line;
390             }
391              
392             sub ReorderBorderRadiusPart {
393 16     16 0 2096 my @part = grep defined, @_;
394              
395 16 100       63 if ( @part == 4 ) {
    100          
    100          
    100          
396 4         25 return join( ' ', @part[ 1, 0, 3, 2 ] );
397             }
398             elsif ( @part == 3 ) {
399 4         20 return join( ' ', @part[ 1, 0, 1, 2 ] );
400             }
401             elsif ( @part == 2 ) {
402 2         10 return join( ' ', @part[ 1, 0, ] );
403             }
404             elsif ( @part == 1 ) {
405 2         7 return $part[ 0 ];
406             }
407             else {
408 4         9 return '';
409             }
410             }
411              
412             sub ReorderBorderRadius {
413 6     6 0 51 my @m = @_;
414              
415 6         24 my $first_group = ReorderBorderRadiusPart( @m[ 3 .. 6 ] );
416 6         24 my $second_group = ReorderBorderRadiusPart( @m[ 7 .. $#m ] );
417              
418 6 100       16 if ( $second_group eq '' ) {
419 4         22 return sprintf( '%sborder-radius%s%s', $m[1], $m[2], $first_group );
420             }
421             else {
422 2         25 return sprintf( '%sborder-radius%s%s / %s', $m[1], $m[2], $first_group, $second_group );
423             }
424             }
425              
426             sub CalculateNewBackgroundPosition {
427 13     13 0 60 my @m = @_;
428              
429 13         30 my $new_x = 100 - $m[4];
430 13 100       27 my $position_string = defined( $m[1] ) ? $m[1] : '';
431              
432 13         97 return sprintf( 'background%s%s%s%s%%%s', $position_string, $m[2], $m[3], $new_x, $m[5] );
433             }
434              
435             sub CalculateNewBackgroundPositionX {
436 2     2 0 9 my @m = @_;
437              
438 2         4 my $new_x = 100 - $m[2];
439              
440 2         17 return sprintf( 'background-position-x%s%s%%', $m[1], $new_x );
441             }
442              
443             sub CalculateNewBackgroundLengthPosition {
444 1 50   1 0 6 my $ignore_bad_bgp = @_ > 7 ? pop( @_ ) : 0;
445 1         10 my @m = @_;
446              
447 1 50       10 unless ( $m[4] =~ $ZERO_LENGTH ) {
448 0         0 warn( "Unmirrorable horizontal value $m[4]: $m[0]" );
449 0 0       0 return $ignore_bad_bgp ? $m[0] : undef;
450             }
451              
452 1 50       6 my $position_string = defined( $m[1] ) ? $m[1] : '';
453              
454 1         17 return sprintf( 'background%s%s%s100%%%s', $position_string, $m[2], $m[3], $m[5] );
455              
456             }
457              
458             sub CalculateNewBackgroundLengthPositionX {
459 1 50   1 0 5 my $ignore_bad_bgp = @_ > 3 ? pop( @_ ) : 0;
460 1         6 my @m = @_;
461              
462 1 50       8 unless ( $m[2] =~ $ZERO_LENGTH ) {
463 0         0 warn( "Unmirrorable horizontal value $m[2]: $m[0]" );
464 0 0       0 return $ignore_bad_bgp ? $m[0] : undef;
465             }
466              
467 1         17 return sprintf( 'background-position-x%s100%%', $m[1] );
468             }
469              
470             sub FixBorderRadius {
471 103     103 0 160 my ( $line ) = @_;
472              
473             # full match not needed, leave undef
474 103         3097 $line =~ s!$BORDER_RADIUS_RE!ReorderBorderRadius(undef,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10)!egms;
  5         20  
475              
476 103         173 return $line;
477             }
478              
479             sub FixFourPartNotation {
480 103     103 0 255 my $line = shift;
481              
482 103         2476 $line =~ s!$FOUR_NOTATION_QUANTITY_RE!$1 $4 $3 $2!g;
483 103         380 $line =~ s!$FOUR_NOTATION_COLOR_RE!$1$2 $5 $4 $3!g;
484              
485 103         200 return $line;
486             }
487              
488             sub ChangeLeftToRightToLeft {
489 102     102 1 231 my ( $lines, $swap_ltr_rtl_in_url, $swap_left_right_in_url, $ignore_bad_bgp ) = @_;
490              
491 102 50       334 my $line = join( $TOKEN_LINES, ref( $lines ) ? @$lines : $lines );
492              
493             # Tokenize any single line rules with the /* noflip */ annotation.
494 102         271 my $noflip_single_tokenizer = CSS::Orientation::Tokenizer->new( $NOFLIP_SINGLE_RE, 'NOFLIP_SINGLE' );
495 102         209 $line = $noflip_single_tokenizer->tokenize( $line );
496              
497             # Tokenize any class rules with the /* noflip */ annotation.
498 102         257 my $noflip_class_tokenizer = CSS::Orientation::Tokenizer->new( $NOFLIP_CLASS_RE, 'NOFLIP_CLASS' );
499 102         230 $line = $noflip_class_tokenizer->tokenize( $line );
500              
501             # Tokenize the comments so we can preserve them through the changes.
502 102         259 my $comment_tokenizer = CSS::Orientation::Tokenizer->new( $COMMENT_RE, 'C' );
503 102         189 $line = $comment_tokenizer->tokenize( $line );
504              
505             # Tokenize gradients since we don't want to mirror the values inside
506 102         245 my $gradient_tokenizer = CSS::Orientation::Tokenizer->new( $GRADIENT_RE, 'GRADIENT' );
507 102         174 $line = $gradient_tokenizer->tokenize( $line );
508              
509             # Here starteth the various left/right orientation fixes.
510 102         354 $line = FixBodyDirectionLtrAndRtl( $line );
511              
512 102 100       236 if ( $swap_left_right_in_url ) {
513 11         23 $line = FixLeftAndRightInUrl( $line );
514             }
515              
516 102 100       518 if ( $swap_ltr_rtl_in_url ) {
517 11         19 $line = FixLtrAndRtlInUrl( $line );
518             }
519              
520 102         177 $line = FixLeftAndRight( $line );
521 102         195 $line = FixCursorProperties( $line );
522              
523 102         266 $line = FixBorderRadius( $line );
524             # Since FourPartNotation conflicts with BorderRadius, we tokenize border-radius properties here.
525 102         252 my $border_radius_tokenizer = CSS::Orientation::Tokenizer->new( $BORDER_RADIUS_TOKENIZER_RE, 'BORDER_RADIUS' );
526 102         194 $line = $border_radius_tokenizer->tokenize( $line );
527 102         184 $line = FixFourPartNotation( $line );
528 102         251 $line = $border_radius_tokenizer->detokenize( $line );
529              
530 102         193 $line = FixBackgroundPosition( $line, $ignore_bad_bgp );
531              
532 102 50       300 unless ( defined( $line ) ) {
533 0         0 return undef;
534             }
535              
536 102         199 $line = $gradient_tokenizer->detokenize( $line );
537              
538             # DeTokenize the single line noflips.
539 102         217 $line = $noflip_single_tokenizer->detokenize( $line );
540              
541             # DeTokenize the class-level noflips.
542 102         191 $line = $noflip_class_tokenizer->detokenize( $line );
543              
544             # DeTokenize the comments.
545 102         188 $line = $comment_tokenizer->detokenize( $line );
546              
547             # Rejoin the lines back together.
548 102         857 my @lines = split( $TOKEN_LINES, $line );
549              
550 102 50       964 return ref( $lines ) ? \@lines : $lines[0];
551             }
552              
553             sub change {
554 0     0 1 0 shift;
555 0         0 ChangeLeftToRightToLeft( @_ );
556             }
557              
558             1;
559              
560             package CSS::Orientation::Tokenizer;
561              
562 12     12   128 use strict;
  12         23  
  12         528  
563 12     12   64 use warnings;
  12         21  
  12         4526  
564              
565             sub new {
566 510     510   779 my ( $class, $re, $string ) = @_;
567 510         2238 my $self = bless( {
568             're' => $re,
569             'string' => $string,
570             'originals' => [],
571             }, $class );
572              
573 510         911 return $self;
574             }
575              
576             sub tokenize {
577 510     510   691 my ( $self, $line ) = @_;
578              
579 510         5065 $line =~ s!$self->{re}!
580 23         109 $CSS::Orientation::TOKEN_DELIMITER .
581             $self->{string} . '_' .
582 23         48 push( @{ $self->{originals} }, $1 ) .
583             $CSS::Orientation::TOKEN_DELIMITER
584             !egx;
585              
586 510         976 return $line;
587             }
588              
589             sub detokenize {
590 510     510   742 my ( $self, $line ) = @_;
591              
592 510         7474 $line =~ s!
593             $CSS::Orientation::TOKEN_DELIMITER
594             $self->{string} _
595             ([0-9]+)
596             $CSS::Orientation::TOKEN_DELIMITER
597             !
598 23 50 33     80 $1 > 0 && $1 <= @{ $self->{originals} } ? $self->{originals}[$1-1] : ''
599             !egx;
600              
601 510         1261 return $line;
602             }
603              
604             1;
605              
606             __END__