فهرست منبع

* Demo for wasm debug object inspector

Michaël Van Canneyt 1 سال پیش
والد
کامیت
4eebcb7f5f

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
demo/wasienv/wasm-oi/bulma.min.css


+ 43 - 0
demo/wasienv/wasm-oi/index.html

@@ -0,0 +1,43 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+  <title>Project1</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <link href="bulma.min.css" rel="stylesheet">
+  <link href="oistyles.css" rel="stylesheet">
+  <script src="wasmhost.js"></script>
+</head>
+<body>
+  <div class="container">
+    <h1 class="title is-3">WebAssembly Object Inspector Demo</h1>
+    <div class="columns">
+      <div class="column">
+        <h1 class="title is-5">Webassembly console:</h1>
+        <div id="pasjsconsole" style="min-height: 480px; min-width: 640px;">
+        </div>
+        <div class="control">
+          <label class="checkbox">
+            <input id="cbconsole" type="checkbox" autocomplete="off" checked>Show console output
+          </label>
+        </div>
+      </div>
+      <div class="column" id="divInspector">
+        <h1 class="title is-5">Object inspector:</h1>
+        <div class="box  columns">
+           <div class="column">
+             <div id="Tree"></div>
+           </div>
+           <div class="column">
+             <div id="Inspector"></div>
+           </div>
+         </div>
+      </div>
+    </div>
+  </div>
+  <script>
+    rtl.showUncaughtExceptions=true;
+    window.addEventListener("load", rtl.run);
+  </script>
+</body>
+</html>

+ 69 - 0
demo/wasienv/wasm-oi/oidemo.lpi

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="oidemo"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <BuildModes>
+      <Item Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+    </RunParams>
+    <Units>
+      <Unit>
+        <Filename Value="oidemo.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target>
+      <Filename Value="oidemo.wasm"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+    <CodeGeneration>
+      <TargetCPU Value="wasm32"/>
+      <TargetOS Value="wasi"/>
+      <Subtarget Value="browser"/>
+    </CodeGeneration>
+    <Linking>
+      <Debugging>
+        <GenerateDebugInfo Value="False"/>
+      </Debugging>
+      <Options>
+        <ExecutableType Value="Library"/>
+      </Options>
+    </Linking>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 253 - 0
demo/wasienv/wasm-oi/oidemo.lpr

