HtmlReader.pas
上传用户:yjb1804
上传日期:2021-01-30
资源大小:3105k
文件大小:18k
源码类别:

Email服务器

开发平台:

Delphi

  1. unit HtmlReader;
  2. interface
  3. uses
  4.   Classes, DomCore;
  5. type
  6.   TDelimiters = set of Byte;
  7.   TReaderState = (rsInitial, rsBeforeAttr, rsBeforeValue, rsInValue, rsInQuotedValue);
  8.   THtmlReader = class
  9.   private
  10.     FHtmlStr: TDomString;
  11.     FPosition: Integer;
  12.     FNodeType: Integer;
  13.     FPrefix: TDomString;
  14.     FLocalName: TDomString;
  15.     FNodeValue: TDomString;
  16.     FPublicID: TDomString;
  17.     FSystemID: TDomString;
  18.     FIsEmptyElement: Boolean;
  19.     FState: TReaderState;
  20.     FQuotation: Word;
  21.     FOnAttributeEnd: TNotifyEvent;
  22.     FOnAttributeStart: TNotifyEvent;
  23.     FOnCDataSection: TNotifyEvent;
  24.     FOnComment: TNotifyEvent;
  25.     FOnDocType: TNotifyEvent;
  26.     FOnElementEnd: TNotifyEvent;
  27.     FOnElementStart: TNotifyEvent;
  28.     FOnEndElement: TNotifyEvent;
  29.     FOnEntityReference: TNotifyEvent;
  30.     FOnNotation: TNotifyEvent;
  31.     FOnProcessingInstruction: TNotifyEvent;
  32.     FOnTextNode: TNotifyEvent;
  33.     function GetNodeName: TDomString;
  34.     function GetToken(Delimiters: TDelimiters): TDomString;
  35.     function IsAttrTextChar: Boolean;
  36.     function IsDigit(HexBase: Boolean): Boolean;
  37.     function IsEndEntityChar: Boolean;
  38.     function IsEntityChar: Boolean;
  39.     function IsEqualChar: Boolean;
  40.     function IsHexEntityChar: Boolean;
  41.     function IsNumericEntity: Boolean;
  42.     function IsQuotation: Boolean;
  43.     function IsSlashChar: Boolean;
  44.     function IsSpecialTagChar: Boolean;
  45.     function IsStartCharacterData: Boolean;
  46.     function IsStartComment: Boolean;
  47.     function IsStartDocumentType: Boolean;
  48.     function IsStartEntityChar: Boolean;
  49.     function IsStartMarkupChar: Boolean;
  50.     function IsStartTagChar: Boolean;
  51.     function Match(const Signature: TDomString; IgnoreCase: Boolean): Boolean;
  52.     function ReadAttrNode: Boolean;
  53.     function ReadAttrTextNode: Boolean;
  54.     function ReadCharacterData: Boolean;
  55.     function ReadComment: Boolean;
  56.     function ReadDocumentType: Boolean;
  57.     function ReadElementNode: Boolean;
  58.     function ReadEndElementNode: Boolean;
  59.     function ReadEntityNode: Boolean;
  60.     function ReadNamedEntityNode: Boolean;
  61.     function ReadNumericEntityNode: Boolean;
  62.     function ReadQuotedValue(var Value: TDomString): Boolean;
  63.     function ReadSpecialNode: Boolean;
  64.     function ReadTagNode: Boolean;
  65.     function ReadValueNode: Boolean;
  66.     function SkipTo(const Signature: TDomString): Boolean;
  67.     procedure FireEvent(Event: TNotifyEvent);
  68.     procedure ReadElementTail;
  69.     procedure ReadTextNode;
  70.     procedure SetHtmlStr(const Value: TDomString);
  71.     procedure SetNodeName(Value: TDomString);
  72.     procedure SkipWhiteSpaces;
  73.   public
  74.     constructor Create;
  75.     function read: Boolean;
  76.     property htmlStr: TDomString read FHtmlStr write SetHtmlStr;
  77.     property isEmptyElement: Boolean read FIsEmptyElement;
  78.     property localName: TDomString read FLocalName;
  79.     property nodeName: TDomString read GetNodeName;
  80.     property nodeType: Integer read FNodeType;
  81.     property position: Integer read FPosition;
  82.     property prefix: TDomString read FPrefix;
  83.     property publicID: TDomString read FPublicID;
  84.     property state: TReaderState read FState;
  85.     property systemID: TDomString read FSystemID;
  86.     property nodeValue: TDomString read FNodeValue;
  87.     property OnAttributeEnd: TNotifyEvent read FOnAttributeEnd write FOnAttributeEnd;
  88.     property OnAttributeStart: TNotifyEvent read FOnAttributeStart write FOnAttributeStart;
  89.     property OnCDataSection: TNotifyEvent read FOnCDataSection write FOnCDataSection;
  90.     property OnComment: TNotifyEvent read FOnComment write FOnComment;
  91.     property OnDocType: TNotifyEvent read FOnDocType write FOnDocType;
  92.     property OnElementEnd: TNotifyEvent read FOnElementEnd write FOnElementEnd;
  93.     property OnElementStart: TNotifyEvent read FOnElementStart write FOnElementStart;
  94.     property OnEndElement: TNotifyEvent read FOnEndElement write FOnEndElement;
  95.     property OnEntityReference: TNotifyEvent read FOnEntityReference write FOnEntityReference;
  96.     property OnNotation: TNotifyEvent read FOnNotation write FOnNotation;
  97.     property OnProcessingInstruction: TNotifyEvent read FOnProcessingInstruction write FOnProcessingInstruction;
  98.     property OnTextNode: TNotifyEvent read FOnTextNode write FOnTextNode;
  99.   end;
  100. implementation
  101. uses
  102.   SysUtils;
  103. const
  104.   startTagChar = Ord('<');
  105.   endTagChar = Ord('>');
  106.   specialTagChar = Ord('!');
  107.   slashChar = Ord('/');
  108.   equalChar = Ord('=');
  109.   quotation = [Ord(''''), Ord('"')];
  110.   tagDelimiter = [slashChar, endTagChar];
  111.   tagNameDelimiter = whiteSpace + tagDelimiter;
  112.   attrNameDelimiter = tagNameDelimiter + [equalChar];
  113.   startEntity = Ord('&');
  114.   startMarkup = [startTagChar, startEntity];
  115.   endEntity = Ord(';');
  116.   notEntity = [endEntity] + startMarkup + whiteSpace;
  117.   notAttrText = whiteSpace + quotation + tagDelimiter;
  118.   numericEntity = Ord('#');
  119.   hexEntity = [Ord('x'), Ord('X')];
  120.   decDigit = [Ord('0')..Ord('9')];
  121.   hexDigit = [Ord('a')..Ord('f'), Ord('A')..Ord('F')];
  122.   DocTypeStartStr = 'DOCTYPE';
  123.   DocTypeEndStr = '>';
  124.   CDataStartStr = '[CDATA[';
  125.   CDataEndStr = ']]>';
  126.   CommentStartStr = '--';
  127.   CommentEndStr = '-->';
  128. function DecValue(const Digit: WideChar): Word;
  129. begin
  130.   Result := Ord(Digit) - Ord('0')
  131. end;
  132. function HexValue(const HexChar: WideChar): Word;
  133. var
  134.   C: Char;
  135. begin
  136.   if Ord(HexChar) in decDigit then
  137.     Result := Ord(HexChar) - Ord('0')
  138.   else
  139.   begin
  140.     C := UpCase(Chr(Ord(HexChar)));
  141.     Result := Ord(C) - Ord('A')
  142.   end
  143. end;
  144. constructor THtmlReader.Create;
  145. begin
  146.   inherited Create;
  147.   FHtmlStr := HtmlStr;
  148.   FPosition := 1
  149. end;
  150. function THtmlReader.GetNodeName: TDomString;
  151. begin
  152.   if FPrefix <> '' then
  153.     Result := FPrefix + ':' + FLocalName
  154.   else
  155.     Result := FLocalName
  156. end;
  157. function THtmlReader.GetToken(Delimiters: TDelimiters): TDomString;
  158. var
  159.   Start: Integer;
  160. begin
  161.   Start := FPosition;
  162.   while (FPosition <= Length(FHtmlStr)) and not (Ord(FHtmlStr[FPosition]) in Delimiters) do
  163.     Inc(FPosition);
  164.   Result := Copy(FHtmlStr, Start, FPosition - Start)
  165. end;
  166. function THtmlReader.IsAttrTextChar: Boolean;
  167. var
  168.   WC: WideChar;
  169. begin
  170.   WC := FHtmlStr[FPosition];
  171.   if FState = rsInQuotedValue then
  172.     Result := (Ord(WC) <> FQuotation) and (Ord(WC) <> startEntity)
  173.   else
  174.     Result := not (Ord(WC) in notAttrText)
  175. end;
  176. function THtmlReader.IsDigit(HexBase: Boolean): Boolean;
  177. var
  178.   WC: WideChar;
  179. begin
  180.   WC := FHtmlStr[FPosition];
  181.   Result := Ord(WC) in decDigit;
  182.   if not Result and HexBase then
  183.     Result := Ord(WC) in hexDigit
  184. end;
  185. function THtmlReader.IsEndEntityChar: Boolean;
  186. var
  187.   WC: WideChar;
  188. begin
  189.   WC := FHtmlStr[FPosition];
  190.   Result := Ord(WC) = endEntity
  191. end;
  192. function THtmlReader.IsEntityChar: Boolean;
  193. var
  194.   WC: WideChar;
  195. begin
  196.   WC := FHtmlStr[FPosition];
  197.   Result := not (Ord(WC) in notEntity)
  198. end;
  199. function THtmlReader.IsEqualChar: Boolean;
  200. var
  201.   WC: WideChar;
  202. begin
  203.   WC := FHtmlStr[FPosition];
  204.   Result := Ord(WC) = equalChar
  205. end;
  206. function THtmlReader.IsHexEntityChar: Boolean;
  207. var
  208.   WC: WideChar;
  209. begin
  210.   WC := FHtmlStr[FPosition];
  211.   Result := Ord(WC) in hexEntity
  212. end;
  213. function THtmlReader.IsNumericEntity: Boolean;
  214. var
  215.   WC: WideChar;
  216. begin
  217.   WC := FHtmlStr[FPosition];
  218.   Result := Ord(WC) = numericEntity
  219. end;
  220. function THtmlReader.IsQuotation: Boolean;
  221. var
  222.   WC: WideChar;
  223. begin
  224.   WC := FHtmlStr[FPosition];
  225.   if FQuotation = 0 then
  226.     Result := Ord(WC) in quotation
  227.   else
  228.     Result := Ord(WC) = FQuotation
  229. end;
  230. function THtmlReader.IsSlashChar: Boolean;
  231. var
  232.   WC: WideChar;
  233. begin
  234.   WC := FHtmlStr[FPosition];
  235.   Result := Ord(WC) = slashChar
  236. end;
  237. function THtmlReader.IsSpecialTagChar: Boolean;
  238. var
  239.   WC: WideChar;
  240. begin
  241.   WC := FHtmlStr[FPosition];
  242.   Result := Ord(WC) = specialTagChar
  243. end;
  244. function THtmlReader.IsStartCharacterData: Boolean;
  245. begin    
  246.   Result := Match(CDataStartStr, false)
  247. end;
  248. function THtmlReader.IsStartComment: Boolean;
  249. begin
  250.   Result := Match(CommentStartStr, false)
  251. end;
  252. function THtmlReader.IsStartDocumentType: Boolean;
  253. begin
  254.   Result := Match(DocTypeStartStr, true)
  255. end;
  256. function THtmlReader.IsStartEntityChar: Boolean;
  257. var
  258.   WC: WideChar;
  259. begin
  260.   WC := FHtmlStr[FPosition];
  261.   Result := Ord(WC) = startEntity
  262. end;
  263. function THtmlReader.IsStartMarkupChar: Boolean;
  264. var
  265.   WC: WideChar;
  266. begin
  267.   WC := FHtmlStr[FPosition];
  268.   Result := Ord(WC) in startMarkup
  269. end;
  270. function THtmlReader.IsStartTagChar: Boolean;
  271. var
  272.   WC: WideChar;
  273. begin
  274.   WC := FHtmlStr[FPosition];
  275.   Result := Ord(WC) = startTagChar
  276. end;
  277. function THtmlReader.Match(const Signature: TDomString; IgnoreCase: Boolean): Boolean;
  278. var
  279.   I, J: Integer;
  280.   W1, W2: WideChar;
  281. begin
  282.   Result := false;
  283.   for I := 1 to Length(Signature) do
  284.   begin
  285.     J := FPosition + I - 1;
  286.     if (J < 1) or (J > Length(FHtmlStr)) then
  287.       Exit;
  288.     W1 := Signature[I];
  289.     W2 := FHtmlStr[J];
  290.     if (W1 <> W2) and (not IgnoreCase or (UpperCase(W1) <> UpperCase(W2))) then
  291.       Exit
  292.   end;
  293.   Result := true
  294. end;
  295. function THtmlReader.ReadAttrNode: Boolean;
  296. var
  297.   AttrName: TDomString;
  298. begin
  299.   Result := false;
  300.   SkipWhiteSpaces;
  301.   AttrName := LowerCase(GetToken(attrNameDelimiter));
  302.   if AttrName = '' then
  303.     Exit;
  304.   SetNodeName(AttrName);
  305.   FireEvent(FOnAttributeStart);
  306.   FState := rsBeforeValue;
  307.   FQuotation := 0;
  308.   Result := true
  309. end;
  310. function THtmlReader.ReadAttrTextNode: Boolean;
  311. var
  312.   Start: Integer;
  313. begin
  314.   Result := false;
  315.   Start := FPosition;
  316.   while (FPosition <= Length(FHtmlStr)) and IsAttrTextChar do
  317.     Inc(FPosition);
  318.   if FPosition = Start then
  319.     Exit;
  320.   FNodeType := TEXT_NODE;
  321.   FNodeValue:= Copy(FHtmlStr, Start, FPosition - Start);
  322.   FireEvent(FOnTextNode);
  323.   Result := true
  324. end;
  325. function THtmlReader.ReadCharacterData: Boolean;
  326. var
  327.   StartPos: Integer;
  328. begin
  329.   Inc(FPosition, Length(CDataStartStr));
  330.   StartPos := FPosition;
  331.   Result := SkipTo(CDataEndStr);
  332.   if Result then
  333.   begin
  334.     FNodeType := CDATA_SECTION_NODE;
  335.     FNodeValue := Copy(FHtmlStr, StartPos, FPosition - StartPos - Length(CDataEndStr));
  336.     FireEvent(FOnCDataSection)
  337.   end
  338. end;
  339. function THtmlReader.ReadComment: Boolean;
  340. var
  341.   StartPos: Integer;
  342. begin
  343.   Inc(FPosition, Length(CommentStartStr));
  344.   StartPos := FPosition;
  345.   Result := SkipTo(CommentEndStr);
  346.   if Result then
  347.   begin
  348.     FNodeType := COMMENT_NODE;
  349.     FNodeValue := Copy(FHtmlStr, StartPos, FPosition - StartPos - Length(CommentEndStr));
  350.     FireEvent(FOnComment)
  351.   end
  352. end;
  353. function THtmlReader.ReadDocumentType: Boolean;
  354. var
  355.   Name: TDomString;
  356. begin
  357.   Result := false;
  358.   Inc(FPosition, Length(DocTypeStartStr));
  359.   SkipWhiteSpaces;
  360.   Name := GetToken(tagNameDelimiter);
  361.   if Name = '' then
  362.     Exit;
  363.   SetNodeName(Name);
  364.   SkipWhiteSpaces;
  365.   GetToken(tagNameDelimiter);
  366.   SkipWhiteSpaces;
  367.   if not ReadQuotedValue(FPublicID) then
  368.     Exit;
  369.   SkipWhiteSpaces;
  370.   if FHtmlStr[FPosition] = '"' then
  371.   begin
  372.     if not ReadQuotedValue(FSystemID) then
  373.       Exit
  374.   end;
  375.   Result := SkipTo(DocTypeEndStr)
  376. end;
  377. function THtmlReader.ReadElementNode: Boolean;
  378. var
  379.   TagName: TDomString;
  380. begin
  381.   Result := false;
  382.   if FPosition < Length(FHtmlStr) then
  383.   begin
  384.     TagName := LowerCase(GetToken(tagNameDelimiter));
  385.     if TagName = '' then
  386.       Exit;
  387.     FNodeType := ELEMENT_NODE;
  388.     SetNodeName(TagName);
  389.     FState := rsBeforeAttr;
  390.     FireEvent(FOnElementStart);
  391.     Result := true
  392.   end
  393. end;
  394. function THtmlReader.ReadEndElementNode: Boolean;
  395. var
  396.   TagName: TDomString;
  397. begin
  398.   Result := false;
  399.   Inc(FPosition);
  400.   if FPosition > Length(FHtmlStr) then
  401.     Exit;
  402.   TagName := LowerCase(GetToken(tagNameDelimiter));
  403.   if TagName = '' then
  404.     Exit;
  405.   Result := SkipTo(WideChar(endTagChar));
  406.   if Result then
  407.   begin
  408.     FNodeType := END_ELEMENT_NODE;  
  409.     SetNodeName(TagName);
  410.     FireEvent(FOnEndElement);
  411.     Result := true
  412.   end
  413. end;
  414. function THtmlReader.ReadEntityNode: Boolean;
  415. var
  416.   CurrPos: Integer;
  417. begin
  418.   Result := false;
  419.   CurrPos := FPosition;
  420.   Inc(FPosition);
  421.   if FPosition > Length(FHtmlStr) then
  422.     Exit;
  423.   if IsNumericEntity then
  424.   begin
  425.     Inc(FPosition);
  426.     Result := ReadNumericEntityNode
  427.   end
  428.   else
  429.     Result := ReadNamedEntityNode;
  430.   if Result then
  431.   begin
  432.     FNodeType := ENTITY_REFERENCE_NODE;
  433.     //FireEvent(FOnEntityReference);  VVV - remove, entity node is added in ReadXXXEntityNode
  434.   end
  435.   else
  436.     FPosition := CurrPos
  437. end;
  438. function THtmlReader.ReadNamedEntityNode: Boolean;
  439. var
  440.   Start: Integer;
  441. begin    
  442.   Result := false;
  443.   if FPosition > Length(FHtmlStr) then
  444.     Exit;
  445.   Start := FPosition;
  446.   while (FPosition <= Length(FHtmlStr)) and IsEntityChar do
  447.     Inc(FPosition);
  448.   if (FPosition > Length(FHtmlStr)) or not IsEndEntityChar then
  449.     Exit;
  450.   FNodeType := ENTITY_REFERENCE_NODE;
  451.   SetNodeName(Copy(FHtmlStr, Start, FPosition - Start));
  452.   Inc(FPosition);
  453.   FireEvent(FOnEntityReference);
  454.   Result := true
  455. end;
  456. function THtmlReader.ReadNumericEntityNode: Boolean;
  457. var
  458.   Value: Word;
  459.   HexBase: Boolean;
  460. begin
  461.   Result := false;
  462.   if FPosition > Length(FHtmlStr) then
  463.     Exit;
  464.   HexBase := IsHexEntityChar;
  465.   if HexBase then
  466.     Inc(FPosition);
  467.   Value := 0;
  468.   while (FPosition <= Length(FHtmlStr)) and IsDigit(HexBase) do
  469.   begin
  470.     try
  471.       if HexBase then
  472.         Value := Value * 16 + HexValue(FHtmlStr[FPosition])
  473.       else
  474.         Value := Value * 10 + DecValue(FHtmlStr[FPosition])
  475.     except
  476.       Exit
  477.     end;
  478.     Inc(FPosition)
  479.   end;
  480.   if (FPosition > Length(FHtmlStr)) or not IsEndEntityChar then
  481.     Exit;
  482.   Inc(FPosition);
  483.   FNodeType := TEXT_NODE;
  484.   FNodeValue := WideChar(Value);
  485.   FireEvent(FOnTextNode);
  486.   Result := true
  487. end;
  488. function THtmlReader.ReadQuotedValue(var Value: TDomString): Boolean;
  489. var
  490.   QuotedChar: WideChar;
  491.   Start: Integer;
  492. begin
  493.   QuotedChar := FHtmlStr[FPosition];
  494.   Inc(FPosition);
  495.   Start := FPosition;
  496.   Result := SkipTo(QuotedChar);
  497.   if Result then
  498.     Value := Copy(FHtmlStr, Start, FPosition - Start)
  499. end;
  500. function THtmlReader.ReadSpecialNode: Boolean;
  501. begin
  502.   Result := false;
  503.   Inc(FPosition);
  504.   if FPosition > Length(FHtmlStr) then
  505.     Exit;
  506.   if IsStartDocumentType then
  507.     Result := ReadDocumentType
  508.   else
  509.   if IsStartCharacterData then
  510.     Result := ReadCharacterData
  511.   else
  512.   if IsStartComment then
  513.     Result := ReadComment
  514. end;
  515. function THtmlReader.ReadTagNode: Boolean;
  516. var
  517.   CurrPos: Integer;
  518. begin
  519.   Result := false;
  520.   CurrPos := FPosition;
  521.   Inc(FPosition);
  522.   if FPosition > Length(FHtmlStr) then
  523.     Exit;
  524.   if IsSlashChar then
  525.     Result := ReadEndElementNode
  526.   else
  527.   if IsSpecialTagChar then
  528.     Result := ReadSpecialNode
  529.   else
  530.     Result := ReadElementNode;
  531.   if not Result then
  532.     FPosition := CurrPos
  533. end;
  534. function THtmlReader.SkipTo(const Signature: TDomString): Boolean;
  535. begin
  536.   while FPosition <= Length(FHtmlStr) do
  537.   begin
  538.     if Match(Signature, false) then
  539.     begin
  540.       Inc(FPosition, Length(Signature));
  541.       Result := true;
  542.       Exit
  543.     end;
  544.     Inc(FPosition)
  545.   end;
  546.   Result := false
  547. end;
  548. procedure THtmlReader.FireEvent(Event: TNotifyEvent);
  549. begin
  550.   if Assigned(Event) then
  551.     Event(Self)
  552. end;
  553. function THtmlReader.read: Boolean;
  554. begin
  555.   FNodeType := NONE;
  556.   FPrefix := '';
  557.   FLocalName := '';
  558.   FNodeValue := '';
  559.   FPublicID := '';
  560.   FSystemID := '';
  561.   FIsEmptyElement := false;
  562.   Result := false;
  563.   if FPosition > Length(FHtmlStr) then
  564.     Exit;
  565.   Result := true;
  566.   if FState in [rsBeforeValue, rsInValue, rsInQuotedValue] then
  567.   begin
  568.     if ReadValueNode then
  569.       Exit;
  570.     if FState = rsInQuotedValue then
  571.       Inc(FPosition);
  572.     FNodeType := ATTRIBUTE_NODE;
  573.     FireEvent(FOnAttributeEnd);
  574.     FState := rsBeforeAttr
  575.   end
  576.   else
  577.   if FState = rsBeforeAttr then
  578.   begin
  579.     if ReadAttrNode then
  580.       Exit;
  581.     ReadElementTail;
  582.     FState := rsInitial;
  583.   end
  584.   else
  585.   if IsStartTagChar then
  586.   begin
  587.     if ReadTagNode then
  588.       Exit;
  589.     Inc(FPosition);
  590.     FNodeType := ENTITY_REFERENCE_NODE;
  591.     SetNodeName('lt');
  592.     FireEvent(FOnEntityReference);
  593.   end
  594.   else
  595.   if IsStartEntityChar then
  596.   begin
  597.     if ReadEntityNode then
  598.       Exit;
  599.     Inc(FPosition);
  600.     FNodeType := ENTITY_REFERENCE_NODE;
  601.     SetNodeName('amp');
  602.     FireEvent(FOnEntityReference)
  603.   end
  604.   else
  605.     ReadTextNode
  606. end;
  607. procedure THtmlReader.ReadTextNode;
  608. var
  609.   Start: Integer;
  610. begin
  611.   Start := FPosition;
  612.   repeat
  613.     Inc(FPosition)
  614.   until (FPosition > Length(FHtmlStr)) or IsStartMarkupChar;
  615.   FNodeType := TEXT_NODE;
  616.   FNodeValue:= Copy(FHtmlStr, Start, FPosition - Start);
  617.   FireEvent(FOnTextNode)
  618. end;
  619. function THtmlReader.ReadValueNode: Boolean;
  620. begin
  621.   Result := false;
  622.   if FState = rsBeforeValue then
  623.   begin
  624.     SkipWhiteSpaces;
  625.     if FPosition > Length(FHtmlStr) then
  626.       Exit;
  627.     if not IsEqualChar then
  628.       Exit;
  629.     Inc(FPosition);
  630.     SkipWhiteSpaces;
  631.     if FPosition > Length(FHtmlStr) then
  632.        Exit;
  633.     if IsQuotation then
  634.     begin
  635.       FQuotation := Ord(FHtmlStr[FPosition]);
  636.       Inc(FPosition);
  637.       FState := rsInQuotedValue
  638.     end
  639.     else
  640.       FState := rsInValue
  641.   end;
  642.   if FPosition > Length(FHtmlStr) then
  643.     Exit;
  644.   if IsStartEntityChar then
  645.   begin
  646.     Result := true;
  647.     if ReadEntityNode then
  648.       Exit;
  649.     Inc(FPosition);
  650.     FNodeType := ENTITY_REFERENCE_NODE;
  651.     SetNodeName('amp');
  652.     FireEvent(FOnEntityReference)
  653.   end
  654.   else
  655.     Result := ReadAttrTextNode
  656. end;
  657. procedure THtmlReader.ReadElementTail;
  658. begin
  659.   SkipWhiteSpaces;
  660.   if (FPosition <= Length(FHtmlStr)) and IsSlashChar then
  661.   begin
  662.     FIsEmptyElement := true;
  663.     Inc(FPosition)
  664.   end;
  665.   SkipTo(WideChar(endTagChar));
  666.     FNodeType := ELEMENT_NODE;
  667.   FireEvent(FOnElementEnd)
  668. end;
  669.                                  
  670. procedure THtmlReader.SetHtmlStr(const Value: TDomString);
  671. begin
  672.   FHtmlStr := Value;
  673.   FPosition := 1
  674. end;
  675. procedure THtmlReader.SetNodeName(Value: TDomString);
  676. var
  677.   I: Integer;
  678. begin
  679.   I := Pos(':', Value);
  680.   if I > 0 then
  681.   begin
  682.     FPrefix := Copy(Value, 1, I - 1);
  683.     FLocalName := Copy(Value, I + 1, Length(Value) - I)
  684.   end
  685.   else
  686.   begin
  687.     FPrefix := '';
  688.     FLocalName := Value
  689.   end
  690. end;
  691. procedure THtmlReader.SkipWhiteSpaces;
  692. begin
  693.   while (FPosition <= Length(FHtmlStr)) and (Ord(FHtmlStr[FPosition]) in whiteSpace) do
  694.     Inc(FPosition)
  695. end;
  696. end.