Oberon/A2/Oberon.HTMLDocs.Mod

(* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.

Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)

MODULE HTMLDocs IN Oberon; (** portable *) (* ejz, Greek &#38; Math support by afi *)

&#160;&#160;&#160;&#160;(* Check that the index of ISOToOberon does not exceed 255. ple, 2005-05-08. *)


 * IMPORT Objects, Input, Strings, Display, Display3, Fonts, HyperDocs, Texts, Gadgets, Documents, HTTPDocs0, TextDocs,


 * Oberon, TextGadgets, Lists, Attributes, Desktops, Links, Streams, TextStreams;


 * CONST


 * (* class *)


 * WhiteSpace* &#61; 0; OpenTag* &#61; 1; OpenEndTag* &#61; 2; CloseTag* &#61; 3; CharRef* &#61; 4; Character* &#61; 5; Value* &#61; 6; Undef* &#61; 7;


 * (* state *)


 * TextPlain* &#61; 1; TextHtml* &#61; 2; InTag* &#61; 3; End* &#61; 4;


 * (* list types *)


 * DefList &#61; 0; DescList &#61; 1; OrderedList &#61; 2;


 * Menu &#61; "Desktops.Copy&#91;Copy&#93; HyperDocs.Back&#91;Back&#93; HyperDocs.Reload&#91;Reload&#93; TextDocs.Search&#91;Search&#93; Desktops.StoreDoc&#91;Store&#93;";


 * GreekCap &#61; "0013143516173415192134222324252728202931323337372636180000000000";


 * GreekMin &#61; "004546674849664751536654555657596052616364656969586850";


 * ASCIIBullets &#61; TRUE;


 * TYPE


 * Integer &#61; LONGINT;


 * DocURL* &#61; POINTER TO DocURLDesc;


 * DocURLDesc* &#61; RECORD (HyperDocs.DefURLDesc)


 * dockey*: LONGINT


 * END;


 * TextAttrs &#61; POINTER TO TextAttrsDesc;


 * TextAttrsDesc &#61; RECORD


 * lib: Objects.Library;


 * style: TextGadgets.Style;


 * col, voff: SHORTINT;


 * next: TextAttrs


 * END;


 * Page* &#61; POINTER TO PageDesc;


 * Scanner* &#61; 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 &#61; POINTER TO ListDesc;


 * ListDesc &#61; RECORD


 * style: TextGadgets.Style;


 * kind, nesting, itemNr: INTEGER;


 * dtok: BOOLEAN;


 * next: List


 * END;


 * Form* &#61; POINTER TO FormDesc;


 * FormDesc* &#61; RECORD (TextGadgets.ControlDesc)


 * elems*: HTTPDocs0.ObjList


 * END;


 * PageDesc* &#61; 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* &#61; POINTER TO ItemDesc;


 * ItemDesc* &#61; RECORD (Lists.ItemDesc)


 * value*: ARRAY 64 OF CHAR;


 * oldSel*, hasVal*: BOOLEAN


 * END;


 * TagHandler* &#61; PROCEDURE (VAR S: Scanner; on: BOOLEAN);


 * ExtTag* &#61; POINTER TO ExtTagDesc;


 * ExtTagDesc* &#61; RECORD


 * tag: ARRAY 32 OF CHAR;


 * handle*: TagHandler;


 * start*, stop*: PROCEDURE (P: Page);


 * next: ExtTag


 * END;


 * TagAttr* &#61; POINTER TO TagAttrDesc;


 * TagAttrDesc* &#61; RECORD


 * name: ARRAY 64 OF CHAR;


 * value*: ARRAY 512 OF CHAR;


 * next: TagAttr


 * END;


 * Task &#61; POINTER TO TaskDesc;


 * TaskDesc &#61; 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 &#60; 3 THEN


 * Texts.WriteLn(P.W); P.blank :&#61; FALSE


 * END


 * END WriteLn;


 * PROCEDURE WriteSpace*(P: Page);


 * BEGIN


 * IF P.blank THEN


 * Texts.Write(P.W, " "); P.blank :&#61; FALSE


 * END


 * END WriteSpace;


 * PROCEDURE WriteObj*(P: Page; obj: Objects.Object);


 * BEGIN


 * Texts.WriteObj(P.W, obj);


 * IF (obj IS Display.Frame) &#38; (obj(Display.Frame).W &#62;&#61; (HyperDocs.docW-16)) THEN


 * INC(P.lines); P.blank :&#61; FALSE


 * ELSIF obj IS TextGadgets.Style THEN


 * P.blank :&#61; FALSE


 * ELSIF &#126;(obj IS TextGadgets.Control) THEN


 * P.lines :&#61; 0; P.blank :&#61; TRUE


 * END


 * END WriteObj;


 * PROCEDURE InitTabs(style: TextGadgets.Style);


 * VAR i: LONGINT;


 * BEGIN


 * style.W :&#61; ((dispW DIV 8) * 5)-24; style.width :&#61; style.W;


 * style.noTabs :&#61; 32;


 * FOR i :&#61; 0 TO 31 DO


 * style.tab&#91;i&#93; :&#61; SHORT(32+i*32)


 * END


 * END InitTabs;


 * PROCEDURE NewStyle: TextGadgets.Style;


 * VAR style: TextGadgets.Style;


 * BEGIN


 * style :&#61; 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 :&#61; "Default";


 * IF dispW &#60; 800 THEN DEC(size, 2) END;


 * IF size &#60;&#61; 8 THEN size :&#61; 8


 * ELSIF size &#60;&#61; 10 THEN size :&#61; 10


 * ELSIF size &#60;&#61; 12 THEN size :&#61; 12


 * ELSIF size &#60;&#61; 14 THEN size :&#61; 14


 * ELSIF size &#60;&#61; 16 THEN size :&#61; 16


 * ELSIF size &#60;&#61; 20 THEN size :&#61; 20


 * ELSE size :&#61; 24


 * END;


 * Strings.IntToStr(size, sizeS);


 * IF attr &#61; 0X THEN (* skip *)


 * ELSIF attr &#61; "i" THEN (* skip *)


 * ELSIF attr &#61; "m" THEN attr :&#61; "b"


 * ELSIF attr &#61; "b" THEN (* skip *)


 * ELSE attr :&#61; 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 :&#61; P.W.lib; attr.col :&#61; P.W.col; attr.voff :&#61; P.W.voff;


 * attr.style :&#61; P.style;


 * attr.next :&#61; P.textAttrs; P.textAttrs :&#61; 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 :&#61; P.textAttrs.style;


 * IF (P.style.mode # style.mode) OR (P.style.leftM # style.leftM) OR (P.style.width # style.width) THEN


 * M.id :&#61; Objects.shallow; Objects.Stamp(M); M.obj :&#61; NIL; M.dlink :&#61; NIL;


 * style.handle(style, M); style :&#61; M.obj(TextGadgets.Style);


 * P.style :&#61; style; WriteObj(P, style)


 * END;


 * P.textAttrs :&#61; P.textAttrs.next


 * ELSE


 * Texts.SetFont(P.W, Syntax(12, 0X));


 * Texts.SetColor(P.W, SHORT(P.textC));


 * Texts.SetOffset(P.W, 0);


 * style :&#61; NewStyle;


 * IF (P.style.mode # style.mode) OR (P.style.leftM # style.leftM) OR (P.style.width # style.width) THEN


 * P.style :&#61; 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 :&#61; 0;


 * WHILE (f.name&#91;i&#93; # 0X) &#38; &#126;Strings.IsDigit(f.name&#91;i&#93;) DO


 * INC(i)


 * END;


 * COPY(f.name, family); family&#91;i&#93; :&#61; 0X;


 * Strings.StrToIntPos(f.name, val, i); size :&#61; SHORT(val);


 * IF Strings.IsAlpha(f.name&#91;i&#93;) THEN


 * attr :&#61; f.name&#91;i&#93;


 * ELSE


 * attr :&#61; 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 &#60; 800 THEN


 * INC(size, 2)


 * END;


 * IF size &#60;&#61; 8 THEN


 * size :&#61; 1


 * ELSIF size &#60;&#61; 10 THEN


 * size :&#61; 2


 * ELSIF size &#60;&#61; 12 THEN


 * size :&#61; 3


 * ELSIF size &#60;&#61; 14 THEN


 * size :&#61; 4


 * ELSIF size &#60;&#61; 16 THEN


 * size :&#61; 5


 * ELSIF size &#60;&#61; 20 THEN


 * size :&#61; 6


 * ELSE


 * size :&#61; 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 &#61; "m" THEN attr :&#61; "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 &#60; 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 :&#61; P.W.lib(Fonts.Font);


 * IF size &#60; 1 THEN


 * size :&#61; 1


 * ELSIF size &#62; 9 THEN


 * size :&#61; 9


 * END;


 * PushTextAttrs(P);


 * CASE size OF


 * 1: Texts.SetFont(P.W, ChangeFontSize(fnt, 8))


 * &#124;2: Texts.SetFont(P.W, ChangeFontSize(fnt, 10))


 * &#124;3: Texts.SetFont(P.W, ChangeFontSize(fnt, 12))


 * &#124;4: Texts.SetFont(P.W, ChangeFontSize(fnt, 14))


 * &#124;5: Texts.SetFont(P.W, ChangeFontSize(fnt, 16))


 * &#124;6: Texts.SetFont(P.W, ChangeFontSize(fnt, 20))


 * &#124;7: Texts.SetFont(P.W, ChangeFontSize(fnt, 24))


 * &#124;8: fnt :&#61; ChangeFontSize(fnt, 24);


 * Texts.SetFont(P.W, ChangeFontAttr(fnt, "m"))


 * &#124;9: fnt :&#61; 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 &#60;&#61; 0 THEN


 * WriteLn(P)


 * END;


 * h :&#61; 2*h;


 * IF h &#60; 4 THEN


 * h :&#61; 4


 * END;


 * obj :&#61; 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 :&#61; 1; WriteLn(P)


 * END HorzRule;


 * PROCEDURE TextAlign*(CONST align: ARRAY OF CHAR): TextGadgets.Style;


 * VAR style: TextGadgets.Style;


 * BEGIN


 * style :&#61; NewStyle; EXCL(style.mode, TextGadgets.left);


 * CASE CAP(align&#91;0&#93;) OF


 * &#124;"L": INCL(style.mode, TextGadgets.left)


 * &#124;"R": INCL(style.mode, TextGadgets.right)


 * &#124;"C", "J": INCL(style.mode, TextGadgets.middle)


 * ELSE (* BLEED... *)


 * CASE CAP(align&#91;5&#93;) OF


 * "L": INCL(style.mode, TextGadgets.left)


 * &#124;"R": INCL(style.mode, TextGadgets.right)


 * &#124;"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 :&#61; NIL;


 * WHILE (P.W.col &#61; SHORT(P.linkC)) OR (P.W.col &#61; SHORT(P.oldLinkC)) DO


 * PopTextAttrs(P);


 * IF P.textAttrs &#61; NIL THEN RETURN END


 * END


 * END


 * END CloseA;


 * PROCEDURE OpenList(P: Page; kind: INTEGER);


 * VAR list: List;


 * BEGIN


 * PushTextAttrs(P);


 * NEW(list); list.itemNr :&#61; 0;


 * list.style :&#61; NewStyle;


 * IF P.lists &#61; NIL THEN


 * list.nesting :&#61; 0;


 * WriteLn(P); WriteLn(P)


 * ELSE


 * list.nesting :&#61; P.lists.nesting+1


 * END;


 * P.style :&#61; list.style; WriteObj(P, list.style);


 * list.dtok :&#61; FALSE; list.kind :&#61; kind;


 * list.next :&#61; P.lists; P.lists :&#61; list


 * END OpenList;


 * PROCEDURE CloseList(P: Page);


 * BEGIN


 * IF P.lists # NIL THEN


 * IF P.lists.itemNr &#61; 0 THEN


 * P.lists.style.leftM :&#61; 32


 * END;


 * P.lists :&#61; P.lists.next


 * END;


 * PopTextAttrs(P);


 * IF P.lists &#61; NIL THEN


 * WriteLn(P); WriteLn(P)


 * END


 * END CloseList;


 * PROCEDURE FindFormObj*(form: Form; CONST name: ARRAY OF CHAR): Objects.Object;


 * VAR


 * ol: HTTPDocs0.ObjList;


 * oname: ARRAY 64 OF CHAR;


 * obj: Objects.Object;


 * BEGIN


 * obj :&#61; NIL;


 * ol :&#61; form.elems;


 * WHILE ol # NIL DO


 * Attributes.GetString(ol.obj, "Name", oname);


 * IF oname &#61; name THEN


 * obj :&#61; ol.obj


 * END;


 * ol :&#61; ol.next


 * END;


 * RETURN obj


 * END FindFormObj;


 * PROCEDURE RememberValue*(obj: Objects.Object);


 * VAR A: Objects.AttrMsg;


 * BEGIN


 * A.id :&#61; Objects.get; A.name :&#61; "Value"; A.class :&#61; Objects.Inval;


 * obj.handle(obj, A);


 * A.id :&#61; Objects.set; A.name :&#61; "IniValue";


 * obj.handle(obj, A)


 * END RememberValue;


 * PROCEDURE AddFormObj*(P: Page; form: Form; obj: Objects.Object; CONST name: ARRAY OF CHAR; storeVal, write: BOOLEAN);


 * VAR ol, op: HTTPDocs0.ObjList;


 * BEGIN


 * NEW(ol); ol.obj :&#61; obj; ol.next :&#61; NIL;


 * op :&#61; form.elems;


 * WHILE (op # NIL) &#38; (op.next # NIL) DO


 * op :&#61; op.next


 * END;


 * IF op # NIL THEN


 * op.next :&#61; ol


 * ELSE


 * form.elems :&#61; 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(CONST name: ARRAY OF CHAR);


 * BEGIN


 * IF name &#61; searchAttr THEN


 * found :&#61; TRUE


 * END


 * END FindA;


 * PROCEDURE HasA(obj: Objects.Object; CONST name : ARRAY OF CHAR): BOOLEAN;


 * VAR A: Objects.AttrMsg;


 * BEGIN


 * found :&#61; FALSE; COPY(name, searchAttr);


 * A.id :&#61; Objects.enum; COPY(name, A.name); A.res :&#61; -1;


 * A.Enum :&#61; FindA;


 * obj.handle(obj, A);


 * RETURN found


 * END HasA;


 * PROCEDURE ResetValues(form: Form);


 * VAR


 * A: Objects.AttrMsg;


 * ol: HTTPDocs0.ObjList;


 * item: Item;


 * BEGIN


 * ol :&#61; form.elems;


 * WHILE ol # NIL DO


 * IF HasA(ol.obj, "IniValue") THEN


 * A.id :&#61; Objects.get; A.name :&#61; "IniValue"; A.class :&#61; Objects.Inval;


 * ol.obj.handle(ol.obj, A);


 * A.id :&#61; Objects.set; A.name :&#61; "Value";


 * ol.obj.handle(ol.obj, A);


 * Gadgets.Update(ol.obj)


 * ELSIF ol.obj IS Lists.List THEN


 * item :&#61; ol.obj(Lists.List).items(Item);


 * WHILE item # NIL DO


 * item.sel :&#61; item.oldSel;


 * IF item.next # NIL THEN


 * item :&#61; item.next(Item)


 * ELSE


 * item :&#61; NIL


 * END


 * END;


 * Gadgets.Update(ol.obj)


 * END;


 * ol :&#61; 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) &#38; (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 :&#61; Desktops.CurDoc(Gadgets.context);


 * node :&#61; HyperDocs.NodeByDoc(curDoc);


 * text :&#61; GetText(Gadgets.context);


 * IF (curDoc # NIL) &#38; (text # NIL) THEN


 * Attributes.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos);


 * Attributes.Scan(S);


 * IF (S.class &#61; Attributes.String) OR (S.class &#61; Attributes.Name) THEN


 * Texts.OpenFinder(F, text, 0);


 * pos :&#61; F.pos; Texts.FindObj(F, obj);


 * WHILE &#126;F.eot DO


 * IF obj IS Gadgets.Frame THEN


 * Attributes.GetString(obj, "Name", name);


 * IF S.s &#61; 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 :&#61; 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 :&#61; Gadgets.executorObj;


 * text :&#61; GetText(Gadgets.context);


 * IF (text # NIL) &#38; (exec # NIL) THEN


 * Texts.OpenFinder(F, text, 0);


 * Texts.FindObj(F, obj);


 * WHILE &#126;F.eot DO


 * IF obj &#61; exec THEN


 * exec :&#61; NIL


 * ELSIF (exec &#61; NIL) &#38; (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 :&#61; Gadgets.executorObj; text :&#61; GetText(context);


 * IF text # NIL THEN


 * Texts.OpenFinder(F, text, 0);


 * Texts.FindObj(F, obj);


 * WHILE &#126;F.eot DO


 * IF obj IS Form THEN


 * ol :&#61; obj(Form).elems;


 * WHILE (ol # NIL) &#38; (ol.obj # exec) DO


 * ol :&#61; 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 :&#61; 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 &#61; Objects.get) &#38; (M.name &#61; "Gen") THEN


 * M.class :&#61; Objects.String;


 * M.s :&#61; "HTMLDocs.NewForm";


 * M.res :&#61; 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 &#61; Objects.load THEN


 * HyperDocs.LoadLink(M.R, key);


 * action :&#61; Gadgets.CreateObject("BasicGadgets.NewInteger");


 * Attributes.SetInt(action, "Value", key);


 * Gadgets.NameObj(action, "@ACTION");


 * NEW(F.elems);


 * F.elems.obj :&#61; action;


 * F.elems.next :&#61; NIL;


 * Gadgets.ReadRef(M.R, F.lib, obj);


 * WHILE obj # NIL DO


 * NEW(ol);


 * ol.next :&#61; F.elems;


 * F.elems :&#61; ol;


 * ol.obj :&#61; obj;


 * Gadgets.ReadRef(M.R, F.lib, obj)


 * END


 * ELSIF M.id &#61; Objects.store THEN


 * action :&#61; FindFormObj(F, "@ACTION");


 * Attributes.GetInt(action, "Value", key);


 * HyperDocs.StoreLink(M.R, key);


 * ol :&#61; F.elems;


 * WHILE ol # NIL DO


 * IF ol.obj # action THEN


 * Gadgets.WriteRef(M.R, F.lib, ol.obj)


 * END;


 * ol :&#61; 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 &#61; F.stamp THEN


 * M.obj :&#61; F.dlink


 * ELSE


 * NEW(F1);


 * F.stamp :&#61; M.stamp; F.dlink :&#61; F1;


 * CopyForm(M, F, F1);


 * M.obj :&#61; F1


 * END


 * END


 * ELSIF M IS Objects.BindMsg THEN


 * ol :&#61; F.elems;


 * WHILE ol # NIL DO


 * ol.obj.handle(ol.obj, M);


 * ol :&#61; ol.next


 * END


 * ELSE


 * TextGadgets.ControlHandler(F, M)


 * END


 * END


 * END FormHandler;


 * PROCEDURE NewForm*;


 * VAR form: Form;


 * BEGIN


 * NEW(form);


 * form.W :&#61; 0; form.H :&#61; 0;


 * form.elems :&#61; NIL;


 * form.handle :&#61; FormHandler;


 * Objects.NewObj :&#61; form


 * END NewForm;


 * PROCEDURE HexDigit(i: INTEGER): CHAR;


 * BEGIN


 * IF i &#60; 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 :&#61; NIL; M.loc :&#61; NIL; M.res :&#61; -1;


 * M.X :&#61; Oberon.Mouse.X; M.Y :&#61; Oberon.Mouse.Y;


 * Display.Broadcast(M);


 * IF M.loc &#61; obj THEN


 * x :&#61; M.u; y :&#61; -M.v


 * ELSE


 * x :&#61; 0; y :&#61; 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 :&#61; Strings.OberonToISO&#91;ORD(ch)&#93;;


 * IF (ch &#60; 020X) OR (ch &#61; "+") OR(ch &#61; "&#38;") OR (ch &#61; "&#61;") OR (ch &#61; "?") OR (ch &#61; "%") OR (ch &#61; "$") OR


 * (ch &#61; ";") OR (ch &#61; "/") OR (ch &#61; "#") OR (ch &#61; ":") THEN


 * Texts.Write(Wr, "%");


 * Texts.Write(Wr, HexDigit(ORD(ch) DIV 16));


 * Texts.Write(Wr, HexDigit(ORD(ch) MOD 16))


 * ELSIF ch &#61; 020X THEN


 * Texts.Write(Wr, "+");


 * ELSIF ch &#62;&#61; 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, "&#61;");


 * Texts.WriteInt(Wr, i, 0);


 * Texts.Write(Wr, "&#38;")


 * END IntAttr;


 * PROCEDURE StrAttr(CONST s: ARRAY OF CHAR);


 * VAR i: INTEGER;


 * BEGIN


 * Texts.WriteString(Wr, name);


 * Texts.Write(Wr, "&#61;");


 * i :&#61; 0;


 * WHILE s&#91;i&#93; # 0X DO


 * WriteEscCh(s&#91;i&#93;);


 * INC(i)


 * END;


 * Texts.Write(Wr, "&#38;")


 * END StrAttr;


 * PROCEDURE RealAttr(y: LONGREAL; l: INTEGER);


 * BEGIN


 * Texts.WriteString(Wr, name);


 * Texts.Write(Wr, "&#61;");


 * Texts.WriteLongReal(Wr, y, l);


 * Texts.Write(Wr, "&#38;")


 * END RealAttr;


 * BEGIN


 * NEW(query);


 * Texts.Open(query, "");


 * ol :&#61; form.elems;


 * WHILE ol # NIL DO


 * obj :&#61; ol.obj;


 * Gadgets.GetObjName(obj, name);


 * IF (name # "") &#38; (name&#91;0&#93; # "@") THEN


 * IF HasA(obj, "FormElem") THEN


 * Attributes.GetString(obj, "FormElem", str);


 * IF (str &#61; "IMAGE") &#38; (exec &#61; obj) THEN


 * MapCoord(obj, x, y);


 * Strings.Append(name, ".x");


 * IntAttr(x);


 * Gadgets.GetObjName(obj, name);


 * Strings.Append(name, ".y");


 * IntAttr(y)


 * ELSIF str &#61; "SELECT" THEN


 * done :&#61; FALSE;


 * Attributes.GetBool(obj, "MultiSel", multisel);


 * item :&#61; obj(Lists.List).items(Item);


 * WHILE (item # NIL) &#38; (&#126;done OR multisel) DO


 * IF item.sel THEN


 * done :&#61; TRUE;


 * IF item.hasVal THEN


 * StrAttr(item.value)


 * ELSE


 * StrAttr(item.s)


 * END


 * END;


 * IF item.next # NIL THEN


 * item :&#61; item.next(Item)


 * ELSE


 * item :&#61; NIL


 * END


 * END;


 * IF &#126;done THEN


 * StrAttr("")


 * END


 * ELSIF str &#61; "TEXTAREA" THEN


 * Texts.WriteString(Wr, name);


 * Texts.Write(Wr, "&#61;");


 * text :&#61; GetText(obj);


 * IF text # NIL THEN


 * Texts.OpenReader(R, text, 0);


 * Texts.Read(R, ch);


 * WHILE &#126;R.eot DO


 * IF ch &#61; Strings.CR THEN


 * (* SendText -&#62; CRLF *)


 * Texts.WriteLn(Wr)


 * ELSE


 * WriteEscCh(ch)


 * END;


 * Texts.Read(R, ch)


 * END


 * END;


 * Texts.Write(Wr, "&#38;")


 * ELSIF str &#61; "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") &#38; (obj &#61; 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 :&#61; SHORT(A.i)


 * ELSE


 * x :&#61; -1


 * END;


 * A.id :&#61; Objects.get;


 * A.name :&#61; "Value";


 * A.class :&#61; Objects.Inval;


 * A.res :&#61; -1;


 * obj.handle(obj, A);


 * CASE A.class OF


 * Objects.Int: IntAttr(A.i);


 * &#124;Objects.String: IF (x &#62;&#61; 0) &#38; (x &#60; LEN(A.s)) THEN


 * A.s&#91;x&#93; :&#61; 0X


 * END;


 * StrAttr(A.s)


 * &#124;Objects.Real: IF x &#62; 0 THEN


 * RealAttr(A.x, x)


 * ELSE


 * RealAttr(A.x, 15)


 * END


 * &#124;Objects.LongReal: IF x &#62; 0 THEN


 * RealAttr(A.y, x)


 * ELSE


 * RealAttr(A.y, 15)


 * END


 * ELSE


 * END


 * END


 * END;


 * ol :&#61; 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 :&#61; NIL;


 * exec :&#61; Gadgets.executorObj;


 * IF HasA(exec, "Query") &#38; HasA(exec, "Method") THEN


 * clearCache :&#61; TRUE;


 * form :&#61; CurForm(Gadgets.context);


 * obj :&#61; FindFormObj(form, "@ACTION");


 * Attributes.GetInt(obj, "Value", key);


 * HyperDocs.DocNameByKey(docname, key);


 * Attributes.GetString(exec, "Query", qury);


 * IF qury &#61; "ISINDEX" THEN


 * obj :&#61; 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 :&#61; "GET"


 * ELSIF qury &#61; "ISMAP" THEN


 * IF HasA(exec, "UseMapKey") THEN


 * clearCache :&#61; FALSE;


 * query :&#61; NIL; Attributes.GetInt(exec, "UseMapKey", mkey)


 * ELSE


 * mkey :&#61; HyperDocs.UndefKey


 * END;


 * IF mkey &#61; 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 :&#61; NIL; key :&#61; mkey


 * END;


 * attr :&#61; "GET"


 * ELSIF qury &#61; "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 &#61; "GET") OR (attr &#61; "POST") THEN


 * NEW(cont);


 * cont.query :&#61; query;


 * COPY(attr, cont.method);


 * cont.user :&#61; ""; cont.passwd :&#61; "";


 * IF clearCache THEN


 * HyperDocs.CacheDoc(key, NIL);


 * HyperDocs.CacheText(key, NIL)


 * END;


 * HyperDocs.FollowKeyLink(cont, key)


 * ELSIF attr &#61; "Authorization" THEN


 * NEW(cont); cont.query :&#61; NIL;


 * cont.method :&#61; "GET";


 * obj :&#61; FindFormObj(form, "Username");


 * Attributes.GetString(obj, "Value", cont.user);


 * obj :&#61; FindFormObj(form, "Password");


 * Attributes.GetString(obj, "Value", cont.passwd);


 * HyperDocs.FollowKeyLink(cont, key)


 * ELSIF attr &#61; "MAILTO" THEN


 * NEW(cont);


 * cont.curDoc :&#61; NIL; cont.new :&#61; NIL;


 * cont.replace :&#61; FALSE; cont.history :&#61; FALSE;


 * cont.old :&#61; HyperDocs.NodeByDoc(Desktops.CurDoc(Gadgets.context));


 * HyperDocs.context :&#61; cont;


 * HyperDocs.RetrieveLink(key, docname);


 * doc :&#61; Documents.Open(docname);


 * HyperDocs.context :&#61; NIL;


 * IF (doc # NIL) &#38; (doc.dsc # NIL) THEN


 * IF (query # NIL) &#38; (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 :&#61; -1; COPY("", cmd); i :&#61; 0;


 * WHILE (href&#91;i&#93; # 0X) &#38; (href&#91;i&#93; &#60;&#61; " ") DO


 * INC(i)


 * END;


 * IF href&#91;i&#93; &#61; "#" THEN


 * COPY("HTMLDocs.Locate ", cmd); href&#91;0&#93; :&#61; " ";


 * Strings.Append(cmd, href); cmd&#91;16&#93; :&#61; 022X;


 * Strings.AppendCh(cmd, 022X)


 * ELSE


 * WHILE (href&#91;i&#93; # 0X) &#38; (href&#91;i&#93; # "#") DO


 * INC(i)


 * END;


 * IF href&#91;i&#93; &#61; "#" THEN


 * DEC(i); j :&#61; 0;


 * WHILE P.base.path&#91;j&#93; # 0X DO


 * INC(j)


 * END; DEC(j);


 * WHILE (i &#62; 0) &#38; (j &#62; 0) &#38; (href&#91;i&#93; &#61; P.base.path&#91;j&#93;) DO


 * DEC(i); DEC(j)


 * END;


 * IF i &#61; 0 THEN


 * COPY("HTMLDocs.Locate ", cmd);


 * j :&#61; 0;


 * WHILE cmd&#91;j&#93; # 0X DO


 * INC(j)


 * END;


 * cmd&#91;j&#93; :&#61; 022X; INC(j);


 * WHILE (href&#91;i&#93; # 0X) &#38; (href&#91;i&#93; # "#") DO


 * INC(i)


 * END; INC(i);


 * WHILE href&#91;i&#93; # 0X DO


 * cmd&#91;j&#93; :&#61; href&#91;i&#93;; INC(j); INC(i)


 * END;


 * cmd&#91;j&#93; :&#61; 022X; cmd&#91;j+1&#93; :&#61; 0X;


 * RETURN


 * END


 * END;


 * key :&#61; HyperDocs.BuildKey(P.base, href);


 * IF key &#61; 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 :&#61; S.S; S.ch :&#61; S.next;


 * IF St.eos &#38; (St.Available(St) &#60;&#61; 0) THEN


 * S.avail :&#61; 0; S.next :&#61; 0X; S.end :&#61; TRUE


 * ELSE


 * TextStreams.Read(St, S.next); DEC(S.avail);


 * Texts.Write(S.page.Ws, S.next);


 * IF S.ch &#61; Strings.CR THEN


 * IF S.next &#61; Strings.LF THEN


 * TextStreams.Read(St, S.next); DEC(S.avail);


 * Texts.Write(S.page.Ws, S.next)


 * END;


 * S.ch :&#61; Strings.CR


 * ELSIF S.ch &#61; Strings.LF THEN


 * S.ch :&#61; Strings.CR


 * END


 * END


 * END Read;


 * PROCEDURE ChangeFontFamily(f: Fonts.Font; CONST 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 &#61; "#" THEN


 * (* Texts.WriteString(Wr, "Numeric character reference pending."); Texts.WriteLn(Wr);


 * Texts.Append(Oberon.Log, Wr.buf); *)


 * Read(S);


 * IF Strings.IsDigit(S.ch) THEN


 * i :&#61; 0;


 * WHILE &#126;S.end &#38; (i &#60; 9100) &#38; Strings.IsDigit(S.ch) DO


 * i :&#61; 10*i+ORD(S.ch)-ORD("0"); Read(S)


 * END;


 * IF S.ch &#61; ";" THEN


 * Read(S)


 * END;


 * CASE i OF


 * 169: Texts.WriteString(P.W, "(c)")


 * &#124;174: Texts.WriteString(P.W, "(R)")


 * &#124;188: Texts.WriteString(P.W, " 1/4")


 * &#124;189: Texts.WriteString(P.W, " 1/2")


 * &#124;190: Texts.WriteString(P.W, " 3/4")


 * ELSE


 * fnt :&#61; P.W.lib(Fonts.Font);


 * IF (i &#62;&#61; 913) &#38; (i &#60;&#61; 982) THEN	(* Greek character *)


 * Strings.IntToStr(i - 900, istr);


 * k :&#61; 0;


 * WHILE ((istr&#91;0&#93; # GreekTab&#91;k&#93;) OR (istr&#91;1&#93; # GreekTab&#91;k+1&#93;)) &#38; (k &#60; 120) DO


 * k :&#61; k + 2


 * END;


 * k :&#61; k DIV 2;


 * Texts.SetFont (P.W, ChangeFontFamily(fnt, "Greek"));


 * Texts.Write(P.W, CHR(k + ORD("@")));


 * Texts.SetFont (P.W, fnt);


 * ELSIF (i &#62;&#61; 8501) &#38; (i &#60;&#61; 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 *)


 * &#124; 8721: Texts.Write(P.W, "e")	(*	e	sum *)


 * (* f *)	(*	f	ll *)


 * (* g *)	(*	g	gg *)


 * &#124; 8968: Texts.Write(P.W, "h")	(*	h	lceil *)


 * &#124; 8969: Texts.Write(P.W, "i")	(*	i	rceil *)


 * &#124; 8970: Texts.Write(P.W, "j")	(*	j	lfloor *)


 * &#124; 8971: Texts.Write(P.W, "k")	(*	k	rfloor *)


 * (* l *)	(*	l	Vert *)


 * (* m *)	(*	m	mp *)


 * (* n *)	(*	n	prec *)


 * (* o *)	(*	o	succ *)


 * &#124; 8776: Texts.Write(P.W, "p")	(*	p	simeq *)


 * &#124; 9002: Texts.Write(P.W, "q")	(*	q	rangle *)


 * (* r *)	(*	r	not supset *)


 * &#124; 8629: Texts.Write(P.W, "s")	(*	s	(carriage return) *)


 * (* t *)	(*	t	?? *)


 * (* u *)	(*	u	?? *)


 * (* v - moved down *)	(*	v	S *)


 * (* w *)	(*	w	?? *)


 * &#124; 8747: Texts.Write(P.W, "x")	(*	x	int *)


 * (* y *)	(*	y	oint *)


 * (* z *)	(*	z	because *)


 * (* C *)	(*	C	wp *)


 * (* D *)	(*	D	oslash *)


 * &#124; 8853: Texts.Write(P.W, "E")	(*	E	oplus *)


 * &#124; 8709: Texts.Write(P.W, "F")	(*	F	emptyset *)


 * &#124; 8745: Texts.Write(P.W, "G")	(*	G	cap *)


 * &#124; 8746: Texts.Write(P.W, "H")	(*	H	cup *)


 * &#124; 8835: Texts.Write(P.W, "I")	(*	I	supset *)


 * &#124; 8839: Texts.Write(P.W, "J")	(*	J	supseteq *)


 * &#124; 8836: Texts.Write(P.W, "K")	(*	K	not subset *)


 * &#124; 8834: Texts.Write(P.W, "L")	(*	L	subset *)


 * &#124; 8838: Texts.Write(P.W, "M")	(*	M	subseteq *)


 * &#124; 8712: Texts.Write(P.W, "N")	(*	N	in *)


 * &#124; 8713: Texts.Write(P.W, "O")	(*	O	not in *)


 * &#124; 8736: Texts.Write(P.W, "P")	(*	P	angle *)


 * &#124; 8711: Texts.Write(P.W, "Q")	(*	Q	nabla *)


 * &#124; 8704: Texts.Write(P.W, "R")	(*	R	forall *)


 * &#124; 8707: Texts.Write(P.W, "S")	(*	S	exists *)


 * &#124; 8715: Texts.Write(P.W, "T")	(*	T	owns *)


 * (* U *)	(*	U	sqcap *)


 * &#124; 8730: Texts.Write(P.W, "V")	(*	V	surb *)


 * &#124; 8901: Texts.Write(P.W, "W")	(*	W	cdot *)


 * (* X *)	(*	X	lnot *)


 * &#124; 8869: Texts.Write(P.W, "Y")	(*	Y	land *)


 * &#124; 8870: Texts.Write(P.W, "Z")	(*	Z	lor *)


 * (* 0 - moved down *)	(*	0	deg *)


 * (* 1 - moved down *)	(*	1	pm *)


 * &#124; 8805: Texts.Write(P.W, "3")	(*	3	geq *)


 * (* 4 *)	(*	4	times *)


 * &#124; 8733: Texts.Write(P.W, "5")	(*	5	propto *)


 * &#124; 8706: Texts.Write(P.W, "6")	(*	6	partial *)


 * (* 7 - moved down *)	(*	7	bullet *)


 * (* 8 - moved down *)	(*	8	div *)


 * &#124; 8800: Texts.Write(P.W, "9")	(*	9	not&#61; *)


 * (* ) *)	(*	)	swarrow *)


 * (* ! *)	(*	!	mathbb R ?? *)


 * &#124; 8501: Texts.Write(P.W, "@")	(*	@	aleph *)


 * &#124; 8804: Texts.Write(P.W, "#")	(*	#	leq *)


 * (* $ - moved down *)	(*	$	/ *)


 * &#124; 8734: Texts.Write(P.W, "%")	(*	%	infty *)


 * &#124; 8658: Texts.Write(P.W, "^")	(*	^	Rightarrow *)


 * (* &#38; *)	(*	&#38;	&#62;&#62; *)


 * (* * *)	(*	*	searrow *)


 * (* ( *)	(*	(	nwarrow *)


 * &#124; 8592: Texts.Write(P.W, ",")	(*	,	gets *)


 * &#124; 8594: Texts.Write(P.W, ".")	(*	.	to *)


 * &#124; 8773: Texts.Write(P.W, ";")	(*	;	approx *)


 * (* &#39; *)	(*	&#39;	nearrow *)




 * &#124; 8593: Texts.Write(P.W, "-")	(*	-	uparrow *)


 * (* &#61; *)	(*	&#61;	vert *)


 * &#124; 8595: Texts.Write(P.W, "/")	(*	/	downarrow *)


 * &#124; 8656: Texts.Write(P.W, "\")	(*	\	Leftarrow *)


 * &#124; 8660: Texts.Write(P.W, "&#91;")	(*	&#91;	Leftrightarrow *)


 * &#124; 8657: Texts.Write(P.W, "&#93;")	(*	&#93;	Uparrow *)


 * (* &#60; - moved down *)	(*	&#60;	dots *)


 * &#124; 8722: Texts.Write(P.W, "&#62;")	(*	&#62;	(minus sign) *)


 * &#124; 8801: Texts.Write(P.W, ":")	(*	:	equiv *)


 * (* " - moved down *)	(*	"	prime *)


 * (* &#126; - moved down *)	(*	&#126;	cents *)


 * &#124; 8659: Texts.Write(P.W, "_")	(*	_	Downarrow *)


 * &#124; 8596: Texts.Write(P.W, "+")	(*	+	leftrightarrow *)


 * ELSE


 * END;


 * Texts.SetFont (P.W, fnt);


 * ELSIF (i &#62;&#61; 8201) &#38; (i &#60;&#61; 8500) THEN	(* Math character *)


 * Texts.SetFont (P.W, ChangeFontFamily(fnt, "Math"));


 * CASE i OF	(* glyph	TeX name *)


 * &#160;&#160;8465: Texts.Write(P.W, "A")	(*	A	Im *)


 * &#124; 8476: Texts.Write(P.W, "B")	(*	B	Re *)


 * &#124; 8243: Texts.Write(P.W, "2")	(*	2	(double prime) *)


 * &#124; 8226: Texts.Write(P.W, "7")	(*	7	bullet *)


 * &#124; 8260: Texts.Write(P.W, "$")	(*	$	/ *)


 * &#124; 8230: Texts.Write(P.W, "&#60;")	(*	&#60;	dots *)


 * &#124; 8242: Texts.Write(P.W, &#39;"&#39;)	(*	"	prime *)


 * &#124; 8224: Texts.Write(P.W, "&#125;")	(*	&#125;	dagger *)


 * ELSE


 * END;


 * Texts.SetFont (P.W, fnt);




 * ELSIF (i &#62;&#61; 160) &#38; (i &#60;&#61; 250) THEN	(* Math character *)


 * &#42;)


 * ELSIF (i &#61; 167) OR (i &#61; 176) OR (i &#61; 177) OR (i &#61; 162) OR (i &#61; 247) THEN (* Math character *)


 * Texts.SetFont (P.W, ChangeFontFamily(fnt, "Math"));


 * CASE i OF	(* glyph	TeX name *)


 * &#160;&#160;167: Texts.Write(P.W, "v")	(*	v	S *)


 * &#124; 176: Texts.Write(P.W, "0")	(*	0	deg *)


 * &#124; 177: Texts.Write(P.W, "1")	(*	1	pm *)


 * &#124; 247: Texts.Write(P.W, "8")	(*	8	div *)


 * &#124; 162: Texts.Write(P.W, "&#126;")	(*	&#126;	cents *)


 * ELSE


 * END;


 * Texts.SetFont (P.W, fnt);

&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;(* ISOToOberon is an array of 256 characters whereas HTML can

&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;refer to any of 65536 characters, &#38;#0; .. &#38;#65535;. Check that i is

&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;a valid index to ISOToOberon. *)


 * ELSIF (i &#60; 256) THEN


 * Texts.Write(P.W, Strings.ISOToOberon&#91;i&#93;)


 * ELSE (* The character is not available in ETH Oberon / PC Native. *)


 * Texts.Write(P.W, CHR(0)) (* Make a nul character. *)


 * END


 * END


 * ELSE (* Faulty HTML character reference. *)


 * Texts.WriteString(P.W, "&#38;#")


 * END


 * ELSE


 * (* Texts.WriteString(Wr, "Named character entity pending."); Texts.WriteLn(Wr);


 * Texts.Append(Oberon.Log, Wr.buf); *)


 * i :&#61; 0;


 * WHILE &#126;S.end &#38; Strings.IsAlpha(S.ch) &#38; (i &#60; 63) DO


 * entity&#91;i&#93; :&#61; S.ch; INC(i); entity&#91;i&#93; :&#61; 0X;


 * j :&#61; 0;


 * WHILE (j &#60; LEN(entities)) &#38; (entities&#91;j&#93; # entity) DO


 * INC(j)


 * END;


 * IF j &#60; LEN(entities) THEN


 * i :&#61; 63


 * END;


 * Read(S)


 * END;


 * entity&#91;i&#93; :&#61; 0X;


 * (* Texts.WriteString(Wr, "Entity name &#61; "); Texts.WriteString(Wr, entity); Texts.WriteLn(Wr);


 * Texts.Append(Oberon.Log, Wr.buf); *)


 * i :&#61; 0;


 * WHILE (i &#60; LEN(entities)) &#38; (entities&#91;i&#93; # entity) DO


 * INC(i)


 * END;


 * IF i &#60; LEN(entities) THEN


 * Texts.Write(P.W, entityEncoding&#91;i&#93;)


 * ELSIF entity &#61; "trade" THEN


 * PushTextAttrs(P); Texts.SetOffset(P.W, 4);


 * Texts.WriteString(P.W, "TM");


 * PopTextAttrs(P)


 * ELSIF entity &#61; "reg" THEN


 * Texts.WriteString(P.W, "(R)")


 * ELSIF entity &#61; "copy" THEN


 * Texts.WriteString(P.W, "(c)")


 * ELSE


 * lib :&#61; Objects.ThisLibrary("HTMLIcons.Lib");


 * Objects.GetRef(lib.dict, entity, ref);


 * IF ref &#62;&#61; 0 THEN


 * lib.GetObj(lib, ref, obj); WriteObj(P, obj)


 * ELSE


 * Texts.Write(P.W, "&#38;"); Texts.WriteString(P.W, entity); RETURN


 * END


 * END;


 * IF S.ch &#61; ";" 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 &#61; "#" THEN


 * Read(S);


 * IF Strings.IsDigit(S.ch) THEN


 * i :&#61; 0;


 * WHILE &#126;S.end &#38; (i &#60; 256) &#38; Strings.IsDigit(S.ch) DO


 * i :&#61; 10*i+ORD(S.ch)-ORD("0"); Read(S)


 * END;


 * IF S.ch &#61; ";" THEN


 * Read(S)


 * END;


 * CASE i OF


 * &#160;169: Strings.Append(str, "(c)")


 * &#124;174: Strings.Append(str, "(R)")


 * &#124;188: Strings.Append(str, " 1/4")


 * &#124;189: Strings.Append(str, " 1/2")


 * &#124;190: Strings.Append(str, " 3/4")


 * ELSE


 * &#160;&#160;&#160;&#160;IF (i &#60; 256) THEN


 * &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Strings.AppendCh(str, Strings.ISOToOberon&#91;i&#93;)


 * &#160;&#160;&#160;&#160;ELSE (* The character is not available in ETH Oberon / PC Native 05.01.2003. *)


 * Strings.AppendCh(str, CHR(0)) (* Make a nul character. *)


 * &#160;&#160;&#160;&#160;END


 * END


 * ELSE (* Faulty HTML character reference. *)


 * Strings.Append(str, "&#38;#")


 * END


 * ELSIF Strings.IsAlpha(S.ch) THEN


 * i :&#61; 0;


 * WHILE &#126;S.end &#38; Strings.IsAlpha(S.ch) &#38; (i &#60; 63) DO


 * entity&#91;i&#93; :&#61; S.ch; INC(i); entity&#91;i&#93; :&#61; 0X;


 * j :&#61; 0;


 * WHILE (j &#60; LEN(entities)) &#38; (entities&#91;j&#93; # entity) DO


 * INC(j)


 * END;


 * IF j &#60; LEN(entities) THEN


 * i :&#61; 63


 * END;


 * Read(S)


 * END;


 * entity&#91;i&#93; :&#61; 0X;


 * i :&#61; 0;


 * WHILE (i &#60; LEN(entities)) &#38; (entities&#91;i&#93; # entity) DO


 * INC(i)


 * END;


 * IF i &#60; LEN(entities) THEN


 * Strings.AppendCh(str, entityEncoding&#91;i&#93;)


 * ELSIF entity &#61; "trade" THEN


 * Strings.Append(str, "TM")


 * ELSIF entity &#61; "reg" THEN


 * Strings.Append(str, "(R)")


 * ELSIF entity &#61; "copy" THEN


 * Strings.Append(str, "(c)")


 * ELSE


 * Strings.AppendCh(str, "&#38;"); Strings.Append(str, entity); RETURN


 * END;


 * IF S.ch &#61; ";" THEN


 * Read(S)


 * END


 * ELSE


 * COPY("&#38;", str)


 * END


 * END CharRefStr;


 * PROCEDURE Next*(VAR S: Scanner);


 * VAR i, l: LONGINT;


 * BEGIN


 * CASE S.state OF


 * TextHtml:


 * IF S.ch &#61; "&#60;" THEN


 * Read(S);


 * IF Strings.IsAlpha(S.ch) OR (S.ch &#61; "/") OR (S.ch &#61; "!") THEN


 * S.class :&#61; OpenTag


 * ELSE


 * S.char :&#61; S.ch; S.class :&#61; Character


 * END


 * ELSIF S.ch &#61; "&#38;" THEN


 * Read(S); S.class :&#61; CharRef


 * ELSIF (S.ch &#60;&#61; " ") &#38; (S.ch # 0X) &#38; &#126;S.pre THEN


 * WHILE &#126;S.end &#38; (S.ch &#60;&#61; " ") DO


 * Read(S)


 * END;


 * IF (S.ch &#61; "&#60;") &#38; (S.class &#61; CloseTag) THEN


 * Read(S);


 * IF Strings.IsAlpha(S.ch) OR (S.ch &#61; "/") OR (S.ch &#61; "!") THEN


 * S.class :&#61; OpenTag


 * ELSE


 * S.char :&#61; S.ch; S.class :&#61; Character


 * END


 * ELSIF S.end THEN


 * S.class :&#61; Undef; S.state :&#61; End


 * ELSE


 * S.class :&#61; WhiteSpace


 * END


 * ELSIF &#126;S.end THEN


 * S.char :&#61; S.ch; Read(S); S.class :&#61; Character(*; S.skipWS :&#61; FALSE*)


 * ELSE


 * S.class :&#61; Undef; S.state :&#61; End


 * END


 * &#124;TextPlain:


 * IF S.ch # 0X THEN


 * S.char :&#61; S.ch; Read(S); S.class :&#61; Character


 * ELSE


 * S.class :&#61; Undef; S.state :&#61; End


 * END


 * &#124;InTag:


 * WHILE &#126;S.end &#38; (S.ch &#60;&#61; " ") DO


 * Read(S)


 * END;


 * IF S.ch &#61; "/" THEN


 * Read(S); S.class :&#61; OpenEndTag


 * ELSIF S.ch &#61; "&#62;" THEN


 * Read(S); S.class :&#61; CloseTag


 * ELSIF S.ch &#61; "&#38;" THEN


 * Read(S); S.class :&#61; CharRef


 * ELSIF Strings.IsAlpha(S.ch) THEN


 * i :&#61; 0; l :&#61; LEN(S.value)-1;


 * WHILE &#126;S.end &#38; (Strings.IsAlpha(S.ch) OR Strings.IsDigit(S.ch)) DO


 * IF i &#60; l THEN


 * S.value&#91;i&#93; :&#61; S.ch; INC(i)


 * END;


 * Read(S)


 * END;


 * S.value&#91;i&#93; :&#61; 0X; S.class :&#61; Value;


 * WHILE &#126;S.end &#38; (S.ch &#60;&#61; " ") DO


 * Read(S)


 * END;


 * IF S.ch &#61; "&#60;" THEN


 * Read(S);


 * WHILE &#126;S.end &#38; (S.ch &#60;&#61; " ") DO


 * Read(S)


 * END;


 * IF Strings.IsAlpha(S.ch) THEN


 * i :&#61; 0; l :&#61; LEN(S.value)-1;


 * WHILE &#126;S.end &#38; (Strings.IsAlpha(S.ch) OR Strings.IsDigit(S.ch)) DO


 * IF i &#60; l THEN


 * S.value&#91;i&#93; :&#61; S.ch; INC(i)


 * END;


 * Read(S)


 * END


 * END


 * END


 * ELSIF &#126;S.end THEN


 * S.char :&#61; S.ch; Read(S); S.class :&#61; Character


 * ELSE


 * S.class :&#61; Undef; S.state :&#61; End


 * END


 * ELSE


 * S.class :&#61; Undef; S.state :&#61; 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 &#126;S.end &#38; (S.ch &#60;&#61; " ") DO


 * Read(S)


 * END;


 * IF (S.ch &#61; ",") OR (S.ch &#61; ";") THEN


 * Read(S);


 * WHILE &#126;S.end &#38; (S.ch &#60;&#61; " ") DO


 * Read(S)


 * END


 * END;


 * i :&#61; 0; l :&#61; LEN(name)-1;


 * WHILE &#126;S.end &#38; Strings.IsAlpha(S.ch) DO


 * IF i &#60; l THEN


 * name&#91;i&#93; :&#61; CAP(S.ch); INC(i)


 * END;


 * Read(S)


 * END;


 * name&#91;i&#93; :&#61; 0X;


 * IF i &#62; 0 THEN


 * WHILE &#126;S.end &#38; (S.ch &#60;&#61; " ") DO


 * Read(S)


 * END;


 * IF S.ch &#61; "&#61;" THEN


 * Read(S);


 * WHILE &#126;S.end &#38; (S.ch &#60;&#61; " ") DO


 * Read(S)


 * END;


 * IF S.ch &#61; 022X THEN


 * Read(S); quoted :&#61; TRUE


 * ELSE


 * quoted :&#61; FALSE


 * END;


 * i :&#61; 0; l :&#61; LEN(S.value)-1;


 * WHILE &#126;S.end &#38; ( (&#126;quoted &#38; (S.ch &#62; " ") &#38; (S.ch # "&#62;")) OR (quoted &#38; (S.ch # 022X)) ) DO


 * IF i &#60; l THEN


 * IF S.ch &#61; "&#38;" THEN


 * Read(S); CharRefStr(S.page, S, charRef);


 * S.value&#91;i&#93; :&#61; 0X; Strings.Append(S.value, charRef);


 * i :&#61; Strings.Length(S.value)


 * ELSIF (S.ch &#62;&#61; " ") OR (S.ch &#61; Strings.Tab) THEN


 * S.value&#91;i&#93; :&#61; S.ch; INC(i); Read(S)


 * ELSE


 * Read(S)


 * END


 * ELSE


 * Read(S)


 * END


 * END;


 * IF S.ch &#61; 022X THEN


 * Read(S)


 * END;


 * WHILE &#126;quoted &#38; (i &#62; 0) &#38; (S.value&#91;i-1&#93; &#60;&#61; " ") DO


 * S.value&#91;i-1&#93; :&#61; 0X; DEC(i)


 * END;


 * S.value&#91;i&#93; :&#61; 0X


 * ELSE


 * COPY("", S.value)


 * END;


 * S.class :&#61; 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 :&#61; NIL; NEW(attr);


 * WHILE NextAttr(S, attr.name) DO


 * attr.next :&#61; attrs; attrs :&#61; attr;


 * COPY(S.value, attr.value);


 * NEW(attr)


 * END


 * END GetAttrs;


 * PROCEDURE FindAttr*(attrs: TagAttr; CONST name: ARRAY OF CHAR): TagAttr;


 * VAR attr: TagAttr;


 * BEGIN


 * attr :&#61; attrs;


 * WHILE (attr # NIL) &#38; (attr.name # name) DO


 * attr :&#61; attr.next


 * END;


 * RETURN attr


 * END FindAttr;


 * PROCEDURE OpenScanner*(VAR S: Scanner; St: Streams.Stream);


 * BEGIN


 * (*S.skipWS :&#61; FALSE; S.genWS :&#61; FALSE;*)


 * S.ch :&#61; 0X; S.next :&#61; 0X; S.end :&#61; FALSE;


 * S.S :&#61; St; S.class :&#61; Undef; S.state :&#61; TextHtml; S.pre :&#61; 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 :&#61; S.page; CloseA(P); P.linkkey :&#61; HyperDocs.UndefKey;


 * IF on THEN


 * isRef :&#61; FALSE; newDoc :&#61; FALSE;


 * WHILE NextAttr(S, attr) DO


 * IF attr &#61; "HREF" THEN


 * isRef :&#61; TRUE; HREF(P, S.value, attr, P.linkkey);


 * IF P.linkkey # HyperDocs.UndefKey THEN


 * P.alink :&#61; HyperDocs.LinkControl(P.linkkey)


 * ELSE


 * P.alink :&#61; Gadgets.CreateObject("TextGadgets.NewControl");


 * Attributes.SetString(P.alink, "Cmd", attr)


 * END


 * ELSIF (attr &#61; "NAME") OR (attr &#61; "ID") THEN


 * IF S.value &#61; P.orgLabel THEN


 * Texts.Append(P.T, P.W.buf); P.orgPos :&#61; P.T.len


 * END;


 * obj :&#61; Gadgets.CreateObject("TextGadgets.NewControl");


 * WriteObj(P, obj); Attributes.SetString(obj, "Name", S.value)


 * ELSIF attr &#61; "TARGET" THEN


 * newDoc :&#61; TRUE


 * END


 * END;


 * IF isRef THEN


 * IF newDoc THEN


 * Attributes.SetString(P.alink, "Opt", "New")


 * END;


 * IF &#126;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 :&#61; 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 :&#61; 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 :&#61; 0;


 * WHILE (url&#91;i&#93; # 0X) &#38; (url&#91;i&#93; # "/") DO


 * INC(i)


 * END;


 * WHILE url&#91;i&#93; &#61; "/" DO


 * INC(i)


 * END;


 * j :&#61; 0;


 * WHILE (url&#91;j&#93; # 0X) &#38; (url&#91;j&#93; # "@") DO


 * INC(j)


 * END;


 * IF url&#91;j&#93; &#61; "@" THEN


 * i :&#61; j+1


 * END;


 * j :&#61; 0;


 * WHILE (url&#91;i&#93; # 0X) &#38; (url&#91;i&#93; # "/") &#38; (url&#91;i&#93; # ":") DO


 * host&#91;j&#93; :&#61; url&#91;i&#93;; INC(i); INC(j)


 * END;


 * host&#91;j&#93; :&#61; 0X;


 * IF url&#91;i&#93; &#61; ":" THEN


 * INC(i); Strings.StrToIntPos(url, val, i);


 * port :&#61; SHORT(val)


 * ELSE


 * port :&#61; 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 :&#61; S.page;


 * WHILE NextAttr(S, attr) DO


 * IF attr &#61; "HREF" THEN


 * P.base.key :&#61; HyperDocs.BuildKey(P.base, S.value);


 * s :&#61; HyperDocs.LinkSchemeByKey(P.base.key);


 * IF s &#61; NIL THEN


 * s :&#61; HyperDocs.LinkSchemeByPrefix("file")


 * END;


 * COPY(s.prefix, P.base.prefix);


 * HyperDocs.RetrieveLink(P.base.key, S.value);


 * IF P.base.prefix &#61; "http" THEN


 * P.base.key :&#61; HTTPDocs0.SplitHTTPAdr(S.value, P.base.host, P.base.path, label, P.base.port)


 * ELSIF P.base.prefix &#61; "file" THEN


 * P.base.key :&#61; HyperDocs.SplitFileAdr(S.value, P.base.path, label);


 * P.base.host :&#61; ""; P.base.port :&#61; 0


 * ELSE (* proxy *)


 * SplitHostPort(S.value, P.base.host, P.base.port);


 * key :&#61; 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 &#62;&#61; "0") &#38; (ch &#60;&#61; "9") THEN


 * RETURN ORD(ch)-ORD("0")


 * ELSIF (ch &#62;&#61; "A") &#38; (ch &#60;&#61; "F") THEN


 * RETURN ORD(ch)-ORD("A")+10


 * ELSIF (ch &#62;&#61; "a") &#38; (ch &#60;&#61; "f") THEN


 * RETURN ORD(ch)-ORD("a")+10


 * ELSE


 * RETURN 256


 * END


 * END HexVal;


 * PROCEDURE Color(CONST val: ARRAY OF CHAR; VAR col: INTEGER);


 * VAR


 * r, g, b, rr, gg, bb, i, bestC: INTEGER;


 * diff, mdiff: REAL;


 * BEGIN


 * IF val &#61; "white" THEN


 * col :&#61; 0; RETURN


 * ELSIF val &#61; "red" THEN


 * col :&#61; 1; RETURN


 * ELSIF val &#61; "green" THEN


 * col :&#61; 2; RETURN


 * ELSIF val &#61; "blue" THEN


 * col :&#61; 3; RETURN


 * ELSIF val &#61; "black" THEN


 * col :&#61; 15; RETURN


 * ELSIF val&#91;0&#93; &#61; "#" THEN


 * r :&#61; 16*HexVal(val&#91;1&#93;)+HexVal(val&#91;2&#93;);


 * g :&#61; 16*HexVal(val&#91;3&#93;)+HexVal(val&#91;4&#93;);


 * b :&#61; 16*HexVal(val&#91;5&#93;)+HexVal(val&#91;6&#93;)


 * ELSE


 * r :&#61; 16*HexVal(val&#91;0&#93;)+HexVal(val&#91;1&#93;);


 * g :&#61; 16*HexVal(val&#91;2&#93;)+HexVal(val&#91;3&#93;);


 * b :&#61; 16*HexVal(val&#91;4&#93;)+HexVal(val&#91;5&#93;)


 * END;


 * IF (r &#62; 255) OR (g &#62; 255) OR (b &#62; 255) THEN


 * RETURN


 * END;


 * mdiff :&#61; MAX(REAL); bestC :&#61; 0;


 * i :&#61; 0;


 * WHILE (i &#60; 256) &#38; (mdiff &#62; 0.0) DO


 * Display.GetColor(i, rr, gg, bb);


 * diff :&#61; LONG(rr-r)*LONG(rr-r);


 * diff :&#61; diff+LONG(gg-g)*LONG(gg-g);


 * diff :&#61; diff+LONG(bb-b)*LONG(bb-b);


 * IF diff &#60; mdiff THEN


 * bestC :&#61; i; mdiff :&#61; diff


 * END;


 * INC(i)


 * END;


 * col :&#61; 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 :&#61; S.page;


 * IF on THEN


 * col :&#61; FALSE; size :&#61; GetFontSize(P);


 * WHILE NextAttr(S, attr) DO


 * IF attr &#61; "SIZE" THEN


 * i :&#61; 1;


 * IF S.value&#91;0&#93; &#61; "-" THEN


 * Strings.StrToIntPos(S.value, val, i);


 * size :&#61; size-SHORT(val)


 * ELSIF S.value&#91;0&#93; &#61; "+" THEN


 * Strings.StrToIntPos(S.value, val, i);


 * size :&#61; size+SHORT(val)


 * ELSE


 * i :&#61; 0;


 * Strings.StrToIntPos(S.value, val, i);


 * size :&#61; SHORT(val)


 * END


 * ELSIF attr &#61; "COLOR" THEN


 * Color(S.value, P.textC); col :&#61; 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 :&#61; 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 :&#61; 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 :&#61; S.page;


 * IF on THEN


 * PushTextAttrs(P);


 * P.style :&#61; NewStyle;


 * WriteObj(P, P.style);


 * Texts.SetFont(P.W, Syntax(12, "i"))


 * ELSE


 * PopTextAttrs(P)


 * END;


 * (*S.skipWS :&#61; TRUE*)


 * END BLOCKQUOTE;


 * PROCEDURE ChangeColors(P: Page; from, to: SHORTINT);


 * VAR tattr: TextAttrs;


 * BEGIN


 * IF P.W.col &#61; from THEN


 * Texts.SetColor(P.W, to)


 * END;


 * tattr :&#61; P.textAttrs;


 * WHILE tattr # NIL DO


 * IF tattr.col &#61; from THEN


 * tattr.col :&#61; to


 * END;


 * tattr :&#61; 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 :&#61; S.page;


 * IF on THEN


 * Texts.Append(P.T, P.W.buf);


 * WHILE NextAttr(S, attr) DO


 * IF attr &#61; "BGCOLOR" THEN


 * Color(S.value, P.textbackC); Attributes.SetInt(P.D.dsc, "Color", P.textbackC)


 * ELSIF attr &#61; "TEXT" THEN


 * col :&#61; P.textC; Color(S.value, col);


 * ChangeColors(P, SHORT(P.textC), SHORT(col));


 * P.textC :&#61; col


 * ELSIF attr &#61; "LINK" THEN (* hyper text link *)


 * col :&#61; P.linkC; Color(S.value, col);


 * ChangeColors(P, SHORT(P.linkC), SHORT(col));


 * P.linkC :&#61; col; Attributes.SetInt(P.D.dsc, "LinkColor", P.linkC)


 * ELSIF attr &#61; "VLINK" THEN (* visited hyper text link *)


 * col :&#61; P.oldLinkC; Color(S.value, col);


 * ChangeColors(P, SHORT(P.oldLinkC), SHORT(col));


 * P.oldLinkC :&#61; col; Attributes.SetInt(P.D.dsc, "OldLinkColor", P.oldLinkC)


 * ELSIF attr &#61; "BACKGROUND" THEN


 * key :&#61; HyperDocs.BuildKey(P.base, S.value);


 * IF imgs THEN


 * NEW(BackE);


 * BackE.basekey :&#61; P.base.dockey; BackE.ol :&#61; NIL; BackE.attrs :&#61; NIL; BackE.text :&#61; P.T;


 * BackE.pos :&#61; -1; BackE.key :&#61; HyperDocs.UndefKey; BackE.obj :&#61; P.D;


 * NEW(doc); HyperDocs.DocNameByKey(doc.name, key); doc.handle :&#61; NIL; doc.dsc :&#61; 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 :&#61; 0*)


 * END;


 * (*S.skipWS :&#61; TRUE*)


 * END BR;


 * PROCEDURE ExecAttrs*;


 * VAR


 * exec, cont: Objects.Object;


 * name, nr: ARRAY 32 OF CHAR;


 * i: LONGINT;


 * BEGIN


 * cont :&#61; Gadgets.context; exec :&#61; Gadgets.executorObj;


 * IF exec # NIL THEN


 * i :&#61; 1; name :&#61; "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 :&#61; "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 :&#61; S.page; CloseA(P);


 * IF on THEN


 * P.clink :&#61; Gadgets.CreateObject("TextGadgets.NewControl");


 * i :&#61; 0;


 * WHILE NextAttr(S, attr) DO


 * IF attr &#61; "CMD" THEN


 * INC(i); name :&#61; "Cmd";


 * Strings.IntToStr(i, attr);


 * Strings.Append(name, attr);


 * Attributes.SetString(P.clink, name, S.value)


 * END


 * END;


 * IF i &#61; 0 THEN


 * P.clink :&#61; 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 :&#61; NIL; PopTextAttrs(P)


 * END


 * END CALL;


 * PROCEDURE CENTER(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; S.page;


 * IF on THEN


 * PushTextAttrs(P);


 * P.style :&#61; NewStyle;


 * EXCL(P.style.mode, TextGadgets.left); INCL(P.style.mode, TextGadgets.middle);


 * WriteObj(P, P.style)


 * ELSE


 * PopTextAttrs(P)


 * END;


 * &#160;(*S.skipWS :&#61; TRUE*)


 * END CENTER;


 * PROCEDURE CITE(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; 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 :&#61; 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 :&#61; S.page;


 * IF on THEN


 * (* IF (P.lists.dtok) THEN


 * Texts.WriteString(Wr, "P.lists.dtok &#61; TRUE"); Texts.WriteLn(Wr); Texts.Append(Oberon.Log, Wr.buf)


 * END; *)


 * (* &#60;DL&#62; and &#60;DD&#62; can exist with &#60;DT&#62; absent. *)


 * IF (P.lists # NIL) &#38; (P.lists.kind &#61; DescList) (* &#38; P.lists.dtok *) THEN


 * Texts.Write(P.W, Strings.Tab)


 * ELSE


 * IF P.lists # NIL THEN


 * INC(P.lists.itemNr);


 * IF P.lists.itemNr &#62; 1 THEN


 * WriteLn(P)


 * END


 * ELSE


 * WriteLn(P)


 * END


 * END


 * END;


 * P.blank :&#61; FALSE


 * (*S.skipWS :&#61; TRUE*)


 * END DD;


 * PROCEDURE DFN(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; 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 :&#61; TRUE*)


 * END DIR;


 * PROCEDURE DIVI(VAR S: Scanner; on: BOOLEAN);


 * VAR


 * P: Page;


 * attr: ARRAY 32 OF CHAR;


 * style: TextGadgets.Style;


 * BEGIN


 * P :&#61; S.page;


 * IF on THEN


 * PushTextAttrs(P);


 * style :&#61; NIL;


 * WHILE NextAttr(S, attr) DO


 * IF attr &#61; "ALIGN" THEN


 * style :&#61; TextAlign(S.value)


 * END


 * END;


 * IF style # NIL THEN


 * P.style :&#61; style; WriteObj(P, P.style)


 * END


 * ELSE


 * PopTextAttrs(P)


 * END;


 * (*S.skipWS :&#61; 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 :&#61; TRUE*)


 * END DL;


 * PROCEDURE DT(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; S.page;


 * IF on THEN


 * IF P.lists # NIL THEN


 * INC(P.lists.itemNr);


 * IF P.lists.itemNr &#62; 1 THEN


 * WriteLn(P)


 * END;


 * IF P.lists.kind &#61; DescList THEN


 * P.lists.dtok :&#61; TRUE;


 * Texts.Write(P.W, Strings.Tab)


 * END


 * ELSE


 * WriteLn(P)


 * END


 * END;


 * P.blank :&#61; FALSE


 * (*S.skipWS :&#61; TRUE*)


 * END DT;


 * PROCEDURE EM(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; 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 :&#61; S.page; CloseA(P); P.linkkey :&#61; HyperDocs.UndefKey;


 * IF on THEN


 * WHILE NextAttr(S, attr) DO


 * IF (attr &#61; "SRC") OR (attr &#61; "HREF") THEN


 * P.lines :&#61; 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 :&#61; HyperDocs.LinkControl(P.linkkey);


 * HyperDocs.RetrieveLink(P.linkkey, S.value);


 * Attributes.SetString(P.alink, "Opt", "N")


 * ELSE


 * P.alink :&#61; 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 :&#61; NIL; P.linkkey :&#61; 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 :&#61; S.page;


 * IF on THEN


 * col :&#61; FALSE; size :&#61; GetFontSize(P);


 * WHILE NextAttr(S, attr) DO


 * IF attr &#61; "SIZE" THEN


 * i :&#61; 1;


 * IF S.value&#91;0&#93; &#61; "-" THEN


 * Strings.StrToIntPos(S.value, val, i);


 * size :&#61; size-SHORT(val)


 * ELSIF S.value&#91;0&#93; &#61; "+" THEN


 * Strings.StrToIntPos(S.value, val, i);


 * size :&#61; size+SHORT(val)


 * ELSE


 * i :&#61; 0;


 * Strings.StrToIntPos(S.value, val, i);


 * size :&#61; SHORT(val)


 * END


 * ELSIF attr &#61; "COLOR" THEN


 * Color(S.value, textC); col :&#61; 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 :&#61; 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 :&#61; S.page;


 * IF on THEN


 * style :&#61; NIL; PushTextAttrs(P);


 * IF &#126;P.left THEN


 * WriteLn(P); WriteLn(P)


 * ELSE


 * P.left :&#61; FALSE


 * END;


 * CASE ORD(S.value&#91;1&#93;)-ORD("0") OF


 * 1: Texts.SetFont(P.W, Syntax(20, 0X))


 * &#124;2: Texts.SetFont(P.W, Syntax(16, 0X))


 * &#124;3: Texts.SetFont(P.W, Syntax(16, "i"))


 * &#124;4: Texts.SetFont(P.W, Syntax(14, 0X))


 * &#124;5: Texts.SetFont(P.W, Syntax(14, "i"))


 * &#124;6: Texts.SetFont(P.W, Syntax(12, 0X))


 * ELSE


 * Texts.SetFont(P.W, Syntax(12, 0X))


 * END;


 * WHILE NextAttr(S, attr) DO


 * IF attr &#61; "ALIGN" THEN


 * style :&#61; TextAlign(S.value)


 * ELSIF attr &#61; "SRC" THEN


 * (* get image *)


 * ELSIF attr &#61; "DINGBAT" THEN


 * (* get icon with name value *)


 * END


 * END;


 * IF style # NIL THEN


 * P.style :&#61; style; WriteObj(P, P.style)


 * END


 * ELSE


 * CloseA(P); PopTextAttrs(P);


 * WriteLn(P); WriteLn(P)


 * END;


 * (*S.skipWS :&#61; TRUE*)


 * END H;


 * PROCEDURE HEAD(VAR S: Scanner; on: BOOLEAN);


 * (* ignore *)


 * END HEAD;


 * PROCEDURE HP(VAR S: Scanner; on: BOOLEAN);


 * (* 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 :&#61; S.page; CloseA(P);


 * w :&#61; dispW; h :&#61; 1;


 * WHILE NextAttr(S, attr) DO


 * IF attr &#61; "SIZE" THEN


 * Strings.StrToInt(S.value, l); h :&#61; SHORT(l)


 * ELSIF attr &#61; "WIDTH" THEN


 * pos :&#61; 0; Strings.StrToIntPos(S.value, l, pos);


 * w :&#61; SHORT(l);


 * IF S.value&#91;pos&#93; &#61; "%" THEN


 * w :&#61; SHORT( (LONG((5*dispW) DIV 8) * w) DIV LONG(100) )


 * END


 * END


 * END;


 * HorzRule(P, w, h)


 * END;


 * (*S.skipWS :&#61; TRUE*)


 * END HR;


 * PROCEDURE HTML(VAR S: Scanner; on: BOOLEAN);

(*


 * BEGIN


 * IF &#126;on THEN


 * Texts.WriteLn(S.page.W);


 * (*S.skipWS :&#61; TRUE; *)S.state :&#61; TextPlain


 * END *)


 * END HTML;


 * PROCEDURE HTTP(VAR S: Scanner; on: BOOLEAN);


 * (* ignore *)


 * END HTTP;


 * PROCEDURE I(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; 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 :&#61; 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 :&#61; S.page;


 * IF on THEN


 * IF P.lists # NIL THEN


 * INC(P.lists.itemNr);


 * IF P.lists.itemNr &#62; 1 THEN


 * WriteLn(P)


 * END;


 * FOR i :&#61; 1 TO P.lists.nesting DO


 * Texts.Write(P.W, Strings.Tab)


 * END


 * ELSE


 * WriteLn(P)


 * END;


 * IF (P.lists # NIL) &#38; (P.lists.kind &#61; OrderedList) THEN


 * Texts.WriteInt(P.W, P.lists.itemNr, 0)


 * ELSE


 * IF P.lists # NIL THEN


 * i :&#61; P.lists.nesting MOD LEN(bullets)


 * ELSE


 * i :&#61; 0


 * END;


 * PushTextAttrs(P);


 * Texts.SetFont(P.W, bullets&#91;i&#93;.f);


 * Texts.Write(P.W, bullets&#91;i&#93;.c);


 * PopTextAttrs(P)


 * END;


 * Texts.Write(P.W, Strings.Tab)


 * END;


 * P.blank :&#61; FALSE


 * (*S.skipWS :&#61; 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 :&#61; S.page; caption :&#61; ""; cmd :&#61; "";


 * WHILE NextAttr(S, attr) DO


 * IF attr &#61; "HREF" THEN


 * HREF(P, S.value, cmd, key);


 * IF key # HyperDocs.UndefKey THEN


 * cmd :&#61; "HyperDocs.FollowLink #Key"


 * END


 * ELSIF attr &#61; "REV" THEN


 * IF caption &#61; "" THEN


 * COPY(S.value, caption)


 * END


 * ELSIF attr &#61; "REL" THEN


 * IF caption &#61; "" THEN


 * COPY(S.value, caption)


 * END


 * ELSIF attr &#61; "TITLE" THEN


 * COPY(S.value, caption)


 * END


 * END;


 * IF cmd # "" THEN


 * obj :&#61; 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 :&#61; S.page;


 * IF on THEN


 * PushTextAttrs(P);


 * Texts.SetFont(P.W, mono);


 * S.pre :&#61; TRUE


 * ELSE


 * PopTextAttrs(P);


 * S.pre :&#61; 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 :&#61; S.page;


 * key :&#61; P.base.key; NewForm; form :&#61; Objects.NewObj(Form);


 * prompt :&#61; "This is a searchable index. Enter search keywords: ";


 * WHILE NextAttr(S, attr) DO


 * IF attr &#61; "PROMPT" THEN


 * COPY(S.value, prompt)


 * ELSIF attr &#61; "HREF" THEN


 * key :&#61; HyperDocs.BuildKey(P.base, S.value)


 * END


 * END;


 * WriteObj(P, form); HorzRule(P, dispW, 1);


 * Texts.WriteString(P.W, prompt);


 * objt :&#61; Gadgets.CreateObject("BasicGadgets.NewInteger");


 * Attributes.SetInt(objt, "Value", key);


 * AddFormObj(P, form, objt, "@ACTION", FALSE, FALSE);


 * objt :&#61; Gadgets.CreateObject("TextFields.NewTextField");


 * AddFormObj(P, form, objt, "QUERY", FALSE, TRUE);


 * objb :&#61; Gadgets.CreateObject("BasicGadgets.NewButton");


 * objb(Display.Frame).H :&#61; 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 :&#61; TRUE*)


 * END MENU;


 * PROCEDURE META(VAR S: Scanner; on: BOOLEAN);


 * (* ignore *)


 * END META;


 * PROCEDURE NEXTID(VAR S: Scanner; on: BOOLEAN);


 * (* ignore *)


 * END NEXTID;


 * PROCEDURE NOFRAMES(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; S.page;


 * HorzRule(P, dispW, 1)


 * END NOFRAMES;


 * PROCEDURE OL(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; S.page;


 * IF on THEN


 * OpenList(P, OrderedList)


 * ELSE


 * CloseList(P)


 * END;


 * (*S.skipWS :&#61; TRUE*)


 * END OL;


 * PROCEDURE P(VAR S: Scanner; on: BOOLEAN);


 * VAR


 * P: Page;


 * attr: ARRAY 64 OF CHAR;


 * style: TextGadgets.Style;


 * BEGIN


 * P :&#61; S.page;


 * IF on THEN


 * style :&#61; NIL; CloseA(P);


 * WriteLn(P); WriteLn(P); (*P.lines :&#61; 0;*)


 * WHILE NextAttr(S, attr) DO


 * IF attr &#61; "ALIGN" THEN


 * style :&#61; TextAlign(S.value)


 * END


 * END;


 * IF style # NIL THEN


 * PushTextAttrs(P);


 * P.style :&#61; style; WriteObj(P, P.style)


 * END


 * ELSE


 * PopTextAttrs(P)


 * END;


 * (*S.skipWS :&#61; TRUE*)


 * END P;


 * PROCEDURE PLAINTEXT(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; S.page;


 * IF on THEN


 * PushTextAttrs(P);


 * Texts.SetFont(P.W, mono);


 * S.state :&#61; TextPlain; S.pre :&#61; TRUE


 * ELSE


 * PopTextAttrs(P);


 * S.state :&#61; TextHtml; S.pre :&#61; FALSE


 * END


 * END PLAINTEXT;


 * PROCEDURE PRE(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; S.page;


 * IF on THEN


 * PushTextAttrs(P);


 * Texts.SetFont(P.W, mono);


 * S.pre :&#61; TRUE


 * ELSE


 * PopTextAttrs(P);


 * S.pre :&#61; FALSE


 * END


 * END PRE;


 * PROCEDURE Q(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; S.page;


 * IF on THEN


 * Texts.WriteString(P.W, "&#60;&#60;")


 * ELSE


 * Texts.WriteString(P.W, "&#62;&#62;")


 * END


 * END Q;


 * PROCEDURE Range(VAR S: Scanner; on: BOOLEAN);


 * (* ignore *)


 * END Range;


 * PROCEDURE SAMP(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; 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 :&#61; 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 ? *)


 * (* ignore *)


 * END STRIKE;


 * PROCEDURE STRONG(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; 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);


 * (* ignore *)


 * END STYLE;


 * PROCEDURE SUB(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; 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 :&#61; 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 :&#61; 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 :&#61; 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 :&#61; P.T.len


 * END


 * ELSIF &#126;on THEN


 * PushTextAttrs(P);


 * Texts.OpenWriter(P.W);


 * PopTextAttrs(P)


 * END


 * (*S.skipWS :&#61; TRUE*)


 * END TITLE;


 * PROCEDURE TT(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; 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 :&#61; S.page;


 * IF on THEN


 * Texts.Write(P.W, Strings.Tab);


 * style :&#61; NIL;


 * WHILE NextAttr(S, attr) DO


 * IF attr &#61; "ALIGN" THEN


 * style :&#61; TextAlign(S.value)


 * END


 * END;


 * IF style # NIL THEN


 * PushTextAttrs(P);


 * P.style :&#61; style; WriteObj(P, P.style)


 * END


 * ELSE


 * PopTextAttrs(P)


 * END;


 * P.blank :&#61; FALSE


 * (*S.skipWS :&#61; TRUE*)


 * END TAB;


 * PROCEDURE U(VAR S: Scanner; on: BOOLEAN);


 * (* underline text, how ? *)


 * (* 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 :&#61; TRUE*)


 * END UL;


 * PROCEDURE VARN(VAR S: Scanner; on: BOOLEAN);


 * VAR P: Page;


 * BEGIN


 * P :&#61; 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 :&#61; S.page;


 * IF on THEN


 * PushTextAttrs(P);


 * Texts.SetFont(P.W, mono);


 * S.pre :&#61; TRUE


 * ELSE


 * PopTextAttrs(P); S.pre :&#61; 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 &#126;S.end &#38; (S.ch &#60;&#61; " ") DO


 * Read(S)


 * END;


 * IF S.ch &#61; "!" THEN


 * prev :&#61; S.ch; Read(S);


 * IF S.ch &#61; "-" THEN


 * prev :&#61; S.ch; Read(S);


 * IF S.ch &#61; "-" THEN


 * WHILE &#126;S.end &#38; ((S.ch # "&#62;") OR ((prev # "-") &#38; (prev # "!"))) DO


 * IF S.ch &#62; " " THEN


 * prev :&#61; S.ch


 * END;


 * Read(S)


 * END


 * ELSE


 * WHILE &#126;S.end &#38; (S.ch # "&#62;") DO


 * Read(S)


 * END


 * END


 * ELSE


 * WHILE &#126;S.end &#38; (S.ch # "&#62;") DO


 * Read(S)


 * END


 * END;


 * Read(S)


 * ELSIF &#126;Strings.IsAlpha(S.ch) &#38; (S.ch # "/") THEN


 * Texts.Write(S.page.W, "&#60;")


 * ELSE


 * S.state :&#61; InTag; Next(S);


 * IF S.class &#61; OpenEndTag THEN


 * on :&#61; FALSE; Next(S)


 * ELSE


 * on :&#61; TRUE


 * END;


 * IF S.class &#61; Value THEN


 * Strings.Upper(S.value, S.value); found :&#61; TRUE;


 * CASE S.value&#91;0&#93; OF


 * "A": IF S.value&#91;1&#93; &#61; 0X THEN


 * A(S, on)


 * ELSIF (S.value &#61; "ADDR") OR (S.value &#61; "Address") THEN


 * Address(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"B": IF S.value&#91;1&#93; &#61; 0X THEN


 * B(S, on)


 * ELSIF S.value &#61; "BASE" THEN


 * BASE(S, on)


 * ELSIF S.value &#61; "BASEFONT" THEN


 * BASEFONT(S, on)


 * ELSIF S.value &#61; "BIG" THEN


 * BIG(S, on)


 * ELSIF S.value &#61; "BLINK" THEN


 * BLINK(S, on)


 * ELSIF (S.value &#61; "BQ") OR (S.value &#61; "BLOCKQUOTE") THEN


 * BLOCKQUOTE(S, on)


 * ELSIF S.value &#61; "BODY" THEN


 * BODY(S, on)


 * ELSIF S.value &#61; "BR" THEN


 * BR(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"C": IF S.value &#61; "CALL" THEN


 * CALL(S, on)


 * ELSIF S.value &#61; "CENTER" THEN


 * CENTER(S, on)


 * ELSIF S.value &#61; "CITE" THEN


 * CITE(S, on)


 * ELSIF S.value &#61; "CODE" THEN


 * CODEx(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"D": IF S.value &#61; "DD" THEN


 * DD(S, on)


 * ELSIF S.value &#61; "DFN" THEN


 * DFN(S, on)


 * ELSIF S.value &#61; "DIR" THEN


 * DIR(S, on)


 * ELSIF S.value &#61; "DIV" THEN


 * DIVI(S, on)


 * ELSIF S.value &#61; "DL" THEN


 * DL(S, on)


 * ELSIF S.value &#61; "DT" THEN


 * DT(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"E": IF S.value &#61; "EM" THEN


 * EM(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"F": IF S.value &#61; "FRAME" THEN


 * FRAME(S, on)


 * ELSIF S.value &#61; "FONT" THEN


 * FONT(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"G": IF S.value &#61; "GREEN" THEN


 * GREEN(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"H": IF (S.value &#61; "HEAD") OR (S.value &#61; "HEADER") THEN


 * HEAD(S, on)


 * ELSIF S.value &#61; "HP" THEN


 * HP(S, on)


 * ELSIF S.value &#61; "HR" THEN


 * HR(S, on)


 * ELSIF S.value &#61; "HTML" THEN


 * HTML(S, on)


 * ELSIF S.value &#61; "HTTP" THEN


 * HTTP(S, on)


 * ELSIF Strings.IsDigit(S.value&#91;1&#93;) THEN


 * H(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"I": IF S.value&#91;1&#93; &#61; 0X THEN


 * I(S, on)


 * ELSIF S.value &#61; "ISINDEX" THEN


 * ISINDEX(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"K": IF S.value &#61; "KBD" THEN


 * KBD(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"L": IF S.value &#61; "LI" THEN


 * LI(S, on)


 * ELSIF S.value &#61; "LINK" THEN


 * LINK(S, on)


 * ELSIF S.value &#61; "LISTING" THEN


 * LISTING(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"M": IF S.value &#61; "MENU" THEN


 * MENU(S, on)


 * ELSIF S.value &#61; "META" THEN


 * META(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"N": IF S.value &#61; "NEXTID" THEN


 * NEXTID(S, on)


 * ELSIF S.value &#61; "NOFRAMES" THEN


 * NOFRAMES(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"O": IF S.value &#61; "OL" THEN


 * OL(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"P": IF S.value&#91;1&#93; &#61; 0X THEN


 * P(S, on)


 * ELSIF S.value &#61; "PLAINTEXT" THEN


 * PLAINTEXT(S, on)


 * ELSIF S.value &#61; "PRE" THEN


 * PRE(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"Q": IF S.value&#91;1&#93; &#61; 0X THEN


 * Q(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"R": IF S.value &#61; "RANGE" THEN


 * Range(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"S": IF S.value&#91;1&#93; &#61; 0X THEN


 * STRIKE(S, on)


 * ELSIF S.value &#61; "SAMP" THEN


 * SAMP(S, on)


 * ELSIF S.value &#61; "STRIKE" THEN


 * STRIKE(S, on)


 * ELSIF S.value &#61; "STRONG" THEN


 * STRONG(S, on)


 * ELSIF S.value &#61; "STYLE" THEN


 * STYLE(S, on)


 * ELSIF S.value &#61; "SMALL" THEN


 * SMALL(S, on)


 * ELSIF S.value &#61; "SUB" THEN


 * SUB(S, on)


 * ELSIF S.value &#61; "SUP" THEN


 * SUP(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"T": IF S.value &#61; "TITLE" THEN


 * TITLE(S, on)


 * ELSIF S.value &#61; "TT" THEN


 * TT(S, on)


 * ELSIF S.value &#61; "TAB" THEN


 * TAB(S, on)


 * ELSIF S.value &#61; "TINY" THEN


 * TINY(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"U": IF S.value&#91;1&#93; &#61; 0X THEN


 * U(S, on)


 * ELSIF S.value &#61; "UL" THEN


 * UL(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"V": IF S.value &#61; "VAR" THEN


 * VARN(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * &#124;"X": IF S.value &#61; "XMP" THEN


 * XMP(S, on)


 * ELSE


 * found :&#61; FALSE


 * END


 * ELSE


 * found :&#61; FALSE


 * END;


 * IF &#126;found THEN


 * e :&#61; extTags;


 * WHILE (e # NIL) &#38; (e.tag # S.value) DO


 * e :&#61; e.next


 * END;


 * IF e &#61; NIL THEN


 * gen :&#61; "HTMLTags."; Strings.Append(gen, S.value);


 * Oberon.OpenScanner(Ss, gen);


 * IF Ss.class IN &#123;Texts.Name, Texts.String&#125; THEN


 * COPY(Ss.s, gen);


 * newTag :&#61; 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 :&#61; newTag; COPY(S.value, e.tag);


 * e.next :&#61; extTags; extTags :&#61; e;


 * e.start(S.page)


 * END


 * END


 * END;


 * IF e # NIL THEN


 * e.handle(S, on); found :&#61; TRUE


 * END


 * END;


 * WHILE &#126;S.end &#38; (S.class # CloseTag) DO


 * Next(S)


 * END;


 * IF &#126;found THEN


 * ELSE


 * END


 * END


 * END;


 * S.state :&#61; 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 &#61; Objects.get) &#38; (M.name &#61; "Gen") THEN


 * M.class :&#61; Objects.String; M.s :&#61; "HTMLDocs.NewDoc"; M.res :&#61; 0


 * ELSE


 * TextDocs.DocHandler(D, M)


 * END


 * END


 * ELSIF M IS Objects.LinkMsg THEN


 * WITH M: Objects.LinkMsg DO


 * IF M.id &#61; Objects.get THEN


 * IF M.name &#61; "DeskMenu" THEN


 * M.obj :&#61; Gadgets.CopyPublicObject("NetDocs.HTMLDeskMenu", TRUE);


 * IF M.obj &#61; NIL THEN M.obj :&#61; Desktops.NewMenu(Menu) END;


 * M.res :&#61; 0


 * ELSIF M.name &#61; "SystemMenu" THEN


 * M.obj :&#61; Gadgets.CopyPublicObject("NetDocs.HTMLSystemMenu", TRUE);


 * IF M.obj &#61; NIL THEN M.obj :&#61; Desktops.NewMenu(Menu) END;


 * M.res :&#61; 0


 * ELSIF M.name &#61; "UserMenu" THEN


 * M.obj :&#61; Gadgets.CopyPublicObject("NetDocs.HTMLUserMenu", TRUE);


 * IF M.obj &#61; NIL THEN M.obj :&#61; Desktops.NewMenu(Menu) END;


 * M.res :&#61; 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) &#62; 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 :&#61; P.task; CloseA(P);


 * e :&#61; extTags;


 * WHILE e # NIL DO


 * e.stop(P); e :&#61; 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 :&#61; NIL


 * END


 * END;


 * P.D.handle :&#61; 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, &#123;1&#125;, NIL, TextColor(P.textbackC), 0)


 * END;


 * IF (P.orgPos &#62; 0) &#38; (P.D.dsc(TextGadgets.Frame).org &#61; 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 :&#61; pages;


 * WHILE (p # NIL) &#38; (p.next # P) DO


 * p :&#61; p.next


 * END;


 * IF p # NIL THEN


 * p.next :&#61; P.next


 * ELSE


 * pages :&#61; P.next


 * END;


 * P.task :&#61; 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 :&#61; me.S.page;


 * IF me.S.state # End THEN


 * i :&#61; Input.Time; timeOut :&#61; i + Input.TimeUnit; me.time :&#61; i;


 * St :&#61; me.S.S; app :&#61; FALSE;


 * me.S.avail :&#61; St.Available(St); minSize :&#61; (*64*)1;	(* workaround for hang problem after NetSystem.State change *)


 * IF (me.S.avail &#60; minSize) &#38; (me.S.avail &#62; 0) THEN


 * IF St.State(St) &#61; Streams.closed THEN


 * minSize :&#61; me.S.avail


 * ELSIF &#126;St.buffer THEN


 * minSize :&#61; 1


 * END


 * END;


 * WHILE (me.S.avail &#62;&#61; minSize) &#38; (me.time &#60; timeOut) DO


 * app :&#61; TRUE;


 * CASE me.S.class OF


 * WhiteSpace: WriteSpace(P)


 * &#124;OpenTag: HandleTag(me.S)


 * &#124;CharRef: WriteCharRef(P, me.S); P.lines :&#61; 0; P.blank :&#61; TRUE


 * &#124;Character: Texts.Write(P.W, me.S.char); P.lines :&#61; 0; P.blank :&#61; TRUE


 * ELSE




 * END;


 * Next(me.S);


 * IF me.S.avail &#60;&#61; minSize THEN


 * me.S.avail :&#61; St.Available(St); me.time :&#61; Input.Time


 * END;


 * IF (me.S.avail &#60; minSize) &#38; (me.S.avail &#62; 0) THEN


 * IF St.State(St) &#61; Streams.closed THEN


 * minSize :&#61; me.S.avail


 * ELSIF &#126;St.buffer THEN


 * minSize :&#61; 1


 * END


 * END


 * END;


 * me.time :&#61; Input.Time; i :&#61; 0;


 * WHILE St.eos &#38; (me.S.state # End) &#38; (i &#60; 5) DO (* :-) *)


 * app :&#61; TRUE;


 * CASE me.S.class OF


 * WhiteSpace: WriteSpace(P)


 * &#124;OpenTag: HandleTag(me.S)


 * &#124;CharRef: WriteCharRef(P, me.S); P.lines :&#61; 0; P.blank :&#61; TRUE


 * &#124;Character: Texts.Write(P.W, me.S.char); P.lines :&#61; 0; P.blank :&#61; TRUE


 * ELSE




 * END;


 * Next(me.S); INC(i)


 * END;


 * me.S.avail :&#61; 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 &#61; End THEN


 * EndPage(P)


 * END


 * END


 * END ParseNext;

(* Query a string setting in the NetSystem section in Registry. *)


 * PROCEDURE QueryString(CONST key: ARRAY OF CHAR; VAR s: ARRAY OF CHAR): BOOLEAN;


 * VAR lKey: ARRAY 32 OF CHAR; S: Texts.Scanner;


 * BEGIN


 * lKey :&#61; "NetSystem."; Strings.Append(lKey, key);


 * Oberon.OpenScanner(S, lKey);


 * IF S.class IN &#123;Texts.Name, Texts.String&#125; THEN


 * COPY(S.s, s)


 * ELSE


 * COPY("", s)


 * END;


 * RETURN s # ""


 * END QueryString;


 * PROCEDURE QueryBool(CONST 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 :&#61; QueryBool("HTMLImages");


 * NEW(me);


 * IF (D.handle &#61; NIL) OR (D.Store &#61; NIL) THEN


 * TextDocs.InitDoc(D); D.W :&#61; HyperDocs.docW; D.H :&#61; HyperDocs.docH


 * END;


 * D.dsc(TextGadgets.Frame).do :&#61; HyperDocs.linkMethods;


 * NEW(me.P); me.P.D :&#61; D; NEW(me.P.T); Texts.Open(me.P.T, ""); me.P.alink :&#61; NIL; me.P.clink :&#61; NIL;


 * me.P.cacheSource :&#61; cache; NEW(me.P.source); Texts.Open(me.P.source, ""); Texts.OpenWriter(me.P.Ws);


 * me.P.next :&#61; pages; pages :&#61; me.P; me.P.task :&#61; me; me.P.docKey :&#61; basekey;


 * me.P.handle :&#61; Gadgets.objecthandle; me.P.blank :&#61; FALSE;


 * Links.SetLink(D.dsc, "Model", me.P.T); me.P.lines :&#61; 0; me.P.linkkey :&#61; HyperDocs.UndefKey;


 * me.P.textAttrs :&#61; NIL; Texts.OpenWriter(me.P.W); me.P.left :&#61; FALSE;


 * me.P.linkC :&#61; HyperDocs.linkC; me.P.oldLinkC :&#61; HyperDocs.oldLinkC;


 * me.P.textC :&#61; Display3.textC; me.P.textbackC :&#61; 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 :&#61; me.P; me.P.lists :&#61; NIL;


 * NEW(me.P.base); me.P.base.key :&#61; basekey; me.P.base.dockey :&#61; me.P.base.key;


 * me.P.style :&#61; NewStyle;


 * WriteObj(me.P, me.P.style); PopTextAttrs(me.P); me.P.orgPos :&#61; 0; me.P.head :&#61; head;


 * OpenScanner(me.S, S);


 * IF head THEN


 * Texts.WriteString(me.P.W, "URL: "); Texts.Write(me.P.W, 22X)


 * END;


 * s :&#61; HyperDocs.LinkSchemeByKey(me.P.base.key);


 * IF s &#61; NIL THEN


 * s :&#61; 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 &#61; "http" THEN


 * me.P.base.key :&#61; HTTPDocs0.SplitHTTPAdr(me.S.value, me.P.base.host, me.P.base.path, me.P.orgLabel, me.P.base.port);


 * i :&#61; 0;


 * WHILE me.S.value&#91;i&#93; # 0X DO


 * INC(i)


 * END;


 * WHILE (i &#62; 0) &#38; (me.S.value&#91;i&#93; # "/") DO


 * DEC(i)


 * END;


 * IF me.S.value&#91;i&#93; &#61; "/" THEN


 * me.S.value&#91;i+1&#93; :&#61; 0X


 * END


 * ELSIF me.P.base.prefix &#61; "file" THEN


 * HyperDocs.RetrieveLink(HyperDocs.loadingKey, me.S.value);


 * i :&#61; 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 :&#61; HyperDocs.SplitFileAdr(me.S.value, me.P.base.path, searchAttr);


 * me.P.base.host :&#61; ""; me.P.base.port :&#61; 0;


 * i :&#61; 0; j :&#61; 0;


 * WHILE me.P.base.path&#91;i&#93; # 0X DO


 * IF me.P.base.path&#91;i&#93; &#61; "/" THEN j :&#61; i+1 END;


 * INC(i)


 * END;


 * me.P.base.path&#91;j&#93; :&#61; 0X


 * ELSE (* proxy *)


 * me.P.orgLabel :&#61; "";


 * SplitHostPort(me.S.value, me.P.base.host, me.P.base.port);


 * IF me.P.base.prefix &#61; "ftp" THEN


 * i :&#61; 6;


 * WHILE (me.S.value&#91;i&#93; # 0X) &#38; (me.S.value&#91;i&#93; # "/") DO


 * INC(i)


 * END;


 * j :&#61; 0;


 * WHILE me.S.value&#91;i&#93; # 0X DO


 * me.P.base.path&#91;j&#93; :&#61; me.S.value&#91;i&#93;; INC(j); INC(i)


 * END;


 * IF j &#62; 0 THEN


 * me.P.base.path&#91;j&#93; :&#61; 0X


 * ELSE


 * me.P.base.path :&#61; "/"


 * END


 * ELSE


 * me.P.base.path :&#61; ""


 * 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 :&#61; extTags;


 * WHILE e # NIL DO


 * e.start(me.P); e :&#61; e.next


 * END;


 * me.P.D.handle :&#61; 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 :&#61; me.P.T.len;


 * me.time :&#61; Oberon.Time; me.safe :&#61; FALSE; me.handle :&#61; ParseNext; Oberon.Install(me);


 * IF blocking THEN


 * WHILE me.P.task &#61; me DO


 * me.handle(me)


 * END


 * END


 * END Parse;


 * PROCEDURE Show*;


 * VAR


 * T: Texts.Text;


 * D: Objects.Object;


 * BEGIN


 * T :&#61; Oberon.MarkedText;


 * IF T # NIL THEN


 * HTTPDocs0.curNode :&#61; NIL;


 * D :&#61; Gadgets.CreateObject("TextDocs.NewDoc");


 * WITH D: Documents.Document DO


 * D.W :&#61; HyperDocs.docW; D.H :&#61; HyperDocs.docH;


 * D.handle :&#61; 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 &#61; Attributes.Char) &#38; (S.c &#61; "*") THEN


 * doc :&#61; Desktops.CurDoc(Gadgets.context)


 * ELSE


 * doc :&#61; NIL; P :&#61; NIL


 * END;


 * IF doc # NIL THEN


 * HTTPDocs0.StopDoc(doc);


 * P :&#61; pages;


 * WHILE (P # NIL) &#38; (P.D # doc) DO


 * P :&#61; P.next


 * END;


 * IF P # NIL THEN


 * HTTPDocs0.StopDoc(P.D); EndPage(P)


 * END


 * ELSE


 * P :&#61; pages;


 * WHILE P # NIL DO


 * nextP :&#61; P.next;


 * HTTPDocs0.StopDoc(P.D); EndPage(P);


 * P :&#61; 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 :&#61; HyperDocs.BuildKey(NIL, D.name);


 * IF key # HyperDocs.UndefKey THEN


 * s :&#61; HyperDocs.LinkSchemeByKey(key);


 * IF s.prefix &#61; "file" THEN


 * IF HyperDocs.context # NIL THEN


 * HTTPDocs0.curNode :&#61; HyperDocs.context.new


 * ELSE


 * HyperDocs.Remember(key, NIL, HTTPDocs0.curNode)


 * END;


 * key :&#61; HyperDocs.SplitFileAdr(D.name, path, label);


 * NEW(T); Texts.Open(T, path);


 * TextDocs.InitDoc(D); D.W :&#61; HyperDocs.docW; D.H :&#61; HyperDocs.docH;


 * Parse(D, key, TextStreams.OpenReader(T, 0), TRUE, FALSE, FALSE);


 * IF (D # NIL) &#38; (D.dsc # NIL) THEN


 * IF HyperDocs.context &#61; NIL THEN


 * HyperDocs.LinkNodeToDoc(D, HTTPDocs0.curNode)


 * ELSE


 * HyperDocs.context.history :&#61; TRUE


 * END;


 * IF D.name &#61; "" THEN


 * HyperDocs.RetrieveLink(key, D.name)


 * END


 * END


 * ELSE

HALT(99)


 * END;


 * IF (HyperDocs.context # NIL) &#38; (HyperDocs.context.old # NIL) THEN


 * so :&#61; HyperDocs.LinkSchemeByKey(HyperDocs.context.old.key);


 * IF (so.prefix &#61; "file") OR (so.prefix &#61; "http") THEN


 * HyperDocs.context.replace :&#61; TRUE


 * END


 * END


 * END


 * END LoadDoc;


 * PROCEDURE NewDoc*;


 * VAR doc: Objects.Object;


 * BEGIN


 * doc :&#61; Gadgets.CreateObject("TextDocs.NewDoc");


 * WITH doc: Documents.Document DO


 * doc.W :&#61; HyperDocs.docW; doc.H :&#61; HyperDocs.docH;


 * doc.Load :&#61; LoadDoc


 * END;


 * doc.handle :&#61; DocHandler;


 * Objects.NewObj :&#61; doc


 * END NewDoc;


 * PROCEDURE InitEntities;


 * VAR i: Integer;


 * BEGIN


 * (* entities determines the map from indices, &#123;i&#125;, to entity names.


 * entityEncoding determines the map from &#123;i&#125; into the font.


 * Display of an entity requires availability in a font and correct encoding into the font. *)


 * i :&#61; 0;


 * (* Control codes


 * Desktops.OpenDoc "https://en.wikipedia.org/wiki/List_of_Unicode_characters#Control_codes" *)


 * entityEncoding&#91;i&#93; :&#61; Strings.Tab; entities&#91;i&#93; :&#61; "tab"; INC(i); (* U+09, Hoizontal tab *)


 * (* Basic Latin


 * Desktops.OpenDoc "https://en.wikipedia.org/wiki/List_of_Unicode_characters#Basic_Latin" *)


 * entityEncoding&#91;i&#93; :&#61; 22X; entities&#91;i&#93; :&#61; "quot"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 22X; entities&#91;i&#93; :&#61; "quote"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 26X; entities&#91;i&#93; :&#61; "amp"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 3CX; entities&#91;i&#93; :&#61; "lt"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 3EX; entities&#91;i&#93; :&#61; "gt"; INC(i);


 * (* Latin-1Supplement.


 * Desktops.OpenDoc "https://en.wikipedia.org/wiki/List_of_Unicode_characters#Latin-1_Supplement" *)


 * entityEncoding&#91;i&#93; :&#61; " "; entities&#91;i&#93; :&#61; "nbsp"; INC(i); (* U+00 *)


 * entityEncoding&#91;i&#93; :&#61; 0C0X; entities&#91;i&#93; :&#61; "Agrave"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0C1X; entities&#91;i&#93; :&#61; "Aacute"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0C2X; entities&#91;i&#93; :&#61; "Acirc"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0C3X; entities&#91;i&#93; :&#61; "Atilde"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0C4X; entities&#91;i&#93; :&#61; "Auml"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0C5X; entities&#91;i&#93; :&#61; "Aring"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0C6X; entities&#91;i&#93; :&#61; "AElig"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0C7X; entities&#91;i&#93; :&#61; "Ccedil"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0C8X; entities&#91;i&#93; :&#61; "Egrave"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0C9X; entities&#91;i&#93; :&#61; "Eacute"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0CAX; entities&#91;i&#93; :&#61; "Ecirc"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0CCX; entities&#91;i&#93; :&#61; "Euml"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0CDX; entities&#91;i&#93; :&#61; "Igrave"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0CEX; entities&#91;i&#93; :&#61; "Iacute"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0CFX; entities&#91;i&#93; :&#61; "Icirc"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0CBX; entities&#91;i&#93; :&#61; "Iuml"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0D0X; entities&#91;i&#93; :&#61; "ETH"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0D1X; entities&#91;i&#93; :&#61; "Ntilde"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0D2X; entities&#91;i&#93; :&#61; "Ograve"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0D3X; entities&#91;i&#93; :&#61; "Oacute"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0D4X; entities&#91;i&#93; :&#61; "Ocirc"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0D5X; entities&#91;i&#93; :&#61; "Otilde"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0D6X; entities&#91;i&#93; :&#61; "Ouml"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0D7X; entities&#91;i&#93; :&#61; "Oslash"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0D9X; entities&#91;i&#93; :&#61; "Ugrave"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0DAX; entities&#91;i&#93; :&#61; "Uacute"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0DBX; entities&#91;i&#93; :&#61; "Ucirc"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0DCX; entities&#91;i&#93; :&#61; "Uuml"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0DDX; entities&#91;i&#93; :&#61; "Yacute"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0DEX; entities&#91;i&#93; :&#61; "THORN"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0DFX; entities&#91;i&#93; :&#61; "szlig"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0E0X; entities&#91;i&#93; :&#61; "agrave"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0E1X; entities&#91;i&#93; :&#61; "aacute"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0E2X; entities&#91;i&#93; :&#61; "acirc"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0E3X; entities&#91;i&#93; :&#61; "atilde"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0E4X; entities&#91;i&#93; :&#61; "auml"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0E5X; entities&#91;i&#93; :&#61; "aring"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0E6X; entities&#91;i&#93; :&#61; "aelig"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0E7X; entities&#91;i&#93; :&#61; "ccedil"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0E8X; entities&#91;i&#93; :&#61; "egrave"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0E9X; entities&#91;i&#93; :&#61; "eacute"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0EAX; entities&#91;i&#93; :&#61; "ecirc"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0EBX; entities&#91;i&#93; :&#61; "euml"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0ECX; entities&#91;i&#93; :&#61; "igrave"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0EDX; entities&#91;i&#93; :&#61; "iacute"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0EEX; entities&#91;i&#93; :&#61; "icirc"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0EFX; entities&#91;i&#93; :&#61; "iuml"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0F0X; entities&#91;i&#93; :&#61; "eth"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0F1X; entities&#91;i&#93; :&#61; "ntilde"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0F2X; entities&#91;i&#93; :&#61; "ograve"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0F3X; entities&#91;i&#93; :&#61; "oacute"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0F4X; entities&#91;i&#93; :&#61; "ocirc"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0F5X; entities&#91;i&#93; :&#61; "otilde"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0F6X; entities&#91;i&#93; :&#61; "ouml"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0F7X; entities&#91;i&#93; :&#61; "oslash"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0F9X; entities&#91;i&#93; :&#61; "ugrave"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0FAX; entities&#91;i&#93; :&#61; "uacute"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0FBX; entities&#91;i&#93; :&#61; "ucirc"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0FCX; entities&#91;i&#93; :&#61; "uuml"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0FDX; entities&#91;i&#93; :&#61; "yacute"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0FEX; entities&#91;i&#93; :&#61; "thorn"; INC(i);


 * entityEncoding&#91;i&#93; :&#61; 0FFX; entities&#91;i&#93; :&#61; "yuml"; INC(i);


 * Texts.WriteInt(Wr, i, 0); Texts.WriteString(Wr, " character entities initialized."); Texts.WriteLn(Wr);


 * Texts.Append(Oberon.Log, Wr.buf)


 * 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 :&#61; Documents.MarkedDoc;


 * node :&#61; HyperDocs.NodeByDoc(D);


 * IF node # NIL THEN


 * Attributes.GetInt(D, "DocURL", key);


 * P :&#61; pages;


 * WHILE (P # NIL) &#38; (P.docKey # key) DO


 * P :&#61; P.next


 * END;


 * IF P # NIL THEN


 * Texts.Append(P.source, P.Ws.buf); T :&#61; P.source


 * ELSE


 * key :&#61; HTTPDocs0.StripLoc(key);


 * T :&#61; HyperDocs.GetCachedText(key)


 * END;


 * IF T # NIL THEN


 * Texts.OpenReader(R, T, 0);


 * Texts.Read(R, ch); pos :&#61; 0;


 * WHILE &#126;R.eot DO


 * IF ch &#61; 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 &#61; Attributes.Name) OR (S.class &#61; 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 &#61; Texts.Inval THEN


 * Texts.WriteString(Wr, "Oberon.Text - HTMLTags not found");


 * Texts.WriteLn(Wr); Texts.Append(Oberon.Log, Wr.buf)


 * END

END Check;

BEGIN


 * Texts.OpenWriter(Wr); Texts.OpenWriter(Wq);


 * imgs :&#61; TRUE; extTags :&#61; NIL; newTag :&#61; NIL;


 * pages :&#61; NIL; dispW :&#61; Display.Width;


 * COPY(GreekCap, GreekTab);


 * Strings.Append(GreekTab, GreekMin);


 * InitEntities; mono :&#61; Fonts.This("Courier10.Scn.Fnt");


 * IF ASCIIBullets THEN


 * bullets&#91;0&#93;.f :&#61; Fonts.This("Default12.Scn.Fnt"); bullets&#91;0&#93;.c :&#61; "*";


 * bullets&#91;1&#93;.f :&#61; bullets&#91;0&#93;.f; bullets&#91;1&#93;.c :&#61; "o"


 * ELSE


 * bullets&#91;0&#93;.f :&#61; Fonts.This("Default10.Scn.Fnt"); bullets&#91;0&#93;.c :&#61; CHR(29);


 * bullets&#91;1&#93;.f :&#61; bullets&#91;0&#93;.f; bullets&#91;1&#93;.c :&#61; CHR(28)


 * END;


 * Check

END HTMLDocs.