@@ -0,0 +1,253 @@
+library oidemo;
+
+uses sysutils, classes, rtti, wasm.debuginspector.rtti;
+
+{$RTTI INHERIT
+   METHODS(DefaultMethodRttiVisibility)
+   FIELDS(DefaultFieldRttiVisibility)
+   PROPERTIES(DefaultPropertyRttiVisibility)
+}
+
+Type
+
+  { TControl }
+  TFontStyle = (fsBold,fsItalic,fsUnderline,fsStrikeThrough);
+  TFontStyles = set of TFontStyle;
+
+  { TFont }
+
+  TFont = Class(TPersistent)
+  private
+    FName: String;
+    FSize: Integer;
+    FStyle: TFontStyles;
+  Public
+    procedure Assign(Source: TPersistent); override;
+
+  Published
+    Property Name : String Read FName Write FName;
+    Property Size : Integer Read FSize Write FSize;
+    Property Style : TFontStyles Read FStyle Write FStyle;
+  end;
+
+  TAlign = (alNone,alClient,alLeft,alTop,alRight,alBottom);
+
+  TControl = class(TComponent)
+  private
+    FAlign: TAlign;
+    FFocused: Boolean;
+    FHeight: Integer;
+    FLeft: Integer;
+    FOnEnter: TNotifyEvent;
+    FOnExit: TNotifyEvent;
+    FTop: Integer;
+    FVisible: Boolean;
+    FWidth: Integer;
+    function GetParent: TControl;
+    function GetRect: TRect;
+  Protected
+    Property Focused : Boolean Read FFocused Write FFocused;
+  Public
+    Property BoundsRect : TRect Read GetRect;
+    Property Parent : TControl Read GetParent;
+  Published
+    Property OnEnter : TNotifyEvent Read FOnEnter Write FOnEnter;
+    Property OnExit : TNotifyEvent Read FOnExit Write FOnExit;
+    Property Align : TAlign Read FAlign Write FAlign;
+    Property Top : Integer Read FTop Write FTop;
+    Property Left : Integer Read FLeft Write FLeft;
+    Property Width : Integer Read FWidth Write FWidth;
+    Property Height : Integer Read FHeight Write FHeight;
+    Property Visible : Boolean Read FVisible Write FVisible;
+  end;
+  TControlClass = Class of TControl;
+
+  { TCaptionControl }
+
+  TCaptionControl = Class(TControl)
+  Private
+    FCaption: String;
+    FFont: TFont;
+    procedure SetFont(const aValue: TFont);
+  public
+    constructor Create(aowner : TComponent); override;
+    destructor Destroy; override;
+  Published
+    Property Caption : String Read FCaption Write FCaption;
+    Property Font : TFont Read FFont Write SetFont;
+  end;
+  TForm = class(TCaptionControl);
+  TPanel = class(TControl);
+
+  { TLabel }
+
+  TLabel = class(TCaptionControl)
+  private
+    FFocusControl: TControl;
+    procedure SetFocusControl(const aValue: TControl);
+  Protected
+    Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+  Published
+    Property FocusControl : TControl Read FFocusControl Write SetFocusControl;
+  end;
+
+  { TEdit }
+
+  TEdit = Class(TControl)
+  Private
+    FPlaceHolder: String;
+    FText: String;
+  Published
+    Property Text : String Read FText Write FText;
+    Property Placeholder : String Read FPlaceHolder Write FPlaceHolder;
+  end;
+
+  { TCheckBox }
+
+  TCheckBox = class(TCaptionControl)
+  private
+    FChecked: Boolean;
+  Published
+    Property Checked : Boolean Read FChecked Write FChecked;
+  end;
+
+  TModalResult = (mrNone,mrOK,mrCancel,mrClose,mrYes,mrYesToAll,mrNo,mrNoToAll);
+
+  { TButton }
+
+  TButton = class(TCaptionControl)
+  private
+    FModalResult: TModalResult;
+  Published
+    Property ModalResult : TModalResult Read FModalResult Write FModalResult;
+  end;
+
+
+{ TLabel }
+
+procedure TLabel.SetFocusControl(const aValue: TControl);
+begin
+  if FFocusControl=aValue then Exit;
+  if Assigned(FFocusControl) then
+    FFocusControl.RemoveFreeNotification(Self);
+  FFocusControl:=aValue;
+  if Assigned(FFocusControl) then
+    FFocusControl.FreeNotification(Self);
+end;
+
+procedure TLabel.Notification(AComponent: TComponent; Operation: TOperation);
+begin
+  inherited Notification(AComponent, Operation);
+  if Operation=opRemove then
+    if aComponent=FFocusControl then
+      FFocusControl:=nil;
+end;
+
+{ TCaptionControl }
+
+procedure TCaptionControl.SetFont(const aValue: TFont);
+begin
+  if FFont=aValue then Exit;
+  FFont.Assign(aValue);
+end;
+
+constructor TCaptionControl.Create(aowner: TComponent);
+begin
+  inherited Create(aowner);
+  FFont:=TFont.Create;
+end;
+
+destructor TCaptionControl.Destroy;
+begin
+  FreeAndNil(FFont);
+  inherited Destroy;
+end;
+
+{ TFont }
+
+procedure TFont.Assign(Source: TPersistent);
+var
+  aSource: TFont;
+begin
+  if Source is TFont then
+  begin
+    aSource:=TFont(Source);
+    Style:=aSource.Style;
+    Size:=aSource.Size;
+    Name:=aSource.Name;
+  end else
+    inherited Assign(Source);
+end;
+
+{ TControl }
+
+function TControl.GetRect: TRect;
+begin
+  Result:=Rect(Left,Top,Left+Width,Top+Height);
+end;
+
+function TControl.GetParent: TControl;
+begin
+  if Owner is TControl then
+    Result:=TControl(Owner)
+  else
+    Result:=Nil;
+end;
+
+var
+  FForm : TForm;
+  ctag : Integer;
+  Inspector:TWasmDebugInspector;
+
+Function CreateForm : TForm;
+
+  Function CreateControl(aType : TControlClass; aParent : TControl; aName : String; aCaption : String = '') : TControl;
+  begin
+    inc(CTag);
+    Result:=aType.Create(aParent);
+    Result.Tag:=CTag;
+    Result.Name:=aName;
+    Result.Left:=10;
+    Result.Top:=24*(cTag+1);
+    Result.Width:=120;
+    Result.Height:=22;
+    if Result is TCaptionControl then
+      TCaptionControl(Result).Caption:=aCaption
+    else if Result is TEdit then
+      TEdit(Result).Text:=aCaption
+  end;
+
+var
+  btn,lbl,Edt,Pnl : TControl;
+
+begin
+  Result:=TForm.Create(Nil);
+  Pnl:=CreateControl(TPanel,Result,'pnlTop','Top panel');
+  Pnl.Align:=alClient;
+  edt:=CreateControl(TEdit,Pnl,'edtFirst','Firstname');
+  lbl:=CreateControl(TLabel,Pnl,'lblFirst','First name');
+  TLabel(lbl).FocusControl:=edt;
+  edt:=CreateControl(TEdit,Pnl,'edtLast','Lastname');
+  lbl:=CreateControl(TLabel,Pnl,'lblLast','Last name');
+  TLabel(lbl).FocusControl:=edt;
+  edt:=CreateControl(TEdit,Pnl,'edtBirth','2001-04-16');
+  lbl:=CreateControl(TLabel,Pnl,'lblBirth','Date of birth');
+  TLabel(lbl).FocusControl:=edt;
+  CreateControl(TCheckBox,Pnl,'cbRemember','Remember me');
+  Pnl:=CreateControl(TPanel,Result,'pnlButtons','');
+  Pnl.Align:=alBottom;
+  btn:=CreateControl(TButton,Pnl,'btnOK','OK');
+  btn.Align:=alRight;
+  TButton(btn).ModalResult:=mrOK;
+  btn:=CreateControl(TButton,Pnl,'btnCancel','Cancel');
+  btn.Align:=alRight;
+  TButton(btn).ModalResult:=mrCancel;
+end;
+
+begin
+  FForm:=CreateForm;
+  Inspector:=TWasmDebugInspector.Create(FFOrm);
+  Inspector.SendObjectTree(FForm);
+  inspector.SendObjectProperties(FForm,[Low(TMemberVisibility)..High(TMemberVisibility)]);
+end.
+

