css.py (20221B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\carbonui\control\editutils\css.py 2 import sys 3 from carbonui.util.various_unsorted import ParseHTMLColor 4 5 class css: 6 7 def __init__(self): 8 self.Reset() 9 10 def Reset(self): 11 self.classes = [] 12 self.s = {} 13 14 def copy(self): 15 new = css() 16 new.classes = self.classes[:] 17 return new 18 19 def ApplyCSS(self, tag, attrs, attrStack): 20 classlist = [] 21 for c in self.classes: 22 if self.CompareSelector(attrStack[-1], c) == 1: 23 b = 0 24 if c['deptag']: 25 i = len(attrStack) - 2 26 if i < 0: 27 continue 28 for t in c['deptag']: 29 if b == 1 or i < 0: 30 break 31 if t['op'] == ' ': 32 while not self.CompareSelector(attrStack[i], t): 33 i -= 1 34 if i < 0: 35 b = 1 36 break 37 38 if i < 0: 39 b = 1 40 break 41 i -= 1 42 elif t['op'] == '>': 43 if self.CompareSelector(attrStack[i], t): 44 i -= 1 45 continue 46 else: 47 b = 1 48 break 49 elif t['op'] == '*': 50 i -= 1 51 if i < 0: 52 break 53 while not self.CompareSelector(attrStack[i], t): 54 i -= 1 55 if i < 0: 56 b = 1 57 break 58 59 if i < 0: 60 b = 1 61 break 62 i -= 1 63 elif t['op'] == '+': 64 if attrStack[i]['lasttag'] == t['tag']: 65 i -= 1 66 continue 67 else: 68 b = 1 69 break 70 71 if b == 0: 72 classlist.append(c) 73 74 classlist.sort(lambda x, y: cmp(x['prio'], y['prio'])) 75 c = {} 76 for each in classlist: 77 c.update(each) 78 79 return c 80 81 def CompareSelector(self, astack, csel): 82 tag = astack['tag'] 83 tid = getattr(astack['attr'], 'id', None) 84 cl = getattr(astack['attr'], 'class', '').lower() 85 if csel['tag'] not in (tag, '*') and csel['tag']: 86 return 87 if csel['class']: 88 if csel['class'] not in cl.split(' '): 89 return 90 if csel['id'] and tid != csel['id']: 91 return 92 return 1 93 94 def SplitSelector(self, sel): 95 s = {} 96 s['tag'] = None 97 s['id'] = None 98 s['class'] = None 99 s['attr'] = None 100 s['pclass'] = None 101 sel = sel.strip().lower() 102 if sel.count(':'): 103 sel, pclass = sel.split(':', 1) 104 s['pclass'] = pclass 105 if sel == '': 106 sel = '*' 107 ccount = sel.count('.') 108 acount = sel.count('[') 109 icount = sel.count('#') 110 if ccount + acount + icount > 1: 111 return 112 if ccount == 1: 113 h = sel.split('.') 114 if len(h) == 1: 115 s['class'] = h[0] 116 else: 117 s['tag'] = h[0] 118 s['class'] = h[1] 119 return s 120 if acount == 1: 121 h = sel.split('[') 122 if sel[-1] == ']': 123 if len(h) == 1: 124 s['attr'] = h[0] 125 else: 126 s['tag'] = h[0] 127 s['attr'] = h[1] 128 else: 129 return 130 return s 131 if icount == 1: 132 h = sel.split('#') 133 if len(h) == 1: 134 s['id'] = h[0] 135 else: 136 s['tag'] = h[0] 137 s['id'] = h[1] 138 return s 139 s['tag'] = sel 140 return s 141 142 def GetClass(self, classID): 143 classID = classID.lower() 144 for c in self.classes: 145 if c['id'] == classID: 146 return c 147 if c['class'] == classID: 148 return c 149 150 def HandlePseudoClass(self, pclass): 151 links = [('link', 'link-color'), 152 ('visited', 'vlink-color'), 153 ('active', 'alink-color'), 154 ('hover', 'alink-color')] 155 d = {} 156 for dclass, attr in links: 157 if pclass == dclass and 'color' in self.s: 158 d[attr] = self.s['color'] 159 160 self.s = d.copy() 161 162 def ParseCSSData(self, cssdata): 163 lines = '' 164 for dataAndComment in cssdata.split('*/'): 165 data = dataAndComment.split('/*', 1)[0] 166 lines += data 167 168 for styleClass in lines.split('}'): 169 if not styleClass: 170 continue 171 if styleClass.count('{') != 1: 172 continue 173 attr, styles = styleClass.strip().split('{') 174 self.s = {} 175 prio = 0 176 if not styles: 177 continue 178 data, imp = self.ParseStyleData(styles.strip()) 179 self.ParseStyle(data) 180 selector = depsel = depop = None 181 prio += attr.count('.') * 100 + attr.count('#') * 1000 + attr.count('[') * 10 + attr.count(' ') + 1 - (attr.count('.') + attr.count('#') + attr.count('[')) 182 selectors = attr.replace('\t', ' ').replace(' ', ' ').strip().split(' ') 183 selector = selectors[-1] 184 selectors = selectors[:-1] 185 selectors.reverse() 186 deptags = [] 187 op = ' ' 188 for t in selectors: 189 if t in ('>', '*', '+'): 190 op = t 191 else: 192 d = self.SplitSelector(t) 193 if d: 194 d['op'] = op 195 deptags.append(d) 196 op = ' ' 197 198 for sel in selector.split(','): 199 attrs = self.SplitSelector(sel) 200 if attrs: 201 if attrs['pclass']: 202 self.HandlePseudoClass(attrs['pclass']) 203 s = self.s.copy() 204 s['tag'] = attrs['tag'] 205 s['class'] = attrs['class'] 206 s['id'] = attrs['id'] 207 s['attr'] = attrs['attr'] 208 s['deptag'] = deptags 209 s['prio'] = prio 210 self.classes.append(s) 211 212 if len(imp): 213 self.s = {} 214 self.ParseStyle(imp) 215 for sel in selector.split(','): 216 attrs = self.SplitSelector(sel) 217 if attrs: 218 if attrs['pclass']: 219 self.HandlePseudoClass(attrs['pclass']) 220 s = self.s.copy() 221 s['tag'] = attrs['tag'] 222 s['class'] = attrs['class'] 223 s['id'] = attrs['id'] 224 s['attr'] = attrs['attr'] 225 s['deptag'] = deptags 226 s['prio'] = 10000 227 self.classes.append(s) 228 229 def ParseStyleData(self, cssdata): 230 data = {} 231 imp = {} 232 for attrAndValue in cssdata.split(';'): 233 if not attrAndValue: 234 continue 235 if ':' in attrAndValue: 236 attr, value = attrAndValue.strip().split(':', 1) 237 else: 238 continue 239 if attr and value: 240 attr = attr.strip().lower() 241 if '!' in value: 242 value, important = value.split('!', -1) 243 if important.strip().lower() == 'important': 244 imp[attr] = value.strip() 245 continue 246 data[attr] = value.strip() 247 248 return (data, imp) 249 250 def ParseCSS(self, attrs = None): 251 self.s = {} 252 sattr = getattr(attrs, 'style', None) 253 if sattr: 254 data, imp = self.ParseStyleData(sattr) 255 data.update(imp) 256 return self.ParseStyle(data) 257 258 def ParseStyle(self, style = None): 259 if style: 260 for k, v in style.iteritems(): 261 if k in self.styleDict: 262 eval('self.%s' % self.styleDict[k])(k, v) 263 264 return self.s 265 266 def ParseStyleNum(self, value): 267 value = unicode(value).lower().strip() 268 if value.isdigit(): 269 return int(value) 270 if value.endswith('%'): 271 return value 272 if value[-2:] in self.absStyleUnits and value[:-2].isdigit(): 273 return int(self.absStyleUnits[value[-2:]] * float(value[:-2].strip())) 274 try: 275 return int(value) 276 except: 277 sys.exc_clear() 278 return None 279 280 absStyleUnits = {'px': 1.0, 281 'in': 100.0, 282 'cm': 100.0 / 2.54, 283 'mm': 100.0 / 25.4, 284 'pt': 100.0 / 72.0, 285 'pc': 100.0 / 6.0} 286 287 def ParseFontSize(self, k, v): 288 if v: 289 if not v.isdigit(): 290 if v in self.fontSize: 291 v = self.fontSize[v] 292 v = self.ParseStyleNum(v) 293 if v is None or not type(v) == int and not v.isdigit(): 294 return 295 self.s['font-size'] = int(v) 296 297 def ParseFontStyle(self, k, v): 298 if v: 299 if v == 'normal': 300 self.s['font-style'] = 'n' 301 if v in ('italic', 'oblique'): 302 self.s['font-style'] = 'i' 303 304 def ParseFontWeight(self, k, v): 305 if v: 306 if v == 'normal': 307 self.s['font-weight'] = 'n' 308 elif v in ('bold', 'bolder'): 309 self.s['font-weight'] = 'b' 310 311 def ParseTextDecoration(self, k, v): 312 if v: 313 if v == 'none': 314 self.s['text-decoration'] = '' 315 return 316 s = unicode(v) 317 s = s.replace('underline', 'u').replace('line-through', 's').replace('overline', 'o') 318 for t in s: 319 if t.strip() not in ('u', 's', 'o'): 320 return 321 322 self.s['text-decoration'] = s 323 324 def ParseTextAlign(self, k, v): 325 if v in ('left', 'right', 'center'): 326 self.s['text-align'] = v 327 328 def ParseFont(self, k, v): 329 if v: 330 for param in v.split(' '): 331 self.ParseFontSize('', param) 332 self.ParseFontStyle('', param) 333 self.ParseFontWeight('', param) 334 self.ParseTextDecoration('', param) 335 336 def ParseMargin(self, k, v): 337 if v: 338 a = [] 339 for p in v.replace(' ', ' ').split(' '): 340 p = self.ParseStyleNum(p) 341 if p: 342 a.append(p) 343 344 if len(a) > 0: 345 self.s['margin-left'] = a[[0, 346 1, 347 1, 348 3][len(a) - 1]] 349 self.s['margin-right'] = a[[0, 350 1, 351 1, 352 1][len(a) - 1]] 353 self.s['margin-top'] = a[[0, 354 0, 355 0, 356 0][len(a) - 1]] 357 self.s['margin-bottom'] = a[[0, 358 0, 359 2, 360 2][len(a) - 1]] 361 362 def ParsePadding(self, k, v): 363 if v: 364 v = self.ParseStyleNum(v) 365 if v: 366 self.s['padding-left'] = self.s['padding-right'] = v 367 self.s['padding-top'] = self.s['padding-bottom'] = v 368 369 def ParseBorderWidth(self, k, v): 370 if v: 371 if v in self.borderWidths: 372 v = self.borderWidths[v] 373 else: 374 v = self.ParseStyleNum(v) 375 if v: 376 if k == 'border-width': 377 for i in ('left', 'right', 'top', 'bottom'): 378 self.s['border-' + i + '-width'] = v 379 380 else: 381 self.s[k] = v 382 383 def ParseBorderColor(self, k, v): 384 if v: 385 v = ParseHTMLColor(v, 1, error=1) 386 if v: 387 self.s['border-left-color'] = self.s['border-right-color'] = v 388 self.s['border-top-color'] = self.s['border-bottom-color'] = v 389 390 def ParseBorderStyles(self, k, v): 391 if v: 392 if v in self.borderStyles: 393 self.s[k] = self.borderStyles[v] 394 395 def ParseBorderStyle(self, k, v): 396 if v in self.borderStyles: 397 self.s['border-left-style'] = self.borderStyles[v] 398 self.s['border-right-style'] = self.borderStyles[v] 399 self.s['border-top-style'] = self.borderStyles[v] 400 self.s['border-bottom-style'] = self.borderStyles[v] 401 402 def ParseBorder(self, k, v): 403 for each in v.split(' '): 404 if each in self.borderStyles: 405 self.ParseBorderStyle(k, each) 406 continue 407 self.ParseBorderColor(k, each) 408 self.ParseBorderWidth('border-width', each) 409 410 def ParseBorders(self, k, v): 411 for each in v.split(' '): 412 if each in self.borderStyles: 413 self.ParseBorderStyles(k, each) 414 continue 415 self.ParseColor(k + '-color', each) 416 self.ParseBorder(k + '-width', each) 417 418 def ParseBorderCollapse(self, k, v): 419 if v in self.borderCollapse: 420 self.s['border-collapse'] = self.borderCollapse[v] 421 else: 422 self.s['border-collapse'] = 0 423 424 def ParseColor(self, k, v): 425 if v: 426 v = ParseHTMLColor(v, 1, error=1) 427 if v: 428 self.s[k] = v 429 430 def ParseBackground(self, k, v): 431 for each in v.split(' '): 432 each = each.replace(' ', '').lower() 433 if each.find('url(') != -1: 434 self.ParseBackgroundImage(None, each) 435 elif each.find('repeat') != -1: 436 self.ParseBackgroundRepeat(None, each) 437 elif v: 438 color = ParseHTMLColor(each, 1, error=1) 439 if color: 440 self.s['background-color'] = color 441 442 def ParseBackgroundRepeat(self, k, v): 443 if v in ('repeat', 'repeat-x', 'repeat-y', 'no-repeat'): 444 self.s['background-repeat'] = v 445 446 def ParseBackgroundImage(self, k, v): 447 if v and v.startswith('url(') and v.endswith(')'): 448 self.s['background-image'] = v[4:-1] 449 450 def ParseBackgroundAttachment(self, k, v): 451 if v in ('fixed', 'scroll'): 452 self.s['background-attachment'] = v 453 454 def ParseBackgroundPosition(self, k, v): 455 if v: 456 v = v.split(' ') 457 if len(v) in (2, 4): 458 self.ParseAbsSize('background-image-left', v[0]) 459 self.ParseAbsSize('background-image-top', v[1]) 460 if len(v) == 4: 461 self.ParseAbsSize('background-image-width', v[2]) 462 self.ParseAbsSize('background-image-height', v[3]) 463 self.s['background-position'] = [] 464 for i in v: 465 if i in ('top', 'center', 'middle', 'bottom', 'left', 'right'): 466 self.s['background-position'].append(i) 467 468 def ParseAbsSize(self, k, v): 469 if v: 470 v = self.ParseStyleNum(v) 471 if v: 472 self.s[k] = int(v) 473 474 def ParsePosAbsSize(self, k, v): 475 if v: 476 v = self.ParseStyleNum(v) 477 if v: 478 if type(v) == int: 479 self.s[k] = max(v, 0) 480 else: 481 self.s[k] = v 482 483 def ParseVerticalAlign(self, k, v): 484 if v in self.vertStyles: 485 self.s['vertical-align'] = self.vertStyles[v] 486 487 def ParseHorizontalAlign(self, k, v): 488 if v in ('left', 'right', 'center'): 489 self.s['horizontal-align'] = v 490 491 def ParsePosition(self, k, v): 492 if v in ('absolute', 'fixed', 'relative', 'static'): 493 self.s['position'] = v 494 495 def ParseFloat(self, k, v): 496 if v in ('left', 'right'): 497 self.s['float'] = v 498 elif v == 'none': 499 self.s['float'] = None 500 elif v == 'inherit' and 'float' in self.s: 501 del self.s['float'] 502 503 fontSize = {'small': 8, 504 'x-small': 7, 505 'xx-small': 6, 506 'smaller': 8, 507 'medium': 10, 508 'large': 12, 509 'larger': 14, 510 'x-large': 20} 511 vertStyles = {'top': 0, 512 'middle': 1, 513 'bottom': 2, 514 'baseline': 3, 515 'sub': 4, 516 'super': 5} 517 borderStyles = {'none': None, 518 'hidden': 0, 519 'solid': 1, 520 'groove': 2, 521 'ridge': 2, 522 'inset': 2, 523 'outset': 2} 524 borderWidths = {'thin': 1, 525 'medium': 2, 526 'thick': 3} 527 borderCollapse = {'seperate': 0, 528 'collapse': 1} 529 absStyles = ('text-indent', 'margin-left', 'margin-right', 'margin-top', 'margin-bottom', 'padding-left', 'padding-right', 'padding-top', 'padding-bottom', 'border-left-width', 'border-right-width', 'border-top-width', 'border-bottom-width', 'letter-spacing', 'word-spacing', 'line-height') 530 styleDict = {'text-indent': 'ParseAbsSize', 531 'margin-left': 'ParsePosAbsSize', 532 'margin-right': 'ParsePosAbsSize', 533 'margin-top': 'ParsePosAbsSize', 534 'margin-bottom': 'ParsePosAbsSize', 535 'padding-left': 'ParsePosAbsSize', 536 'padding-right': 'ParsePosAbsSize', 537 'padding-top': 'ParsePosAbsSize', 538 'padding-bottom': 'ParsePosAbsSize', 539 'border-left-width': 'ParseBorderWidth', 540 'border-right-width': 'ParseBorderWidth', 541 'border-top-width': 'ParseBorderWidth', 542 'border-bottom-width': 'ParseBorderWidth', 543 'border-left-style': 'ParseBorderStyles', 544 'border-right-style': 'ParseBorderStyles', 545 'border-top-style': 'ParseBorderStyles', 546 'border-bottom-style': 'ParseBorderStyles', 547 'letter-spacing': 'ParseAbsSize', 548 'word-spacing': 'ParseAbsSize', 549 'line-height': 'ParsePosAbsSize', 550 'font-size': 'ParseFontSize', 551 'font-weight': 'ParseFontWeight', 552 'font-style': 'ParseFontStyle', 553 'text-decoration': 'ParseTextDecoration', 554 'text-align': 'ParseTextAlign', 555 'font': 'ParseFont', 556 'margin': 'ParseMargin', 557 'padding': 'ParsePadding', 558 'border-width': 'ParseBorderWidth', 559 'border-color': 'ParseBorderColor', 560 'border-style': 'ParseBorderStyle', 561 'border': 'ParseBorder', 562 'border-left': 'ParseBorders', 563 'border-right': 'ParseBorders', 564 'border-top': 'ParseBorders', 565 'border-bottom': 'ParseBorders', 566 'color': 'ParseColor', 567 'link-color': 'ParseColor', 568 'alink-color': 'ParseColor', 569 'vlink-color': 'ParseColor', 570 'background': 'ParseBackground', 571 'background-color': 'ParseColor', 572 'border-left-color': 'ParseColor', 573 'border-right-color': 'ParseColor', 574 'border-top-color': 'ParseColor', 575 'border-bottom-color': 'ParseColor', 576 'vertical-align': 'ParseVerticalAlign', 577 'align': 'ParseHorizontalAlign', 578 'background-repeat': 'ParseBackgroundRepeat', 579 'background-image': 'ParseBackgroundImage', 580 'background-image-color': 'ParseColor', 581 'background-image-width': 'ParseAbsSize', 582 'background-image-height': 'ParseAbsSize', 583 'background-image-left': 'ParseAbsSize', 584 'background-image-top': 'ParseAbsSize', 585 'background-attachment': 'ParseBackgroundAttachment', 586 'background-position': 'ParseBackgroundPosition', 587 'border-collapse': 'ParseBorderCollapse', 588 'position': 'ParsePosition', 589 'float': 'ParseFloat', 590 'left': 'ParseAbsSize', 591 'right': 'ParseAbsSize', 592 'top': 'ParseAbsSize', 593 'bottom': 'ParseAbsSize', 594 'width': 'ParsePosAbsSize', 595 'min-width': 'ParsePosAbsSize', 596 'max-width': 'ParsePosAbsSize', 597 'height': 'ParsePosAbsSize', 598 'min-height': 'ParsePosAbsSize', 599 'max-height': 'ParsePosAbsSize'}