def __init__(self, width=800, height=600, url='http://localhost/'):
        self.VERSION = '0.6'
        self.USER_AGENT = 'SimpleWebBrowser/{version}'.format(version=self.VERSION)
        self.VSPACES    = {'h1': 10, 'h2': 8, 'h3': 6, 'h4': 4, 'h5': 4, 'h6': 4,
                           'p': 8, 'pre': 8, 'ul': 8, 'ol': 8, 'table': 8,
                           'li': 0, 'tr': 0}
        self.IGNORE_TAGS = ['title', 'style', 'script']

        pygame.init()
        pygame.key.set_repeat(100, 1000 // 25)

        self.top    = 20
        self.left   = 0
        self.width  = width
        self.height = height

        self.g         = Graphics()
        self.g.surface = pygame.display.set_mode((self.width, self.height), pygame.RESIZABLE)
        self.do_redraw = True

        self.address_bar   = TextField(0,  0, self.width, 20, on_action_handler=self)
        self.text_box      = TextBox  (0, 20, self.width, self.height - 20)
        self.active_widget = self

        self.fonts = Fonts()
        self.g.font = self.fonts.normal

        self.ATTR = {'default': AttributeWidget(font=self.fonts.normal,fg=(0, 0, 0, 255),bg=(255, 255, 255, 255)),

                     'h1': AttributeWidget(font=self.fonts.h1),
                     'h2': AttributeWidget(font=self.fonts.h2),
                     'h3': AttributeWidget(font=self.fonts.h3),
                     'h4': AttributeWidget(font=self.fonts.h4),
                     'h5': AttributeWidget(font=self.fonts.h5),
                     'h6': AttributeWidget(font=self.fonts.h6),

                     'pre' : AttributeWidget(font=self.fonts.monospace),
                     'code': AttributeWidget(font=self.fonts.monospace)
                     }
        self.attr_stack   = []
        self.attr_current = self.ATTR['default']

        self.links        = []
        self.link_current = None
        self.link_active  = ''

        pygame.display.set_caption('SimpleWebBrowser V{version}'.format(version=self.VERSION))

        self.load(url)
class SimpleWebbrowser:
    def __init__(self, width=800, height=600, url='http://localhost/'):
        self.VERSION = '0.6'
        self.USER_AGENT = 'SimpleWebBrowser/{version}'.format(version=self.VERSION)
        self.VSPACES    = {'h1': 10, 'h2': 8, 'h3': 6, 'h4': 4, 'h5': 4, 'h6': 4,
                           'p': 8, 'pre': 8, 'ul': 8, 'ol': 8, 'table': 8,
                           'li': 0, 'tr': 0}
        self.IGNORE_TAGS = ['title', 'style', 'script']

        pygame.init()
        pygame.key.set_repeat(100, 1000 // 25)

        self.top    = 20
        self.left   = 0
        self.width  = width
        self.height = height

        self.g         = Graphics()
        self.g.surface = pygame.display.set_mode((self.width, self.height), pygame.RESIZABLE)
        self.do_redraw = True

        self.address_bar   = TextField(0,  0, self.width, 20, on_action_handler=self)
        self.text_box      = TextBox  (0, 20, self.width, self.height - 20)
        self.active_widget = self

        self.fonts = Fonts()
        self.g.font = self.fonts.normal

        self.ATTR = {'default': AttributeWidget(font=self.fonts.normal,fg=(0, 0, 0, 255),bg=(255, 255, 255, 255)),

                     'h1': AttributeWidget(font=self.fonts.h1),
                     'h2': AttributeWidget(font=self.fonts.h2),
                     'h3': AttributeWidget(font=self.fonts.h3),
                     'h4': AttributeWidget(font=self.fonts.h4),
                     'h5': AttributeWidget(font=self.fonts.h5),
                     'h6': AttributeWidget(font=self.fonts.h6),

                     'pre' : AttributeWidget(font=self.fonts.monospace),
                     'code': AttributeWidget(font=self.fonts.monospace)
                     }
        self.attr_stack   = []
        self.attr_current = self.ATTR['default']

        self.links        = []
        self.link_current = None
        self.link_active  = ''

        pygame.display.set_caption('SimpleWebBrowser V{version}'.format(version=self.VERSION))

        self.load(url)


    def load(self, uri):
        """ Load new web page."""
        self.link_active = urljoin(self.link_active, uri)
        self.address_bar.set_text(self.link_active)
        self.address_bar.draw(self.g)

        r = requests.get(self.link_active, headers={'user-agent': self.USER_AGENT})
        self.html_sourcecode = r.text

        self.attr_stack       = []
        self.attr_current     = self.ATTR['default']
        self.links            = []

        self.text_box.scroll_left = 0
        self.text_box.scroll_top  = 0
        self.text_box.widgets     = []

        html_parser = etree.HTMLParser(remove_blank_text=True)
        html_tree   = etree.fromstring(self.html_sourcecode, html_parser)
        self.parse_html(html_tree)

        self.text_box.layout(self.g)

    def parse_html(self, element):
        if element.tag in self.IGNORE_TAGS:
            return

        if element.tag in self.VSPACES:
            self.text_box.widgets.append(VSpaceWidget(self.VSPACES[element.tag]))

        if element.tag in self.ATTR:
            self.attr_stack.append(self.attr_current)
            self.attr_current = self.ATTR[element.tag]
            self.text_box.widgets.append(self.attr_current)

        if element.tag == 'a':
            self.link_current = Link(href=element.get('href'))
            self.links.append(self.link_current)

            self.attr_stack.append(self.attr_current)
            self.attr_current = AttributeWidget(fg=(0,0,255,255), bg=(255,255,255,255))
            self.text_box.widgets.append(self.attr_current)

        if element.tag in ['ul', 'ol']:
            self.text_box.widgets.append(IndentWidget(32))

        if element.text != None:
            for word in element.text.split():
                text_widget = TextWidget(word)
                self.text_box.widgets.append(text_widget)

                if self.link_current != None:
                    self.link_current.widgets.append(text_widget)

        for child in element:
            self.parse_html(child)

        if element.tag in self.ATTR:
            self.attr_current = self.attr_stack.pop()
            self.text_box.widgets.append(self.attr_current)

        if element.tag == 'a':
            self.link_current = None

            self.attr_current = self.attr_stack.pop()
            self.text_box.widgets.append(self.attr_current)

        if element.tag in ['ul', 'ol']:
            self.text_box.widgets.append(IndentWidget(-32))

        if element.tail != None:
            for word in element.tail.split():
                text_widget = TextWidget(word)
                self.text_box.widgets.append(text_widget)

                if self.link_current != None:
                    self.link_current.widgets.append(text_widget)

    def set_size(self, size):
        self.width     = size[0]
        self.height    = size[1]
        self.g.surface = pygame.display.set_mode(size, pygame.RESIZABLE)

        self.address_bar.set_size(self.width, 20)
        self.address_bar.layout(self.g)

        self.text_box.set_size(self.width, self.height - 20)
        self.text_box.layout(self.g)

        self.draw(self.g)

    def draw(self, g):
        g.fg = (  0,   0,   0, 255)
        g.bg = (255, 255, 255, 255)
        g.font = self.fonts.normal

        self.g.surface.set_clip(None)
        g.surface.fill(g.bg)
        self.address_bar.draw(g)
        self.text_box.draw(g)

    def on_action(self, widget):
        self.load(self.address_bar.text)
        self.draw(self.g)
        pygame.display.flip()

    def on_focus(self):
        pass

    def on_blur(self):
        pass

    def on_mouse_down(self, event):
        pass

    def on_mouse_up(self, event):
        for link in self.links:
            if link.is_point_in(event.pos[0] - self.text_box.left + self.text_box.scroll_left,
                                event.pos[1] - self.text_box.top  + self.text_box.scroll_top):
                self.load(link.href)
                break

    def on_key_down(self, event):
        if   event.key == pygame.K_UP   : self.text_box.scroll(  0, -5)
        elif event.key == pygame.K_DOWN : self.text_box.scroll(  0,  5)
        elif event.key == pygame.K_LEFT : self.text_box.scroll(-20,  0)
        elif event.key == pygame.K_RIGHT: self.text_box.scroll( 20,  0)

        elif event.key == pygame.K_PAGEUP  : self.text_box.scroll_page_up()
        elif event.key == pygame.K_PAGEDOWN: self.text_box.scroll_page_down()

        elif event.key == pygame.K_HOME:
            if event.mod & pygame.KMOD_SHIFT:
                self.text_box.scroll_x_home()
            else:
                self.text_box.scroll_y_home()

        elif event.key == pygame.K_END:
            if event.mod & pygame.KMOD_SHIFT:
                self.text_box.scroll_x_end()
            else:
                self.text_box.scroll_y_end()

    def run(self):
        while True:
            if self.active_widget.do_redraw:
                self.active_widget.draw(self.g)
                pygame.display.flip()

            if self.active_widget == self:
                self.do_redraw = True
            else:
                self.do_redraw = False

            event = pygame.event.wait()

            if event.type == pygame.QUIT:
                break

            if event.type == pygame.VIDEORESIZE:
                self.set_size(event.size)

            if event.type == pygame.MOUSEMOTION:
                self.do_redraw = False

            if event.type == pygame.MOUSEBUTTONDOWN:
                if self.address_bar.is_point_in(event.pos[0], event.pos[1]):
                    new_active_widget = self.address_bar
                else:
                    new_active_widget = self

                if new_active_widget != self.active_widget:
                    self.active_widget.on_blur()
                    self.active_widget.draw(self.g)

                    self.active_widget = new_active_widget

                    self.active_widget.on_focus()
                    self.active_widget.draw(self.g)

                    pygame.display.flip()

                self.active_widget.on_mouse_down(event)

            if event.type == pygame.MOUSEBUTTONUP:
                self.active_widget.on_mouse_up(event)

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    break

                self.active_widget.on_key_down(event)

    def quit(self):
        pygame.quit()