parserbase.py (78144B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\carbonui\control\editutils\parserbase.py 2 import _weakref 3 import sys 4 import types 5 import urllib2 6 import blue 7 import carbonui.const as uiconst 8 import eveLocalization 9 import eveformat 10 import log 11 import stackless 12 import telemetry 13 from carbonui import fontflags 14 from carbonui.control.editutils import parser 15 from carbonui.control.editutils.css import css as Css 16 from carbonui.control.browser import browserutil 17 from carbonui.control.inlines import get_inline_deco_class 18 from carbonui.control.scrollentries import ScrollEntryNode, SE_TextlineCore 19 from carbonui.primitives.container import Container 20 from carbonui.text.color import TextColor 21 from carbonui.util.bunch import Bunch 22 from carbonui.util.various_unsorted import GetBrowser, ParseHTMLColor, GetTrace 23 from carbonui.control.editutils.parser import Parser, HtmlOptionalClose, HtmlAutoClose 24 from carbonui.control.label import LabelOverride as Label 25 from carbonui.uicore import uicore 26 from eveprefs import prefs 27 28 class ParserBaseCore(object): 29 __guid__ = 'eveparser.ParserBaseCore' 30 __update_on_reload__ = 1 31 32 def Prepare(self): 33 self.getWidths = 0 34 self.minWidth = 0 35 self.totalWidth = 0 36 self.xmargin = 8 37 self.tagdepth = 0 38 self.readonly = 1 39 self.htmldebug = 0 40 self.glyphString = None 41 self.plainload = 0 42 self.title = None 43 self.charset = 'cp1252' 44 self.textbuffers = [] 45 self.css = Css() 46 self.sr.parser = Parser(self) 47 self.sr.parser.notify_load = self.Loading 48 self.sr.window = None 49 if self.sr.Get('content', None): 50 parent = self.sr.content.parent 51 idx = self.sr.content.parent.children.index(self.sr.content) 52 else: 53 parent = self 54 idx = -1 55 self.sr.underlays_content = Container(parent=parent, name='underlays_content', align=uiconst.TOPLEFT, idx=idx + 1) 56 self.sr.overlays_content = Container(parent=parent, name='overlays_content', align=uiconst.TOPLEFT, idx=idx) 57 self.sr.background = Container(parent=parent, name='background') 58 self.sr.cacheContainer = Container(name='cacheContainer', parent=self, state=uiconst.UI_HIDDEN) 59 60 def Loading(self, *args): 61 if self.sr.window and hasattr(self.sr.window, 'ShowLoadBar'): 62 self.sr.window.ShowLoadBar(*args) 63 64 def SetStatus(self, *args): 65 if self.sr.window and hasattr(self.sr.window, 'ShowStatus'): 66 self.sr.window.ShowStatus(*args) 67 68 def ShowHint(self, hint = None): 69 if self.sr.window and hasattr(self.sr.window, 'ShowHint'): 70 self.sr.window.ShowHint(hint) 71 72 @telemetry.ZONE_METHOD 73 def OnStartTag(self, tag, attrs): 74 if not self or not getattr(self, 'sr', None): 75 return 76 closeTags = HtmlOptionalClose.get(tag, ()) 77 if self.attrStack[-1]['tag'] in closeTags: 78 self.OnEndTag(self.attrStack[-1]['tag']) 79 if tag == 'table' and self.sr.table: 80 self.blockBufferClose += 1 81 elif tag == 'div' and self.divs: 82 self.divs += 1 83 if self.attrStack[-1]['bufferStack'] is not None: 84 attrs.form = self.sr.form 85 self.attrStack[-1]['bufferStack'].append(('OnStartTag', (tag, attrs))) 86 return 87 f = getattr(self, 'OnStart_%s' % tag, None) 88 if f: 89 f(attrs) 90 else: 91 self.OnStart_default(tag, attrs) 92 93 @telemetry.ZONE_METHOD 94 def OnEndTag(self, tag): 95 if not self or not getattr(self, 'sr', None): 96 return 97 if self.attrStack[-1]['bufferStack'] is not None: 98 if tag == 'table' and self.sr.table: 99 self.blockBufferClose = max(0, self.blockBufferClose - 1) 100 if tag == 'div' and self.divs: 101 self.divs = max(0, self.divs - 1) 102 if (tag in 'div' and self.sr.table is None or tag in ('td', 'th', 'tr') and self.sr.table) and not self.blockBufferClose and not self.divs: 103 self.attrStack[-1]['bufferStack'].append(('FlushBuffer', (None,))) 104 else: 105 self.attrStack[-1]['bufferStack'].append(('OnEndTag', (tag,))) 106 return 107 closeTags = HtmlAutoClose.get(tag, ()) 108 if self.attrStack[-1]['tag'] != tag and self.attrStack[-1]['tag'] in closeTags: 109 self.OnEndTag(self.attrStack[-1]['tag']) 110 f = getattr(self, 'OnEnd_%s' % tag, None) 111 if f: 112 f() 113 else: 114 self.OnEnd_default(tag) 115 116 def OnData(self, data, *args): 117 if not self or not getattr(self, 'sr', None): 118 return 119 if self.attrStack[-1]['bufferStack'] is not None: 120 self.attrStack[-1]['bufferStack'].append(('OnData', (data,))) 121 return 122 tag, attrs = self.attrStack[-1]['tag'], self.attrStack[-1]['attr'] 123 data = self.sr.buffer + data 124 self.sr.buffer = u'' 125 if not data: 126 return 127 f = getattr(self, 'OnData_%s' % (tag or ''), None) 128 if f: 129 f(data, attrs) 130 elif isinstance(data, basestring): 131 data = self.StripText(data) 132 if data: 133 self.AddTextToBuffer(data, fromW='Ondata') 134 else: 135 log.LogWarn('NonString data outside any tag', repr(data)) 136 137 def StripText(self, text): 138 text = text.replace('\n', u' ').replace('\r', u' ').replace('\t', u' ') 139 if self.plainload: 140 return text 141 while text.find(u' ') != -1: 142 text = text.replace(u' ', u' ') 143 144 if not self.textbuffer: 145 text = text.lstrip() 146 return text 147 148 @telemetry.ZONE_METHOD 149 def OnStart_default(self, tag, attrs, defaultStyles = None): 150 self.attrStack.append(self.attrStack[-1].copy()) 151 self.attrStack[-1]['tag'] = tag 152 self.attrStack[-1]['attr'] = attrs 153 self.SetStyles(defaultStyles) 154 self.ApplyCSS(tag, attrs) 155 self.ParseStdStyles(attrs) 156 157 @telemetry.ZONE_METHOD 158 def OnEnd_default(self, tag): 159 idx = -1 160 while self.attrStack[idx]['tag'] != tag: 161 log.LogInfo('Incorrect order of tags in html string, trying to close', tag, 'but lasttag is', self.attrStack[idx]['tag']) 162 idx -= 1 163 if len(self.attrStack) <= abs(idx): 164 log.LogInfo('Reached begining of attrStack while trying to close tag:', tag) 165 return 166 167 self.attrStack.pop(idx) 168 if len(self.attrStack) > abs(idx): 169 self.attrStack[idx - 1]['lasttag'] = tag 170 171 def FlushBuffer(self, fakeLines = None): 172 data = self.StripText(self.sr.buffer) 173 self.sr.buffer = u'' 174 if data: 175 self.AddTextToBuffer(data) 176 if self.textbuffer: 177 obj = self.textbuffer[-1] 178 if obj.type == '<text>' and obj.letters == u' ': 179 self.textbuffer.pop(-1) 180 if self.getWidths: 181 self.BreakLines(self.textbuffer) 182 self.textbuffer = [] 183 return 184 if fakeLines is not None: 185 _lines = self.BuildEntries(fakeLines, self.textbuffer, self.attrStack[-1]) 186 self.textbuffers.append(([self.GetTextObject('')], self.attrStack[-1])) 187 self.AddLines(_lines) 188 return 189 if len(self.textbuffer): 190 self.textbuffers.append((self.textbuffer, self.attrStack[-1])) 191 lines = self.BreakLines(self.textbuffer) 192 scrollNodes = self.BuildEntries(lines, self.textbuffer, self.attrStack[-1]) 193 self.AddLines(scrollNodes) 194 self.textbuffer = [] 195 196 def GetFontFlagFromStack(self): 197 fontFlag = 0 198 if self.attrStack[-1]['font-style'] == 'i': 199 fontFlag |= fontflags.i 200 if self.attrStack[-1]['font-weight'] == 'b' or self.a: 201 fontFlag |= fontflags.b 202 if 'u' in self.attrStack[-1]['text-decoration'] or self.a: 203 fontFlag |= fontflags.u 204 return fontFlag 205 206 @telemetry.ZONE_METHOD 207 def _LoadHTML(self, htmlstr): 208 if self.destroyed: 209 return 210 self.title = None 211 self.sr.htmlstr = htmlstr 212 self._Reset() 213 if self.destroyed: 214 return 215 self.sr.parser.reset() 216 self.sr.parser.feed(self.sr.htmlstr) 217 if self.destroyed: 218 return 219 self.sr.parser.close() 220 221 def LoadBuffer(self, buff, getWidths = 0, setWidth = None, singleWordMax = None): 222 self._Reset() 223 self.getWidths = getWidths 224 self.minWidth = 0 225 self.totalWidth = 0 226 self.singleWordMax = singleWordMax or self.sr.browser and self.sr.browser.GetContentWidth() - self.sr.browser.xmargin * 2 227 if setWidth is not None: 228 self.sr.width = setWidth 229 self.attrStack[-1]['background-color'] = None 230 for func, args in buff: 231 if hasattr(self, func): 232 getattr(self, func, None)(*args) 233 234 if self.getWidths: 235 return 236 237 @telemetry.ZONE_METHOD 238 def _Reset(self): 239 for form in self.sr.get('forms', []): 240 if form is not None: 241 for attrs, wnd in form.fields: 242 wnd.Close() 243 244 self.textbuffers = [] 245 self.textbuffer = [] 246 self.attrStack = [] 247 self.areamap = [] 248 self.a = None 249 self.blockquote = 0 250 self.list = 0 251 self.contentHeight = 0 252 self.contentWidth = 0 253 self.blockBufferClose = 0 254 self.divs = 0 255 self.sr.table = None 256 self.sr.buffer = '' 257 self.sr.listIndex = [] 258 self.sr.overlays = [] 259 self.sr.underlays = [] 260 self.sr.lines = [] 261 self.sr.forms = [] 262 self.sr.anchor = None 263 self.sr.form = None 264 self.sr.select = None 265 attrEntry = {} 266 attrEntry['font-size'] = getattr(self, 'defaultFontSize', 12) 267 attrEntry['font-style'] = '' 268 attrEntry['font-weight'] = '' 269 attrEntry['basefontsize'] = 3 270 attrEntry['text-decoration'] = '' 271 attrEntry['color'] = getattr(self, 'defaultFontColor', TextColor.NORMAL.rgba) 272 attrEntry['background-color'] = None 273 attrEntry['background-image'] = None 274 attrEntry['background-image-color'] = (1.0, 1.0, 1.0, 1.0) 275 attrEntry['background-image-width'] = 0 276 attrEntry['background-image-height'] = 0 277 attrEntry['background-image-left'] = 0 278 attrEntry['background-image-top'] = 0 279 attrEntry['background-repeat'] = 'repeat' 280 attrEntry['background-attachment'] = 'scroll' 281 attrEntry['line-height'] = 12 282 attrEntry['align'] = None 283 attrEntry['text-align'] = None 284 attrEntry['text-indent'] = 0 285 attrEntry['vertical-align'] = 3 286 attrEntry['letter-spacing'] = 0 287 attrEntry['word-spacing'] = 0 288 attrEntry['line-height'] = None 289 attrEntry['margin-left'] = 0 290 attrEntry['margin-right'] = 0 291 attrEntry['margin-top'] = 0 292 attrEntry['margin-bottom'] = 0 293 attrEntry['padding-left'] = 0 294 attrEntry['padding-right'] = 0 295 attrEntry['padding-top'] = 0 296 attrEntry['padding-bottom'] = 0 297 attrEntry['border-left-width'] = 0 298 attrEntry['border-right-width'] = 0 299 attrEntry['border-top-width'] = 0 300 attrEntry['border-bottom-width'] = 0 301 attrEntry['border-left-color'] = (1.0, 1.0, 1.0, 1.0) 302 attrEntry['border-right-color'] = (1.0, 1.0, 1.0, 1.0) 303 attrEntry['border-top-color'] = (1.0, 1.0, 1.0, 1.0) 304 attrEntry['border-bottom-color'] = (1.0, 1.0, 1.0, 1.0) 305 attrEntry['border-left-style'] = None 306 attrEntry['border-right-style'] = None 307 attrEntry['border-top-style'] = None 308 attrEntry['border-bottom-style'] = None 309 attrEntry['border-collapse'] = 0 310 attrEntry['link-color'] = (1.0, 0.65, 0.0, 1.0) 311 attrEntry['alink-color'] = None 312 attrEntry['vlink-color'] = (1.0, 0.8, 0.0, 1.0) 313 attrEntry['class'] = 'default' 314 attrEntry['tag'] = 'default' 315 attrEntry['lasttag'] = '' 316 attrEntry['attr'] = None 317 attrEntry['position'] = None 318 attrEntry['float'] = None 319 attrEntry['id'] = None 320 attrEntry['bufferStack'] = None 321 attrEntry['pstart'] = 0 322 attrEntry['background-position'] = [] 323 self.attrStack.append(attrEntry) 324 self.css.Reset() 325 self.Reset() 326 327 def GetLinespace(self): 328 return int(self.attrStack[-1]['line-height'] or self.attrStack[-1]['font-size']) 329 330 def Reset(self, *args): 331 pass 332 333 def LoadBodyAttrs(self, *args): 334 pass 335 336 def BuildEntries(self, lines, stack, attrs): 337 if stack and stack[0].type == '<hr>': 338 from carbonui.control.edit_components import hr as SE_hr 339 data = {'decoClass': SE_hr, 340 'leftmargin': stack[0].lpush, 341 'rightmargin': stack[0].rpush, 342 'attrs': stack[0].attrs, 343 'bgcolor': None} 344 se = ScrollEntryNode(**data) 345 return [se] 346 _lines = [] 347 for each in lines: 348 entry = self.GetTextEntry(attrs, stack, *each) 349 _lines.append(entry) 350 351 return _lines 352 353 def GetFirstLine(self): 354 lpush, rpush = self.GetMargins(0, self.GetLinespace()) 355 textObj = self.GetTextObject('') 356 self.AddObjectToBuffer(textObj) 357 self.textbuffers.append(([textObj], self.attrStack[-1])) 358 return self.GetTextEntry(self.attrStack[-1], [textObj], lpush=lpush, rpush=rpush) 359 360 def GetTextEntry(self, attrs, stack, glyphString = None, inlines = None, links = None, lpush = 0, rpush = 0, bBox = None, baseHeight = 12, baseLine = 10, abspos = 0): 361 lineWidth = 0 362 if bBox is not None: 363 lineWidth = bBox.Width() 364 data = {'decoClass': SE_TextlineCore, 365 'glyphString': glyphString or [], 366 'inlines': inlines or [], 367 'pos': abspos, 368 'stack': stack, 369 'lpush': lpush, 370 'rpush': rpush, 371 'maxBaseHeight': baseHeight, 372 'maxBaseLine': baseLine, 373 'bBox': bBox, 374 'lineWidth': lineWidth, 375 'align': attrs['text-align'], 376 'links': links or [], 377 'SelectionHandler': self, 378 'URLHandler': self.sr.window} 379 entry = ScrollEntryNode(**data) 380 self.contentHeight = max(self.contentHeight, self.contentHeight + baseHeight) 381 self.contentWidth = max(self.ReverseScaleDpi(lineWidth) + lpush + rpush, self.contentWidth) 382 return entry 383 384 def InsertLines(self, stack, abspos, idx, attrs): 385 lines = self.BreakLines(stack, abspos=abspos) 386 nodes = self.BuildEntries(lines, stack, attrs) 387 self.AddNodes(idx, nodes) 388 return nodes 389 390 def Simplify(self, quiet = 0): 391 rem = [] 392 for idx, (stack, attrs) in enumerate(self.textbuffers): 393 oldSize = None 394 oldColor = None 395 oldFlags = None 396 oldA = None 397 i = 0 398 while i < len(stack): 399 obj = stack[i] 400 if obj.type == '<text>': 401 if len(obj.letters) == 0 and len(stack) > 1: 402 stack.pop(i) 403 continue 404 if obj.a: 405 obj.color = (1.0, 0.65, 0.0, 1.0) 406 obj.lcolor = (1.0, 0.65, 0.0, 1.0) 407 obj.fontFlags |= fontflags.b | fontflags.u 408 if obj.fontSize == oldSize and obj.fontFlags == oldFlags and obj.color == oldColor and obj.a == oldA and stack[i - 1].type == '<text>': 409 stack[i - 1].letters += obj.letters 410 stack.pop(i) 411 continue 412 oldSize = obj.fontSize 413 oldColor = obj.color 414 oldFlags = obj.fontFlags 415 oldA = obj.a 416 i += 1 417 418 if not len(stack): 419 rem.append(idx) 420 421 for idx in rem: 422 self.textbuffers.pop(idx) 423 424 def AddLines(self, lines): 425 self.sr.lines += lines 426 427 def AddTextToBuffer(self, text, width = None, fromW = None): 428 self.AddObjectToBuffer(self.GetTextObject(text, width)) 429 430 @telemetry.ZONE_METHOD 431 def GetTextObject(self, text, width = None): 432 obj = Bunch() 433 obj.spacing = self.attrStack[-1]['letter-spacing'] 434 obj.a = self.a 435 if obj.a: 436 visited = settings.public.ui.VisitedURLs or [] 437 obj.color = [self.attrStack[-1]['link-color'], self.attrStack[-1]['vlink-color'] or self.attrStack[-1]['link-color']][obj.a.href in visited] 438 obj.lcolor = self.attrStack[-1]['alink-color'] or obj.color 439 else: 440 obj.color = self.attrStack[-1]['color'] 441 obj.fontFlags = self.GetFontFlagFromStack() 442 obj.valign = self.attrStack[-1]['vertical-align'] 443 obj.type = '<text>' 444 obj.width = width 445 if type(text) != types.UnicodeType: 446 text = unicode(text, 'utf-8', 'replace') 447 obj.letters = text 448 obj.wordSpacing = self.attrStack[-1]['word-spacing'] 449 obj.letterSpacing = self.attrStack[-1]['letter-spacing'] 450 obj.fontSize = obj.fontsize = self.attrStack[-1]['font-size'] 451 return obj 452 453 @telemetry.ZONE_METHOD 454 def AddObjectToBuffer(self, obj): 455 obj.depth = len(self.attrStack) - 1 456 obj.tags = [] 457 obj.tagattrs = [] 458 while obj.depth > self.tagdepth: 459 self.tagdepth += 1 460 obj.tags.append(self.attrStack[self.tagdepth]['tag']) 461 obj.tagattrs.append(self.attrStack[self.tagdepth]['attr']) 462 463 self.textbuffer.append(obj) 464 465 @telemetry.ZONE_METHOD 466 def GetWidthInBuffer(self): 467 totalWidth = 0 468 for obj in self.textbuffer: 469 if obj.type == '<overlay>': 470 continue 471 elif obj.type == '<text>': 472 textWidth, textHeight = Label.MeasureTextSize(obj.letters.replace(u'\xa0', u' '), fontsize=obj.fontSize, letterspace=obj.letterSpacing, bold=bool(obj.fontFlags & fontflags.b), italic=bool(obj.fontFlags & fontflags.i)) 473 totalWidth += textWidth 474 elif obj.type in '<table><img><input>': 475 totalWidth += obj.width 476 477 return totalWidth 478 479 @telemetry.ZONE_METHOD 480 def NewLine(self): 481 self.FlushBuffer() 482 if type(self.attrStack[-1]['line-height']) == str and self.attrStack[-1]['line-height'].endswith('%'): 483 self.attrStack[-1]['line-height'] = int(float(self.attrStack[-1]['line-height'][:-1]) * self.attrStack[-1]['font-size'] / 100.0) 484 linespace = self.attrStack[-1]['line-height'] or self.attrStack[-1]['font-size'] 485 lpush, rpush = self.GetMargins(self.contentHeight, self.contentHeight + linespace) 486 self.FlushBuffer([([], 487 [], 488 [], 489 lpush, 490 rpush, 491 None, 492 linespace, 493 0, 494 0)]) 495 496 @telemetry.ZONE_METHOD 497 def GetMargins(self, fromY, toY): 498 blockquote = self.blockquote * 32 499 list = self.list * 32 500 if self.destroyed: 501 return (0, 0) 502 contentWidth = self.GetContentWidth() 503 self.attrStack[-1]['margin-left'] = self.GetPercent(self.attrStack[-1]['margin-left'], contentWidth) 504 self.attrStack[-1]['margin-right'] = self.GetPercent(self.attrStack[-1]['margin-right'], contentWidth) 505 lpush = self.attrStack[-1]['margin-left'] + self.attrStack[-1]['padding-left'] + self.attrStack[-1]['border-left-width'] + blockquote + list + self.xmargin 506 rpush = self.attrStack[-1]['margin-right'] + self.attrStack[-1]['padding-right'] + self.attrStack[-1]['border-right-width'] + blockquote + self.xmargin 507 for overlay, attrs, x, y in self.sr.overlays: 508 if overlay.state == uiconst.UI_HIDDEN: 509 continue 510 w = overlay.width 511 h = overlay.height 512 y = attrs.top 513 if toY > y and fromY < y + h: 514 if attrs.align == 'left': 515 lpush = max(lpush, getattr(attrs, 'left', 0) + w) 516 elif attrs.align == 'right': 517 rpush = max(rpush, getattr(attrs, 'left', 0) + w) 518 519 return (lpush, rpush) 520 521 @telemetry.ZONE_METHOD 522 def StackPosToObjPos(self, stack, abspos, allobj = 0): 523 objpos = 0 524 pos = abspos 525 for obj in stack: 526 if not obj.letters: 527 if allobj == 1 and pos < 1: 528 objpos = stack.index(obj) 529 break 530 pos -= 1 531 continue 532 if pos < len(obj.letters): 533 objpos = stack.index(obj) 534 break 535 pos -= len(obj.letters) 536 else: 537 return (0, abspos) 538 539 return (objpos, pos) 540 541 @telemetry.ZONE_METHOD 542 def GetWidths(self, textbuffer): 543 s = uicore.font.GetGlyphString() 544 s.shadow = None 545 for obj in textbuffer: 546 fParams = obj 547 fParams.fontsize = obj.fontSize 548 if obj.type == '<overlay>': 549 continue 550 elif obj.type == '<text>': 551 if not obj.letters: 552 continue 553 fParams = obj 554 fParams.fontsize = obj.fontSize 555 fParams.letterspace = obj.letterSpacing 556 fParams.wordspace = obj.wordSpacing 557 fParams.bold = bool(obj.fontFlags & fontflags.b) 558 fParams.italic = bool(obj.fontFlags & fontflags.i) 559 fParams.underline = bool(obj.fontFlags & fontflags.u) 560 s.Append(fParams, obj.letters) 561 elif obj.type in ('<table>', '<img>'): 562 s.AddSpace(fParams, obj.control.width) 563 else: 564 s.AddSpace(fParams, obj.width) 565 566 w = 0 567 wordWidth = 0 568 for t in s: 569 if t[4] == u' ': 570 maxWidth = max(wordWidth, w) 571 w = 0 572 else: 573 w += t[0] 574 if w > self.singleWordMax: 575 w = 0 576 continue 577 wordWidth = max(wordWidth, w) 578 579 self.minWidth = max(self.minWidth, wordWidth) 580 self.totalWidth = max(self.totalWidth, s.width) 581 582 @telemetry.ZONE_METHOD 583 def GetLine(self, textbuffer, startpos, recurse): 584 s = self.glyphString 585 if s: 586 s.Reset() 587 else: 588 s = uicore.font.GetGlyphString() 589 s.shadow = None 590 self.glyphString = s 591 bufnum, pos = self.StackPosToObjPos(textbuffer, startpos, allobj=1) 592 inlines = [] 593 links = [] 594 ustart = astart = None 595 wordlen = 0 596 aa = None 597 acolor = None 598 activeLinkText = None 599 curr = stackless.getcurrent() 600 onMainThread = curr.is_main 601 contentWidth = self.GetContentWidth() 602 startTime = blue.os.GetWallclockTime() 603 while contentWidth <= 0 and not onMainThread and not self.destroyed: 604 blue.synchro.Yield() 605 contentWidth = self.GetContentWidth() 606 if blue.os.TimeDiffInMs(startTime, blue.os.GetWallclockTime()) > 500: 607 contentWidth = 256 608 warning = 'Someone is trying to load text into Edit which has 0 or less width. This controls visible state under desktop is: ' + str(self.IsVisible()) + '. Edit location: ' + GetTrace(self, trace='', div='/') 609 log.LogWarn(warning) 610 break 611 612 for i in xrange(bufnum, len(textbuffer)): 613 obj = textbuffer[i] 614 fParams = obj 615 fParams.fontsize = self.attrStack[-1]['font-size'] 616 if obj.type == '<overlay>': 617 obj.attrs.top = self.contentHeight 618 obj.overlay.top = self.contentHeight 619 obj.overlay.state = uiconst.UI_NORMAL 620 if obj.attrs.Get('align', None) == 'right': 621 obj.overlay.left = contentWidth - obj.overlay.width - obj.attrs.left 622 if not obj.overlay.loaded: 623 obj.overlay.Load() 624 elif obj.type == '<text>': 625 fParams.fontsize = fontsize = obj.fontSize 626 fParams.letterspace = obj.letterSpacing 627 fParams.wordspace = obj.wordSpacing 628 fParams.underline = bool(obj.fontFlags & fontflags.u) 629 fParams.bold = bool(obj.fontFlags & fontflags.b) 630 fParams.italic = bool(obj.fontFlags & fontflags.i) 631 if astart is not None and aa != obj.a: 632 links.append((astart, 633 uicore.ReverseScaleDpi(s.width), 634 aa, 635 acolor, 636 activeLinkText)) 637 astart = None 638 activeLinkText = None 639 if obj.a and astart is None: 640 astart = uicore.ReverseScaleDpi(s.width) 641 aa = obj.a 642 acolor = obj.lcolor 643 activeLinkText = obj.letters 644 if pos: 645 s.Append(fParams, obj.letters[pos:]) 646 pos = 0 647 else: 648 s.Append(fParams, obj.letters) 649 elif obj.type in '<table><img><input>': 650 objectWidth = obj.width 651 obj.inlineWidth = obj.control.width 652 obj.inlineHeight = obj.control.height 653 inlines.append((obj, uicore.ReverseScaleDpi(s.GetWidth()))) 654 s.AddSpace(fParams, objectWidth) 655 else: 656 s.AddSpace(fParams, obj.width) 657 658 if astart is not None: 659 links.append((astart, 660 uicore.ReverseScaleDpi(s.width), 661 aa, 662 acolor, 663 activeLinkText)) 664 rpush, lpush = self.GetMargins(self.contentHeight + self.batchHeight, self.contentHeight + self.batchHeight + self.GetLinespace()) 665 contentWidth = self.GetContentWidth() 666 text = s.GetText() 667 wpl = eveLocalization.WrapPointList(unicode(text), session.languageID) 668 wrapPoints = wpl.GetLinebreakPoints() 669 scaledWrapWidth = 0 670 scaledGlyphStringPosition = 0 671 lastWrapIndex = 0 672 lastLineWrapIndex = 0 673 lastLineXPosition = 0 674 maxwidth = contentWidth - (lpush + rpush) 675 i = 0 676 for t in s: 677 advance = t[0] 678 if uicore.ReverseScaleDpi(scaledWrapWidth + advance) > maxwidth: 679 if lastWrapIndex == lastLineWrapIndex: 680 lastWrapIndex = i 681 line = uicore.font.GetGlyphString() 682 line.shadow = None 683 line += s[lastLineWrapIndex:lastWrapIndex] 684 scaledNewLineWidth = sum([ each[0] for each in line ]) 685 yield (rpush, 686 lpush, 687 startpos + lastLineWrapIndex, 688 line, 689 self.GetOthers(inlines, links, uicore.ReverseScaleDpi(scaledGlyphStringPosition), uicore.ReverseScaleDpi(scaledNewLineWidth), fParams)) 690 if not recurse: 691 return 692 lastLineWrapIndex = lastWrapIndex 693 scaledWrapWidth -= scaledNewLineWidth 694 scaledGlyphStringPosition += scaledNewLineWidth 695 rpush, lpush = self.GetMargins(self.contentHeight + self.batchHeight, self.contentHeight + self.batchHeight + self.GetLinespace()) 696 maxwidth = contentWidth - (lpush + rpush) 697 if i in wrapPoints: 698 lastWrapIndex = i 699 scaledWrapWidth += advance 700 i += 1 701 702 if i > lastLineWrapIndex: 703 line = uicore.font.GetGlyphString() 704 line.shadow = None 705 line += s[lastLineWrapIndex:] 706 scaledNewLineWidth = sum([ each[0] for each in line ]) 707 yield (rpush, 708 lpush, 709 startpos + lastLineWrapIndex, 710 line, 711 self.GetOthers(inlines, links, uicore.ReverseScaleDpi(scaledGlyphStringPosition), uicore.ReverseScaleDpi(scaledNewLineWidth), fParams)) 712 713 @telemetry.ZONE_METHOD 714 def GetOthers(self, inlines, links, x, width, fParam): 715 endpos = x + width 716 newinlines = [] 717 for obj, pos in inlines: 718 if x <= pos < endpos: 719 newinlines.append((obj, pos - x)) 720 721 newlinks = [] 722 for s, e, aa, color, linkText in links: 723 if not aa.href: 724 continue 725 e = min(e - x, width + s) 726 s = max(s - x, 0) 727 if s < e and s < width: 728 link = Bunch() 729 link.left = s 730 link.hint = aa.alt or '' 731 link.url = aa.href 732 link.width = e - s 733 link.alink_color = color 734 link.linkText = linkText 735 if not aa.href.startswith('showinfo:'): 736 link.draggable = False 737 newlinks.append(link) 738 739 return (newinlines, newlinks) 740 741 @telemetry.ZONE_METHOD 742 def BreakLines(self, textbuffer, batchHeight = 0, abspos = 0, recurse = 1): 743 if self.getWidths: 744 self.GetWidths(textbuffer) 745 return 746 if not len(textbuffer): 747 return [] 748 self.batchHeight = batchHeight 749 lines = [] 750 for rpush, lpush, startpos, s, others in self.GetLine(textbuffer, abspos, recurse): 751 inlines, links = others 752 bBox = s.GetBBox() 753 baseHeight = max(uicore.ReverseScaleDpi(s.baseHeight), self.attrStack[-1]['line-height']) or self.attrStack[-1]['font-size'] 754 baseLine = uicore.ReverseScaleDpi(s.baseLine) 755 for obj, pos in inlines: 756 objectWidth = obj.width 757 objectHeight = obj.height 758 obj.inlineWidth = obj.control.width 759 obj.inlineHeight = obj.control.height 760 valign = getattr(obj, 'valign', parser.ALIGNBASELINE) 761 if valign in (parser.ALIGNTOP, parser.ALIGNSUB, parser.ALIGNSUPER): 762 baseHeight = max(baseHeight, objectHeight) 763 elif valign == parser.ALIGNMIDDLE: 764 baseLine = max(baseLine, max(baseHeight, objectHeight) / 2 - (baseHeight - baseLine) / 2) 765 baseHeight = max(baseHeight, objectHeight) 766 elif valign == parser.ALIGNBOTTOM: 767 diffBefore = baseHeight - baseLine 768 baseHeight = max(baseHeight, objectHeight) 769 baseLine = baseHeight - diffBefore 770 elif valign == parser.ALIGNBASELINE: 771 if objectHeight > baseLine: 772 diffBefore = baseHeight - baseLine 773 baseHeight = objectHeight + diffBefore 774 baseLine = objectHeight 775 776 lines.append((s, 777 inlines, 778 links, 779 rpush, 780 lpush, 781 bBox, 782 baseHeight, 783 baseLine, 784 startpos)) 785 self.batchHeight += baseHeight 786 787 return lines 788 789 def AddOverlay(self, attrs, x, y, lpush = None, rpush = None): 790 attrs.height = getattr(attrs, 'height', 1) or 1 791 if lpush is None or rpush is None: 792 lpush, rpush = self.GetMargins(y, y + attrs.height) 793 if attrs.align == 'right': 794 attrs.left = rpush + getattr(attrs, 'rightmargin', 0) 795 elif attrs.align == 'left': 796 attrs.left = lpush + getattr(attrs, 'leftmargin', 0) 797 else: 798 attrs.left = x + lpush 799 attrs.top = y 800 overlay = self.GetOverlay_Underlay(self.GetScrollEntry(attrs), self.sr.overlays_content) 801 self.sr.overlays.append((overlay, 802 attrs, 803 x, 804 y)) 805 return overlay 806 807 def GetScrollEntry(self, attrs): 808 decoClass = get_inline_deco_class(attrs.type) 809 return ScrollEntryNode(decoClass=decoClass, attrs=attrs) 810 811 def AddUnderlay(self, attrs, x, y): 812 attrs.left = x 813 attrs.top = y 814 underlay = self.GetOverlay_Underlay(self.GetScrollEntry(attrs), self.sr.underlays_content) 815 self.sr.underlays.append((underlay, 816 attrs, 817 x, 818 y)) 819 820 def AddFloater(self): 821 attrs = self.attrStack[-1]['attr'] 822 attrs.type = 'div' 823 attrs.content = self.attrStack[-1]['bufferStack'][:] 824 self.attrStack[-1]['bufferStack'] = None 825 attrs.top = getattr(attrs, 'top', self.contentHeight) 826 attrs.stack = self.attrStack[-1].copy() 827 floater = self.GetOverlay_Underlay(self.GetScrollEntry(attrs), self.sr.overlays_content) 828 floater.state = uiconst.UI_NORMAL 829 floater.SetOrder(0) 830 contentWidth = self.GetContentWidth() 831 if self.attrStack[-1]['float'] and not getattr(attrs, 'left', 0): 832 attrs.align = self.attrStack[-1]['float'] 833 y = attrs.top 834 lpush, rpush = self.GetMargins(-1, -1) 835 if lpush + floater.width + rpush <= contentWidth: 836 while True: 837 lpush, rpush = self.GetMargins(y, y + floater.height) 838 if lpush + floater.width + rpush < contentWidth: 839 break 840 y += floater.height 841 if y > 100000: 842 raise RuntimeError("OK, at this point it seems obviousI don't know how to render this one.I give up so I won't loop forever.") 843 844 floater.left = attrs.left = lpush 845 floater.top = attrs.top = y 846 self.contentHeight = max(self.contentHeight, floater.height) 847 self.contentWidth = max(self.contentWidth, floater.width) 848 self.sr.overlays.append((floater, 849 attrs, 850 getattr(attrs, 'left', 0), 851 attrs.top)) 852 self.blockBufferClose = 0 853 854 def GetOverlay_Underlay(self, data, container): 855 if getattr(data, 'attrs', None) and getattr(data.attrs, 'uiwindow', None): 856 overlay = data.attrs.uiwindow 857 overlay.SetParent(container) 858 else: 859 overlay = data.decoClass(parent=container) 860 overlay.state = uiconst.UI_HIDDEN 861 overlay.data = data 862 overlay.Startup(self) 863 overlay.loaded = 0 864 return overlay 865 866 def AddBorder(self, y, height): 867 if self.getWidths: 868 return 869 s = self.attrStack[-1] 870 if s['background-color'] or s['border-left-width'] != 0 or s['border-right-width'] != 0 or s['border-top-width'] != 0 or s['border-bottom-width'] != 0: 871 lpush, rpush = self.GetMargins(y, y + 1) 872 contentWidth = self.GetContentWidth() 873 attrs = Bunch() 874 attrs.bgcolor = s['background-color'] 875 attrs.lbstyle = s['border-left-style'] 876 attrs.rbstyle = s['border-right-style'] 877 attrs.tbstyle = s['border-top-style'] 878 attrs.bbstyle = s['border-bottom-style'] 879 attrs.lbcolor = s['border-left-color'] 880 attrs.rbcolor = s['border-right-color'] 881 attrs.tbcolor = s['border-top-color'] 882 attrs.bbcolor = s['border-bottom-color'] 883 attrs.lbwidth = s['border-left-width'] 884 attrs.rbwidth = s['border-right-width'] 885 attrs.tbwidth = s['border-top-width'] 886 attrs.bbwidth = s['border-bottom-width'] 887 attrs.type = 'border' 888 attrs.height = height - s['margin-top'] - s['margin-bottom'] 889 attrs.width = contentWidth - (lpush + rpush - s['padding-left'] - s['padding-right'] - s['border-left-width'] - s['border-right-width']) 890 attrs.border = 10 891 self.AddUnderlay(attrs, lpush - s['padding-left'] - s['border-left-width'], y + s['margin-top']) 892 893 def AddTopBorders(self): 894 if self.getWidths: 895 return 896 self.attrStack[-1]['margin-top'] = self.GetPercent(self.attrStack[-1]['margin-top'], self.contentHeight) 897 height = self.attrStack[-1]['margin-top'] + self.attrStack[-1]['padding-top'] + self.attrStack[-1]['border-top-width'] 898 if height: 899 lpush, rpush = self.GetMargins(self.contentHeight, self.contentHeight + height) 900 data = {'decoClass': SE_TextlineCore, 901 'align': 'left', 902 'lpush': lpush, 903 'rpush': rpush, 904 'maxBaseHeight': height, 905 'stack': [], 906 'pos': 0} 907 _lines = [ScrollEntryNode(**data)] 908 self.contentHeight = max(self.contentHeight, self.contentHeight + height) 909 self.AddLines(_lines) 910 911 def AddBottomBorders(self): 912 if self.getWidths: 913 return 914 self.attrStack[-1]['margin-bottom'] = self.GetPercent(self.attrStack[-1]['margin-bottom'], self.contentHeight) 915 height = self.attrStack[-1]['margin-bottom'] + self.attrStack[-1]['padding-bottom'] + self.attrStack[-1]['border-bottom-width'] 916 if height: 917 lpush, rpush = self.GetMargins(self.contentHeight, self.contentHeight + height) 918 data = {'decoClass': SE_TextlineCore, 919 'align': 'left', 920 'lpush': lpush, 921 'rpush': rpush, 922 'maxBaseHeight': height, 923 'stack': [], 924 'pos': 0} 925 _lines = [ScrollEntryNode(**data)] 926 self.contentHeight = max(self.contentHeight, self.contentHeight + height) 927 self.AddLines(_lines) 928 929 def ApplyCSS(self, tag, attrs): 930 s = self.css.ApplyCSS(tag, attrs, self.attrStack) 931 self.SetStyles(s) 932 self.attrStack[-1]['tag'] = tag 933 self.attrStack[-1]['attr'] = attrs 934 935 def SetStyles(self, s): 936 if s is not None: 937 self.attrStack[-1].update(s) 938 939 def ParseStdStyles(self, attrs): 940 if getattr(attrs, 'style', None): 941 s = self.css.ParseCSS(attrs) 942 self.attrStack[-1].update(s) 943 if getattr(attrs, 'id', None): 944 c = self.css.GetClass(attrs.id) 945 if c: 946 for key, value in c.iteritems(): 947 if key in ('tag', 'attr'): 948 continue 949 self.attrStack[-1][key] = value 950 951 elif getattr(attrs, 'class', None): 952 c = self.css.GetClass(getattr(attrs, 'class', None)) 953 if c: 954 for key, value in c.iteritems(): 955 if key in ('tag', 'attr'): 956 continue 957 self.attrStack[-1][key] = value 958 959 def AddStyles(self, stack, css): 960 self.attrStack.append(stack) 961 self.css = css.copy() 962 963 def CheckURL(self, URL, getAnchor = 0): 964 browser = GetBrowser(self) 965 currentURL = None 966 if browser: 967 currentURL = browser.sr.currentURL 968 URL, anchor = browserutil.ParseURL(URL, currentURL) 969 if URL.startswith('./'): 970 if currentURL.endswith('/'): 971 base = currentURL 972 else: 973 base = browserutil.DirUp(currentURL) 974 URL = base + URL[2:] 975 elif URL.startswith('../'): 976 base = browserutil.DirUp(browserutil.DirUp(currentURL)) 977 URL = base + URL[3:] 978 if getAnchor: 979 return (URL, anchor) 980 return URL 981 982 def LocalizeURL(self, url): 983 if prefs.languageID != 'EN' and url and (url[:4] == 'res:' or url.find('res') > 0): 984 current = None 985 if '/en/' not in url: 986 return url 987 newUrl = url.replace('/en/', '/%s/' % prefs.languageID.lower()) 988 if blue.paths.exists(newUrl): 989 return newUrl 990 log.LogWarn('No localized image found for', url, 'with languageID', prefs.languageID) 991 return url 992 993 @telemetry.ZONE_METHOD 994 def OnStart_body(self, attrs): 995 self.OnStart_default('body', attrs) 996 self.bodyAttrs = attrs 997 self.LoadBodyAttrs(attrs) 998 999 @telemetry.ZONE_METHOD 1000 def OnEnd_body(self): 1001 self.FlushBuffer() 1002 self.OnEnd_default('body') 1003 1004 @telemetry.ZONE_METHOD 1005 def OnStart_meta(self, attrs): 1006 content = attrs.content 1007 if content: 1008 options = content.split(';') 1009 for option in options: 1010 option = option.strip() 1011 if option.startswith('charset='): 1012 charset = option[8:].strip().lower() 1013 try: 1014 import codecs 1015 codecs.lookup(str(charset)) 1016 self.charset = charset 1017 except: 1018 sys.exc_clear() 1019 1020 def IsIconOrSimilarType(self, attrs): 1021 if attrs.src.startswith('icon:'): 1022 return True 1023 return False 1024 1025 def OnStart_img_Game(self, attrs): 1026 pass 1027 1028 @telemetry.ZONE_METHOD 1029 def OnStart_img(self, attrs): 1030 attrs.a = self.a 1031 attrs.loadlater = 0 1032 gameHandling = self.OnStart_img_Game(attrs) 1033 if gameHandling: 1034 texture, tWidth, tHeight = gameHandling 1035 attrs.width = getattr(attrs, 'width', None) or tWidth 1036 attrs.height = getattr(attrs, 'height', None) or tHeight 1037 elif attrs.src.startswith('res:'): 1038 if getattr(attrs, 'localize', False): 1039 attrs.src = self.LocalizeURL(attrs.src) 1040 texture, tWidth, tHeight = sm.GetService('browserImage').GetTextureFromURL(attrs.src, fromWhere='OnStart_img') 1041 attrs.width = getattr(attrs, 'width', None) or tWidth 1042 attrs.height = getattr(attrs, 'height', None) or tHeight 1043 else: 1044 browser = GetBrowser(self) 1045 currentURL = None 1046 if browser: 1047 currentURL = browser.sr.currentURL 1048 attrs.src = self.CheckURL(attrs.src) 1049 tWidth, tHeight = (32, 32) 1050 if attrs.width is None or attrs.height is None: 1051 cache = sm.GetService('browserImage').GetTextureInfoFromURL(attrs.src, currentURL, fromWhere='OnStart_img') 1052 if cache is not None: 1053 path, tWidth, tHeight, bw, bh = cache 1054 texture = None 1055 attrs.loadlater = 1 1056 attrs.currentURL = currentURL 1057 if attrs.width: 1058 if unicode(attrs.width).endswith('%'): 1059 attrs.width = int(float(unicode(attrs.width)[:-1]) / 100.0 * self.sr.content.width) 1060 else: 1061 attrs.width = int(attrs.width) 1062 if attrs.height: 1063 if unicode(attrs.height).endswith('%'): 1064 attrs.height = int(float(unicode(attrs.height)[:-1]) / 100.0 * self.sr.content.height) 1065 else: 1066 attrs.height = int(attrs.height) 1067 if not attrs.width: 1068 if attrs.height: 1069 attrs.width = int(attrs.height * (tWidth / float(tHeight))) 1070 else: 1071 attrs.width = tWidth 1072 if not attrs.height: 1073 if attrs.width: 1074 attrs.height = int(attrs.width * (tHeight / float(tWidth))) 1075 else: 1076 attrs.height = tHeight 1077 s = {'border-left-width': 0, 1078 'border-right-width': 0, 1079 'border-top-width': 0, 1080 'border-bottom-width': 0, 1081 'margin-left': 0, 1082 'margin-right': 0, 1083 'margin-top': 0, 1084 'margin-bottom': 0, 1085 'float': None} 1086 for side in ['left', 1087 'top', 1088 'right', 1089 'bottom']: 1090 if attrs.border is not None: 1091 s['border-%s-width' % side] = self.GetInt(attrs.border) 1092 if attrs.bordercolor: 1093 s['border-%s-color' % side] = ParseHTMLColor(attrs.bordercolor, 1) 1094 1095 if attrs.hspace is not None: 1096 s['margin-left'] = int(attrs.hspace) 1097 s['margin-right'] = int(attrs.hspace) 1098 if attrs.vspace is not None: 1099 s['margin-top'] = s['margin-bottom'] = int(attrs.vspace) 1100 if attrs.usemap: 1101 if attrs.usemap.startswith('#'): 1102 umap = attrs.usemap[1:] 1103 for each in self.areamap: 1104 if each['name'] == umap: 1105 attrs.areamap = each['coords'] 1106 break 1107 1108 attrs.texture = texture 1109 attrs.type = 'img' 1110 if getattr(attrs, 'bgcolor', None): 1111 attrs.bgcolor = ParseHTMLColor(attrs.bgcolor, 1) 1112 lpush, rpush = self.GetMargins(self.contentHeight, self.contentHeight + attrs.height) 1113 self.OnStart_default('img', attrs, s) 1114 for side in ['left', 1115 'top', 1116 'right', 1117 'bottom']: 1118 setattr(attrs, '%sborder' % side, self.attrStack[-1]['border-%s-width' % side]) 1119 setattr(attrs, '%scolor' % side, self.attrStack[-1]['border-%s-color' % side]) 1120 setattr(attrs, '%smargin' % side, self.attrStack[-1]['margin-%s' % side]) 1121 1122 if self.attrStack[-1]['float']: 1123 attrs.align = self.attrStack[-1]['float'] 1124 if (getattr(attrs, 'align', None) or 'None').lower() not in ('left', 'right'): 1125 img = self.GetInline(self.GetScrollEntry(attrs)) 1126 obj = Bunch() 1127 obj.type = '<img>' 1128 obj.width = attrs.width 1129 obj.height = attrs.height 1130 obj.attrs = attrs 1131 obj.valign = [None, 1][attrs.align == 'center'] or self.attrStack[-1]['vertical-align'] 1132 obj.control = img 1133 self.AddObjectToBuffer(obj) 1134 else: 1135 overlay = self.AddOverlay(attrs, 0, self.contentHeight, lpush=lpush, rpush=rpush) 1136 obj = Bunch() 1137 obj.type = '<overlay>' 1138 obj.attrs = attrs 1139 obj.overlay = overlay 1140 obj.lpush = lpush 1141 obj.rpush = rpush 1142 self.AddObjectToBuffer(obj) 1143 self.attrStack.pop() 1144 1145 def OnStart_script(self, attrs): 1146 self.OnStart_default('script', attrs) 1147 1148 def OnData_script(self, data, attrs): 1149 pass 1150 1151 def OnEnd_script(self): 1152 self.OnEnd_default('script') 1153 1154 def OnStart_b(self, attrs): 1155 self.OnStart_default('b', attrs) 1156 self.attrStack[-1]['font-weight'] = 'b' 1157 1158 def OnEnd_b(self): 1159 self.OnEnd_default('b') 1160 self.attrStack[-1]['font-weight'] = '' 1161 1162 def OnStart_i(self, attrs): 1163 self.OnStart_default('i', attrs) 1164 self.attrStack[-1]['font-style'] = 'i' 1165 1166 def OnEnd_i(self): 1167 self.OnEnd_default('i') 1168 self.attrStack[-1]['font-style'] = '' 1169 1170 def OnStart_u(self, attrs): 1171 self.OnStart_default('u', attrs) 1172 if 'u' not in self.attrStack[-1]['text-decoration']: 1173 self.attrStack[-1]['text-decoration'] += 'u' 1174 1175 def OnEnd_u(self): 1176 self.OnEnd_default('u') 1177 self.attrStack[-1]['text-decoration'] = self.attrStack[-1]['text-decoration'].replace('u', '') 1178 1179 def OnData_pre(self, data, attrs): 1180 for line in data.replace('\r', '').split('\n'): 1181 self.AddTextToBuffer(line, fromW='pre') 1182 if not self.textbuffer: 1183 self.NewLine() 1184 else: 1185 self.FlushBuffer() 1186 1187 @telemetry.ZONE_METHOD 1188 def OnStart_li(self, attrs): 1189 if self.attrStack[-1]['tag'] == 'li': 1190 self.OnEnd_li() 1191 self.FlushBuffer() 1192 tag = self.attrStack and self.attrStack[-1]['tag'] 1193 if not self.sr.listIndex: 1194 self.sr.listIndex = [0] 1195 self.sr.listIndex[-1] += 1 1196 if tag == 'ul': 1197 bullType = attrs.__dict__.get('type', None) or self.attrStack[-1]['attr'].__dict__.get('type', 'disc') 1198 text = {'disc': unichr(8226), 1199 'square': unichr(9632), 1200 'circle': unichr(9675)}.get(bullType.lower(), unichr(8226)) 1201 elif tag == 'ol': 1202 idx = self.sr.listIndex[-1] 1203 orderType = attrs.__dict__.get('type', None) or self.attrStack[-1]['attr'].__dict__.get('type', '1') 1204 if orderType in 'aA': 1205 text = '%s.' % unichr(96 + idx) 1206 elif orderType in 'iI': 1207 text = eveformat.number_roman(idx).lower() 1208 else: 1209 text = '%s.' % self.sr.listIndex[-1] 1210 if orderType in 'AI': 1211 text = text.upper() 1212 else: 1213 text = unichr(8226) 1214 obj = Bunch() 1215 obj.width = -24 1216 obj.type = '<nbsp>' 1217 self.AddObjectToBuffer(obj) 1218 self.AddTextToBuffer(text, fromW='OnStart_li') 1219 obj = Bunch() 1220 obj.width = 24 - self.GetWidthInBuffer() 1221 obj.type = '<nbsp>' 1222 self.AddObjectToBuffer(obj) 1223 self.OnStart_default('li', attrs) 1224 self.list += 1 1225 1226 @telemetry.ZONE_METHOD 1227 def OnEnd_li(self): 1228 self.FlushBuffer() 1229 self.OnEnd_default('li') 1230 self.list = max(0, self.list - 1) 1231 1232 @telemetry.ZONE_METHOD 1233 def OnStartList(self): 1234 if self.attrStack and self.attrStack[-1]['tag'] not in ('ul', 'ol'): 1235 self.FlushBuffer() 1236 self.sr.listIndex.append(0) 1237 1238 def OnDataList(self, data, attrs): 1239 if data.strip(): 1240 log.LogError('Character data directly in UL?') 1241 1242 @telemetry.ZONE_METHOD 1243 def OnEndList(self): 1244 if len(self.attrStack) > 1 and self.attrStack[-2]['tag'] not in ('ul', 'ol'): 1245 self.FlushBuffer() 1246 if len(self.sr.listIndex): 1247 self.sr.listIndex.pop() 1248 1249 @telemetry.ZONE_METHOD 1250 def OnStart_ol(self, attrs): 1251 self.OnStart_default('ol', attrs) 1252 self.OnStartList() 1253 1254 OnData_ol = OnDataList 1255 1256 @telemetry.ZONE_METHOD 1257 def OnEnd_ol(self): 1258 self.OnEndList() 1259 self.OnEnd_default('ol') 1260 1261 @telemetry.ZONE_METHOD 1262 def OnStart_ul(self, attrs): 1263 self.OnStart_default('ul', attrs) 1264 self.OnStartList() 1265 1266 OnData_ul = OnDataList 1267 1268 @telemetry.ZONE_METHOD 1269 def OnEnd_ul(self): 1270 self.OnEndList() 1271 self.OnEnd_default('ul') 1272 1273 @telemetry.ZONE_METHOD 1274 def OnStart_sub(self, attrs): 1275 s = {'vertical-align': 4} 1276 self.OnStart_default('sub', attrs, s) 1277 1278 @telemetry.ZONE_METHOD 1279 def OnEnd_sub(self): 1280 self.OnEnd_default('sub') 1281 1282 @telemetry.ZONE_METHOD 1283 def OnStart_sup(self, attrs): 1284 s = {'vertical-align': 5} 1285 self.OnStart_default('sup', attrs, s) 1286 1287 @telemetry.ZONE_METHOD 1288 def OnEnd_sup(self): 1289 self.OnEnd_default('sup') 1290 1291 @telemetry.ZONE_METHOD 1292 def OnStart_h1(self, attrs): 1293 s = {'font-size': 28, 1294 'font-weight': 'b'} 1295 if attrs.align: 1296 s['text-align'] = attrs.align 1297 self.OnStart_default('h1', attrs, s) 1298 self.FlushBuffer() 1299 1300 @telemetry.ZONE_METHOD 1301 def OnEnd_h1(self): 1302 self.FlushBuffer() 1303 self.OnEnd_default('h1') 1304 1305 @telemetry.ZONE_METHOD 1306 def OnStart_h2(self, attrs): 1307 s = {'font-size': 24, 1308 'font-weight': 'b'} 1309 if attrs.align: 1310 s['text-align'] = attrs.align 1311 self.OnStart_default('h2', attrs, s) 1312 self.FlushBuffer() 1313 1314 @telemetry.ZONE_METHOD 1315 def OnEnd_h2(self): 1316 self.FlushBuffer() 1317 self.OnEnd_default('h2') 1318 1319 @telemetry.ZONE_METHOD 1320 def OnStart_h3(self, attrs): 1321 s = {'font-size': 20, 1322 'font-weight': 'b'} 1323 if attrs.align: 1324 s['text-align'] = attrs.align 1325 self.OnStart_default('h3', attrs, s) 1326 self.FlushBuffer() 1327 1328 @telemetry.ZONE_METHOD 1329 def OnEnd_h3(self): 1330 self.FlushBuffer() 1331 self.OnEnd_default('h3') 1332 1333 @telemetry.ZONE_METHOD 1334 def OnStart_h4(self, attrs): 1335 s = {'font-size': 18, 1336 'font-weight': 'b'} 1337 if attrs.align: 1338 s['text-align'] = attrs.align 1339 self.OnStart_default('h4', attrs, s) 1340 self.FlushBuffer() 1341 1342 @telemetry.ZONE_METHOD 1343 def OnEnd_h4(self): 1344 self.FlushBuffer() 1345 self.OnEnd_default('h4') 1346 1347 @telemetry.ZONE_METHOD 1348 def OnStart_h5(self, attrs): 1349 s = {'font-size': 16, 1350 'font-weight': 'b'} 1351 if attrs.align: 1352 s['text-align'] = attrs.align 1353 self.OnStart_default('h5', attrs, s) 1354 self.FlushBuffer() 1355 1356 @telemetry.ZONE_METHOD 1357 def OnEnd_h5(self): 1358 self.FlushBuffer() 1359 self.OnEnd_default('h5') 1360 1361 @telemetry.ZONE_METHOD 1362 def OnStart_h6(self, attrs): 1363 s = {'font-size': 14, 1364 'font-weight': 'bold'} 1365 if attrs.align: 1366 s['text-align'] = attrs.align 1367 self.OnStart_default('h6', attrs, s) 1368 self.FlushBuffer() 1369 1370 @telemetry.ZONE_METHOD 1371 def OnEnd_h6(self): 1372 self.FlushBuffer() 1373 self.OnEnd_default('h6') 1374 1375 @telemetry.ZONE_METHOD 1376 def OnStart_style(self, attrs): 1377 self.OnStart_default('style', attrs) 1378 1379 @telemetry.ZONE_METHOD 1380 def OnData_style(self, data, attrs): 1381 self.css.ParseCSSData(data) 1382 1383 @telemetry.ZONE_METHOD 1384 def OnEnd_style(self): 1385 self.OnEnd_default('style') 1386 1387 @telemetry.ZONE_METHOD 1388 def OnStart_link(self, attrs): 1389 rel = attrs.rel 1390 t = attrs.type 1391 href = attrs.href 1392 if not rel or not href: 1393 return 1394 if rel.lower() == 'stylesheet': 1395 url = self.CheckURL(href) 1396 browser = GetBrowser(self) 1397 currentURL = None 1398 if browser: 1399 currentURL = browser.sr.currentURL 1400 if href.startswith('res:') or href.startswith('script:'): 1401 try: 1402 f = blue.ResFile() 1403 f.OpenAlways(href) 1404 self.css.ParseCSSData(str(f.Read())) 1405 del f 1406 except Exception: 1407 log.LogException() 1408 1409 return 1410 fullPath = browserutil.ParseURL(url, currentURL)[0] 1411 try: 1412 buff = browserutil.GetStringFromURL(*(browserutil.ParseURL(fullPath)[:1] + (None,))).read() 1413 self.css.ParseCSSData(buff) 1414 except urllib2.URLError as what: 1415 sys.exc_clear() 1416 except Exception: 1417 log.LogException() 1418 1419 @telemetry.ZONE_METHOD 1420 def OnStart_title(self, attrs): 1421 self.OnStart_default('title', attrs) 1422 1423 @telemetry.ZONE_METHOD 1424 def OnData_title(self, data, attrs): 1425 data = data.strip() 1426 self.title = data 1427 if self.sr.window and getattr(self.sr.window, 'SetCaption', None): 1428 self.sr.window.SetCaption(data) 1429 1430 @telemetry.ZONE_METHOD 1431 def OnEnd_title(self): 1432 self.OnEnd_default('title') 1433 1434 @telemetry.ZONE_METHOD 1435 def OnStart_form(self, attrs): 1436 if not self or getattr(self, 'sr', None) is None: 1437 return 1438 if self.sr.form: 1439 self.OnEnd_form() 1440 if self.sr.Get('browser', None): 1441 self.sr.form = browserutil.NewBrowserForm(attrs, _weakref.proxy(self.sr.browser)) 1442 else: 1443 self.sr.form = browserutil.NewBrowserForm(attrs, _weakref.proxy(self)) 1444 1445 @telemetry.ZONE_METHOD 1446 def OnEnd_form(self): 1447 self.sr.forms.append(self.sr.form) 1448 self.sr.form = None 1449 1450 @telemetry.ZONE_METHOD 1451 def OnStart_input(self, attrs): 1452 form = self.sr.form or getattr(attrs, 'form', None) 1453 if form is None: 1454 form = self.sr.form = browserutil.NewBrowserForm(attrs, _weakref.proxy(self)) 1455 s = {'width': None, 1456 'height': None} 1457 self.OnStart_default('input', attrs, s) 1458 if not hasattr(attrs, 'valign'): 1459 attrs.valign = 1 1460 attrs.fontcolor = self.attrStack[-1]['color'] 1461 attrs.bordercolor = self.attrStack[-1]['color'] 1462 attrs.form = form 1463 attrs.width = self.attrStack[-1]['width'] 1464 attrs.height = self.attrStack[-1]['height'] 1465 if getattr(attrs, 'value', None): 1466 attrs.value = self.sr.parser.decode_string(attrs.value) 1467 obj = form.AddInput(attrs, not self.getWidths) 1468 if obj: 1469 self.AddObjectToBuffer(obj) 1470 self.attrStack.pop() 1471 1472 def OnEnd_input(self): 1473 pass 1474 1475 @telemetry.ZONE_METHOD 1476 def OnStart_select(self, attrs): 1477 form = self.sr.form or getattr(attrs, 'form', None) 1478 if form is None: 1479 form = self.sr.form = browserutil.NewBrowserForm(attrs, _weakref.proxy(self)) 1480 self.sr.select = Bunch() 1481 self.sr.select.attrs = attrs 1482 self.sr.select.attrs.form = form 1483 self.sr.select.options = [] 1484 s = {'width': None, 1485 'height': None} 1486 self.OnStart_default('select', attrs, s) 1487 attrs.fontcolor = self.attrStack[-1]['color'] 1488 attrs.width = self.attrStack[-1]['width'] 1489 attrs.height = self.attrStack[-1]['height'] 1490 1491 @telemetry.ZONE_METHOD 1492 def OnEnd_select(self): 1493 if self.sr.select is None: 1494 raise RuntimeError('ParsingError', {'what': 'Unmatched select end'}) 1495 obj = self.sr.select.attrs.form.AddSelect(self.sr.select.attrs, self.sr.select.options, not self.getWidths) 1496 if obj: 1497 self.AddObjectToBuffer(obj) 1498 self.sr.select = None 1499 self.OnEnd_default('select') 1500 1501 def OnData_select(self, data, attrs): 1502 pass 1503 1504 def OnStart_option(self, attrs): 1505 self.OnStart_default('option', attrs) 1506 1507 def OnData_option(self, data, attrs): 1508 self.sr.buffer += data 1509 1510 @telemetry.ZONE_METHOD 1511 def OnEnd_option(self): 1512 if self.sr.select is not None: 1513 self.sr.buffer = self.sr.buffer.replace('\n', '').replace('\r', '') 1514 value = self.attrStack[-1]['attr'].value 1515 if value is None: 1516 value = self.sr.buffer 1517 self.sr.select.options.append((self.sr.buffer, value, self.attrStack[-1]['attr'].selected)) 1518 self.sr.buffer = '' 1519 else: 1520 log.LogWarn('Bogus HTML syntax, Option outside any select') 1521 self.OnEnd_default('option') 1522 1523 @telemetry.ZONE_METHOD 1524 def OnStart_textarea(self, attrs): 1525 s = {'width': None, 1526 'height': None} 1527 self.OnStart_default('textarea', attrs, s) 1528 form = self.sr.form or getattr(attrs, 'form', None) 1529 if form is None: 1530 form = self.sr.form = browserutil.NewBrowserForm(attrs, _weakref.proxy(self)) 1531 attrs.fontcolor = self.attrStack[-1]['color'] 1532 attrs.width = self.attrStack[-1]['width'] 1533 attrs.height = self.attrStack[-1]['height'] 1534 attrs.form = form 1535 1536 @telemetry.ZONE_METHOD 1537 def OnData_textarea(self, data, attrs): 1538 data = data.replace('\n', '<br>').replace('\r', '') 1539 self.sr.buffer += data 1540 1541 @telemetry.ZONE_METHOD 1542 def OnEnd_textarea(self): 1543 attrs = self.attrStack[-1]['attr'] 1544 attrs.value = self.sr.buffer 1545 obj = attrs.form.AddTextArea(attrs, not self.getWidths) 1546 if obj: 1547 self.AddObjectToBuffer(obj) 1548 self.sr.buffer = '' 1549 self.OnEnd_default('textarea') 1550 1551 @telemetry.ZONE_METHOD 1552 def OnStart_center(self, attrs): 1553 self.FlushBuffer() 1554 self.OnStart_default('center', attrs) 1555 self.attrStack[-1]['text-align'] = 'center' 1556 1557 @telemetry.ZONE_METHOD 1558 def OnEnd_center(self): 1559 self.FlushBuffer() 1560 self.OnEnd_default('center') 1561 1562 @telemetry.ZONE_METHOD 1563 def OnStart_p(self, attrs): 1564 if self.attrStack[-1]['tag'] == 'p': 1565 self.OnEnd_p() 1566 s = {} 1567 s['text-align'] = getattr(attrs, 'align', None) 1568 self.OnStart_default('p', attrs, s) 1569 self.NewLine() 1570 if int(self.attrStack[-1]['text-indent']) > 0: 1571 obj = Bunch() 1572 obj.width = int(self.attrStack[-1]['text-indent']) 1573 obj.type = '<list>' 1574 self.AddObjectToBuffer(obj) 1575 self.attrStack[-1]['pstart'] = self.contentHeight 1576 self.AddTopBorders() 1577 1578 @telemetry.ZONE_METHOD 1579 def OnEnd_p(self): 1580 if self.attrStack[-1]['margin-bottom'] == 0: 1581 self.NewLine() 1582 else: 1583 self.FlushBuffer() 1584 self.AddBottomBorders() 1585 self.AddBorder(self.attrStack[-1]['pstart'], self.contentHeight - self.attrStack[-1]['pstart']) 1586 self.OnEnd_default('p') 1587 1588 @telemetry.ZONE_METHOD 1589 def OnStart_font(self, attrs): 1590 s = {} 1591 fontsize = getattr(attrs, 'size', None) 1592 basefontsize = None 1593 if unicode(fontsize).startswith('+') or unicode(fontsize).startswith('-'): 1594 try: 1595 m = int(fontsize[1:]) 1596 except: 1597 m = 0 1598 sys.exc_clear() 1599 1600 basefontsize = max(0, min(7, self.attrStack[-1]['basefontsize'] + [-1, 1][unicode(fontsize).startswith('+')] * m)) 1601 elif len(unicode(fontsize)) == 1 and unicode(fontsize) in '1234567': 1602 try: 1603 basefontsize = int(fontsize) 1604 except: 1605 sys.exc_clear() 1606 1607 if basefontsize is not None: 1608 fontsize = {1: 6, 1609 2: 8, 1610 3: 10, 1611 4: 12, 1612 5: 14, 1613 6: 17, 1614 7: 20}.get(basefontsize, fontsize) 1615 s['basefontsize'] = basefontsize 1616 spacing = getattr(attrs, 'letter-spacing', None) or getattr(attrs, 'space', None) 1617 if fontsize is not None and unicode(fontsize).isdigit(): 1618 s['font-size'] = int(fontsize) 1619 if spacing is not None: 1620 s['letter-spacing'] = int(spacing) 1621 if getattr(attrs, 'color', None): 1622 s['color'] = ParseHTMLColor(getattr(attrs, 'color', None), 1) 1623 self.OnStart_default('font', attrs, s) 1624 1625 @telemetry.ZONE_METHOD 1626 def OnEnd_font(self): 1627 self.OnEnd_default('font') 1628 1629 @telemetry.ZONE_METHOD 1630 def OnStart_br(self, attrs): 1631 if not self.textbuffer: 1632 self.NewLine() 1633 else: 1634 self.FlushBuffer() 1635 1636 @telemetry.ZONE_METHOD 1637 def OnStart_hr(self, attrs): 1638 self.FlushBuffer() 1639 s = {'margin-top': 2, 1640 'margin-bottom': 2, 1641 'padding-top': 0, 1642 'padding-bottom': 0, 1643 'border-top-width': 0, 1644 'border-bottom-width': 0} 1645 self.OnStart_default('hr', attrs, s) 1646 self.AddTopBorders() 1647 self.FlushBuffer() 1648 lineHeight = int(getattr(attrs, 'size', 0) or 1) 1649 lpush, rpush = self.GetMargins(self.contentHeight, self.contentHeight + lineHeight) 1650 obj = Bunch() 1651 obj.type = '<hr>' 1652 obj.width = 0 1653 obj.height = lineHeight 1654 obj.lpush = lpush 1655 obj.rpush = rpush 1656 obj.attrs = attrs 1657 self.AddObjectToBuffer(obj) 1658 self.contentHeight += lineHeight 1659 self.FlushBuffer() 1660 self.AddBottomBorders() 1661 self.attrStack.pop() 1662 1663 def ClearAttrs(self, s): 1664 for side in ['left', 1665 'top', 1666 'right', 1667 'bottom']: 1668 s['border-%s-width' % side] = 0 1669 s['border-%s-color' % side] = None 1670 s['margin-%s' % side] = 0 1671 1672 s['background-color'] = None 1673 1674 def OnStart_blockquote(self, attrs): 1675 self.NewLine() 1676 self.OnStart_default('blockquote', attrs) 1677 self.blockquote += 1 1678 1679 def OnEnd_blockquote(self): 1680 self.NewLine() 1681 self.OnEnd_default('blockquote') 1682 self.blockquote = max(0, self.blockquote - 1) 1683 1684 def OnStart_var(self, attrs): 1685 s = {'font-style': 'i'} 1686 self.OnStart_default('var', attrs, s) 1687 1688 def OnEnd_var(self): 1689 self.OnEnd_default('var') 1690 1691 def OnStart_cite(self, attrs): 1692 s = {'font-style': 'i'} 1693 self.OnStart_default('cite', attrs, s) 1694 1695 def OnEnd_cite(self): 1696 self.OnEnd_default('cite') 1697 1698 def OnStart_address(self, attrs): 1699 s = {'font-style': 'i'} 1700 self.OnStart_default('address', attrs, s) 1701 1702 def OnEnd_address(self): 1703 self.OnEnd_default('address') 1704 1705 def OnStart_dfn(self, attrs): 1706 s = {'font-style': 'i'} 1707 self.OnStart_default('dfn', attrs, s) 1708 1709 def OnEnd_dfn(self): 1710 self.OnEnd_default('dfn') 1711 1712 def OnStart_code(self, attrs): 1713 self.OnStart_default('code', attrs, s) 1714 self.plainload = 1 1715 1716 def OnEnd_code(self): 1717 self.plainload = 0 1718 self.OnEnd_default('code') 1719 1720 def OnStart_frame(self, attrs): 1721 pass 1722 1723 @telemetry.ZONE_METHOD 1724 def OnStart_div(self, attrs): 1725 self.FlushBuffer() 1726 contentWidth = self.GetContentWidth() 1727 if hasattr(attrs, 'width') and type(attrs.width) == str and attrs.width.endswith('%'): 1728 attrs.width = self.GetInt(attrs.width) * contentWidth / 100.0 1729 else: 1730 attrs.width = int(getattr(attrs, 'width', None) or contentWidth) 1731 attrs.height = int(getattr(attrs, 'height', None) or 0) 1732 attrs.left = int(getattr(attrs, 'left', None) or 0) 1733 attrs.top = int(getattr(attrs, 'top', self.contentHeight) or 0) 1734 s = {'position': 'unset', 1735 'text-align': 'left', 1736 'float': None, 1737 'width': attrs.width, 1738 'height': attrs.height, 1739 'left': attrs.left, 1740 'top': attrs.top} 1741 for side in ['left', 1742 'top', 1743 'right', 1744 'bottom']: 1745 s['border-%s-width' % side] = int(attrs.border or 0) 1746 if attrs.bordercolor: 1747 s['border-%s-color' % side] = ParseHTMLColor(attrs.bordercolor, 1) 1748 1749 if attrs.align: 1750 s['text-align'] = attrs.align 1751 s['align'] = attrs.align 1752 if attrs.bgcolor is not None: 1753 s['background-color'] = ParseHTMLColor(attrs.bgcolor, 1) 1754 self.OnStart_default('div', attrs, s) 1755 if self.attrStack[-1]['width'] is not None: 1756 attrs.width = self.attrStack[-1]['width'] 1757 if self.attrStack[-1]['height'] is not None: 1758 attrs.width = self.attrStack[-1]['height'] 1759 if self.attrStack[-1]['position'] in ('absolute', 'relative') or self.attrStack[-1]['float'] in ('left', 'right'): 1760 self.attrStack[-1]['bufferStack'] = [('AddStyles', (self.attrStack[-1].copy(), self.css.copy()))] 1761 self.divs += 1 1762 else: 1763 self.attrStack[-1]['pstart'] = self.contentHeight 1764 self.AddTopBorders() 1765 1766 @telemetry.ZONE_METHOD 1767 def OnEnd_div(self): 1768 if self.attrStack[-1]['bufferStack'] and self.attrStack[-1]['tag'] == 'div': 1769 self.divs = max(0, self.divs - 1) 1770 self.AddFloater() 1771 else: 1772 self.FlushBuffer() 1773 self.AddBottomBorders() 1774 self.AddBorder(self.attrStack[-1]['pstart'], self.contentHeight - self.attrStack[-1]['pstart']) 1775 self.OnEnd_default('div') 1776 1777 @telemetry.ZONE_METHOD 1778 def OnStart_table(self, attrs): 1779 if self.sr.table is not None: 1780 self.OnEnd_table() 1781 self.FlushBuffer() 1782 self.sr.table = attrs 1783 self.sr.table.rows = [] 1784 self.sr.table.colgroups = [] 1785 s = self.GetTableAttrs(attrs) 1786 self.OnStart_default('table', attrs, s) 1787 if self.attrStack[-1]['border-collapse']: 1788 for side in ['left', 1789 'top', 1790 'right', 1791 'bottom']: 1792 self.attrStack[-1]['spacing-%s' % side] = 0 1793 1794 self.attrStack[-1]['table-width'] = attrs.width 1795 self.attrStack[-1]['table-height'] = attrs.height 1796 1797 @telemetry.ZONE_METHOD 1798 def OnEnd_table(self): 1799 if self.sr.table is None: 1800 self.OnEnd_default('table') 1801 self.FlushBuffer() 1802 return 1803 attrs = self.attrStack[-1]['attr'] 1804 attrs.type = 'table' 1805 attrs.charset = self.charset 1806 se = self.GetScrollEntry(attrs) 1807 se.attrs = self.sr.table 1808 table = self.GetInline(se) 1809 if getattr(attrs, 'align', None) is None: 1810 obj = Bunch() 1811 obj.type = '<table>' 1812 obj.width = table.width 1813 obj.height = table.height 1814 obj.attrs = attrs 1815 obj.valign = self.attrStack[-1]['vertical-align'] 1816 obj.control = table 1817 self.AddObjectToBuffer(obj) 1818 else: 1819 attrs.height = table.height 1820 attrs.width = table.width 1821 table.Close() 1822 self.AddOverlay(attrs, 0, self.contentHeight) 1823 if self.destroyed: 1824 return 1825 self.sr.table = None 1826 self.OnEnd_default('table') 1827 self.FlushBuffer() 1828 1829 def OnData_table(self, data, attrs): 1830 pass 1831 1832 def GetTableAttrs(self, attrs): 1833 s = {} 1834 for side in ['left', 1835 'top', 1836 'right', 1837 'bottom']: 1838 s['border-%s-width' % side] = 0 1839 s['border-%s-color' % side] = self.attrStack[-1]['color'] 1840 s['spacing-%s' % side] = 1 1841 s['padding-%s' % side] = 2 1842 1843 s['background-color'] = None 1844 s['background-image'] = None 1845 for side in ['left', 1846 'top', 1847 'right', 1848 'bottom']: 1849 if hasattr(attrs, 'border') and attrs.border and unicode(attrs.border).isdigit(): 1850 s['border-%s-width' % side] = int(attrs.border) 1851 s['border-%s-style' % side] = 1 1852 if getattr(attrs, 'bordercolor', None): 1853 s['border-%s-color' % side] = ParseHTMLColor(attrs.bordercolor, 1, error=1) or s['border-%s-color' % side] 1854 if getattr(attrs, 'cellspacing', None) is not None: 1855 s['spacing-%s' % side] = max(0, int((int(attrs.cellspacing) + [0, 1][side in ('right', 'bottom')]) / 2)) 1856 if getattr(attrs, 'cellpadding', None) is not None: 1857 s['padding-%s' % side] = max(0, int(attrs.cellpadding)) 1858 1859 if getattr(attrs, 'bgcolor', None) is not None: 1860 s['background-color'] = ParseHTMLColor(attrs.bgcolor, 1) 1861 return s 1862 1863 def OnStart_colgroup(self, attrs): 1864 if self.sr.table is None: 1865 return 1866 s = {} 1867 s['colgroup-width'] = attrs.width 1868 s['colgroup-span'] = attrs.span 1869 s['cols'] = [] 1870 self.OnStart_default('colgroup', attrs, s) 1871 1872 def OnData_colgroup(self, data, attrs): 1873 pass 1874 1875 def OnEnd_colgroup(self): 1876 s = self.attrStack[-1] 1877 for i in xrange(s['colgroup-span'] or len(s['cols']) or 1): 1878 if len(s['cols']): 1879 self.sr.table.colgroups.append(s['cols'].pop(0) or s['colgroup-width']) 1880 else: 1881 self.sr.table.colgroups.append(s['colgroup-width']) 1882 1883 self.OnEnd_default('colgroup') 1884 1885 def OnStart_col(self, attrs): 1886 if self.sr.table is None: 1887 return 1888 if 'cols' not in self.attrStack[-1]: 1889 return 1890 for i in xrange(attrs.span or 1): 1891 self.attrStack[-1]['cols'].append(attrs.width) 1892 1893 @telemetry.ZONE_METHOD 1894 def OnStart_tr(self, attrs): 1895 if self.sr.table is None: 1896 return 1897 attrs.cols = [] 1898 if unicode(attrs.height).isdigit(): 1899 attrs.height = int(attrs.height or 0) 1900 else: 1901 attrs.height = 0 1902 s = {} 1903 for side in ['left', 1904 'top', 1905 'right', 1906 'bottom']: 1907 if getattr(attrs, 'border', 0): 1908 s['border-%s-width' % side] = int(attrs.border or 0) 1909 if getattr(attrs, 'bordercolor', None): 1910 s['border-%s-color' % side] = ParseHTMLColor(attrs.bordercolor or '#000000', 1) 1911 if hasattr(attrs, 'cellspacing') and attrs.cellspacing is not None: 1912 s['spacing-%s' % side] = max(0, int((int(attrs.cellspacing) + [0, 1][side in ('right', 'bottom')]) / 2)) 1913 if hasattr(attrs, 'cellpadding') and attrs.cellpadding is not None: 1914 s['padding-%s' % side] = max(0, int(attrs.cellpadding)) 1915 1916 if attrs.valign is not None: 1917 s['vertical-align'] = getattr(parser, 'ALIGN%s' % unicode(attrs.valign).upper(), 1) 1918 else: 1919 s['vertical-align'] = parser.ALIGNTOP 1920 s['background-image'] = None 1921 s['background-color'] = None 1922 if attrs.bgcolor is not None: 1923 s['background-color'] = ParseHTMLColor(attrs.bgcolor, 1) 1924 self.OnStart_default('tr', attrs, s) 1925 attrs.styles = self.attrStack[-1].copy() 1926 self.sr.table.rows.append(attrs) 1927 1928 @telemetry.ZONE_METHOD 1929 def OnEnd_tr(self): 1930 self.OnEnd_default('tr') 1931 1932 def OnData_tr(self, data, attrs): 1933 pass 1934 1935 def GetInt(self, stringVal): 1936 if stringVal is None: 1937 return stringVal 1938 stringVal = filter(lambda x: x in '0123456789', unicode(stringVal)) 1939 return int(stringVal) 1940 1941 def GetPercent(self, value, reference): 1942 if type(value) == str and value.endswith('%'): 1943 value = self.GetInt(value) 1944 return int(value * reference / 100.0) 1945 return int(value) 1946 1947 @telemetry.ZONE_METHOD 1948 def GetInline(self, data): 1949 inline = data.decoClass(parent=self.sr.cacheContainer) 1950 inline.data = data 1951 inline.Startup(self) 1952 inline.loaded = 0 1953 return inline 1954 1955 @telemetry.ZONE_METHOD 1956 def OnStart_td(self, attrs): 1957 if self.sr.table is None: 1958 return 1959 if not getattr(self.sr.table, 'rows', None): 1960 attrs.height = getattr(attrs, 'height', None) 1961 self.OnStart_tr(attrs) 1962 h = unicode(getattr(attrs, 'height', None)) 1963 if h and h[-1] == '%': 1964 attrs.height = None 1965 attrs.content = [] 1966 s = {} 1967 s['text-align'] = 'left' 1968 for side in ['left', 1969 'top', 1970 'right', 1971 'bottom']: 1972 s['border-%s-width' % side] = int(getattr(attrs, 'border', 0)) 1973 if getattr(attrs, 'bordercolor', None): 1974 s['border-%s-color' % side] = ParseHTMLColor(attrs.bordercolor or '#000000', 1) 1975 1976 if getattr(attrs, 'valign', None): 1977 s['vertical-align'] = getattr(parser, 'ALIGN%s' % unicode(attrs.valign).upper(), 1) 1978 s['text-align'] = getattr(attrs, 'align', None) or s['text-align'] 1979 if attrs.bgcolor is not None: 1980 s['background-color'] = ParseHTMLColor(attrs.bgcolor, 1) 1981 self.OnStart_default('td', attrs, s) 1982 attrs.styles = self.attrStack[-1].copy() 1983 attrs.css = self.css.copy() 1984 self.attrStack[-1]['bufferStack'] = [] 1985 self.sr.table.rows[-1].cols.append(attrs) 1986 1987 @telemetry.ZONE_METHOD 1988 def OnEnd_td(self): 1989 if self.attrStack[-1]['bufferStack'] is not None and self.attrStack[-1]['tag'] == 'td': 1990 if self.sr.table and len(self.sr.table.rows[-1].cols): 1991 self.sr.table.rows[-1].cols[-1].content = self.attrStack[-1]['bufferStack'][:] 1992 else: 1993 log.LogWarn('Ending unknown td') 1994 self.OnEnd_default('td') 1995 1996 @telemetry.ZONE_METHOD 1997 def OnStart_th(self, attrs): 1998 if self.sr.table is None: 1999 return 2000 if not getattr(self.sr.table, 'rows', None): 2001 attrs.height = getattr(attrs, 'height', None) 2002 self.OnStart_tr(attrs) 2003 h = unicode(getattr(attrs, 'height', None)) 2004 if h and h[-1] == '%': 2005 attrs.height = None 2006 attrs.content = [] 2007 s = {} 2008 s['text-align'] = 'center' 2009 s['font-weight'] = 'b' 2010 for side in ['left', 2011 'top', 2012 'right', 2013 'bottom']: 2014 s['border-%s-width' % side] = int(getattr(attrs, 'border', 0)) 2015 if getattr(attrs, 'bordercolor', None): 2016 s['border-%s-color' % side] = ParseHTMLColor(attrs.bordercolor or '#000000', 1) 2017 2018 if getattr(attrs, 'valign', None): 2019 s['vertical-align'] = getattr(parser, 'ALIGN%s' % unicode(attrs.valign).upper(), 1) 2020 s['text-align'] = getattr(attrs, 'align', None) or s['text-align'] 2021 if attrs.bgcolor is not None: 2022 s['background-color'] = ParseHTMLColor(attrs.bgcolor, 1) 2023 self.OnStart_default('th', attrs, s) 2024 attrs.styles = self.attrStack[-1].copy() 2025 attrs.css = self.css.copy() 2026 self.attrStack[-1]['bufferStack'] = [] 2027 self.sr.table.rows[-1].cols.append(attrs) 2028 2029 @telemetry.ZONE_METHOD 2030 def OnEnd_th(self): 2031 if self.attrStack[-1]['bufferStack'] is not None and self.attrStack[-1]['tag'] == 'th': 2032 if self.sr.table and len(self.sr.table.rows[-1].cols): 2033 self.sr.table.rows[-1].cols[-1].content = self.attrStack[-1]['bufferStack'][:] 2034 else: 2035 log.LogWarn('Ending unknown th') 2036 self.OnEnd_default('th') 2037 2038 @telemetry.ZONE_METHOD 2039 def OnStart_a(self, attrs): 2040 s = {'color': self.attrStack[-1]['link-color'], 2041 'link-color': None} 2042 visited = settings.public.ui.VisitedURLs or [] 2043 self.OnStart_default('a', attrs, s) 2044 self.CheckForMailAddress(attrs) 2045 self.attrStack[-1]['link-color'] = self.attrStack[-1]['link-color'] or self.attrStack[-1]['color'] 2046 attrs.color = [self.attrStack[-1]['link-color'], self.attrStack[-1]['vlink-color'] or self.attrStack[-1]['link-color']][attrs.href in visited] 2047 self.a = attrs 2048 2049 def CheckForMailAddress(self, attrs): 2050 pass 2051 2052 def OnEnd_a(self): 2053 self.a = None 2054 self.OnEnd_default('a') 2055 2056 def OnStart_span(self, attrs): 2057 self.OnStart_default('span', attrs) 2058 self.ParseStdStyles(attrs) 2059 2060 def OnEnd_span(self): 2061 self.OnEnd_default('span') 2062 2063 def OnStart_map(self, attrs): 2064 self.OnStart_default('map', attrs) 2065 if attrs.name: 2066 amap = {} 2067 amap['name'] = attrs.name 2068 amap['coords'] = [] 2069 self.areamap.append(amap) 2070 else: 2071 return 2072 2073 def OnData_map(self, data, attrs): 2074 pass 2075 2076 def OnEnd_map(self): 2077 self.OnEnd_default('map') 2078 2079 @telemetry.ZONE_METHOD 2080 def OnStart_area(self, attrs): 2081 if attrs.shape and attrs.coords and attrs.href: 2082 if attrs.shape != 'rect': 2083 return 2084 if attrs.coords.count(',') != 3: 2085 return 2086 self.CheckForMailAddress(attrs) 2087 x0, y0, x1, y1 = attrs.coords.split(',') 2088 area = {} 2089 area['x0'] = int(x0) 2090 area['y0'] = int(y0) 2091 area['x1'] = int(x1) 2092 area['y1'] = int(y1) 2093 area['url'] = attrs.href 2094 self.areamap[-1]['coords'].append(area) 2095 2096 2097 class ParserBaseCoreOverride(ParserBaseCore): 2098 2099 @classmethod 2100 def __reload_update__(cls, old_cls): 2101 return old_cls