# -*- coding: utf-8 -*-

# Pyntor::Pyromaniac - Display HTML pages
# Copyright (C) 2004 - 2006 Josef Spillner <josef@coolprojects.org>
# Published under GNU GPL conditions

# Based on the Pyromaniac program
# http://pyro.coolprojects.org/pyromaniac.html

### External python modules ============================================== ###
###
###

import formatter
import htmllib
import urllib
import urlparse
import re
import md5
import os
import sys

import pygame

### Global variables ===================================================== ###
###
###

progresscount = 0
progresstotal = 0
progressenabled = 0

zoomfactor = 100

globalx = 0
globaly = 0
globalpage = None

pagewidth = 800
pageheight = 6000

displaywidth = 800
displayheight = 600

searchterm = None

### Table container class ================================================ ###
###
###

class table:

	### Initialize a table with inheritance

	def __init__(self, ancestor, name):
		self.name = name
		self.children = []
		self.x = 0
		self.y = 0
		self.w = 0
		self.h = 0
		self.content = None
		if ancestor is not None:
			self.parent = ancestor
			if ancestor.align == "horizontal":
				self.align = "vertical"
			else:
				self.align = "horizontal"
			ancestor.children.append(self)
		else:
			self.parent = None
			self.align = "vertical"

	### Dump a table structure

	def dump(self, x = 0):
		p = ""
		for i in range(0, x):
			p += "  "
		p += self.name + " "
		p += str(self.x) + "/" + str(self.y) + " - " + str(self.w) + "/" + str(self.h)
		p += " " + self.align
		print p
		for c in self.children:
			c.dump(x + 1)

	### Fix all absolute size values

	def cons(self):
		givenw = 0
		givenh = 0
		count = 0
		for c in self.children:
			if self.align == "horizontal":
				if c.w > 0:
					givenw += c.w
					count += 1
				c.h = self.h
			if self.align == "vertical":
				if c.h > 0:
					givenh += c.h
					count += 1
				c.w = self.w
		if len(self.children) <= count:
			return
		if self.align == "horizontal":
			n = (self.w - givenw) / (len(self.children) - count)
			for c in self.children:
				if c.w == 0:
					c.w = n
		else:
			n = (self.h - givenh) / (len(self.children) - count)
			for c in self.children:
				if c.h == 0:
					c.h = n
		for c in self.children:
			c.cons()

	### Normalize widths or heights

	def normalize(self, direction):
		if direction == "vertical":
			if len(self.children) == 0:
				print "##### weird!"
				return
			l = len(self.children[0].children)
			max = 0
			for d in range(0, l):
				max = 0
				for c in self.children:
					if d < len(c.children):
						if c.children[d].w > max:
							max = c.children[d].w
				for c in self.children:
					if d < len(c.children):
						c.children[d].w = max
			if self.w == 0:
				self.w = max * l
		elif direction == "horizontal":
			for c in self.children:
				max = 0
				for d in c.children:
					if d.h > max:
						max = d.h
				for d in c.children:
					d.h = max
				c.h = max

	### Finish a table by converting all sizes

	def finish(self):
		y = 0
		for c in self.children:
			x = 0
			for d in c.children:
				d.x = x
				d.w += x
				x = d.w
			c.y = y
			c.h += y
			y = c.h

### Helper classes ======================================================= ###
###
###

### Surface data without content allocation

class dummypage:
	def __init__(self, w):
		self.w = w

	def get_width(self):
		return self.w

### Stylesheet top class

class stylesheet:
	def __init__(self):
		self.ident = None
		self.element = None
		self.styles = {}

### Surface enhancements with absolute positions

class surface:
	def __init__(self, surface):
		self.surface = surface
		self.original = None
		self.x = -1
		self.y = -1

### Render part

class renderpart:
	def __init__(self):
		self.x = -1
		self.y = -1
		self.surfaces = []
		self.content = []
		self.attributes = {}
		self.children = []
		self.parent = None
		self.activated = 0
		self.container = None
		self.hyperlink = None

### Main render object containing the renderpart tree

class autodom:
	def __init__(self, object):
		self.ref = object

	def __str__(self):
		return self.ref.attributes['content']

	def __setitem__(self, key, value):
		if self.ref is not None:
			self.ref.attributes[key] = value

	def __getitem__(self, key):
		print "#>>", key
		if self.ref is not None:
			return self.ref.attributes['content'][key]

	def __getattr__(self, name):
		if name == "ref":
			return self.ref

		print ">>>", name, "<<<"
		newref = None
		if self.ref is not None:
			for childpart in self.ref.children:
				print "???", childpart.attributes['__type']
				if childpart.attributes['__type'] == name:
					newref = childpart
					print "!!!", newref.attributes

		x = autodom(newref)
		return x

class renderobject:
	def __init__(self):
		self.title = None
		self.root = renderpart()
		self.part = self.root
		self.bodypart = None
		self.stylepart = None
		self.resources = []
		self.containers = []
		self.dom = autodom(self.root)

### HTML validation actions

def validator(str):
	print str

### Return a string backwards

def reverse(s):
	rs = ""
	for x in s:
		rs = x + rs
	return rs

### Return the int representation of a color component

def hex2bin(hex):
	number = 0
	factor = 1
	for x in reverse(hex):
		n = x
		if n == 'a' or n == "A":
			n = '10'
		if n == 'b' or n == "B":
			n = '11'
		if n == 'c' or n == "C":
			n = '12'
		if n == 'd' or n == "D":
			n = '13'
		if n == 'e' or n == "E":
			n = '14'
		if n == 'f' or n == "F":
			n = '15'
		#try:
		number = number + int(n) * factor
		#except:
		#	print "**** unhandled color: " + hex
		factor = factor * 16
	return number

### Return a color tuple

def getcolor(str):
	map = {
		"blue": "#0000ff",
		"yellow": "#ffff00",
		"teal": "#207090",
		"white": "#ffffff",
		"red": "#0000ff",
		"gray": "#c0c0c0",
		"purple": "#ff00ff",
		"black": "#000000"
	}

	if str.lower() in map.keys():
		rgbcolor = map[str.lower()]
	else:
		if str[0:1] != "#":
			str = "#" + str
		rgbcolor = str
	colors = (hex2bin(rgbcolor[1:3]), hex2bin(rgbcolor[3:5]), hex2bin(rgbcolor[5:7]))
	return colors

### Parser class ========================================================= ###
###
###

