Index: trunk/phase3/maintenance/cssjanus/cssjanus.py |
— | — | @@ -0,0 +1,574 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +# |
| 4 | +# Copyright 2008 Google Inc. All Rights Reserved. |
| 5 | + |
| 6 | +"""Converts a LeftToRight Cascading Style Sheet into a RightToLeft one. |
| 7 | + |
| 8 | + This is a utility script for replacing "left" oriented things in a CSS file |
| 9 | + like float, padding, margin with "right" oriented values. |
| 10 | + It also does the opposite. |
| 11 | + The goal is to be able to conditionally serve one large, cat'd, compiled CSS |
| 12 | + file appropriate for LeftToRight oriented languages and RightToLeft ones. |
| 13 | + This utility will hopefully help your structural layout done in CSS in |
| 14 | + terms of its RTL compatibility. It will not help with some of the more |
| 15 | + complicated bidirectional text issues. |
| 16 | +""" |
| 17 | + |
| 18 | +__author__ = 'elsigh@google.com (Lindsey Simon)' |
| 19 | +__version__ = '0.1' |
| 20 | + |
| 21 | +import logging |
| 22 | +import re |
| 23 | +import sys |
| 24 | +import getopt |
| 25 | +import os |
| 26 | + |
| 27 | +import csslex |
| 28 | + |
| 29 | +logging.getLogger().setLevel(logging.INFO) |
| 30 | + |
| 31 | +# Global for the command line flags. |
| 32 | +SWAP_LTR_RTL_IN_URL_DEFAULT = False |
| 33 | +SWAP_LEFT_RIGHT_IN_URL_DEFAULT = False |
| 34 | +FLAGS = {'swap_ltr_rtl_in_url': SWAP_LTR_RTL_IN_URL_DEFAULT, |
| 35 | + 'swap_left_right_in_url': SWAP_LEFT_RIGHT_IN_URL_DEFAULT} |
| 36 | + |
| 37 | +# Generic token delimiter character. |
| 38 | +TOKEN_DELIMITER = '~' |
| 39 | + |
| 40 | +# This is a temporary match token we use when swapping strings. |
| 41 | +TMP_TOKEN = '%sTMP%s' % (TOKEN_DELIMITER, TOKEN_DELIMITER) |
| 42 | + |
| 43 | +# Token to be used for joining lines. |
| 44 | +TOKEN_LINES = '%sJ%s' % (TOKEN_DELIMITER, TOKEN_DELIMITER) |
| 45 | + |
| 46 | +# Global constant text strings for CSS value matches. |
| 47 | +LTR = 'ltr' |
| 48 | +RTL = 'rtl' |
| 49 | +LEFT = 'left' |
| 50 | +RIGHT = 'right' |
| 51 | + |
| 52 | +# This is a lookbehind match to ensure that we don't replace instances |
| 53 | +# of our string token (left, rtl, etc...) if there's a letter in front of it. |
| 54 | +# Specifically, this prevents replacements like 'background: url(bright.png)'. |
| 55 | +LOOKBEHIND_NOT_LETTER = r'(?<![a-zA-Z])' |
| 56 | + |
| 57 | +# This is a lookahead match to make sure we don't replace left and right |
| 58 | +# in actual classnames, so that we don't break the HTML/CSS dependencies. |
| 59 | +# Read literally, it says ignore cases where the word left, for instance, is |
| 60 | +# directly followed by valid classname characters and a curly brace. |
| 61 | +# ex: .column-left {float: left} will become .column-left {float: right} |
| 62 | +LOOKAHEAD_NOT_OPEN_BRACE = (r'(?!(?:%s|%s|%s|#|\:|\.|\,|\+|>)*?{)' % |
| 63 | + (csslex.NMCHAR, TOKEN_LINES, csslex.SPACE)) |
| 64 | + |
| 65 | + |
| 66 | +# These two lookaheads are to test whether or not we are within a |
| 67 | +# background: url(HERE) situation. |
| 68 | +# Ref: http://www.w3.org/TR/CSS21/syndata.html#uri |
| 69 | +VALID_AFTER_URI_CHARS = r'[\'\"]?%s' % csslex.WHITESPACE |
| 70 | +LOOKAHEAD_NOT_CLOSING_PAREN = r'(?!%s?%s\))' % (csslex.URL_CHARS, |
| 71 | + VALID_AFTER_URI_CHARS) |
| 72 | +LOOKAHEAD_FOR_CLOSING_PAREN = r'(?=%s?%s\))' % (csslex.URL_CHARS, |
| 73 | + VALID_AFTER_URI_CHARS) |
| 74 | + |
| 75 | +# Compile a regex to swap left and right values in 4 part notations. |
| 76 | +# We need to match negatives and decimal numeric values. |
| 77 | +# ex. 'margin: .25em -2px 3px 0' becomes 'margin: .25em 0 3px -2px'. |
| 78 | +POSSIBLY_NEGATIVE_QUANTITY = r'((?:-?%s)|(?:inherit|auto))' % csslex.QUANTITY |
| 79 | +POSSIBLY_NEGATIVE_QUANTITY_SPACE = r'%s%s%s' % (POSSIBLY_NEGATIVE_QUANTITY, |
| 80 | + csslex.SPACE, |
| 81 | + csslex.WHITESPACE) |
| 82 | +FOUR_NOTATION_QUANTITY_RE = re.compile(r'%s%s%s%s' % |
| 83 | + (POSSIBLY_NEGATIVE_QUANTITY_SPACE, |
| 84 | + POSSIBLY_NEGATIVE_QUANTITY_SPACE, |
| 85 | + POSSIBLY_NEGATIVE_QUANTITY_SPACE, |
| 86 | + POSSIBLY_NEGATIVE_QUANTITY), |
| 87 | + re.I) |
| 88 | +COLOR = r'(%s|%s)' % (csslex.NAME, csslex.HASH) |
| 89 | +COLOR_SPACE = r'%s%s' % (COLOR, csslex.SPACE) |
| 90 | +FOUR_NOTATION_COLOR_RE = re.compile(r'(-color%s:%s)%s%s%s(%s)' % |
| 91 | + (csslex.WHITESPACE, |
| 92 | + csslex.WHITESPACE, |
| 93 | + COLOR_SPACE, |
| 94 | + COLOR_SPACE, |
| 95 | + COLOR_SPACE, |
| 96 | + COLOR), |
| 97 | + re.I) |
| 98 | + |
| 99 | +# Compile the cursor resize regexes |
| 100 | +CURSOR_EAST_RE = re.compile(LOOKBEHIND_NOT_LETTER + '([ns]?)e-resize') |
| 101 | +CURSOR_WEST_RE = re.compile(LOOKBEHIND_NOT_LETTER + '([ns]?)w-resize') |
| 102 | + |
| 103 | +# Matches the condition where we need to replace the horizontal component |
| 104 | +# of a background-position value when expressed in horizontal percentage. |
| 105 | +# Had to make two regexes because in the case of position-x there is only |
| 106 | +# one quantity, and otherwise we don't want to match and change cases with only |
| 107 | +# one quantity. |
| 108 | +BG_HORIZONTAL_PERCENTAGE_RE = re.compile(r'background(-position)?(%s:%s)' |
| 109 | + '([^%%]*?)(%s)%%' |
| 110 | + '(%s(?:%s|%s))' % (csslex.WHITESPACE, |
| 111 | + csslex.WHITESPACE, |
| 112 | + csslex.NUM, |
| 113 | + csslex.WHITESPACE, |
| 114 | + csslex.QUANTITY, |
| 115 | + csslex.IDENT)) |
| 116 | + |
| 117 | +BG_HORIZONTAL_PERCENTAGE_X_RE = re.compile(r'background-position-x(%s:%s)' |
| 118 | + '(%s)%%' % (csslex.WHITESPACE, |
| 119 | + csslex.WHITESPACE, |
| 120 | + csslex.NUM)) |
| 121 | + |
| 122 | +# Matches the opening of a body selector. |
| 123 | +BODY_SELECTOR = r'body%s{%s' % (csslex.WHITESPACE, csslex.WHITESPACE) |
| 124 | + |
| 125 | +# Matches anything up until the closing of a selector. |
| 126 | +CHARS_WITHIN_SELECTOR = r'[^\}]*?' |
| 127 | + |
| 128 | +# Matches the direction property in a selector. |
| 129 | +DIRECTION_RE = r'direction%s:%s' % (csslex.WHITESPACE, csslex.WHITESPACE) |
| 130 | + |
| 131 | +# These allow us to swap "ltr" with "rtl" and vice versa ONLY within the |
| 132 | +# body selector and on the same line. |
| 133 | +BODY_DIRECTION_LTR_RE = re.compile(r'(%s)(%s)(%s)(ltr)' % |
| 134 | + (BODY_SELECTOR, CHARS_WITHIN_SELECTOR, |
| 135 | + DIRECTION_RE), |
| 136 | + re.I) |
| 137 | +BODY_DIRECTION_RTL_RE = re.compile(r'(%s)(%s)(%s)(rtl)' % |
| 138 | + (BODY_SELECTOR, CHARS_WITHIN_SELECTOR, |
| 139 | + DIRECTION_RE), |
| 140 | + re.I) |
| 141 | + |
| 142 | + |
| 143 | +# Allows us to swap "direction:ltr" with "direction:rtl" and |
| 144 | +# vice versa anywhere in a line. |
| 145 | +DIRECTION_LTR_RE = re.compile(r'%s(ltr)' % DIRECTION_RE) |
| 146 | +DIRECTION_RTL_RE = re.compile(r'%s(rtl)' % DIRECTION_RE) |
| 147 | + |
| 148 | +# We want to be able to switch left with right and vice versa anywhere |
| 149 | +# we encounter left/right strings, EXCEPT inside the background:url(). The next |
| 150 | +# two regexes are for that purpose. We have alternate IN_URL versions of the |
| 151 | +# regexes compiled in case the user passes the flag that they do |
| 152 | +# actually want to have left and right swapped inside of background:urls. |
| 153 | +LEFT_RE = re.compile('%s(%s)%s%s' % (LOOKBEHIND_NOT_LETTER, |
| 154 | + LEFT, |
| 155 | + LOOKAHEAD_NOT_CLOSING_PAREN, |
| 156 | + LOOKAHEAD_NOT_OPEN_BRACE), |
| 157 | + re.I) |
| 158 | +RIGHT_RE = re.compile('%s(%s)%s%s' % (LOOKBEHIND_NOT_LETTER, |
| 159 | + RIGHT, |
| 160 | + LOOKAHEAD_NOT_CLOSING_PAREN, |
| 161 | + LOOKAHEAD_NOT_OPEN_BRACE), |
| 162 | + re.I) |
| 163 | +LEFT_IN_URL_RE = re.compile('%s(%s)%s' % (LOOKBEHIND_NOT_LETTER, |
| 164 | + LEFT, |
| 165 | + LOOKAHEAD_FOR_CLOSING_PAREN), |
| 166 | + re.I) |
| 167 | +RIGHT_IN_URL_RE = re.compile('%s(%s)%s' % (LOOKBEHIND_NOT_LETTER, |
| 168 | + RIGHT, |
| 169 | + LOOKAHEAD_FOR_CLOSING_PAREN), |
| 170 | + re.I) |
| 171 | +LTR_IN_URL_RE = re.compile('%s(%s)%s' % (LOOKBEHIND_NOT_LETTER, |
| 172 | + LTR, |
| 173 | + LOOKAHEAD_FOR_CLOSING_PAREN), |
| 174 | + re.I) |
| 175 | +RTL_IN_URL_RE = re.compile('%s(%s)%s' % (LOOKBEHIND_NOT_LETTER, |
| 176 | + RTL, |
| 177 | + LOOKAHEAD_FOR_CLOSING_PAREN), |
| 178 | + re.I) |
| 179 | + |
| 180 | +COMMENT_RE = re.compile('(%s)' % csslex.COMMENT, re.I) |
| 181 | + |
| 182 | +NOFLIP_TOKEN = r'\@noflip' |
| 183 | +# The NOFLIP_TOKEN inside of a comment. For now, this requires that comments |
| 184 | +# be in the input, which means users of a css compiler would have to run |
| 185 | +# this script first if they want this functionality. |
| 186 | +NOFLIP_ANNOTATION = r'/\*%s%s%s\*/' % (csslex.WHITESPACE, |
| 187 | + NOFLIP_TOKEN, |
| 188 | + csslex. WHITESPACE) |
| 189 | + |
| 190 | +# After a NOFLIP_ANNOTATION, and within a class selector, we want to be able |
| 191 | +# to set aside a single rule not to be flipped. We can do this by matching |
| 192 | +# our NOFLIP annotation and then using a lookahead to make sure there is not |
| 193 | +# an opening brace before the match. |
| 194 | +NOFLIP_SINGLE_RE = re.compile(r'(%s%s[^;}]+;?)' % (NOFLIP_ANNOTATION, |
| 195 | + LOOKAHEAD_NOT_OPEN_BRACE), |
| 196 | + re.I) |
| 197 | + |
| 198 | +# After a NOFLIP_ANNOTATION, we want to grab anything up until the next } which |
| 199 | +# means the entire following class block. This will prevent all of its |
| 200 | +# declarations from being flipped. |
| 201 | +NOFLIP_CLASS_RE = re.compile(r'(%s%s})' % (NOFLIP_ANNOTATION, |
| 202 | + CHARS_WITHIN_SELECTOR), |
| 203 | + re.I) |
| 204 | + |
| 205 | + |
| 206 | +class Tokenizer: |
| 207 | + """Replaces any CSS comments with string tokens and vice versa.""" |
| 208 | + |
| 209 | + def __init__(self, token_re, token_string): |
| 210 | + """Constructor for the Tokenizer. |
| 211 | + |
| 212 | + Args: |
| 213 | + token_re: A regex for the string to be replace by a token. |
| 214 | + token_string: The string to put between token delimiters when tokenizing. |
| 215 | + """ |
| 216 | + logging.debug('Tokenizer::init token_string=%s' % token_string) |
| 217 | + self.token_re = token_re |
| 218 | + self.token_string = token_string |
| 219 | + self.originals = [] |
| 220 | + |
| 221 | + def Tokenize(self, line): |
| 222 | + """Replaces any string matching token_re in line with string tokens. |
| 223 | + |
| 224 | + By passing a function as an argument to the re.sub line below, we bypass |
| 225 | + the usual rule where re.sub will only replace the left-most occurrence of |
| 226 | + a match by calling the passed in function for each occurrence. |
| 227 | + |
| 228 | + Args: |
| 229 | + line: A line to replace token_re matches in. |
| 230 | + |
| 231 | + Returns: |
| 232 | + line: A line with token_re matches tokenized. |
| 233 | + """ |
| 234 | + line = self.token_re.sub(self.TokenizeMatches, line) |
| 235 | + logging.debug('Tokenizer::Tokenize returns: %s' % line) |
| 236 | + return line |
| 237 | + |
| 238 | + def DeTokenize(self, line): |
| 239 | + """Replaces tokens with the original string. |
| 240 | + |
| 241 | + Args: |
| 242 | + line: A line with tokens. |
| 243 | + |
| 244 | + Returns: |
| 245 | + line with any tokens replaced by the original string. |
| 246 | + """ |
| 247 | + |
| 248 | + # Put all of the comments back in by their comment token. |
| 249 | + for i, original in enumerate(self.originals): |
| 250 | + token = '%s%s_%s%s' % (TOKEN_DELIMITER, self.token_string, i + 1, |
| 251 | + TOKEN_DELIMITER) |
| 252 | + line = line.replace(token, original) |
| 253 | + logging.debug('Tokenizer::DeTokenize i:%s w/%s' % (i, token)) |
| 254 | + logging.debug('Tokenizer::DeTokenize returns: %s' % line) |
| 255 | + return line |
| 256 | + |
| 257 | + def TokenizeMatches(self, m): |
| 258 | + """Replaces matches with tokens and stores the originals. |
| 259 | + |
| 260 | + Args: |
| 261 | + m: A match object. |
| 262 | + |
| 263 | + Returns: |
| 264 | + A string token which replaces the CSS comment. |
| 265 | + """ |
| 266 | + logging.debug('Tokenizer::TokenizeMatches %s' % m.group(1)) |
| 267 | + self.originals.append(m.group(1)) |
| 268 | + return '%s%s_%s%s' % (TOKEN_DELIMITER, |
| 269 | + self.token_string, |
| 270 | + len(self.originals), |
| 271 | + TOKEN_DELIMITER) |
| 272 | + |
| 273 | + |
| 274 | +def FixBodyDirectionLtrAndRtl(line): |
| 275 | + """Replaces ltr with rtl and vice versa ONLY in the body direction. |
| 276 | + |
| 277 | + Args: |
| 278 | + line: A string to replace instances of ltr with rtl. |
| 279 | + Returns: |
| 280 | + line with direction: ltr and direction: rtl swapped only in body selector. |
| 281 | + line = FixBodyDirectionLtrAndRtl('body { direction:ltr }') |
| 282 | + line will now be 'body { direction:rtl }'. |
| 283 | + """ |
| 284 | + |
| 285 | + line = BODY_DIRECTION_LTR_RE.sub('\\1\\2\\3%s' % TMP_TOKEN, line) |
| 286 | + line = BODY_DIRECTION_RTL_RE.sub('\\1\\2\\3%s' % LTR, line) |
| 287 | + line = line.replace(TMP_TOKEN, RTL) |
| 288 | + logging.debug('FixBodyDirectionLtrAndRtl returns: %s' % line) |
| 289 | + return line |
| 290 | + |
| 291 | + |
| 292 | +def FixLeftAndRight(line): |
| 293 | + """Replaces left with right and vice versa in line. |
| 294 | + |
| 295 | + Args: |
| 296 | + line: A string in which to perform the replacement. |
| 297 | + |
| 298 | + Returns: |
| 299 | + line with left and right swapped. For example: |
| 300 | + line = FixLeftAndRight('padding-left: 2px; margin-right: 1px;') |
| 301 | + line will now be 'padding-right: 2px; margin-left: 1px;'. |
| 302 | + """ |
| 303 | + |
| 304 | + line = LEFT_RE.sub(TMP_TOKEN, line) |
| 305 | + line = RIGHT_RE.sub(LEFT, line) |
| 306 | + line = line.replace(TMP_TOKEN, RIGHT) |
| 307 | + logging.debug('FixLeftAndRight returns: %s' % line) |
| 308 | + return line |
| 309 | + |
| 310 | + |
| 311 | +def FixLeftAndRightInUrl(line): |
| 312 | + """Replaces left with right and vice versa ONLY within background urls. |
| 313 | + |
| 314 | + Args: |
| 315 | + line: A string in which to replace left with right and vice versa. |
| 316 | + |
| 317 | + Returns: |
| 318 | + line with left and right swapped in the url string. For example: |
| 319 | + line = FixLeftAndRightInUrl('background:url(right.png)') |
| 320 | + line will now be 'background:url(left.png)'. |
| 321 | + """ |
| 322 | + |
| 323 | + line = LEFT_IN_URL_RE.sub(TMP_TOKEN, line) |
| 324 | + line = RIGHT_IN_URL_RE.sub(LEFT, line) |
| 325 | + line = line.replace(TMP_TOKEN, RIGHT) |
| 326 | + logging.debug('FixLeftAndRightInUrl returns: %s' % line) |
| 327 | + return line |
| 328 | + |
| 329 | + |
| 330 | +def FixLtrAndRtlInUrl(line): |
| 331 | + """Replaces ltr with rtl and vice versa ONLY within background urls. |
| 332 | + |
| 333 | + Args: |
| 334 | + line: A string in which to replace ltr with rtl and vice versa. |
| 335 | + |
| 336 | + Returns: |
| 337 | + line with left and right swapped. For example: |
| 338 | + line = FixLtrAndRtlInUrl('background:url(rtl.png)') |
| 339 | + line will now be 'background:url(ltr.png)'. |
| 340 | + """ |
| 341 | + |
| 342 | + line = LTR_IN_URL_RE.sub(TMP_TOKEN, line) |
| 343 | + line = RTL_IN_URL_RE.sub(LTR, line) |
| 344 | + line = line.replace(TMP_TOKEN, RTL) |
| 345 | + logging.debug('FixLtrAndRtlInUrl returns: %s' % line) |
| 346 | + return line |
| 347 | + |
| 348 | + |
| 349 | +def FixCursorProperties(line): |
| 350 | + """Fixes directional CSS cursor properties. |
| 351 | + |
| 352 | + Args: |
| 353 | + line: A string to fix CSS cursor properties in. |
| 354 | + |
| 355 | + Returns: |
| 356 | + line reformatted with the cursor properties substituted. For example: |
| 357 | + line = FixCursorProperties('cursor: ne-resize') |
| 358 | + line will now be 'cursor: nw-resize'. |
| 359 | + """ |
| 360 | + |
| 361 | + line = CURSOR_EAST_RE.sub('\\1' + TMP_TOKEN, line) |
| 362 | + line = CURSOR_WEST_RE.sub('\\1e-resize', line) |
| 363 | + line = line.replace(TMP_TOKEN, 'w-resize') |
| 364 | + logging.debug('FixCursorProperties returns: %s' % line) |
| 365 | + return line |
| 366 | + |
| 367 | + |
| 368 | +def FixFourPartNotation(line): |
| 369 | + """Fixes the second and fourth positions in 4 part CSS notation. |
| 370 | + |
| 371 | + Args: |
| 372 | + line: A string to fix 4 part CSS notation in. |
| 373 | + |
| 374 | + Returns: |
| 375 | + line reformatted with the 4 part notations swapped. For example: |
| 376 | + line = FixFourPartNotation('padding: 1px 2px 3px 4px') |
| 377 | + line will now be 'padding: 1px 4px 3px 2px'. |
| 378 | + """ |
| 379 | + line = FOUR_NOTATION_QUANTITY_RE.sub('\\1 \\4 \\3 \\2', line) |
| 380 | + line = FOUR_NOTATION_COLOR_RE.sub('\\1\\2 \\5 \\4 \\3', line) |
| 381 | + logging.debug('FixFourPartNotation returns: %s' % line) |
| 382 | + return line |
| 383 | + |
| 384 | + |
| 385 | +def FixBackgroundPosition(line): |
| 386 | + """Fixes horizontal background percentage values in line. |
| 387 | + |
| 388 | + Args: |
| 389 | + line: A string to fix horizontal background position values in. |
| 390 | + |
| 391 | + Returns: |
| 392 | + line reformatted with the 4 part notations swapped. |
| 393 | + """ |
| 394 | + line = BG_HORIZONTAL_PERCENTAGE_RE.sub(CalculateNewBackgroundPosition, line) |
| 395 | + line = BG_HORIZONTAL_PERCENTAGE_X_RE.sub(CalculateNewBackgroundPositionX, |
| 396 | + line) |
| 397 | + logging.debug('FixBackgroundPosition returns: %s' % line) |
| 398 | + return line |
| 399 | + |
| 400 | + |
| 401 | +def CalculateNewBackgroundPosition(m): |
| 402 | + """Fixes horizontal background-position percentages. |
| 403 | + |
| 404 | + This function should be used as an argument to re.sub since it needs to |
| 405 | + perform replacement specific calculations. |
| 406 | + |
| 407 | + Args: |
| 408 | + m: A match object. |
| 409 | + |
| 410 | + Returns: |
| 411 | + A string with the horizontal background position percentage fixed. |
| 412 | + BG_HORIZONTAL_PERCENTAGE_RE.sub(FixBackgroundPosition, |
| 413 | + 'background-position: 75% 50%') |
| 414 | + will return 'background-position: 25% 50%'. |
| 415 | + """ |
| 416 | + |
| 417 | + # The flipped value is the offset from 100% |
| 418 | + new_x = str(100-int(m.group(4))) |
| 419 | + |
| 420 | + # Since m.group(1) may very well be None type and we need a string.. |
| 421 | + if m.group(1): |
| 422 | + position_string = m.group(1) |
| 423 | + else: |
| 424 | + position_string = '' |
| 425 | + |
| 426 | + return 'background%s%s%s%s%%%s' % (position_string, m.group(2), m.group(3), |
| 427 | + new_x, m.group(5)) |
| 428 | + |
| 429 | + |
| 430 | +def CalculateNewBackgroundPositionX(m): |
| 431 | + """Fixes percent based background-position-x. |
| 432 | + |
| 433 | + This function should be used as an argument to re.sub since it needs to |
| 434 | + perform replacement specific calculations. |
| 435 | + |
| 436 | + Args: |
| 437 | + m: A match object. |
| 438 | + |
| 439 | + Returns: |
| 440 | + A string with the background-position-x percentage fixed. |
| 441 | + BG_HORIZONTAL_PERCENTAGE_X_RE.sub(CalculateNewBackgroundPosition, |
| 442 | + 'background-position-x: 75%') |
| 443 | + will return 'background-position-x: 25%'. |
| 444 | + """ |
| 445 | + |
| 446 | + # The flipped value is the offset from 100% |
| 447 | + new_x = str(100-int(m.group(2))) |
| 448 | + |
| 449 | + return 'background-position-x%s%s%%' % (m.group(1), new_x) |
| 450 | + |
| 451 | + |
| 452 | +def ChangeLeftToRightToLeft(lines, |
| 453 | + swap_ltr_rtl_in_url=None, |
| 454 | + swap_left_right_in_url=None): |
| 455 | + """Turns lines into a stream and runs the fixing functions against it. |
| 456 | + |
| 457 | + Args: |
| 458 | + lines: An list of CSS lines. |
| 459 | + swap_ltr_rtl_in_url: Overrides this flag if param is set. |
| 460 | + swap_left_right_in_url: Overrides this flag if param is set. |
| 461 | + |
| 462 | + Returns: |
| 463 | + The same lines, but with left and right fixes. |
| 464 | + """ |
| 465 | + |
| 466 | + global FLAGS |
| 467 | + |
| 468 | + # Possibly override flags with params. |
| 469 | + logging.debug('ChangeLeftToRightToLeft swap_ltr_rtl_in_url=%s, ' |
| 470 | + 'swap_left_right_in_url=%s' % (swap_ltr_rtl_in_url, |
| 471 | + swap_left_right_in_url)) |
| 472 | + if swap_ltr_rtl_in_url is None: |
| 473 | + swap_ltr_rtl_in_url = FLAGS['swap_ltr_rtl_in_url'] |
| 474 | + if swap_left_right_in_url is None: |
| 475 | + swap_left_right_in_url = FLAGS['swap_left_right_in_url'] |
| 476 | + |
| 477 | + # Turns the array of lines into a single line stream. |
| 478 | + logging.debug('LINES COUNT: %s' % len(lines)) |
| 479 | + line = TOKEN_LINES.join(lines) |
| 480 | + |
| 481 | + # Tokenize any single line rules with the /* noflip */ annotation. |
| 482 | + noflip_single_tokenizer = Tokenizer(NOFLIP_SINGLE_RE, 'NOFLIP_SINGLE') |
| 483 | + line = noflip_single_tokenizer.Tokenize(line) |
| 484 | + |
| 485 | + # Tokenize any class rules with the /* noflip */ annotation. |
| 486 | + noflip_class_tokenizer = Tokenizer(NOFLIP_CLASS_RE, 'NOFLIP_CLASS') |
| 487 | + line = noflip_class_tokenizer.Tokenize(line) |
| 488 | + |
| 489 | + # Tokenize the comments so we can preserve them through the changes. |
| 490 | + comment_tokenizer = Tokenizer(COMMENT_RE, 'C') |
| 491 | + line = comment_tokenizer.Tokenize(line) |
| 492 | + |
| 493 | + # Here starteth the various left/right orientation fixes. |
| 494 | + line = FixBodyDirectionLtrAndRtl(line) |
| 495 | + |
| 496 | + if swap_left_right_in_url: |
| 497 | + line = FixLeftAndRightInUrl(line) |
| 498 | + |
| 499 | + if swap_ltr_rtl_in_url: |
| 500 | + line = FixLtrAndRtlInUrl(line) |
| 501 | + |
| 502 | + line = FixLeftAndRight(line) |
| 503 | + line = FixCursorProperties(line) |
| 504 | + line = FixFourPartNotation(line) |
| 505 | + line = FixBackgroundPosition(line) |
| 506 | + |
| 507 | + # DeTokenize the single line noflips. |
| 508 | + line = noflip_single_tokenizer.DeTokenize(line) |
| 509 | + |
| 510 | + # DeTokenize the class-level noflips. |
| 511 | + line = noflip_class_tokenizer.DeTokenize(line) |
| 512 | + |
| 513 | + # DeTokenize the comments. |
| 514 | + line = comment_tokenizer.DeTokenize(line) |
| 515 | + |
| 516 | + # Rejoin the lines back together. |
| 517 | + lines = line.split(TOKEN_LINES) |
| 518 | + |
| 519 | + return lines |
| 520 | + |
| 521 | +def usage(): |
| 522 | + """Prints out usage information.""" |
| 523 | + |
| 524 | + print 'Usage:' |
| 525 | + print ' ./cssjanus.py < file.css > file-rtl.css' |
| 526 | + print 'Flags:' |
| 527 | + print ' --swap_left_right_in_url: Fixes "left"/"right" string within urls.' |
| 528 | + print ' Ex: ./cssjanus.py --swap_left_right_in_url < file.css > file_rtl.css' |
| 529 | + print ' --swap_ltr_rtl_in_url: Fixes "ltr"/"rtl" string within urls.' |
| 530 | + print ' Ex: ./cssjanus --swap_ltr_rtl_in_url < file.css > file_rtl.css' |
| 531 | + |
| 532 | +def setflags(opts): |
| 533 | + """Parse the passed in command line arguments and set the FLAGS global. |
| 534 | + |
| 535 | + Args: |
| 536 | + opts: getopt iterable intercepted from argv. |
| 537 | + """ |
| 538 | + |
| 539 | + global FLAGS |
| 540 | + |
| 541 | + # Parse the arguments. |
| 542 | + for opt, arg in opts: |
| 543 | + logging.debug('opt: %s, arg: %s' % (opt, arg)) |
| 544 | + if opt in ("-h", "--help"): |
| 545 | + usage() |
| 546 | + sys.exit() |
| 547 | + elif opt in ("-d", "--debug"): |
| 548 | + logging.getLogger().setLevel(logging.DEBUG) |
| 549 | + elif opt == '--swap_ltr_rtl_in_url': |
| 550 | + FLAGS['swap_ltr_rtl_in_url'] = True |
| 551 | + elif opt == '--swap_left_right_in_url': |
| 552 | + FLAGS['swap_left_right_in_url'] = True |
| 553 | + |
| 554 | + |
| 555 | +def main(argv): |
| 556 | + """Sends stdin lines to ChangeLeftToRightToLeft and writes to stdout.""" |
| 557 | + |
| 558 | + # Define the flags. |
| 559 | + try: |
| 560 | + opts, args = getopt.getopt(argv, 'hd', ['help', 'debug', |
| 561 | + 'swap_left_right_in_url', |
| 562 | + 'swap_ltr_rtl_in_url']) |
| 563 | + except getopt.GetoptError: |
| 564 | + usage() |
| 565 | + sys.exit(2) |
| 566 | + |
| 567 | + # Parse and set the flags. |
| 568 | + setflags(opts) |
| 569 | + |
| 570 | + # Call the main routine with all our functionality. |
| 571 | + fixed_lines = ChangeLeftToRightToLeft(sys.stdin.readlines()) |
| 572 | + sys.stdout.write(''.join(fixed_lines)) |
| 573 | + |
| 574 | +if __name__ == '__main__': |
| 575 | + main(sys.argv[1:]) |
Property changes on: trunk/phase3/maintenance/cssjanus/cssjanus.py |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 576 | + native |
Added: svn:executable |
2 | 577 | + * |
Index: trunk/phase3/maintenance/cssjanus/LICENSE |
— | — | @@ -0,0 +1,202 @@ |
| 2 | + |
| 3 | + Apache License |
| 4 | + Version 2.0, January 2004 |
| 5 | + http://www.apache.org/licenses/ |
| 6 | + |
| 7 | + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
| 8 | + |
| 9 | + 1. Definitions. |
| 10 | + |
| 11 | + "License" shall mean the terms and conditions for use, reproduction, |
| 12 | + and distribution as defined by Sections 1 through 9 of this document. |
| 13 | + |
| 14 | + "Licensor" shall mean the copyright owner or entity authorized by |
| 15 | + the copyright owner that is granting the License. |
| 16 | + |
| 17 | + "Legal Entity" shall mean the union of the acting entity and all |
| 18 | + other entities that control, are controlled by, or are under common |
| 19 | + control with that entity. For the purposes of this definition, |
| 20 | + "control" means (i) the power, direct or indirect, to cause the |
| 21 | + direction or management of such entity, whether by contract or |
| 22 | + otherwise, or (ii) ownership of fifty percent (50%) or more of the |
| 23 | + outstanding shares, or (iii) beneficial ownership of such entity. |
| 24 | + |
| 25 | + "You" (or "Your") shall mean an individual or Legal Entity |
| 26 | + exercising permissions granted by this License. |
| 27 | + |
| 28 | + "Source" form shall mean the preferred form for making modifications, |
| 29 | + including but not limited to software source code, documentation |
| 30 | + source, and configuration files. |
| 31 | + |
| 32 | + "Object" form shall mean any form resulting from mechanical |
| 33 | + transformation or translation of a Source form, including but |
| 34 | + not limited to compiled object code, generated documentation, |
| 35 | + and conversions to other media types. |
| 36 | + |
| 37 | + "Work" shall mean the work of authorship, whether in Source or |
| 38 | + Object form, made available under the License, as indicated by a |
| 39 | + copyright notice that is included in or attached to the work |
| 40 | + (an example is provided in the Appendix below). |
| 41 | + |
| 42 | + "Derivative Works" shall mean any work, whether in Source or Object |
| 43 | + form, that is based on (or derived from) the Work and for which the |
| 44 | + editorial revisions, annotations, elaborations, or other modifications |
| 45 | + represent, as a whole, an original work of authorship. For the purposes |
| 46 | + of this License, Derivative Works shall not include works that remain |
| 47 | + separable from, or merely link (or bind by name) to the interfaces of, |
| 48 | + the Work and Derivative Works thereof. |
| 49 | + |
| 50 | + "Contribution" shall mean any work of authorship, including |
| 51 | + the original version of the Work and any modifications or additions |
| 52 | + to that Work or Derivative Works thereof, that is intentionally |
| 53 | + submitted to Licensor for inclusion in the Work by the copyright owner |
| 54 | + or by an individual or Legal Entity authorized to submit on behalf of |
| 55 | + the copyright owner. For the purposes of this definition, "submitted" |
| 56 | + means any form of electronic, verbal, or written communication sent |
| 57 | + to the Licensor or its representatives, including but not limited to |
| 58 | + communication on electronic mailing lists, source code control systems, |
| 59 | + and issue tracking systems that are managed by, or on behalf of, the |
| 60 | + Licensor for the purpose of discussing and improving the Work, but |
| 61 | + excluding communication that is conspicuously marked or otherwise |
| 62 | + designated in writing by the copyright owner as "Not a Contribution." |
| 63 | + |
| 64 | + "Contributor" shall mean Licensor and any individual or Legal Entity |
| 65 | + on behalf of whom a Contribution has been received by Licensor and |
| 66 | + subsequently incorporated within the Work. |
| 67 | + |
| 68 | + 2. Grant of Copyright License. Subject to the terms and conditions of |
| 69 | + this License, each Contributor hereby grants to You a perpetual, |
| 70 | + worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
| 71 | + copyright license to reproduce, prepare Derivative Works of, |
| 72 | + publicly display, publicly perform, sublicense, and distribute the |
| 73 | + Work and such Derivative Works in Source or Object form. |
| 74 | + |
| 75 | + 3. Grant of Patent License. Subject to the terms and conditions of |
| 76 | + this License, each Contributor hereby grants to You a perpetual, |
| 77 | + worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
| 78 | + (except as stated in this section) patent license to make, have made, |
| 79 | + use, offer to sell, sell, import, and otherwise transfer the Work, |
| 80 | + where such license applies only to those patent claims licensable |
| 81 | + by such Contributor that are necessarily infringed by their |
| 82 | + Contribution(s) alone or by combination of their Contribution(s) |
| 83 | + with the Work to which such Contribution(s) was submitted. If You |
| 84 | + institute patent litigation against any entity (including a |
| 85 | + cross-claim or counterclaim in a lawsuit) alleging that the Work |
| 86 | + or a Contribution incorporated within the Work constitutes direct |
| 87 | + or contributory patent infringement, then any patent licenses |
| 88 | + granted to You under this License for that Work shall terminate |
| 89 | + as of the date such litigation is filed. |
| 90 | + |
| 91 | + 4. Redistribution. You may reproduce and distribute copies of the |
| 92 | + Work or Derivative Works thereof in any medium, with or without |
| 93 | + modifications, and in Source or Object form, provided that You |
| 94 | + meet the following conditions: |
| 95 | + |
| 96 | + (a) You must give any other recipients of the Work or |
| 97 | + Derivative Works a copy of this License; and |
| 98 | + |
| 99 | + (b) You must cause any modified files to carry prominent notices |
| 100 | + stating that You changed the files; and |
| 101 | + |
| 102 | + (c) You must retain, in the Source form of any Derivative Works |
| 103 | + that You distribute, all copyright, patent, trademark, and |
| 104 | + attribution notices from the Source form of the Work, |
| 105 | + excluding those notices that do not pertain to any part of |
| 106 | + the Derivative Works; and |
| 107 | + |
| 108 | + (d) If the Work includes a "NOTICE" text file as part of its |
| 109 | + distribution, then any Derivative Works that You distribute must |
| 110 | + include a readable copy of the attribution notices contained |
| 111 | + within such NOTICE file, excluding those notices that do not |
| 112 | + pertain to any part of the Derivative Works, in at least one |
| 113 | + of the following places: within a NOTICE text file distributed |
| 114 | + as part of the Derivative Works; within the Source form or |
| 115 | + documentation, if provided along with the Derivative Works; or, |
| 116 | + within a display generated by the Derivative Works, if and |
| 117 | + wherever such third-party notices normally appear. The contents |
| 118 | + of the NOTICE file are for informational purposes only and |
| 119 | + do not modify the License. You may add Your own attribution |
| 120 | + notices within Derivative Works that You distribute, alongside |
| 121 | + or as an addendum to the NOTICE text from the Work, provided |
| 122 | + that such additional attribution notices cannot be construed |
| 123 | + as modifying the License. |
| 124 | + |
| 125 | + You may add Your own copyright statement to Your modifications and |
| 126 | + may provide additional or different license terms and conditions |
| 127 | + for use, reproduction, or distribution of Your modifications, or |
| 128 | + for any such Derivative Works as a whole, provided Your use, |
| 129 | + reproduction, and distribution of the Work otherwise complies with |
| 130 | + the conditions stated in this License. |
| 131 | + |
| 132 | + 5. Submission of Contributions. Unless You explicitly state otherwise, |
| 133 | + any Contribution intentionally submitted for inclusion in the Work |
| 134 | + by You to the Licensor shall be under the terms and conditions of |
| 135 | + this License, without any additional terms or conditions. |
| 136 | + Notwithstanding the above, nothing herein shall supersede or modify |
| 137 | + the terms of any separate license agreement you may have executed |
| 138 | + with Licensor regarding such Contributions. |
| 139 | + |
| 140 | + 6. Trademarks. This License does not grant permission to use the trade |
| 141 | + names, trademarks, service marks, or product names of the Licensor, |
| 142 | + except as required for reasonable and customary use in describing the |
| 143 | + origin of the Work and reproducing the content of the NOTICE file. |
| 144 | + |
| 145 | + 7. Disclaimer of Warranty. Unless required by applicable law or |
| 146 | + agreed to in writing, Licensor provides the Work (and each |
| 147 | + Contributor provides its Contributions) on an "AS IS" BASIS, |
| 148 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| 149 | + implied, including, without limitation, any warranties or conditions |
| 150 | + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
| 151 | + PARTICULAR PURPOSE. You are solely responsible for determining the |
| 152 | + appropriateness of using or redistributing the Work and assume any |
| 153 | + risks associated with Your exercise of permissions under this License. |
| 154 | + |
| 155 | + 8. Limitation of Liability. In no event and under no legal theory, |
| 156 | + whether in tort (including negligence), contract, or otherwise, |
| 157 | + unless required by applicable law (such as deliberate and grossly |
| 158 | + negligent acts) or agreed to in writing, shall any Contributor be |
| 159 | + liable to You for damages, including any direct, indirect, special, |
| 160 | + incidental, or consequential damages of any character arising as a |
| 161 | + result of this License or out of the use or inability to use the |
| 162 | + Work (including but not limited to damages for loss of goodwill, |
| 163 | + work stoppage, computer failure or malfunction, or any and all |
| 164 | + other commercial damages or losses), even if such Contributor |
| 165 | + has been advised of the possibility of such damages. |
| 166 | + |
| 167 | + 9. Accepting Warranty or Additional Liability. While redistributing |
| 168 | + the Work or Derivative Works thereof, You may choose to offer, |
| 169 | + and charge a fee for, acceptance of support, warranty, indemnity, |
| 170 | + or other liability obligations and/or rights consistent with this |
| 171 | + License. However, in accepting such obligations, You may act only |
| 172 | + on Your own behalf and on Your sole responsibility, not on behalf |
| 173 | + of any other Contributor, and only if You agree to indemnify, |
| 174 | + defend, and hold each Contributor harmless for any liability |
| 175 | + incurred by, or claims asserted against, such Contributor by reason |
| 176 | + of your accepting any such warranty or additional liability. |
| 177 | + |
| 178 | + END OF TERMS AND CONDITIONS |
| 179 | + |
| 180 | + APPENDIX: How to apply the Apache License to your work. |
| 181 | + |
| 182 | + To apply the Apache License to your work, attach the following |
| 183 | + boilerplate notice, with the fields enclosed by brackets "[]" |
| 184 | + replaced with your own identifying information. (Don't include |
| 185 | + the brackets!) The text should be enclosed in the appropriate |
| 186 | + comment syntax for the file format. We also recommend that a |
| 187 | + file or class name and description of purpose be included on the |
| 188 | + same "printed page" as the copyright notice for easier |
| 189 | + identification within third-party archives. |
| 190 | + |
| 191 | + Copyright [yyyy] [name of copyright owner] |
| 192 | + |
| 193 | + Licensed under the Apache License, Version 2.0 (the "License"); |
| 194 | + you may not use this file except in compliance with the License. |
| 195 | + You may obtain a copy of the License at |
| 196 | + |
| 197 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 198 | + |
| 199 | + Unless required by applicable law or agreed to in writing, software |
| 200 | + distributed under the License is distributed on an "AS IS" BASIS, |
| 201 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 202 | + See the License for the specific language governing permissions and |
| 203 | + limitations under the License. |
Property changes on: trunk/phase3/maintenance/cssjanus/LICENSE |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 204 | + native |
Index: trunk/phase3/maintenance/cssjanus/COPYING |
— | — | @@ -0,0 +1,13 @@ |
| 2 | + Copyright 2008 Google Inc. |
| 3 | + |
| 4 | + Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + you may not use this file except in compliance with the License. |
| 6 | + You may obtain a copy of the License at |
| 7 | + |
| 8 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + |
| 10 | + Unless required by applicable law or agreed to in writing, software |
| 11 | + distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + See the License for the specific language governing permissions and |
| 14 | + limitations under the License. |
Property changes on: trunk/phase3/maintenance/cssjanus/COPYING |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 15 | + native |
Index: trunk/phase3/maintenance/cssjanus/csslex.py |
— | — | @@ -0,0 +1,114 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +# |
| 4 | +# Copyright 2007 Google Inc. All Rights Reserved. |
| 5 | + |
| 6 | +"""CSS Lexical Grammar rules. |
| 7 | + |
| 8 | +CSS lexical grammar from http://www.w3.org/TR/CSS21/grammar.html |
| 9 | +""" |
| 10 | + |
| 11 | +__author__ = ['elsigh@google.com (Lindsey Simon)', |
| 12 | + 'msamuel@google.com (Mike Samuel)'] |
| 13 | + |
| 14 | +# public symbols |
| 15 | +__all__ = [ "NEWLINE", "HEX", "NON_ASCII", "UNICODE", "ESCAPE", "NMSTART", "NMCHAR", "STRING1", "STRING2", "IDENT", "NAME", "HASH", "NUM", "STRING", "URL", "SPACE", "WHITESPACE", "COMMENT", "QUANTITY", "PUNC" ] |
| 16 | + |
| 17 | +# The comments below are mostly copied verbatim from the grammar. |
| 18 | + |
| 19 | +# "@import" {return IMPORT_SYM;} |
| 20 | +# "@page" {return PAGE_SYM;} |
| 21 | +# "@media" {return MEDIA_SYM;} |
| 22 | +# "@charset" {return CHARSET_SYM;} |
| 23 | +KEYWORD = r'(?:\@(?:import|page|media|charset))' |
| 24 | + |
| 25 | +# nl \n|\r\n|\r|\f ; a newline |
| 26 | +NEWLINE = r'\n|\r\n|\r|\f' |
| 27 | + |
| 28 | +# h [0-9a-f] ; a hexadecimal digit |
| 29 | +HEX = r'[0-9a-f]' |
| 30 | + |
| 31 | +# nonascii [\200-\377] |
| 32 | +NON_ASCII = r'[\200-\377]' |
| 33 | + |
| 34 | +# unicode \\{h}{1,6}(\r\n|[ \t\r\n\f])? |
| 35 | +UNICODE = r'(?:(?:\\' + HEX + r'{1,6})(?:\r\n|[ \t\r\n\f])?)' |
| 36 | + |
| 37 | +# escape {unicode}|\\[^\r\n\f0-9a-f] |
| 38 | +ESCAPE = r'(?:' + UNICODE + r'|\\[^\r\n\f0-9a-f])' |
| 39 | + |
| 40 | +# nmstart [_a-z]|{nonascii}|{escape} |
| 41 | +NMSTART = r'(?:[_a-z]|' + NON_ASCII + r'|' + ESCAPE + r')' |
| 42 | + |
| 43 | +# nmchar [_a-z0-9-]|{nonascii}|{escape} |
| 44 | +NMCHAR = r'(?:[_a-z0-9-]|' + NON_ASCII + r'|' + ESCAPE + r')' |
| 45 | + |
| 46 | +# ident -?{nmstart}{nmchar}* |
| 47 | +IDENT = r'-?' + NMSTART + NMCHAR + '*' |
| 48 | + |
| 49 | +# name {nmchar}+ |
| 50 | +NAME = NMCHAR + r'+' |
| 51 | + |
| 52 | +# hash |
| 53 | +HASH = r'#' + NAME |
| 54 | + |
| 55 | +# string1 \"([^\n\r\f\\"]|\\{nl}|{escape})*\" ; "string" |
| 56 | +STRING1 = r'"(?:[^\"\\]|\\.)*"' |
| 57 | + |
| 58 | +# string2 \'([^\n\r\f\\']|\\{nl}|{escape})*\' ; 'string' |
| 59 | +STRING2 = r"'(?:[^\'\\]|\\.)*'" |
| 60 | + |
| 61 | +# string {string1}|{string2} |
| 62 | +STRING = '(?:' + STRING1 + r'|' + STRING2 + ')' |
| 63 | + |
| 64 | +# num [0-9]+|[0-9]*"."[0-9]+ |
| 65 | +NUM = r'(?:[0-9]*\.[0-9]+|[0-9]+)' |
| 66 | + |
| 67 | +# s [ \t\r\n\f] |
| 68 | +SPACE = r'[ \t\r\n\f]' |
| 69 | + |
| 70 | +# w {s}* |
| 71 | +WHITESPACE = '(?:' + SPACE + r'*)' |
| 72 | + |
| 73 | +# url special chars |
| 74 | +URL_SPECIAL_CHARS = r'[!#$%&*-~]' |
| 75 | + |
| 76 | +# url chars ({url_special_chars}|{nonascii}|{escape})* |
| 77 | +URL_CHARS = r'(?:%s|%s|%s)*' % (URL_SPECIAL_CHARS, NON_ASCII, ESCAPE) |
| 78 | + |
| 79 | +# url |
| 80 | +URL = r'url\(%s(%s|%s)%s\)' % (WHITESPACE, STRING, URL_CHARS, WHITESPACE) |
| 81 | + |
| 82 | +# comments |
| 83 | +# see http://www.w3.org/TR/CSS21/grammar.html |
| 84 | +COMMENT = r'/\*[^*]*\*+([^/*][^*]*\*+)*/' |
| 85 | + |
| 86 | +# {E}{M} {return EMS;} |
| 87 | +# {E}{X} {return EXS;} |
| 88 | +# {P}{X} {return LENGTH;} |
| 89 | +# {C}{M} {return LENGTH;} |
| 90 | +# {M}{M} {return LENGTH;} |
| 91 | +# {I}{N} {return LENGTH;} |
| 92 | +# {P}{T} {return LENGTH;} |
| 93 | +# {P}{C} {return LENGTH;} |
| 94 | +# {D}{E}{G} {return ANGLE;} |
| 95 | +# {R}{A}{D} {return ANGLE;} |
| 96 | +# {G}{R}{A}{D} {return ANGLE;} |
| 97 | +# {M}{S} {return TIME;} |
| 98 | +# {S} {return TIME;} |
| 99 | +# {H}{Z} {return FREQ;} |
| 100 | +# {K}{H}{Z} {return FREQ;} |
| 101 | +# % {return PERCENTAGE;} |
| 102 | +UNIT = r'(?:em|ex|px|cm|mm|in|pt|pc|deg|rad|grad|ms|s|hz|khz|%)' |
| 103 | + |
| 104 | +# {num}{UNIT|IDENT} {return NUMBER;} |
| 105 | +QUANTITY = '%s(?:%s%s|%s)?' % (NUM, WHITESPACE, UNIT, IDENT) |
| 106 | + |
| 107 | +# "<!--" {return CDO;} |
| 108 | +# "-->" {return CDC;} |
| 109 | +# "~=" {return INCLUDES;} |
| 110 | +# "|=" {return DASHMATCH;} |
| 111 | +# {w}"{" {return LBRACE;} |
| 112 | +# {w}"+" {return PLUS;} |
| 113 | +# {w}">" {return GREATER;} |
| 114 | +# {w}"," {return COMMA;} |
| 115 | +PUNC = r'<!--|-->|~=|\|=|[\{\+>,:;]' |
Property changes on: trunk/phase3/maintenance/cssjanus/csslex.py |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 116 | + native |
Added: svn:executable |
2 | 117 | + * |
Index: trunk/phase3/maintenance/cssjanus/README |
— | — | @@ -0,0 +1,91 @@ |
| 2 | +=CSSJanus= |
| 3 | + |
| 4 | +_Flips CSS from LTR to an RTL orienation and vice-versa_ |
| 5 | + |
| 6 | +Author: `Lindsey Simon <elsigh@google.com>` |
| 7 | + |
| 8 | +==Introduction== |
| 9 | + |
| 10 | +CSSJanus is CSS parser utility designed to aid the conversion of a website's |
| 11 | +layout from left-to-right(LTR) to right-to-left(RTL). The script was born out of |
| 12 | +a need to convert CSS for RTL languages when tables are not being used for layout (since tables will automatically reorder TD's in RTL). |
| 13 | +CSSJanus will change most of the obvious CSS property names and their values as |
| 14 | +well as some not-so-obvious ones (cursor, background-position %, etc...). |
| 15 | +The script is designed to offer flexibility to account for cases when you do |
| 16 | +not want to change certain rules which exist to account for bidirectional text |
| 17 | +display bugs, as well as situations where you may or may not want to flip annotations inside of the background url string. |
| 18 | +Note that you can disable CSSJanus from running on an entire class or any |
| 19 | +rule within a class by prepending a /* @noflip */ comment before the rule(s) |
| 20 | +you want CSSJanus to ignore. |
| 21 | + |
| 22 | +CSSJanus itself is not always enough to make a website that works in a LTR |
| 23 | +language context work in a RTL language all the way, but it is a start. |
| 24 | + |
| 25 | +==Getting the code== |
| 26 | + |
| 27 | +View the trunk at: |
| 28 | + |
| 29 | + http://cssjanus.googlecode.com/svn/trunk/ |
| 30 | + |
| 31 | +Check out the latest development version anonymously with: |
| 32 | + |
| 33 | +{{{ |
| 34 | + $ svn checkout http://cssjanus.googlecode.com/svn/trunk/ cssjanus |
| 35 | +}}} |
| 36 | + |
| 37 | +==Using== |
| 38 | + |
| 39 | +Usage: |
| 40 | + ./cssjanus.py < file.css > file-rtl.css |
| 41 | +Flags: |
| 42 | + --swap_left_right_in_url: Fixes "left"/"right" string within urls. |
| 43 | + Ex: ./cssjanus.py --swap_left_right_in_url < file.css > file_rtl.css |
| 44 | + --swap_ltr_rtl_in_url: Fixes "ltr"/"rtl" string within urls. |
| 45 | + Ex: ./cssjanus.py --swap_ltr_rtl_in_url < file.css > file_rtl.css |
| 46 | + |
| 47 | +If you'd like to make use of the webapp version of cssjanus, you'll need to |
| 48 | +download the Google App Engine SDK |
| 49 | + http://code.google.com/appengine/downloads.html |
| 50 | +and also drop a "django" directory into this directory, with the latest svn |
| 51 | +from django. You should be good to go with that setup. Please let me know |
| 52 | +otherwise. |
| 53 | + |
| 54 | +==Bugs, Patches== |
| 55 | + |
| 56 | +Patches and bug reports are welcome, just please keep the style |
| 57 | +consistent with the original source. If you find a bug, please include a diff |
| 58 | +of cssjanus_test.py with the bug included as a new unit test which fails. It |
| 59 | +will make understanding and fixing the bug easier. |
| 60 | + |
| 61 | +==Todo== |
| 62 | + |
| 63 | +* Include some helpers for some typical bidi text solutions? |
| 64 | +* Aural CSS (azimuth) swapping? |
| 65 | + |
| 66 | +==Contributors== |
| 67 | + |
| 68 | +Additional thanks to Mike Samuel for his work on csslex.py, Andy Perelson for |
| 69 | +his help coding and reviewing, Stephen Zabel for his help with i18n and my sanity, |
| 70 | +and to Eric Meyer for his thoughtful input. |
| 71 | +Thanks to Junyu Wang for the Chinese translation. |
| 72 | +Thanks to Masashi Kawashima for the Japanese translation. |
| 73 | +Thanks to Taaryk Taar and Tariq Al-Omaireeni for an updated Arabic translation. |
| 74 | +Thanks to Jens Meiert for the German translation. |
| 75 | + |
| 76 | +==License== |
| 77 | + |
| 78 | +{{{ |
| 79 | + Copyright 2008 Google Inc. All Rights Reserved. |
| 80 | + |
| 81 | + Licensed under the Apache License, Version 2.0 (the 'License'); |
| 82 | + you may not use this file except in compliance with the License. |
| 83 | + You may obtain a copy of the License at |
| 84 | + |
| 85 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 86 | + |
| 87 | + Unless required by applicable law or agreed to in writing, software |
| 88 | + distributed under the License is distributed on an 'AS IS' BASIS, |
| 89 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 90 | + See the License for the specific language governing permissions and |
| 91 | + limitations under the License. |
| 92 | +}}} |
Property changes on: trunk/phase3/maintenance/cssjanus/README |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 93 | + native |
Index: trunk/phase3/skins/vector/Makefile |
— | — | @@ -4,12 +4,12 @@ |
5 | 5 | |
6 | 6 | all: main-rtl.css |
7 | 7 | |
8 | | -main-rtl.css: main-ltr.css cssjanus/cssjanus.py |
9 | | - python cssjanus/cssjanus.py --swap_ltr_rtl_in_url < main-ltr.css > main-rtl.css |
| 8 | +main-rtl.css: main-ltr.css ../../maintenance/cssjanus/cssjanus.py |
| 9 | + python ../../maintenance/cssjanus/cssjanus.py --swap_ltr_rtl_in_url < main-ltr.css > main-rtl.css |
10 | 10 | |
11 | 11 | # SVN version is broken; checking in our own. |
12 | | -#cssjanus/cssjanus.py: |
13 | | -# svn co http://cssjanus.googlecode.com/svn/trunk cssjanus |
| 12 | +#../../maintenance/cssjanus/cssjanus.py: |
| 13 | +# svn co http://cssjanus.googlecode.com/svn/trunk ../maintenance/cssjanus |
14 | 14 | |
15 | 15 | #distclean: clean |
16 | 16 | # rm -rf cssjanus |