+ 169 - 0
demo/wasienv/wasm-oi/oistyles.css

@@ -0,0 +1,169 @@
+:root {
+  --light-bg-color: rgb(237 238 242);
+  font-family: sans-serif;
+}
+
+/* 
+ * 
+ * Object Yree
+ * 
+ */
+
+.ot-caption {
+  padding: 1px;
+  font-size: 1rem;
+  font-weight: 500;
+  background-color: rgb(237 238 242);
+  display: flex;
+  align-items: center;  
+  justify-content: space-between;
+}
+
+ot-hidden {
+  display: none;
+}
+
+ul.ot-tree-nested {
+ list-style-type: none;
+ font-size: 10pt;
+ padding-left: 2em;
+}
+
+li.ot-collapsed ul.ot-tree-nested {
+ display: none
+}
+.ot-tree-item-caption {
+   user-select: none;
+}
+
+li.ot-selected > .ot-tree-item-caption {
+   background-color: blue;
+   color: white;
+}
+
+.ot-tree-item-caption::before {
+ color: black;
+ display: inline-block;
+ margin-right: 4px;
+}
+
+li.ot-collapsed > span.ot-tree-item-caption::before {
+  content: "\27A4";
+}
+
+li.ot-expanded > span.ot-tree-item-caption::before {
+  content: "\2B9F";
+}
+
+
+/* 
+ * 
+ * Object Inspector
+ * 
+ */
+ 
+.oi-caption {
+  padding: 1px;
+  font-size: 1rem;
+  font-weight: 500;
+  background-color: rgb(237 238 242);
+  display: flex;
+  align-items: center;  
+  justify-content: space-between;
+}
+
+.oi-caption-lbl {
+  flex-grow: 1;
+  text-align: center;
+}
+/* Object inspector caption button */
+.oi-icon-btn {
+  padding: 1px 10px;
+  font-size: 1.2rem;
+  cursor: pointer;
+}
+
+/* Object inspector config panel */
+
+.oi-config-panel {
+  border-top: 1px solid rgb(189, 192, 194);
+  background-color: rgb(237, 240, 248);
+  overflow-y: hidden;
+  max-height: 0px;
+  transition: max-height 0.5s ease-out;
+}
+.oi-config-panel-open {
+  max-height: 250px;
+}
+
+.oi-config-panel-closed {
+  display: none;
+}
+
+.oi-config-panel-desc {
+  font-weight: 400;
+  padding: 10px;
+  margin: 0;
+}
+/*  Object inspector table */
+.oi-table {
+  width: 100%;
+  border-collapse: collapse;
+  border: 2px solid rgb(140 140 140);
+  font-size: 0.8rem;
+  letter-spacing: 1px;
+}
+
+.oi-table thead {
+  background-color: rgb(228 240 245);
+}
+
+.oi-table th,
+td {
+  border: 1px solid rgb(160 160 160);
+  padding: 0px 10px;
+  text-align: left;
+}
+
+.oi-table tbody > tr:nth-of-type(even) {
+  background-color: rgb(237 238 242);
+}
+
+.oi-checkbox-div {
+  /* margin: 5px 20px; */
+  font-size: 0.8rem;
+  letter-spacing: 1px;
+}
+
+.oi-checkbox-div label {
+  margin-left: 0.8em;
+}
+
+.oi-checkbox-col {
+  display: inline-block;
+}
+
+.oi-checkbox-col:last-child {
+  margin-left: 2em;
+}
+
+.oi-checkbox-header {
+  display: flex;
+  font-weight: 600;
+}
+
+.oi-checkbox-row {
+  display: flex;
+  margin: 5px 0;
+}
+
+.oi-checkbox-last {
+  font-size: 0.8rem;
+  margin: 15px 0;
+}
+
+.oi-checkbox-last label {
+  margin-left: 0.8em;
+}
+
+