class myparser(htmllib.HTMLParser):

	### Initialize parser

	def __init__(self):
		f = formatter.NullFormatter()
		htmllib.HTMLParser.__init__(self, f)
		self.statelist = []
		self.statestack = []
		self.object = None
		self.container = None
		self.baseurl = ""

		self.tempdir = "/tmp/pyromaniac/"

		self.nextslide = ""
		self.previousslide = ""

	### Invoke end tag handler

	def __getattr__(self, name):
		if name[0:3] == "end":
			tag = name[4:]
			self.handle_endtag(tag, None)

	### Always return the absolute URL

	def correcturl(self, url):
		return urlparse.urljoin(self.baseurl, url)

	### Download an external resource (may be cached)

	def download(self, url):
		url = self.correcturl(url)
		localfile = self.tempdir + md5.md5(url).hexdigest()
		try:
			file = open(localfile, "r")
			self.debug("DOWNLOADING (cached): " + url)
		except:
			self.debug("DOWNLOADING (live): " + url)
			try:
				file = urllib.urlopen(url)
			except:
				file = None
				validator("Warning! Resource " + url + " not found!")
		progress(-1)
		if file is not None:
			contents = file.read()
			file.close()
			try:
				os.mkdir(self.tempdir)
			except:
				pass
			local = open(localfile, "w")
			local.writelines(contents)
			local.close()
			return localfile
		return None

	### Parse a file or URL

	def parse(self, filename, object):
		self.baseurl = filename
		if filename[0:7] == "http://":
			f = self.download(filename)
			filename = f
		try:
			contents = open(filename).read()
		except:
			return
		contents = re.sub("\\n", " ", contents) #### FIXME! TODO!
		contents = re.sub("\\t", " ", contents) #### FIXME! TODO!

		while 1:
			match = re.search("\\ \\ ", contents)
			if match:
				contents = re.sub("\\ \\ ", " ", contents) #### FIXME! TODO!
			else:
				break

		self.object = object

		# hack-ish: pyro navigation commands in comments
		self.nextslide = ""
		self.previousslide = ""
		pos = contents.find("pyro:next")
		if pos != -1:
			pos2 = contents.find(" ", pos + 10)
			self.nextslide = contents[pos + 10:pos2]
		pos = contents.find("pyro:previous")
		if pos != -1:
			pos2 = contents.find(" ", pos + 14)
			self.previousslide = contents[pos + 14:pos2]
		print "COMMENTS: next=", self.nextslide, "previous=", self.previousslide

		self.feed(contents)
		self.close()
		print "!!!! linklist: ", self.object.resources

	### Debug function

	def debug(self, str):
		#print str
		pass

	### Handler for HTML tag data

	def handle_data(self, data):
		if len(self.statelist) > 0:
			state = self.statelist[len(self.statelist) - 1]
			self.object.part = self.statestack[len(self.statestack) - 1]
		else:
			state = "[invalid]"
			stack = None
		self.debug("DATA " + data + " in " + state)

		if state == "html":
			return
		if state == "title":
			self.object.title = self.object.part
			self.object.part.attributes['content'] = [data]
			return
		if state == "style":
			self.object.part.attributes['content'] = [data]
			return

		if state == "table" or state == "tr":
			return

		tags = ["body", "h1", "h2", "h3", "h4", "h5", "h6", "h7", "li", "hr"]
		tags += ["td", "a", "b", "i", "p", "font", "tt", "sub", "sup", "s"]
		tags += ["strike", "big", "small", "u", "center", "th", "form", "div"]
		tags += ["span", "em", "strong"]
		if state in tags:
			if len(self.object.part.children) > 0:
				child = self.object.part.children[-1]
				if child.attributes['__type'] == "__text":
					child.attributes['content'] = [child.attributes['content'][0] + data]
					return

			if data == " ":
				return

			child = renderpart()
			child.parent = self.object.part
			self.object.part.children.append(child)
			child.attributes['__type'] = "__text"
			child.attributes['content'] = [data]
			if state == "span":
				child.attributes['css::color'] = "#106f10"
		else:
			child = renderpart()
			child.parent = self.object.part
			self.object.part.children.append(child)
			child.attributes['__type'] = "__text"
			child.attributes['css::color'] = "#c0c0c0"
			child.attributes['css::strikethrough'] = 1
			child.attributes['content'] = [data]

	### Handler for HTML tag begin

	def handle_starttag(self, tag, data, attributes):
		global pagewidth

		self.debug("<" + tag + ">")
		child = renderpart()
		child.parent = self.object.part
		self.object.part.children.append(child)
		child.attributes['__type'] = tag
		for attribute in attributes:
			child.attributes[attribute[0]] = attribute[1]

		single = ["br", "img", "input", "hr"]
		if tag not in single:
			self.object.part = child
			self.statelist.append(tag)
			self.statestack.append(child)

		state = tag

		self.debug(">> new state could be: " + state)

		resource = None
		if tag == "img":
			if child.attributes.has_key('src'):
				resource = child.attributes['src']
		if tag == "td" or tag == "th" or tag == "body":
			if child.attributes.has_key('background'):
				resource = child.attributes['background']
		if resource is not None:
			self.object.resources.append(resource)

		if tag == "body":
			self.object.bodypart = self.object.part
		if tag == "style":
			self.object.stylepart = self.object.part

		### Container handling

		if tag == "table" or tag == "tr" or tag == "td" or tag == "th" or tag == "body":
			if tag == "body":
				# QUICK HACK
				self.container = table(self.container, "(body-table)")
				self.container = table(self.container, "(body-tr)")
			self.container = table(self.container, tag)
			a = self.object.part.attributes
			if a.has_key('width'):
				w = a['width']
				if w[-1:] == "%":
					w = pagewidth
				self.container.w = int(w)
			if a.has_key('height'):
				h = a['height']
				if h[-1:] == "%":
					h = 800
				self.container.h = int(h)
			if tag == "td" or tag == "th" or tag == "body":
				self.container.content = self.object.part
				self.object.part.container = self.container
			print "##### TABLE #####", tag, "for", self.object.part.attributes['__type']

		self.debug(">> new state is: " + state)

	### Handler for HTML tag end

	def handle_endtag(self, tag, foo):
		single = ["br", "img", "input", "hr"]
		if tag in single:
			self.debug("** circumvent " + tag)
			return

		self.debug("</" + tag + ">")
		self.statelist = self.statelist[:-1]
		self.statestack = self.statestack[:-1]

		### Container handling

		if self.container is not None:
			if self.container.name == tag:
				old = self.container
				self.container = self.container.parent
				if tag == "body":
					# QUICK HACK
					self.container = self.container.parent
					old = self.container
					self.container = self.container.parent
				if self.container == None:
					print "##### TABLE FINISHED #####", tag
					self.object.containers.append(old)

		if len(self.statelist) > 0:
			state = self.statelist[len(self.statelist) - 1]
			self.object.part = self.statestack[len(self.statestack) - 1]
		else:
			state = "[invalid]"
			stack = None
		self.debug(">> new state is now again: " + state)

	### Optimize all headings (h1-h7) to __h

	def optimizer_headings(self, part):
		a = part.attributes
		if a.has_key('__type'):
			if a['__type'] == "h1":
				a['__type'] = "__h"
				a['__headingsize'] = 1
			if a['__type'] == "h2":
				a['__type'] = "__h"
				a['__headingsize'] = 2
			if a['__type'] == "h3":
				a['__type'] = "__h"
				a['__headingsize'] = 3
			if a['__type'] == "h4":
				a['__type'] = "__h"
				a['__headingsize'] = 4
			if a['__type'] == "h5":
				a['__type'] = "__h"
				a['__headingsize'] = 5
			if a['__type'] == "h6":
				a['__type'] = "__h"
				a['__headingsize'] = 6
			if a['__type'] == "h7":
				a['__type'] = "__h"
				a['__headingsize'] = 7

		for part in part.children:
			self.optimizer_headings(part)

	### Generic tree walker method

	def optimizer_treewalk(self, part, attribute, value):
		part.attributes[attribute] = value
		for part in part.children:
			self.optimizer_treewalk(part, attribute, value)

	### Generic additive tree walker method

	def optimizer_treewalk_add(self, part, attribute, value):
		print "XXX ADD", value, "TO", attribute, "IN", part
		if part.attributes.has_key(attribute):
			part.attributes[attribute] += value
		else:
			part.attributes[attribute] = value
		for part in part.children:
			self.optimizer_treewalk_add(part, attribute, value)

	### Non-destructive supplementive tree walker

	def optimizer_treewalk_fill(self, part, attribute, value):
		if not part.attributes.has_key(attribute):
			part.attributes[attribute] = value
			for childpart in part.children:
				self.optimizer_treewalk_fill(childpart, attribute, value)

	### Optimize all attributes (except CSS)

	def optimizer_attributes(self, part):
		a = part.attributes
		if part.parent != None:
			clist = part.parent.children
		if a.has_key('__type'):
			types = ['td', 'th', 'tr', 'table', 'body']
			if a['__type'] in types:
				if a.has_key('bgcolor'):
					for childpart in part.children:
						self.optimizer_treewalk_fill(childpart, "bgcolor", a['bgcolor'])
				if not a.has_key('border'):
					if a['__type'] != "body":
						a['border'] = 1
				if a.has_key('border'):
					for childpart in part.children:
						self.optimizer_treewalk_fill(childpart, "border", a['border'])
			if a['__type'] == "font":
				for childpart in part.children:
					if a.has_key('size'):
						size = a['size']
						if size == "small":
							size = "-1"
						if size[0:1] == "+" or size[0:1] == "-":
							rel = size[0:1] + str(int(size) * 5)
							self.optimizer_treewalk(childpart, "css::fontsizerel", rel)
						elif size != "":
							self.optimizer_treewalk(childpart, "css::font-size", int(size) * 7)
					if a.has_key('color'):
						self.optimizer_treewalk(childpart, "css::color", a['color'])
					childpart.parent = part.parent
					clist.insert(clist.index(part), childpart)
				part.parent.children.remove(part)
				part = part.parent
			if a['__type'] == "__h":
				size = 48
				if a['__headingsize'] == 1:
					size = "49"
				if a['__headingsize'] == 2:
					size = "42"
				if a['__headingsize'] == 3:
					size = "35"
				if a['__headingsize'] == 4:
					size = "28"
				if a['__headingsize'] == 5:
					size = "21"
				if a['__headingsize'] == 6:
					size = "14"
				if a['__headingsize'] == 7:
					#size = "5" ### FIXME: pygame bug
					size = "10"
				childpart = part.children[0]
				if childpart is not None:
					childpart.attributes['__prebreak'] = 1
				childpart = None
				for childpart in part.children:
					self.optimizer_treewalk(childpart, "css::font-size", size)
					childpart.parent = part.parent
					clist.insert(clist.index(part), childpart)
				newpart = renderpart()
				newpart.attributes['__type'] = "br"
				clist.insert(clist.index(part), newpart)

				part.parent.children.remove(part)
				part = part.parent
			if a['__type'] == "big":
				for childpart in part.children:
					self.optimizer_treewalk(childpart, "css::sizerel", 1);
					clist.insert(clist.index(part), childpart)
					childpart.parent = part.parent
				part.parent.children.remove(part)
				part = part.parent
			if a['__type'] == "small":
				for childpart in part.children:
					self.optimizer_treewalk(childpart, "css::sizerel", -1);
					clist.insert(clist.index(part), childpart)
					childpart.parent = part.parent
				part.parent.children.remove(part)
				part = part.parent

			if a['__type'] == "form":
				for childpart in part.children:
					self.optimizer_treewalk(childpart, "__form", 1);
					clist.insert(clist.index(part), childpart)
					childpart.parent = part.parent
				part.parent.children.remove(part)
				part = part.parent

			css = {
				"i": "css::italic",
				"b": "css::font-weight",
				"em": "css::font-weight",
				"strong": "css::font-weight",
				"tt": "css::fixedwidth",
				"u": "css::underline",
				"s": "css::strikethrough",
				"strike": "css::strikethrough",
				"sup": "css::superscript",
				"sub": "css::subscript",
				"pre": "css:formatted",
				"center": "css::centered"
			}
			if css.has_key(a['__type']):
				for childpart in part.children:
					self.optimizer_treewalk(childpart, css[a['__type']], 1);
					clist.insert(clist.index(part), childpart)
					childpart.parent = part.parent
				clist.remove(part)
				part = part.parent

			if a['__type'] == "ol" or a['__type'] == "dl":
				i = 0
				for childpart in part.children:
					if childpart.attributes['__type'] == "li":
						i += 1
						self.optimizer_treewalk(childpart, "css::ordered", i);
						self.optimizer_treewalk_add(childpart, "css::leftmargin", 20);
						clist.insert(clist.index(part), childpart)
						childpart.parent = part.parent
				part.parent.children.remove(part)
				part = part.parent
			if a['__type'] == "ul":
				for childpart in part.children:
					self.optimizer_treewalk(childpart, "css::unordered", 1);
					self.optimizer_treewalk_add(childpart, "css::leftmargin", 20);
					clist.insert(clist.index(part), childpart)
					childpart.parent = part.parent
				part.parent.children.remove(part)
				part = part.parent

			if a['__type'] == "body" or a['__type'] == "td" or a['__type'] == "table":
				print "XXX OPTIMIZE MARGIN", a['__type']
				if a.has_key('leftmargin'):
					for childpart in part.children:
						self.optimizer_treewalk_add(childpart, "css::leftmargin", int(a['leftmargin']));
				if a.has_key('topmargin'):
					for childpart in part.children:
						self.optimizer_treewalk_add(childpart, "css::topmargin", int(a['topmargin']));

			if a['__type'] == "a":
				if a.has_key('href'):
					for childpart in part.children:
						self.optimizer_treewalk(childpart, "href", a['href']);
						clist.insert(clist.index(part), childpart)
						childpart.parent = part.parent
				elif a.has_key('name'):
					for childpart in part.children:
						self.optimizer_treewalk(childpart, "name", a['name']);
						clist.insert(clist.index(part), childpart)
						childpart.parent = part.parent
				else:
					validator("A is missing HREF or NAME")
				part.parent.children.remove(part)
				part = part.parent

			if a['__type'] == "tr" or a['__type'] == "table":
				a['__break'] = 1

			if a['__type'] == "li" or a['__type'] == "dd" or a['__type'] == "dt":
				childpart = renderpart()
				if a.has_key('css::unordered'):
					childpart.attributes['__type'] = "bullet"
					childpart.attributes['css::bullettype'] = "round"
				elif a.has_key('css::ordered'):
					childpart.attributes['__type'] = "__text"
					childpart.attributes['content'] = [str(a['css::ordered']) + "."]
				else:
					validator("LI is not enclosed by OL or UL")
				clist.insert(clist.index(part), childpart)
				childpart.attributes['__prebreak'] = 1
				if a.has_key('css::leftmargin'):
					childpart.attributes['css::leftmargin'] = a['css::leftmargin']
				childpart.parent = part.parent
				childpart = None
				for childpart in part.children:
					clist.insert(clist.index(part), childpart)
					childpart.parent = part.parent
				clist.remove(part)
				part = part.parent
				if childpart != None:
					childpart.attributes['__break'] = 1

		for childpart in part.children:
			if a.has_key('__type'):
				print "XXX I'm a", a['__type'], "my child is a", childpart.attributes['__type']
			else:
				print "XXX ???"
			self.optimizer_attributes(childpart)

	### Dump the optimized HTML tree

	def dump(self, part, level):
		a = part.attributes
		if a.has_key('__type'):
			tag = a['__type']
		else:
			tag = "(unknown)"
		tagline = ""
		for i in range(0, level):
			tagline += " "
		tagline += "<" + tag
		for key in a:
			if key != "__type":
				value = a[key]
				if type(value) == type([]):
					value = "[list:" + value[0] + "]"
				if type(value) == type(0):
					value = str(value)
				tagline += " " + key + "=\"" + value + "\""
		tagline += ">"
		print tagline
		if len(part.surfaces) > 0:
			self.debug("# surfaces" + str(part.surfaces))
		if part.container is not None:
			self.debug("() container" + str(part.container))
		for childpart in part.children:
			self.dump(childpart, level + 1)
		if len(part.children) > 0:
			tagline = ""
			for i in range(0, level):
				tagline += " "
			tagline += "</" + tag + ">"
			print tagline

	### Apply a CSS style to a parse tree

	def optimizer_styleapply(self, part, style, active):
		a = part.attributes
		if a.has_key('__type'):
			self.debug("CSS DETECTION: " + part.attributes['__type'])
			if a.has_key('class'):
				if a['class'] == style.ident:
					active = 1
					self.debug("CSS IS ACTIVE!")

			if active:
				if style.element == None or style.element == a['__type']:
					for s in style.styles.keys():
						self.debug("CSS APPLY!")
						a["css::" + s] = style.styles[s]

		for childpart in part.children:
			self.optimizer_styleapply(childpart, style, active)

	### Apply a list of CSS styles to a parse tree

	def optimizer_style(self, part, stylelist):
		if stylelist == None:
			return
		print "*** CASCADING STYLE SHEETS ***"
		for s in stylelist:
			print "NAME", s.ident, "ELEM", s.element, "STYLES", s.styles
			self.optimizer_styleapply(part, s, 0)

	### Parse document style sheet (CSS) definitions

	def stylesheetparser(self, part):
		if part == None:
			return
		if not part.attributes.has_key('content'):
			return

		definition = part.attributes['content']
		tokens = definition[0].split(" ")
		state = "ident"
		stylelist = []
		style = None
		attribute = None
		for t in tokens:
			if t == "":
				continue

			if state == "ident":
				state = "element-or-brace"
				style = stylesheet()
				ident = t
				if ident[0] == ".":
					ident = ident[1:]
				style.ident = ident
				stylelist.append(style)
			elif state == "element-or-brace":
				if t == "{":
					state = "attribute"
				else:
					state = "brace"
					style.element = t
			elif state == "brace":
				state = "attribute"
			elif state == "attribute":
				if t == "}":
					state = "ident"
				else:
					state = "value"
					attribute = t[:-1]
			elif state == "value":
				state = "attribute"
				style.styles[attribute] = t[:-1]
			else:
				print "Invalid state! " + state

		return stylelist

