Oberon/ETH Oberon/2.3.7/HTMLDocs.Mod

(* ETH Oberon, Copyright 1990-2003 Computer Systems Institute, ETH Zurich, CH-8092 Zurich. Refer to the license.txt file provided with this distribution. *) MODULE HTMLDocs; (** portable *) (* ejz, Greek & Math support by afi *) IMPORT Objects, Input, Strings, Display, Display3, Fonts, HyperDocs, Texts, Gadgets, Documents, HTTPDocs0, TextDocs, Oberon, TextGadgets, Lists, Attributes, Desktops, Links, Streams, TextStreams; CONST (* class *) WhiteSpace* = 0; OpenTag* = 1; OpenEndTag* = 2; CloseTag* = 3; CharRef* = 4; Character* = 5; Value* = 6; Undef* = 7; (* state *) TextPlain* = 1; TextHtml* = 2; InTag* = 3; End* = 4; (* list types *) DefList = 0; DescList = 1; OrderedList = 2; Menu = "Desktops.Copy[Copy] HyperDocs.Back[Back] HyperDocs.Reload[Reload] TextDocs.Search[Search] Desktops.StoreDoc[Store]"; GreekCap = "0013143516173415192134222324252728202931323337372636180000000000"; GreekMin = "004546674849664751536654555657596052616364656969586850"; ASCIIBullets = TRUE; TYPE DocURL* = POINTER TO DocURLDesc; DocURLDesc* = RECORD (HyperDocs.DefURLDesc) dockey*: LONGINT END; TextAttrs = POINTER TO TextAttrsDesc; TextAttrsDesc = RECORD lib: Objects.Library; style: TextGadgets.Style; col, voff: SHORTINT; next: TextAttrs END; Page* = POINTER TO PageDesc; Scanner* = RECORD page*: Page; S*: Streams.Stream; class*, state*: INTEGER; value*: ARRAY 1024 OF CHAR; pre*, end*: BOOLEAN; ch*, next*, char*: CHAR; avail: LONGINT END; List = POINTER TO ListDesc; ListDesc = RECORD style: TextGadgets.Style; kind, nesting, itemNr: INTEGER; dtok: BOOLEAN; next: List END; Form* = POINTER TO FormDesc; FormDesc* = RECORD (TextGadgets.ControlDesc) elems*: HTTPDocs0.ObjList END; PageDesc* = RECORD (Gadgets.ObjDesc) W*, Ws*: Texts.Writer; textAttrs: TextAttrs; T*, source*: Texts.Text; D*: Documents.Document; alink*, clink: Objects.Object; style: TextGadgets.Style; base*: DocURL; orgLabel: ARRAY 64 OF CHAR; lists: List; orgPos, linkkey*, headerLen, docKey*: LONGINT; next*: Page; task: Oberon.Task; textC*, linkC*, oldLinkC*, textbackC*, lines*: INTEGER; left*, blank*, head, cacheSource*: BOOLEAN END; Item* = POINTER TO ItemDesc; ItemDesc* = RECORD (Lists.ItemDesc) value*: ARRAY 64 OF CHAR; oldSel*, hasVal*: BOOLEAN END; TagHandler* = PROCEDURE (VAR S: Scanner; on: BOOLEAN); ExtTag* = POINTER TO ExtTagDesc; ExtTagDesc* = RECORD tag: ARRAY 32 OF CHAR; handle*: TagHandler; start*, stop*: PROCEDURE (P: Page); next: ExtTag END; TagAttr* = POINTER TO TagAttrDesc; TagAttrDesc* = RECORD name: ARRAY 64 OF CHAR; value*: ARRAY 512 OF CHAR; next: TagAttr END; Task = POINTER TO TaskDesc; TaskDesc = RECORD (Oberon.TaskDesc) S: Scanner; P: Page END; VAR bullets: ARRAY 2 OF RECORD f: Fonts.Font; c: CHAR END; Wr*, Wq: Texts.Writer; imgs*: BOOLEAN; extTags, newTag*: ExtTag; entities*: ARRAY 69, 7 OF CHAR; entityEncoding*: ARRAY 69 OF CHAR; pages*: Page; searchAttr: ARRAY 32 OF CHAR; mono: Fonts.Font; found: BOOLEAN; dispW: INTEGER; GreekTab: ARRAY 128 OF CHAR; PROCEDURE WriteLn*(P: Page); BEGIN INC(P.lines); IF P.lines < 3 THEN Texts.WriteLn(P.W); P.blank := FALSE END END WriteLn; PROCEDURE WriteSpace*(P: Page); BEGIN IF P.blank THEN Texts.Write(P.W, " "); P.blank := FALSE END END WriteSpace; PROCEDURE WriteObj*(P: Page; obj: Objects.Object); BEGIN Texts.WriteObj(P.W, obj); IF (obj IS Display.Frame) & (obj(Display.Frame).W >= (HyperDocs.docW-16)) THEN INC(P.lines); P.blank := FALSE ELSIF obj IS TextGadgets.Style THEN P.blank := FALSE ELSIF ~(obj IS TextGadgets.Control) THEN P.lines := 0; P.blank := TRUE END END WriteObj; PROCEDURE InitTabs(style: TextGadgets.Style); VAR i: LONGINT; BEGIN style.W := ((dispW DIV 8) * 5)-24; style.width := style.W; 		style.noTabs := 32; FOR i := 0 TO 31 DO 			style.tab[i] := SHORT(32+i*32) END END InitTabs; PROCEDURE NewStyle: TextGadgets.Style; VAR style: TextGadgets.Style; BEGIN style := TextGadgets.newStyle; InitTabs(style); Attributes.SetBool(style, "FrameW", TRUE); RETURN style END NewStyle; PROCEDURE Syntax(size: INTEGER; attr: CHAR): Fonts.Font; VAR name: ARRAY 32 OF CHAR; sizeS: ARRAY 4 OF CHAR; BEGIN name := "Default"; IF dispW < 800 THEN DEC(size, 2) END; IF size <= 8 THEN size := 8 ELSIF size <= 10 THEN size := 10 ELSIF size <= 12 THEN size := 12 ELSIF size <= 14 THEN size := 14 ELSIF size <= 16 THEN size := 16 ELSIF size <= 20 THEN size := 20 ELSE size := 24 END; Strings.IntToStr(size, sizeS); IF attr = 0X THEN (* skip *) ELSIF attr = "i" THEN (* skip *) ELSIF attr = "m" THEN attr := "b" ELSIF attr = "b" THEN (* skip *) ELSE attr := 0X END; Strings.Append(name, sizeS); Strings.AppendCh(name, attr); Strings.Append(name, ".Scn.Fnt"); RETURN Fonts.This(name) END Syntax; PROCEDURE PushTextAttrs*(P: Page); VAR attr: TextAttrs; BEGIN NEW(attr); attr.lib := P.W.lib; attr.col := P.W.col; attr.voff := P.W.voff; attr.style := P.style; attr.next := P.textAttrs; P.textAttrs := attr END PushTextAttrs; PROCEDURE PopTextAttrs*(P: Page); VAR style: TextGadgets.Style; M: Objects.CopyMsg; BEGIN IF P.textAttrs # NIL THEN Texts.SetFont(P.W, P.textAttrs.lib); Texts.SetColor(P.W, P.textAttrs.col); Texts.SetOffset(P.W, P.textAttrs.voff); style := P.textAttrs.style; IF (P.style.mode # style.mode) OR (P.style.leftM # style.leftM) OR (P.style.width # style.width) THEN M.id := Objects.shallow; Objects.Stamp(M); M.obj := NIL; M.dlink := NIL; style.handle(style, M); style := M.obj(TextGadgets.Style); P.style := style; WriteObj(P, style) END; P.textAttrs := P.textAttrs.next ELSE Texts.SetFont(P.W, Syntax(12, 0X)); Texts.SetColor(P.W, SHORT(P.textC)); Texts.SetOffset(P.W, 0); style := NewStyle; IF (P.style.mode # style.mode) OR (P.style.leftM # style.leftM) OR (P.style.width # style.width) THEN P.style := style; WriteObj(P, style) END END END PopTextAttrs; PROCEDURE SplitFontName(f: Fonts.Font; VAR family: ARRAY OF CHAR; VAR size: INTEGER; VAR attr: CHAR); VAR val: LONGINT; i: INTEGER; BEGIN i := 0; WHILE (f.name[i] # 0X) & ~Strings.IsDigit(f.name[i]) DO 			INC(i) END; COPY(f.name, family); family[i] := 0X; Strings.StrToIntPos(f.name, val, i); size := SHORT(val); IF Strings.IsAlpha(f.name[i]) THEN attr := f.name[i] ELSE attr := 0X END END SplitFontName; PROCEDURE FontSize(f: Fonts.Font; VAR size: INTEGER); VAR family: ARRAY 64 OF CHAR; attr: CHAR; BEGIN SplitFontName(f, family, size, attr) END FontSize; PROCEDURE GetFontSize*(P: Page): INTEGER; VAR size: INTEGER; BEGIN FontSize(P.W.lib(Fonts.Font), size); IF dispW < 800 THEN INC(size, 2) END; IF size <= 8 THEN size := 1 ELSIF size <= 10 THEN size := 2 ELSIF size <= 12 THEN size := 3 ELSIF size <= 14 THEN size := 4 ELSIF size <= 16 THEN size := 5 ELSIF size <= 20 THEN size := 6 ELSE size := 7 END; RETURN size END GetFontSize; PROCEDURE ChangeFontAttr(f: Fonts.Font; attr: CHAR): Fonts.Font; VAR family: ARRAY 64 OF CHAR; str: ARRAY 4 OF CHAR; size: INTEGER; oldattr: CHAR; BEGIN IF attr = "m" THEN attr := "b" END; SplitFontName(f, family, size, oldattr); IF oldattr # attr THEN Strings.IntToStr(size, str); Strings.Append(family, str); IF attr # 0X THEN Strings.AppendCh(family, attr) END; Strings.Append(family, ".Scn.Fnt"); RETURN Fonts.This(family) ELSE RETURN f 		END END ChangeFontAttr; PROCEDURE ChangeFontSize(f: Fonts.Font; size: INTEGER): Fonts.Font; VAR family: ARRAY 64 OF CHAR; str: ARRAY 4 OF CHAR; oldsize: INTEGER; attr: CHAR; BEGIN IF dispW < 800 THEN DEC(size, 2) END; SplitFontName(f, family, oldsize, attr); IF oldsize # size THEN Strings.IntToStr(size, str); Strings.Append(family, str); IF attr # 0X THEN Strings.AppendCh(family, attr) END; Strings.Append(family, ".Scn.Fnt"); RETURN Fonts.This(family) ELSE RETURN f 		END END ChangeFontSize; PROCEDURE SetFontSize*(P: Page; size: INTEGER); VAR fnt: Fonts.Font; BEGIN fnt := P.W.lib(Fonts.Font); IF size < 1 THEN size := 1 ELSIF size > 9 THEN size := 9 END; PushTextAttrs(P); CASE size OF 			1: Texts.SetFont(P.W, ChangeFontSize(fnt, 8)) |2: Texts.SetFont(P.W, ChangeFontSize(fnt, 10)) |3: Texts.SetFont(P.W, ChangeFontSize(fnt, 12)) |4: Texts.SetFont(P.W, ChangeFontSize(fnt, 14)) |5: Texts.SetFont(P.W, ChangeFontSize(fnt, 16)) |6: Texts.SetFont(P.W, ChangeFontSize(fnt, 20)) |7: Texts.SetFont(P.W, ChangeFontSize(fnt, 24)) |8: fnt := ChangeFontSize(fnt, 24); Texts.SetFont(P.W, ChangeFontAttr(fnt, "m")) |9: fnt := ChangeFontSize(fnt, 24); Texts.SetFont(P.W, ChangeFontAttr(fnt, "b")) END END SetFontSize; PROCEDURE HorzRule*(P: Page; w, h: INTEGER); VAR obj: Objects.Object; BEGIN IF P.lines <= 0 THEN WriteLn(P) END; h := 2*h; IF h < 4 THEN h := 4 END; obj := Gadgets.CreateObject("BasicFigures.NewRect3D"); Attributes.SetBool(obj, "Filled", TRUE); Attributes.SetInt(obj, "Color", P.textbackC); Gadgets.ModifySize(obj(Display.Frame), w, h); WriteObj(P, obj); P.lines := 1; WriteLn(P) END HorzRule; PROCEDURE TextAlign*(VAR align: ARRAY OF CHAR): TextGadgets.Style; VAR style: TextGadgets.Style; BEGIN style := NewStyle; EXCL(style.mode, TextGadgets.left); CASE CAP(align[0]) OF 			|"L": INCL(style.mode, TextGadgets.left) |"R": INCL(style.mode, TextGadgets.right) |"C", "J": INCL(style.mode, TextGadgets.middle) ELSE (* BLEED... *) CASE CAP(align[5]) OF 				"L": INCL(style.mode, TextGadgets.left) |"R": INCL(style.mode, TextGadgets.right) |"C", "J": INCL(style.mode, TextGadgets.middle) ELSE RETURN NIL END END; RETURN style END TextAlign; PROCEDURE CloseA*(P: Page); BEGIN IF P.alink # NIL THEN WriteObj(P, P.alink); P.alink := NIL; WHILE (P.W.col = SHORT(P.linkC)) OR (P.W.col = SHORT(P.oldLinkC)) DO 				PopTextAttrs(P); IF P.textAttrs = NIL THEN RETURN END END END END CloseA; PROCEDURE OpenList(P: Page; kind: INTEGER); VAR list: List; BEGIN PushTextAttrs(P); NEW(list); list.itemNr := 0; list.style := NewStyle; IF P.lists = NIL THEN list.nesting := 0; WriteLn(P); WriteLn(P) ELSE list.nesting := P.lists.nesting+1 END; P.style := list.style; WriteObj(P, list.style); list.dtok := FALSE; list.kind := kind; list.next := P.lists; P.lists := list END OpenList; PROCEDURE CloseList(P: Page); BEGIN IF P.lists # NIL THEN IF P.lists.itemNr = 0 THEN P.lists.style.leftM := 32 END; P.lists := P.lists.next END; PopTextAttrs(P); IF P.lists = NIL THEN WriteLn(P); WriteLn(P) END END CloseList; PROCEDURE FindFormObj*(form: Form; name: ARRAY OF CHAR): Objects.Object; VAR ol: HTTPDocs0.ObjList; oname: ARRAY 64 OF CHAR; obj: Objects.Object; BEGIN obj := NIL; ol := form.elems; WHILE ol # NIL DO 			Attributes.GetString(ol.obj, "Name", oname); IF oname = name THEN obj := ol.obj END; ol := ol.next END; RETURN obj END FindFormObj; PROCEDURE RememberValue*(obj: Objects.Object); VAR A: Objects.AttrMsg; BEGIN A.id := Objects.get; A.name := "Value"; A.class := Objects.Inval; obj.handle(obj, A); A.id := Objects.set; A.name := "IniValue"; obj.handle(obj, A) 	END RememberValue; PROCEDURE AddFormObj*(P: Page; form: Form; obj: Objects.Object; name: ARRAY OF CHAR; storeVal, write: BOOLEAN); VAR ol, op: HTTPDocs0.ObjList; BEGIN NEW(ol); ol.obj := obj; ol.next := NIL; op := form.elems; WHILE (op # NIL) & (op.next # NIL) DO 			op := op.next END; IF op # NIL THEN op.next := ol 		ELSE form.elems := ol 		END; IF storeVal THEN RememberValue(obj) END; IF name # "" THEN Gadgets.NameObj(obj, name) END; IF write THEN WriteObj(P, obj) END END AddFormObj; PROCEDURE *FindA(name: ARRAY OF CHAR); BEGIN IF name = searchAttr THEN found := TRUE END END FindA; PROCEDURE HasA(obj: Objects.Object; name : ARRAY OF CHAR): BOOLEAN; VAR A: Objects.AttrMsg; BEGIN found := FALSE; COPY(name, searchAttr); A.id := Objects.enum; COPY(name, A.name); A.res := -1; A.Enum := FindA; obj.handle(obj, A); RETURN found END HasA; PROCEDURE ResetValues(form: Form); VAR A: Objects.AttrMsg; ol: HTTPDocs0.ObjList; item: Item; BEGIN ol := form.elems; WHILE ol # NIL DO 			IF HasA(ol.obj, "IniValue") THEN A.id := Objects.get; A.name := "IniValue"; A.class := Objects.Inval; ol.obj.handle(ol.obj, A); A.id := Objects.set; A.name := "Value"; ol.obj.handle(ol.obj, A); Gadgets.Update(ol.obj) ELSIF ol.obj IS Lists.List THEN item := ol.obj(Lists.List).items(Item); WHILE item # NIL DO 					item.sel := item.oldSel; IF item.next # NIL THEN item := item.next(Item) ELSE item := NIL END END; Gadgets.Update(ol.obj) END; ol := ol.next END END ResetValues; PROCEDURE GetText*(view: Objects.Object): Texts.Text; VAR model: Objects.Object; BEGIN IF view # NIL THEN Links.GetLink(view, "Model", model); IF (model # NIL) & (model IS Texts.Text) THEN RETURN model(Texts.Text) ELSE RETURN NIL END ELSE RETURN NIL END END GetText; (** HTMLDocs.Locate label 		Used for hyperlinks within the same page. *) PROCEDURE Locate*; VAR S: Attributes.Scanner; F: Texts.Finder; pos: LONGINT; obj: Objects.Object; name: ARRAY 32 OF CHAR; curDoc: Documents.Document; node: HyperDocs.Node; text: Texts.Text; BEGIN curDoc := Desktops.CurDoc(Gadgets.context); node := HyperDocs.NodeByDoc(curDoc); text := GetText(Gadgets.context); IF (curDoc # NIL) & (text # NIL) THEN Attributes.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Attributes.Scan(S); IF (S.class = Attributes.String) OR (S.class = Attributes.Name) THEN Texts.OpenFinder(F, text, 0); pos := F.pos; Texts.FindObj(F, obj); WHILE ~F.eot DO 					IF obj IS Gadgets.Frame THEN Attributes.GetString(obj, "Name", name); IF S.s = name THEN IF node # NIL THEN HyperDocs.RememberOrg(curDoc.dsc(TextGadgets.Frame).org, node, node); HyperDocs.LinkNodeToDoc(curDoc, node) END; HyperDocs.ScrollTo(curDoc.dsc(TextGadgets.Frame), pos); RETURN END END; pos := F.pos; Texts.FindObj(F, obj) END END END END Locate; (** HTMLDocs.ExecNext 		Used by clickable images to activate the surrounding hyperlink. *) PROCEDURE ExecNext*; VAR F: Texts.Finder; exec, obj: Objects.Object; text: Texts.Text; BEGIN exec := Gadgets.executorObj; text := GetText(Gadgets.context); IF (text # NIL) & (exec # NIL) THEN Texts.OpenFinder(F, text, 0); Texts.FindObj(F, obj); WHILE ~F.eot DO 				IF obj = exec THEN exec := NIL ELSIF (exec = NIL) & (obj IS TextGadgets.Control) THEN Gadgets.ExecuteAttr(obj(TextGadgets.Control), "Cmd", Gadgets.context, NIL, NIL); RETURN END; Texts.FindObj(F, obj) END END END ExecNext; PROCEDURE CurForm(context: Objects.Object): Form; VAR F: Texts.Finder; exec, obj: Objects.Object; ol: HTTPDocs0.ObjList; text: Texts.Text; BEGIN IF context # NIL THEN exec := Gadgets.executorObj; text := GetText(context); IF text # NIL THEN Texts.OpenFinder(F, text, 0); Texts.FindObj(F, obj); WHILE ~F.eot DO 					IF obj IS Form THEN ol := obj(Form).elems; WHILE (ol # NIL) & (ol.obj # exec) DO 							ol := ol.next END; IF ol # NIL THEN RETURN obj(Form) END END; Texts.FindObj(F, obj) END END; RETURN CurForm(context.dlink) END; RETURN NIL END CurForm; (** HTMLDocs.Reset 		Used by the "Reset" button in forms. *) PROCEDURE Reset*; BEGIN ResetValues(CurForm(Gadgets.context)) END Reset; PROCEDURE CopyForm(VAR M: Objects.CopyMsg; from, to: Form); BEGIN TextGadgets.CopyControl(M, from, to); to.elems := from.elems END CopyForm; PROCEDURE *FormHandler(F: Objects.Object; VAR M: Objects.ObjMsg); VAR ol: HTTPDocs0.ObjList; obj, action: Objects.Object; key: LONGINT; F1: Form; BEGIN WITH F: Form DO 			IF M IS Objects.AttrMsg THEN WITH M: Objects.AttrMsg DO 					IF (M.id = Objects.get) & (M.name = "Gen") THEN M.class := Objects.String; M.s := "HTMLDocs.NewForm"; M.res := 0 ELSE TextGadgets.ControlHandler(F, M) 					END END ELSIF M IS Objects.FileMsg THEN WITH M: Objects.FileMsg DO 					TextGadgets.ControlHandler(F, M); IF M.id = Objects.load THEN HyperDocs.LoadLink(M.R, key); action := Gadgets.CreateObject("BasicGadgets.NewInteger"); Attributes.SetInt(action, "Value", key); Gadgets.NameObj(action, "@ACTION"); NEW(F.elems); F.elems.obj := action; F.elems.next := NIL; Gadgets.ReadRef(M.R, F.lib, obj); WHILE obj # NIL DO 							NEW(ol); ol.next := F.elems; F.elems := ol; ol.obj := obj; Gadgets.ReadRef(M.R, F.lib, obj) END ELSIF M.id = Objects.store THEN action := FindFormObj(F, "@ACTION"); Attributes.GetInt(action, "Value", key); HyperDocs.StoreLink(M.R, key); ol := F.elems; WHILE ol # NIL DO 							IF ol.obj # action THEN Gadgets.WriteRef(M.R, F.lib, ol.obj) END; ol := ol.next END; Gadgets.WriteRef(M.R, F.lib, NIL) END END ELSIF M IS Objects.CopyMsg THEN WITH M: Objects.CopyMsg DO 					IF M.stamp = F.stamp THEN M.obj := F.dlink ELSE NEW(F1); F.stamp := M.stamp; F.dlink := F1; CopyForm(M, F, F1); M.obj := F1 					END END ELSIF M IS Objects.BindMsg THEN ol := F.elems; WHILE ol # NIL DO 					ol.obj.handle(ol.obj, M); ol := ol.next END ELSE TextGadgets.ControlHandler(F, M) 			END END END FormHandler; PROCEDURE NewForm*; VAR form: Form; BEGIN NEW(form); form.W := 0; form.H := 0; form.elems := NIL; form.handle := FormHandler; Objects.NewObj := form END NewForm; PROCEDURE HexDigit(i: INTEGER): CHAR; BEGIN IF i < 10 THEN RETURN CHR(i+ORD("0")) ELSE RETURN CHR(i-10+ORD("A")) END END HexDigit; PROCEDURE MapCoord(obj: Objects.Object; VAR x, y: INTEGER); VAR M: Display.LocateMsg; BEGIN M.F := NIL; M.loc := NIL; M.res := -1; M.X := Oberon.Mouse.X; M.Y := Oberon.Mouse.Y; 		Display.Broadcast(M); IF M.loc = obj THEN x := M.u; y := -M.v 		ELSE x := 0; y := 0 END END MapCoord; PROCEDURE ComposeQuery(form: Form; exec: Objects.Object; VAR query: Texts.Text); VAR ol: HTTPDocs0.ObjList; x, y: INTEGER; obj: Objects.Object; text: Texts.Text; name: Objects.Name; str: ARRAY 64 OF CHAR; (* LEN(Objects.AttrMsg.s) *) item: Item; A: Objects.AttrMsg; R: Texts.Reader; ch: CHAR; multisel, done: BOOLEAN; PROCEDURE WriteEscCh(ch: CHAR); BEGIN ch := Strings.OberonToISO[ORD(ch)]; IF (ch < 020X) OR (ch = "+") OR(ch = "&") OR (ch = "=") OR (ch = "?") OR (ch = "%") OR (ch = "$") OR 				(ch = ";") OR (ch = "/") OR (ch = "#") OR (ch = ":") THEN Texts.Write(Wr, "%"); Texts.Write(Wr, HexDigit(ORD(ch) DIV 16)); Texts.Write(Wr, HexDigit(ORD(ch) MOD 16)) ELSIF ch = 020X THEN Texts.Write(Wr, "+"); ELSIF ch >= CHR(128) THEN Texts.Write(Wr, "%"); Texts.Write(Wr, HexDigit(ORD(ch) DIV 16)); Texts.Write(Wr, HexDigit(ORD(ch) MOD 16)) ELSE Texts.Write(Wr, ch) END END WriteEscCh; PROCEDURE IntAttr(i: LONGINT); BEGIN Texts.WriteString(Wr, name); Texts.Write(Wr, "="); Texts.WriteInt(Wr, i, 0); Texts.Write(Wr, "&") END IntAttr; PROCEDURE StrAttr(s: ARRAY OF CHAR); VAR i: INTEGER; BEGIN Texts.WriteString(Wr, name); Texts.Write(Wr, "="); i := 0; WHILE s[i] # 0X DO 				WriteEscCh(s[i]); INC(i) END; Texts.Write(Wr, "&") END StrAttr; PROCEDURE RealAttr(y: LONGREAL; l: INTEGER); BEGIN Texts.WriteString(Wr, name); Texts.Write(Wr, "="); Texts.WriteLongReal(Wr, y, l); Texts.Write(Wr, "&") END RealAttr; BEGIN NEW(query); Texts.Open(query, ""); ol := form.elems; WHILE ol # NIL DO 			obj := ol.obj; Gadgets.GetObjName(obj, name); IF (name # "") & (name[0] # "@") THEN IF HasA(obj, "FormElem") THEN Attributes.GetString(obj, "FormElem", str); IF (str = "IMAGE") & (exec = obj) THEN MapCoord(obj, x, y); Strings.Append(name, ".x"); IntAttr(x); Gadgets.GetObjName(obj, name); Strings.Append(name, ".y"); IntAttr(y) ELSIF str = "SELECT" THEN done := FALSE; Attributes.GetBool(obj, "MultiSel", multisel); item := obj(Lists.List).items(Item); WHILE (item # NIL) & (~done OR multisel) DO 							IF item.sel THEN done := TRUE; IF item.hasVal THEN StrAttr(item.value) ELSE StrAttr(item.s) 								END END; IF item.next # NIL THEN item := item.next(Item) ELSE item := NIL END END; IF ~done THEN StrAttr("") END ELSIF str = "TEXTAREA" THEN Texts.WriteString(Wr, name); Texts.Write(Wr, "="); text := GetText(obj); IF text # NIL THEN Texts.OpenReader(R, text, 0); Texts.Read(R, ch); WHILE ~R.eot DO 								IF ch = Strings.CR THEN (* SendText -> CRLF *) Texts.WriteLn(Wr) ELSE WriteEscCh(ch) END; Texts.Read(R, ch) END END; Texts.Write(Wr, "&") ELSIF str = "PASSWORD" THEN Attributes.GetString(obj, "Value", A.s); StrAttr(A.s) 					END ELSIF HasA(obj, "YesVal") THEN Attributes.GetBool(obj, "Value", A.b); IF A.b THEN Attributes.GetString(obj, "YesVal", str); StrAttr(str) ELSIF HasA(obj, "SubmitVal") & (obj = exec) THEN Attributes.GetString(obj, "SubmitVal", str); StrAttr(str) END ELSIF HasA(obj, "Value") THEN IF HasA(obj, "MaxLen") THEN Attributes.GetInt(obj, "MaxLen", A.i); x := SHORT(A.i) 					ELSE x := -1 END; A.id := Objects.get; A.name := "Value"; A.class := Objects.Inval; A.res := -1; obj.handle(obj, A); CASE A.class OF 						Objects.Int: IntAttr(A.i); |Objects.String: IF (x >= 0) & (x < LEN(A.s)) THEN A.s[x] := 0X END; StrAttr(A.s) 						|Objects.Real: IF x > 0 THEN RealAttr(A.x, x) 												ELSE RealAttr(A.x, 15) END |Objects.LongReal: IF x > 0 THEN RealAttr(A.y, x) 												ELSE RealAttr(A.y, 15) END ELSE END END END; ol := ol.next END; Texts.Append(query, Wr.buf); Texts.Delete(query, query.len-1, query.len) END ComposeQuery; (** HTMLDocs.SubmitQuery 		Used by the "Submit" button in forms. *) PROCEDURE SubmitQuery*; VAR exec, obj: Objects.Object; attr, qury: ARRAY 32 OF CHAR; key, mkey: LONGINT; docname: ARRAY 1024 OF CHAR; query: Texts.Text; form: Form; x, y: INTEGER; cont: HTTPDocs0.Context; doc: Documents.Document; clearCache: BOOLEAN; BEGIN query := NIL; exec := Gadgets.executorObj; IF HasA(exec, "Query") & HasA(exec, "Method") THEN clearCache := TRUE; form := CurForm(Gadgets.context); obj := FindFormObj(form, "@ACTION"); Attributes.GetInt(obj, "Value", key); HyperDocs.DocNameByKey(docname, key); Attributes.GetString(exec, "Query", qury); IF qury = "ISINDEX" THEN obj := FindFormObj(CurForm(Gadgets.context), "QUERY"); Attributes.GetString(obj, "Value", attr); NEW(query); Texts.Open(query, ""); Texts.WriteString(Wq, attr); Texts.Append(query, Wq.buf); attr := "GET" ELSIF qury = "ISMAP" THEN IF HasA(exec, "UseMapKey") THEN clearCache := FALSE; query := NIL; Attributes.GetInt(exec, "UseMapKey", mkey) ELSE mkey := HyperDocs.UndefKey END; IF mkey = HyperDocs.UndefKey THEN NEW(query); Texts.Open(query, ""); MapCoord(exec, x, y); Texts.WriteInt(Wq, x, 0); Texts.Write(Wq, ","); Texts.WriteInt(Wq, y, 0); Texts.Append(query, Wq.buf) ELSE query := NIL; key := mkey END; attr := "GET" ELSIF qury = "FORM" THEN ComposeQuery(form, exec, query); Attributes.GetString(exec, "Method", attr) ELSE Texts.WriteString(Wr, "unknown query type "); Texts.WriteString(Wr, qury); Texts.WriteLn(Wr); Texts.Append(Oberon.Log, Wr.buf); RETURN END; IF (attr = "GET") OR (attr = "POST") THEN NEW(cont); cont.query := query; COPY(attr, cont.method); cont.user := ""; cont.passwd := ""; IF clearCache THEN HyperDocs.CacheDoc(key, NIL); HyperDocs.CacheText(key, NIL) END; HyperDocs.FollowKeyLink(cont, key) ELSIF attr = "Authorization" THEN NEW(cont); cont.query := NIL; cont.method := "GET"; obj := FindFormObj(form, "Username"); Attributes.GetString(obj, "Value", cont.user); obj := FindFormObj(form, "Password"); Attributes.GetString(obj, "Value", cont.passwd); HyperDocs.FollowKeyLink(cont, key) ELSIF attr = "MAILTO" THEN NEW(cont); cont.curDoc := NIL; cont.new := NIL; cont.replace := FALSE; cont.history := FALSE; cont.old := HyperDocs.NodeByDoc(Desktops.CurDoc(Gadgets.context)); HyperDocs.context := cont; HyperDocs.RetrieveLink(key, docname); doc := Documents.Open(docname); HyperDocs.context := NIL; IF (doc # NIL) & (doc.dsc # NIL) THEN IF (query # NIL) & (doc.dsc IS TextGadgets.Frame) THEN Texts.WriteLn(Wr); Texts.Insert(query, 0, Wr.buf); Texts.WriteLn(Wr); Texts.Append(query, Wr.buf); Texts.Save(query, 0, query.len, Wr.buf); Texts.Append(doc.dsc(TextGadgets.Frame).text, Wr.buf) END; Desktops.ShowDoc(doc) ELSIF query # NIL THEN TextDocs.ShowText("mailto query", query, HyperDocs.docW, HyperDocs.docH) END ELSE Texts.WriteString(Wr, "unknown query method "); Texts.WriteString(Wr, attr); Texts.WriteLn(Wr); Texts.Append(Oberon.Log, Wr.buf); RETURN END END END SubmitQuery; PROCEDURE HREF(P: Page; VAR href, cmd: ARRAY OF CHAR; VAR key: LONGINT); VAR i, j: LONGINT; BEGIN key := -1; COPY("", cmd); i := 0; WHILE (href[i] # 0X) & (href[i] <= " ") DO 			INC(i) END; IF href[i] = "#" THEN COPY("HTMLDocs.Locate ", cmd); href[0] := " "; Strings.Append(cmd, href); cmd[16] := 022X; Strings.AppendCh(cmd, 022X) ELSE WHILE (href[i] # 0X) & (href[i] # "#") DO 				INC(i) END; IF href[i] = "#" THEN DEC(i); j := 0; WHILE P.base.path[j] # 0X DO 					INC(j) END; DEC(j); WHILE (i > 0) & (j > 0) & (href[i] = P.base.path[j]) DO 					DEC(i); DEC(j) END; IF i = 0 THEN COPY("HTMLDocs.Locate ", cmd); j := 0; WHILE cmd[j] # 0X DO 						INC(j) END; cmd[j] := 022X; INC(j); WHILE (href[i] # 0X) & (href[i] # "#") DO 						INC(i) END; INC(i); WHILE href[i] # 0X DO 						cmd[j] := href[i]; INC(j); INC(i) END; cmd[j] := 022X; cmd[j+1] := 0X; RETURN END END; key := HyperDocs.BuildKey(P.base, href); IF key = HyperDocs.UndefKey THEN Texts.WriteString(Wr, href); Texts.WriteString(Wr, " link-typ not supported"); Texts.WriteLn(Wr); Texts.Append(Oberon.Log, Wr.buf) END END END HREF; PROCEDURE Read*(VAR S: Scanner); VAR St: Streams.Stream; BEGIN St := S.S; S.ch := S.next; IF St.eos & (St.Available(St) <= 0) THEN S.avail := 0; S.next := 0X; S.end := TRUE ELSE TextStreams.Read(St, S.next); DEC(S.avail); Texts.Write(S.page.Ws, S.next); IF S.ch = Strings.CR THEN IF S.next = Strings.LF THEN TextStreams.Read(St, S.next); DEC(S.avail); Texts.Write(S.page.Ws, S.next) END; S.ch := Strings.CR 			ELSIF S.ch = Strings.LF THEN S.ch := Strings.CR 			END END END Read; PROCEDURE ChangeFontFamily(f: Fonts.Font; newfamily: ARRAY OF CHAR): Fonts.Font; VAR family: ARRAY 64 OF CHAR; str: ARRAY 4 OF CHAR; size: INTEGER; attr: CHAR; BEGIN SplitFontName(f, family, size, attr); IF family # newfamily THEN COPY(newfamily, family); Strings.IntToStr(size, str); Strings.Append(family, str); IF attr # 0X THEN Strings.AppendCh(family, attr) END; Strings.Append(family, ".Scn.Fnt"); RETURN Fonts.This(family) ELSE RETURN f 		END END ChangeFontFamily; PROCEDURE WriteCharRef*(P: Page; VAR S: Scanner); VAR i, j, k : LONGINT; entity: ARRAY 64 OF CHAR; istr: ARRAY 4 OF CHAR; lib: Objects.Library; obj: Objects.Object; fnt: Fonts.Font; ref: INTEGER; BEGIN IF S.ch = "#" THEN Read(S); IF Strings.IsDigit(S.ch) THEN i := 0; WHILE ~S.end & (i < 9100 ) & Strings.IsDigit(S.ch) DO 					i := 10*i+ORD(S.ch)-ORD("0"); Read(S) END; IF S.ch = ";" THEN Read(S) END; CASE i OF 					169: Texts.WriteString(P.W, "(c)") |174: Texts.WriteString(P.W, "(R)") |188: Texts.WriteString(P.W, " 1/4") |189: Texts.WriteString(P.W, " 1/2") |190: Texts.WriteString(P.W, " 3/4") ELSE fnt := P.W.lib(Fonts.Font); IF (i >= 913) & (i <= 982) THEN	(* Greek character *) Strings.IntToStr(i - 900, istr); k := 0; WHILE ((istr[0] # GreekTab[k]) OR (istr[1] # GreekTab[k+1])) & (k < 120) DO 							k := k + 2 END; k := k DIV 2; Texts.SetFont (P.W, ChangeFontFamily(fnt, "Greek")); Texts.Write(P.W, CHR(k + ORD("@"))); Texts.SetFont (P.W, fnt); ELSIF (i >= 8501) & (i <= 9002) THEN	(* Math character *) Texts.SetFont (P.W, ChangeFontFamily(fnt, "Math")); CASE i OF	(*  glyph	TeX name *) 9001: Texts.Write(P.W, "a")	(*	 a	 langle *) (* b *)	(*	 b	 vdash *) (* c *)	(*	 c	 models *) (* d *)	(*	 d	 dashv *) | 8721: Texts.Write(P.W, "e")	(*	 e	 sum *) (* f *)	(*	 f	 ll *) (* g *)	(*	 g	 gg *) | 8968: Texts.Write(P.W, "h")	(*	 h 	lceil *) | 8969: Texts.Write(P.W, "i")	(*	 i 	rceil *) | 8970: Texts.Write(P.W, "j")	(*	 j 	lfloor *) | 8971: Texts.Write(P.W, "k")	(*	 k 	rfloor *) (* l *)	(*	 l 	Vert *) (* m *)	(*	 m 	mp *) (* n *)	(*	 n 	prec *) (* o *)	(*	 o 	succ *) | 8776: Texts.Write(P.W, "p")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">p 	simeq *) | 9002: Texts.Write(P.W, "q")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">q 	rangle *) (* r *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">r 	not supset *) | 8629: Texts.Write(P.W, "s")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">s 	(carriage return) *) (* t *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">t 	?? *) (* u *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">u 	?? *) (* v - moved down *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">v 	S *) (* w *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">w 	?? *) | 8747: Texts.Write(P.W, "x")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">x	 int *) (* y *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">y 	oint *) (* z *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">z 	because *) (* C *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">C 	wp *) (* D *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">D 	oslash *) | 8853: Texts.Write(P.W, "E")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">E 	oplus *) | 8709: Texts.Write(P.W, "F")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">F 	emptyset *) | 8745: Texts.Write(P.W, "G")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">G 	cap *) | 8746: Texts.Write(P.W, "H")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">H 	cup *) | 8835: Texts.Write(P.W, "I")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">I 	supset *) | 8839: Texts.Write(P.W, "J")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">J 	supseteq *) | 8836: Texts.Write(P.W, "K")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">K 	not subset *) | 8834: Texts.Write(P.W, "L")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">L 	subset *) | 8838: Texts.Write(P.W, "M")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">M 	subseteq *) | 8712: Texts.Write(P.W, "N")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">N 	in *) | 8713: Texts.Write(P.W, "O")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">O 	not in *) | 8736: Texts.Write(P.W, "P")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">P 	angle *) | 8711: Texts.Write(P.W, "Q")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">Q 	nabla *) | 8704: Texts.Write(P.W, "R")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">R 	forall *) | 8707: Texts.Write(P.W, "S")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">S 	exists *) | 8715: Texts.Write(P.W, "T")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">T 	owns *) (* U *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">U 	sqcap *) | 8730: Texts.Write(P.W, "V")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">V 	surb *) | 8901: Texts.Write(P.W, "W")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">W 	cdot *) (* X *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">X 	lnot *) | 8869: Texts.Write(P.W, "Y")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">Y 	land *) | 8870: Texts.Write(P.W, "Z")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">Z 	lor *) (* 0 - moved down *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">0 	deg *) (* 1 - moved down *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">1 	pm *) | 8805: Texts.Write(P.W, "3")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">3 	geq *) (* 4 *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">4 	times *) | 8733: Texts.Write(P.W, "5")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">5 	propto *) | 8706: Texts.Write(P.W, "6")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">6 	partial *) (* 7 - moved down *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">7 	bullet *) (* 8 - moved down *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">8 	div *) | 8800: Texts.Write(P.W, "9")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">9 	not= *) (* ) *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">) 	swarrow *) (* ! *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">! 	mathbb R ?? *) | 8501: Texts.Write(P.W, "@")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">@ 	aleph *) | 8804: Texts.Write(P.W, "#")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF"># 	leq *) (* $ - moved down *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">$ 	/ *) | 8734: Texts.Write(P.W, "%")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">% 	infty *) | 8658: Texts.Write(P.W, "^")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">^ 	Rightarrow *) (* & *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">& 	>> *) (* * *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">* 	searrow *) (* ( *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">( 	nwarrow *) | 8592: Texts.Write(P.W, ",")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">, 	gets *) | 8594: Texts.Write(P.W, ".")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">. 	to *) | 8773: Texts.Write(P.W, ";")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">; 	approx *) (* ' *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">' 	nearrow *) (* ` *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">` 	?? *) | 8593: Texts.Write(P.W, "-")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">- 	uparrow *) (* = *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">= 	vert *) | 8595: Texts.Write(P.W, "/")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">/ 	downarrow *) | 8656: Texts.Write(P.W, "\")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">\ 	Leftarrow *) | 8660: Texts.Write(P.W, "[")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">[ 	Leftrightarrow *) | 8657: Texts.Write(P.W, "]")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">] 	Uparrow *) (* < - moved down *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">< 	dots *) | 8722: Texts.Write(P.W, ">")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">> 	(minus sign) *) | 8801: Texts.Write(P.W, ":")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">: 	equiv *) (* " - moved down *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">" 	prime *) (* ~ - moved down *)	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">~ 	cents *) | 8659: Texts.Write(P.W, "_")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">_ 	Downarrow *) | 8596: Texts.Write(P.W, "+")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">+ 	leftrightarrow *) ELSE END; Texts.SetFont (P.W, fnt); ELSIF (i >= 8201) & (i <= 8500) THEN	(* Math character *) Texts.SetFont (P.W, ChangeFontFamily(fnt, "Math")); CASE i OF	(*  glyph	TeX name *) 8465: Texts.Write(P.W, "A")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">A 	Im *) | 8476: Texts.Write(P.W, "B")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">B 	Re *) | 8243: Texts.Write(P.W, "2")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">2 	(double prime) *) | 8226: Texts.Write(P.W, "7")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">7 	bullet *) | 8260: Texts.Write(P.W, "$")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">$ 	/ *) | 8230: Texts.Write(P.W, "<")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">< 	dots *) | 8242: Texts.Write(P.W, '"')	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">" 	prime *) | 8224: Texts.Write(P.W, "}")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">} 	dagger *) ELSE END; Texts.SetFont (P.W, fnt); (* 					ELSIF (i >= 160) & (i <= 250) THEN	(* Math character *) *) ELSIF (i = 167) OR (i = 176) OR (i = 177) OR (i = 162) OR (i = 247) THEN	(* Math character *) Texts.SetFont (P.W, ChangeFontFamily(fnt, "Math")); CASE i OF	(*  glyph	TeX name *) 167: Texts.Write(P.W, "v")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">v 	S *) | 176: Texts.Write(P.W, "0")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">0 	deg *) | 177: Texts.Write(P.W, "1")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">1 	pm *) | 247: Texts.Write(P.W, "8")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">8 	div *) | 162: Texts.Write(P.W, "~")	(*	 <span style="font-family: Math, sans-serif; font-size: 1.08rem; color: #0000FF">~ 	cents *) ELSE END; Texts.SetFont (P.W, fnt); ELSE Texts.Write(P.W, Strings.ISOToOberon[i]) END END ELSE Texts.WriteString(P.W, "&&#35;") END ELSE i := 0; WHILE ~S.end & Strings.IsAlpha(S.ch) & (i < 63) DO 				entity[i] := S.ch; INC(i); entity[i] := 0X; j := 0; WHILE (j < LEN(entities)) & (entities[j] # entity) DO 					INC(j) END; IF j < LEN(entities) THEN i := 63 END; Read(S) END; entity[i] := 0X; i := 0; WHILE (i < LEN(entities)) & (entities[i] # entity) DO 				INC(i) END; IF i < LEN(entities) THEN Texts.Write(P.W, entityEncoding[i]) ELSIF entity = "trade" THEN PushTextAttrs(P); Texts.SetOffset(P.W, 4); Texts.WriteString(P.W, "TM"); PopTextAttrs(P) ELSIF entity = "reg" THEN Texts.WriteString(P.W, "(R)") ELSIF entity = "copy" THEN Texts.WriteString(P.W, "(c)") ELSE lib := Objects.ThisLibrary("HTMLIcons.Lib"); Objects.GetRef(lib.dict, entity, ref); IF ref >= 0 THEN lib.GetObj(lib, ref, obj); WriteObj(P, obj) ELSE Texts.Write(P.W, "&"); Texts.WriteString(P.W, entity); RETURN END END; IF S.ch = ";" THEN Read(S) END END END WriteCharRef; PROCEDURE CharRefStr*(P: Page; VAR S: Scanner; VAR str: ARRAY OF CHAR); VAR i, j: LONGINT; entity: ARRAY 64 OF CHAR; BEGIN COPY("", str); IF S.ch = "#" THEN Read(S); IF Strings.IsDigit(S.ch) THEN i := 0; WHILE ~S.end & (i < 256) & Strings.IsDigit(S.ch) DO 					i := 10*i+ORD(S.ch)-ORD("0"); Read(S) END; IF S.ch = ";" THEN Read(S) END; CASE i OF 					169: Strings.Append(str, "(c)") |174: Strings.Append(str, "(R)") |188: Strings.Append(str, " 1/4") |189: Strings.Append(str, " 1/2") |190: Strings.Append(str, " 3/4") ELSE Strings.AppendCh(str, Strings.ISOToOberon[i]) END ELSE Strings.Append(str, "&&#35;") END ELSIF Strings.IsAlpha(S.ch) THEN i := 0; WHILE ~S.end & Strings.IsAlpha(S.ch) & (i < 63) DO 				entity[i] := S.ch; INC(i); entity[i] := 0X; j := 0; WHILE (j < LEN(entities)) & (entities[j] # entity) DO 					INC(j) END; IF j < LEN(entities) THEN i := 63 END; Read(S) END; entity[i] := 0X; i := 0; WHILE (i < LEN(entities)) & (entities[i] # entity) DO 				INC(i) END; IF i < LEN(entities) THEN Strings.AppendCh(str, entityEncoding[i]) ELSIF entity = "trade" THEN Strings.Append(str, "TM") ELSIF entity = "reg" THEN Strings.Append(str, "(R)") ELSIF entity = "copy" THEN Strings.Append(str, "(c)") ELSE Strings.AppendCh(str, "&"); Strings.Append(str, entity); RETURN END; IF S.ch = ";" THEN Read(S) END ELSE COPY("&", str) END END CharRefStr; PROCEDURE Next*(VAR S: Scanner); VAR i, l: LONGINT; BEGIN CASE S.state OF 			TextHtml: IF S.ch = "<" THEN Read(S); IF Strings.IsAlpha(S.ch) OR (S.ch = "/") OR (S.ch = "!") THEN S.class := OpenTag ELSE S.char := S.ch; S.class := Character END ELSIF S.ch = "&" THEN Read(S); S.class := CharRef ELSIF (S.ch <= " ") & (S.ch # 0X) & ~S.pre THEN WHILE ~S.end & (S.ch <= " ") DO 									Read(S) END; IF (S.ch = "<") & (S.class = CloseTag) THEN Read(S); IF Strings.IsAlpha(S.ch) OR (S.ch = "/") OR (S.ch = "!") THEN S.class := OpenTag ELSE S.char := S.ch; S.class := Character END ELSIF S.end THEN S.class := Undef; S.state := End ELSE S.class := WhiteSpace END ELSIF ~S.end THEN S.char := S.ch; Read(S); S.class := Character(*; S.skipWS := FALSE*) ELSE S.class := Undef; S.state := End END |TextPlain: IF S.ch # 0X THEN S.char := S.ch; Read(S); S.class := Character ELSE S.class := Undef; S.state := End END |InTag: WHILE ~S.end & (S.ch <= " ") DO 							Read(S) END; IF S.ch = "/" THEN Read(S); S.class := OpenEndTag ELSIF S.ch = ">" THEN Read(S); S.class := CloseTag ELSIF S.ch = "&" THEN Read(S); S.class := CharRef ELSIF Strings.IsAlpha(S.ch) THEN i := 0; l := LEN(S.value)-1; WHILE ~S.end & (Strings.IsAlpha(S.ch) OR Strings.IsDigit(S.ch)) DO 								IF i < l THEN S.value[i] := S.ch; INC(i) END; Read(S) END; S.value[i] := 0X; S.class := Value; WHILE ~S.end & (S.ch <= " ") DO 	Read(S) END; IF S.ch = "<" THEN Read(S); WHILE ~S.end & (S.ch <= " ") DO 		Read(S) END; IF Strings.IsAlpha(S.ch) THEN i := 0; l := LEN(S.value)-1; WHILE ~S.end & (Strings.IsAlpha(S.ch) OR Strings.IsDigit(S.ch)) DO 			IF i < l THEN S.value[i] := S.ch; INC(i) END; Read(S) END END END ELSIF ~S.end THEN S.char := S.ch; Read(S); S.class := Character ELSE S.class := Undef; S.state := End END ELSE S.class := Undef; S.state := End END END Next; PROCEDURE NextAttr*(VAR S: Scanner; VAR name: ARRAY OF CHAR): BOOLEAN; VAR i, l: LONGINT; charRef: ARRAY 16 OF CHAR; quoted: BOOLEAN; BEGIN WHILE ~S.end & (S.ch <= " ") DO 			Read(S) END; IF (S.ch = ",") OR (S.ch = ";") THEN Read(S); WHILE ~S.end & (S.ch <= " ") DO 				Read(S) END END; i := 0; l := LEN(name)-1; WHILE ~S.end & Strings.IsAlpha(S.ch) DO 			IF i < l THEN name[i] := CAP(S.ch); INC(i) END; Read(S) END; name[i] := 0X; IF i > 0 THEN WHILE ~S.end & (S.ch <= " ") DO 				Read(S) END; IF S.ch = "=" THEN Read(S); WHILE ~S.end & (S.ch <= " ") DO 					Read(S) END; IF S.ch = 022X THEN Read(S); quoted := TRUE ELSE quoted := FALSE END; i := 0; l := LEN(S.value)-1; WHILE ~S.end & ( (~quoted & (S.ch > " ") & (S.ch # ">")) OR (quoted & (S.ch # 022X)) ) DO 					IF i < l THEN IF S.ch = "&" THEN Read(S); CharRefStr(S.page, S, charRef); S.value[i] := 0X; Strings.Append(S.value, charRef); i := Strings.Length(S.value) ELSIF (S.ch >= " ") OR (S.ch = Strings.Tab) THEN S.value[i] := S.ch; INC(i); Read(S) ELSE Read(S) END ELSE Read(S) END END; IF S.ch = 022X THEN Read(S) END; WHILE ~quoted & (i > 0) & (S.value[i-1] <= " ") DO 					S.value[i-1] := 0X; DEC(i) END; S.value[i] := 0X ELSE COPY("", S.value) END; S.class := Value; RETURN TRUE END; COPY("", name); COPY("", S.value); RETURN FALSE END NextAttr; PROCEDURE GetAttrs*(VAR S: Scanner; VAR attrs: TagAttr); VAR attr: TagAttr; BEGIN attrs := NIL; NEW(attr); WHILE NextAttr(S, attr.name) DO 			attr.next := attrs; attrs := attr; COPY(S.value, attr.value); NEW(attr) END END GetAttrs; PROCEDURE FindAttr*(attrs: TagAttr; name: ARRAY OF CHAR): TagAttr; VAR attr: TagAttr; BEGIN attr := attrs; WHILE (attr # NIL) & (attr.name # name) DO 			attr := attr.next END; RETURN attr END FindAttr; PROCEDURE OpenScanner*(VAR S: Scanner; St: Streams.Stream); BEGIN (*S.skipWS := FALSE; S.genWS := FALSE;*) S.ch := 0X; S.next := 0X; S.end := FALSE; S.S := St; S.class := Undef; S.state := TextHtml; S.pre := FALSE; Read(S) END OpenScanner; PROCEDURE A(VAR S: Scanner; on: BOOLEAN); VAR P: Page; attr: ARRAY 64 OF CHAR; obj: Objects.Object; isRef, newDoc: BOOLEAN; BEGIN P := S.page; CloseA(P); P.linkkey := HyperDocs.UndefKey; IF on THEN isRef := FALSE; newDoc := FALSE; WHILE NextAttr(S, attr) DO 				IF attr = "HREF" THEN isRef := TRUE; HREF(P, S.value, attr, P.linkkey); IF P.linkkey # HyperDocs.UndefKey THEN P.alink := HyperDocs.LinkControl(P.linkkey) ELSE P.alink := Gadgets.CreateObject("TextGadgets.NewControl"); Attributes.SetString(P.alink, "Cmd", attr) END ELSIF (attr = "NAME") OR (attr = "ID") THEN IF S.value = P.orgLabel THEN Texts.Append(P.T, P.W.buf); P.orgPos := P.T.len END; obj := Gadgets.CreateObject("TextGadgets.NewControl"); WriteObj(P, obj); Attributes.SetString(obj, "Name", S.value) ELSIF attr = "TARGET" THEN newDoc := TRUE END END; IF isRef THEN IF newDoc THEN Attributes.SetString(P.alink, "Opt", "New") END; IF ~HyperDocs.Visited(P.linkkey) THEN PushTextAttrs(P); Texts.SetColor(P.W, SHORT(P.linkC)) ELSE PushTextAttrs(P); Texts.SetColor(P.W, SHORT(P.oldLinkC)) END END END END A; 	PROCEDURE ADDRESS(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, Syntax(12, "i")) ELSE PopTextAttrs(P) END END ADDRESS; PROCEDURE B(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, ChangeFontAttr(P.W.lib(Fonts.Font), "b")) ELSE PopTextAttrs(P) END END B; 	PROCEDURE SplitHostPort*(VAR url, host: ARRAY OF CHAR; VAR port: INTEGER); VAR i, j: INTEGER; val: LONGINT; BEGIN i := 0; WHILE (url[i] # 0X) & (url[i] # "/") DO 			INC(i) END; WHILE url[i] = "/" DO 			INC(i) END; j := 0; WHILE (url[j] # 0X) & (url[j] # "@") DO 			INC(j) END; IF url[j] = "@" THEN i := j+1 END; j := 0; WHILE (url[i] # 0X) & (url[i] # "/") & (url[i] # ":") DO 			host[j] := url[i]; INC(i); INC(j) END; host[j] := 0X; IF url[i] = ":" THEN INC(i); Strings.StrToIntPos(url, val, i); port := SHORT(val) ELSE port := 0 END END SplitHostPort; PROCEDURE BASE(VAR S: Scanner; on: BOOLEAN); VAR P: Page; attr, label: ARRAY 64 OF CHAR; s: HyperDocs.LinkScheme; key: LONGINT; port: INTEGER; BEGIN IF on THEN P := S.page; WHILE NextAttr(S, attr) DO 				IF attr = "HREF" THEN P.base.key := HyperDocs.BuildKey(P.base, S.value); s := HyperDocs.LinkSchemeByKey(P.base.key); IF s = NIL THEN s := HyperDocs.LinkSchemeByPrefix("file") END; COPY(s.prefix, P.base.prefix); HyperDocs.RetrieveLink(P.base.key, S.value); IF P.base.prefix = "http" THEN P.base.key := HTTPDocs0.SplitHTTPAdr(S.value, P.base.host, P.base.path, label, P.base.port) ELSIF P.base.prefix = "file" THEN P.base.key := HyperDocs.SplitFileAdr(S.value, P.base.path, label); P.base.host := ""; P.base.port := 0 ELSE (* proxy *) SplitHostPort(S.value, P.base.host, P.base.port); key := HTTPDocs0.SplitHTTPAdr(S.value, P.base.host, P.base.path, label, port) END END END END END BASE; PROCEDURE HexVal(ch: CHAR): INTEGER; BEGIN IF (ch >= "0") & (ch <= "9") THEN RETURN ORD(ch)-ORD("0") ELSIF (ch >= "A") & (ch <= "F") THEN RETURN ORD(ch)-ORD("A")+10 ELSIF (ch >= "a") & (ch <= "f") THEN RETURN ORD(ch)-ORD("a")+10 ELSE RETURN 256 END END HexVal; PROCEDURE Color(VAR val: ARRAY OF CHAR; VAR col: INTEGER); VAR r, g, b, rr, gg, bb, i, bestC: INTEGER; diff, mdiff: REAL; BEGIN IF val = "white" THEN col := 0; RETURN ELSIF val = "red" THEN col := 1; RETURN ELSIF val = "green" THEN col := 2; RETURN ELSIF val = "blue" THEN col := 3; RETURN ELSIF val = "black" THEN col := 15; RETURN ELSIF val[0] = "#" THEN r := 16*HexVal(val[1])+HexVal(val[2]); g := 16*HexVal(val[3])+HexVal(val[4]); b := 16*HexVal(val[5])+HexVal(val[6]) ELSE r := 16*HexVal(val[0])+HexVal(val[1]); g := 16*HexVal(val[2])+HexVal(val[3]); b := 16*HexVal(val[4])+HexVal(val[5]) END; IF (r > 255) OR (g > 255) OR (b > 255) THEN RETURN END; mdiff := MAX(REAL); bestC := 0; i := 0; WHILE (i < 256) & (mdiff > 0.0) DO 			Display.GetColor(i, rr, gg, bb); diff := LONG(rr-r)*LONG(rr-r); diff := diff+LONG(gg-g)*LONG(gg-g); diff := diff+LONG(bb-b)*LONG(bb-b); IF diff < mdiff THEN bestC := i; mdiff := diff END; INC(i) END; col := bestC END Color; PROCEDURE BASEFONT(VAR S: Scanner; on: BOOLEAN); VAR P: Page; attr: ARRAY 64 OF CHAR; val: LONGINT; size, i: INTEGER; col: BOOLEAN; BEGIN P := S.page; IF on THEN col := FALSE; size := GetFontSize(P); WHILE NextAttr(S, attr) DO 				IF attr = "SIZE" THEN i := 1; IF S.value[0] = "-" THEN Strings.StrToIntPos(S.value, val, i); size := size-SHORT(val) ELSIF S.value[0] = "+" THEN Strings.StrToIntPos(S.value, val, i); size := size+SHORT(val) ELSE i := 0; Strings.StrToIntPos(S.value, val, i); size := SHORT(val) END ELSIF attr = "COLOR" THEN Color(S.value, P.textC); col := TRUE END END; SetFontSize(P, size); IF col THEN Texts.SetColor(P.W, SHORT(P.textC)) END ELSE PopTextAttrs(P) END END BASEFONT; PROCEDURE BIG(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN SetFontSize(P, GetFontSize(P)+2) ELSE PopTextAttrs(P) END END BIG; PROCEDURE BLINK(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetColor(P.W, SHORT(Display3.red)) ELSE PopTextAttrs(P) END END BLINK; PROCEDURE BLOCKQUOTE(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); P.style := NewStyle; WriteObj(P, P.style); Texts.SetFont(P.W, Syntax(12, "i")) ELSE PopTextAttrs(P) END; (*S.skipWS := TRUE*) END BLOCKQUOTE; PROCEDURE ChangeColors(P: Page; from, to: SHORTINT); VAR tattr: TextAttrs; BEGIN IF P.W.col = from THEN Texts.SetColor(P.W, to) END; tattr := P.textAttrs; WHILE tattr # NIL DO 			IF tattr.col = from THEN tattr.col := to 			END; tattr := tattr.next END END ChangeColors; PROCEDURE BODY(VAR S: Scanner; on: BOOLEAN); VAR P: Page; attr: ARRAY 64 OF CHAR; key: LONGINT; doc: Documents.Document; BackE: HTTPDocs0.Entry; col: INTEGER; BEGIN P := S.page; IF on THEN Texts.Append(P.T, P.W.buf); WHILE NextAttr(S, attr) DO 				IF attr = "BGCOLOR" THEN Color(S.value, P.textbackC); Attributes.SetInt(P.D.dsc, "Color", P.textbackC) ELSIF attr = "TEXT" THEN col := P.textC; Color(S.value, col); ChangeColors(P, SHORT(P.textC), SHORT(col)); P.textC := col ELSIF attr = "LINK" THEN (* hyper text link *) col := P.linkC; Color(S.value, col); ChangeColors(P, SHORT(P.linkC), SHORT(col)); P.linkC := col; Attributes.SetInt(P.D.dsc, "LinkColor", P.linkC) ELSIF attr = "VLINK" THEN (* visited hyper text link *) col := P.oldLinkC; Color(S.value, col); ChangeColors(P, SHORT(P.oldLinkC), SHORT(col)); P.oldLinkC := col; Attributes.SetInt(P.D.dsc, "OldLinkColor", P.oldLinkC) ELSIF attr = "BACKGROUND" THEN key := HyperDocs.BuildKey(P.base, S.value); IF imgs THEN NEW(BackE); BackE.basekey := P.base.dockey; BackE.ol := NIL; BackE.attrs := NIL; BackE.text := P.T; 						BackE.pos := -1; BackE.key := HyperDocs.UndefKey; BackE.obj := P.D; 						NEW(doc); HyperDocs.DocNameByKey(doc.name, key); doc.handle := NIL; doc.dsc := NIL; HTTPDocs0.RequestDoc(doc, HTTPDocs0.httpProxy, key, "GET", FALSE, BackE, NIL) END END END END END BODY; PROCEDURE BR(VAR S: Scanner; on: BOOLEAN); BEGIN IF on THEN WriteLn(S.page)(*; S.page.lines := 0*) END; (*S.skipWS := TRUE*) END BR; PROCEDURE ExecAttrs*; VAR exec, cont: Objects.Object; name, nr: ARRAY 32 OF CHAR; i: LONGINT; BEGIN cont := Gadgets.context; exec := Gadgets.executorObj; IF exec # NIL THEN i := 1; name := "Cmd"; Strings.IntToStr(i, nr); Strings.Append(name, nr); WHILE Attributes.FindAttr(name, exec(Gadgets.Frame).attr) # NIL DO 				Gadgets.ExecuteAttr(exec(Gadgets.Frame), name, cont, NIL, NIL); INC(i); name := "Cmd"; Strings.IntToStr(i, nr); Strings.Append(name, nr) END END END ExecAttrs; PROCEDURE CALL(VAR S: Scanner; on: BOOLEAN); (* ejz *) VAR P: Page; name, attr: ARRAY 32 OF CHAR; i: LONGINT; BEGIN P := S.page; CloseA(P); IF on THEN P.clink := Gadgets.CreateObject("TextGadgets.NewControl"); i := 0; WHILE NextAttr(S, attr) DO 				IF attr = "CMD" THEN INC(i); name := "Cmd"; Strings.IntToStr(i, attr); Strings.Append(name, attr); Attributes.SetString(P.clink, name, S.value) END END; IF i = 0 THEN P.clink := NIL ELSE Attributes.SetString(P.clink, "Cmd", "HTMLDocs.ExecAttrs") END; PushTextAttrs(P); Texts.SetColor(P.W, SHORT(Display3.red)) ELSE IF P.clink # NIL THEN WriteObj(P, P.clink) END; P.clink := NIL; PopTextAttrs(P) END END CALL; PROCEDURE CENTER(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); P.style := NewStyle; EXCL(P.style.mode, TextGadgets.left); INCL(P.style.mode, TextGadgets.middle); WriteObj(P, P.style) ELSE PopTextAttrs(P) END; (*S.skipWS := TRUE*) END CENTER; PROCEDURE CITE(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, ChangeFontAttr(P.W.lib(Fonts.Font), "i")) ELSE PopTextAttrs(P) END END CITE; PROCEDURE CODEx(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, mono) ELSE PopTextAttrs(P) END END CODEx; PROCEDURE DD(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN IF (P.lists # NIL) & (P.lists.kind = DescList) & P.lists.dtok THEN Texts.Write(P.W, Strings.Tab) ELSE IF P.lists # NIL THEN INC(P.lists.itemNr); IF P.lists.itemNr > 1 THEN WriteLn(P) END ELSE WriteLn(P) END END END; P.blank := FALSE (*S.skipWS := TRUE*) END DD; PROCEDURE DFN(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, ChangeFontAttr(P.W.lib(Fonts.Font), "i")) ELSE PopTextAttrs(P) END END DFN; PROCEDURE DIR(VAR S: Scanner; on: BOOLEAN); BEGIN IF on THEN OpenList(S.page, DefList) ELSE CloseList(S.page) END; (*S.skipWS := TRUE*) END DIR; PROCEDURE DIVI(VAR S: Scanner; on: BOOLEAN); VAR P: Page; attr: ARRAY 32 OF CHAR; style: TextGadgets.Style; BEGIN P := S.page; IF on THEN PushTextAttrs(P); style := NIL; WHILE NextAttr(S, attr) DO 				IF attr = "ALIGN" THEN style := TextAlign(S.value) END END; IF style # NIL THEN P.style := style; WriteObj(P, P.style) END ELSE PopTextAttrs(P) END; (*S.skipWS := TRUE*) END DIVI; PROCEDURE DL(VAR S: Scanner; on: BOOLEAN); BEGIN IF on THEN OpenList(S.page, DescList) ELSE CloseList(S.page) END; (*S.skipWS := TRUE*) END DL; PROCEDURE DT(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN IF P.lists # NIL THEN INC(P.lists.itemNr); IF P.lists.itemNr > 1 THEN WriteLn(P) END; IF P.lists.kind = DescList THEN P.lists.dtok := TRUE; Texts.Write(P.W, Strings.Tab) END ELSE WriteLn(P) END END; P.blank := FALSE (*S.skipWS := TRUE*) END DT; PROCEDURE EM(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, ChangeFontAttr(P.W.lib(Fonts.Font), "i")) ELSE PopTextAttrs(P) END END EM; PROCEDURE FRAME(VAR S: Scanner; on: BOOLEAN); VAR P: Page; attr: ARRAY 64 OF CHAR; BEGIN P := S.page; CloseA(P); P.linkkey := HyperDocs.UndefKey; IF on THEN WHILE NextAttr(S, attr) DO 				IF (attr = "SRC") OR (attr = "HREF") THEN P.lines := 0; WriteLn(P); WriteLn(P); Texts.WriteString(P.W, "FRAME Reference: "); HREF(P, S.value, attr, P.linkkey); IF P.linkkey # HyperDocs.UndefKey THEN P.alink := HyperDocs.LinkControl(P.linkkey); HyperDocs.RetrieveLink(P.linkkey, S.value); Attributes.SetString(P.alink, "Opt", "N") ELSE P.alink := Gadgets.CreateObject("TextGadgets.NewControl"); Attributes.SetString(P.alink, "Cmd", attr) END; PushTextAttrs(P); Texts.SetColor(P.W, SHORT(P.linkC)); Texts.WriteString(P.W, S.value); WriteObj(P, P.alink); P.alink := NIL; P.linkkey := HyperDocs.UndefKey; PopTextAttrs(P); WriteLn(P) END END END END FRAME; PROCEDURE FONT(VAR S: Scanner; on: BOOLEAN); VAR P: Page; attr: ARRAY 64 OF CHAR; val: LONGINT; size, i, textC: INTEGER; col: BOOLEAN; BEGIN P := S.page; IF on THEN col := FALSE; size := GetFontSize(P); WHILE NextAttr(S, attr) DO 				IF attr = "SIZE" THEN i := 1; IF S.value[0] = "-" THEN Strings.StrToIntPos(S.value, val, i); size := size-SHORT(val) ELSIF S.value[0] = "+" THEN Strings.StrToIntPos(S.value, val, i); size := size+SHORT(val) ELSE i := 0; Strings.StrToIntPos(S.value, val, i); size := SHORT(val) END ELSIF attr = "COLOR" THEN Color(S.value, textC); col := TRUE END END; SetFontSize(P, size); IF col THEN Texts.SetColor(P.W, SHORT(textC)) END ELSE PopTextAttrs(P) END END FONT; PROCEDURE GREEN(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetColor(P.W, SHORT(Display3.green)) ELSE PopTextAttrs(P) END END GREEN; PROCEDURE H(VAR S: Scanner; on: BOOLEAN); VAR P: Page; attr: ARRAY 64 OF CHAR; style: TextGadgets.Style; BEGIN P := S.page; IF on THEN style := NIL; PushTextAttrs(P); IF ~P.left THEN WriteLn(P); WriteLn(P) ELSE P.left := FALSE END; CASE ORD(S.value[1])-ORD("0") OF 				1: Texts.SetFont(P.W, Syntax(20, 0X)) |2: Texts.SetFont(P.W, Syntax(16, 0X)) |3: Texts.SetFont(P.W, Syntax(16, "i")) |4: Texts.SetFont(P.W, Syntax(14, 0X)) |5: Texts.SetFont(P.W, Syntax(14, "i")) |6: Texts.SetFont(P.W, Syntax(12, 0X)) ELSE Texts.SetFont(P.W, Syntax(12, 0X)) END; WHILE NextAttr(S, attr) DO 				IF attr = "ALIGN" THEN style := TextAlign(S.value) ELSIF attr = "SRC" THEN (* get image *) ELSIF attr = "DINGBAT" THEN (* get icon with name value *) END END; IF style # NIL THEN P.style := style; WriteObj(P, P.style) END ELSE CloseA(P); PopTextAttrs(P); WriteLn(P); WriteLn(P) END; (*S.skipWS := TRUE*) END H; 	PROCEDURE HEAD(VAR S: Scanner; on: BOOLEAN); BEGIN (* ignore *) END HEAD; PROCEDURE HP(VAR S: Scanner; on: BOOLEAN); BEGIN (* ignore *) END HP; PROCEDURE HR(VAR S: Scanner; on: BOOLEAN); VAR attr: ARRAY 64 OF CHAR; P: Page; l: LONGINT; w, h, pos: INTEGER; BEGIN IF on THEN P := S.page; CloseA(P); w := dispW; h := 1; WHILE NextAttr(S, attr) DO 				IF attr = "SIZE" THEN Strings.StrToInt(S.value, l); h := SHORT(l) ELSIF attr = "WIDTH" THEN pos := 0; Strings.StrToIntPos(S.value, l, pos); w := SHORT(l); IF S.value[pos] = "%" THEN w := SHORT( (LONG((5*dispW) DIV 8) * w) DIV LONG(100) ) END END END; HorzRule(P, w, h) 		END; (*S.skipWS := TRUE*) END HR; PROCEDURE HTML(VAR S: Scanner; on: BOOLEAN); BEGIN (*		IF ~on THEN 			Texts.WriteLn(S.page.W); 			(*S.skipWS := TRUE; *)S.state := TextPlain 		END *) END HTML; PROCEDURE HTTP(VAR S: Scanner; on: BOOLEAN); BEGIN (* ignore *) END HTTP; PROCEDURE I(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, ChangeFontAttr(P.W.lib(Fonts.Font), "i")) ELSE PopTextAttrs(P) END END I; 	PROCEDURE KBD(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, mono) ELSE PopTextAttrs(P) END END KBD; PROCEDURE LI(VAR S: Scanner; on: BOOLEAN); VAR P: Page; i: INTEGER; BEGIN P := S.page; IF on THEN IF P.lists # NIL THEN INC(P.lists.itemNr); IF P.lists.itemNr > 1 THEN WriteLn(P) END; FOR i := 1 TO P.lists.nesting DO 					Texts.Write(P.W, Strings.Tab) END ELSE WriteLn(P) END; IF (P.lists # NIL) & (P.lists.kind = OrderedList) THEN Texts.WriteInt(P.W, P.lists.itemNr, 0) ELSE IF P.lists # NIL THEN i := P.lists.nesting MOD LEN(bullets) ELSE i := 0 END; PushTextAttrs(P); Texts.SetFont(P.W, bullets[i].f); Texts.Write(P.W, bullets[i].c); PopTextAttrs(P) END; Texts.Write(P.W, Strings.Tab) END; P.blank := FALSE (*S.skipWS := TRUE*) END LI; PROCEDURE LINK(VAR S: Scanner; on: BOOLEAN); (*		VAR 			P: Page; 			attr, cmd, caption: ARRAY 64 OF CHAR; 			key: LONGINT; 			obj: Objects.Object; 	BEGIN 		IF on THEN 			P := S.page; caption := ""; cmd := ""; 			WHILE NextAttr(S, attr) DO 				IF attr = "HREF" THEN 					HREF(P, S.value, cmd, key); 					IF key # HyperDocs.UndefKey THEN 						cmd := "HyperDocs.FollowLink #Key" 					END 				ELSIF attr = "REV" THEN 					IF caption = "" THEN 						COPY(S.value, caption) 					END 				ELSIF attr = "REL" THEN 					IF caption = "" THEN 						COPY(S.value, caption) 					END 				ELSIF attr = "TITLE" THEN 					COPY(S.value, caption) 				END 			END; 			IF cmd # "" THEN 				obj := Gadgets.CreateObject("BasicGadgets.NewButton"); 				Attributes.SetString(obj, "Caption", caption); 				Attributes.SetString(obj, "Cmd", cmd); (* ?! fixup on reload *) 				Attributes.SetInt(obj, "Key", key); 				WriteObj(P, obj) 			END 		END	*) END LINK; PROCEDURE LISTING(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, mono); S.pre := TRUE ELSE PopTextAttrs(P); S.pre := FALSE END END LISTING; PROCEDURE ISINDEX(VAR S: Scanner; on: BOOLEAN); VAR P: Page; objt, objb: Objects.Object; form: Form; attr: ARRAY 64 OF CHAR; prompt: ARRAY 512 OF CHAR; key: LONGINT; BEGIN IF on THEN P := S.page; key := P.base.key; NewForm; form := Objects.NewObj(Form); prompt := "This is a searchable index. Enter search keywords: "; WHILE NextAttr(S, attr) DO 				IF attr = "PROMPT" THEN COPY(S.value, prompt) ELSIF attr = "HREF" THEN key := HyperDocs.BuildKey(P.base, S.value) END END; WriteObj(P, form); HorzRule(P, dispW, 1); Texts.WriteString(P.W, prompt); objt := Gadgets.CreateObject("BasicGadgets.NewInteger"); Attributes.SetInt(objt, "Value", key); AddFormObj(P, form, objt, "@ACTION", FALSE, FALSE); objt := Gadgets.CreateObject("TextFields.NewTextField"); AddFormObj(P, form, objt, "QUERY", FALSE, TRUE); objb := Gadgets.CreateObject("BasicGadgets.NewButton"); objb(Display.Frame).H := objt(Display.Frame).H; 			Attributes.SetString(objb, "Query", "ISINDEX"); Attributes.SetString(objb, "Method", "GET"); Attributes.SetString(objb, "Caption", "Search"); Attributes.SetString(objb, "Cmd", "HTMLDocs.SubmitQuery"); AddFormObj(P, form, objb, "", FALSE, TRUE); HorzRule(P, dispW, 1) END END ISINDEX; PROCEDURE MENU(VAR S: Scanner; on: BOOLEAN); BEGIN IF on THEN OpenList(S.page, DefList) ELSE CloseList(S.page) END; (*S.skipWS := TRUE*) END MENU; PROCEDURE META(VAR S: Scanner; on: BOOLEAN); BEGIN (* ignore *) END META; PROCEDURE NEXTID(VAR S: Scanner; on: BOOLEAN); BEGIN (* ignore *) END NEXTID; PROCEDURE NOFRAMES(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; HorzRule(P, dispW, 1) END NOFRAMES; PROCEDURE OL(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN OpenList(P, OrderedList) ELSE CloseList(P) END; (*S.skipWS := TRUE*) END OL; PROCEDURE P(VAR S: Scanner; on: BOOLEAN); VAR P: Page; attr: ARRAY 64 OF CHAR; style: TextGadgets.Style; BEGIN P := S.page; IF on THEN style := NIL; CloseA(P); WriteLn(P); WriteLn(P); (*P.lines := 0;*) WHILE NextAttr(S, attr) DO 				IF attr = "ALIGN" THEN style := TextAlign(S.value) END END; IF style # NIL THEN PushTextAttrs(P); P.style := style; WriteObj(P, P.style) END ELSE PopTextAttrs(P) END; (*S.skipWS := TRUE*) END P; 	PROCEDURE PLAINTEXT(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, mono); S.state := TextPlain; S.pre := TRUE ELSE PopTextAttrs(P); S.state := TextHtml; S.pre := FALSE END END PLAINTEXT; PROCEDURE PRE(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, mono); S.pre := TRUE ELSE PopTextAttrs(P); S.pre := FALSE END END PRE; PROCEDURE Q(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN Texts.WriteString(P.W, "<<") ELSE Texts.WriteString(P.W, ">>") END END Q; 	PROCEDURE RANGE(VAR S: Scanner; on: BOOLEAN); BEGIN (* ignore *) END RANGE; PROCEDURE SAMP(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, mono) ELSE PopTextAttrs(P) END END SAMP; PROCEDURE SMALL(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN SetFontSize(P, GetFontSize(P)-2) ELSE PopTextAttrs(P) END END SMALL; PROCEDURE STRIKE(VAR S: Scanner; on: BOOLEAN); (* strikethrough text, how ? *) BEGIN (* ignore *) END STRIKE; PROCEDURE STRONG(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, ChangeFontAttr(P.W.lib(Fonts.Font), "b")) ELSE PopTextAttrs(P) END END STRONG; PROCEDURE STYLE(VAR S: Scanner; on: BOOLEAN); BEGIN (* ignore *) END STYLE; PROCEDURE SUB(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN SetFontSize(P, GetFontSize(P)-2); Texts.SetOffset(P.W, P.W.voff-4) ELSE PopTextAttrs(P) END END SUB; PROCEDURE SUP(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN SetFontSize(P, GetFontSize(P)-2); Texts.SetOffset(P.W, P.W.voff+4) ELSE PopTextAttrs(P) END END SUP; PROCEDURE TINY(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN SetFontSize(P, GetFontSize(P)-2) ELSE PopTextAttrs(P) END END TINY; PROCEDURE TITLE(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF P.head THEN IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, Syntax(12, 0X)); Texts.WriteString(P.W, "Title: ") ELSE WriteLn(P); PopTextAttrs(P); HorzRule(P, dispW, 2); Texts.Append(P.T, P.W.buf); P.headerLen := P.T.len END ELSIF ~on THEN PushTextAttrs(P); Texts.OpenWriter(P.W); PopTextAttrs(P) END (*S.skipWS := TRUE*) END TITLE; PROCEDURE TT(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, mono) ELSE PopTextAttrs(P) END END TT; PROCEDURE TAB(VAR S: Scanner; on: BOOLEAN); VAR P: Page; attr: ARRAY 64 OF CHAR; style: TextGadgets.Style; BEGIN P := S.page; IF on THEN Texts.Write(P.W, Strings.Tab); style := NIL; WHILE NextAttr(S, attr) DO 				IF attr = "ALIGN" THEN style := TextAlign(S.value) END END; IF style # NIL THEN PushTextAttrs(P); P.style := style; WriteObj(P, P.style) END ELSE PopTextAttrs(P) END; P.blank := FALSE (*S.skipWS := TRUE*) END TAB; PROCEDURE U(VAR S: Scanner; on: BOOLEAN); (* underline text, how ? *) BEGIN (* ignore *) END U; 	PROCEDURE UL(VAR S: Scanner; on: BOOLEAN); BEGIN IF on THEN OpenList(S.page, DefList) ELSE CloseList(S.page) END; (*S.skipWS := TRUE*) END UL; PROCEDURE VARN(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, ChangeFontAttr(P.W.lib(Fonts.Font), "i")) ELSE PopTextAttrs(P) END END VARN; PROCEDURE XMP(VAR S: Scanner; on: BOOLEAN); VAR P: Page; BEGIN P := S.page; IF on THEN PushTextAttrs(P); Texts.SetFont(P.W, mono); S.pre := TRUE ELSE PopTextAttrs(P); S.pre := FALSE END END XMP; PROCEDURE HandleTag(VAR S: Scanner); VAR Ss: Texts.Scanner; gen: ARRAY 64 OF CHAR; e: ExtTag; i: INTEGER; found, on: BOOLEAN; prev: CHAR; BEGIN WHILE ~S.end & (S.ch <= " ") DO 			Read(S) END; IF S.ch = "!" THEN prev := S.ch; Read(S); IF S.ch = "-" THEN prev := S.ch; Read(S); IF S.ch = "-" THEN WHILE ~S.end & ((S.ch # ">") OR ((prev # "-") & (prev # "!"))) DO 						IF S.ch > " " THEN prev := S.ch 						END; Read(S) END ELSE WHILE ~S.end & (S.ch # ">") DO 						Read(S) END END ELSE WHILE ~S.end & (S.ch # ">") DO 					Read(S) END END; Read(S) ELSIF ~Strings.IsAlpha(S.ch) & (S.ch # "/") THEN Texts.Write(S.page.W, "<") ELSE S.state := InTag; Next(S); IF S.class = OpenEndTag THEN on := FALSE; Next(S) ELSE on := TRUE END; IF S.class = Value THEN Strings.Upper(S.value, S.value); found := TRUE; CASE S.value[0] OF 					"A": IF S.value[1] = 0X THEN A(S, on) ELSIF (S.value = "ADDR") OR (S.value = "ADDRESS") THEN ADDRESS(S, on) ELSE found := FALSE END |"B": IF S.value[1] = 0X THEN B(S, on) ELSIF S.value = "BASE" THEN BASE(S, on) ELSIF S.value = "BASEFONT" THEN BASEFONT(S, on) ELSIF S.value = "BIG" THEN BIG(S, on) ELSIF S.value = "BLINK" THEN BLINK(S, on) ELSIF (S.value = "BQ") OR (S.value = "BLOCKQUOTE") THEN BLOCKQUOTE(S, on) ELSIF S.value = "BODY" THEN BODY(S, on) ELSIF S.value = "BR" THEN BR(S, on) ELSE found := FALSE END |"C": IF S.value = "CALL" THEN CALL(S, on) ELSIF S.value = "CENTER" THEN CENTER(S, on) ELSIF S.value = "CITE" THEN CITE(S, on) ELSIF S.value = "CODE" THEN CODEx(S, on) ELSE found := FALSE END |"D": IF S.value = "DD" THEN DD(S, on) ELSIF S.value = "DFN" THEN DFN(S, on) ELSIF S.value = "DIR" THEN DIR(S, on) ELSIF S.value = "DIV" THEN DIVI(S, on) ELSIF S.value = "DL" THEN DL(S, on) ELSIF S.value = "DT" THEN DT(S, on) ELSE found := FALSE END |"E": IF S.value = "EM" THEN EM(S, on) ELSE found := FALSE END |"F": IF S.value = "FRAME" THEN FRAME(S, on) ELSIF S.value = "FONT" THEN FONT(S, on) ELSE found := FALSE END |"G": IF S.value = "GREEN" THEN GREEN(S, on) ELSE found := FALSE END |"H": IF (S.value = "HEAD") OR (S.value = "HEADER") THEN HEAD(S, on) ELSIF S.value = "HP" THEN HP(S, on) ELSIF S.value = "HR" THEN HR(S, on) ELSIF S.value = "HTML" THEN HTML(S, on) ELSIF S.value = "HTTP" THEN HTTP(S, on) ELSIF Strings.IsDigit(S.value[1]) THEN H(S, on) ELSE found := FALSE END |"I": IF S.value[1] = 0X THEN I(S, on) ELSIF S.value = "ISINDEX" THEN ISINDEX(S, on) ELSE found := FALSE END |"K": IF S.value = "KBD" THEN KBD(S, on) ELSE found := FALSE END |"L": IF S.value = "LI" THEN LI(S, on) ELSIF S.value = "LINK" THEN LINK(S, on) ELSIF S.value = "LISTING" THEN LISTING(S, on) ELSE found := FALSE END |"M": IF S.value = "MENU" THEN MENU(S, on) ELSIF S.value = "META" THEN META(S, on) ELSE found := FALSE END |"N": IF S.value = "NEXTID" THEN NEXTID(S, on) ELSIF S.value = "NOFRAMES" THEN NOFRAMES(S, on) ELSE found := FALSE END |"O": IF S.value = "OL" THEN OL(S, on) ELSE found := FALSE END |"P": IF S.value[1] = 0X THEN P(S, on) ELSIF S.value = "PLAINTEXT" THEN PLAINTEXT(S, on) ELSIF S.value = "PRE" THEN PRE(S, on) ELSE found := FALSE END |"Q": IF S.value[1] = 0X THEN Q(S, on) ELSE found := FALSE END |"R": IF S.value = "RANGE" THEN RANGE(S, on) ELSE found := FALSE END |"S": IF S.value[1] = 0X THEN STRIKE(S, on) ELSIF S.value = "SAMP" THEN SAMP(S, on) ELSIF S.value = "STRIKE" THEN STRIKE(S, on) ELSIF S.value = "STRONG" THEN STRONG(S, on) ELSIF S.value = "STYLE" THEN STYLE(S, on) ELSIF S.value = "SMALL" THEN SMALL(S, on) ELSIF S.value = "SUB" THEN SUB(S, on) ELSIF S.value = "SUP" THEN SUP(S, on) ELSE found := FALSE END |"T": IF S.value = "TITLE" THEN TITLE(S, on) ELSIF S.value = "TT" THEN TT(S, on) ELSIF S.value = "TAB" THEN TAB(S, on) ELSIF S.value = "TINY" THEN TINY(S, on) ELSE found := FALSE END |"U": IF S.value[1] = 0X THEN U(S, on) ELSIF S.value = "UL" THEN UL(S, on) ELSE found := FALSE END |"V": IF S.value = "VAR" THEN VARN(S, on) ELSE found := FALSE END |"X": IF S.value = "XMP" THEN XMP(S, on) ELSE found := FALSE END ELSE found := FALSE END; IF ~found THEN e := extTags; WHILE (e # NIL) & (e.tag # S.value) DO 						e := e.next END; IF e = NIL THEN gen := "HTMLTags."; Strings.Append(gen, S.value); Oberon.OpenScanner(Ss, gen); IF Ss.class IN {Texts.Name, Texts.String} THEN COPY(Ss.s, gen); newTag := NIL; Oberon.Call(gen, Oberon.Par, FALSE, i); IF i # 0 THEN Texts.WriteString(Wr, gen); Texts.WriteLn(Wr); Texts.Append(Oberon.Log, Wr.buf) END; IF newTag # NIL THEN e := newTag; COPY(S.value, e.tag); e.next := extTags; extTags := e; 								e.start(S.page) END END END; IF e # NIL THEN e.handle(S, on); found := TRUE END END; WHILE ~S.end & (S.class # CloseTag) DO 					Next(S) END; IF ~found THEN ELSE END END END; S.state := TextHtml END HandleTag; PROCEDURE *DocHandler(D: Objects.Object; VAR M: Objects.ObjMsg); BEGIN WITH D: Documents.Document DO 			IF M IS Objects.AttrMsg THEN WITH M: Objects.AttrMsg DO 					IF (M.id = Objects.get) & (M.name = "Gen") THEN M.class := Objects.String; M.s := "HTMLDocs.NewDoc"; M.res := 0 ELSE TextDocs.DocHandler(D, M) 					END END ELSIF M IS Objects.LinkMsg THEN WITH M: Objects.LinkMsg DO 					IF M.id = Objects.get THEN IF M.name = "DeskMenu" THEN M.obj := Gadgets.CopyPublicObject("NetDocs.HTMLDeskMenu", TRUE); IF M.obj = NIL THEN M.obj := Desktops.NewMenu(Menu) END; M.res := 0 ELSIF M.name = "SystemMenu" THEN M.obj := Gadgets.CopyPublicObject("NetDocs.HTMLSystemMenu", TRUE); IF M.obj = NIL THEN M.obj := Desktops.NewMenu(Menu) END; M.res := 0 ELSIF M.name = "UserMenu" THEN M.obj := Gadgets.CopyPublicObject("NetDocs.HTMLUserMenu", TRUE); IF M.obj = NIL THEN M.obj := Desktops.NewMenu(Menu) END; M.res := 0 ELSE TextDocs.DocHandler(D, M) 						END ELSE TextDocs.DocHandler(D, M) 					END END ELSE TextDocs.DocHandler(D, M) 			END END END DocHandler; PROCEDURE TextColor(col: INTEGER): SHORTINT; VAR r, g, b: INTEGER; BEGIN Display.GetColor(col, r, g, b); IF (0.3*r+0.59*g+0.11*b) > 0.35 THEN RETURN SHORT(Display3.black) ELSE RETURN SHORT(Display3.white) END END TextColor; PROCEDURE EndPage(P: Page); VAR me: Oberon.Task; e: ExtTag; p: Page; BEGIN me := P.task; CloseA(P); e := extTags; WHILE e # NIL DO 			e.stop(P); e := e.next END; Texts.Append(P.T, P.W.buf); WITH me: Task DO 			IF me.S.S # NIL THEN me.S.S.Close(me.S.S); me.S.S := NIL END END; P.D.handle := DocHandler; Attributes.SetString(P.D, "Type", "HTML"); Attributes.SetInt(P.D.dsc, "Color", P.textbackC); IF P.textbackC # Display3.textbackC THEN Texts.ChangeLooks(P.T, 0, P.headerLen, {1}, NIL, TextColor(P.textbackC), 0) END; IF (P.orgPos > 0) & (P.D.dsc(TextGadgets.Frame).org = 0) THEN HyperDocs.ScrollTo(P.D.dsc(TextGadgets.Frame), P.orgPos); IF HTTPDocs0.curNode # NIL THEN HyperDocs.RememberOrg(P.orgPos, HTTPDocs0.curNode, HTTPDocs0.curNode) END END; Texts.Append(P.source, P.Ws.buf); IF P.cacheSource THEN HyperDocs.CacheText(HTTPDocs0.StripLoc(P.docKey), P.source) END; Oberon.Remove(me); p := pages; WHILE (p # NIL) & (p.next # P) DO 			p := p.next END; IF p # NIL THEN p.next := P.next ELSE pages := P.next END; P.task := NIL; Oberon.Collect END EndPage; PROCEDURE *ParseNext(me: Oberon.Task); VAR P: Page; St: Streams.Stream; i, minSize, timeOut: LONGINT; app: BOOLEAN; BEGIN WITH me: Task DO 			P := me.S.page; IF me.S.state # End THEN i := Input.Time; timeOut := i + Input.TimeUnit; me.time := i; 				St := me.S.S; app := FALSE; me.S.avail := St.Available(St); minSize := (* 64 *)1 ;	 (* workaround for hang problem after NetSystem.State change *) IF (me.S.avail < minSize) & (me.S.avail > 0) THEN IF St.State(St) = Streams.closed THEN minSize := me.S.avail ELSIF ~St.buffer THEN minSize := 1 END END; WHILE (me.S.avail >= minSize) & (me.time < timeOut) DO 					app := TRUE; CASE me.S.class OF 						WhiteSpace: WriteSpace(P) |OpenTag: HandleTag(me.S) 						|CharRef: WriteCharRef(P, me.S); P.lines := 0; P.blank := TRUE |Character: Texts.Write(P.W, me.S.char); P.lines := 0; P.blank := TRUE ELSE (* ??? *) 					END; Next(me.S); IF me.S.avail <= minSize THEN me.S.avail := St.Available(St); me.time := Input.Time END; IF (me.S.avail < minSize) & (me.S.avail > 0) THEN IF St.State(St) = Streams.closed THEN minSize := me.S.avail ELSIF ~St.buffer THEN minSize := 1 END END END; me.time := Input.Time; i := 0; WHILE St.eos & (me.S.state # End) & (i < 5) DO (* :-) *) 					app := TRUE; 					CASE me.S.class OF 						WhiteSpace: WriteSpace(P) 						|OpenTag: HandleTag(me.S) 						|CharRef: WriteCharRef(P, me.S); P.lines := 0; P.blank := TRUE 						|Character: Texts.Write(P.W, me.S.char); P.lines := 0; P.blank := TRUE 					ELSE 					(* ??? *) 					END; 					Next(me.S); INC(i) 				END; 				me.S.avail := 0; 				IF app THEN 					Texts.Append(P.T, P.W.buf); INC(me.time, Input.TimeUnit DIV 5) 				ELSE 					INC(me.time, Input.TimeUnit) 				END 			END; 			IF me.S.state = End THEN 				EndPage(P) 			END 		END 	END ParseNext; (* Query a string setting in the NetSystem section in Registry. *) 	PROCEDURE QueryString(key: ARRAY OF CHAR; VAR s: ARRAY OF CHAR): BOOLEAN; 	VAR lKey: ARRAY 32 OF CHAR; S: Texts.Scanner; 	BEGIN 		lKey := "NetSystem."; Strings.Append(lKey, key); 		Oberon.OpenScanner(S, lKey); 		IF S.class IN {Texts.Name, Texts.String} THEN 			COPY(S.s, s) 		ELSE 			COPY("", s) 		END; 		RETURN s # "" 	END QueryString; 	PROCEDURE QueryBool(key: ARRAY OF CHAR): BOOLEAN; 		VAR 			str: ARRAY 16 OF CHAR; 			b: BOOLEAN; 	BEGIN 		IF QueryString(key, str) THEN 			Strings.StrToBool(str, b); 			RETURN b 		ELSE 			RETURN FALSE 		END 	END QueryBool; 	PROCEDURE Parse*(D: Documents.Document; basekey: LONGINT; S: Streams.Stream; head, cache, blocking: BOOLEAN); 		VAR 			me: Task; 			s: HyperDocs.LinkScheme; 			e: ExtTag; 			i, j: LONGINT; 	BEGIN 		imgs := QueryBool("HTMLImages"); 		NEW(me); 		IF (D.handle = NIL) OR (D.Store = NIL) THEN 			TextDocs.InitDoc(D); D.W := HyperDocs.docW; D.H := HyperDocs.docH END; D.dsc(TextGadgets.Frame).do := HyperDocs.linkMethods; NEW(me.P); me.P.D := D; NEW(me.P.T); Texts.Open(me.P.T, ""); me.P.alink := NIL; me.P.clink := NIL; me.P.cacheSource := cache; NEW(me.P.source); Texts.Open(me.P.source, ""); Texts.OpenWriter(me.P.Ws); me.P.next := pages; pages := me.P; me.P.task := me; me.P.docKey := basekey; me.P.handle := Gadgets.objecthandle; me.P.blank := FALSE; Links.SetLink(D.dsc, "Model", me.P.T); me.P.lines := 0; me.P.linkkey := HyperDocs.UndefKey; me.P.textAttrs := NIL; Texts.OpenWriter(me.P.W); me.P.left := FALSE; me.P.linkC := HyperDocs.linkC; me.P.oldLinkC := HyperDocs.oldLinkC; me.P.textC := Display3.textC; me.P.textbackC := Display3.textbackC; Attributes.SetInt(me.P.D.dsc, "LinkColor", me.P.linkC); Attributes.SetInt(me.P.D.dsc, "OldLinkColor", me.P.oldLinkC); me.S.page := me.P; me.P.lists := NIL; NEW(me.P.base); me.P.base.key := basekey; me.P.base.dockey := me.P.base.key; me.P.style := NewStyle; WriteObj(me.P, me.P.style); PopTextAttrs(me.P); me.P.orgPos := 0; me.P.head := head; OpenScanner(me.S, S); IF head THEN Texts.WriteString(me.P.W, "URL: "); Texts.Write(me.P.W, 22X) END; s := HyperDocs.LinkSchemeByKey(me.P.base.key); IF s = NIL THEN s := HyperDocs.LinkSchemeByPrefix("file") END; COPY(s.prefix, me.P.base.prefix); HyperDocs.RetrieveLink(me.P.base.key, me.S.value); IF me.P.base.prefix = "http" THEN me.P.base.key := HTTPDocs0.SplitHTTPAdr(me.S.value, me.P.base.host, me.P.base.path, me.P.orgLabel, me.P.base.port); i := 0; WHILE me.S.value[i] # 0X DO 				INC(i) END; WHILE (i > 0) & (me.S.value[i] # "/") DO 				DEC(i) END; IF me.S.value[i] = "/" THEN me.S.value[i+1] := 0X END ELSIF me.P.base.prefix = "file" THEN HyperDocs.RetrieveLink(HyperDocs.loadingKey, me.S.value); i := HyperDocs.SplitFileAdr(me.S.value, me.P.base.path, me.P.orgLabel); HyperDocs.RetrieveLink(me.P.base.key, me.S.value); me.P.base.key := HyperDocs.SplitFileAdr(me.S.value, me.P.base.path, searchAttr); me.P.base.host := ""; me.P.base.port := 0; i := 0; j := 0; WHILE me.P.base.path[i] # 0X DO 				IF me.P.base.path[i] = "/" THEN j := i+1 END; INC(i) END; me.P.base.path[j] := 0X ELSE (* proxy *) me.P.orgLabel := ""; SplitHostPort(me.S.value, me.P.base.host, me.P.base.port); IF me.P.base.prefix = "ftp" THEN i := 6; WHILE (me.S.value[i] # 0X) & (me.S.value[i] # "/") DO 					INC(i) END; j := 0; WHILE me.S.value[i] # 0X DO 					me.P.base.path[j] := me.S.value[i]; INC(j); INC(i) END; IF j > 0 THEN me.P.base.path[j] := 0X ELSE me.P.base.path := "/" END ELSE me.P.base.path := "" END END; HyperDocs.RetrieveLink(me.P.base.key, me.S.value); IF head THEN Texts.WriteString(me.P.W, me.S.value); Texts.Write(me.P.W, 22X); WriteLn(me.P) 		END; e := extTags; WHILE e # NIL DO 			e.start(me.P); e := e.next END; me.P.D.handle := DocHandler; Attributes.SetString(me.P.D, "Type", "HTML"); Attributes.SetInt(me.P.D.dsc, "Color", me.P.textbackC); Texts.Append(me.P.T, me.P.W.buf); Read(me.S); Next(me.S); me.P.headerLen := me.P.T.len; me.time := Oberon.Time; me.safe := FALSE; me.handle := ParseNext; Oberon.Install(me); IF blocking THEN WHILE me.P.task = me DO 				me.handle(me) END END END Parse; PROCEDURE Show*; VAR T: Texts.Text; D: Objects.Object; BEGIN T := Oberon.MarkedText; IF T # NIL THEN HTTPDocs0.curNode := NIL; D := Gadgets.CreateObject("TextDocs.NewDoc"); WITH D: Documents.Document DO 				D.W := HyperDocs.docW; D.H := HyperDocs.docH; D.handle := DocHandler; Parse(D, HyperDocs.UndefKey, TextStreams.OpenReader(T, 0), TRUE, FALSE, TRUE); Desktops.ShowDoc(D) END END END Show; (** HTMLDocs.Stop * 	Stop all background task for the marked html document. *) PROCEDURE Stop*; VAR S: Attributes.Scanner; doc: Documents.Document; P, nextP: Page; BEGIN Attributes.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Attributes.Scan(S); IF (S.class = Attributes.Char) & (S.c = "*") THEN doc := Desktops.CurDoc(Gadgets.context) ELSE doc := NIL; P := NIL END; IF doc # NIL THEN HTTPDocs0.StopDoc(doc); P := pages; WHILE (P # NIL) & (P.D # doc) DO 				P := P.next END; IF P # NIL THEN HTTPDocs0.StopDoc(P.D); EndPage(P) END ELSE P := pages; WHILE P # NIL DO 				nextP := P.next; HTTPDocs0.StopDoc(P.D); EndPage(P); P := nextP END END END Stop; PROCEDURE *LoadDoc(D: Documents.Document); VAR key: LONGINT; s, so: HyperDocs.LinkScheme; T: Texts.Text; path, label: ARRAY 64 OF CHAR; BEGIN key := HyperDocs.BuildKey(NIL, D.name); IF key # HyperDocs.UndefKey THEN s := HyperDocs.LinkSchemeByKey(key); IF s.prefix = "file" THEN IF HyperDocs.context # NIL THEN HTTPDocs0.curNode := HyperDocs.context.new ELSE HyperDocs.Remember(key, NIL, HTTPDocs0.curNode) END; key := HyperDocs.SplitFileAdr(D.name, path, label); NEW(T); Texts.Open(T, path); TextDocs.InitDoc(D); D.W := HyperDocs.docW; D.H := HyperDocs.docH; Parse(D, key, TextStreams.OpenReader(T, 0), TRUE, FALSE, FALSE); IF (D # NIL) & (D.dsc # NIL) THEN IF HyperDocs.context = NIL THEN HyperDocs.LinkNodeToDoc(D, HTTPDocs0.curNode) ELSE HyperDocs.context.history := TRUE END; IF D.name = "" THEN HyperDocs.RetrieveLink(key, D.name) END END ELSE HALT(99) END; IF (HyperDocs.context # NIL) & (HyperDocs.context.old # NIL) THEN so := HyperDocs.LinkSchemeByKey(HyperDocs.context.old.key); IF (so.prefix = "file") OR (so.prefix = "http") THEN HyperDocs.context.replace := TRUE END END END END LoadDoc; PROCEDURE NewDoc*; VAR doc: Objects.Object; BEGIN doc := Gadgets.CreateObject("TextDocs.NewDoc"); WITH doc: Documents.Document DO 			doc.W := HyperDocs.docW; doc.H := HyperDocs.docH; doc.Load := LoadDoc END; doc.handle := DocHandler; Objects.NewObj := doc END NewDoc; PROCEDURE InitEntities; BEGIN entityEncoding := "&AElig;&Aacute;&Acirc;&Agrave;&Aring;&Atilde;&Auml;&Ccedil;&ETH;&Eacute;&Ecirc;&Egrave&Euml;&Iacute;&Icirc;&Igrave;&Iuml;&Ntilde;&Oacute;&Ocirc;&Ograve;&Oslash;&Otilde;&Ouml;&THORN;&Uacute;&Ucirc;&Ugrave;&Uuml;&Yacute;&aacute;&acirc;&aelig;&agrave;&amp;&aring;&atilde;&auml;&ccedil;&eacute;&ecirc;&egrave;&eth;&euml;&gt;&iacute;&icirc;&igrave;&iuml;&lt;&ntilde;&oacute;&ocirc;&ograve;&oslash;&otilde;&ouml;&quot;&szlig;&thorn;&uacute;&ucirc;&ugrave;&uuml;&yacute;&yuml;"; entityEncoding[57] := 22X; entityEncoding[66] := 22X; entityEncoding[67] := Strings.Tab; entityEncoding[68] := " "; entities[0] := "AElig"; entities[1] := "Aacute"; entities[2] := "Acirc"; entities[3] := "Agrave"; entities[4] := "Aring"; entities[5] := "Atilde"; entities[6] := "Auml"; entities[7] := "Ccedil"; entities[8] := "ETH"; entities[9] := "Eacute"; entities[10] := "Ecirc"; entities[11] := "Egrave"; entities[12] := "Euml"; entities[13] := "Iacute"; entities[14] := "Icirc"; entities[15] := "Igrave"; entities[16] := "Iuml"; entities[17] := "Ntilde"; entities[18] := "Oacute"; entities[19] := "Ocirc"; entities[20] := "Ograve"; entities[21] := "Oslash"; entities[22] := "Otilde"; entities[23] := "Ouml"; entities[24] := "THORN"; entities[25] := "Uacute"; entities[26] := "Ucirc"; entities[27] := "Ugrave"; entities[28] := "Uuml"; entities[29] := "Yacute"; entities[30] := "aacute"; entities[31] := "acirc"; entities[32] := "aelig"; entities[33] := "agrave"; entities[34] := "amp"; entities[35] := "aring"; entities[36] := "atilde"; entities[37] := "auml"; entities[38] := "ccedil"; entities[39] := "eacute"; entities[40] := "ecirc"; entities[41] := "egrave"; entities[42] := "eth"; entities[43] := "euml"; entities[44] := "gt"; entities[45] := "iacute"; entities[46] := "icirc"; entities[47] := "igrave"; entities[48] := "iuml"; entities[49] := "lt"; entities[50] := "ntilde"; entities[51] := "oacute"; entities[52] := "ocirc"; entities[53] := "ograve"; entities[54] := "oslash"; entities[55] := "otilde"; entities[56] := "ouml"; entities[57] := "quot"; entities[58] := "szlig"; entities[59] := "thorn"; entities[60] := "uacute"; entities[61] := "ucirc"; entities[62] := "ugrave"; entities[63] := "uuml"; entities[64] := "yacute"; entities[65] := "yuml"; entities[66] := "quote"; entities[67] := "tab"; entities[68] := "nbsp" END InitEntities; (** HTMLDocs.ShowHTML 		Display the HTML-source of the marked page. *) PROCEDURE ShowHTML*; VAR D: Documents.Document; node: HyperDocs.Node; P: Page; key, pos: LONGINT; T: Texts.Text; R: Texts.Reader; ch: CHAR; BEGIN D := Documents.MarkedDoc; node := HyperDocs.NodeByDoc(D); IF node # NIL THEN Attributes.GetInt(D, "DocURL", key); P := pages; WHILE (P # NIL) & (P.docKey # key) DO 			P := P.next END; IF P # NIL THEN Texts.Append(P.source, P.Ws.buf); T := P.source ELSE key := HTTPDocs0.StripLoc(key); T := HyperDocs.GetCachedText(key) END; IF T # NIL THEN Texts.OpenReader(R, T, 0); Texts.Read(R, ch); pos := 0; WHILE ~R.eot DO 				IF ch = Strings.LF THEN Texts.WriteLn(Wr); Texts.Delete(T, pos, pos+1); Texts.Insert(T, pos, Wr.buf); Texts.OpenReader(R, T, pos) ELSE INC(pos) END; Texts.Read(R, ch) END; TextDocs.ShowText("HTML", T, HyperDocs.docW, HyperDocs.docH) END END END ShowHTML; (** HTMLDocs.SetImages 	Switch image loading on or off. *) PROCEDURE SetImages*; VAR S: Attributes.Scanner; BEGIN Attributes.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Attributes.Scan(S); IF (S.class = Attributes.Name) OR (S.class = Attributes.String) THEN Strings.StrToBool(S.s, imgs) END; END SetImages; PROCEDURE Check; VAR S: Texts.Scanner; BEGIN Oberon.OpenScanner(S, "HTMLTags"); IF S.class = Texts.Inval THEN Texts.WriteString(Wr, "Oberon.Text - HTMLTags not found"); Texts.WriteLn(Wr); Texts.Append(Oberon.Log, Wr.buf) END END Check; BEGIN COPY(GreekCap, GreekTab); Strings.Append(GreekTab, GreekMin); InitEntities; mono := Fonts.This("Courier10.Scn.Fnt"); IF ASCIIBullets THEN bullets[0].f := Fonts.This("Default12.Scn.Fnt"); bullets[0].c := "*"; bullets[1].f := bullets[0].f; bullets[1].c := "o" ELSE bullets[0].f := Fonts.This("Default10.Scn.Fnt"); bullets[0].c := CHR(29); bullets[1].f := bullets[0].f; bullets[1].c := CHR(28) END; Texts.OpenWriter(Wr); Texts.OpenWriter(Wq); imgs := TRUE; extTags := NIL; newTag := NIL; pages := NIL; dispW := Display.Width; Check END HTMLDocs.