+ 90 - 0
demo/wasienv/wasm-oi/wasmhost.lpi

@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="wasmhost"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <CustomData Count="5">
+      <Item0 Name="MaintainHTML" Value="1"/>
+      <Item1 Name="Pas2JSProject" Value="1"/>
+      <Item2 Name="PasJSLocation" Value="$NameOnly($(ProjFile))"/>
+      <Item3 Name="PasJSWebBrowserProject" Value="1"/>
+      <Item4 Name="RunAtReady" Value="1"/>
+    </CustomData>
+    <BuildModes>
+      <Item Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+    </RunParams>
+    <Units>
+      <Unit>
+        <Filename Value="wasmhost.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="index.html"/>
+        <IsPartOfProject Value="True"/>
+        <CustomData Count="1">
+          <Item0 Name="PasJSIsProjectHTMLFile" Value="1"/>
+        </CustomData>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target FileExt=".js">
+      <Filename Value="wasmhost"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <UnitOutputDirectory Value="js"/>
+    </SearchPaths>
+    <Parsing>
+      <SyntaxOptions>
+        <AllowLabel Value="False"/>
+        <UseAnsiStrings Value="False"/>
+        <CPPInline Value="False"/>
+      </SyntaxOptions>
+    </Parsing>
+    <CodeGeneration>
+      <TargetOS Value="browser"/>
+    </CodeGeneration>
+    <Linking>
+      <Debugging>
+        <GenerateDebugInfo Value="False"/>
+        <UseLineInfoUnit Value="False"/>
+      </Debugging>
+    </Linking>
+    <Other>
+      <CustomOptions Value="-Jeutf-8 -Jirtl.js -Jc -Jminclude"/>
+      <CompilerPath Value="$(pas2js)"/>
+    </Other>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 88 - 0
demo/wasienv/wasm-oi/wasmhost.lpr

@@ -0,0 +1,88 @@
+program wasmhost;
+
+{$mode objfpc}
+
+uses
+  BrowserConsole, BrowserApp, JS, Classes, SysUtils, Web, WasiHostApp, debug.objectinspector.wasm, debug.objectinspector.html;
+
+type
+
+  { TDemoApplication }
+
+  TDemoApplication = class(TBrowserWASIHostApplication)
+  private
+    function DoShowOUtputChange(Event: TEventListenerEvent): boolean;
+    procedure DoWrite(Sender: TObject; const aOutput: String);
+    function HookWasmConsole: boolean;
+  protected
+    FTreeView : THTMLObjectTree;
+    FInspector : THTMLObjectInspector;
+    FInspectorAPI : TWasmObjectInspectorApi;
+    CBShowOutput : TJSHTMLInputElement;
+    procedure DoRun; override;
+  public
+    constructor create(aOwner : TComponent); override;
+
+  end;
+
+function TDemoApplication.HookWasmConsole : boolean;
+
+begin
+  Result:=CBShowOutput.Checked;
+  if Result then
+    begin
+    WasiEnvironment.OnStdOutputWrite:=@DoWrite;
+    WasiEnvironment.OnStdErrorWrite:=@DoWrite;
+    end
+  else
+    begin
+    WasiEnvironment.OnStdOutputWrite:=Nil;
+    WasiEnvironment.OnStdErrorWrite:=Nil;
+    end;
+end;
+
+function TDemoApplication.DoShowOUtputChange(Event: TEventListenerEvent): boolean;
+begin
+  HookWasmConsole;
+end;
+
+procedure TDemoApplication.DoWrite(Sender: TObject; const aOutput: String);
+begin
+  Writeln('Wasm ',aOutput);
+end;
+
+procedure TDemoApplication.DoRun;
+
+var
+  wasmModule : string;
+
+begin
+ wasmmodule:='oidemo.wasm';
+ RunEntryFunction:='_initialize';
+ StartWebAssembly(WasmModule,true);
+end;
+
+constructor TDemoApplication.create(aOwner: TComponent);
+begin
+  Inherited;
+  FTreeView:=THTMLObjectTree.Create(Self);
+  FTreeView.ParentElementID:='Tree';
+  FInspector:=THTMLObjectInspector.Create(Self);
+  FInspector.ParentElementID:='Inspector';
+  FInspectorApi:=TWasmObjectInspectorApi.Create(WasiEnvironment);
+  FInspectorApi.DefaultInspector:=FInspector;
+  FInspectorApi.DefaultObjectTree:=FTReeview;
+  FInspectorApi.HandleObjectSelection:=True;
+  CBShowOutput:=TJSHTMLInputElement(GetHTMLElement('cbconsole'));
+  CBShowOutput.onchange:=@DoShowOUtputChange;
+  HookWasmConsole;
+end;
+
+var
+  Application : TDemoApplication;
+
+begin
+  Application:=TDemoApplication.Create(nil);
+  Application.Initialize;
+  Application.Run;
+end.

