shithub: purgatorio

ref: a411870ee4640241e3c494367d922847da84f972
dir: purgatorio/appl/charon/build.m

View raw version
Build: module
{
PATH: con "/dis/charon/build.dis";

# Item layout is dictated by desire to have all but formfield and table
# items allocated in one piece.
# Also aiming for the 128-byte allocation quantum, which means
# keeping the total size at 17 32-bit words, including pick tag.
Item: adt
{
	next:		cyclic ref Item;	# successor in list of items
	width:	int;			# width in pixels (0 for floating items)
	height:	int;			# height in pixels
	ascent:	int;			# ascent (from top to baseline) in pixels
	anchorid:	int;			# if nonzero, which anchor we're in
	state:	int;			# flags and values (see below)
	genattr:	ref Genattr;	# generic attributes and events

	pick {
		Itext =>
			s: string;		# the characters
			fnt: int;		# style*NumSize+size (see font stuff, below)
			fg: int;		# Pixel (color) for text
			voff: byte;		# Voffbias+vertical offset from baseline, in pixels (+ve == down)
			ul: byte;		# ULnone, ULunder, or ULmid
		Irule =>
			align: byte;	# alignment spec
			noshade: byte;	# if true, don't shade
			size: int;		# size attr (rule height)
			wspec: Dimen;	# width spec
		Iimage =>
			imageid: int;		# serial no. of image within its doc
			ci: ref CharonUtils->CImage;		# charon image (has src, actual width, height)
			imwidth: int;		# spec width (actual, if no spec)
			imheight: int;		# spec height (actual, if no spec)
			altrep: string;		# alternate representation, in absence of image
			map: ref Map;		# if non-nil, client side map
			name: string;		# name attribute
			ctlid: int;			# if animated
			align: byte;		# vertical alignment
			hspace: byte;		# in pixels; buffer space on each side
			vspace: byte;		# in pixels; buffer space on top and bottom
			border: byte;		# in pixels: border width to draw around image
		Iformfield =>
			formfield: ref Formfield;
		Itable =>
			table: ref Table;
		Ifloat =>
			item: ref Item;		# content of float
			x: int;			# x coord of top (from right, if Aright)
			y: int;			# y coord of top
			side: byte;			# margin it floats to: Aleft or Aright
			infloats: byte;		# true if this has been added to a lay.floats
		Ispacer =>
			spkind: int;		# ISPnone, etc.
			fnt: int;			# font number
	}

	newtext: fn(s: string, fnt, fg, voff: int, ul: byte) : ref Item;
	newrule: fn(align: byte, size, noshade: int, wspec: Dimen) : ref Item;
	newimage: fn(di: ref Docinfo, src: ref Url->Parsedurl, lowsrc: ref Url->Parsedurl, altrep: string,
		align: byte, width, height, hspace, vspace, border, ismap, isbkg: int,
		map: ref Map, name: string, genattr: ref Genattr) : ref Item;
	newformfield: fn(ff: ref Formfield) : ref Item;
	newtable: fn(t: ref Table) : ref Item;
	newfloat: fn(i: ref Item, side: byte) : ref Item;
	newspacer: fn(spkind, font: int) : ref Item;

	revlist: fn(itl: list of ref Item) : list of ref Item;
	print: fn(it: self ref Item);
	printlist: fn(items: self ref Item, msg: string);
};

# Item state flags and value fields
IFbrk:		con (1<<31);	# forced break before this item
IFbrksp:		con (1<<30);	# add 1 line space to break (IFbrk set too)
IFnobrk:		con (1<<29);	# break not allowed before this item
IFcleft:		con (1<<28);	# clear left floats (IFbrk set too)
IFcright:		con (1<<27);	# clear right floats (IFbrk set too)
IFwrap:		con (1<<26);	# in a wrapping (non-pre) line
IFhang:		con (1<<25);	# in a hanging (into left indent) item
IFrjust:		con (1<<24);	# right justify current line
IFcjust:		con (1<<23);	# center justify current line
IFsmap:		con (1<<22);	# image is server-side map
IFbkg:		con (1<<21);	# Item.image is a background image
IFindentshift:	con 8;
IFindentmask:	con (255<<IFindentshift);	# current indent, in tab stops
IFhangmask:	con 255;	# current hang into left indent, in 1/10th tabstops

Voffbias:	con 128;

# Spacer kinds.  ISPnull has 0 height and width,
# ISPvline has height/ascent of current font
# ISPhspace has width of space in current font
# ISPgeneral used for other purposes (e.g. between list markers and list).
ISPnull, ISPvline, ISPhspace, ISPgeneral: con iota;

# Generic attributes and events (not many elements will have any of these set)
Genattr: adt
{
	id: string;			# document-wide unique id
	class: string;		# space-separated list of classes
	style: string;		# associated style info
	title: string;		# advisory title
	events: list of Lex->Attr;	# attid will be Aonblur, etc., value is script
	evmask: int;		# Aonblur|Aonfocus, etc. when present
};


# Formfield Item: a field from a form

# form field types (ints because often case on them)
Ftext, Fpassword, Fcheckbox, Fradio, Fsubmit, Fhidden, Fimage,
		Freset, Ffile, Fbutton, Fselect, Ftextarea: con iota;

Formfield: adt
{
	ftype: int;		# Ftext, Fpassword, etc.
	fieldid: int;		# serial no. of field within its form
	form: cyclic ref Form;	# containing form
	name: string;		# name attr
	value: string;		# value attr
	size: int;			# size attr
	maxlength: int;		# maxlength attr
	rows: int;			# rows attr
	cols: int;			# cols attr
	flags: byte;		# FFchecked, etc.
	options: list of ref Option;	# for Fselect fields
	image: cyclic ref Item;	# image item, for Fimage fields
	ctlid: int;			# identifies control for this field in layout
	events: list of Lex->Attr;	# same as genattr.events of containing item
	evmask: int;

	new: fn(ftype, fieldid: int, form: ref Form, name, value: string, size, maxlength: int) : ref Formfield;
};

# Form flags
FFchecked: con byte (1<<7);
FFmultiple: con byte (1<<6);

# Option holds info about an option in a "select" form field
Option: adt {
	selected: int;		# true if selected initially
	value: string;		# value attr
	display: string;	# display string
};

# Form holds info about a form
Form: adt
{
	formid: int;		# serial no. of form within its doc
	name: string;		# name or id attr (netscape uses name, HTML 4.0 uses id)
	action: ref Url->Parsedurl;		# action attr
	target: string;		# target attribute
	method: int;		# HGet or HPost
	events: list of Lex->Attr;	# attid will be Aonreset or Aonsubmit
	evmask: int;
	nfields: int;		# number of fields
	fields: cyclic list of ref Formfield;	# field's forms, in input order
	state: int;			# see Form states enum

	new: fn(formid: int, name: string, action: ref Url->Parsedurl, target: string, method: int, events: list of Lex->Attr) : ref Form;
};

# Form states
FormBuild,					# seen <FORM>
FormDone,					# seen </FORM>
FormTransferred : con iota;		# tx'd to javascript

# Flags used in various table structures
TFparsing:	con byte (1<<7);
TFnowrap:	con byte (1<<6);
TFisth:		con byte (1<<5);

# A Table Item is for a table.
Table: adt
{
	tableid: int;			# serial no. of table within its doc
	nrow: int;			# total number of rows
	ncol: int;			# total number of columns
	ncell: int;			# total number of cells
	align: Align;			# alignment spec for whole table
	width: Dimen;			# width spec for whole table
	border: int;			# border attr
	cellspacing: int;		# cellspacing attr
	cellpadding: int;		# cellpadding attr
	background: Background;	# table background
	caption: cyclic ref Item;	# linked list of Items, giving caption
	caption_place: byte;		# Atop or Abottom
	caption_lay: int;		# identifies layout of caption
	currows: cyclic list of ref Tablerow;	# during parsing
	cols: array of Tablecol;		# column specs
	rows: cyclic array of ref Tablerow;	# row specs
	cells: cyclic list of ref Tablecell;		# the unique cells
	totw: int;					# total width
	toth: int;					# total height
	caph: int;					# caption height
	availw: int;				# used for previous 3 sizes
	grid: cyclic array of array of ref Tablecell;
	tabletok: ref Lex->Token;		# token that started the table
	flags: byte;				# Lchanged

	new: fn(tableid: int, align: Align, width: Dimen,
		border, cellspacing, cellpadding: int, bg: Background, tok: ref Lex->Token) : ref Table;
};

# A table column info
Tablecol: adt
{
	width: int;
	align: Align;
	pos: Draw->Point;
};

# A table row spec
Tablerow: adt
{
	cells: cyclic list of ref Tablecell;
	height: int;
	ascent: int;
	align: Align;
	background: Background;
	pos: Draw->Point;
	flags: byte;		# 0 or TFparsing

	new: fn(align: Align, bg: Background, flags: byte) : ref Tablerow;
};

# A Tablecell is one cell of a table.
# It may span multiple rows and multiple columns.
# The (row,col) given indexes upper left corner of cell.
# Try to keep this under 17 words long.
Tablecell: adt
{
	cellid: int;			# serial no. of cell within table
	content: cyclic ref Item;	# contents before layout
	layid: int;			# identifies layout of cell
	rowspan: int;		# number of rows spanned by this cell
	colspan: int;		# number of cols spanned by this cell
	align: Align;		# alignment spec
	flags: byte;		# TFparsing, TFnowrap, TFisth
	wspec: Dimen;		# suggested width
	hspec: int;			# suggested height
	background: Background;	# cell background
	minw: int;			# minimum possible width
	maxw: int;		# maximum width
	ascent: int;
	row: int;
	col: int;
	pos: Draw->Point;		# nw corner of cell contents, in cell

	new: fn(cellid, rowspan, colspan: int, align: Align, wspec: Dimen,
			hspec: int, bg: Background, flags: byte) : ref Tablecell;
};

# Align holds both a vertical and a horizontal alignment.
# Usually not all are possible in a given context.
# Anone means no dimension was specified

# alignment types
Anone, Aleft, Acenter, Aright, Ajustify, Achar, Atop, Amiddle, Abottom, Abaseline: con byte iota;

Align: adt
{
	halign: byte;		# one of Anone, Aleft, etc.
	valign: byte;		# one of Anone, Atop, etc.
};

# A Dimen holds a dimension specification, especially for those
# cases when a number can be followed by a % or a * to indicate
# percentage of total or relative weight.
# Dnone means no dimension was specified

# Dimen
# To fit in a word, use top bits to identify kind, rest for value
Dnone:	con 0;
Dpixels:	con 1<<29;
Dpercent:	con 2<<29;
Drelative:	con 3<<29;
Dkindmask:	con 3<<29;
Dspecmask:	con ~Dkindmask;

Dimen: adt
{
	kindspec: int;	# kind | spec

	kind: fn(d: self Dimen) : int;
	spec: fn(d: self Dimen) : int;

	make: fn(kind, spec: int) : Dimen;
};


# Anchor is for info about hyperlinks that go somewhere
Anchor: adt
{
	index: int;			# serial no. of anchor within its doc
	name: string;		# name attr
	href: ref Url->Parsedurl;	# href attr
	target: string;		# target attr
	events: list of Lex->Attr;	# same as genattr.events of containing items
	evmask: int;
};

# DestAnchor is for info about hyperlinks that are destinations
DestAnchor: adt
{
	index: int;		# serial no. of anchor within its doc
	name: string;		# name attr
	item: ref Item;		# the destination
};

# Maps (client side)
Map: adt
{
	name: string;		# map name
	areas: list of Area;	# hotzones

	new: fn(name: string) : ref Map;
};

Area: adt
{
	shape: string;		# rect, circle, or poly
	href: ref Url->Parsedurl;		# associated hypertext link
	target: string;			# associated target frame
	coords: array of Dimen;	# coords for shape
};

# Background is either an image or a color.
# If both are set, the image has precedence.
Background: adt
{
	image: ref Item.Iimage;	# with state |= IFbkg
	color: int;			# RGB in lower 3 bytes
};

# Font styles
FntR, FntI, FntB, FntT, NumStyle: con iota;

# Font sizes
Tiny, Small, Normal, Large, Verylarge, NumSize: con iota;

NumFnt: con (NumStyle*NumSize);
DefFnt: con (FntR*NumSize+Normal);

# Lines are needed through some text items, for underlining or strikethrough
ULnone, ULunder, ULmid: con byte iota;

# List number types
LTdisc, LTsquare, LTcircle, LT1, LTa, LTA, LTi, LTI: con byte iota;

# Kidinfo flags
FRnoresize, FRnoscroll, FRhscroll, FRvscroll, FRhscrollauto, FRvscrollauto: con (1<<iota);

# Information about child frame or frameset
Kidinfo: adt {
	isframeset: int;

	# fields for "frame"
	src: ref Url->Parsedurl;		# only nil if a "dummy" frame or this is frameset
	name: string;			# always non-empty if this isn't frameset
	marginw: int;
	marginh: int;
	framebd: int;
	flags: int;
	
	# fields for "frameset"
	rows: array of Dimen;
	cols: array of Dimen;
	kidinfos: cyclic list of ref Kidinfo;

	new: fn(isframeset: int) : ref Kidinfo;
};

# Document info (global information about HTML page)
Docinfo: adt {
	# stuff from HTTP headers, doc head, and body tag
	src: ref Url->Parsedurl;			# original source of doc
	base: ref Url->Parsedurl;			# base URL of doc
	referrer: ref Url->Parsedurl;			# JavaScript document.referrer
	doctitle: string;				# from <title> element
	background: Background;	# background specification
	backgrounditem: ref Item;	# Image Item for doc background image, or nil
	text: int;					# doc foreground (text) color
	link: int;					# unvisited hyperlink color
	vlink: int;					# visited hyperlink color
	alink: int;					# highlighting hyperlink color
	target: string;				# target frame default
	refresh: string;				# content of <http-equiv=Refresh ...>
	chset: string;				# charset encoding
	lastModified: string;				# last-modified time
	scripttype: int;				# CU->TextJavascript, etc.
	hasscripts: int;				# true if scripts used
	events: list of Lex->Attr;			# event handlers
	evmask: int;
	kidinfo: ref Kidinfo;			# if a frameset
	frameid: int;				# id of document frame

	# info needed to respond to user actions
	anchors: list of ref Anchor;	# info about all href anchors
	dests: list of ref DestAnchor;	# info about all destination anchors
	forms: list of ref Form;		# info about all forms
	tables: list of ref Table;		# info about all tables
	maps: list of ref Map;		# info about all maps
	images: list of ref Item;		# all image items in doc

	new: fn() : ref Docinfo;
	reset: fn(f: self ref Docinfo);
};

# Parsing stuff

# Parsing state
Pstate: adt {
	skipping: int;			# true when we shouldn't add items
	skipwhite: int;			# true when we should strip leading space
	curfont: int;			# font index for current font
	curfg: int;				# current foreground color
	curbg: Background;		# current background
	curvoff: int;			# current baseline offset
	curul: byte;			# current underline/strike state
	curjust: byte;			# current justify state
	curanchor: int;			# current (href) anchor id (if in one), or 0
	curstate: int;			# current value of item state
	literal: int;				# current literal state
	inpar: int;				# true when in a paragraph-like construct
	adjsize: int;			# current font size adjustment
	items: ref Item;			# dummy head of item list we're building
	lastit: ref Item;			# tail of item list we're building
	prelastit: ref Item;		# item before lastit
	fntstylestk: list of int;		# style stack
	fntsizestk: list of int;		# size stack
	fgstk: list of int;			# text color stack
	ulstk: list of byte;		# underline stack
	voffstk: list of int;		# vertical offset stack
	listtypestk: list of byte;	# list type stack
	listcntstk: list of int;		# list counter stack
	juststk: list of byte;		# justification stack
	hangstk: list of int;		# hanging stack

	new: fn() : ref Pstate;
};


# A source of Items (resulting of HTML parsing).
# After calling new with a ByteSource (which is past 'gethdr' stage),
# call getitems repeatedly until get nil.  Errors are signalled by exceptions.
# Possible exceptions raised:
#	EXInternal		(start, getitems)
#	exGeterror	(getitems)
#	exAbort		(getitems)
ItemSource: adt
{
	ts: ref Lex->TokenSource;	# source of tokens
	mtype: int;			# media type (TextHtml or TextPlain)
	doc: ref Docinfo;		# global information about page
	frame: ref Layout->Frame;	# containing frame
	psstk: list of ref Pstate;	# local parsing state stack
	nforms: int;			# used to make formids
	ntables: int;			# used to make tableids
	nanchors: int;			# used to make anchor ids
	nframes: int;			# used to make names for frames
	curform: ref Form;		# current form (if in one)
	curmap: ref Map;		# current map (if in one)
	tabstk: list of ref Table;	# table stack
	kidstk: list of ref Kidinfo;	# kidinfo stack
	reqdurl: ref Url->Parsedurl;
	reqddata: array of byte;
	toks: array of ref Lex->Token;

	new: fn(bs: ref CharonUtils->ByteSource, f: ref Layout->Frame, mtype: int) : ref ItemSource;
	getitems: fn(is: self ref ItemSource) : ref Item;
};

init: fn(cu: CharonUtils);
trim_white: fn(data: string): string;
};