### More helper classes and methods ====================================== ###
###
###

### Display download progress

def progress(p):
	global navpage
	global progresscount
	global progresstotal
	global pagewidth
	global progressenabled

	if not progressenabled:
		return

	if p < 0:
		progresscount -= 1
	else:
		progresscount = p
		progresstotal = p
	print "-- progress --", progresscount

	if progresstotal > 0:
		bar = pygame.Surface((pagewidth, 10))
		bar.fill((0, 0, 140))
		bar.fill((0, 0, 200), (0, 0, pagewidth - pagewidth * progresscount / progresstotal, 10))

		#navpage.blit(bar, (0, 0))
		#screen.blit(navpage, (0, 10))
		screen.blit(bar, (0, 0))
		pygame.display.update()

### Return a container background part

def containerrender(part):
	a = part.attributes

	colors = None
	img = None
	if a.has_key('bgcolor'):
		rgbcolor = a['bgcolor']
		colors = getcolor(rgbcolor)
	elif a.has_key('css::background'):
		rgbcolor = a['css::background']
		colors = getcolor(rgbcolor)
	else:
		colors = (255, 255, 255)
	if a.has_key('background'):
		bg = parser.download(a['background'])
		img = pygame.image.load(bg)
		#img = pygame.transform.scale(img, (pagewidth, 600))

	x = None
	if a['__type'] == "th" or a['__type'] == "td" or a['__type'] == "body":
		w = 100
		h = 100
		if a['__type'] == "body":
			w = pagewidth
			h = pageheight
		if a.has_key('width'):
			wx = a['width']
			if type(wx) == type(""):
				if wx[-1:] == "%":
					wx = "200" ### FIXME!
			w = int(wx)
		if a.has_key('height'):
			hx = a['height']
			if type(hx) == type(""):
				if hx[-1:] == "%":
					hx = "200" ### FIXME!
			h = int(hx)
		if w > 0 and h > 0:
			x = pygame.Surface((w, h))
			x.fill(colors)

			if img is not None:
				x.blit(img, (0, 0))

			if a.has_key('border'):
				for j in range(0, x.get_height()):
					for q in range(0, int(a['border'])):
						x.set_at((q, j), (0, 0, 0))
						x.set_at((x.get_width() - 1 - q, j), (0, 0, 0))
				for i in range(0, x.get_width()):
					for q in range(0, int(a['border'])):
						x.set_at((i, q), (0, 0, 0))
						x.set_at((i, x.get_height() - 1 - q), (0, 0, 0))

	return x

