summaryrefslogtreecommitdiff
path: root/appl/ebook/stylesheet.b
blob: f142c1218326bc1383ec9d2844cb73eca38525a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
implement Stylesheet;

include "sys.m";
	sys: Sys;
include "stylesheet.m";
include "strmap.m";
	strmap: Strmap;
	Map: import strmap;
include "cssparser.m";
	Decl: import CSSparser;

stylemap: ref Map;
numstyles: int;

RULEHASH:	con 23;

# specificity:
# bits 0-26	declaration order
# bit 27		class count	(0 or 1)
# bit 28		tagname count	(0 or 1)
# bit 28		id count		(0 or 1)	(inline style only)
# bit 29-30	origin		(0, 1 or 2 - default, reader, author)	
# bit 31		importance

# order of these as implied by CSS1 §3.2
TAG,
CLASS,
ID:			con 1 << (iota + 26);
ORIGINSHIFT:	con 29;
IMPORTANCE:	con 1<<30;

init(a: array of string)
{
	sys = load Sys Sys->PATH;
	strmap = load Strmap Strmap->PATH;
	if (strmap == nil) {
		sys->fprint(sys->fildes(2), "stylesheet: cannot load %s: %r\n", Strmap->PATH);
		raise "fail:bad module";
	}
	stylemap = Map.new(a);
	numstyles = len a;
}

Sheet.new(): ref Sheet
{
	return ref Sheet(array[RULEHASH] of list of Rule, 0);
}

Sheet.addrules(sheet: self ref Sheet, rules: list of (string, list of Decl), origin: int)
{
	origin <<= ORIGINSHIFT;
	for (; rules != nil; rules = tl rules) {
		(sel, decls) := hd rules;
		(tag, class) := selector(sel);
		(key, sub) := (tag, "");
		specificity := sheet.ruleid++;
		if (tag != nil)
			specificity |= TAG;
		if (class != nil) {
			specificity |= CLASS;
			(key, sub) = ("." + class, tag);
		}
		specificity |= origin;

		attrs: list of (int, int, string);
		for (; decls != nil; decls = tl decls) {
			d := mkdecl(hd decls, specificity);
			if (d.attrid != -1)
				attrs = d :: attrs;
		}

		n := hashfn(key, RULEHASH);
		sheet.rules[n] = (key, sub, attrs) :: sheet.rules[n];
	}
}

# assume selector is well-formed, having been previously parsed.
selector(s: string): (string, string)
{
	for (i := 0; i < len s; i++)
		if (s[i] == '.')
			break;
	if (i == len s)
		return (s, nil);
	return (s[0:i], s[i + 1:]);
}


Sheet.newstyle(sheet: self ref Sheet): ref Style
{
	return ref Style(sheet, array[numstyles] of string, array[numstyles] of {* => 0});
}

adddecl(style: ref Style, d: Ldecl)
{
	if (d.specificity > style.spec[d.attrid]) {
		style.attrs[d.attrid]  = d.val;
		style.spec[d.attrid] = d.specificity;
	}
}

Style.add(style: self ref Style, tag, class: string)
{
	rules := style.sheet.rules;
	if (class != nil) {
		key := "." + class;
		v := hashfn(key, RULEHASH);
		for (r := rules[v]; r != nil; r = tl r)
			if ((hd r).key == key && ((hd r).sub == nil || (hd r).sub == tag))
				for (decls := (hd r).decls; decls != nil; decls = tl decls)
					adddecl(style, hd decls);
	}
	v := hashfn(tag, RULEHASH);
	for (r := rules[v]; r != nil; r = tl r)
		if ((hd r).key == tag)
			for (decls := (hd r).decls; decls != nil; decls = tl decls)
				adddecl(style, hd decls);
}

# add a specific set of attributes to a style;
# attrs is list of (attrname, important, val).
Style.adddecls(style: self ref Style, decls: list of Decl)
{
	specificity := ID | (AUTHOR << ORIGINSHIFT);
	for (; decls != nil; decls = tl decls) {
		d := mkdecl(hd decls, specificity);
		if (d.attrid != -1)
			adddecl(style, d);
	}
}

Style.addone(style: self ref Style, attrid: int, origin: int, val: string)
{
	# XXX specificity is probably wrong here.
	adddecl(style, (attrid, origin << ORIGINSHIFT, val));
}

# convert a declaration from extern (attrname, important, val) form
# to intern (attrid, specificity, val) form.
# XXX could warn for unknown attribute here...
mkdecl(d: Decl, specificity: int): Ldecl
{
	if (d.important)
		specificity |= IMPORTANCE;
	return (stylemap.i(d.name), specificity, d.val);
}

hashfn(s: string, n: int): int
{
	h := 0;
	m := len s;
	for(i:=0; i<m; i++){
		h = 65599*h+s[i];
	}
	return (h & 16r7fffffff) % n;
}