+ 174 - 29
packages/wasm-oi/src/debug.objectinspector.html.pas

@@ -27,6 +27,7 @@ Type
 
   { THTMLTreeBuilder }
   TObjectSelectedEvent = procedure(Sender : TObject; aObjectId : Integer) of object;
+  TMemberVisibilities = Set of TMemberVisibility;
 
   THTMLTreeBuilder = class(TObject)
   private
@@ -98,7 +99,7 @@ Type
   TOIColumn = (ocName,ocValue,ocKind,ocVisibility);
   TOIColumns = set of TOIColumn;
 
-  TOIOption = (ooHidePropertiesWithoutValue,ooShowCaption);
+  TOIOption = (ooHidePropertiesWithoutValue,ooShowCaption,ooShowConfigPanel);
   TOIOptions = set of TOIOption;
 
   { THTMLObjectInspector }
@@ -114,22 +115,31 @@ Type
     FCaption: String;
     FOnRefresh: TNotifyEvent;
     FOptions: TOIOptions;
+    FPropertyVisibilities: TMemberVisibilities;
+    FSuffix: String;
     FVisibleColumns: TOIColumns;
     FObjectID: integer;
     FParentElement : TJSHTMLElement;
     FTableElement : TJSHTMLTableElement;
     FCaptionElement : TJSHTMLElement;
+    FConfigPanel : TJSHTMLElement;
+    function AppendEl(aParent: TJSHTMLElement; aTag: String; const aID: String; const aInnerText: String=''): TJSHTMLElement;
+    function AppendSpan(aParent: TJSHTMLElement; const aInnerText: String=''): TJSHTMLElement;
+    function CreateEl(aTag: String; const aID: String; const aInnerText: String=''): TJSHTMLElement;
     function GetParentElement: TJSHTMLElement;
     function GetParentElementID: String;
     procedure RenderCaption(aEl: TJSHTMLElement);
     procedure SetBorder(AValue: Boolean);
     procedure SetCaption(AValue: String);
     procedure SetOptions(AValue: TOIOptions);
+    procedure SetPropertyVisibilities(AValue: TMemberVisibilities);
     procedure SetVisibleColumns(AValue: TOIColumns);
     procedure SetParentElementID(AValue: String);
+    procedure ToggleConfig(aEvent: TJSEvent);
   protected
     procedure DisplayChanged;
     procedure Refresh;
+    function CreateConfigPanel() : TJSHTMLElement; virtual;
     function CreateTable(aParent : TJSHTMLElement) : TJSHTMLTableElement; virtual;
     procedure SetObjectID(AValue: integer); virtual;
     procedure SetParentElement(AValue: TJSHTMLElement);virtual;
@@ -145,11 +155,13 @@ Type
     procedure AddProperty(aIndex : Integer; aVisibility : TMemberVisibility; aKind : TTypeKind; aFlags : TPropDataFlags; const aName,aValue : String);
     procedure AddProperty(aPropData: TOIPropData);
     Property ParentElement : TJSHTMLElement Read GetParentElement Write SetParentElement;