### Font enhancement class

class fontobject:
	def __init__(self, font, rfontsize):
		self.font = font
		self.rfontsize = rfontsize

### Return an appropriate font

def getfont(part):
	a = part.attributes

	fontsize = 15
	if a.has_key('css::font-size'):
		param = str(a['css::font-size'])
		if len(param) > 2 and param[-2:] == "pt":
			size = param[:-2]
			fontsize = int(size) * 2
		else:
			fontsize = int(param)
	if a.has_key('css::fontsizerel'):
		relsize = str(a['css::fontsizerel'])
		sign = relsize[0:1]
		amount = relsize[1:]
		if sign == "-":
			fontsize -= int(amount)
		elif sign == "+":
			fontsize += int(amount)
	rfontsize = fontsize
	if a.has_key('css::superscript'):
		fontsize = int(fontsize * 0.7)
	if a.has_key('css::subscript'):
		fontsize = int(fontsize * 0.7)
	if fontsize < 10:
		fontsize = 10 # FIXME! pygame bug
	font = pygame.font.Font(None, fontsize)

	if a.has_key('css::font-weight'):
		font.set_bold(1)
	if a.has_key('css::italic'):
		font.set_italic(1)
	if a.has_key('css::underline'):
		font.set_underline(1)

	return fontobject(font, rfontsize)

### Return appropriate font colors

def getfontcolors(part):
	a = part.attributes

	if a.has_key('css::color'):
		rgbcolor = a['css::color']
		colors = getcolor(rgbcolor)
	else:
		colors = (0, 0, 0)
	if a.has_key('href'):
		colors = (255, 255, 0)

	return colors

