//*************************************************************
//                      EwbCoreTools                          *
//                                                            *
//                     Freeware Unit 			      *
//                       For Delphi                           *
//      Developing Team:                                      *
//          Serge Voloshenyuk (SergeV@bsalsa.com)             *
//          Eran Bodankin (bsalsa) -(bsalsa@gmail.com)       *
//                                                            *
//       Documentation and updated versions:                  *
//                                                            *
//               http://www.bsalsa.com                        *
//*************************************************************
{LICENSE:
THIS SOFTWARE IS PROVIDED TO YOU "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESSED OR IMPLIED INCLUDING BUT NOT LIMITED TO THE APPLIED
WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
YOU ASSUME THE ENTIRE RISK AS TO THE ACCURACY AND THE USE OF THE SOFTWARE
AND ALL OTHER RISK ARISING OUT OF THE USE OR PERFORMANCE OF THIS SOFTWARE
AND DOCUMENTATION. BSALSA PRODUCTIONS DOES NOT WARRANT THAT THE SOFTWARE IS ERROR-FREE
OR WILL OPERATE WITHOUT INTERRUPTION. THE SOFTWARE IS NOT DESIGNED, INTENDED
OR LICENSED FOR USE IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE CONTROLS,
INCLUDING WITHOUT LIMITATION, THE DESIGN, CONSTRUCTION, MAINTENANCE OR
OPERATION OF NUCLEAR FACILITIES, AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS,
AIR TRAFFIC CONTROL, AND LIFE SUPPORT OR WEAPONS SYSTEMS. BSALSA PRODUCTIONS SPECIFICALLY
DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR SUCH PURPOSE.

You may use/ change/ modify the component under 3 conditions:
1. In your website, add a link to "http://www.bsalsa.com"
2. In your application, add credits to "Embedded Web Browser"
3. Mail me  (bsalsa@gmail.com) any code change in the unit  for the benefit
   of the other users.
4. Please, consider donation in our web site!
{*******************************************************************************}


unit EwbCoreTools;

{$I EWB.inc}

interface

uses
  Graphics, ActiveX, Mshtml_Ewb, Windows, SysUtils;

function IsWinXPSP2OrLater(): Boolean;
function ColorToHTML(const Color: TColor): string;
function WideStringToLPOLESTR(const Source: Widestring): POleStr;
function XPath4Node(node: IHTMLElement): string;
function TaskAllocWideString(const S: string): PWChar;
function AnsiIndexStr(const AText: string; const AValues: array of string): Integer;
function GetPos(const SubSt, Text: string; StartPos: Integer = -1): Integer;
function _CharPos(const C: Char; const S: string): Integer;
function CutString(var Text: string; const Delimiter: string = ' ';
  const Remove: Boolean = True): string;
procedure FormatPath(Path: string);
function GetWinText(WinHandle: THandle): string;
function GetWinClass(Handle: Hwnd): WideString;
function GetParentWinByClass(ChildHandle: HWND; const ClassName: string): HWND;
{$IFDEF DELPHI5}
function DirectoryExists(const Directory: string): Boolean;
function VarSupports(const V: Variant; const IID: TGUID; out Intf): Boolean;
{$ENDIF}
{$IFNDEF DELPHI12_UP}
function CharInSet(C: Char; const CharSet: TSysCharSet): Boolean;
{$ENDIF}
function AddBackSlash(const S: string): string;

const
  WM_SETWBFOCUS = $0400 {WM_USER} + $44;

implementation

uses
  IeConst, EwbAcc;

type
   {VerifyVersion}
  fn_VerifyVersionInfo = function(var VersionInformation: OSVERSIONINFOEX;
    dwTypeMask: DWORD; dwlConditionMask: LONGLONG): BOOL; stdcall;
  fn_VerSetConditionMask = function(ConditionMask: LONGLONG; TypeMask: DWORD;
    Condition: Byte): LONGLONG; stdcall;


function IsWinXPSP2OrLater(): Boolean;
var
  osvi: TOSVersionInfoEx;
  dwlConditionMask: LONGLONG;
  op: Integer;
  hlib: THandle;
  VerifyVersionInfo: fn_VerifyVersionInfo;
  VerSetConditionMask: fn_VerSetConditionMask;
begin
  Result := False;
  hLib := GetModuleHandle('kernel32.dll');
  if hLib = 0 then
    hLib := LoadLibrary('kernel32.dll');
  if (hLib <> 0) then
  begin
    @VerifyVersionInfo := GetProcAddress(hLib, 'VerifyVersionInfoA');
    @VerSetConditionMask := GetProcAddress(hLib, 'VerSetConditionMask');
    if ((@VerifyVersionInfo = nil) or (@VerSetConditionMask = nil)) then Exit;

    dwlConditionMask := 0;
    op := VER_GREATER_EQUAL;

    // Initialize the OSVERSIONINFOEX structure.
    ZeroMemory(@osvi, SizeOf(OSVERSIONINFOEX));
    osvi.dwOSVersionInfoSize := SizeOf(OSVERSIONINFOEX);
    osvi.dwMajorVersion := 5;
    osvi.dwMinorVersion := 1;
    osvi.wServicePackMajor := 2;
    osvi.wServicePackMinor := 0;

    // Initialize the condition mask.
    dwlConditionMask := VerSetConditionMask(dwlConditionMask, VER_MAJORVERSION, op);
    dwlConditionMask := VerSetConditionMask(dwlConditionMask, VER_MINORVERSION, op);
    dwlConditionMask := VerSetConditionMask(dwlConditionMask, VER_SERVICEPACKMAJOR, op);
    dwlConditionMask := VerSetConditionMask(dwlConditionMask, VER_SERVICEPACKMINOR, op);

    // Perform the test.
    Result := VerifyVersionInfo(osvi, VER_MAJORVERSION or VER_MINORVERSION or
      VER_SERVICEPACKMAJOR or VER_SERVICEPACKMINOR, dwlConditionMask);
  end;
end;

function GetParentWinByClass(ChildHandle: HWND; const ClassName: string): HWND;
var
  szClass: array[0..255] of Char;
begin
  Result := GetParent(ChildHandle);
  while IsWindow(Result) do
  begin
    if (GetClassName(Result, szClass, SizeOf(szClass)) > 0) and
      (AnsiStrComp(PChar(ClassName), szClass) = 0) then Exit;
    Result := GetParent(Result);
  end;
end;



{$IFNDEF DELPHI12_UP}

function CharInSet(C: Char; const CharSet: TSysCharSet): Boolean;
begin
  Result := C in CharSet;
end;
{$ENDIF}

{$IFDEF DELPHI5}

function DirectoryExists(const Directory: string): Boolean;
var
  Code: Integer;
begin
{$RANGECHECKS OFF}
  Code := GetFileAttributes(PChar(Directory));
  Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
{$RANGECHECKS ON}
end;

function VarSupports(const V: Variant; const IID: TGUID; out Intf): Boolean;
begin
  Result := Supports(V, IID, Intf);
end;
{$ENDIF}

function AddBackSlash(const S: string): string;
begin
{$IFDEF DELPHI5}
  Result := IncludeTrailingBackslash(S);
{$ELSE}
{$IFDEF DELPHI6UP}
  Result := IncludeTrailingPathDelimiter(S);
{$ELSE}
  if Copy(S, Length(S), 1) = '\' then
    Result := S
  else
    Result := S + '\';
{$ENDIF}
{$ENDIF}
end;

function CutString(var Text: string; const Delimiter: string = ' ';
  const Remove: Boolean = True): string;
var
  IdxPos: Integer;
begin
  if Delimiter = #0 then
    IdxPos := Pos(Delimiter, Text)
  else
    IdxPos := AnsiPos(Delimiter, Text);

  if (IdxPos = 0) then
  begin
    Result := Text;
    if Remove then
      Text := '';
  end
  else
  begin
    Result := Copy(Text, 1, IdxPos - 1);
    if Remove then
      Delete(Text, 1, IdxPos + Length(Delimiter) - 1);
  end;
end;


function GetPos(const SubSt, Text: string; StartPos: Integer = -1): Integer;
var
  i: Integer;
  LStartPos: Integer;
  LTokenLen: Integer;
begin
  result := 0;
  LTokenLen := Length(SubSt);
  if StartPos = -1 then
  begin
    StartPos := Length(Text);
  end;
  if StartPos < (Length(Text) - LTokenLen + 1) then
  begin
    LStartPos := StartPos;
  end
  else
  begin
    LStartPos := (Length(Text) - LTokenLen + 1);
  end;
  for i := LStartPos downto 1 do
  begin
    if AnsiSameText(Copy(Text, i, LTokenLen), SubSt) then
    begin
      Result := i;
      Break;
    end;
  end;
end;

function _CharPos(const C: Char; const S: string): Integer;
begin
  for Result := 1 to Length(S) do
    if S[Result] = C then Exit;
  Result := 0;
end;

procedure FormatPath(Path: string);
var
  i: Integer;
begin
  i := 1;
  while i <= Length(Path) do
  begin
    if CharInSet(Path[i], LeadBytes) then
      Inc(i, 2)
    else
      if Path[i] = '\' then
      begin
        Path[i] := '/';
        Inc(i, 1);
      end
      else
        Inc(i, 1);
  end;
end;

function AnsiIndexStr(const AText: string; const AValues: array of string): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := Low(AValues) to High(AValues) do
    if AnsiSameStr(AText, AValues[I]) then
    begin
      Result := I;
      Break;
    end;
end;


function TaskAllocWideString(const S: string): PWChar;
var
  WideLength: integer;
  Wide: PWideChar;
begin
  WideLength := Length(S) + 1;
  Wide := CoTaskMemAlloc(WideLength * SizeOf(WideChar));
  StringToWideChar(S, Wide, WideLength);
  Result := Wide;
end;

{
function TaskAllocWideString(const S: string): PWChar;
var
  Len: Integer;
begin
  Len := Length(S) + 1;
  Result := CoTaskMemAlloc(2 * Len);
  StringToWideChar(S, Result, Len);
end;
}

function WideStringToLPOLESTR(const Source: Widestring): POleStr;
var
  Len: Integer;
begin
  Len := Length(Source) * SizeOf(WideChar);
  Result := CoTaskMemAlloc(Len + 2);
  FillChar(Result^, Len + 2, 0);
  Move(Result^, PWideString(Source)^, Len);
end;

function ColorToHTML(const Color: TColor): string;
var
  ColorRGB: LongWord;
begin
  ColorRGB := ColorToRGB(Color);
  FmtStr(Result, '#%0.2X%0.2X%0.2X',
    [Byte(ColorRGB), Byte(ColorRGB shr 8), Byte(ColorRGB shr 16)]);
end;

function GetWinText(WinHandle: THandle): string;
var
  DlgName: string;
  TxtLength: Integer;
begin
  TxtLength := GetWindowTextLength(WinHandle);
  SetLength(DlgName, TxtLength + 1);
  GetWindowText(WinHandle, PChar(DlgName), TxtLength + 1);
  Result := DlgName;
end;


function GetWinClass(Handle: Hwnd): WideString;
var
  pwc: PWideChar;
const
  maxbufsize = 32767 * SizeOf(WideChar);
begin
  Result := '';
  if IsWindow(Handle) then
  begin
    pwc := GetMemory(maxbufsize);
    if Assigned(pwc) then
    try
      ZeroMemory(pwc, maxbufsize);
      if GetClassnameW(Handle, pwc, maxbufsize) > 0 then
        SetString(Result, pwc, lstrlenW(pwc));
    finally
      FreeMemory(pwc);
    end;
  end;
end;

{
function GetWinClass(WinHandle: THANDLE): string;
begin
  SetLength(Result, 80);
  SetLength(Result, GetClassName(WinHandle, PChar(Result), Length(Result)));
end;
}


function XPath4Node(node: IHTMLElement): string;

  function NodePosition(elem: IHTMLElement): string;
  var tag: Widestring;
    Idx: Integer;
    n: IHTMLElement;
    cl: IHTMLElementCollection;
    itm: IDispatch;
    I, C, mI: Integer;
  begin
    Result := '';
    if (elem.parentElement = nil) or
      not Supports(elem.parentElement.children, IHTMLElementCollection, cl) then Exit;

    Tag := elem.tagName;
    Idx := elem.sourceIndex;
    C := 0;
    mI := -1;

    for I := 0 to cl.length - 1 do
    begin
      itm := cl.item(I, I);
      if Supports(itm, IHTMLElement, n) then
      begin
        if n.tagName = Tag then
        begin
          if n.sourceIndex = Idx then mI := C;
          Inc(C);
        end;
      end;
    end;
    if (mI > 0) or (C > 1) then Result := Format('[%d]', [mI]);
  end;

var id: string;
begin
  if node <> nil then
  begin
    id := node.id;
    if id <> '' then
      Result := Format('%s[@id="%s"]', [node.tagName, id])
    else if node.parentElement = nil then
      Result := '/' + node.tagName
    else Result := Format('%s/%s%s',
        [XPath4Node(node.parentElement), node.tagName, NodePosition(node)]);
  end else Result := '';
end;

end.