+    Property Suffix : String Read FSuffix Write FSuffix;
   Published
     Property ObjectID : integer Read FObjectID Write SetObjectID;
     Property ParentElementID : String Read GetParentElementID Write SetParentElementID;
     Property Border : Boolean Read FBorder Write SetBorder;
     property VisibleColumns : TOIColumns read FVisibleColumns write SetVisibleColumns;
+    property PropertyVisibilities : TMemberVisibilities Read FPropertyVisibilities Write SetPropertyVisibilities;
     Property Options : TOIOptions Read FOptions Write SetOptions;
     property BeforeAddProperty : TBeforeAddPropertyEvent Read FBeforeAddProperty Write FBeforeAddProperty;
     property AfterAddProperty : TAfterAddPropertyEvent Read FAfterAddProperty Write FAfterAddProperty;
@@ -181,8 +193,8 @@ var
 
 begin
   El:=TJSHTMLElement(event.targetElement.parentElement);
-  El.classList.toggle('expanded');
-  El.classList.toggle('collapsed');
+  El.classList.toggle('ot-expanded');
+  El.classList.toggle('ot-collapsed');
 end;
 
 procedure THTMLTreeBuilder.HandleItemSelect(Event : TJSEvent);
@@ -195,11 +207,11 @@ var
 begin
   // List element
   El:=TJSHTMLElement(event.targetElement.parentElement);
-  lList:=FRootElement.querySelectorAll('li.selected');
+  lList:=FRootElement.querySelectorAll('li.ot-selected');
   for I:=0 to lList.length-1 do
     if El<>lList.item(I) then
-      TJSHtmlElement(lList.item(I)).classList.remove('selected');
-  El.classList.add('selected');
+      TJSHtmlElement(lList.item(I)).classList.remove('ot-selected');
+  El.classList.add('ot-selected');
   if Assigned(FOnObjectSelect) then
     begin
     lSelectID:=StrToIntDef(el.dataset['objectId'],-1);
@@ -221,7 +233,7 @@ begin
     if FRootElement=Nil then
       begin
       FRootElement:=TJSHTMLElement(Document.createElement('ul'));
-      FRootElement.className:='tree-nested';
+      FRootElement.className:='ot-tree-nested';
       FParentElement.appendChild(FRootElement);
       end;
     aParent:=FParentElement;
@@ -232,25 +244,25 @@ begin
       Raise EHTMLTreeBuilder.CreateFmt('Invalid parent item type: %s',[aParent.tagName]);
     if Not StartCollapsed then
       begin
-      aParent.ClassList.remove('collapsed');
-      aParent.ClassList.add('expanded');
+      aParent.ClassList.remove('ot-collapsed');
+      aParent.ClassList.add('ot-expanded');
       end;
     end;
-  List:=TJSHTMLELement(aParent.querySelector('ul.tree-nested'));
+  List:=TJSHTMLELement(aParent.querySelector('ul.ot-tree-nested'));
   if List=Nil then
     begin
     List:=TJSHTMLElement(Document.createElement('ul'));
-    List.className:='tree-nested';
+    List.className:='ot-tree-nested';
     aParent.appendChild(List);
     end;
   Item:=TJSHTMLElement(Document.createElement('li'));
-  Item.className:='tree-item collapsed';
+  Item.className:='ot-tree-item ot-collapsed';
   Item.dataset['objectId']:=IntToStr(aID);
   Span:=TJSHTMLElement(Document.createElement('span'));
   Span.InnerText:=aCaption;
-  Span.className:='tree-item-caption' ;
-  Span.addEventListener('click',@HandleItemCollapse);
-  Span.addEventListener('dblclick',@HandleItemSelect);
+  Span.className:='ot-tree-item-caption' ;
+  Span.addEventListener('dblclick',@HandleItemCollapse);
+  Span.addEventListener('click',@HandleItemSelect);
   Item.appendChild(Span);
   List.AppendChild(Item);
   Result:=Item;
@@ -323,14 +335,14 @@ var
 begin
   aParent.InnerHTML:='';
   DC:=TJSHTMLElement(document.createElement('div'));
-  DC.className:='otCaption';
+  DC.className:='ot-caption';
   aParent.AppendChild(DC);
   FCaptionElement:=DC;
   if Not (otShowCaption in Options) then
-    DC.classList.Add('otHidden');
+    DC.classList.Add('ot-hidden');
   RenderCaption(DC);
   DT:=TJSHTMLElement(document.createElement('div'));
-  DT.className:='otTree';
+  DT.className:='ot-tree';
   aParent.AppendChild(DT);
   Result:=DT;
 end;