### Render a text using a given font

def fontrender(part, text, fontobject = None, colors = None):
	global searchterm

	a = part.attributes

	if fontobject == None:
		fontobject = getfont(part)
	if colors == None:
		colors = getfontcolors(part)

	if a.has_key('bgcolor'):
		bgcol = getcolor(a['bgcolor'])
	else:
		bgcol = (255, 255, 255)

	if searchterm is not None:
		if text.find(searchterm) >= 0:
			print "*** SEARCH MATCH ***"
			bgcol = (200, 100, 0)
			#a['bgcolor'] = "#e09000"
	#print "'" + searchterm + "'", "'" + text + "'"

	font = fontobject.font
	rfontsize = fontobject.rfontsize
	fontsize = font.get_height()

	echar = chr(160)
	#print "1-RENDER '" + text + "'"
	#if text == echar:
	#	print "TABBED!"
	#	text = ""
	text = text.replace(chr(160), " ")
	textline = font.render(text, 1, colors)
	tmp = pygame.Surface((textline.get_width(), rfontsize))
	tmp.fill(bgcol) ### ALPHA
	tmp.blit(textline, (0, 0))
	textline = tmp

	if a.has_key('css::strikethrough'):
		j = textline.get_height() / 2
		for i in range(0, textline.get_width()):
			textline.set_at((i, j), colors)
	if a.has_key('css::superscript') or a.has_key('css::subscript'):
		tmp = pygame.Surface((textline.get_width(), rfontsize))
		tmp.fill(bgcol) ### ALPHA
		if a.has_key('css::superscript'):
			tmp.blit(textline, (0, 0))
		else:
			tmp.blit(textline, (0, rfontsize - fontsize))
		textline = tmp
	if a.has_key('css::centered'): ### FIXME! centering
		tmp = pygame.Surface((textline.get_width(), rfontsize))
		tmp.fill(bgcol) ### ALPHA
		tmp.blit(textline, (tmp.get_width() / 2 - textline.get_width() / 2, 0))
		textline = tmp

	return textline

### Render each element of a parse tree

def render(part):
	global pagewidth

	if part == None:
		return
	a = part.attributes
	if a.has_key('__type'):
		if a['__type'] == "img":
			if a.has_key('src'):
				src = parser.download(a['src'])
				try:
					img = pygame.image.load(src)
					width = img.get_width()
					height = img.get_height()
					if a.has_key('width'):
						width = int(a['width'])
					if a.has_key('height'):
						height = int(a['height'])
					img = pygame.transform.scale(img, (width, height))
				except:
					width = 20
					height = 20
					if a.has_key('width'):
						width = int(a['width'])
					if a.has_key('height'):
						width = int(a['height'])
					img = pygame.Surface((width, height))
					img.fill((255, 0, 0))
				s = surface(img)
				#part.surfaces.append(s)
				part.surfaces = [s]
			else:
				validator("IMG is missing SRC")
		elif a['__type'] == "bullet":
			if a.has_key('css::color'):
				rgbcolor = a['css::color']
				colors = getcolor(rgbcolor)
			else:
				colors = (0, 0, 0)
			img = pygame.Surface((7, 7))
			img.fill(colors)
			s = surface(img)
			part.surfaces.append(s)
		elif a['__type'] == "hr":
			img = pygame.Surface((pagewidth - 20, 5))
			img.fill((255, 200, 200))
			s = surface(img)
			part.surfaces.append(s)
		elif a['__type'] == "input":
			type = "text"
			if a.has_key('width'):
				width = int(a['width'])
			else:
				width = 300
			if a.has_key('type'):
				type = a['type']
			if type == "text":
				img = pygame.Surface((width, 20))
				img.fill((255, 200, 200))
			elif type == "checkbox":
				img = pygame.Surface((20, 20))
				img.fill((255, 200, 200))
				if a.has_key('checked'):
					if a['checked'] == "CHECKED":
						img.fill((0, 0, 0), (5, 5, 10, 10))
			elif type == "submit":
				title = "Submit"
				if a.has_key('value'):
					title = a['value']
				fontsize = 20
				font = pygame.font.Font(None, fontsize)
				print "2-RENDER '", title, "'"
				text = font.render(title, 1, (0, 0, 0))
				img = pygame.Surface((text.get_width(), text.get_height()))
				img.fill((255, 200, 200))
				img.blit(text, (0, 0))
			else:
				img = pygame.Surface((20, 20))
				img.fill((255, 200, 200))
			s = surface(img)
			part.surfaces.append(s)
		elif a['__type'] == "body":
			img = containerrender(part)
			font = pygame.font.Font(None, 20)
			for text in part.content:
				print "3-RENDER '", text, "'"
				textline = font.render(text, 1, (0, 0, 0), colors)
				s = surface(textline)
				part.surfaces.append(s)
		elif a['__type'] == "__text":
			fontobject = getfont(part)
			colors = getfontcolors(part)
			font = fontobject.font
			rfontsize = fontobject.rfontsize

			width = pagewidth - 100 ### FIXME: containers
			textstrategy = "words" ### words or width
			for text in a['content']:
				(x, y) = font.size(text)
				txt = text
				while len(txt):
					if textstrategy == "words":
						txt2 = ""
						ready = 0
						while not ready:
							txt2 += txt[0:1]
							txt = txt[1:]
							if txt2[-1:] == " ":
								ready = 1
							if len(txt) == 0:
								ready = 1
					elif textstrategy == "width":
						txt2 = txt
						while font.size(txt2)[0] > width and len(txt2):
							txt2 = txt2[:-1]
						txt = txt[len(txt2):]
						if len(txt2) == 0:
							txt2 = txt
							txt = ""
					else:
						print "Error! Unknown text strategy"
					a['content'].insert(a['content'].index(text), txt2)
				a['content'].remove(text)

			for text in a['content']:
				if text != " ":
					textline = fontrender(part, text, fontobject, colors)
					s = surface(textline)
					part.surfaces.append(s)
	for part in part.children:
		render(part)

### Class representing a hyperlink

class hyperlink:
	def __init__(self, surface, x, y, part, xoff, yoff, page):
		self.surface = surface
		self.x = x
		self.y = y
		self.part = part
		self.xoff = xoff
		self.yoff = yoff
		self.page = page

### Return user input from a text input field

def getinput(h):
	url = ""
	shadow = {}
	shift = 0
	while 1:
		pygame.event.pump()
		keystates = pygame.key.get_pressed()
		for keycode in range(0, len(keystates)):
			key = pygame.key.name(keycode)
			if not shadow.has_key(keycode):
				shadow[keycode] = 0
			if keystates[keycode] and not shadow[keycode]:
				print "keycode", keycode, "key", key
				shadow[keycode] = 1
				if key == "escape":
					return ""
				elif key == "return":
					return url
				elif key == "backspace":
					url = url[:-1]
				elif key == "right shift":
					shift = 1
				elif key == "left shift":
					shift = 1
				else:
					if shift == 1:
						if key == ".":
							key = ":"
						if key == "7":
							key = "/"
						chars = "abcdefghijklmnopqrstuvwxyz"
						bchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
						for c in range(len(chars)):
							if chars[c] == key:
								key = bchars[c]
					url = url + key
			if not keystates[keycode] and shadow[keycode]:
				shadow[keycode] = 0
				if key == "right shift":
					shift = 0
				if key == "left shift":
					shift = 0

		h.page.blit(h.part.surfaces[0].surface, (h.part.x, h.part.y))
		if url is not "":
			font = pygame.font.Font(None, 20)
			print "4-RENDER '", url, "'"
			textline = font.render(url, 1, (0, 0, 255))
			h.page.blit(textline, (h.part.x, h.part.y))
		screen.blit(h.page, (h.xoff, h.yoff))
		pygame.display.update()

