unit fOMHTML;
{$OPTIMIZATION OFF}                              // REMOVE AFTER UNIT IS DEBUGGED
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  fOMAction, StdCtrls, OleCtrls, SHDocVw, MSHTML, activex, rOrders, uConst,
  ExtCtrls;
type
  TfrmOMHTML = class(TfrmOMAction)
    btnOK: TButton;
    btnCancel: TButton;
    btnBack: TButton;
    pnlWeb: TPanel;
    webView: TWebBrowser;
    btnShow: TButton;
    procedure btnOKClick(Sender: TObject);
    procedure btnCancelClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure webViewDocumentComplete(Sender: TObject;
      const pDisp: IDispatch; var URL: OleVariant);
    procedure webViewBeforeNavigate2(Sender: TObject;
      const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
      Headers: OleVariant; var Cancel: WordBool);
    procedure FormDestroy(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure btnBackClick(Sender: TObject);
    procedure btnShowClick(Sender: TObject);
  private
    FOwnedBy: TComponent;
    FRefNum:  Integer;
    FDialog:  Integer;
    FSetList: TStringList;
    FPageCache: TList;
    FCurrentIndex: Integer;
    FCurrentURL: string;
    FCurrentDoc: IHtmlDocument2;
    FDelayEvent: TOrderDelayEvent;
    FHistoryStack: TStringList;
    FHistoryIndex: Integer;
    function GetPageIndex(const URL: string): Integer;
    function MetaElementExists(const AName, AContent: string): Boolean;
    procedure AddPageToCache;
    procedure SaveState;
    procedure RestoreState;
    procedure SetDialog(Value: Integer);
  public
    procedure SetEventDelay(AnEvent: TOrderDelayEvent);
    property Dialog:  Integer     read FDialog  write SetDialog;
    property OwnedBy: TComponent  read FOwnedBy write FOwnedBy;
    property RefNum:  Integer     read FRefNum  write FRefNum;
    property SetList: TStringList read FSetList write FSetList;
  end;
var
  frmOMHTML: TfrmOMHTML;
implementation
{$R *.DFM}
uses ORFn, rCore, uCore, uOrders, ORNet, TRPCB, rMisc;
const
  TAB = #9;
type
  TPageState = class
  private
    FURL:        string;
    FTagStates:  TStringList;
    FSubmitData: TStringList;
  public
    constructor Create;
    destructor Destroy; override;
  end;
{ TPageState }
constructor TPageState.Create;
begin
  FTagStates  := TStringList.Create;
  FSubmitData := TStringList.Create;
end;
destructor TPageState.Destroy;
begin
  FTagStates.Free;
  FSubmitData.Free;
  inherited;
end;
{ temporary RPC's }
function GetIENforHtml(const AnID: string): Integer;
{AnID, O.name or O.ien for 101.41, H.name or H.ien for 101.14}
begin
  Result := StrToIntDef(sCallV('ORWDHTM GETIEN', [AnID]), 0);
end;
function GetHTMLText(AnIEN: Integer): string;
{return HTML text from 101.14 given IEN}
begin
  CallV('ORWDHTM HTML', [AnIEN, Patient.DFN]);
  Result := RPCBrokerV.Results.Text;
end;
function GetURLforDialog(AnIEN: Integer): string;
begin
  Result := sCallV('ORWDHTM URL', [AnIEN]);
  if Result = '' then Result := 'about:URL not found';
end;
procedure NameValueToViewList(Src, Dest: TStringList);
{ xform namevalue into DlgIEN^DlgType^DisplayName list }
var
  i: Integer;
  Subs: string;
begin
  RPCBrokerV.ClearParameters := True;
  RPCBrokerV.RemoteProcedure := 'ORWDHTM NV2DNM';
  RPCBrokerV.Param[0].PType := list;
  for i := 0 to Pred(Src.Count) do
  begin
    Subs := IntToStr(Succ(i));
    RPCBrokerV.Param[0].Mult[Subs] := Copy(Src[i], 1, 245);
  end; {for i}
  CallBroker;
  Dest.Assign(RPCBrokerV.Results);
end;
procedure NameValueToOrderSet(Src, Dest: TStringList);
{ xform namevalue into DlgIEN^DlgType^DisplayName list }
var
  i, j: Integer;
  Subs: string;
  WPText: TStringList;
begin
  RPCBrokerV.ClearParameters := True;
  RPCBrokerV.RemoteProcedure := 'ORWDHTM NV2SET';
  RPCBrokerV.Param[0].PType := list;
  WPText := TStringList.Create;
  for i := 0 to Pred(Src.Count) do
  begin
    WPText.Clear;
    WPText.Text := Copy(Src[i], Pos(TAB, Src[i]) + 1, Length(Src[i]));
    Subs := IntToStr(Succ(i));
    if WPText.Count = 1 then RPCBrokerV.Param[0].Mult[Subs] := Src[i] else
    begin
      RPCBrokerV.Param[0].Mult['"WP",' + Subs] :=
        Piece(Src[i], TAB, 1) + TAB + 'NMVAL("WP",' + Subs + ')';
      for j := 0 to Pred(WPText.Count) do
        RPCBrokerV.Param[0].Mult['"WP",' + Subs + ',' + IntToStr(Succ(j)) + ',0'] := WPText[j];
    end; {if WPText}
  end; {for i}
  CallBroker;
  WPText.Free;
  Dest.Assign(RPCBrokerV.Results);
end;
{ general procedures }
procedure TfrmOMHTML.SetEventDelay(AnEvent: TOrderDelayEvent);
begin
  FDelayEvent := AnEvent;
end;
function TfrmOMHTML.GetPageIndex(const URL: string): Integer;
var
  i: Integer;
begin
  Result := -1;
  for i := 0 to Pred(FPageCache.Count) do
    if TPageState(FPageCache[i]).FURL = URL then
    begin
      Result := i;
      break;
    end;
end;
function TfrmOMHTML.MetaElementExists(const AName, AContent: string): Boolean;
var
  i: Integer;
  AnElement: IHtmlElement;
  AllElements: IHtmlElementCollection;
begin
  Result := False;
  AllElements := FCurrentDoc.All;
  for i := 0 to Pred(AllElements.Length) do
  begin
    AnElement := AllElements.Item(i, 0) as IHtmlElement;
    if AnElement.tagName = 'META' then
      with AnElement as IHtmlMetaElement do
        if (CompareText(name, AName) = 0) and (CompareText(content, AContent) = 0)
           then Result := True;
    if Result then Break;
  end;
end;
procedure TfrmOMHTML.AddPageToCache;
var
  APageState: TPageState;
begin
  APageState := TPageState.Create;
  APageState.FURL := FCurrentURL;
  FCurrentIndex := FPageCache.Add(APageState);
end;
procedure TfrmOMHTML.SaveState;
var
  i: Integer;
  SelectName, State, NmVal, x: string;
  APageState: TPageState;
  AnElement: IHtmlElement;
  AnInput: IHtmlInputElement;
  ASelect: IHtmlSelectElement;
  AnOption: IHtmlOptionElement;
  ATextArea: IHtmlTextAreaElement;
  AllElements: IHtmlElementCollection;
begin
  if FCurrentIndex < 0 then Exit;
  Assert(Assigned(FCurrentDoc));
  APageState := FPageCache[FCurrentIndex];
  APageState.FTagStates.Clear;
  APageState.FSubmitData.Clear;
  if not MetaElementExists('VistAuse', 'ORWDSET') then Exit;
  AllElements := FCurrentDoc.All;
  for i := 0 to Pred(AllElements.Length) do
  begin
    AnElement := AllElements.Item(i, 0) as IHtmlElement;
    NmVal := '';
    State := '';
    if AnElement.tagName = 'INPUT' then
    begin
      AnInput := AnElement as IHtmlInputElement;
      if AnInput.type_ = 'checkbox' then
      begin
        if AnInput.checked then
        begin
          State := AnInput.name + TAB + '1';
          NmVal := AnInput.name + TAB + '1';
        end
        else State := AnInput.name + TAB + '0';
      end; {checkbox}
      if AnInput.type_ = 'radio' then
      begin
        if AnInput.checked then
        begin
          State := AnInput.name + AnInput.Value + TAB + '1';
          NmVal := AnInput.value + TAB + '1';
        end
        else State := AnInput.name + AnInput.Value + TAB + '0';
      end; {radio}
      if (AnInput.type_ = 'hidden') or (AnInput.type_ = 'password') or (AnInput.type_ = 'text') then
      begin
        State := AnInput.name + TAB + AnInput.value;
        NmVal := State;
      end; {hidden, password, text}
    end; {INPUT}
    if AnElement.tagname = 'SELECT' then
    begin
      ASelect := AnElement as IHtmlSelectElement;
      SelectName := ASelect.name;
    end; {SELECT}
    if AnElement.tagName = 'OPTION' then
    begin
      AnOption := AnElement as IHtmlOptionElement;
      x := AnOption.value;
      if x = '' then x := AnOption.text;
      if AnOption.Selected then
      begin
        State := SelectName + x + TAB + '1';
        NmVal := SelectName + TAB + x;
      end
      else State := SelectName + x + TAB + '0';
    end; {OPTION}
    if AnElement.tagName = 'TEXTAREA' then
    begin
      ATextArea := AnElement as IHtmlTextAreaElement;
      State := ATextArea.name + TAB + ATextArea.value;
      NmVal := State;
    end; {TEXTAREA}
    if Length(State) > 0 then APageState.FTagStates.Add(State);
    if Length(NmVal) > 0 then APageState.FSubmitData.Add(NmVal);
  end; {for i}
end;
procedure TfrmOMHTML.RestoreState;
var
  i: Integer;
  SelectName, x: string;
  APageState: TPageState;
  AnElement: IHtmlElement;
  AnInput: IHtmlInputElement;
  ASelect: IHtmlSelectElement;
  AnOption: IHtmlOptionElement;
  ATextArea: IHtmlTextAreaElement;
  AllElements: IHtmlElementCollection;
  function GetStateFromName(const AName: string): string;
  var
    i: Integer;
  begin
    Result := '';
    for i := 0 to Pred(APageState.FTagStates.Count) do
    begin
      if Piece(APageState.FTagStates[i], TAB, 1) = AName then
      begin
        Result := Piece(APageState.FTagStates[i], TAB, 2);
        Break;
      end; {if Piece}
    end; {for i}
  end; {GetStateFromName}
begin
  APageState := TPageState(FPageCache.Items[FCurrentIndex]);
  if APageState.FTagStates.Count = 0 then Exit;
  AllElements := FCurrentDoc.All;
  for i := 0 to Pred(AllElements.Length) do
  begin
    AnElement := AllElements.Item(i, 0) as IHtmlElement;
    if AnElement.tagName = 'INPUT' then
    begin
      AnInput := AnElement as IHtmlInputElement;
      if AnInput.type_ = 'checkbox'
        then AnInput.Set_checked(GetStateFromName(AnInput.name) = '1');
      if AnInput.Type_ = 'radio'
        then AnInput.Set_checked(GetStateFromName(AnInput.name + AnInput.Value) = '1');
      if (AnInput.type_ = 'hidden') or (AnInput.type_ = 'password') or (AnInput.type_ = 'text')
        then AnInput.Set_value(GetStateFromName(AnInput.name));
    end; {INPUT}
    if AnElement.tagname = 'SELECT' then
    begin
      ASelect := AnElement as IHtmlSelectElement;
      SelectName := ASelect.name;
    end; {SELECT}
    if AnElement.tagName = 'OPTION' then
    begin
      AnOption := AnElement as IHtmlOptionElement;
      x := AnOption.value;
      if x = '' then x := AnOption.text;
      AnOption.Set_selected(GetStateFromName(SelectName + x) = '1');
    end; {OPTION}
    if AnElement.tagName = 'TEXTAREA' then
    begin
      ATextArea := AnElement as IHtmlTextAreaElement;
      ATextArea.Set_value(GetStateFromName(ATextArea.name));
    end; {TEXTAREA}
  end; {for i}
end;
procedure TfrmOMHTML.SetDialog(Value: Integer);
begin
  FDialog := Value;
  webView.Navigate(GetURLForDialog(FDialog));
end;
{ Form events (get the initial page loaded) }
procedure TfrmOMHTML.FormCreate(Sender: TObject);
begin
  AutoSizeDisabled := True;
  inherited;
  FPageCache := TList.Create;
  FSetList := TStringList.Create;
  FHistoryStack := TStringList.Create;
  FHistoryIndex := -1;
  FCurrentIndex := -1;
end;
procedure TfrmOMHTML.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  inherited;
  SaveUserBounds(Self);
  if (FOwnedBy <> nil) and (FOwnedBy is TWinControl)
      then SendMessage(TWinControl(FOwnedBy).Handle, UM_DESTROY, FRefNum, 0);
end;
procedure TfrmOMHTML.FormDestroy(Sender: TObject);
var
  i: Integer;
begin
  for i := Pred(FPageCache.Count) downto 0 do TPageState(FPageCache[i]).Free;
  DestroyingOrderHTML;
  FSetList.Free;
  FHistoryStack.Free;
  inherited;
end;
{ webBrowser events }
procedure TfrmOMHTML.webViewDocumentComplete(Sender: TObject; const pDisp: IDispatch;
  var URL: OleVariant);
{ This event happens after a navigation.  It is at this point that there is an instantiated
  instance of IHtmlDocument available. }
begin
  inherited;
  if not Assigned(webView.Document) then Exit;
  FCurrentDoc := webView.Document as IHtmlDocument2;
  FCurrentURL := URL;
  FHistoryStack.Add(FCurrentURL);
  btnBack.Enabled := FHistoryStack.Count > 1;
  FCurrentIndex := GetPageIndex(FCurrentURL);
  if FCurrentIndex >= 0 then RestoreState else AddPageToCache;
end;
function CopyToCtrlChar(const Src: string; StartAt: Integer): string;
var
  i: Integer;
begin
  Result := '';
  if StartAt < 1 then StartAt := 1;
  for i := StartAt to Length(Src) do
    if Ord(Src[i]) > 31 then Result := Result + Src[i] else break;
end;
procedure TfrmOMHTML.webViewBeforeNavigate2(Sender: TObject;  const pDisp: IDispatch;
  var URL, Flags, TargetFrameName, PostData, Headers: OleVariant; var Cancel: WordBool);
begin
  inherited;
  SaveState;
  // activate order dialog here, i.e., 'about:CPRSOrder=FHW1'
end;
{ button events }
procedure TfrmOMHTML.btnOKClick(Sender: TObject);
var
  i, j: Integer;
  APageState: TPageState;
begin
  inherited;
  SaveState;
  // create an order set based on all the saved states of pages navigated to
  for i := 0 to Pred(FPageCache.Count) do
  begin
    APageState := FPageCache[i];
    for j := 0 to Pred(APageState.FSubmitData.Count) do
    begin
      FSetList.Add(APageState.FSubmitData[j]);
    end;
  end;
  NameValueToOrderSet(FSetList, FSetList);
  // put in reference number, key variables, & caption later as necessary
  //ActivateOrderList(NameValuePairs, FDelayEvent, Self, 0, '', '');
  Close;
end;
procedure TfrmOMHTML.btnCancelClick(Sender: TObject);
begin
  inherited;
  Close;
end;
procedure TfrmOMHTML.btnBackClick(Sender: TObject);
var
  BackURL: string;
begin
  inherited;
  if FHistoryStack.Count > 1 then
  begin
    FHistoryStack.Delete(Pred(FHistoryStack.Count));
    BackURL := FHistoryStack[Pred(FHistoryStack.Count)];
    FHistoryStack.Delete(Pred(FHistoryStack.Count));
    if FHistoryStack.Count < 2 then btnBack.Enabled := False;
    webView.Navigate(BackURL);
  end;
end;
procedure TfrmOMHTML.btnShowClick(Sender: TObject);
var
  i, j: Integer;
  APageState: TPageState;
  tmpList: TStringList;
begin
  inherited;
  SaveState;
  tmpList := TStringList.Create;
  // create an order set based on all the saved states of pages navigated to
  for i := 0 to Pred(FPageCache.Count) do
  begin
    APageState := FPageCache[i];
    for j := 0 to Pred(APageState.FSubmitData.Count) do
    begin
      tmpList.Add(APageState.FSubmitData[j]);
    end;
  end;
  NameValueToViewList(tmpList, tmpList);
  InfoBox(tmpList.Text, 'Current Selections', MB_OK);
  tmpList.Free;
end;
end.