@@ -429,6 +441,13 @@ begin
   DisplayChanged;
 end;
 
+procedure THTMLObjectInspector.SetPropertyVisibilities(AValue: TMemberVisibilities);
+begin
+  if FPropertyVisibilities=AValue then Exit;
+  FPropertyVisibilities:=AValue;
+  DisplayChanged;
+end;
+
 procedure THTMLObjectInspector.SetVisibleColumns(AValue: TOIColumns);
 begin
   if FVisibleColumns=AValue then Exit;
@@ -473,17 +492,127 @@ begin
     FonRefresh(Self);
 end;
 
-Procedure THTMLObjectInspector.RenderCaption(aEl : TJSHTMLElement);
+function THTMLObjectInspector.AppendSpan(aParent: TJSHTMLElement; const aInnerText: String): TJSHTMLElement;
+begin
+  Result:=CreateEl('span','',aInnerText);
+  aParent.AppendChild(Result);
+end;
+
+function THTMLObjectInspector.CreateEl(aTag: String; const aID: String; const aInnerText: String): TJSHTMLElement;
+
+begin
+  Result:=TJSHTMLElement(Document.CreateElement(aTag));
+  if aID<>'' then
+    Result.id:=aID;
+  if aInnerText<>'' then
+    Result.InnerText:=aInnerText;
+end;
+
+function THTMLObjectInspector.AppendEl(aParent: TJSHTMLElement; aTag: String; const aID: String; const aInnerText: String
+  ): TJSHTMLElement;
+
+begin
+  Result:=CreateEl(aTag,aID,aInnerText);
+  aParent.AppendChild(Result);
+end;
+
+function THTMLObjectInspector.CreateConfigPanel(): TJSHTMLElement;
+
+  Function AppendCheckbox(aParent : TJSHTMLElement; aName,aLabel : String; aOrd : Integer; isChecked: Boolean) : TJSHTMLInputElement;
+
+  var
+    Tmp : TJSHTMLElement;
+
+  begin
+    Tmp:=AppendSpan(aParent,'');
+    Tmp.ClassName:='oi-checkbox-row';
+    Result:=TJSHTMLInputElement(AppendEl(Tmp,'input','cb'+aName+Suffix));
+    Result.Checked:=isChecked;
+    Result._type:='checkbox';
+    Result.dataset['ord']:=IntToStr(aOrd);
+    Tmp:=AppendEl(Tmp,'label','',aLabel);
+    Tmp['for']:='cb'+aName;
+  end;
+
+var
+  Tmp,CBDiv,CBhead,CBCol,cbRow : TJSHTMLElement;
+  CB : TJSHTMLInputElement;
+  Vis : TMemberVisibility;
+
+begin
+  Result:=CreateEl('div','oiConfig'+Suffix);
+  Result.classList.add('oi-config-panel-closed');
+
+  appendEl(Result,'h5','Use the checkboxes to show/hide fields in the table:');
+  CBDiv:=appendEl(Result,'div','');
+  CBDiv.ClassName:='oi-checkbox-div';
+  // Col 1
+  CBCol:=appendEl(CBDiv,'div','');
+  CBCol.ClassName:='oi-checkbox-col';
+  CBHead:=AppendEl(CBCol,'div','');
+  CBHead.ClassName:='oi-checkbox-header';
+  AppendSpan(CBHead,'Columns');
+  AppendCheckBox(CBCol,'PropertyName','Property name',Ord(ocName),ocName in VisibleColumns);
+  AppendCheckBox(CBCol,'PropertyVisibility','Visibility',Ord(ocVisibility),ocVisibility in VisibleColumns);
+  AppendCheckBox(CBCol,'PropertyKind','Kind',Ord(ocKind),ocKind in VisibleColumns);
+  AppendCheckBox(CBCol,'PropertyValue','Value',Ord(ocValue),ocValue in VisibleColumns);
+  // Col 2
+  CBCol:=appendEl(CBDiv,'div','');
+  CBCol.ClassName:='oi-checkbox-col';
+  CBHead:=AppendEl(CBCol,'div','');
+  CBHead.ClassName:='oi-checkbox-header';
+  AppendSpan(CBHead,'Visibilities');
+  For Vis in TMemberVisibility do
+    AppendCheckBox(CBCol,'PropVis'+VisibilityNames[Vis],VisibilityNames[Vis],Ord(Vis),Vis in PropertyVisibilities);
+  Tmp:=AppendEl(Result,'div','');
+  Tmp.classname:='oi-checkbox-last';
+  AppendCheckBox(Tmp,'noShowNoValue','Hide properties without value',0,ooHidePropertiesWithoutValue in Options);
+
+
+(*
+
+    <div class="checkbox-last">
+      <span class="width-300">
+        <input
+          type="checkbox"
+          id="colPublishedCheckbox"
+          name="cbxPublished"
+          unchecked
+        />
+        <label for="cbxPublished">Hide properties without value</label>
+      </span>
+    </div>
+  </div>
+
+*)
+end;
+
+procedure THTMLObjectInspector.RenderCaption(aEl: TJSHTMLElement);
 
 begin
   aEl.innerText:=Caption;
 end;
 