### Displayer class ====================================================== ###
###
###

class displayer:
	global globalpage

	### Initialize class, including reset

	def __init__(self):
		self.edgex = 0 ## w00t, should be 10, kludge!
		self.edgey = 0 ## w00t, should be 10, kludge!
		self.depth = 0
		self.fontstrategy = "passive" # overlay or passive

		self.reset()

	### Reset all values

	def reset(self):
		self.posx = self.edgex
		self.posy = self.edgey
		self.oldmaxy = -2
		self.leftmargin = 0
		self.totalheight = 0

	### Assign absolute positions to all renderparts

	def display(self, page, part, construction, reset = 1):
		global globalx, globaly
		global zoomfactor

		if part == None:
			return

		if reset == 1:
			print "##### rpage container?", part.container, reset

		if reset == 1:
			self.reset()
			print "reset image:", page.get_width()

		a = part.attributes

		dobreak = 0
		centerparts = []

		types = ["ol", "br", "p", "table", "tr"]
		if a.has_key('__type'):
			if a['__type'] in types:
				dobreak = 1

		if a.has_key('__prebreak'):
			if a['__prebreak'] == 1:
				dobreak = 1

		if a.has_key('css::leftmargin'):
			self.leftmargin = a['css::leftmargin']
		else:
			self.leftmargin = 0

		if a.has_key('css::topmargin'):
			self.posy += a['css::topmargin']

		if dobreak:
			self.posx = self.edgex
			self.posy += self.oldmaxy + 2
			self.oldmaxy = 0

		if reset == 0:
			if part.container is not None:
				print "##### want rpage"
				d = displayer()
				rpage = containerrender(part)
				if rpage is not None:
					print "##### rpage", rpage.get_width(), rpage.get_height()
					d.display(rpage, part, 1)
					d.blitter(rpage, part, 1, 1)
					part.surfaces = [surface(rpage)]

		maxx = 0
		lastx = 0
		maxy = 0
		rposx = 0
		for s in part.surfaces:
			if a.has_key('css::centered'):
				centerparts.append(s)
			if a.has_key('bgcolor'):
				bgcol = getcolor(a['bgcolor'])
			else:
				bgcol = (255, 255, 255)

			if lastx > 0:
				self.posx += lastx
			if s.original == None:
				o = pygame.Surface((s.surface.get_width(), s.surface.get_height()))
				o.fill(bgcol) ### ALPHA
				o.blit(s.surface, (0, 0))
				s.original = o ### FIXME: s.original = s.surface
			if zoomfactor != 100:
				zoomx = (s.original.get_width() * zoomfactor) / 100
				zoomy = (s.original.get_height() * zoomfactor) / 100
				print "zoom", s.original.get_width(), s.original.get_height(), "=>", zoomx, zoomy, zoomfactor
				s.surface = pygame.transform.scale(s.original, (zoomx, zoomy))
			else:
				o = pygame.Surface((s.original.get_width(), s.original.get_height()))
				o.fill(bgcol) ### ALPHA
				o.blit(s.original, (0, 0))
				s.surface = o ### FIXME: s.surface = s.original

			if self.posx + s.surface.get_width() > page.get_width() - self.leftmargin + 2: ### FIXME: containers
				self.posx = self.edgex
				self.posy += self.oldmaxy + 2
				self.oldmaxy = 0

			rposx = self.posx + self.leftmargin
			part.x = rposx
			part.y = self.posy
			s.x = rposx
			s.y = self.posy

			if construction == 1:
				if a.has_key('href'):
					h = hyperlink(s, rposx, self.posy, part, globalx, globaly, globalpage)
					links.append(h)
					part.hyperlink = h
				if a.has_key('__type'):
					if a['__type'] == "input":
						if a.has_key('name'):
							part.attributes['href'] = a['name']
						else:
							part.attributes['href'] = "internal:input"
						h = hyperlink(s, rposx, self.posy, part, globalx, globaly, globalpage)
						links.append(h)
						part.hyperlink = h

			if s.surface.get_width() > maxx:
				maxx = s.surface.get_width() + 1
			lastx = s.surface.get_width() + 1
			if s.surface.get_height() > maxy:
				maxy = s.surface.get_height() + 1

			if self.posy + s.surface.get_height() > self.totalheight:
				self.totalheight = self.posy + s.surface.get_height()

			if maxy > 0 and maxy > self.oldmaxy:
				self.oldmaxy = maxy

		dobreak = 0

		types = ["p", "br", "table"]
		if a.has_key('__type'):
			if a['__type'] in types:
				dobreak = 1

		if a.has_key('__break'):
			if a['__break'] == 1:
				dobreak = 1

		## center-kludge
		shift = page.get_width() / 2 - (rposx + lastx - self.leftmargin) / 2
		shifted = 0
		for p in centerparts:
			#print "CP", rposx - self.leftmargin, pagewidth
			p.x += shift
			shifted = 1
		if part.hyperlink is not None:
			if shifted:
				part.hyperlink.x += shift
				print "yooo shift", shift

		subrender = 1
		if reset == 0:
			if part.container is not None:
				subrender = 0

		if subrender:
			for childpart in part.children:
				self.depth += 1
				maxy = self.display(page, childpart, construction, 0)
				self.depth -= 1
			maxy = maxy - self.oldmaxy
			if maxy < 0:
				maxy = 0

		if dobreak:
			self.posx = self.edgex
			self.posy += maxy + 2
		else:
			self.posx += lastx + 2

		return maxy

	### Blits renderparts with fixed positions onto a surface

	def blitter(self, page, part, construction, override):
		if part == None:
			return

		a = part.attributes

		### Blit each surface of the renderpart

		counter = 0
		for s in part.surfaces:
			rposx = s.x
			posy = s.y
			if a['__type'] == "__text" and self.fontstrategy == "overlay":
				tmp = fontrender(part, a['content'][counter])
				page.blit(tmp, (rposx, posy))
			else:
				page.blit(s.surface, (rposx, posy))
			counter += 1

			### Some special effects

			if construction == 0:
				if part.activated == 1:
					for i in range(0, s.surface.get_width()):
						try:
							page.set_at((rposx + i, posy + s.surface.get_height() - 1), (0, 0, 0))
						except:
							pass
			if construction == 1:
				if a.has_key('__type'):
					if a['__type'] == 'input':
						try:
							for j in range(0, s.surface.get_height()):
								page.set_at((rposx, posy + j), (0, 0, 0))
								page.set_at((rposx + s.surface.get_width() - 1, posy + j), (0, 0, 0))
							for i in range(0, s.surface.get_width()):
								page.set_at((rposx + i, posy), (0, 0, 0))
								page.set_at((rposx + i, posy + s.surface.get_height() - 1), (0, 0, 0))
						except:
							pass

		### Also blit the renderpart's children

		if part.container == None or override == 1:
			#print "*** goblit childrenof", part.attributes['__type']
			override = 0
			for c in part.children:
				self.blitter(page, c, construction, override)

### Main program ========================================================= ###
###
###

### Initialization

def setup(withprogress):
	global parser
	global screen
	global font
	global page
	global links
	global history
	global progressenabled

	progressenabled = withprogress

	parser = myparser()

	#pygame.init()
	#screen = pygame.display.set_mode((pagewidth, 600))
	screen = pygame.display.get_surface()
	page = pygame.Surface((pagewidth, pageheight))

	font = pygame.font.Font(None, 48)

	links = []
	history = []

### Navigation panel

def setup_navigation_panel():
	global globaly
	global parser
	global hosted
	global globalpage

	navigation = """
	<html>
	<body bgcolor='#ffffff'>
	<a href='internal:back'>Zurück</a>
	<b> | </b>
	<a href='internal:forward'>Vor</a>
	<b> | </b>
	URL: <input type='text' name='internal:url'>
	<b> | </b>
	<a href='internal:clear'>Cache leeren</a>
	<b> | </b>
	<a href='internal:reload'>Neu laden</a>
	<b> | </b>
	Suche: <input width=100 type='text' name='internal:search'>
	</body>
	</html>
	"""

	if hosted:
		navigation = "" ##### temp! FIXME TODO
	navigation = re.sub("\\n", " ", navigation)
	navigation = re.sub("\\t", "", navigation)
	navpage = pygame.Surface((pagewidth, 30))
	globalpage = navpage

	object = initial_render(None, navigation, 10)

	final_render(object, 0)

### Setup navigation

def setup_navigation():
	global navlinks
	global globalpage
	global page
	global url
	global history
	global historyposition

	navlinks = []
	for l in links:
		navlinks.append(l)
	globalpage = page

	if url is not None:
		history.append(url)
	historyposition = 0

### Clearance functions

def clearwalker(arg, dirname, fnames):
	for f in fnames:
		fn = dirname + "/" + f
		os.remove(fn)

### Initial rendering

def initial_render(url, istext, height):
	global globalx
	global globaly

	### Parse document

	object = renderobject()
	if istext is not None:
		parser.object = object
		parser.feed(istext)
		parser.close()
	else:
		parser.parse(url, object)
	progress(len(object.resources))

	### Prepare container documents

	for c in object.containers:
		c.w = pagewidth ### FIXME
		c.dump()
		c.normalize("vertical")
		c.cons()

	### Optimizations

	style = parser.stylesheetparser(object.stylepart)
	parser.optimizer_style(object.root, style)
	parser.optimizer_headings(object.root)
	parser.optimizer_attributes(object.root)
	parser.dump(object.root, 0)

	# Rendering

	globalx = 0
	globaly = height
	render(object.bodypart)

	### Apply containers

	for c in object.containers:
		for d in c.children:
			for e in d.children:
				part = e.content
				dd = displayer()
				w = part.container.w - part.container.x
				rpage = dummypage(w) # FIXME: relative values
				dd.display(rpage, part, 1)
				e.h = dd.totalheight

	for c in object.containers:
		c.normalize("horizontal")

	for c in object.containers:
		for d in c.children:
			for e in d.children:
				part = e.content
				w = part.container.w - part.container.x
				h = part.container.h
				w += 20
				h += 20
				part.attributes['width'] = w
				part.attributes['height'] = h

	for c in object.containers:
		c.finish()
		c.dump()

	return object

### Final rendering

def final_render(object, height):
	global screen
	global scrollx
	global scrolly
	global scrolled
	global zoomed
	global page
	global globalpage

	### Display page off-screen

	d = displayer()
#	d.display(page, object.bodypart, 1)
#	page = containerrender(object.bodypart)
#	d.display(page, object.bodypart, 0)
	d.display(page, object.root, 1) # QUICK HACK
	page.fill((255, 255, 255))
	d.blitter(page, object.bodypart, 1, 0)

	### Display on screen

	scrollx = 0
	scrolly = 0
	scrolled = 0
	zoomed = 0

	screen.blit(page, (0, height), (scrollx, scrolly, scrollx + pagewidth, scrolly + displayheight - height))

	if object.title is not None:
		pygame.display.set_caption(object.title.attributes['content'][0])
	pygame.display.update()

### Main loop