+procedure THTMLObjectInspector.ToggleConfig(aEvent : TJSEvent);
+
+begin
+  if not FConfigPanel.classList.toggle('oi-config-panel-open') then
+    begin
+    aEvent.TargetElement.innerHTML:='&#x2699;';
+    FConfigPanel.classList.add('oi-config-panel-closed');
+    end
+  else
+    begin
+    aEvent.TargetElement.innerHTML:='&#x25b4;';
+    FConfigPanel.classList.remove('oi-config-panel-closed');
+    end
+end;
+
 function THTMLObjectInspector.CreateTable(aParent : TJSHTMLElement): TJSHTMLTableElement;
 
 
 var
-  DP,DC,P,R,C : TJSHTMLElement;
+  CS,DP,DC,P,R,C : TJSHTMLElement;
 
   function AddHeader(aText,aClass : string) : TJSHTMLTableCellElement;
   begin
@@ -497,32 +626,47 @@ begin
   if (ooShowCaption in Options) and (Caption<>'') then
     begin
     DP:=TJSHTMLElement(Document.createElement('div'));
-    DP.className:='oiWrapper';
+    DP.className:='oi-wrapper';
     aParent.AppendChild(DP);
     DC:=TJSHTMLElement(Document.createElement('div'));
-    DC.className:='oiCaption';
-    RenderCaption(DC);
+    DC.className:='oi-caption';
+    CS:=TJSHTMLElement(Document.createElement('span'));
+    DC.AppendChild(CS);
+    RenderCaption(CS);
     DP.AppendChild(DC);
     FCaptionElement:=DC;
+    FConfigPanel:=nil;
+    if ooShowConfigPanel in Options then
+      begin
+      CS:=TJSHTMLElement(Document.createElement('span'));
+      CS.innerHTML:='&#x2699;';
+      CS.className:='oi-icon-btn';
+      CS.addEventListener('click',@ToggleConfig);
+      DC.AppendChild(CS);
+      FConfigPanel:=CreateConfigPanel;
+      DP.appendChild(FConfigPanel);
+      end
     end
   else
     begin
+    FConfigPanel:=nil;
     FCaptionElement:=DC;
     DP:=aParent;
     end;
+
   Result:=TJSHTMLTableElement(Document.createElement('TABLE'));
-  Result.ClassName:='objectInspectorTable';
+  Result.ClassName:='oi-table';
   P:=TJSHTMLTableElement(Document.createElement('THEAD'));
   Result.appendChild(P);
   R:=TJSHTMLTableRowElement(Document.createElement('TR'));
   if ocName in VisibleColumns then
-    addHeader('Property Name','oiPropertyName');
+    addHeader('Property Name','oi-property-name');
   if ocVisibility in VisibleColumns then
-    addHeader('Visibility','oiPropertyVisibility');
+    addHeader('Visibility','oi-property-visibility');
   if ocKind in VisibleColumns then
-    addHeader('Kind','oiPropertyKind');
+    addHeader('Kind','oi-property-kind');
   if ocValue in VisibleColumns then
-    addHeader('Value','oiPropertyValue');
+    addHeader('Value','oi-property-value');
   P.appendChild(R);
   P:=TJSHTMLTableElement(Document.createElement('TBODY'));
   Result.border:=IntToStr(Ord(Border));
@@ -651,8 +795,9 @@ constructor THTMLObjectInspector.Create(aOwner: TComponent);
 begin
   inherited Create(aOwner);
   Caption:='Property inspector';
-  Options:=[ooShowCaption,ooHidePropertiesWithoutValue];
+  Options:=[ooShowCaption,ooShowConfigPanel,ooHidePropertiesWithoutValue];
   VisibleColumns:=[ocName,ocValue];
+  PropertyVisibilities:=[Low(TMemberVisibility)..High(TMemberVisibility)];
 end;
 
 destructor THTMLObjectInspector.destroy;

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است