def mainloop():
	global url
	global zoomed
	global links
	global scrolled
	global scrollx
	global scrolly
	global pagewidth
	global page
	global historyposition
	global history
	global navlinks
	global hosted
	global zoomfactor

	setup_navigation_panel()
	setup_navigation()

	if url == None:
		url = ""

	while url is not None:

		### Reset navigation

		links = []
		for l in navlinks:
			links.append(l)

		### Rendering

		object = initial_render(url, None, 10)

		### DOM access

		#print object.dom.html.head.title[0]
		#object.dom.html.head.title['content'] = ["FOOBAR"]
		#print object.dom.html.head.title[0]

		### Rendering on screen

		topheight = 50
		if hosted:
			topheight = 0
		final_render(object, topheight)

		### Page event loop

		# evil hack!
		zoomed = 1

		loop = 1
		activelink = None
		oldbutton = 0
		while loop:

			### Check for keyboard events

			link = None

			pygame.event.pump()
			key = pygame.key.get_pressed()
			mods = pygame.key.get_mods()
			if key[pygame.K_RETURN]:
				scrollx = 0
				scrolly = 0
				zoomfactor = 100
				scrolled = 1
				zoomed = 1
			if key[pygame.K_ESCAPE]:
				loop = 0
				url = None
			if key[pygame.K_PAGEDOWN]:
				scrolly += 50
				scrolled = 1
			if key[pygame.K_PAGEUP]:
				scrolly -= 50
				scrolled = 1
			if key[pygame.K_DOWN]:
				scrolly += 3
				scrolled = 1
			if key[pygame.K_UP]:
				scrolly -= 3
				scrolled = 1
			if key[pygame.K_RIGHT]:
				if mods & (pygame.KMOD_LALT | pygame.KMOD_RALT):
					link = "internal:forward"
				else:
					scrollx += 3
					scrolled = 1
			if key[pygame.K_LEFT]:
				if mods & (pygame.KMOD_LALT | pygame.KMOD_RALT):
					link = "internal:back"
				else:
					scrollx -= 3
					scrolled = 1
			if key[pygame.K_MINUS] or key[pygame.K_KP_MINUS]:
				if zoomfactor > 10:
					zoomfactor -= 10
					zoomed = 1
			if key[pygame.K_PLUS] or key[pygame.K_KP_PLUS]:
				if zoomfactor < 400:
					zoomfactor += 10
					zoomed = 1
			if key[pygame.K_1]:
				if pagewidth > 50:
					page = pygame.Surface((pagewidth, pageheight - 50))
					page.fill((0, 0, 0))
					screen.blit(page, (0, 50))
					pagewidth = pagewidth - 50
					page = pygame.Surface((pagewidth, pageheight))
					loop = 0
			if key[pygame.K_2]:
				pagewidth = pagewidth + 50
				page = pygame.Surface((pagewidth, pageheight))
				loop = 0
			if key[pygame.K_f]:
				print "fullscreen"
				pygame.display.toggle_fullscreen()
			if key[pygame.K_r]:
				print "reload"
				loop = 0
			if key[pygame.K_F12]:
				print "screenshot! (shot.bmp)"
				pygame.image.save(screen, "shot.bmp")
			if key[pygame.K_n]:
				print "goto next slide:", parser.nextslide
				if parser.nextslide is not "":
					#url = parser.correcturl(parser.nextslide)
					#loop = 0
					link = parser.nextslide
			if key[pygame.K_p]:
				print "goto previous slide:", parser.previousslide
				if parser.previousslide is not "":
					link = parser.previousslide

			### Execute keyboard events

			if zoomed:
				page = containerrender(object.bodypart)
				d = displayer()
				d.display(page, object.bodypart, 1)
				#d.display(page, object.root, 1)
				d.blitter(page, object.bodypart, 1, 0)
				#print "XXXVAL", scrolly + displayheight - topheight
				# XXX: next statement is part of the hack!
				screen.fill((255, 255, 255), (0, topheight, pagewidth, displayheight + topheight))
				# XXX: next statement ensures right drawing after the previous
				# ones?
				screen.blit(page, (0, topheight), (scrollx, scrolly, scrollx + pagewidth, scrolly + displayheight - topheight))
				pygame.display.update()
				zoomed = 0

			if scrolled:
				xscrollx = scrollx
				xscrolly = scrolly
				if xscrollx >= pagewidth - pagewidth:
					xscrollx = pagewidth - pagewidth
				if xscrolly >= pageheight - (displayheight - topheight):
					xscrolly = pageheight - (displayheight - topheight)
				if xscrollx < 0:
					xscrollx = 0
				if xscrolly < 0:
					xscrolly = 0
				screen.fill((255, 255, 255), (0, topheight, pagewidth, displayheight + topheight))
				#print "XXXVAL", scrolly + displayheight - topheight
				screen.blit(page, (0, topheight), (scrollx, scrolly, xscrollx + pagewidth, xscrolly + displayheight - topheight))
				pygame.display.update()
				scrolled = 0

			### Check and execute mouse move events

			(mx, my) = pygame.mouse.get_rel()
			if mx != 0 or my != 0:
				(mx, my) = pygame.mouse.get_pos()
				if my > topheight:
					mx = mx + scrollx
					my = my + scrolly
				if activelink is not None:
					activelink.part.activated = 0
					#posx = activelink.x # FIXME!
					#posy = activelink.y
					#d = displayer()
					#d.display(activelink.page, activelink.part, 0)

					activelink = None
					#print "mouse reset"
				for h in links:
					s = h.surface.surface
					x = h.x + h.xoff
					#y = h.y + h.yoff
					y = h.y
					print "///", mx, my, "--", x, y, ">>", h.part.attributes['href']
					if mx < x + s.get_width() and my < y + s.get_height():
						if mx >= x and my >= y:
							#print "-->", s, "at", x, y
							h.part.activated = 1
							#posx = h.x
							#posy = h.y # FIXME!
							#d = displayer()
							#d.display(h.page, h.part, 0)
							activelink = h

			### Check and execute mouse press events

			(button1, button2, button3) = pygame.mouse.get_pressed()
			if button1 is not oldbutton:
				oldbutton = button1
				if button1 == 1:
					if activelink is not None:
						link = activelink.part.attributes['href']
						print "HYPERREF", link

			### Navigation

			if link is not None:
				if link == "internal:back":
					if historyposition > 0:
						historyposition -= 1
						url = history[historyposition]
						loop = 0
						print "*** GET", url, "FROM HISTORY"
				elif link == "internal:forward":
					if historyposition < len(history) - 1:
						historyposition += 1
						url = history[historyposition]
						loop = 0
						print "*** GET", url, "FROM HISTORY (forward)"
				elif link == "internal:clear":
					os.path.walk("/tmp/pyromaniac", clearwalker, None)
					pass
				elif link == "internal:url":
					if activelink.page is not None:
						newurl = getinput(activelink)
						if newurl is not "":
							url = newurl
							loop = 0
							history.append(url)
				elif link == "internal:search":
					if activelink.page is not None:
						searchterm = getinput(activelink)
						loop = 0
				elif link == "internal:reload":
					loop = 0
				else:
					url = parser.correcturl(link)
					loop = 0
					historyposition += 1
					history = history[0:historyposition]
					history.append(url)

### Read command line parameters

url = None
hosted = 0

if sys.__dict__.has_key('argv'):
	for i in range(1, len(sys.argv)):
		if sys.argv[i] == "--help":
			print sys.argv[0] + " [--help] [--hosted] [<url>]"
			sys.exit()
		elif sys.argv[i] == "--hosted":
			hosted = 1
		elif i < len(sys.argv) - 1:
			if sys.argv[i] == "--foo":
				myarg = sys.argv[i + 1]
			else:
				url = sys.argv[i]
		else:
			url = sys.argv[i]
else:
	hosted = 1

### Stand-alone kick starter ============================================= ###

### Used when run embedded by a host

def kickstart():
	url = "dynamic-desktop/index.html"

	setup(0)
	setup_navigation()

	object = initial_render(url, None, 10)
	final_render(object, 0)

def keyevent(keycode, shift, alt):
	key = pygame.key.name(keycode)
	print "pyromaniac:", key, "(keycode:", keycode, "shift", shift, "alt", alt, ")"

def mouseevent(x, y, clicked):
	print "pyromaniac: mouse at:", x, y, "(clicked:", clicked, ")"

def redrawevent():
	print "pyromaniac: redraw"
	pygame.display.update()

### Used when run from the command line

if __name__ == "__main__":
	setup(1)
	mainloop()

### Pyntor integration =================================================== ###

### Convert pyntor options

class Pyromaniac:
	def init(self, options):
		self.url = options ["url"]
		self.pages = 1

	def render(self, screen, page, globalpage):
		global url
		global hosted
		global pagewidth, pageheight

		url = self.url
		hosted = 1

		#screen = pygame.display.get_surface()
		pagewidth = screen.get_width()
		#pageheight = screen.get_height()

		setup(1)
		pygame.mouse.set_visible(1)
		mainloop()
		pygame.mouse.set_visible(0)

	def interactive(self, event):
		if event.type == pygame.KEYDOWN:
			key = event.key
			if key == pygame.K_ESCAPE or pygame.event.peek(pygame.QUIT):
				return "exit"
			if key == pygame.K_RETURN:
				return "next"
			if key == pygame.K_BACKSPACE:
				return "previous"

component = Pyromaniac()

parameters = (
	("url", "URL or HTML file to display", None),
)

metainfo = {
	"version": "0.2",
	"author": "Josef Spillner",
	"authoremail": "<josef@coolprojects.org>",
	"licence": "GPL"
}

doc = """
Pyromaniac: rendering of HTML pages

Usage: 'pyromaniac' -url <url>
 Where <url> is either a file or a HTTP URL containing a HTML document

Display: yes

Interactivity: not really :/
"""


### Program end ========================================================== ###
###
###

