diff --git a/demo/webwidget/designdemo/design.css b/demo/webwidget/designdemo/design.css
new file mode 100644
index 0000000..4321837
--- /dev/null
+++ b/demo/webwidget/designdemo/design.css
@@ -0,0 +1,52 @@
+button[data-widget-class] {
+ background-repeat: no-repeat;
+ background-position: center;
+ width: 36px;
+ height: 36px;
+}
+#toolbar {
+ width: 80%;
+ background-color: #DDDDDD;
+ margin-bottom: 32px;
+ padding: 4px 4px 4px 4px;
+ margin-left: 30px;
+}
+#designpage {
+ background-color: #A0A0A0;
+ width: 80%;
+ height: 80vh;
+ margin-left: 30px;
+ margin-top: 32px;
+}
+
+.designerActive {
+ position: relative;
+ border: 1px dashed #87cefa;
+}
+
+.designerToolbar {
+ position: absolute;
+ top: 0px;
+ left: -18px;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+/* justify-content: space-around; */
+}
+
+.designerDragHandle{
+ margin-left: 1px;
+}
+
+.designerPlaceholder {
+ border: 3px dotted black;
+ margin: 1em 1em 1em 1em;
+ height: 50px;
+}
+
+.source {
+ display: flex;
+ width: 540px;
+ margin: 10px auto;
+ font-size: 12px;
+}
\ No newline at end of file
diff --git a/demo/webwidget/designdemo/designdemo.html b/demo/webwidget/designdemo/designdemo.html
new file mode 100644
index 0000000..439a07c
--- /dev/null
+++ b/demo/webwidget/designdemo/designdemo.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Designdemo
+
+
+
+
+
+
+
+
+
diff --git a/demo/webwidget/designdemo/designdemo.lpi b/demo/webwidget/designdemo/designdemo.lpi
new file mode 100644
index 0000000..2a103cd
--- /dev/null
+++ b/demo/webwidget/designdemo/designdemo.lpi
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/webwidget/designdemo/designdemo.lpr b/demo/webwidget/designdemo/designdemo.lpr
new file mode 100644
index 0000000..9253345
--- /dev/null
+++ b/demo/webwidget/designdemo/designdemo.lpr
@@ -0,0 +1,35 @@
+program designdemo;
+
+{$mode objfpc}
+{$DEFINE USEIDE}
+
+uses
+ browserapp, JS, Classes, SysUtils, Web, designer, webideclient;
+
+type
+ TMyApplication = class(TBrowserApplication)
+ Public
+ FDemo : TDesignDemo;
+ FIDEIntf : TIDEClient;
+ procedure doRun; override;
+ end;
+
+procedure TMyApplication.doRun;
+
+begin
+ FDemo:=TDesignDemo.Create(Self);
+ {$IFDEF USEIDE}
+ FIDEIntf:=TIDEClient.Create(Self);
+ FDemo.IDEClient:=FIDEintf;
+ FIDEIntf.RegisterClient;
+ {$ENDIF}
+end;
+
+var
+ Application : TMyApplication;
+
+begin
+ Application:=TMyApplication.Create(nil);
+ Application.Initialize;
+ Application.Run;
+end.
diff --git a/demo/webwidget/designdemo/designer.pp b/demo/webwidget/designdemo/designer.pp
new file mode 100644
index 0000000..71f16d3
--- /dev/null
+++ b/demo/webwidget/designdemo/designer.pp
@@ -0,0 +1,353 @@
+unit designer;
+
+{$mode objfpc}
+
+interface
+
+uses
+ Classes, SysUtils, libjquery, webwidget, htmlwidgets, contnrs, js, web, webideclient;
+
+Type
+
+ { TRegisteredWidget }
+
+ TRegisteredWidget = Class
+ Private
+ FClass : TWebwidgetClass;
+ FImageName : String;
+ Public
+ Constructor Create(aClass : TWebwidgetClass; aImageName : String);
+ Property WidgetClass : TWebwidgetClass Read FClass;
+ Property ImageName : String Read FimageName;
+ end;
+
+ TWidgetButtonWidget = Class(TButtonWidget)
+ MyWidget : TRegisteredWidget;
+ end;
+
+ TSortable = Class helper for TJQuery
+ Procedure sortable(Options : TJSObject); external name 'sortable'; overload;
+ Procedure sortable(Options : string); external name 'sortable'; overload;
+ end;
+
+ { TDesignDemo }
+
+ TDesignDemo = class(TComponent)
+ private
+ FIDEClient: TIDEClient;
+ procedure AddWidgetByName(aID: NativeInt; AName: String);
+ function CreateNewWidget(aParent: TCustomWebWidget; aClass: TCustomWebWidgetClass): TCustomWebWidget;
+ function DoActive(Event: TEventListenerEvent): boolean;
+ procedure DoCommandsReceived(Sender: TObject; aCommands: TJSArray);
+ procedure DoWidgetAddClick(Sender: TObject; Event: TJSEvent);
+ procedure SetIDEClient(AValue: TIDEClient);
+ function SortableOptions: TJSObject;
+ function StreamWidget(aWidget: TCustomWebWidget): String;
+ Public
+ FConfirmAdd : NativeInt;
+ FAddWidget : TRegisteredWidget;
+ FPage : TWebPage;
+ FToolBar : TContainerWidget;
+ FButtons : Array[1..10] of TButtonWidget;
+ FWidgetButtons : Array of TWidgetButtonWidget;
+ FRegisteredWidgets : TObjectList;
+ Constructor Create(aOwner : TComponent); override;
+ Destructor Destroy; override;
+ Procedure RegisterWidgets;
+ Procedure RegisterWidget(aClass : TWebWidgetClass; aImageName : String);
+ procedure SetAddMode(aRegisteredWidget: TRegisteredWidget);
+ Procedure FillToolBar;
+ Procedure SetupPage;
+ property IDEClient: TIDEClient Read FIDEClient Write SetIDEClient;
+ end;
+
+implementation
+
+Const
+ SSortableSelect = '#designpage, #designpage [data-ww-element-content]';
+
+Type
+
+ { TJumboWidget }
+
+ TJumboWidget = class(TCustomTemplateWidget)
+ Public
+ Constructor Create(aOwner: TComponent); override;
+ end;
+type
+ TWidgetHack = Class(TCustomWebWidget)
+ Property Element;
+ Property ElementID;
+ Property TopElement;
+ Property ContentElement;
+ end;
+ TPageHack = Class(TWebPage)
+ Property Element;
+ Property ElementID;
+ end;
+
+{ TJumboWidget }
+
+constructor TJumboWidget.Create(aOwner: TComponent);
+begin
+ inherited Create(aOwner);
+ Template.Text:=''+sLineBreak+
+ '
Hello, world!
'+sLineBreak+
+ '
This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.
'+sLineBreak+
+ '
'+sLineBreak+
+ '
It uses utility classes for typography and spacing to space content out within the larger container.
'+sLineBreak+
+ '
'+sLineBreak+
+ ' Learn more'+sLineBreak+
+ '
'+sLineBreak+
+ '
';
+end;
+
+
+{ TRegisteredWidget }
+
+constructor TRegisteredWidget.Create(aClass: TWebwidgetClass; aImageName: String);
+begin
+ FClass:=aClass;
+ FImageName:=aImageName;
+end;
+
+{ TDesignDemo }
+
+function TDesignDemo.CreateNewWidget(aParent : TCustomWebWidget; aClass : TCustomWebWidgetClass) : TCustomWebWidget;
+
+begin
+ Result:=aClass.Create(FPage);
+ Result.Name:=Result.ClassName+IntToStr(FPage.ChildCount);
+ Result.Parent:=aParent;
+ Result.Refresh;
+ if Assigned(IDEClient) then
+ IDEClient.SendAction('create',New(['widget',Result.Name,'class',Result.ClassName]));
+
+end;
+
+function TDesignDemo.StreamWidget(aWidget : TCustomWebWidget) : String;
+
+Var
+ S: TBytesStream;
+ T : TStringStream;
+
+begin
+ T:=Nil;
+ S:=TBytesStream.Create(Nil);
+ try
+ S.WriteComponent(aWidget);
+ S.Position:=0;
+ T:=TStringStream.Create('');
+ ObjectBinaryToText(S,T);
+ Result:=T.DataString;
+ finally
+ T.Free;
+ S.Free;
+ end;
+end;
+function TDesignDemo.DoActive(Event: TEventListenerEvent): boolean;
+
+Const
+ Toolbar = '';
+
+var
+ aParent,aNew : TCustomWebWidget;
+ aNewActive : TJSHTMLElement;
+ aNewWidget : TRegisteredWidget;
+
+begin
+ Result:=True;
+ JQuery('.designerActive').removeClass('designerActive');
+ JQuery('.designerToolbar').remove();
+ aNewActive:=TJSHTMLElement(event.target);
+ aParent:=FPage.FindWidgetByID(String(aNewActive.dataset[STopElementData]));
+ if (FAddWidget<>Nil) and (aParent<>Nil) then
+ begin
+ aNewWidget:=FAddWidget;
+ FAddWidget:=Nil;
+ if aParent<>Nil then
+ begin
+ aNew:=CreateNewWidget(aParent,aNewWidget.WidgetClass);
+ aNewActive:=TWidgetHack(aNew).TopElement;
+ if Assigned(TWidgetHack(aNew).ContentElement) then
+ JQuery(TWidgetHack(aNew).ContentElement).Sortable(SortableOptions);
+ jQuery(aNewActive).on_('click',@DoActive);
+ aParent:=aNew;
+ end;
+ end;
+ JQuery(aNewActive).AddClass('designerActive').prepend(toolbar);
+ if assigned(aParent) and Assigned(IDEClient) then
+ IDEClient.SendAction('select',New(['widget',aParent.Name,'class',aParent.ClassName,'state',StreamWidget(aParent)]));
+end;
+
+procedure TDesignDemo.SetAddMode(aRegisteredWidget : TRegisteredWidget);
+
+begin
+ FAddWidget:=aRegisteredWidget;
+end;
+
+procedure TDesignDemo.DoWidgetAddClick(Sender: TObject; Event: TJSEvent);
+begin
+ SetAddMode((Sender as TWidgetButtonWidget).MyWidget);
+end;
+
+procedure TDesignDemo.AddWidgetByName(aID : NativeInt; AName : String);
+
+Var
+ I : integer;
+ Btn : TWidgetButtonWidget;
+
+begin
+ I:=FRegisteredWidgets.Count-1;
+ While (i>=0) and Not SameText(TRegisteredWidget(FRegisteredWidgets[i]).WidgetClass.ClassName,aName) do
+ Dec(I);
+ if I<0 then exit;
+ SetAddMode(TRegisteredWidget(FRegisteredWidgets[i]));
+ FConfirmAdd:=aID;
+end;
+
+procedure TDesignDemo.DoCommandsReceived(Sender: TObject; aCommands: TJSArray);
+
+var
+ J,P : TJSOBject;
+ aName : String;
+ aID : NativeInt;
+ I : integer;
+
+begin
+ for I:=0 to aCommands.Length-1 do
+ begin
+ J:=TJSObject(aCommands[i]);
+ aName:=String(J['name']);
+ aID:=NativeInt(J['id']);
+ p:=TJSObject(J['payload']);
+ case aName of
+ 'addWidget' : AddWidgetByName(aID,String(P['class']));
+ end;
+ end;
+end;
+
+procedure TDesignDemo.SetIDEClient(AValue: TIDEClient);
+begin
+ if FIDEClient=AValue then Exit;
+ FIDEClient:=AValue;
+ if assigned(FIDEClient) then
+ begin
+ FIDEClient.OnCommands:=@DoCommandsReceived;
+ FIDEClient.StartCommandPolling;
+ FToolBar.Visible:=False;
+ end;
+end;
+
+function TDesignDemo.SortableOptions: TJSObject;
+
+begin
+ Result:=New([
+ 'items','> [data-ww-element-top]',
+ 'connectWith','[data-ww-element-content]',
+ 'placeholder','designerPlaceholder',
+ 'tolerance','pointer',
+// 'containment',TPageHack(FPage).Element,
+// 'handle','[data-ww-element-top]',
+ 'cancel',''
+ ]);
+end;
+
+constructor TDesignDemo.Create(aOwner: TComponent);
+
+begin
+ inherited Create(aOwner);
+ FRegisteredWidgets:=TObjectList.Create;
+ RegisterWidgets;
+ FillToolBar;
+ SetUpPage;
+ // []
+ JQuery(SSortableSelect).Sortable(SortableOptions);
+ jQuery('#designpage').on_('click','[data-ww-element-top]',@DoActive);
+ jQuery('#designpage').on_('click',@DoActive);
+end;
+
+destructor TDesignDemo.Destroy;
+begin
+ FreeAndNil(FRegisteredWidgets);
+ inherited Destroy;
+end;
+
+procedure TDesignDemo.RegisterWidgets;
+
+begin
+ RegisterWidget(TButtonWidget,'button');
+ RegisterWidget(TCheckBoxInputWidget,'checkbox');
+ RegisterWidget(TRadioInputWidget,'radio');
+ RegisterWidget(TTextInputWidget,'edit');
+ RegisterWidget(TImageWidget,'image');
+ RegisterWidget(TTextAreaWidget,'memo');
+ RegisterWidget(TSelectWidget,'select');
+ RegisterWidget(TContainerWidget,'container');
+ RegisterWidget(TJumboWidget,'jumbo');
+end;
+
+procedure TDesignDemo.RegisterWidget(aClass: TWebWidgetClass; aImageName: String);
+begin
+ FRegisteredWidgets.Add(TRegisteredWidget.Create(aClass,aImageName));
+end;
+
+procedure TDesignDemo.SetupPage;
+
+Const
+ ButtonClasses : Array[0..8] of string
+ = ('primary','secondary','success','danger','warning','info','light','dark','link');
+
+var
+ I : Integer;
+
+begin
+ FPage:=TWebPage.Create(Self);
+ FPage.ElementID:='designpage';
+ FPage.Refresh;
+ For I:=0 to 9 do
+ begin
+ FButtons[I]:=TWidgetButtonWidget.Create(FPage);
+ FButtons[I].Classes:='btn btn-'+ButtonClasses[I mod 9];
+ FButtons[I].Text:='Button #'+IntToStr(I+1);
+ FButtons[I].Parent:=FPage;
+ FButtons[I].Refresh;
+ end;
+end;
+
+procedure TDesignDemo.FillToolBar;
+
+Var
+ RW : TRegisteredWidget;
+ I : Integer;
+ Btn : TWidgetButtonWidget;
+
+begin
+ FToolBar:=TContainerWidget.Create(Self);
+ FToolbar.Styles.EnsureStyle('min-height','34px');
+ FToolbar.Styles.RemoveStyle('width');
+ FToolbar.Styles.RemoveStyle('height');
+ FToolbar.ElementId:='toolbar';
+ FToolbar.Refresh;
+ SetLength(FWidgetButtons,FRegisteredWidgets.Count);
+ For I:=0 to FRegisteredWidgets.Count-1 do
+ begin
+ RW:=TRegisteredWidget(FRegisteredWidgets[I]);
+ Btn:=TWidgetButtonWidget.Create(Self);
+ FWidgetButtons[I]:=Btn;
+ Btn.MyWidget:=RW;
+ Btn.Classes:='btn btn-light';
+ Btn.Text:='';
+ Btn.Parent:=FToolbar;
+ Btn.Styles.Add('background-image','url("widgets/'+RW.ImageName+'.png")');
+ Btn.Refresh;
+ Btn.Data['widgetClass']:=RW.WidgetClass.ClassName;
+ Btn.OnClick:=@DoWidgetAddClick;
+ end;
+end;
+
+end.
+
diff --git a/demo/webwidget/designdemo/webideclient.pp b/demo/webwidget/designdemo/webideclient.pp
new file mode 100644
index 0000000..a160bc9
--- /dev/null
+++ b/demo/webwidget/designdemo/webideclient.pp
@@ -0,0 +1,232 @@
+unit webideclient;
+
+{$mode objfpc}
+
+interface
+
+uses
+ Classes, SysUtils, js, web;
+
+type
+ TIDEClient = Class;
+
+ TIDEResponseHandler = Procedure (aCode : Integer; aCodeText : String; aPayload : TJSObject) of object;
+
+ { TIDERequest }
+
+ TIDERequest = Class(TObject)
+ Private
+ FXHR : TJSXMLHttpRequest;
+ FOnResponse: TIDEResponseHandler;
+ Procedure ProcessResponse;
+ procedure DoStateChange;
+ Public
+ Constructor Create(aMethod, aURl: String; aPayLoad: TJSObject; aOnResponse: TIDEResponseHandler);
+ end;
+
+ { TIDEClient }
+ TCommandEvent = Procedure (Sender : TObject; aCommands : TJSArray) of object;
+ TActionEvent = Procedure (Sender : TObject; aID : Int64; aName : String; aPayload : TJSObject) of object;
+
+ TIDEClient = Class(TComponent)
+ private
+ FActionID : NativeInt;
+ FOnActionResponse: TActionEvent;
+ FPollID : NativeInt;
+ FCommandPollInterval : Integer;
+ FClientID: NativeInt;
+ FIDEURL: String;
+ FOnCommands: TCommandEvent;
+ FLastPoll : TIDERequest;
+ FStartPolling : Boolean;
+ procedure DoCommandPoll;
+ procedure OnActionSent(aCode: Integer; aCodeText: String; aPayload: TJSObject);
+ procedure OnClientRegistered(aCode: Integer; aCodeText: String; aPayload: TJSObject);
+ procedure OnCommandsReceived(aCode: Integer; aCodeText: String; aPayload: TJSObject);
+ Public
+ Constructor Create(aOwner : TComponent); override;
+ Procedure RegisterClient;
+ Procedure UnRegisterClient;
+ Procedure StartCommandPolling;
+ Procedure StopCommandPolling;
+ Function GetNextID : NativeInt;
+ procedure SendAction(Const aName : String; aPayLoad : TJSObject);
+ Property IDEURL : String read FIDEURL Write FIDEURL;
+ Property ClientID : Int64 read FClientID Write FClientID;
+ Property CommandPollInterval : Integer Read FCommandPollInterval Write FCommandPollInterval;
+ Property OnCommands : TCommandEvent Read FOnCommands Write FOnCommands;
+ Property OnActionResponse : TActionEvent Read FOnActionResponse Write FOnActionResponse;
+ end;
+
+implementation
+
+{ TIDEClient }
+
+
+procedure TIDEClient.DoCommandPoll;
+
+begin
+ if Not Assigned(FLastPoll) then
+ FLastPoll:=TIDERequest.Create('Get',IDEURL+'Command/'+IntToStr(ClientID)+'/',Nil,@OnCommandsReceived);
+end;
+
+procedure TIDEClient.OnActionSent(aCode: Integer; aCodeText: String; aPayload: TJSObject);
+
+Var
+ aID : NativeInt;
+ aName : string;
+ aActionPayload : TJSObject;
+
+begin
+ if (aCode div 100)=2 then
+ begin
+ aID:=NativeInt(aPayLoad['id']);
+ aName:=String(aPayLoad['name']);
+ aActionPayLoad:=TJSObject(aPayLoad['payload']);
+ If Assigned(OnActionResponse) then
+ OnActionResponse(Self,aID,aName,aActionPayload);
+ end;
+end;
+
+procedure TIDEClient.OnClientRegistered(aCode: Integer; aCodeText: String; aPayload: TJSObject);
+
+begin
+ if (aCode div 100)=2 then
+ begin
+ FClientID:=NativeInt(aPayload['id']);
+ if FStartPolling then
+ StartCommandPolling;
+ end
+ else
+ FClientID:=0;
+end;
+
+procedure TIDEClient.OnCommandsReceived(aCode: Integer; aCodeText: String; aPayload: TJSObject);
+
+Var
+ A: TJSArray;
+
+begin
+ FLastPoll:=Nil;
+ if (aCode div 100)<>2 then
+ exit;
+ if Assigned(aPayload) and isArray(aPayload['commands']) then
+ begin
+ A:=TJSArray(aPayload['commands']);
+ if (A.Length>0) then
+ OnCommands(Self,A);
+ end;
+end;
+
+constructor TIDEClient.Create(aOwner: TComponent);
+begin
+ Inherited;
+ FLastPoll:=Nil;
+ IDEURL:='http://'+Window.location.hostname+':'+Window.location.port+'/IDE/';
+end;
+
+procedure TIDEClient.RegisterClient;
+
+Var
+ P : TJSObject;
+ Req : TIDERequest;
+
+begin
+ P:=New(['url',window.locationString]);
+ req:=TIDERequest.Create('POST',IDEURL+'Client',P,@OnClientRegistered);
+end;
+
+procedure TIDEClient.UnRegisterClient;
+
+Var
+ Req : TIDERequest;
+
+begin
+ Req:=TIDERequest.Create('DELETE',IDEURL+'Client/'+IntToStr(ClientID),Nil,@OnClientRegistered);
+end;
+
+procedure TIDEClient.StartCommandPolling;
+begin
+ if ClientID<>0 then
+ FPollID:=Window.setInterval(@DoCommandPoll,FCommandPollInterval)
+ else
+ FStartPolling:=True;
+end;
+
+procedure TIDEClient.StopCommandPolling;
+begin
+ FStartPolling:=False;
+ if (FPollID>0) then
+ Window.clearInterval(FPollID);
+end;
+
+function TIDEClient.GetNextID: NativeInt;
+begin
+ Inc(FActionID);
+ Result:=FActionID;
+end;
+
+procedure TIDEClient.SendAction(const aName: String; aPayLoad: TJSObject);
+
+Var
+ aAction : TJSObject;
+ aID : NativeInt;
+ req: TIDERequest;
+
+begin
+ aID:=GetNextID;
+ aAction:=New(['id',aID,
+ 'name',aName,
+ 'payload',aPayLoad]);
+ req:=TIDERequest.Create('POST',IDEURL+'Action/'+IntToStr(ClientID)+'/'+IntToStr(aID),aAction,@OnActionSent);
+end;
+
+{ TIDERequest }
+
+procedure TIDERequest.ProcessResponse;
+
+var
+ P : TJSObject;
+
+begin
+ if ((FXHR.Status div 100)=2) and (FXHR.ResponseHeaders['Content-Type']='application/json') then
+ P:=TJSJSON.parseObject(FXHR.responseText)
+ else
+ P:=Nil;
+ if Assigned(FOnResponse) then
+ FOnResponse(FXHR.Status,FXHR.StatusText,P);
+end;
+
+procedure TIDERequest.DoStateChange;
+begin
+ case FXHR.readyState of
+ TJSXMLHttpRequest.DONE :
+ begin
+ if Assigned(FOnResponse) then
+ ProcessResponse;
+ Free;
+ end;
+ end;
+end;
+
+constructor TIDERequest.Create(aMethod, aURl: String; aPayLoad: TJSObject; aOnResponse: TIDEResponseHandler);
+
+Var
+ S : String;
+
+begin
+ FOnResponse:=aOnResponse;
+ FXHR:=TJSXMLHttpRequest.New;
+ FXHR.open(aMethod,aURL);
+ if assigned(aPayload) then
+ S:=TJSJSON.Stringify(aPayload)
+ else
+ S:='';
+ FXHR.setRequestHeader('Content-Type','application/json');
+ FXHR.onreadystatechange:=@DoStateChange;
+ FXHR.send(S);
+end;
+
+
+end.
+
diff --git a/demo/webwidget/designdemo/widgets/button.png b/demo/webwidget/designdemo/widgets/button.png
new file mode 100644
index 0000000..9af0a27
Binary files /dev/null and b/demo/webwidget/designdemo/widgets/button.png differ
diff --git a/demo/webwidget/designdemo/widgets/checkbox.png b/demo/webwidget/designdemo/widgets/checkbox.png
new file mode 100644
index 0000000..8100ad4
Binary files /dev/null and b/demo/webwidget/designdemo/widgets/checkbox.png differ
diff --git a/demo/webwidget/designdemo/widgets/container.png b/demo/webwidget/designdemo/widgets/container.png
new file mode 100644
index 0000000..ca9b60c
Binary files /dev/null and b/demo/webwidget/designdemo/widgets/container.png differ
diff --git a/demo/webwidget/designdemo/widgets/edit.png b/demo/webwidget/designdemo/widgets/edit.png
new file mode 100644
index 0000000..9411424
Binary files /dev/null and b/demo/webwidget/designdemo/widgets/edit.png differ
diff --git a/demo/webwidget/designdemo/widgets/image.png b/demo/webwidget/designdemo/widgets/image.png
new file mode 100644
index 0000000..6ead802
Binary files /dev/null and b/demo/webwidget/designdemo/widgets/image.png differ
diff --git a/demo/webwidget/designdemo/widgets/jumbo.png b/demo/webwidget/designdemo/widgets/jumbo.png
new file mode 100644
index 0000000..5b1587e
Binary files /dev/null and b/demo/webwidget/designdemo/widgets/jumbo.png differ
diff --git a/demo/webwidget/designdemo/widgets/memo.png b/demo/webwidget/designdemo/widgets/memo.png
new file mode 100644
index 0000000..1f5499d
Binary files /dev/null and b/demo/webwidget/designdemo/widgets/memo.png differ
diff --git a/demo/webwidget/designdemo/widgets/radio.png b/demo/webwidget/designdemo/widgets/radio.png
new file mode 100644
index 0000000..4a58ea8
Binary files /dev/null and b/demo/webwidget/designdemo/widgets/radio.png differ
diff --git a/demo/webwidget/designdemo/widgets/select.png b/demo/webwidget/designdemo/widgets/select.png
new file mode 100644
index 0000000..c6725f7
Binary files /dev/null and b/demo/webwidget/designdemo/widgets/select.png differ
diff --git a/demo/webwidget/nativedesign/frmmain.lfm b/demo/webwidget/nativedesign/frmmain.lfm
new file mode 100644
index 0000000..7850e45
--- /dev/null
+++ b/demo/webwidget/nativedesign/frmmain.lfm
@@ -0,0 +1,1553 @@
+object MainForm: TMainForm
+ Left = 684
+ Height = 541
+ Top = 193
+ Width = 698
+ Caption = 'IDE Design demo'
+ ClientHeight = 541
+ ClientWidth = 698
+ OnCloseQuery = FormCloseQuery
+ OnCreate = FormCreate
+ OnShow = FormShow
+ LCLVersion = '2.1.0.0'
+ object PBottom: TPanel
+ Left = 0
+ Height = 44
+ Top = 497
+ Width = 698
+ Align = alBottom
+ ClientHeight = 44
+ ClientWidth = 698
+ TabOrder = 0
+ object Project: TLabel
+ Left = 25
+ Height = 17
+ Top = 15
+ Width = 39
+ Caption = 'Project'
+ ParentColor = False
+ end
+ object FEProject: TFileNameEdit
+ Left = 80
+ Height = 29
+ Top = 8
+ Width = 608
+ FileName = '/home/michael/P2JS/trunk/packages/webwidget/designdemo/designdemo.html'
+ DialogTitle = 'Select Project HTML File'
+ Filter = 'HTML Files|*.html|All files|*.*'
+ FilterIndex = 0
+ DefaultExt = '.html'
+ HideDirectories = False
+ ButtonWidth = 23
+ NumGlyphs = 1
+ Flat = True
+ Anchors = [akTop, akLeft, akRight]
+ MaxLength = 0
+ TabOrder = 0
+ OnEditingDone = DEProjectEditingDone
+ Text = '/home/michael/P2JS/trunk/packages/webwidget/designdemo/designdemo.html'
+ end
+ end
+ object TBWidgets: TToolBar
+ Left = 0
+ Height = 36
+ Top = 0
+ Width = 698
+ ButtonHeight = 34
+ ButtonWidth = 34
+ Caption = 'TBWidgets'
+ Images = ILWidgets
+ TabOrder = 1
+ object TBGo: TToolButton
+ Left = 1
+ Top = 2
+ Action = AGo
+ end
+ object ToolButton1: TToolButton
+ Left = 73
+ Height = 34
+ Top = 2
+ Caption = 'ToolButton1'
+ Style = tbsSeparator
+ end
+ object TBExternalGo: TToolButton
+ Left = 37
+ Top = 2
+ Action = AGoExternal
+ end
+ end
+ object PCDesigner: TPageControl
+ Left = 0
+ Height = 461
+ Top = 36
+ Width = 698
+ ActivePage = TSBrowser
+ Align = alClient
+ TabIndex = 0
+ TabOrder = 2
+ object TSBrowser: TTabSheet
+ Caption = 'Design browser'
+ end
+ object TSLog: TTabSheet
+ Caption = 'Log'
+ ClientHeight = 430
+ ClientWidth = 688
+ object MLog: TMemo
+ Left = 0
+ Height = 430
+ Top = 0
+ Width = 688
+ Align = alClient
+ Lines.Strings = (
+ 'MLog'
+ )
+ ScrollBars = ssAutoBoth
+ TabOrder = 0
+ end
+ end
+ object TSInspector: TTabSheet
+ Caption = 'Inspector'
+ end
+ end
+ object ILWidgets: TImageList
+ Height = 32
+ Width = 32
+ left = 384
+ top = 16
+ Bitmap = {
+ 4C690B00000020000000200000004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000120000002D00000049000000490000002D000000124C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C704700000000210000007E0000
+ 00CD000000FF000000FF000000FF000000FF000000FF000000FF000000CD0000
+ 007D000000204C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047000000001400000090000000F9000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000F80000008E000000134C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C70470000000036000000E5000000FF000000FF000000FF0000
+ 00F0000000A80000007C00000060000000600000007C000000A9000000F10000
+ 00FF000000FF000000FF000000E4000000354C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C70470000000051000000F7000000FF000000FF000000DF0000005C0000
+ 00064C7047004C7047004C7047004C7047004C7047004C704700000000060000
+ 005D000000E0000000FF000000FF000000F6000000504C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 470000000036000000F7000000FF000000FF00000099000000074C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000080000009C000000FF000000FF000000F6000000354C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0014000000E5000000FF000000FF000000794C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047000000007B000000FF000000FF000000E4000000134C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0091000000FF000000FF0000009E4C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C70470000000099000000FF000000FF0000008E4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C704700000000210000
+ 00F9000000FF000000DF000000074C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C70470000000008000000E1000000FF000000F80000
+ 00204C7047004C7047004C7047004C7047004C7047004C7047000000007E0000
+ 00FF000000FF0000005C4C7047004C7047004C7047004C7047004C7047000000
+ 00190000000C4C7047004C7047004C7047004C7047004C7047000000001B0000
+ 0025000000044C7047004C7047004C7047000000005F000000FF000000FF0000
+ 007D4C7047004C7047004C7047004C7047004C7047004C704700000000CF0000
+ 00FF000000F0000000064C7047004C7047000000002C000000B6000000F30000
+ 00FF000000FF000000B54C7047004C70470000000035000000D1000000FF0000
+ 00FF000000EC000000644C7047004C70470000000007000000F1000000FF0000
+ 00CD4C7047004C7047004C7047004C7047004C70470000000012000000FE0000
+ 00FF000000A84C7047004C7047000000002B000000F1000000FB0000009D0000
+ 006B00000078000000674C7047000000001D000000EF000000F5000000720000
+ 0060000000E4000000FF000000434C7047004C704700000000AA000000FF0000
+ 00FE000000124C7047004C7047004C7047004C7047000000002F000000FF0000
+ 00FF0000007A4C7047004C704700000000AC000000FF000000644C7047004C70
+ 47004C7047004C7047004C70470000000084000000FF000000764C7047004C70
+ 470000000045000000FF000000BC4C7047004C7047000000007D000000FF0000
+ 00FF0000002E4C7047004C7047004C7047004C70470000000049000000FF0000
+ 00FF000000614C7047004C704700000000E9000000FD0000000A4C7047000000
+ 0083000000990000009900000018000000B2000000FF000000354C7047004C70
+ 470000000005000000FC000000EB4C7047004C70470000000062000000FF0000
+ 00FF000000484C7047004C7047004C7047004C70470000000048000000FF0000
+ 00FF000000624C7047004C704700000000F0000000FD000000094C7047000000
+ 00BD000000F2000000FF00000028000000B7000000FF000000334C7047004C70
+ 470000000005000000FC000000EF4C7047004C70470000000063000000FF0000
+ 00FF000000474C7047004C7047004C7047004C7047000000002E000000FF0000
+ 00FF0000007B4C7047004C704700000000C0000000FF000000604C7047004C70
+ 47000000009B000000FF0000002800000089000000FF000000744C7047004C70
+ 470000000045000000FF000000BC4C7047004C7047000000007E000000FF0000
+ 00FF0000002D4C7047004C7047004C7047004C70470000000012000000FE0000
+ 00FF000000A94C7047004C70470000000044000000FC000000F9000000930000
+ 0062000000C8000000FF000000280000001B000000F4000000F5000000730000
+ 0060000000E4000000FE000000454C7047004C704700000000AB000000FF0000
+ 00FE000000114C7047004C7047004C7047004C7047004C704700000000CD0000
+ 00FF000000F3000000074C7047004C7047000000004A000000CF000000FF0000
+ 00FF000000FF000000D60000001B4C70470000000043000000DC000000FF0000
+ 00FF000000E4000000594C7047004C70470000000007000000F2000000FF0000
+ 00CB4C7047004C7047004C7047004C7047004C7047004C7047000000007D0000
+ 00FF000000FF0000005E4C7047004C7047004C7047004C7047000000000C0000
+ 001D000000074C7047004C7047004C7047004C7047004C7047000000001F0000
+ 0021000000014C7047004C7047004C70470000000060000000FF000000FF0000
+ 007C4C7047004C7047004C7047004C7047004C7047004C704700000000200000
+ 00F8000000FF000000E0000000084C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C70470000000009000000E2000000FF000000F80000
+ 001F4C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 008E000000FF000000FF0000009F4C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047000000009B000000FF000000FF0000008C4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0013000000E4000000FF000000FF0000007B4C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047000000007C000000FF000000FF000000E3000000124C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 470000000035000000F6000000FF000000FF0000009C000000084C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000090000009F000000FF000000FF000000F6000000344C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C70470000000050000000F6000000FF000000FF000000E10000005E0000
+ 00074C7047004C7047004C7047004C7047004C7047004C704700000000070000
+ 0060000000E2000000FF000000FF000000F6000000504C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C70470000000035000000E4000000FF000000FF000000FF0000
+ 00F2000000A90000007D00000061000000610000007E000000AA000000F20000
+ 00FF000000FF000000FF000000E3000000344C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C704700000000130000008E000000F8000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000F80000008D000000124C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C704700000000200000007D0000
+ 00CD000000FF000000FF000000FF000000FF000000FF000000FE000000CC0000
+ 007C0000001F4C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000120000002D00000049000000490000002C000000114C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047000000000C00000027000000280000000B4C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047000000000D0000005E0000
+ 00AC000000CF000000BF000000A4000000A4000000C0000000CF000000AC0000
+ 005C0000000D4C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C704700000000060000006E000000CF000000770000
+ 00274C7047004C7047000000000A0000000C4C7047004C704700000000280000
+ 0079000000CF0000006C000000054C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047000000001E000000C6000000780000000A000000240000
+ 00110000004B000000290000002C0000003200000022000000520000000D0000
+ 00270000000A0000007B000000C60000001D4C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C70470000000035000000CF000000360000001A0000001E0000002B0000
+ 00134C7047004C7047004C7047004C7047004C7047004C704700000000100000
+ 002F000000180000001C00000037000000CE000000344C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47000000001E000000CF0000001E000000010000001C0000001F4C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47000000001F000000214C7047000000001F000000CF0000001D4C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0006000000C6000000364C704700000000724C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C704700000000720000000200000038000000C5000000054C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 006D0000007A0000001C000000224C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047000000001C0000001B0000007B0000006C4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047000000000D0000
+ 00CF0000000A000000180000001D4C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047000000001F0000001E0000000B000000CF0000
+ 000C4C7047004C7047004C7047004C7047004C7047004C7047000000005D0000
+ 007800000028000000304C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047000000002900000025000000790000
+ 005C4C7047004C7047004C7047004C7047004C7047004C704700000000AD0000
+ 00270000000C0000000F4C7047004C7047004C7047000000004A000000A00000
+ 00B000000072000000094C7047004C7047000000000700000074000000AD0000
+ 009F000000494C7047004C7047004C70470000000014000000120000002A0000
+ 00AA4C7047004C7047004C7047004C7047004C7047004C704700000000CF4C70
+ 4700000000524C7047004C7047004C70470000000067000000F80000008B0000
+ 0072000000DC000000B14C70470000000001000000BE000000DD000000750000
+ 008E000000F9000000664C7047004C7047004C7047000000004A4C7047000000
+ 00CF4C7047004C7047004C7047004C7047004C7047000000000E000000BD4C70
+ 4700000000214C7047004C70470000000002000000EC000000724C7047004C70
+ 470000000025000000AA0000001100000049000000FA000000214C7047004C70
+ 470000000077000000EC000000024C7047004C704700000000294C7047000000
+ 00C00000000C4C7047004C7047004C7047004C70470000000028000000A30000
+ 000C000000334C7047004C70470000000020000000FF0000002B4C7047000000
+ 002300000033000000330000000E0000007A000000D14C7047004C7047004C70
+ 47000000002C000000FF0000001F4C7047004C7047000000002C000000090000
+ 00A6000000254C7047004C7047004C7047004C70470000000026000000A50000
+ 00090000002B4C7047004C70470000000020000000FF0000002C4C7047000000
+ 008C000000CC000000FE0000004400000079000000D14C7047004C7047004C70
+ 47000000002D000000FF0000001F4C7047004C704700000000320000000C0000
+ 00A6000000254C7047004C7047004C7047004C7047000000000D000000BE4C70
+ 4700000000294C7047004C70470000000002000000EC000000764C7047004C70
+ 470000000021000000FF0000002200000049000000FA000000224C7047004C70
+ 470000000078000000EA000000024C7047004C704700000000224C7047000000
+ 00C10000000B4C7047004C7047004C7047004C7047004C704700000000CF4C70
+ 47000000004A4C7047004C7047004C70470000000068000000FA0000008F0000
+ 0072000000D8000000B24C70470000000001000000BF000000DE000000760000
+ 0090000000FA000000654C7047004C7047004C704700000000524C7047000000
+ 00CF4C7047004C7047004C7047004C7047004C7047004C704700000000AC0000
+ 002800000012000000134C7047004C7047004C7047000000004B0000009F0000
+ 00AE00000073000000074C7047004C7047000000000700000074000000AD0000
+ 009E000000474C7047004C7047004C704700000000100000000C0000002C0000
+ 00A84C7047004C7047004C7047004C7047004C7047004C7047000000005D0000
+ 007800000025000000284C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C70470000000030000000270000007B0000
+ 005A4C7047004C7047004C7047004C7047004C7047004C7047000000000D0000
+ 00CF0000000A0000001E0000001F4C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047000000001E000000180000000B000000CF0000
+ 000C4C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 006D0000007A0000001A0000001C4C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C704700000000220000001B0000007E0000006A4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0006000000C50000003800000002000000714C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C704700000000724C70470000000038000000C4000000054C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47000000001D000000CE0000001E4C704700000000210000001F4C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000200000001C000000010000001F000000CE0000001C4C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C70470000000033000000CE000000370000001C00000018000000300000
+ 00104C7047004C7047004C7047004C7047004C7047004C704700000000130000
+ 002A0000001E0000001900000039000000CE000000334C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047000000001C000000C50000007C0000000B000000270000
+ 000D0000005300000021000000330000002C000000290000004A000000110000
+ 00240000000B0000007D000000C40000001C4C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C704700000000050000006B000000CF0000007A0000
+ 002A4C7047004C7047000000000C000000094C7047004C7047000000002B0000
+ 007A000000CF0000006A000000054C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047000000000C0000005B0000
+ 00AB000000CF000000C1000000A4000000A5000000C1000000CF000000A90000
+ 005B0000000C4C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047000000000B00000027000000260000000A4C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C704700000000070000008A000000E6000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000E600000089000000060000008A000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF00000088000000E6000000FF0000003A4C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47000000003B000000FF000000E5000000FF000000FF4C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C704700000000FF000000FF000000FF000000FF4C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C704700000000FF000000FF000000FF000000FF4C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C704700000000FF000000FF000000FF000000FF4C7047004C7047004C70
+ 47004C7047000000004B000000E4000000E3000000494C7047004C7047004C70
+ 47004C7047000000004B000000E4000000E3000000494C7047004C7047004C70
+ 47004C7047000000004B000000E4000000E3000000494C7047004C7047004C70
+ 47004C704700000000FF000000FF000000FF000000FF4C7047004C7047004C70
+ 47004C704700000000E3000000FF000000FF000000E24C7047004C7047004C70
+ 47004C704700000000E3000000FF000000FF000000E24C7047004C7047004C70
+ 47004C704700000000E3000000FF000000FF000000E24C7047004C7047004C70
+ 47004C704700000000FF000000FF000000FF000000FF4C7047004C7047004C70
+ 47004C704700000000E3000000FF000000FF000000E24C7047004C7047004C70
+ 47004C704700000000E3000000FF000000FF000000E24C7047004C7047004C70
+ 47004C704700000000E3000000FF000000FF000000E24C7047004C7047004C70
+ 47004C704700000000FF000000FF000000FF000000FF4C7047004C7047004C70
+ 47004C70470000000049000000E3000000E2000000484C7047004C7047004C70
+ 47004C70470000000049000000E3000000E2000000484C7047004C7047004C70
+ 47004C70470000000049000000E3000000E2000000484C7047004C7047004C70
+ 47004C704700000000FF000000FF000000FF000000FF4C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C704700000000FF000000FF000000FF000000FF4C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C704700000000FF000000FF000000FF000000FF4C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C704700000000FF000000FF000000E6000000FF0000003B4C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47000000003D000000FF000000E500000088000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000860000000600000089000000E5000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000E500000087000000064C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0015000000AC000000F4000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000F4000000AC000000144C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00AC000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000AB4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00F4000000FF000000E2000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000E2000000FF000000F34C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0006000000A3000000394C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C704700000000060000
+ 00B1000000FF000000F1000000384C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000006000000AF0000
+ 00FF000000FF000000E80000002A4C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C70470000000006000000AF000000FF0000
+ 00FF000000E80000002A4C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C70470000000006000000AF000000FF000000FF0000
+ 00E80000002A4C7047004C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047000000000E000000504C7047004C70
+ 47004C7047004C70470000000006000000B1000000FF000000FF000000E50000
+ 00274C7047004C7047004C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047000000000E000000C7000000FE000000694C70
+ 47004C70470000000006000000B0000000FF000000FF000000E80000002A4C70
+ 47004C7047004C7047004C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C70470000000050000000FD000000FF000000FD0000
+ 006300000006000000B1000000FF000000FF000000E5000000264C7047004C70
+ 47004C7047004C7047004C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C70470000000063000000FD000000FF0000
+ 00FD000000CD000000FF000000FF000000E5000000264C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047004C70470000000063000000FD0000
+ 00FF000000FF000000FF000000E5000000264C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047004C7047004C704700000000630000
+ 00FD000000FF000000E5000000264C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047004C7047004C7047004C7047000000
+ 0062000000E60000002A4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047004C7047004C7047004C7047004C70
+ 47000000000C4C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000A94C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000A9000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00F4000000FF000000E2000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000E2000000FF000000F34C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00AB000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000AA4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0014000000AB000000F3000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000F3000000AA000000144C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000120000002D000000490000004E0000003C000000144C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C704700000000210000007E0000
+ 00CD000000FF000000FF000000FF000000FF000000FF000000FF000000CB0000
+ 00760000001E4C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047000000001400000090000000F9000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000F90000008B0000000F4C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C70470000000036000000E5000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000E90000003D4C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C70470000000051000000F7000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000F90000005F4C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 470000000032000000F5000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FA000000514C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0014000000E5000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FA000000524C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0090000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FA000000524C7047004C7047000000000F0000006E4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C704700000000210000
+ 00F9000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FA000000524C7047004C70470000000010000000C9000000FB0000
+ 00254C7047004C7047004C7047004C7047004C7047004C7047000000007F0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FA000000514C7047004C70470000000010000000C9000000FF000000FF0000
+ 00864C7047004C7047004C7047004C7047004C7047004C704700000000CE0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FA0000
+ 00524C7047004C70470000000012000000CC000000FF000000FF000000FF0000
+ 00DA4C7047004C7047004C7047004C7047004C70470000000012000000FE0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000ED000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FA000000524C70
+ 47004C70470000000013000000CE000000FF000000FF000000FF000000FF0000
+ 00FE000000144C7047004C7047004C7047004C7047000000002E000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000800000001C000000DB0000
+ 00FF000000FF000000FF000000FF000000FF000000FA000000524C7047004C70
+ 470000000014000000CF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF0000003B4C7047004C7047004C7047004C70470000000048000000FF0000
+ 00FF000000FF000000FF000000FF000000A84C7047004C7047000000001C0000
+ 00DB000000FF000000FF000000FF000000FA000000524C7047004C7047000000
+ 0015000000D1000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF0000004C4C7047004C7047004C7047004C70470000000048000000FF0000
+ 00FF000000FF000000FF000000FF000000FA000000514C7047004C7047000000
+ 0018000000D6000000FF000000FA000000514C7047004C704700000000150000
+ 00D2000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000474C7047004C7047004C7047004C7047000000002E000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FA000000514C7047004C70
+ 470000000018000000D1000000514C7047004C70470000000016000000D20000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF0000002D4C7047004C7047004C7047004C70470000000012000000FE0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FA000000514C70
+ 47004C704700000000044C7047004C70470000000018000000D5000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FE000000114C7047004C7047004C7047004C7047004C704700000000CD0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FC0000
+ 00584C7047004C7047004C70470000000017000000D4000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00CB4C7047004C7047004C7047004C7047004C7047004C7047000000007E0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FC000000584C70470000000018000000D5000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 007C4C7047004C7047004C7047004C7047004C7047004C704700000000200000
+ 00F8000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FA00000069000000DA000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000F80000
+ 001F4C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 008E000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF0000008C4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0013000000E4000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000E3000000124C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 470000000032000000F5000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000F7000000364C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C70470000000050000000F6000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000F6000000504C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C70470000000030000000E1000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000E5000000354C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C704700000000130000008E000000F8000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000F80000008D000000124C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C704700000000200000007D0000
+ 00CD000000FF000000FF000000FF000000FF000000FF000000FE000000CC0000
+ 007C0000001F4C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000120000002D00000049000000490000002C000000114C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047000000000D0000
+ 007B000000A7000000AA000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000A70000007A0000
+ 000C4C7047004C7047004C7047004C7047004C70470000000004000000CA0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00C9000000044C7047004C7047004C7047004C7047000000003C000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF0000003A4C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00AA000000AA0000009C0000000E4C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000FF000000CC000000114C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000FF000000FF000000CC000000114C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047000000003B000000FF0000
+ 00FF000000FF000000FF000000FF000000B7000000084C7047004C7047000000
+ 0077000000FF000000FF000000FF000000FF000000CC000000114C7047004C70
+ 470000000063000000FD000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000394C7047004C7047004C7047004C70470000000004000000C90000
+ 00FF000000FF000000FF000000FF000000FF000000B3000000074C7047004C70
+ 470000000077000000FF000000FF000000FF000000FF000000CC000000114C70
+ 47004C70470000000066000000FE000000FF000000FF000000FF000000FF0000
+ 00C8000000044C7047004C7047004C7047004C7047004C7047000000000C0000
+ 0079000000A7000000AA000000AA000000AA000000AA0000005F4C7047004C70
+ 47004C70470000000077000000FF000000FF000000FF000000FF000000CC0000
+ 00114C7047004C70470000000060000000AA000000AA000000A7000000790000
+ 000C4C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C70470000000077000000FF000000FF000000FF000000FF0000
+ 00CC000000114C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C70470000000080000000FF000000FF000000FF0000
+ 00F5000000394C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C70470000000080000000FF000000F50000
+ 0043000000130000009C0000000E4C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047000000007B000000430000
+ 0013000000D0000000FF000000BF4C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047000000000A0000
+ 00D0000000FF000000FF000000AB4C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0077000000FF000000B6000000074C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 470000000038000000054C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047000000000D0000
+ 007A000000A7000000AA000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000A70000007A0000
+ 000C4C7047004C7047004C7047004C7047004C70470000000004000000CA0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00C9000000044C7047004C7047004C7047004C7047000000003D000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF0000003A4C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047000000
+ 001600000046000000034C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00D8000000FF000000824C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00D7000000FF000000814C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047000000
+ 001600000045000000034C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C704700000000174C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000063000000E20000
+ 00144C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C704700000000194C70
+ 47004C7047004C7047004C7047004C70470000000049000000FB000000FF0000
+ 00B3000000014C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047000000004E000000F20000
+ 003E4C7047004C7047004C7047000000002D000000F0000000FF000000FF0000
+ 00FF000000744C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C70470000000025000000F0000000FF0000
+ 00F40000003E4C7047000000001B000000E3000000FF000000FF000000FF0000
+ 00FF000000FB0000003B4C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047000000000B000000D3000000FF000000FF0000
+ 00FF000000F100000049000000D4000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000E1000000134C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C704700000000A1000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000B5000000014C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047000000000E000000550000005500000055000000550000
+ 0055000000550000005500000055000000550000005500000055000000550000
+ 0055000000550000005500000055000000124C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047000000003B000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000394C7047004C7047004C7047004C70470000000004000000C90000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00C7000000044C7047004C7047004C7047004C7047004C7047000000000C0000
+ 0079000000A7000000AA000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000A7000000780000
+ 000C4C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0015000000AC000000F4000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000F4000000AB000000144C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00AC000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000AA4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00F4000000FF000000E3000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000E3000000FF000000F34C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C70470000000071000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000714C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C704700000000AB000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000AB4C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C704700000000AB000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000AB4C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047000000003800000055000000550000
+ 00550000005500000055000000554C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C704700000000AB000000FF000000FF0000
+ 00FF000000FF000000FF000000FF4C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C704700000000AB000000FF000000FF0000
+ 00FF000000FF000000FF000000FF4C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047000000003800000055000000550000
+ 00550000005500000055000000554C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047000000001C000000550000
+ 005500000055000000550000005500000055000000C7000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF0000007F4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF0000007F4C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000FF000000FF000000FF000000FF0000007F4C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000FF000000FF000000FF0000007F4C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000FF000000FF0000007F4C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00F4000000FF000000E3000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000C6000000FF0000
+ 00FF000000FF000000804C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00AB000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000804C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0014000000AB000000F3000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00804C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C70470000000015000000AC000000F4000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000F4000000AC00000014000000AC000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000AB000000F4000000FF000000E2000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000C6000000FF0000
+ 00FF000000C6000000AA000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000E2000000FF000000F3000000FF000000FF000000A94C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000A9000000FF000000FF000000FF000000FF000000A94C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000A9000000FF000000FF000000FF000000FF000000A94C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000A9000000FF000000FF000000FF000000FF000000A94C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000550000000E00000055000000550000005500000055000000474C70
+ 4700000000A9000000FF000000FF000000FF000000FF000000A94C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C70470000000077000000FF000000FF000000F1000000384C70
+ 4700000000A9000000FF000000FF000000FF000000FF000000A94C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C70470000000077000000F1000000384C7047004C70
+ 4700000000A9000000FF000000FF000000FF000000FF000000A94C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047000000001C4C7047004C7047004C70
+ 4700000000A9000000FF000000FF000000FF000000FF000000A94C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000A9000000FF000000FF000000FF000000FF000000A94C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000A9000000FF000000FF000000FF000000FF000000A94C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C70470000000055000000FF0000
+ 00FF000000554C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000A9000000FF000000FF000000F4000000FF000000E2000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000AA000000AA000000AA000000AA000000AA000000C6000000FF0000
+ 00FF000000C6000000AA000000AA000000AA000000AA000000AA000000AA0000
+ 00AA000000E2000000FF000000F3000000AB000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000AA00000014000000AB000000F3000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000F3000000AA000000144C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000AB4C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000AB000000FF000000FF000000FF000000FF000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000AB4C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000AB000000FF000000FF000000FF000000FF000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000E3000000AA000000AA000000AA000000714C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 470000000071000000AA000000AA000000AA000000E3000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00AB000000AB000000714C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C70470000000071000000AB000000AB4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C704700000000300000002F4C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047000000002F000000FD000000FD0000002F4C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047000000002F000000FD000000FD0000002E4C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047000000002F0000002F4C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00AB000000AB000000714C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C70470000000071000000AB000000AB4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000AA4C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C704700000000AA000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000E3000000AA000000AA000000AA000000714C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 470000000071000000AA000000AA000000AA000000E3000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000AB4C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000AB000000FF000000FF000000FF000000FF000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000AB4C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000AB000000FF000000FF000000FF000000FF000000FF000000FF4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047000000000A0000006C0000009F0000008D000000474C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C704700000000120000003E0000005500000055000000550000
+ 001F0000000D000000D7000000FF000000FF000000FF000000FF000000804C70
+ 470000000009000000350000004B0000004A000000154C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 470000000021000000AC000000FA000000FF000000FF000000FF000000FF0000
+ 003400000068000000FF000000FF000000FF000000FF000000FF000000F90000
+ 009D000000F6000000FF000000FF000000FF000000FC00000094000000064C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0037000000EE000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 000F0000009C000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000AA4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C704700000000150000
+ 00E6000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 0007000000A3000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 004D4C7047004C7047004C7047004C7047004C7047004C7047000000008D0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 001E00000089000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00B74C7047004C7047004C7047004C7047004C70470000000003000000ED0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00540000004B000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00F6000000054C7047004C7047004C7047004C7047000000002F000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00A200000007000000E8000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF0000002D4C7047004C7047004C7047004C7047000000004B000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00F80000001700000079000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000AD000000AD000000FF0000
+ 00FF000000424C7047004C7047004C7047004C7047000000004B000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF0000009800000008000000DA000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000AD000000AD000000FF0000
+ 00FF000000504C7047004C7047004C7047004C70470000000031000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FC0000004100000031000000F3000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000474C7047004C7047004C7047004C70470000000004000000EF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000E8000000270000003B000000E3000000FF000000FE0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF0000002E4C7047004C7047004C7047004C7047004C704700000000970000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000E70000004C0000000C0000006B000000FF0000
+ 00B000000062000000CA000000FF000000FF000000FF000000FF000000FF0000
+ 00FF0000000F4C7047004C7047004C7047004C7047004C704700000000300000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000EC000000F8000000DF0000
+ 00094C7047000000000C000000DE000000FF000000FF000000FF000000FF0000
+ 00EB4C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 00CC000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000794C70
+ 47004C7047004C70470000000079000000FF000000FF000000FF000000FF0000
+ 00B74C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0076000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000
+ 00FF000000FF000000FF000000FF000000FF000000FF000000FF000000334C70
+ 47004C7047004C7047000000002E000000FF000000FF000000FF000000FF0000
+ 00754C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0035000000FF000000FF000000FF000000FF000000FF000000C7000000550000
+ 0055000000C7000000FF000000FF000000FF000000FF000000FC000000074C70
+ 47004C7047004C70470000000014000000FF000000FF000000FF000000FE0000
+ 00254C7047004C7047004C7047004C7047004C7047004C7047004C7047000000
+ 0005000000F8000000FF000000FF000000FF000000FF000000AA4C7047004C70
+ 4700000000AA000000FF000000FF000000FF000000FF000000E04C7047004C70
+ 47004C7047004C70470000000008000000FF000000FF000000FF000000B14C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000DD000000FF000000FF000000FF000000FF000000AA4C7047004C70
+ 4700000000AA000000FF000000FF000000FF000000FF000000CD4C7047004C70
+ 47004C7047000000000700000086000000FF000000FF000000ED0000001F4C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000C1000000FF000000FF000000FF000000FF000000AA4C7047004C70
+ 4700000000AA000000FF000000FF000000FF000000FF000000BF4C7047004C70
+ 47004C70470000000055000000FF000000FE000000B7000000244C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000B3000000FF000000FF000000FF000000FF000000AA4C7047004C70
+ 4700000000AA000000FF000000FF000000FF000000FF000000B14C7047004C70
+ 47004C7047000000001C0000004B0000001E4C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000B0000000FF000000FF000000FF000000FF000000AA4C7047004C70
+ 4700000000AA000000FF000000FF000000FF000000FF000000A74C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 4700000000AC000000FF000000FF000000FF000000FF000000AA4C7047004C70
+ 4700000000AA000000FF000000FF000000FF000000FF000000A94C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C7047004C7047004C7047004C7047004C7047004C70
+ 47004C7047004C7047004C704700
+ }
+ end
+ object ALWidgets: TActionList
+ Images = ILWidgets
+ left = 344
+ top = 16
+ object AGo: TAction
+ Caption = 'AGo'
+ ImageIndex = 0
+ OnExecute = AGoExecute
+ OnUpdate = AGoUpdate
+ end
+ object AGoExternal: TAction
+ Caption = 'Go External'
+ Hint = 'Design using external browser'
+ ImageIndex = 1
+ OnExecute = AGoExternalExecute
+ end
+ end
+ object tmrShowChromium: TTimer
+ Enabled = False
+ Interval = 300
+ OnTimer = tmrShowChromiumTimer
+ left = 84
+ top = 116
+ end
+end
diff --git a/demo/webwidget/nativedesign/frmmain.pp b/demo/webwidget/nativedesign/frmmain.pp
new file mode 100644
index 0000000..382d5f4
--- /dev/null
+++ b/demo/webwidget/nativedesign/frmmain.pp
@@ -0,0 +1,556 @@
+unit frmmain;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, webideintf, Forms, Controls, Graphics, Dialogs, EditBtn, ExtCtrls, ComCtrls, StdCtrls, ActnList,
+ {$IFDEF LINUX}
+ WebBrowserCtrls, WebBrowserIntf,
+ {$ELSE}
+ {$IFDEF WINDOWS}
+ Windows, Messages, uCEFChromium, uCEFWindowParent, uCEFChromiumWindow, uCEFTypes, uCEFInterfaces, uCEFWinControl, uCEFApplication,
+ {$ELSE}
+ {$ERROR Unsupported platform}
+ {$ENDIF}
+ {$ENDIF} fpJSON;
+
+type
+
+ { TMainForm }
+
+ TMainForm = class(TForm)
+ AGoExternal: TAction;
+ AGo: TAction;
+ ALWidgets: TActionList;
+ FEProject: TFileNameEdit;
+ ILWidgets: TImageList;
+ MLog: TMemo;
+ PCDesigner: TPageControl;
+ Project: TLabel;
+ PBottom: TPanel;
+ TBExternalGo: TToolButton;
+ tmrShowChromium: TTimer;
+ TSInspector: TTabSheet;
+ TSBrowser: TTabSheet;
+ TSLog: TTabSheet;
+ TBWidgets: TToolBar;
+ TBGo: TToolButton;
+ ToolButton1: TToolButton;
+ procedure AGoExecute(Sender: TObject);
+ procedure AGoExternalExecute(Sender: TObject);
+ procedure AGoUpdate(Sender: TObject);
+ procedure DEProjectEditingDone(Sender: TObject);
+ procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
+ procedure FormCreate(Sender: TObject);
+ procedure FormShow(Sender: TObject);
+ procedure tmrShowChromiumTimer(Sender: TObject);
+ private
+ FClientID : Int64; // Just one for now
+ FDesignCaption : String;
+ FWebIDEIntf : TIDEServer;
+ FWidgetCount : Integer;
+ FWidgets : Array of String;
+ FURL : String;
+ FURLCount : Integer;
+ FCanClose : Boolean;
+ FAllowGo: Boolean;
+{$IFDEF LINUX}
+ FWBDesign : TWebBrowser;
+ FWIDesign : TWebInspector;
+ FLastEmbeddedURI : String;
+ procedure wbDesignConsoleMessage(Sender: TObject; const Message, Source: string; Line: Integer);
+ procedure wbDesignError(Sender: TObject; const Uri: string; ErrorCode: LongWord; const ErrorMessage: string; var Handled: Boolean);
+ procedure wbDesignFavicon(Sender: TObject);
+ procedure wbDesignHitTest(Sender: TObject; X, Y: Integer; HitTest: TWebHitTest; const Link, Media: string);
+ procedure wbDesignLoadStatusChange(Sender: TObject);
+ procedure wbDesignLocationChange(Sender: TObject);
+ procedure wbDesignNavigate(Sender: TObject; const Uri: string; var aAction: TWebNavigateAction);
+ procedure wbDesignProgress(Sender: TObject; Progress: Integer);
+ procedure wbDesignRequest(Sender: TObject; var Uri: string);
+ procedure wbDesignScriptDialog(Sender: TObject; Dialog: TWebScriptDialog; const Message: string; var Input: string; var Accepted: Boolean; var Handled: Boolean);
+{$ENDIF}
+{$IFDEF WINDOWS}
+ FClosing : Boolean;
+ cwDesign : TChromiumWindow;
+ procedure WMMove(var aMessage : TWMMove); message WM_MOVE;
+ procedure WMMoving(var aMessage : TMessage); message WM_MOVING;
+ // You also have to handle these two messages to set GlobalCEFApp.OsmodalLoop
+ procedure WMEnterMenuLoop(var aMessage: TMessage); message WM_ENTERMENULOOP;
+ procedure WMExitMenuLoop(var aMessage: TMessage); message WM_EXITMENULOOP;
+ procedure cwBeforeClose(Sender: TObject);
+ procedure cwClose(Sender: TObject);
+ procedure cwAfterCreated(Sender: TObject);
+ procedure cwOnBeforePopup(Sender: TObject;
+ const browser: ICefBrowser; const frame: ICefFrame; const targetUrl,
+ targetFrameName: ustring; targetDisposition: TCefWindowOpenDisposition;
+ userGesture: Boolean; const popupFeatures: TCefPopupFeatures;
+ var windowInfo: TCefWindowInfo; var client: ICefClient;
+ var settings: TCefBrowserSettings;
+ var extra_info: ICefDictionaryValue;
+ var noJavascriptAccess: Boolean;
+ var Result: Boolean);
+{$ENDIF}
+ function GetProjectURL: String;
+ procedure DoAddWidget(Sender: TObject);
+ procedure DoAction(Sender: TObject; aExchange: TIDEExchange);
+ procedure DoClientCame(Sender: TObject; aClient: TIDEClient);
+ procedure DoClientLeft(Sender: TObject; aClient: TIDEClient);
+ procedure DoLogRequest(Sender: TObject; aURL: String);
+ procedure IsWidgetEnabled(Sender: TObject);
+ procedure LogRequest;
+ Procedure RegisterWidgets;
+ Procedure RegisterWidget(aWidget: String; aImageIndex : Integer);
+ procedure SetUpEmbeddedBrowser;
+ public
+ Procedure Log(Msg : String);
+ Procedure Log(Fmt : String; Args : Array of const);
+ end;
+
+var
+ MainForm: TMainForm;
+
+implementation
+
+uses lclintf, fpmimetypes;
+
+{$R *.lfm}
+
+{ TMainForm }
+
+procedure TMainForm.DEProjectEditingDone(Sender: TObject);
+begin
+ FWebIDEIntf.ProjectDir:=ExtractFilePath(FEProject.FileName);
+end;
+
+procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: boolean);
+begin
+ FWebIDEIntf.Active:=False;
+ CanClose:=FCanClose;
+{$IFDEF WINDOWS}
+ if not(FClosing) then
+ begin
+ FClosing := True;
+ Visible := False;
+ cwDesign.CloseBrowser(True);
+ end;
+{$ENDIF}
+end;
+
+Function TMainForm.GetProjectURL : String;
+
+begin
+ Result:=Format('http://localhost:%d/Project/%s',[FWebIDEIntf.Port,ExtractFileName(FEProject.FileName)]);
+end;
+
+procedure TMainForm.AGoExecute(Sender: TObject);
+
+Var
+ URL : String;
+
+begin
+ URL:=GetProjectURL;
+ Log('Going to URL: %s',[URL]);
+{$IFDEF LINUX}
+ FWBDesign.Location:=URL;
+{$ENDIF}
+{$IFDEF WINDOWS}
+ cwDesign.LoadURL(URL);
+{$ENDIF}
+end;
+
+procedure TMainForm.AGoExternalExecute(Sender: TObject);
+Var
+ URL : String;
+
+begin
+ URL:=GetProjectURL;
+ Log('Going to URL: %s',[URL]);
+ OpenURL(URL);
+end;
+
+procedure TMainForm.AGoUpdate(Sender: TObject);
+begin
+ (Sender as Taction).Enabled:=FAllowGo;
+end;
+
+procedure TMainForm.FormCreate(Sender: TObject);
+
+
+
+begin
+ FAllowGo:=False;
+ FDesignCaption:=Caption;
+{$IFDEF Linux}
+ MimeTypes.LoadFromFile('/etc/mime.types');
+{$ENDIF}
+{$IFDEF WINDOWS}
+ MimeTypes.LoadFromFile(ExtractFilePath(Paramstr(0))+'mime.types');
+{$ENDIF}
+ FEProject.FileName:=ExtractFilePath(Paramstr(0))+'designdemo'+PathDelim+'designdemo.html';
+ FWebIDEIntf:=TIDEServer.Create(Self);
+ FWebIDEIntf.ProjectDir:=ExtractFilePath(FEProject.FileName);
+ FWebIDEIntf.OnClientAdded:=@DoClientCame;
+ FWebIDEIntf.OnClientRemoved:=@DoClientLeft;
+ FWebIDEIntf.OnRequest:=@DoLogRequest;
+ FWebIDEIntf.OnAction:=@DoAction;
+ FWebIDEIntf.Active:=True;
+ RegisterWidgets;
+ SetUpEmbeddedBrowser;
+end;
+
+procedure TMainForm.FormShow(Sender: TObject);
+begin
+{$IFDEF WINDOWS}
+ with cwDesign do
+ begin
+ ChromiumBrowser.OnBeforePopup := @cwOnBeforePopup;
+ if not CreateBrowser then
+ tmrShowChromium.Enabled := True;
+ end;
+{$ENDIF}
+end;
+
+procedure TMainForm.tmrShowChromiumTimer(Sender: TObject);
+begin
+ tmrShowChromium.Enabled := False;
+{$IFDEF WINDOWS}
+ With cwDesign do
+ if not (CreateBrowser or Initialized) then
+ tmrShowChromium.Enabled := True;
+{$ENDIF}
+end;
+
+{$IFDEF WINDOWS}
+procedure TMainForm.SetUpEmbeddedBrowser;
+
+begin
+ FCanClose:=False;
+ cwDesign:=TChromiumWindow.Create(Self);
+ With cwDesign do
+ begin
+ Parent:=TSBrowser;
+ Align:=alClient;
+ OnClose:=@cwClose;
+ OnBeforeClose:=@cwBeforeClose;
+ OnAfterCreated:=@cwAfterCreated;
+ end;
+ TSInspector.TabVisible:=False;
+end;
+
+procedure TMainForm.WMMove(var aMessage : TWMMove);
+
+begin
+ inherited;
+ if (cwDesign <> nil) then
+ cwDesign.NotifyMoveOrResizeStarted;
+end;
+
+procedure TMainForm.WMMoving(var aMessage : TMessage);
+
+begin
+ inherited;
+ if (cwDesign <> nil) then
+ cwDesign.NotifyMoveOrResizeStarted;
+end;
+
+procedure TMainForm.WMEnterMenuLoop(var aMessage: TMessage);
+
+begin
+ inherited;
+ if (aMessage.wParam = 0) and (GlobalCEFApp <> nil) then
+ GlobalCEFApp.OsmodalLoop := True;
+end;
+
+procedure TMainForm.WMExitMenuLoop(var aMessage: TMessage);
+
+begin
+ inherited;
+ if (aMessage.wParam = 0) and (GlobalCEFApp <> nil) then
+ GlobalCEFApp.OsmodalLoop := False;
+end;
+
+procedure TMainForm.cwBeforeClose(Sender: TObject);
+begin
+ FCanClose := True;
+ PostMessage(Handle, WM_CLOSE, 0, 0);
+end;
+
+
+procedure TMainForm.cwClose(Sender: TObject);
+begin
+ // DestroyChildWindow will destroy the child window created by CEF at the top of the Z order.
+ if not(cwDesign.DestroyChildWindow) then
+ begin
+ FCanClose := True;
+ Close;
+ end;
+end;
+
+procedure TMainForm.cwOnBeforePopup(Sender: TObject;
+ const browser: ICefBrowser; const frame: ICefFrame; const targetUrl,
+ targetFrameName: ustring; targetDisposition: TCefWindowOpenDisposition;
+ userGesture: Boolean; const popupFeatures: TCefPopupFeatures;
+ var windowInfo: TCefWindowInfo; var client: ICefClient;
+ var settings: TCefBrowserSettings;
+ var extra_info: ICefDictionaryValue;
+ var noJavascriptAccess: Boolean;
+ var Result: Boolean);
+begin
+ // For simplicity, this demo blocks all popup windows and new tabs
+ Result := (targetDisposition in [WOD_NEW_FOREGROUND_TAB, WOD_NEW_BACKGROUND_TAB, WOD_NEW_POPUP, WOD_NEW_WINDOW]);
+end;
+
+procedure TMainForm.cwAfterCreated(Sender: TObject);
+begin
+ // Now the browser is fully initialized we can load the initial web page.
+ FAllowGo:=True;
+end;
+
+{$ENDIF}
+
+{$IFDEF LINUX}
+procedure TMainForm.SetUpEmbeddedBrowser;
+
+begin
+ FAllowGo:=True;
+ FCanClose:=True;
+ FWBDesign:=TWebBrowser.Create(Self);
+ With FWBDesign do
+ begin
+ Parent:=TSBrowser;
+ Align:=alClient;
+ DesignMode := False;
+ SourceView := False;
+ ZoomContent := False;
+ ZoomFactor := 1;
+ { lots of logging }
+ OnConsoleMessage:=@wbDesignConsoleMessage;
+ OnScriptDialog:=@wbDesignScriptDialog;
+ OnError:=@wbDesignError;
+ OnFavicon:=@wbDesignFavicon;
+ OnHitTest:=@wbDesignHitTest;
+ OnLoadStatusChange:=@wbDesignLoadStatusChange;
+ OnLocationChange:=@wbDesignLocationChange;
+ OnNavigate:=@wbDesignNavigate;
+ OnProgress:=@wbDesignProgress;
+ OnRequest:=@wbDesignRequest;
+ end;
+ FWIDesign:=TWebInspector.Create(Self);
+ With FWIDesign do
+ begin
+ Parent:=TSInspector;
+ Align:=alClient;
+ Active:=True;
+ WebBrowser:=FWBDesign;
+ end;
+ TSInspector.TabVisible:=true;
+ PCDesigner.ActivePage:=TSBrowser;
+end;
+
+procedure TMainForm.wbDesignConsoleMessage(Sender: TObject; const Message, Source: string; Line: Integer);
+begin
+ Log('Console message: %s (%s: %d)',[Message,Source,Line]);
+end;
+
+
+procedure TMainForm.wbDesignError(Sender: TObject; const Uri: string; ErrorCode: LongWord; const ErrorMessage: string;
+ var Handled: Boolean);
+begin
+ Log('Error: %s, code: %d, Message: %s',[URI,ErrorCode,ErrorMessage]);
+ Handled:=True;
+end;
+
+procedure TMainForm.wbDesignFavicon(Sender: TObject);
+begin
+ Log('Favicon available/missed');
+end;
+
+procedure TMainForm.wbDesignHitTest(Sender: TObject; X, Y: Integer; HitTest: TWebHitTest; const Link, Media: string);
+begin
+// Log('Hit test (%d,%d) link: %s, media: %s',[x,y,link,media]);
+end;
+
+procedure TMainForm.wbDesignLoadStatusChange(Sender: TObject);
+begin
+ Log('Load status change');
+end;
+
+procedure TMainForm.wbDesignLocationChange(Sender: TObject);
+begin
+ Log('Location change');
+end;
+
+procedure TMainForm.wbDesignNavigate(Sender: TObject; const Uri: string; var aAction: TWebNavigateAction);
+begin
+ Log('Navigation: %s',[URI]);
+ aAction:=naAllow;
+end;
+
+procedure TMainForm.wbDesignProgress(Sender: TObject; Progress: Integer);
+begin
+ Log('Progress: %d',[Progress])
+end;
+
+procedure TMainForm.wbDesignRequest(Sender: TObject; var Uri: string);
+begin
+ if Uri<>FLastEmbeddedURI then
+ Log('Embedded browser doing request : %s',[URI]);
+ FLastEmbeddedURI:=URI;
+end;
+
+procedure TMainForm.wbDesignScriptDialog(Sender: TObject; Dialog: TWebScriptDialog; const Message: string; var Input: string;
+ var Accepted: Boolean; var Handled: Boolean);
+begin
+ Log('Script dialog Message: %s; Input : %s',[message,Input]);
+ Accepted:=true;
+ Handled:=true;
+end;
+{$ENDIF}
+
+procedure TMainForm.DoAction(Sender: TObject; aExchange: TIDEExchange);
+
+var
+ PayJSON : TJSONObject;
+
+begin
+ payJSON:=Nil;
+ if Not (aExchange.Payload is TJSONObject) then
+ begin
+ Log('Payload is not JSON Object');
+ exit;
+ end;
+ payJSON:=aExchange.Payload as TJSONObject;
+ with aExchange do
+ case Name of
+ 'create':
+ Log('Browser created widget of class %s, name %s',[PayJSON.Get('class',''),PayJSON.Get('widget','')]);
+ 'select':
+ begin
+ Log('Browser selected widget of class %s, name %s',[PayJSON.Get('class',''),PayJSON.Get('widget','')]);
+ Log('Selected widget state: '+PayJSON.Get('state',''));
+ end;
+ end;
+end;
+
+procedure TMainForm.DoClientCame(Sender: TObject; aClient: TIDEClient);
+begin
+ if FClientID>0 then
+ Log('Ignoring second client (id: %d) attachment.',[aClient.ID])
+ else
+ begin
+ FClientID:=aClient.ID;
+ Caption:=FDesignCaption+Format(' [Client: %d]',[FClientID]);
+ end;
+end;
+
+procedure TMainForm.DoAddWidget(Sender: TObject);
+
+Var
+ Cmd : TIDECommand;
+ aName : String;
+
+begin
+ aName:=FWidgets[(Sender as TAction).Tag];
+ Cmd:=TIDECommand.Create;
+ Cmd.NeedsConfirmation:=True;
+ Cmd.ClientID:=FClientID;
+ Cmd.name:='addWidget';
+ Cmd.PayLoad:=TJSONObject.Create(['class','T'+aName+'Widget']);
+ FWebIDEIntf.SendCommand(cmd);
+end;
+
+procedure TMainForm.DoClientLeft(Sender: TObject; aClient: TIDEClient);
+begin
+ if (aClient.ID=FClientID) then
+ begin
+ FClientID:=-1;
+ Caption:=FDesignCaption;
+ end;
+end;
+
+procedure TMainForm.LogRequest;
+
+begin
+ if (FURLCount=1) then // avoid excessive logging, command loop is on very short interval.
+ Log('Internal server request received: '+FURL);
+end;
+
+procedure TMainForm.DoLogRequest(Sender: TObject; aURL: String);
+begin
+ if (aURL<>FURL) then
+ begin
+ FURLCount:=1;
+ FURL:=aURL
+ end
+ else
+ Inc(FURLCount);
+ TThread.Synchronize(TThread.CurrentThread,@LogRequest);
+end;
+
+procedure TMainForm.IsWidgetEnabled(Sender: TObject);
+begin
+ (Sender as TAction).Enabled:=(FClientID<>-1);
+end;
+
+procedure TMainForm.RegisterWidgets;
+begin
+ SetLength(FWidgets,9);
+ FWidgetCount:=0;
+ RegisterWidget('Button',2);
+ RegisterWidget('Checkbox',3);
+ RegisterWidget('Radio',4);
+ RegisterWidget('Edit',5);
+ RegisterWidget('Image',6);
+ RegisterWidget('TextArea',7);
+ RegisterWidget('Select',8);
+ RegisterWidget('Container',9);
+ RegisterWidget('Jumbo',10);
+end;
+
+procedure TMainForm.RegisterWidget(aWidget: String; aImageIndex: Integer);
+
+Var
+ A : TAction;
+ B : TToolButton;
+ L,i : Integer;
+
+begin
+ FWidgets[FWidgetCount]:=aWidget;
+ A:=TAction.Create(Self);
+ A.ActionList:=ALWidgets;
+ A.Name:='AAdd'+aWidget;
+ A.Hint:='Add '+aWidget;
+ A.Caption:='Add '+aWidget;
+ A.ImageIndex:=aImageIndex;
+ A.Tag:=FWidgetCount;
+ A.OnExecute:=@DoAddWidget;
+ A.OnUpdate:=@IsWidgetEnabled;
+ L:=0;
+ For I:=0 to TBWidgets.ControlCount-1 do
+ if TBWidgets.Controls[i].BoundsRect.Right>L then
+ L:=TBWidgets.Controls[i].BoundsRect.Right;
+ B:=TToolButton.Create(Self);
+ B.Parent:=TBWidgets;
+ B.Left:=L;
+ B.Height:=32;
+ B.Action:=A;
+ inc(FWidgetCount);
+// TBWidgets.AddControl;;
+
+end;
+
+procedure TMainForm.Log(Msg: String);
+begin
+ MLog.Lines.Add(Msg);
+end;
+
+procedure TMainForm.Log(Fmt: String; Args: array of const);
+begin
+ Log(Format(Fmt,Args));
+end;
+
+
+end.
+
diff --git a/demo/webwidget/nativedesign/nativedesigner.ico b/demo/webwidget/nativedesign/nativedesigner.ico
new file mode 100644
index 0000000..25c186a
Binary files /dev/null and b/demo/webwidget/nativedesign/nativedesigner.ico differ
diff --git a/demo/webwidget/nativedesign/nativedesigner.lpi b/demo/webwidget/nativedesign/nativedesigner.lpi
new file mode 100644
index 0000000..215dd70
--- /dev/null
+++ b/demo/webwidget/nativedesign/nativedesigner.lpi
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/webwidget/nativedesign/nativedesigner.lpr b/demo/webwidget/nativedesign/nativedesigner.lpr
new file mode 100644
index 0000000..ee50568
--- /dev/null
+++ b/demo/webwidget/nativedesign/nativedesigner.lpr
@@ -0,0 +1,22 @@
+program nativedesigner;
+
+{$mode objfpc}{$H+}
+
+uses
+ {$IFDEF UNIX}
+ cthreads,
+ {$ENDIF}
+ Interfaces, // this includes the LCL widgetset
+ Forms, frmmain, webideintf
+ { you can add units after this };
+
+{$R *.res}
+
+begin
+ RequireDerivedFormResource:=True;
+ Application.Scaled:=True;
+ Application.Initialize;
+ Application.CreateForm(TMainForm, MainForm);
+ Application.Run;
+end.
+
diff --git a/demo/webwidget/nativedesign/nativedesigner.res b/demo/webwidget/nativedesign/nativedesigner.res
new file mode 100644
index 0000000..0ce2e2b
Binary files /dev/null and b/demo/webwidget/nativedesign/nativedesigner.res differ
diff --git a/demo/webwidget/nativedesign/webideintf.pp b/demo/webwidget/nativedesign/webideintf.pp
new file mode 100644
index 0000000..ddab347
--- /dev/null
+++ b/demo/webwidget/nativedesign/webideintf.pp
@@ -0,0 +1,817 @@
+unit webideintf;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ fpMimeTypes, Classes, SysUtils, StrUtils, httpdefs, fphttpclient,custhttpapp, fpjson, jsonparser, httproute;
+
+Const
+ SFilesURL = '/Project/';
+ SIDEURL = '/IDE/';
+
+Type
+ TClientObject = Class(TObject)
+ Private
+ FID: Int64;
+ public
+ Procedure FromJSON(aJSON : TJSONObject); virtual; abstract;
+ Procedure ToJSON(aJSON : TJSONObject); virtual; abstract;
+ Property ID : Int64 Read FID Write FID;
+ end;
+ { TIDEClient }
+
+ TIDEClient = Class(TClientObject)
+ private
+ FURL: String;
+ Public
+ Procedure FromJSON(aJSON : TJSONObject); override;
+ Procedure ToJSON(aJSON : TJSONObject); override;
+ Property URL : String Read FURL Write FURL;
+ end;
+ { TIDEExchange }
+
+ TIDEExchange = Class(TClientObject)
+ private
+ FClientID: Int64;
+ FName: String;
+ FPayLoad: TJSONData;
+ Public
+ Destructor Destroy; override;
+ Procedure FromJSON(aJSON : TJSONObject); override;
+ Procedure ToJSON(aJSON : TJSONObject); override;
+ Property ClientID : Int64 Read FClientID Write FClientID;
+ Property Name : String Read FName Write FName;
+ Property PayLoad : TJSONData Read FPayLoad Write FPayLoad;
+ end;
+
+ TIDEAction = Class(TIDEExchange)
+ end;
+
+ { TClientObjectList }
+
+ TClientObjectList = Class(TThreadList)
+ Public
+ Function FindID(aID : int64) : TClientObject;
+ end;
+
+ { TIDECommand }
+
+ TIDECommand = Class(TIDEExchange)
+ private
+ FConfirmed: Boolean;
+ FNeedsConfirmation: Boolean;
+ FSent: Boolean;
+ Public
+ Property NeedsConfirmation : Boolean Read FNeedsConfirmation Write FNeedsConfirmation;
+ Property Sent : Boolean Read FSent Write FSent;
+ Property Confirmed : Boolean Read FConfirmed Write FConfirmed;
+ end;
+
+ { TIDEThread }
+
+ TIDEThread = Class(TThread)
+ Private
+ FHandler : TFPHTTPServerHandler;
+ FExceptionClass : String;
+ FExceptionMessage : String;
+ Public
+ Constructor Create(aHandler : TFPHTTPServerHandler);
+ Procedure Execute; override;
+ end;
+
+
+ TIDENotification = Procedure(Sender : TObject; aExchange : TIDEExchange) of object;
+ TIDEClientNotification = Procedure(Sender : TObject; aClient : TIDEClient) of object;
+ TIDERequestNotification = Procedure(Sender : TObject; aURL : String) of object;
+
+ { TIDEServer }
+
+ TIDEServer = Class(TComponent)
+ private
+ FOnRequest: TIDERequestNotification;
+ FQuitting : Boolean;
+ FClients,
+ FCommands,
+ FActions : TClientObjectList;
+ FIDCounter: Int64;
+ FOnAction: TIDENotification;
+ FOnClient: TIDEClientNotification;
+ FOnClientRemoved: TIDEClientNotification;
+ FOnConfirmCommand: TIDENotification;
+ FProjectDir: String;
+ FWebHandler : TFPHTTPServerHandler;
+ FThread : TIDEThread;
+ FLastAction : TIDEAction;
+ FLastCommand : TIDECommand;
+ FLastClient : TIDEClient;
+ function CheckClient(aRequest: TRequest): INt64;
+ procedure DeActivatedThread(Sender: TObject);
+ function Do404(is404: boolean; aResponse: TResponse): Boolean;
+ procedure DoEvent(aProc: TThreadMethod);
+ procedure DoQuit(ARequest: TRequest; AResponse: TResponse);
+ procedure DoRouteRequest(Sender: TObject; ARequest: TRequest; AResponse: TResponse);
+ function GetAction(Index : Integer): TIDEAction;
+ function GetActionCount: Integer;
+ function GetPort: Integer;
+ function GetActive: Boolean;
+ procedure SetActive(AValue: Boolean);
+ procedure SetPort(AValue: Integer);
+ procedure SetProjectDir(AValue: String);
+ Protected
+ procedure RegisterRoutes; virtual;
+ // HTTP request extraction
+ procedure GetClientObjectFromRequest(ARequest: TRequest; AObject: TClientObject);
+ function GetActionFromRequest(ARequest: TRequest): TIDEAction;
+ function GetCommandFromRequest(ARequest: TRequest): TIDECommand;
+ function GetClientFromRequest(ARequest: TRequest): TIDEClient;
+ function GetJSONFromRequest(ARequest: TRequest): TJSONObject;
+ // Sending responses
+ procedure SendClientObjectResponse(AObject: TClientObject; AResponse: TResponse);
+ Procedure SendJSONResponse(aJSON : TJSONObject; aResponse : TResponse);
+ // HTTP route handlers
+ procedure DoDeleteAction(ARequest: TRequest; AResponse: TResponse); virtual;
+ procedure DoDeleteClient(ARequest: TRequest; AResponse: TResponse); virtual;
+ procedure DoGetCommand(ARequest: TRequest; AResponse: TResponse);virtual;
+ procedure DoGetFile(ARequest: TRequest; AResponse: TResponse);virtual;
+ procedure DoPostAction(ARequest: TRequest; AResponse: TResponse);virtual;
+ procedure DoPostClient(ARequest: TRequest; AResponse: TResponse);virtual;
+ procedure DoPutCommand(ARequest: TRequest; AResponse: TResponse);virtual;
+ // Event handler synchronisation. Rework this to objects
+ Procedure DoOnAction;
+ Procedure DoOnConfirmCommand;
+ Procedure DoOnClientAdded;
+ Procedure DoOnClientRemoved;
+ Public
+ Constructor Create(aOwner : TComponent); override;
+ Destructor Destroy; override;
+ Function GetNextCounter : Int64;
+ // Public API to communicate with browser
+ Function SendCommand(aCommand : TIDECommand) : Int64;
+ Procedure GetClientActions(aClientID : Int64; aList : TFPList);
+ Function DeleteAction(aID: Int64; Const aClientID : Int64 = -1): Boolean;
+ // Public properties
+ Property ProjectDir : String Read FProjectDir Write SetProjectDir;
+ Property Port : Integer Read GetPort Write SetPort;
+ Property Active : Boolean read GetActive write SetActive;
+ Property ActionCount : Integer Read GetActionCount;
+ Property Action[Index : Integer] : TIDEAction Read GetAction;
+ // Events
+ Property OnRequest : TIDERequestNotification Read FOnRequest Write FOnRequest;
+ Property OnConfirmCommand : TIDENotification Read FOnConfirmCommand Write FOnConfirmCommand;
+ Property OnAction : TIDENotification Read FOnAction Write FOnAction;
+ Property OnClientAdded : TIDEClientNotification Read FOnClient Write FOnClient;
+ Property OnClientRemoved : TIDEClientNotification Read FOnClientRemoved Write FOnClientRemoved;
+ end;
+
+implementation
+
+{ TClientObjectList }
+
+
+function TClientObjectList.FindID(aID: int64): TClientObject;
+
+Var
+ L : TList;
+ I : integer;
+
+begin
+ Result:=Nil;
+ L:=LockList;
+ try
+ I:=L.Count-1;
+ While (Result=Nil) and (I>=0) do
+ begin
+ Result:=TClientObject(L[i]);
+ if Result.ID<>aID then
+ Result:=nil;
+ Dec(I);
+ end;
+ finally
+ UnlockList;
+ end;
+end;
+
+{ TIDEClient }
+
+procedure TIDEClient.FromJSON(aJSON: TJSONObject);
+begin
+ FID:=aJSON.Get('id',Int64(-1));
+ FURL:=aJSON.Get('url','');
+end;
+
+procedure TIDEClient.ToJSON(aJSON: TJSONObject);
+begin
+ aJSON.Add('id',ID);
+ aJSON.Add('url',url);
+end;
+
+{ TIDEExchange }
+
+destructor TIDEExchange.Destroy;
+begin
+ FreeAndNil(FPayload);
+ Inherited;
+end;
+
+procedure TIDEExchange.FromJSON(aJSON: TJSONObject);
+
+Var
+ P : TJSONObject;
+
+begin
+ ID:=aJSON.Get('id',Int64(0));
+ Name:=aJSON.Get('name','');
+ P:=aJSON.Get('payload',TJSONObject(Nil));
+ if Assigned(P) then
+ Payload:=aJSON.Extract('payload');
+end;
+
+procedure TIDEExchange.ToJSON(aJSON: TJSONObject);
+begin
+ aJSON.Add('id',ID);
+ aJSON.Add('name',name);
+ if Assigned(Payload) then
+ aJSON.Add('payload',Payload.Clone);
+end;
+
+{ TIDEThread }
+
+
+constructor TIDEThread.Create(aHandler: TFPHTTPServerHandler);
+begin
+ FHandler:=AHandler;
+ FreeOnTerminate:=True;
+ Inherited Create(False);
+end;
+
+procedure TIDEThread.Execute;
+begin
+ try
+ FHandler.Run;
+ FHandler:=nil;
+ except
+ On E : Exception do
+ begin
+ FExceptionClass:=E.ClassName;
+ FExceptionMessage:=E.Message;
+ end;
+ end;
+end;
+
+{ TIDEServer }
+
+function TIDEServer.GetAction(Index : Integer): TIDEAction;
+
+Var
+ L : TList;
+
+begin
+ L:=FActions.LockList;
+ try
+ Result:=TIDEAction(L.Items[Index]);
+ finally
+ FActions.UnlockList;
+ end;
+end;
+
+procedure TIDEServer.DeActivatedThread(Sender: TObject);
+begin
+ FThread:=Nil;
+end;
+
+function TIDEServer.GetActionCount: Integer;
+
+Var
+ L : TList;
+
+begin
+ L:=FActions.LockList;
+ try
+ Result:=L.Count;
+ finally
+ FActions.UnlockList;
+ end;
+end;
+
+function TIDEServer.GetActive: Boolean;
+begin
+ Result:=Assigned(FThread);
+end;
+
+function TIDEServer.GetPort: Integer;
+begin
+ Result:=FWebHandler.Port;
+end;
+
+procedure TIDEServer.SetActive(AValue: Boolean);
+begin
+ if Active=AValue then Exit;
+ if AValue then
+ begin
+ FThread:=TIDEThread.Create(FWebHandler);
+ FThread.OnTerminate:=@DeActivatedThread;
+ end
+ else
+ begin
+ FWebHandler.Terminate; // will cause thread to stop.
+ try
+ // Send a Quit request just in case. Normally this should fail.
+ FQuitting:=True;
+ TFPHTTPClient.SimpleGet(Format('http://localhost:%d/Quit',[Port]));
+ except
+ FQuitting:=False;
+ end;
+ end;
+end;
+
+procedure TIDEServer.SetPort(AValue: Integer);
+begin
+ FWebHandler.Port:=aValue;
+end;
+
+procedure TIDEServer.SetProjectDir(AValue: String);
+begin
+ if FProjectDir=AValue then Exit;
+ FProjectDir:=IncludeTrailingPathDelimiter(AValue);
+end;
+
+procedure TIDEServer.DoOnAction;
+begin
+ If Assigned(FOnAction) then
+ FonAction(Self,FLastAction);
+ FLastAction:=Nil;
+end;
+
+procedure TIDEServer.DoOnConfirmCommand;
+begin
+ If Assigned(FOnAction) then
+ FonAction(Self,FLastCommand);
+ FLastCommand:=Nil;
+end;
+
+procedure TIDEServer.DoOnClientAdded;
+begin
+ if Assigned(FOnClient) then
+ FOnClient(Self,FLastClient);
+ FLastClient:=Nil;
+end;
+
+procedure TIDEServer.DoOnClientRemoved;
+begin
+ if Assigned(FOnClientRemoved) then
+ FOnClientRemoved(Self,FLastClient);
+ FLastClient:=Nil;
+end;
+
+procedure TIDEServer.DoGetCommand(ARequest: TRequest; AResponse: TResponse);
+
+Var
+ L : TList;
+ I : integer;
+ J,C : TJSONObject;
+ A :TJSONArray;
+ Cmd : TIDECommand;
+ L2 : TFPList;
+ aClient : Int64;
+
+begin
+ aClient:=CheckClient(aRequest);
+ J:=nil;
+ A:=nil;
+ L:=FCommands.LockList;
+ try
+ L2:=TFPList.Create;
+ J:=TJSONObject.Create;
+ A:=TJSONArray.Create;
+ J.Add('commands',A);
+ For I:=0 to L.Count-1 do
+ begin
+ CMD:=TIDECommand(L[i]);
+ if Not Cmd.Sent and (Cmd.ClientID=aClient) then
+ begin
+ C:=TJSONObject.Create;
+ Cmd.ToJSON(C);
+ A.Add(C);
+ L2.Add(C);
+ end;
+ end;
+ SendJSONResponse(J,aResponse);
+ // Remove sent from list
+ for I:=0 to L2.Count-1 do
+ begin
+ Cmd:=TIDECommand(L[i]);
+ if Cmd.NeedsConfirmation then
+ Cmd.Sent:=True
+ else
+ begin
+ Cmd.Free;
+ L.Remove(Cmd);
+ end;
+ end;
+ finally
+ J.Free;
+ FCommands.UnLockList;
+ l2.Free;
+ end;
+end;
+
+procedure TIDEServer.DoPutCommand(ARequest: TRequest; AResponse: TResponse);
+
+Var
+ cmd,oCmd : TIDECommand;
+ aID,aClient : Int64;
+
+begin
+ aClient:=CheckClient(aRequest);
+ aID:=StrToIntDef(aRequest.RouteParams['ID'],-1);
+ cmd:=TIDECommand.Create;
+ try
+ GetClientObjectFromRequest(aRequest,Cmd);
+ cmd.ClientID:=aClient;
+ oCmd:=TIDECommand(FCommands.FindID(aID));
+ if Do404((oCmd=Nil) or (oCmd.ClientID<>aClient),aResponse) then
+ exit;
+ // Later on we can add more modifications
+ oCmd.Confirmed:=True;
+ aResponse.Code:=204;
+ aResponse.CodeText:='OK';
+ aResponse.SendResponse;
+ FLastCommand:=oCmd;
+ DoEvent(@DoOnConfirmCommand);
+ FCommands.Remove(oCmd);
+ Finally
+ cmd.Free;
+ end;
+end;
+
+procedure TIDEServer.DoQuit(ARequest: TRequest; AResponse: TResponse);
+begin
+ if FQuitting then
+ aResponse.Code:=200
+ else
+ aResponse.Code:=401;
+ aResponse.SendResponse;
+end;
+
+procedure TIDEServer.DoRouteRequest(Sender: TObject; ARequest: TRequest; AResponse: TResponse);
+begin
+ If Assigned(FonRequest) then
+ FOnRequest(Self,aRequest.URI);
+end;
+
+function TIDEServer.GetJSONFromRequest(ARequest: TRequest): TJSONObject;
+
+var
+ D : TJSONData;
+
+begin
+ if ARequest.ContentType<>'application/json' then
+ Raise Exception.Create('Not valid JSON payload: content type must be application/json');
+ D:=GetJSON(ARequest.Content);
+ if Not (D is TJSONObject) then
+ begin
+ FreeAndNil(D);
+ Raise EJSON.Create('Payload is valid JSON but not a JSON object');
+ end;
+ Result:=D as TJSONObject;
+end;
+
+procedure TIDEServer.SendJSONResponse(aJSON: TJSONObject; aResponse: TResponse);
+
+Var
+ JS : TJSONStringType;
+
+begin
+ JS:=aJSON.AsJSON;
+ aResponse.FreeContentStream:=True;
+ aResponse.ContentStream:=TMemoryStream.Create;
+ aResponse.ContentStream.WriteBuffer(JS[1],Length(JS));
+ aResponse.ContentLength:=Length(JS);
+ aResponse.ContentType:='application/json';
+ aResponse.SendResponse;
+end;
+
+procedure TIDEServer.GetClientObjectFromRequest(ARequest: TRequest; AObject: TClientObject);
+
+Var
+ J : TJSONObject;
+
+begin
+ J:=GetJSONFromRequest(aRequest);
+ try
+ AObject.FromJSON(J);
+ finally
+ J.Free;
+ end;
+end;
+
+procedure TIDEServer.SendClientObjectResponse(AObject: TClientObject; AResponse: TResponse);
+
+Var
+ J : TJSONObject;
+
+begin
+ J:=TJSONObject.Create;
+ try
+ aObject.ToJSON(J);
+ SendJSONResponse(J,aResponse);
+ finally
+ J.Free;
+ end;
+end;
+
+function TIDEServer.GetActionFromRequest(ARequest: TRequest): TIDEAction;
+
+begin
+ Result:=TIDEAction.Create;
+ try
+ GetClientObjectFromRequest(aRequest,Result);
+ except
+ Result.Free;
+ raise;
+ end;
+end;
+
+function TIDEServer.GetCommandFromRequest(ARequest: TRequest): TIDECommand;
+
+begin
+ Result:=TIDECommand.Create;
+ try
+ GetClientObjectFromRequest(aRequest,Result);
+ except
+ Result.Free;
+ Raise;
+ end;
+end;
+
+function TIDEServer.GetClientFromRequest(ARequest: TRequest): TIDEClient;
+begin
+ Result:=TIDEClient.Create;
+ try
+ GetClientObjectFromRequest(aRequest,Result);
+ except
+ Result.Free;
+ Raise;
+ end;
+end;
+
+procedure TIDEServer.DoPostAction(ARequest: TRequest; AResponse: TResponse);
+
+var
+ A : TIDEAction;
+ aId,aClient : Int64;
+
+begin
+ aClient:=CheckClient(aRequest);
+ aID:=StrToInt64Def(aRequest.RouteParams['ID'],-1);
+ Try
+ A:=GetACtionFromRequest(aRequest);
+ A.ClientID:=aClient;
+ if A.ID=0 then
+ a.ID:=aID;
+ FActions.Add(A);
+ FLastAction:=A;
+ DoEvent(@DoOnAction);
+ AResponse.Code:=201;
+ AResponse.Codetext:='Created';
+ except
+ On E: Exception do
+ begin
+ AResponse.Code:=400;
+ AResponse.Codetext:='Invalid Param';
+ AResponse.Content:='Invalid data ('+E.ClassName+'): '+E.Message;
+ end;
+ end;
+ aResponse.SendResponse;
+end;
+
+function TIDEServer.CheckClient(aRequest: TRequest): INt64;
+
+Var
+ S : String;
+
+begin
+ S:=ARequest.RouteParams['Client'];
+ if (S='') then
+ Raise EJSON.Create('Missing client ID in request');
+ if Not TryStrToInt64(S,Result) then
+ Raise EJSON.CreateFmt('Invalid client ID: %s',[S]);
+end;
+
+procedure TIDEServer.DoDeleteAction(ARequest: TRequest; AResponse: TResponse);
+
+var
+ SID : String;
+ ID,aClient : Int64;
+
+begin
+ Try
+ aClient:=CheckClient(ARequest);
+ SID:=ARequest.RouteParams['ID'];
+ ID:=StrtoInt64Def(SID,-1);
+ if Do404((ID=-1) or not (DeleteAction(ID,aClient)),aResponse) then
+ exit;
+ AResponse.Code:=204;
+ AResponse.Codetext:='No content';
+ aResponse.SendResponse;
+ except
+ On E: Exception do
+ begin
+ AResponse.Code:=400;
+ AResponse.Codetext:='Invalid Param';
+ AResponse.Content:='Invalid data ('+E.ClassName+'): '+E.Message;
+ end;
+ end;
+end;
+
+
+procedure TIDEServer.DoGetFile(ARequest: TRequest; AResponse: TResponse);
+
+Var
+ FN : String;
+
+begin
+ FN:=ARequest.URL;
+ if AnsiStartsText(SFilesURL,FN) then
+ Delete(FN,1,Length(SFilesURL));
+ FN:=ExpandFileName(FProjectDir+FN);
+ if Pos('..',ExtractRelativepath(FProjectDir,FN))<>0 then
+ begin
+ aResponse.Code:=401;
+ aResponse.CodeText:='Forbidden';
+ aResponse.Content:='Forbidden
';
+ end
+ else if Do404(Not FileExists(FN),aResponse) then
+ exit;
+ aResponse.FreeContentStream:=True;
+ aResponse.ContentStream:=TFileStream.Create(FN,fmOpenRead or fmShareDenyWrite);
+ aResponse.ContentLength:=aResponse.ContentStream.Size;
+ aResponse.ContentType:=MimeTypes.GetMimeType(ExtractFileExt(FN));
+ if aResponse.ContentType='' then
+ aResponse.ContentType:='text/html';
+ aResponse.SendResponse;
+end;
+
+
+constructor TIDEServer.Create(aOwner: TComponent);
+begin
+ Inherited;
+ FProjectDir:=ExtractFilePath(Paramstr(0));
+ FActions:=TClientObjectList.Create;
+ FCommands:=TClientObjectList.Create;
+ FClients:=TClientObjectList.Create;
+ FWebHandler:=TFPHTTPServerHandler.Create(Self);
+ FWebHandler.Port:=8080;
+ RegisterRoutes;
+end;
+
+procedure TIDEServer.DoEvent(aProc : TThreadMethod);
+
+begin
+ if Assigned(FThread) then
+ FThread.Synchronize(aProc)
+ else
+ aProc;
+end;
+
+procedure TIDEServer.DoPostClient(ARequest: TRequest; AResponse: TResponse);
+
+Var
+ aClient : TIDEClient;
+
+begin
+ aClient:=GetClientFromRequest(aRequest);
+ aClient.FID:=GetNextCounter;
+ FClients.Add(aClient);
+ SendClientObjectResponse(aClient,aResponse);
+ FLastClient:=aClient;
+ DoEvent(@DoOnClientAdded);
+end;
+
+function TIDEServer.Do404(is404: boolean; aResponse: TResponse): Boolean;
+
+begin
+ Result:=is404;
+ if Result then
+ begin
+ aResponse.Code:=404;
+ aResponse.Codetext:='Not found';
+ aResponse.SendResponse;
+ end;
+end;
+
+procedure TIDEServer.DoDeleteClient(ARequest: TRequest; AResponse: TResponse);
+
+Var
+ aClientID : Int64;
+ aClient : TIDEClient;
+
+begin
+ aClientID:=CheckClient(aRequest);
+ aClient:=TIDEClient(FClients.FindID(aClientID));
+ if Do404(not Assigned(aClient),aResponse) then
+ exit;
+ FLastClient:=aClient;
+ DoEvent(@DoOnClientRemoved);
+ FClients.Remove(aClient);
+end;
+
+
+procedure TIDEServer.RegisterRoutes;
+
+begin
+ // get command
+ HTTPRouter.RegisterRoute(SIDEURL+'Quit',rmGet,@DoQuit);
+ HTTPRouter.RegisterRoute(SIDEURL+'Client/',rmPost,@DoPostClient);
+ HTTPRouter.RegisterRoute(SIDEURL+'Client/:Client',rmDelete,@DoDeleteClient);
+ HTTPRouter.RegisterRoute(SIDEURL+'Command/:Client/',rmGet,@DoGetCommand);
+ // PUT command for confirm.
+ HTTPRouter.RegisterRoute(SIDEURL+'Command/:Client/:ID',rmPut,@DoPutCommand);
+ // POST action
+ HTTPRouter.RegisterRoute(SIDEURL+'Action/:Client/:ID',rmPost,@DoPostAction);
+ HTTPRouter.RegisterRoute(SIDEURL+'Action/:Client/:ID',rmDelete,@DoDeleteAction);
+ // GET file
+ HTTPRouter.RegisterRoute(SFilesURL+'*',rmGet,@DoGetFile,true);
+ HTTPRouter.BeforeRequest:=@DoRouteRequest;
+end;
+
+destructor TIDEServer.Destroy;
+begin
+ Active:=False;
+ While Active do
+ Sleep(20);
+ FreeAndNil(FActions);
+ FreeAndNil(FCommands);
+ FreeAndNil(FClients);
+ inherited Destroy;
+end;
+
+function TIDEServer.GetNextCounter: Int64;
+begin
+ Inc(FIDCounter);
+ Result:=FIDCounter;
+end;
+
+function TIDEServer.SendCommand(aCommand: TIDECommand): Int64;
+begin
+ Result:=GetNextCounter;
+ aCommand.ID:=Result;
+ FCommands.Add(aCommand);
+end;
+
+function TIDEServer.DeleteAction(aID: Int64; const aClientID: Int64): Boolean;
+
+Var
+ P : TIDEAction;
+ L : TList;
+ I : Integer;
+
+begin
+ P:=nil;
+ L:=FActions.LockList;
+ try
+ I:=L.Count-1;
+ While (I>=0) and (P=Nil) do
+ begin
+ P:=TIDEAction(L[i]);
+ if P.ID<>AID then P:=Nil;
+ Dec(i)
+ end;
+ finally
+ L.Free;
+ end;
+ Result:=(P<>Nil) and ((aClientID=-1) or (P.ClientID=aClientID));
+ if Result then
+ FActions.Remove(P);
+end;
+
+procedure TIDEServer.GetClientActions(aClientID: Int64; aList: TFPList);
+
+Var
+ P : TIDEAction;
+ L : TList;
+ I : Integer;
+begin
+ P:=nil;
+ L:=FActions.LockList;
+ try
+ I:=L.Count-1;
+ While (I>=0) and (P=Nil) do
+ begin
+ P:=TIDEAction(L[i]);
+ if P.ClientID=aClientID then
+ begin
+ aList.Add(P);
+ L.Delete(I);
+ end;
+ Dec(i);
+ end;
+ finally
+ L.Free;
+ end;
+end;
+
+end.
+
diff --git a/packages/webwidget/htmlwidgets.pp b/packages/webwidget/htmlwidgets.pp
new file mode 100644
index 0000000..7d18a41
--- /dev/null
+++ b/packages/webwidget/htmlwidgets.pp
@@ -0,0 +1,1587 @@
+unit htmlwidgets;
+
+{$mode objfpc}
+
+interface
+
+uses
+ Classes, SysUtils, webwidget, js, web;
+
+Type
+
+ { TButtonWidget }
+
+ TButtonWidget = Class(TWebWidget)
+ private
+ FText: String;
+ procedure SetText(AValue: String);
+ Protected
+ Procedure SetName(const NewName: TComponentName); override;
+ Procedure ApplyWidgetSettings(aElement: TJSHTMLElement); override;
+ Public
+ Procedure Click;
+ Function HTMLTag : String; override;
+ Property Text : String Read FText Write SetText;
+ end;
+
+ { TViewPort }
+
+ TViewPort = Class(TCustomWebWidget)
+ Private
+ Class var FInstance : TViewPort;
+ Protected
+ Class Function FixedParent : TJSHTMLElement; override;
+ Class Function FixedElement : TJSHTMLElement; override;
+ Function DoRenderHTML(aParent,aElement : TJSHTMLElement) :TJSHTMLElement; override;
+ Public
+ Constructor Create (aOwner: TComponent); override;
+ Function HTMLTag : String; override;
+ Class Function Instance : TViewPort;
+ Property Element;
+ end;
+
+ { TWebPage }
+
+ TWebPage = Class(TCustomWebWidget)
+ private
+ Protected
+ Class Function DefaultParentElement: TJSHTMLElement; override;
+ Class Function DefaultParent : TCustomWebWidget; override;
+ Procedure DoUnRender(aParent : TJSHTMLElement) ; override;
+ Public
+ Constructor Create(AOwner : TComponent); override;
+ Function HTMLTag : String; override;
+ // Later on, allow IFrame;
+ Published
+ Property ParentID;
+ Property ElementID;
+ Property Classes;
+ Property Styles;
+ Property StyleRefresh;
+ Property Visible;
+ // Events
+ Property BeforeRenderHTML;
+ Property AfterRenderHTML;
+ Property OnAbort;
+ Property OnAnimationCancel;
+ Property OnAnimationEnd;
+ Property OnAnimationIteration;
+ Property OnAnimationStart;
+ Property OnAuxClick;
+ Property OnBlur;
+ Property OnCancel;
+ Property OnCanPlay;
+ Property OnCanPlayThrough;
+ Property OnChange;
+ Property OnClick;
+ Property OnCompositionEnd;
+ Property OnCompositionStart;
+ Property OnCompositionUpdate;
+ Property OnContextMenu;
+ Property OnCopy;
+ Property OnCut;
+ Property OnCueChange;
+ Property OnDblClick;
+ Property OnDurationChange;
+ Property OnEnded ;
+ Property OnError ;
+ Property OnFocus;
+ Property OnFocusIn ;
+ Property OnFocusOut ;
+ Property OnGotPointerCapture;
+ Property OnInput;
+ Property OnInvalid;
+ Property OnKeyDown;
+ Property OnKeyPress;
+ Property OnKeyUp;
+ Property OnLoad;
+ Property OnLoadedData;
+ Property OnLoadedMetaData;
+ Property OnLoadend;
+ Property OnLoadStart;
+ Property OnLostPointerCapture;
+ Property OnMouseDown;
+ Property OnMouseEnter;
+ Property OnMouseLeave;
+ Property OnMouseMove;
+ Property OnMouseOut;
+ Property OnMouseUp;
+ Property OnOverFlow;
+ Property OnPaste;
+ Property OnPause;
+ Property OnPlay;
+ Property OnPointerCancel;
+ Property OnPointerDown;
+ Property OnPointerEnter;
+ Property OnPointerLeave;
+ Property OnPointerMove;
+ Property OnPointerOut;
+ Property OnPointerOver;
+ Property OnPointerUp;
+ Property OnReset;
+ Property OnResize;
+ Property OnScroll;
+ Property OnSelect;
+ Property OnSubmit;
+ Property OnTouchStart;
+ Property OnTransitionCancel;
+ Property OnTransitionEnd;
+ Property OnTransitionRun;
+ Property OnTransitionStart;
+ Property OnWheel;
+ end;
+
+ { TCustomInputWidget }
+
+ TCustomInputWidget = Class(TWebWidget)
+ private
+ FValue : String;
+ FValueName : String;
+ FText : String;
+ FReadOnly : Boolean;
+ FRequired : Boolean;
+ function GetReadOnly: Boolean;
+ function GetRequired: Boolean;
+ function GetText: String;
+ function GetValue: String;
+ function GetValueName: String;
+ procedure SetReadonly(AValue: Boolean);
+ procedure SetRequired(AValue: Boolean);
+ procedure SetText(AValue: String);
+ procedure SetValue(AValue: String);
+ function GetInputElement: TJSHTMLInputElement;
+ procedure SetValueName(AValue: String);
+ Protected
+ Procedure SetName(const NewName: TComponentName); override;
+ Procedure ApplyWidgetSettings(aElement: TJSHTMLElement); override;
+ Property InputElement : TJSHTMLInputElement Read GetInputElement;
+ // Text to show (checkbox etc). Enable in descendents as needed
+ Property Text : String Read GetText Write SetText;
+ Public
+ function InputType : String; virtual; abstract;
+ Function HTMLTag : String; override;
+ // Value as string
+ Property Value : String Read GetValue Write SetValue;
+ // Value Name to use when submitting using form.
+ Property ValueName : String Read GetValueName Write SetValueName;
+ Property ReadOnly : Boolean Read GetReadOnly Write SetReadonly;
+ Property Required : Boolean Read GetRequired Write SetRequired;
+ end;
+
+ { TTextInputWidget }
+
+ TInputTextType = (ittText,ittPassword,ittNumber,ittEmail,ittSearch,ittTelephone,ittURL,ittColor);
+ TTextInputWidget = class(TCustomInputWidget)
+ private
+ FMaxLength : Integer;
+ FMinLength : Integer;
+ FTextType : TInputTextType;
+ function GetAsNumber: NativeInt;
+ function GetMaxLength: NativeInt;
+ function GetMinLength: NativeInt;
+ function GetTextType: TInputTextType;
+ procedure SetAsNumber(AValue: NativeInt);
+ procedure SetMaxLength(AValue: NativeInt);
+ procedure SetMinLength(AValue: NativeInt);
+ procedure SetTextType(AValue: TInputTextType);
+ Protected
+ Procedure ApplyWidgetSettings(aElement: TJSHTMLElement); override;
+ Public
+ Class Function AllowChildren : Boolean; override;
+ function InputType : String; override;
+ Published
+ Property Value;
+ Property ValueName;
+ Property TextType : TInputTextType Read GetTextType Write SetTextType;
+ property AsNumber : NativeInt Read GetAsNumber Write SetAsNumber;
+ Property MaxLength : NativeInt Read GetMaxLength Write SetMaxLength;
+ Property MinLength : NativeInt Read GetMinLength Write SetMinLength;
+ // Todo: List support
+ end;
+
+
+ { TButtonInputWidget }
+ TInputButtonType = (ibtSubmit,ibtReset,ibtImage);
+ TInputButtonTypes = set of TInputButtonType;
+
+ TButtonInputWidget = class(TCustomInputWidget)
+ private
+ FButtonType: TInputButtonType;
+ FSrc: String;
+ procedure SetButtonType(AValue: TInputButtonType);
+ procedure SetSrc(AValue: String);
+ Public
+ Procedure ApplyWidgetSettings(aElement: TJSHTMLElement); override;
+ function InputType : String; override;
+ Class Function AllowChildren : Boolean; override;
+ Published
+ Property ButtonType : TInputButtonType Read FButtonType Write SetButtonType;
+ Property Value;
+ Property ValueName;
+ Property Src : String Read FSrc Write SetSrc;
+ end;
+
+ { TCheckableInputWidget }
+
+ TCheckableInputWidget = class(TCustomInputWidget)
+ private
+ FChecked: Boolean;
+ function GetChecked: Boolean;
+ procedure SetChecked(AValue: Boolean);
+ Protected
+ Procedure ApplyWidgetSettings(aElement: TJSHTMLElement); override;
+ Public
+ Property Value;
+ Property ValueName;
+ Property Checked : Boolean Read GetChecked Write SetChecked;
+ Property Text;
+ end;
+
+ { TRadioInputWidget }
+
+ TRadioInputWidget = class(TCheckableInputWidget)
+ private
+ Public
+ function InputType : String; override;
+ Published
+ Property Value;
+ Property ValueName;
+ Property Checked;
+ Property Text;
+ end;
+
+ { TCheckboxInputWidget }
+
+ TCheckboxInputWidget = class(TCheckableInputWidget)
+ private
+ Public
+ function InputType : String; override;
+ Published
+ Property Value;
+ Property ValueName;
+ Property Checked;
+ Property Text;
+ end;
+
+
+ { TDateInputWidget }
+
+ TDateInputWidget = class(TCustomInputWidget)
+ private
+ FDate: TDateTime;
+ function GetDate: TDateTime;
+ procedure SetDate(AValue: TDateTime);
+ Public
+ function InputType : String; override;
+ Class Function AllowChildren : Boolean; override;
+ Published
+ Property ValueName;
+ Property Date : TDateTime Read GetDate Write SetDate;
+ end;
+
+ { TFileInputWidget }
+ TFileInfo = record
+ Name : String;
+ TimeStamp : TDateTime;
+ FileType : String;
+ Size : NativeInt;
+ end;
+
+ TFileInputWidget = class(TCustomInputWidget)
+ private
+ FMultiple: Boolean;
+ function GetFileCount: Integer;
+ function GetFileDate(aIndex : Integer): TDateTime;
+ function GetFileInfo(aIndex : Integer): TFileInfo;
+ function GetFileName(aIndex : Integer): String;
+ function GetFileSize(aIndex : Integer): NativeInt;
+ function GetFileType(aIndex : Integer): String;
+ function GetMultiple: Boolean;
+ procedure SetMultiple(AValue: Boolean);
+ Protected
+ Procedure ApplyWidgetSettings(aElement: TJSHTMLElement); override;
+ Public
+ Class Function AllowChildren : Boolean; override;
+ function InputType : String; override;
+ Property FileCount : Integer read GetFileCount;
+ Property Files[aIndex : Integer] : String Read GetFileName;
+ Property FileSizes[aIndex : Integer] : NativeInt Read GetFileSize;
+ Property FileTypes[aIndex : Integer] : String Read GetFileType;
+ Property FileDates[aIndex : Integer] : TDateTime Read GetFileDate;
+ Property FileInfos[aIndex : Integer] : TFileInfo Read GetFileInfo;
+ Published
+ Property ValueName;
+ Property Multiple : Boolean Read GetMultiple Write SetMultiple;
+ end;
+
+ { THiddenInputWidget }
+
+ THiddenInputWidget = class(TCustomInputWidget)
+ Public
+ Class Function AllowChildren : Boolean; override;
+ function InputType : String; override;
+ Published
+ Property ValueName;
+ Property Value;
+ end;
+
+ { TTextAreaWidget }
+
+ TTextAreaWrap = (tawSoft,tawHard,tawOff);
+ TTextAreaWidget = Class(TWebWidget)
+ private
+ FLines: TStrings;
+ FIgnoreChanges : Boolean;
+ FMaxLength: Cardinal;
+ FValueName : String;
+ FRows,
+ FColumns : Cardinal;
+ FWrap: TTextAreaWrap;
+ FRequired,
+ FReadOnly : Boolean;
+ procedure ApplyWrap(aElement: TJSHTMLTextAreaElement);
+ procedure DoLineChanges(Sender: TObject);
+ function GetColumns: Cardinal;
+ function GetLines: TStrings;
+ function GetReadOnly: Boolean;
+ function GetRequired: Boolean;
+ function GetRows: Cardinal;
+ function GetText: String;
+ function GetValueName: string;
+ procedure SetColumns(AValue: Cardinal);
+ procedure SetLines(AValue: TStrings);
+ procedure SetMaxLength(AValue: Cardinal);
+ procedure SetReadonly(AValue: Boolean);
+ procedure SetRequired(AValue: Boolean);
+ procedure SetRows(AValue: Cardinal);
+ procedure SetText(AValue: String);
+ procedure SetValueName(AValue: string);
+ Function GetTextArea : TJSHTMLTextAreaElement;
+ procedure SetWrap(AValue: TTextAreaWrap);
+ Protected
+ procedure ApplyLines(aElement: TJSHTMLTextAreaElement);
+ procedure LinesFromHTML(aHTML : String);
+ Procedure SetName(const NewName: TComponentName); override;
+ Procedure ApplyWidgetSettings(aElement: TJSHTMLElement); override;
+ Property TextArea :TJSHTMLTextAreaElement Read GetTextArea;
+ Public
+ Constructor Create(aOwner : TComponent); override;
+ Destructor Destroy; override;
+ Class Function AllowChildren : Boolean; override;
+ Function HTMLTag : String; override;
+ Property InnerHTML : String Read GetText Write SetText;
+ Published
+ Property ValueName : string Read GetValueName Write SetValueName;
+ Property Rows : Cardinal Read GetRows Write SetRows;
+ Property Columns : Cardinal Read GetColumns Write SetColumns;
+ Property Lines : TStrings Read GetLines Write SetLines;
+ Property MaxLength : Cardinal Read FMaxLength Write SetMaxLength;
+ Property Wrap : TTextAreaWrap Read FWrap Write SetWrap;
+ Property ReadOnly : Boolean Read GetReadOnly Write SetReadonly;
+ Property Required : Boolean Read GetRequired Write SetRequired;
+ end;
+
+ { TImageWidget }
+
+ TImageWidget = class(TWebWidget)
+ private
+ FHeight: Integer;
+ FWidth: Integer;
+ FSrc : String;
+ function GetHeight: Integer;
+ function GetImg: TJSHTMLImageElement;
+ function GetSrc: String;
+ function GetWidth: Integer;
+ procedure SetHeight(AValue: Integer);
+ procedure SetSrc(AValue: String);
+ procedure SetWidth(AValue: Integer);
+ Protected
+ Procedure ApplyWidgetSettings(aElement: TJSHTMLElement); override;
+ Property ImgElement : TJSHTMLImageElement Read GetImg;
+ Public
+ Function HTMLTag : String; override;
+ Published
+ Property Src : String Read GetSrc Write SetSrc;
+ Property Width : Integer Read GetWidth Write SetWidth;
+ Property Height : Integer Read GetHeight Write SetHeight;
+ end;
+
+ { TSelectWidget }
+ TJSHTMLOptionElementArray = Array of TJSHTMLOptionElement;
+ TSelectWidget = class(TWebWidget)
+ private
+ FItems : TStrings;
+ FValues : TStrings;
+ FOptions : TJSHTMLOptionElementArray;
+ FSelectedIndex : Integer;
+ FMultiple : Boolean;
+ function GetMultiple: Boolean;
+ function GetSelected(Index : Integer): Boolean;
+ function GetSelectedIndex: Integer;
+ function GetItems: TStrings;
+ function GetSelect: TJSHTMLSelectElement;
+ function GetSelectionCount: Integer;
+ function GetSelectionItem(aIndex : Integer): String;
+ function GetSelectionValue(aIndex : Integer): String;
+ function GetValues: TStrings;
+ procedure OptionsChanged(Sender: TObject);
+ procedure SetMultiple(AValue: Boolean);
+ procedure SetSelected(Index : Integer; AValue: Boolean);
+ procedure SetSelectedIndex(AValue: Integer);
+ procedure setItems(AValue: TStrings);
+ procedure setValues(AValue: TStrings);
+ Protected
+ Procedure BuildOptions(aSelect : TJSHTMLSelectElement); virtual;
+ Procedure ApplyWidgetSettings(aElement: TJSHTMLElement); override;
+ Property SelectElement : TJSHTMLSelectElement Read GetSelect;
+ Property Options : TJSHTMLOptionElementArray Read Foptions;
+ Public
+ Constructor Create(aOWner : TComponent); override;
+ Destructor Destroy; override;
+ Function HTMLTag : String; override;
+ // Items that are selected
+ Property Selected[Index : Integer] : Boolean Read GetSelected Write SetSelected;
+ Property SelectionCount : Integer Read GetSelectionCount;
+ Property SelectionValue[aIndex : Integer] : String Read GetSelectionValue;
+ Property SelectionItem[aIndex : Integer] : String Read GetSelectionItem;
+ Published
+ Property Items : TStrings Read GetItems Write setItems;
+ Property Values : TStrings Read GetValues Write setValues;
+ property SelectedIndex : Integer Read GetSelectedIndex Write SetSelectedindex;
+ Property Multiple : Boolean Read GetMultiple Write SetMultiple;
+ end;
+
+ { TLabelWidget }
+
+ TLabelWidget = Class(TWebWidget)
+ private
+ FLabelFor: TWebWidget;
+ FText: String;
+ function GetLabelEl: TJSHTMLLabelElement;
+ function GetText: String;
+ procedure SetLabelFor(AValue: TWebWidget);
+ procedure SetText(AValue: String);
+ Protected
+ procedure ApplyLabelFor(aLabelElement: TJSHTMLLabelElement);
+ Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+ Procedure SetName(const NewName: TComponentName); override;
+ Procedure ApplyWidgetSettings(aElement: TJSHTMLElement); override;
+ Property LabelElement : TJSHTMLLabelElement Read GetLabelEl;
+ Public
+ Function HTMLTag : String; override;
+ Property Text : String Read GetText Write SetText;
+ Property LabelFor : TWebWidget Read FLabelFor Write SetLabelFor;
+ end;
+
+
+Function ViewPort : TViewPort;
+
+implementation
+
+uses DateUtils;
+
+resourcestring
+ SErrInvalidIndex = 'Index %s not in valid range of [0..%d]';
+
+Function ViewPort : TViewPort;
+
+begin
+ Result:=TViewPort.Instance;
+end;
+
+{ TLabelWidget }
+
+procedure TLabelWidget.ApplyLabelFor(aLabelElement : TJSHTMLLabelElement);
+
+begin
+ if Assigned(FlabelFor) then
+ begin
+ FlabelFor.EnsureElement;
+ aLabelElement.for_:=FlabelFor.ElementID;
+ end
+ else
+ aLabelElement.for_:='';
+end;
+
+procedure TLabelWidget.SetLabelFor(AValue: TWebWidget);
+begin
+ if (FLabelFor=AValue) then Exit;
+ if Assigned(FLabelFor) then
+ FLabelFor.RemoveFreeNotification(Self);
+ FLabelFor:=AValue;
+ if Assigned(FLabelFor) then
+ FLabelFor.FreeNotification(Self);
+ If IsRendered then
+ ApplyLabelFor(LabelElement);
+end;
+
+function TLabelWidget.GetText: String;
+begin
+ if IsElementDirty then
+ FText:=Element.InnerText;
+ Result:=FText;
+end;
+
+function TLabelWidget.GetLabelEl: TJSHTMLLabelElement;
+begin
+ Result:=TJSHTMLLabelElement(Element);
+end;
+
+procedure TLabelWidget.SetText(AValue: String);
+begin
+ If Text=aValue then exit;
+ Ftext:=aValue;
+ If IsRendered then
+ Element.innerText:=aValue;
+end;
+
+procedure TLabelWidget.Notification(AComponent: TComponent; Operation: TOperation);
+begin
+ inherited Notification(AComponent, Operation);
+ if (Operation=opRemove) and (aComponent=FLabelFor) then
+ FLabelFor:=Nil;
+end;
+
+procedure TLabelWidget.SetName(const NewName: TComponentName);
+
+Var
+ Old : String;
+
+begin
+ Old:=Name;
+ inherited SetName(NewName);
+ if (csDesigning in ComponentState) then
+ if Old=Text then
+ Text:=Old;
+end;
+
+procedure TLabelWidget.ApplyWidgetSettings(aElement: TJSHTMLElement);
+
+var
+ lbl : TJSHTMLLabelElement absolute aElement;
+
+begin
+ inherited ApplyWidgetSettings(aElement);
+ lbl.InnerText:=Text;
+ ApplyLabelFor(Lbl);
+end;
+
+
+function TLabelWidget.HTMLTag: String;
+begin
+ Result:='label';
+end;
+
+{ TSelectWidget }
+
+function TSelectWidget.GetSelectedIndex: Integer;
+begin
+ if IsRendered then
+ FSelectedIndex:=SelectElement.selectedIndex;
+ Result:=FSelectedIndex
+end;
+
+function TSelectWidget.GetMultiple: Boolean;
+
+begin
+ if IsElementDirty then
+ FMultiple:=SelectElement.multiple;
+ Result:=FMultiple;
+end;
+
+function TSelectWidget.GetSelected(Index : Integer): Boolean;
+begin
+ if (Index<0) or (Index>=Length(Foptions)) then
+ Raise EWidgets.CreateFmt(SErrInvalidIndex,[Index,Length(Foptions)-1]);
+ Result:=FOptions[Index].Selected
+end;
+
+function TSelectWidget.GetItems: TStrings;
+begin
+ Result:=FItems;
+end;
+
+function TSelectWidget.GetSelect: TJSHTMLSelectElement;
+begin
+ Result:=TJSHTMLSelectElement(Element);
+end;
+
+function TSelectWidget.GetSelectionCount: Integer;
+begin
+ Result:=SelectElement.selectedOptions.length;
+end;
+
+function TSelectWidget.GetSelectionItem(aIndex : Integer): String;
+begin
+ if (aIndex<0) or (aindex>=GetSelectionCount) then
+ Raise EWidgets.CreateFmt(SErrInvalidIndex,[aIndex,GetSelectionCount-1]);
+ Result:=TJSHTMLOptionElement(SelectElement.selectedOptions.item(aIndex)).innerText;
+end;
+
+function TSelectWidget.GetSelectionValue(aIndex : Integer): String;
+begin
+ if (aIndex<0) or (aindex>=GetSelectionCount) then
+ Raise EWidgets.CreateFmt(SErrInvalidIndex,[aIndex,GetSelectionCount-1]);
+ Result:=TJSHTMLOptionElement(SelectElement.selectedOptions.item(aIndex)).value;
+end;
+
+function TSelectWidget.GetValues: TStrings;
+begin
+ Result:=FValues;
+end;
+
+procedure TSelectWidget.OptionsChanged(Sender: TObject);
+begin
+ if IsRendered then
+ BuildOptions(SelectElement);
+end;
+
+procedure TSelectWidget.SetMultiple(AValue: Boolean);
+begin
+ If (AValue=Multiple) then exit;
+ FMultiple:=aValue;
+ If IsRendered then
+ SelectElement.multiple:=FMultiple;
+end;
+
+procedure TSelectWidget.SetSelected(Index : Integer; AValue: Boolean);
+begin
+ if (Index<0) or (Index>=Length(Foptions)) then
+ Raise EWidgets.CreateFmt(SErrInvalidIndex,[Index,Length(Foptions)-1]);
+ FOptions[Index].Selected:=True;
+end;
+
+procedure TSelectWidget.SetSelectedIndex(AValue: Integer);
+
+begin
+ if (SelectedIndex=aValue) then
+ Exit;
+ FSelectedIndex:=aValue;
+ if IsRendered then
+ SelectElement.SelectedIndex:=FSelectedIndex;
+end;
+
+procedure TSelectWidget.setItems(AValue: TStrings);
+begin
+ If (AValue=FItems) then exit;
+ FItems.Assign(aValue);
+end;
+
+
+procedure TSelectWidget.setValues(AValue: TStrings);
+begin
+ If (AValue=FValues) then exit;
+ FValues.Assign(aValue);
+end;
+
+procedure TSelectWidget.BuildOptions(aSelect: TJSHTMLSelectElement);
+
+Var
+ O : TJSHTMLOptionElement;
+ Itms,Vals : TStrings;
+ I,Idx : Integer;
+
+begin
+ // Clear
+ SetLength(FOptions,0);
+ aSelect.InnerHTML:='';
+ // Rebuild
+ Itms:=Fitems;
+ Vals:=FValues;
+ Idx:=FSelectedIndex;
+ SetLength(Foptions,Itms.Count);
+ For I:=0 to Itms.Count-1 do
+ begin
+ O:=TJSHTMLOptionElement(CreateElement('option',''));
+ FOptions[I]:=O;
+ O.innerText:=Itms[I];
+ if I0 then
+ area.maxlength:=FMaxLength;
+ if FColumns>0 then
+ area.cols:=FColumns;
+ if FRows>0 then
+ area.Rows:=FRows;
+ if FLines.Count>0 then
+ ApplyLines(area);
+ if FValueName<>'' then
+ area.Name:=FValueName;
+ area.Readonly:=FReadOnly;
+ area.Required:=FRequired;
+ ApplyWrap(area);
+end;
+
+
+constructor TTextAreaWidget.Create(aOwner: TComponent);
+begin
+ inherited Create(aOwner);
+ FLines:=TStringList.Create;
+ TStringList(FLines).OnChange:=@DoLineChanges;
+ FColumns:=50;
+ FRows:=10;
+end;
+
+destructor TTextAreaWidget.Destroy;
+begin
+ FreeAndNil(Flines);
+ inherited;
+end;
+
+class function TTextAreaWidget.AllowChildren: Boolean;
+begin
+ Result:=False;
+end;
+
+function TTextAreaWidget.GetTextArea: TJSHTMLTextAreaElement;
+begin
+ Result:=TJSHTMLTextAreaElement(Element);
+end;
+
+procedure TTextAreaWidget.ApplyWrap(aElement :TJSHTMLTextAreaElement);
+
+Const
+ Wraps : Array[TTextAreaWrap] of string = ('soft','hard','off');
+
+begin
+ aElement.wrap:=Wraps[FWrap];
+end;
+
+procedure TTextAreaWidget.ApplyLines(aElement: TJSHTMLTextAreaElement);
+begin
+ aElement.innerHTML:=FLines.Text;
+end;
+
+procedure TTextAreaWidget.LinesFromHTML(aHTML: String);
+begin
+ FLines.Text:= StringReplace(aHTML,'
',sLineBreak,[rfIgnoreCase,rfReplaceAll]);
+end;
+
+
+
+procedure TTextAreaWidget.SetWrap(AValue: TTextAreaWrap);
+
+begin
+ if FWrap=AValue then Exit;
+ FWrap:=AValue;
+ if IsRendered then
+ ApplyWrap(TextArea)
+end;
+
+function TTextAreaWidget.HTMLTag: String;
+begin
+ result:='textarea';
+end;
+
+{ TCheckboxInputWidget }
+
+function TCheckboxInputWidget.InputType: String;
+begin
+ Result:='checkbox';
+end;
+
+{ TRadioInputWidget }
+
+function TRadioInputWidget.InputType: String;
+begin
+ Result:='radio';
+end;
+
+{ THiddenInputWidget }
+
+class function THiddenInputWidget.AllowChildren: Boolean;
+begin
+ Result:=False;
+end;
+
+function THiddenInputWidget.InputType: String;
+begin
+ Result:='hidden';
+end;
+
+{ TFileInputWidget }
+
+procedure TFileInputWidget.SetMultiple(AValue: Boolean);
+begin
+ if FMultiple=AValue then Exit;
+ FMultiple:=AValue;
+ if Isrendered then
+ InputElement.multiple:=FMultiple;
+end;
+
+function TFileInputWidget.GetMultiple: Boolean;
+begin
+ if IsElementDirty then
+ FMultiple:=InputElement.multiple;
+ Result:=FMultiple;
+end;
+
+
+function TFileInputWidget.GetFileName(aIndex : Integer): String;
+begin
+ Result:=InputElement.files.Files[aIndex].name;
+end;
+
+function TFileInputWidget.GetFileSize(aIndex : Integer): NativeInt;
+begin
+ Result:=InputElement.files.Files[aIndex].Size;
+end;
+
+function TFileInputWidget.GetFileType(aIndex : Integer): String;
+begin
+ Result:=InputElement.files.Files[aIndex]._Type;
+end;
+
+function TFileInputWidget.GetFileCount: Integer;
+begin
+ Result:=InputElement.files.Length;
+end;
+
+function TFileInputWidget.GetFileDate(aIndex : Integer): TDateTime;
+begin
+ Result:=JSDateToDateTime(InputElement.files.Files[aIndex].lastModifiedDate);
+end;
+
+function TFileInputWidget.GetFileInfo(aIndex : Integer): TFileInfo;
+
+Var
+ f : TJSHTMLFile;
+
+begin
+ F:=InputElement.files.Files[aIndex];
+ Result.Name:=F.name;
+ Result.Size:=F.size;
+ Result.FileType:=F._type;
+ Result.TimeStamp:= JSDateToDateTime(F.lastModifiedDate);
+end;
+
+
+procedure TFileInputWidget.ApplyWidgetSettings(aElement: TJSHTMLElement);
+
+Var
+ Old : String;
+
+begin
+ Old:=FValue;
+ FValue:='';
+ try
+ inherited ApplyWidgetSettings(aElement);
+ TJSHTMLInputElement(aElement).multiple:=FMultiple;
+ finally
+ FValue:=Old;
+ end;
+end;
+
+class function TFileInputWidget.AllowChildren: Boolean;
+begin
+ Result:=False;
+end;
+
+function TFileInputWidget.InputType: String;
+begin
+ Result:='file';
+end;
+
+
+{ TDateInputWidget }
+
+function TDateInputWidget.GetDate: TDateTime;
+
+var
+ aDate : TDateTime;
+
+begin
+ if IsElementDirty then
+ begin
+ aDate:=ScanDateTime('yyyy-mm-dd',Value);
+ if aDate<>0 then
+ FDate:=aDate;
+ end;
+ Result:=FDate;
+end;
+
+
+procedure TDateInputWidget.SetDate(AValue: TDateTime);
+begin
+ FDate:=aValue;
+ Value:=FormatDateTime('yyyy-mm-dd',FDate);
+end;
+
+function TDateInputWidget.InputType: String;
+begin
+ Result:='date';
+end;
+
+class function TDateInputWidget.AllowChildren: Boolean;
+begin
+ Result:=False;
+end;
+
+{ TCheckableInputWidget }
+
+
+procedure TCheckableInputWidget.SetChecked(AValue: Boolean);
+begin
+ // Get actual value
+ if Checked=AValue then Exit;
+ if isRendered then
+ InputElement.checked:=aValue;
+ FChecked:=AValue;
+end;
+
+procedure TCheckableInputWidget.ApplyWidgetSettings(aElement: TJSHTMLElement);
+begin
+ inherited ApplyWidgetSettings(aElement);
+ TJSHTMLInputElement(aElement).Checked:=FChecked;
+end;
+
+
+function TCheckableInputWidget.GetChecked: Boolean;
+begin
+ if IsElementDirty then
+ FChecked:=InputElement.Checked;
+ Result:=FChecked;
+end;
+
+
+{ TButtonInputWidget }
+
+procedure TButtonInputWidget.SetButtonType(AValue: TInputButtonType);
+begin
+ if FButtonType=AValue then Exit;
+ FButtonType:=AValue;
+ if IsRendered then
+ Refresh;
+end;
+
+procedure TButtonInputWidget.SetSrc(AValue: String);
+begin
+ if FSrc=AValue then Exit;
+ FSrc:=AValue;
+ if IsRendered and (ButtonType=ibtImage) then
+ Element.setAttribute('src',FSrc);
+end;
+
+procedure TButtonInputWidget.ApplyWidgetSettings(aElement: TJSHTMLElement);
+begin
+ inherited ApplyWidgetSettings(aElement);
+ if ButtonType=ibtImage then
+ aElement.setAttribute('src',FSrc);
+end;
+
+function TButtonInputWidget.InputType: String;
+
+Const
+ Types : Array[TInputButtonType] of string = ('submit','reset','image');
+
+begin
+ Result:=Types[FButtonType]
+end;
+
+class function TButtonInputWidget.AllowChildren: Boolean;
+begin
+ Result:=False;
+end;
+
+{ TTextInputWidget }
+
+function TTextInputWidget.GetAsNumber: NativeInt;
+begin
+ Result:=StrToIntDef(Value,0);
+end;
+
+function TTextInputWidget.GetMaxLength: NativeInt;
+begin
+ if IsElementDirty then
+ FMaxLength:=InputElement.maxLength;
+ Result:=FMaxLength;
+end;
+
+function TTextInputWidget.GetMinLength: NativeInt;
+begin
+ if IsElementDirty then
+ FMinLength:=InputElement.minLength;
+ Result:=FMinLength;
+end;
+
+function TTextInputWidget.GetTextType: TInputTextType;
+begin
+ Result:=FTextType;
+end;
+
+procedure TTextInputWidget.SetAsNumber(AValue: NativeInt);
+begin
+ Value:=IntToStr(aValue);
+end;
+
+
+procedure TTextInputWidget.SetMaxLength(AValue: NativeInt);
+begin
+ if (aValue=FMaxLength) then exit;
+ FMaxLength:=aValue;
+ if IsRendered then
+ InputElement.maxLength:=FMaxLength;
+end;
+
+procedure TTextInputWidget.SetMinLength(AValue: NativeInt);
+begin
+ if (aValue=FMinLength) then exit;
+ FMinLength:=aValue;
+ if IsRendered then
+ InputElement.minLength:=FMinLength;
+end;
+
+procedure TTextInputWidget.SetTextType(AValue: TInputTextType);
+begin
+ if aValue=FTextType then exit;
+ FTextType:=aValue;
+ if IsRendered then
+ Refresh;
+end;
+
+procedure TTextInputWidget.ApplyWidgetSettings(aElement: TJSHTMLElement);
+
+var
+ inp : TJSHTMLInputElement absolute aElement;
+
+begin
+ inherited ApplyWidgetSettings(aElement);
+ if FMaxLength<>0 then
+ inp.maxLength:=FMaxLength;
+ if FMinLength<>0 then
+ inp.minLength:=FMinLength;
+end;
+
+class function TTextInputWidget.AllowChildren: Boolean;
+begin
+ Result:=False;
+end;
+
+function TTextInputWidget.InputType: String;
+
+Const
+ Types : Array[TInputTextType] of string =
+ ('text','password','number','email','search','tel','url','color');
+
+begin
+ Result:=Types[FTextType];
+end;
+
+{ TWebPage }
+
+constructor TWebPage.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ Classes:='WebPage';
+end;
+
+
+class function TWebPage.DefaultParentElement: TJSHTMLElement;
+begin
+ Result:=TViewport.Instance.Element;
+end;
+
+class function TWebPage.DefaultParent: TCustomWebWidget;
+begin
+ Result:=TViewport.Instance;
+end;
+
+procedure TWebPage.DoUnRender(aParent: TJSHTMLElement);
+begin
+ inherited DoUnRender(aParent);
+end;
+
+function TWebPage.HTMLTag: String;
+begin
+ Result:='div';
+end;
+
+
+{ TViewPort }
+
+function TViewPort.HTMLTag: String;
+begin
+ Result:='body';
+end;
+
+class function TViewPort.FixedParent: TJSHTMLElement;
+begin
+ Result:=TJSHTMLElement(Document.documentElement);
+end;
+
+class function TViewPort.FixedElement: TJSHTMLElement;
+begin
+ Result:=TJSHTMLElement(Document.Body);
+end;
+
+function TViewPort.DoRenderHTML(aParent, aElement: TJSHTMLElement): TJSHTMLElement;
+begin
+ Result:=FixedElement;
+end;
+
+constructor TViewPort.Create(aOwner: TComponent);
+begin
+ inherited Create(aOwner);
+ EnsureElement;
+end;
+
+class function TViewPort.Instance: TViewPort;
+begin
+ if Finstance=Nil then
+ FInstance:=TViewPort.Create(Nil);
+ Result:=FInstance;
+end;
+
+{ TButtonWidget }
+
+{ TButtonWidget }
+
+procedure TButtonWidget.SetText(AValue: String);
+
+begin
+ if FText=AValue then Exit;
+ FText:=AValue;
+ if IsRendered then
+ Element.InnerText:=Ftext;
+end;
+
+
+procedure TButtonWidget.SetName(const NewName: TComponentName);
+
+Var
+ Old : String;
+
+begin
+ Old:=Name;
+ inherited SetName(NewName);
+ if (FText=Old) and (csDesigning in ComponentState) then
+ FText:=NewName;
+end;
+
+function TButtonWidget.HTMLTag: String;
+begin
+ Result:='button';
+end;
+
+procedure TButtonWidget.ApplyWidgetSettings(aElement: TJSHTMLElement);
+begin
+ Inherited;
+ aElement.InnerText:=Text;
+end;
+
+procedure TButtonWidget.Click;
+
+begin
+ DispatchEvent('click');
+end;
+
+
+{ TCustomInputWidget }
+
+function TCustomInputWidget.GetValue: String;
+
+Var
+ Inp : TJSHTMLInputElement;
+begin
+ Inp:=InputElement;
+ If Assigned(Inp) then
+ Result:=Inp.value
+ else
+ Result:=FValue
+end;
+
+function TCustomInputWidget.GetText: String;
+Var
+ Inp : TJSHTMLElement;
+
+begin
+ Inp:=Element;
+ If Assigned(Inp) then
+ Result:=Inp.InnerText
+ else
+ Result:=FText;
+end;
+
+function TCustomInputWidget.GetReadOnly: Boolean;
+begin
+ if IsElementDirty then
+ FReadonly:=InputElement.readOnly;
+ Result:=FReadonly;
+end;
+
+function TCustomInputWidget.GetRequired: Boolean;
+begin
+ if IsElementDirty then
+ FRequired:=InputElement.Required;
+ Result:=FRequired;
+end;
+
+function TCustomInputWidget.GetValueName: String;
+
+Var
+ Inp : TJSHTMLInputElement;
+
+begin
+ Inp:=InputElement;
+ If Assigned(Inp) then
+ Result:=Inp.Name
+ else
+ begin
+ Result:=FValueName;
+ if Result='' then
+ Result:=Name;
+ end;
+end;
+
+procedure TCustomInputWidget.SetReadonly(AValue: Boolean);
+begin
+ If aValue=ReadOnly then exit;
+ FReadOnly:=aValue;
+ if IsRendered then
+ InputElement.Readonly:=FReadOnly;
+end;
+
+procedure TCustomInputWidget.SetRequired(AValue: Boolean);
+begin
+ If aValue=Required then exit;
+ FRequired:=aValue;
+ if IsRendered then
+ InputElement.Required:=FRequired;
+end;
+
+procedure TCustomInputWidget.SetText(AValue: String);
+Var
+ Inp : TJSHTMLElement;
+
+begin
+ if aValue=Text then exit;
+ FText:=aValue;
+ Inp:=Element;
+ If Assigned(Inp) then
+ Inp.innerText:=aValue;
+end;
+
+procedure TCustomInputWidget.SetValue(AValue: String);
+
+Var
+ Inp : TJSHTMLInputElement;
+
+begin
+ if aValue=Value then exit;
+ FValue:=aValue;
+ Inp:=InputElement;
+ If Assigned(Inp) then
+ Inp.value:=aValue;
+end;
+
+procedure TCustomInputWidget.ApplyWidgetSettings(aElement: TJSHTMLElement);
+
+var
+ Inp : TJSHTMLInputElement absolute aElement;
+
+begin
+ Inherited;
+ if (ExternalElement) and (FValue='') then
+ FValue:=TJSHTMLInputElement(aElement).value
+ else
+ begin
+ Inp._type:=InputType;
+ Inp.name:=FValueName;
+ Inp.value:=FValue;
+ Inp.Required:=FRequired;
+ Inp.ReadOnly:=FReadOnly;
+ end;
+end;
+
+function TCustomInputWidget.HTMLTag: String;
+begin
+ Result:='input';
+end;
+
+function TCustomInputWidget.GetInputElement: TJSHTMLInputElement;
+begin
+ Result:=TJSHTMLInputElement(Element);
+end;
+
+procedure TCustomInputWidget.SetValueName(AValue: String);
+Var
+ Inp : TJSHTMLInputElement;
+begin
+ if aValue=ValueName then exit;
+ FValueName:=aValue;
+ Inp:=InputElement;
+ If Assigned(Inp) then
+ Inp.name:=aValue;
+end;
+
+procedure TCustomInputWidget.SetName(const NewName: TComponentName);
+
+Var
+ Old : String;
+
+begin
+ Old:=Name;
+ inherited SetName(NewName);
+ if (Value=Old) then
+ Value:=NewName;
+end;
+
+end.
+
diff --git a/packages/webwidget/lazwebwidgets.lpk b/packages/webwidget/lazwebwidgets.lpk
new file mode 100644
index 0000000..162d027
--- /dev/null
+++ b/packages/webwidget/lazwebwidgets.lpk
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/webwidget/lazwebwidgets.pas b/packages/webwidget/lazwebwidgets.pas
new file mode 100644
index 0000000..a26f92f
--- /dev/null
+++ b/packages/webwidget/lazwebwidgets.pas
@@ -0,0 +1,15 @@
+{ This file was automatically created by Lazarus. Do not edit!
+ This source is only used to compile and install the package.
+ }
+
+unit lazwebwidgets;
+
+{$warn 5023 off : no warning about unused units}
+interface
+
+uses
+ webwidget;
+
+implementation
+
+end.
diff --git a/packages/webwidget/tests/btnrun.pp b/packages/webwidget/tests/btnrun.pp
new file mode 100644
index 0000000..ab79b59
--- /dev/null
+++ b/packages/webwidget/tests/btnrun.pp
@@ -0,0 +1,42 @@
+unit btnrun;
+
+{$mode objfpc}
+
+interface
+
+uses
+ Classes, fpcunitreport, BrowserConsole, web;
+
+Type
+
+ { TConsoleRunner }
+
+ TConsoleRunner = Class(TRunForm)
+ Private
+ FRun : TJSHTMLButtonElement;
+ function DoRunTest(aEvent: TJSMouseEvent): boolean;
+ public
+ procedure initialize; override;
+ end;
+
+implementation
+
+{ TConsoleRunner }
+
+function TConsoleRunner.DoRunTest(aEvent: TJSMouseEvent): boolean;
+begin
+ Result:=False;
+ ResetConsole;
+ If Assigned(OnRun) then
+ OnRun(Self);
+end;
+
+procedure TConsoleRunner.initialize;
+begin
+ FRun:=TJSHTMLButtonElement(document.getElementById('RunTest'));
+ FRun.onClick:=@DoRunTest;
+ ResetConsole;
+end;
+
+end.
+
diff --git a/packages/webwidget/tests/tchtmlwidgets.pp b/packages/webwidget/tests/tchtmlwidgets.pp
new file mode 100644
index 0000000..4bf6764
--- /dev/null
+++ b/packages/webwidget/tests/tchtmlwidgets.pp
@@ -0,0 +1,1155 @@
+unit tcHTMLWidgets;
+
+{$mode objfpc}
+
+interface
+
+uses
+ Classes, SysUtils, fpcunit, testregistry, web, webwidget, htmlwidgets, tcwidget;
+
+Type
+ { TTestButtonWidget }
+
+ TTestButtonWidget = Class(TBaseTestWidget)
+ private
+ FButton: TButtonWidget;
+ Protected
+ Procedure SetUp; override;
+ Procedure TearDown; override;
+ Property Button : TButtonWidget Read FButton;
+ Published
+ Procedure TestTextBeforeRender;
+ Procedure TestTextAfterRender;
+ Procedure TestTextElementID;
+ Procedure TestClick;
+ end;
+
+ { TTestLabelWidget }
+ TMyLabelWidget = Class(TLabelWidget)
+ Public
+ Property LabelElement;
+ end;
+
+ TTestLabelWidget = Class(TBaseTestWidget)
+ private
+ FEdit: TTextInputWidget;
+ FMy: TMyLabelWidget;
+ Protected
+ Procedure SetUp; override;
+ Procedure TearDown; override;
+ Property My : TMyLabelWidget Read FMy;
+ Property Edit : TTextInputWidget Read FEdit;
+ Published
+ Procedure TestPropsBeforeRender;
+ Procedure TestPropsAfterRender;
+ end;
+
+ { TTestViewPort }
+
+ { TMyViewPort }
+
+ TMyViewPort = Class(TViewPort)
+ Public
+ Procedure SetParentId;
+ Procedure SetParent;
+ Procedure SetElementID;
+ end;
+
+ TTestViewPort = Class(TBaseTestWidget)
+ private
+ FMy: TMyViewPort;
+ Protected
+ Procedure Setup; override;
+ Procedure TearDown; override;
+ Property My : TMyViewPort Read FMy;
+ Published
+ Procedure TestInstance;
+ Procedure TestHTMLTag;
+ Procedure TestElement;
+ Procedure TestUnrender;
+ Procedure TestNoParent;
+ Procedure TestNoElementID;
+ Procedure TestNoParentID;
+ end;
+
+ { TTestPage }
+
+ { TMyWebPage }
+
+ TMyWebPage = Class(TWebPage)
+ Public
+ Procedure SetParentId;
+ Procedure SetParent;
+ Procedure SetElementID;
+ end;
+
+ TTestPage = Class(TBaseTestWidget)
+ private
+ FMy: TMyWebPage;
+ Protected
+ Function CreateElement(aID : String) : TJSHTMLElement;
+ Procedure Setup; override;
+ Procedure TearDown; override;
+ Property My : TMyWebPage Read FMy;
+ Published
+ Procedure TestEmpty;
+ Procedure TestAsWindow;
+ Procedure TestNoParentOK;
+ Procedure TestDefaultTag;
+ end;
+
+ { TBaseTestInputElement }
+
+ TInputHack = class(TCustomInputWidget)
+ Public
+ Property Element;
+ Property InputElement;
+ end;
+
+ TBaseTestInputElement = Class(TBaseTestWidget)
+ private
+ FMy: TCustomInputWidget;
+ function GetInputElement: TJSHTMLInputElement;
+ Protected
+ // Must be handled in descendent. Called during setup to populate My.
+ Function CreateInput : TCustomInputWidget; virtual; abstract;
+ // (Re)create my. Calls createinput
+ Procedure CreateMy; virtual;
+ Procedure Setup; override;
+ Procedure TearDown; override;
+ // Assert basic properties are correct on the element.
+ procedure AssertBaseProps(aType, aValueName, aValue: String; aText: String='');
+ Property My : TCustomInputWidget Read FMy;
+ Property InputElement : TJSHTMLInputElement Read GetInputElement;
+ Published
+ Procedure TestEmpty;
+ Procedure TestRequiredOnRender;
+ Procedure TestReadOnlyOnRender;
+ Procedure TestRequiredAfterRender;
+ Procedure TestReadOnlyAfterRender;
+ end;
+
+ { TTestTextInputElement }
+
+ TTestTextInputElement = Class(TBaseTestInputElement)
+ Protected
+ FITT: TInputTextType;
+ Procedure setup; override;
+ Function CreateInput : TCustomInputWidget; override;
+ Function MyText : TTextInputWidget;
+ Published
+ Procedure TestDefaultTextType;
+ Procedure TestRender;
+ Procedure TestChangeValue;
+ Procedure TestChangeName;
+ Procedure TestChangeTextType;
+ Procedure TestTypePassword;
+ Procedure TestTypeNumber;
+ Procedure TestAsNumber;
+ Procedure TestTypeEmail;
+ Procedure TestTypeSearch;
+ Procedure TestTypeTel;
+ Procedure TestTypeURL;
+ Procedure TestTypeColor;
+ end;
+
+ { TTestRadioInputElement }
+
+ TTestRadioInputElement = Class(TBaseTestInputElement)
+ Protected
+ Function CreateInput : TCustomInputWidget; override;
+ Function MyRadio : TRadioInputWidget;
+ Published
+ Procedure TestPropsOnRender;
+ Procedure TestPropsAfterRender;
+ end;
+
+ TTestCheckboxInputElement = Class(TBaseTestInputElement)
+ Protected
+ Function CreateInput : TCustomInputWidget; override;
+ Function MyCheckbox : TCheckboxInputWidget;
+ Published
+ Procedure TestPropsOnRender;
+ Procedure TestPropsAfterRender;
+ end;
+
+ TMyDateInputWidget = Class(TDateInputWidget)
+ end;
+
+ { TTestDateInputElement }
+
+ TTestDateInputElement = Class(TBaseTestInputElement)
+ Protected
+ Function CreateInput : TCustomInputWidget; override;
+ Procedure CreateMy; override;
+ Function MyDate : TMyDateInputWidget;
+ Published
+ Procedure TestPropsOnRender;
+ Procedure TestPropsAfterRender;
+ end;
+
+ TMyFileInputWidget = Class(TFileInputWidget)
+ end;
+
+ { TTestFileInputElement }
+
+ TTestFileInputElement = Class(TBaseTestInputElement)
+ Protected
+ Function CreateInput : TCustomInputWidget; override;
+ Procedure CreateMy; override;
+ Function MyFile : TMyFileInputWidget;
+ Published
+ Procedure TestPropsOnRender;
+ Procedure TestPropsAfterRender;
+ end;
+
+ TMyHiddenInputWidget = Class(THiddenInputWidget)
+ end;
+
+ { TTestHiddenInputElement }
+
+ TTestHiddenInputElement = Class(TBaseTestInputElement)
+ Protected
+ Function CreateInput : TCustomInputWidget; override;
+ Function MyHidden : TMyHiddenInputWidget;
+ Published
+ Procedure TestPropsOnRender;
+ Procedure TestPropsAfterRender;
+ end;
+
+ { TTestTextAreaElement }
+ TMyTextAreaWidget = Class(TTextAreaWidget)
+ Public
+ Property TextArea;
+ end;
+
+ TTestTextAreaElement = Class(TBaseTestWidget)
+ private
+ FMy: TMyTextAreaWidget;
+ function GetArea: TJSHTMLTextAreaElement;
+ Protected
+ Procedure Setup; override;
+ Procedure TearDown; override;
+ Property My : TMyTextAreaWidget Read FMy;
+ Property Area : TJSHTMLTextAreaElement Read GetArea;
+ Published
+ Procedure TestEmpty;
+ Procedure TestPropsOnRender;
+ Procedure TestPropsAfterRender;
+ end;
+
+ TMyImageWidget = Class(TImageWidget)
+ Public
+ Property Element;
+ end;
+
+ { TTestImageElement }
+
+ TTestImageElement = Class(TBaseTestWidget)
+ private
+ FMy: TMyImageWidget;
+ function GetImg: TJSHTMLImageElement;
+ Protected
+ Procedure Setup; override;
+ Procedure TearDown; override;
+ Function ThisURL : String;
+ Property My : TMyImageWidget Read FMy;
+ Property Image : TJSHTMLImageElement Read GetImg;
+ Published
+ Procedure TestEmpty;
+ Procedure TestPropsOnRender;
+ Procedure TestPropsAfterRender;
+ end;
+
+ TMySelectWidget = Class(TSelectWidget)
+ Public
+ Property Element;
+ Property SelectElement;
+ Property Options;
+ end;
+
+ { TTestSelectElement }
+
+ TTestSelectElement = Class(TBaseTestWidget)
+ private
+ FMy: TMySelectWidget;
+ procedure AssertOption(Idx: Integer; aText, aValue: String; Selected: Boolean=False);
+ function GetOptions: TJSHTMLOPtionElementArray;
+ function GetSelect: TJSHTMLSelectElement;
+ Protected
+ Procedure Setup; override;
+ Procedure TearDown; override;
+ Property My : TMySelectWidget Read FMy;
+ Property Select : TJSHTMLSelectElement Read GetSelect;
+ Property Options : TJSHTMLOPtionElementArray Read GetOptions;
+ Published
+ Procedure TestEmpty;
+ Procedure TestPropsOnRender;
+ Procedure TestPropsAfterRender;
+ Procedure TestMultiSelect;
+ end;
+
+
+implementation
+
+{ TTestLabelWidget }
+
+procedure TTestLabelWidget.SetUp;
+begin
+ inherited SetUp;
+ FMy:=TMyLabelWidget.Create(Nil);
+ My.Text:='Your name';
+ My.ParentID:=SBaseWindowID;
+ FEdit:=TTextInputWidget.Create(Nil);
+ FEdit.ParentID:=SBaseWindowID;
+ FMy.LabelFor:=Edit;
+end;
+
+procedure TTestLabelWidget.TearDown;
+begin
+ FreeAndNil(Fmy);
+ FreeAndNil(FEdit);
+ inherited TearDown;
+end;
+
+procedure TTestLabelWidget.TestPropsBeforeRender;
+begin
+ Edit.Refresh;
+ My.Refresh;
+ AssertEquals('text','Your name',My.LabelElement.innerText);
+ AssertEquals('for',Edit.ElementID,My.LabelElement.For_);
+end;
+
+procedure TTestLabelWidget.TestPropsAfterRender;
+begin
+ My.LabelFor:=Nil;
+ My.Refresh;
+ AssertEquals('text','Your name',My.LabelElement.innerText);
+ AssertEquals('for','',My.LabelElement.For_);
+ // Will render Edit!
+ My.LabelFor:=Edit;
+ AssertTrue('Have edit id',Edit.ElementID<>'');
+ My.Text:='My Name';
+ My.Refresh;
+ AssertEquals('text','My Name',My.LabelElement.innerText);
+ AssertEquals('for',Edit.ElementID,My.LabelElement.For_);
+end;
+
+
+{ TTestSelectElement }
+
+function TTestSelectElement.GetOptions: TJSHTMLOPtionElementArray;
+begin
+ Result:=My.Options;
+end;
+
+function TTestSelectElement.GetSelect: TJSHTMLSelectElement;
+begin
+ Result:=My.SelectElement;
+end;
+
+procedure TTestSelectElement.Setup;
+begin
+ inherited Setup;
+ FMy:=TMySelectWidget.Create(Nil);
+ FMy.ParentID:=SBaseWindowID;
+ FMy.Items.Add('One');
+ FMy.Items.Add('Two');
+ FMy.Items.Add('Three');
+ FMy.Values.Add('1');
+ FMy.Values.Add('2');
+ FMy.Values.Add('3');
+ FMy.SelectedIndex:=0;
+end;
+
+procedure TTestSelectElement.TearDown;
+begin
+ FreeAndNil(FMy);
+ inherited TearDown;
+end;
+
+
+procedure TTestSelectElement.TestEmpty;
+begin
+ AssertNotNull('Have widget',My);
+ AssertNull('Not rendered',My.Element);
+end;
+
+procedure TTestSelectElement.AssertOption(Idx : Integer; aText,aValue : String; Selected : Boolean= False);
+
+Var
+ O : TJSHTMLOptionElement;
+
+begin
+ AssertTrue('Correct index',Idx
+
+
+}
+Var
+ I : Integer;
+begin
+ Result:=inherited DoRenderHTML(aParent, aElement);
+ FUL:=TJSHTMLElement(Document.CreateElement(''));
+ Result.insertBefore(Ful,FSub);
+ Result.AppendChild(FUL);
+ SetLength(FSubs,10);
+ For I:=0 to 9 do
+ begin
+ FSubs[i]:=TJSHTMLElement(Document.CreateElement('li'));
+ FSubs[i].InnerText:='item '+IntToStr(I);
+ FUL.AppendChild(FSubs);
+ end;
+end;
+
+{ TTestWebWidgetReferences }
+
+function TTestWebWidgetReferences.GetItems: TWebWidgetReferences;
+begin
+ Result:=FMy.References;
+end;
+
+procedure TTestWebWidgetReferences.Setup;
+begin
+ inherited Setup;
+end;
+
+procedure TTestWebWidgetReferences.TearDown;
+begin
+ inherited TearDown;
+end;
+
+procedure TTestWebWidgetReferences.TestEmpty;
+begin
+
+end;
+
+{ TTestWebWidgetStyles }
+
+function TTestWebWidgetStyles.GetItems: TWebWidgetStyles;
+begin
+ Result:=MyWidget.Styles;
+end;
+
+procedure TTestWebWidgetStyles.Setup;
+begin
+ inherited Setup;
+ FMy:=TMyChildWidget.Create(Nil);
+ FMy.ParentID:=SBaseWindowID;
+end;
+
+procedure TTestWebWidgetStyles.TearDown;
+begin
+ FreeAndNil(FMy);
+ inherited TearDown;
+end;
+
+procedure TTestWebWidgetStyles.TestEmpty;
+begin
+ AssertNotNull('Have widget',MyWidget);
+ AssertNotNull('Have widget styles',MyWidget.Styles);
+ AssertNull('Not yet rendered',MyWidget.MyElement);
+end;
+
+procedure TTestWebWidgetStyles.AssertStyle(aIdx : integer; const AName,aValue : String; CheckElement : Boolean = True);
+
+begin
+ AssertTrue('Correct index',(aIdx>=0) and (aIdx-1);
+ AssertStyle(idx,'display','block',False);
+ AssertTrue('display imported',Styles[idx].Imported);
+ idx:=Styles.IndexOfStyle('min-width');
+ AssertTrue('Have min-width',idx<>-1);
+ AssertStyle(idx,'min-width','40px',False);
+ AssertTrue('min-width imported',Styles[idx].Imported);
+ idx:=Styles.IndexOfStyle('min-height');
+ AssertTrue('Have min-height',idx<>-1);
+ AssertStyle(idx,'min-height','50px',False);
+ AssertTrue('min-height imported',Styles[idx].Imported);
+end;
+
+procedure TTestWebWidgetStyles.TestDirty;
+begin
+ MyWidget.Refresh;
+ Styles.Add('display','none');
+ AssertStyle(0,'display','none',True);
+ Styles.EnsureStyle('display','block');
+ AssertStyle(0,'display','block',True);
+end;
+
+procedure TTestWebWidgetStyles.TestClearImported;
+begin
+ TestRefreshFromDOM;
+ Styles.ClearImported;
+ AssertEquals('None left',0,Styles.Count);
+end;
+
+{ TMyPrefixSubContentWidget }
+
+function TMyPrefixSubContentWidget.DoRenderHTML(aParent, aElement: TJSHTMLElement): TJSHTMLElement;
+begin
+ Result:=inherited DoRenderHTML(aParent, aElement);
+ FTop:=CreateElement('span',aElement.ID+'-span');
+ aParent.removeChild(aElement);
+ FTop.AppendChild(aElement);
+ aParent.appendChild(FTop);
+end;
+
+procedure TMyPrefixSubContentWidget.DoUnRender(aParent: TJSHTMLElement);
+begin
+ inherited DoUnRender(aParent);
+ FTop:=Nil;
+end;
+
+function TMyPrefixSubContentWidget.GetTopElement: TJSHTMLELement;
+begin
+ Result:=FTop;
+end;
+
+
+{ TMySubContentWidget }
+
+function TMySubContentWidget.DoRenderHTML(aParent, aElement: TJSHTMLElement): TJSHTMLElement;
+begin
+ Result:=inherited DoRenderHTML(aParent, aElement);
+ FSub:=CreateElement('div',aElement.ID+'-sub');
+ Result.AppendChild(FSub);
+end;
+
+procedure TMySubContentWidget.DoUnRender(aParent: TJSHTMLElement);
+begin
+ inherited DoUnRender(aParent);
+ FSub:=nil;
+end;
+
+function TMySubContentWidget.GetContentElement: TJSHTMLELement;
+begin
+ Result:=FSub;
+end;
+
+{ TMyWebWidget }
+
+function TMyWebWidget.WidgetClasses: String;
+begin
+ Result:=FAdd;
+end;
+
+function TMyWebWidget.DoRenderHTML(aParent,aElement: TJSHTMLElement): TJSHTMLElement;
+begin
+ // Do nothing
+ if aParent<>Nil then;
+ Result:=aElement;
+end;
+
+function TMyWebWidget.HTMLTag: String;
+begin
+ Result:='div'
+end;
+
+function TMyWebWidget.MyElement: TJSHTMLElement;
+begin
+ Result:=Element;
+end;
+
+function TMyWebWidget.MyParent: TJSHTMLElement;
+begin
+ Result:=ParentElement;
+end;
+
+function TMyWebWidget.MyContent: TJSHTMLElement;
+begin
+ Result:=ContentElement;
+end;
+
+function TMyWebWidget.MyTop: TJSHTMLElement;
+
+begin
+ Result:=TopElement;
+end;
+
+{ TTestWidgetBasicOperations }
+
+function TTestWidgetBasicOperations.GetTop: TMyPrefixSubContentWidget;
+begin
+ if FMyTop=Nil then
+ FMyTop:=TMyPrefixSubContentWidget.Create(Nil);
+ Result:=FMyTop;
+end;
+
+function TTestWidgetBasicOperations.GetMySub: TMySubContentWidget;
+begin
+ If FMySub=Nil then
+ FMySub:=TMySubContentWidget.Create(Nil);
+ Result:=FMySub;
+end;
+
+procedure TTestWidgetBasicOperations.SetUp;
+
+begin
+ inherited SetUp;
+ FMy:=TMyChildWidget.Create(Nil);
+ AssertEquals('Correct tag','div',FMy.HTMLTag);
+ FMyParent:=TMyParentWidget.Create(Nil);
+ AssertEquals('Correct parent tag','div',FMyParent.HTMLTag);
+ FEventCount:=0;
+ FEventSender:=Nil;
+ FEventName:='';
+ FDefaultPrevented:=False;
+ FMySub:=Nil;
+ FMyTop:=Nil;
+end;
+
+procedure TTestWidgetBasicOperations.TearDown;
+begin
+ FreeAndNil(FMySub);
+ FreeAndNil(FMyTop);
+ FreeAndNil(FMy);
+ FreeAndNil(FMyParent);
+ inherited TearDown;
+end;
+
+procedure TTestWidgetBasicOperations.TriggerEvent(aName: String; aClass : TJSEventClass = Nil);
+
+Var
+ ev : TJSEvent;
+
+begin
+ if aClass=Nil then
+ ev:=TJSEvent.New(aName)
+ else
+ ev:=aClass.New(aName);
+ FDefaultPrevented:=FMy.EnsureElement.dispatchEvent(ev);
+end;
+
+
+
+function TTestWidgetBasicOperations.CreateParentElement: TJSHTMLElement;
+
+
+begin
+ Result:=TJSHTMLElement(Document.CreateElement('div'));
+ Result.Id:=SMyParentID;
+ BaseWindow.AppendChild(Result);
+end;
+
+function TTestWidgetBasicOperations.CreateMyElement(asChild: Boolean = False): TJSHTMLELement;
+Var
+ El : TJSHTMLElement;
+begin
+ if AsChild then
+ El:=CreateParentElement
+ else
+ El:=BaseWindow;
+ Result:=TJSHTMLElement(Document.CreateElement('div'));
+ Result.Id:=SMyChildID;
+ El.AppendChild(Result);
+end;
+
+procedure TTestWidgetBasicOperations.SetParentElement;
+
+begin
+ FMy.Parent:=FMyParent;
+end;
+
+procedure TTestWidgetBasicOperations.DoSetElementID;
+begin
+ FMy.ElementID:=SMyChildID;
+end;
+
+procedure TTestWidgetBasicOperations.DoSetParentID;
+begin
+ FMy.ParentID:=SMyParentID;;
+end;
+
+procedure TTestWidgetBasicOperations.DoSetDataset;
+begin
+ MyWidget.Data['name']:='me';
+end;
+
+
+function TTestWidgetBasicOperations.SetupParent: TJSHTMLElement;
+begin
+ Result:=CreateParentElement;
+ FMyParent.ElementID:=SMyParentID;
+end;
+
+procedure TTestWidgetBasicOperations.TestEmpty;
+begin
+ AssertNotNull(MyWidget);
+ AssertNotNull(MyParentWidget);
+end;
+
+procedure TTestWidgetBasicOperations.TestNoElementIDAndParentElementID;
+begin
+ DoSetElementID;
+ AssertException('Cannot set both',EWidgets,@DoSetParentID);
+end;
+
+procedure TTestWidgetBasicOperations.TestNoParentElementIDAndElementID;
+begin
+ DoSetParentID;
+ AssertException('Cannot set both',EWidgets,@DoSetElementID);
+end;
+
+procedure TTestWidgetBasicOperations.TestParentIDToElement;
+
+Var
+ El : TJSHTMLElement;
+
+begin
+ El:=CreateParentElement;
+ DoSetParentID;
+ AssertSame('Correct parent element',El,MyWidget.MyParent);
+ AssertSame('Correct content element',MyWidget.MyElement,MyWidget.MyContent);
+ AssertTree('div('+SMyParentID+')');
+end;
+
+procedure TTestWidgetBasicOperations.TestElementIDToElement;
+Var
+ El : TJSHTMLElement;
+
+begin
+ El:=CreateMyElement;
+ DoSetElementID;
+ AssertSame('Correct element',El,MyWidget.MyElement);
+ AssertSame('Correct content element',El,MyWidget.MyContent);
+ AssertTree('div('+SMyChildID+')');
+ AssertEquals('Have element data',el.ID,String(el.dataset['WwElement']));
+ AssertEquals('Have element top data',el.ID,String(el.dataset['WwElementTop']));
+ AssertEquals('Have element content data',el.ID,String(el.dataset['WwElementContent']));
+end;
+
+procedure TTestWidgetBasicOperations.TestSetParent;
+
+Var
+ El : TJSHTMLElement;
+
+begin
+ El:=SetupParent;
+ AssertSame('Correct parent element',El,MyParentWidget.MyElement);
+ MyWidget.Parent:=MyParentWidget;
+ AssertEquals('Child count correct',1,MyParentWidget.ChildCount);
+ AssertSame('Correct parent element',El,MyWidget.MyParent);
+ AssertNull('No element yet',MyWidget.Element);
+ MyWidget.Refresh;
+ AssertTrue('Have element ID',MyWidget.ElementID<>'');
+ AssertTree('div('+SMyParentID+')/div('+MyWidget.ElementID+')'); // No ID assigned !
+ MyWidget.Parent:=Nil;
+ AssertEquals('Child count correct',0,MyParentWidget.ChildCount);
+ AssertNull('No more parent element ',MyWidget.MyParent);
+end;
+
+procedure TTestWidgetBasicOperations.TestRenderParent;
+
+Var
+ El,El2 : TJSHTMLElement;
+
+begin
+ El:=SetupParent;
+ MyWidget.Parent:=MyParentWidget;
+ MyWidget.Refresh;
+ El2:=MyWidget.MyElement;
+ AssertNotNull('Have element',El2);
+ AssertSame('Have content element',El2,MyWidget.MyContent);
+ AssertSame('Have correct parent element',El,El2.parentElement);
+ AssertSame('Have correct parent',El,MyWidget.MyParent);
+ AssertEquals('Correct ID',el2.ID,MyWidget.ElementiD);
+ AssertEquals('Have element data',el2.ID,String(el2.dataset['WwElement']));
+ AssertEquals('Have element top data',el2.ID,String(el2.dataset['WwElementTop']));
+ AssertEquals('Have element content data',el2.ID,String(el2.dataset['WwElementContent']));
+end;
+
+procedure TTestWidgetBasicOperations.TestRenderParentID;
+
+Var
+ El,El2 : TJSHTMLElement;
+
+begin
+ El:=CreateParentElement;
+ MyWidget.ParentID:=el.ID;
+ MyWidget.Refresh;
+ El2:=MyWidget.MyElement;
+ AssertNotNull('Have element',El2);
+ AssertSame('Have content element',El2,MyWidget.MyContent);
+ AssertSame('Have correct parent element',El,El2.parentElement);
+ AssertSame('Have correct parent',El,MyWidget.MyParent);
+ AssertEquals('Correct ID',el2.ID,MyWidget.ElementiD);
+ AssertEquals('Have element data',el2.ID,String(el2.dataset['WwElement']));
+ AssertEquals('Have element top data',el2.ID,String(el2.dataset['WwElementTop']));
+ AssertEquals('Have element content data',el2.ID,String(el2.dataset['WwElementContent']));
+end;
+
+procedure TTestWidgetBasicOperations.TestUnRenderParent;
+Var
+ El,El2 : TJSHTMLElement;
+
+begin
+ El:=SetupParent;
+ MyWidget.Parent:=MyParentWidget;
+ MyWidget.Refresh;
+ El2:=MyWidget.MyElement;
+ AssertNotNull('Have element',El2);
+ MyWidget.Parent:=Nil;
+ AssertEquals('Not rendered any more',0,el.childElementCount);
+ AssertNull('Have no more element',MyWidget.MyElement);
+ AssertNull('Have no more parent element',MyWidget.MyParent);
+end;
+
+procedure TTestWidgetBasicOperations.TestUnRenderParentID;
+Var
+ El,El2 : TJSHTMLElement;
+
+begin
+ El:=CreateParentElement;
+ MyWidget.ParentID:=el.ID;
+ MyWidget.Refresh;
+ El2:=MyWidget.MyElement;
+ AssertNotNull('Have element',El2);
+ MyWidget.ParentID:='';
+ AssertEquals('Not rendered any more',0,el.childElementCount);
+ AssertNull('Have no more element',MyWidget.MyElement);
+ AssertNull('Have no more parent element',MyWidget.MyParent);
+end;
+
+procedure TTestWidgetBasicOperations.TestUnRenderElementID;
+
+Var
+ El : TJSHTMLElement;
+
+begin
+ TestElementIDToElement;
+ El:=MyWidget.MyElement;
+ // This will unrender
+ MyWidget.ElementID:='';
+ AssertTrue('Have removed element data', IsUndefined(el.dataset['WwElement']));
+ AssertTrue('Have removed element top data',isUndefined(el.dataset['WwElementTop']));
+ AssertTrue('Have removed element content data',isUndefined(el.dataset['WwElementContent']));
+
+end;
+
+procedure TTestWidgetBasicOperations.TestSubContent;
+
+
+begin
+ SetupParentElement;
+ MySubWidget.Parent:=MyParentWidget;
+ MySubWidget.Refresh;
+ AssertTree('div('+SMyParentID+')/div('+MySubWidget.ElementID+')/div('+MySubWidget.ElementID+'-sub)');
+ AssertNotNull('have content',MySubWidget.MyContent);
+ AssertSame('content element parent is element',MySubWidget.MyElement,MySubWidget.MyContent.parentElement);
+end;
+
+procedure TTestWidgetBasicOperations.TestTopContent;
+begin
+{ SetupParentElement;
+ MySubWidget.Parent:=MyParentWidget;
+ MySubWidget.Refresh;
+ AssertTree('div('+SMyParentID+')/span('+MySubWidget.ElementID+'-top)/div('+MySubWidget.ElementID+')/div('+MySubWidget.ElementID+'-sub)');
+ AssertNotNull('have content',MySubWidget.MyContent);
+ AssertNotNull('have top content',MySubWidget.MyTop);
+ AssertSame('top element parent is element',MyParentWidget.MyContent,MySubWidget.MyTop.parentElement);
+ AssertSame('element parent is top element',MySubWidget.MyTop,MySubWidget.MyElement.parentElement);
+ AssertSame('content element parent is element',MySubWidget.MyElement,MySubWidget.MyContent.parentElement);}
+end;
+
+procedure TTestWidgetBasicOperations.TestAddClasses;
+
+Var
+ S : String;
+
+begin
+ S:=TWebWidget.AddClasses('a b c','d c e');
+ AssertEquals('Correctly added, no duplicates','a b c d e',S);
+end;
+
+procedure TTestWidgetBasicOperations.TestAddClassesNormalized;
+Var
+ S : String;
+
+begin
+ S:=TWebWidget.AddClasses('a b c','d c e',true);
+ AssertEquals('Correctly added, no duplicates','a b c d e',S);
+end;
+
+procedure TTestWidgetBasicOperations.TestRemoveClasses;
+
+Var
+ S : String;
+
+begin
+ S:=TWebWidget.RemoveClasses('a b c','d c e');
+ AssertEquals('Correctly removed','a b ',S);
+end;
+
+procedure TTestWidgetBasicOperations.TestRemoveClassesNormalized;
+Var
+ S : String;
+
+begin
+ S:=TWebWidget.RemoveClasses('a b c','d c e',True);
+ AssertEquals('Correctly removed','a b',S);
+end;
+
+procedure TTestWidgetBasicOperations.TestClassesBeforeRender;
+begin
+ SetupParent;
+ MyWidget.Parent:=MyParentWidget;
+ MyWidget.Classes:='a b c';
+ MyWidget.Refresh;
+ AssertNotNull('Have element',MyWidget.Element);
+ AssertEquals('Have element classes ','a b c',MyWidget.Element.className);
+end;
+
+procedure TTestWidgetBasicOperations.TestClassesAfterRender;
+begin
+ SetupParent;
+ MyWidget.Parent:=MyParentWidget;
+ MyWidget.Refresh;
+ AssertNotNull('Have element',MyWidget.Element);
+ MyWidget.Classes:='a b c';
+ AssertEquals('Have element classes ','a b c',MyWidget.Element.className);
+end;
+
+procedure TTestWidgetBasicOperations.TestWidgetClassesBeforeRender;
+begin
+ SetupParent;
+ MyWidget.Parent:=MyParentWidget;
+ MyWidget.AddedClasses:='d e c';
+ MyWidget.Refresh;
+ AssertNotNull('Have element',MyWidget.Element);
+ MyWidget.Classes:='a b c';
+ AssertEquals('Have element classes ','a b c d e',MyWidget.Element.className);
+end;
+
+procedure TTestWidgetBasicOperations.TestWidgetClassesAfterRender;
+begin
+ SetupParent;
+ MyWidget.Parent:=MyParentWidget;
+ MyWidget.Refresh;
+ AssertNotNull('Have element',MyWidget.Element);
+ MyWidget.AddedClasses:='d e c';
+ // They are not yet added
+ AssertEquals('Have element classes before setting','',MyWidget.Element.className);
+ // but now they are added
+ MyWidget.Classes:='a b c';
+ AssertEquals('Have element classes ','a b c d e',MyWidget.Element.className);
+end;
+
+procedure TTestWidgetBasicOperations.TestClassesOnElementID;
+
+var
+ el : TJSHTMLElement;
+
+begin
+ el:=CreateMyElement();
+ MyWidget.Classes:='a b c';
+ MyWidget.ElementID:=SMyChildID;
+ AssertNotNull('Have element',MyWidget.MyElement);
+ AssertSame('Have element',El,MyWidget.MyElement);
+ AssertEquals('Have element classes ','a b c',MyWidget.Element.className);
+end;
+
+procedure TTestWidgetBasicOperations.TestStylesBeforeRender;
+begin
+ MyWidget.ParentID:=BaseID;
+ MyWidget.Styles.Add('display').Value:='none';
+ MyWidget.Refresh;
+ AssertTree('div('+MyWidget.ElementID+')');
+ AssertEquals('have style applied','none',MyWidget.MyElement.style.getPropertyValue('display'));
+end;
+
+procedure TTestWidgetBasicOperations.TestStylesAfterRender;
+begin
+ MyWidget.ParentID:=BaseID;
+ MyWidget.Refresh;
+ MyWidget.Styles.Add('display').Value:='none';
+ AssertTree('div('+MyWidget.ElementID+')');
+ AssertEquals('have style applied','none',MyWidget.MyElement.style.getPropertyValue('display'));
+end;
+
+procedure TTestWidgetBasicOperations.TestStylesRefreshOnRender;
+
+var
+ Idx : Integer;
+
+begin
+ CreateMyElement(False).Style.setProperty('width','30px');
+ AssertTree('div('+MyWidget.ElementID+')');
+ MyWidget.ElementID:=SMyChildID;
+ AssertTree('div('+MyWidget.ElementID+')');
+ MyWidget.Refresh;
+ MyWidget.Styles.Add('display').Value:='none';
+ AssertEquals('have style applied','none',MyWidget.MyElement.style.getPropertyValue('display'));
+ AssertEquals('have pre-existing imported: count',2,MyWidget.Styles.Count);
+ Idx:=MyWidget.Styles.IndexOfStyle('width');
+ AssertTrue('have pre-existing imported: find name',Idx<>-1);
+ AssertEquals('have pre-existing imported: have value','30px',MyWidget.Styles[idx].Value);
+ AssertEquals('have pre-existing imported: marked as imported',True,MyWidget.Styles[idx].IMported);
+end;
+
+procedure TTestWidgetBasicOperations.TestVisibleBeforeRender;
+begin
+ MyWidget.Visible:=False;
+ SetupElement;
+ AssertFalse('Visible false',MyWidget.Visible);
+ AssertEquals('Display none','none',MyWidget.MyElement.Style.getPropertyValue('display'));
+end;
+
+procedure TTestWidgetBasicOperations.TestVisibleAfterRender;
+begin
+ SetupElement;
+ MyWidget.Refresh;
+ MyWidget.Visible:=False;
+ AssertFalse('Visible false',MyWidget.Visible);
+ AssertEquals('Display none','none',MyWidget.MyElement.Style.getPropertyValue('display'));
+end;
+
+procedure TTestWidgetBasicOperations.TestVisibleBeforeRenderPreserves;
+begin
+ MyWidget.Visible:=False;
+ SetupElement.style.setProperty('display','inline');
+ MyWidget.Refresh;
+ AssertFalse('Visible false',MyWidget.Visible);
+ AssertEquals('Display none','none',MyWidget.MyElement.Style.getPropertyValue('display'));
+ MyWidget.Visible:=True;
+ AssertTrue('Visible true',MyWidget.Visible);
+ AssertEquals('Display none','inline',MyWidget.MyElement.Style.getPropertyValue('display'));
+end;
+
+procedure TTestWidgetBasicOperations.TestVisibleAfterRenderPreserves;
+begin
+ SetupElement.style.setProperty('display','inline');
+ MyWidget.Refresh;
+ MyWidget.Visible:=False;
+ AssertFalse('Visible false',MyWidget.Visible);
+ AssertEquals('Display none','none',MyWidget.MyElement.Style.getPropertyValue('display'));
+ MyWidget.Visible:=True;
+ AssertTrue('Visible true',MyWidget.Visible);
+ AssertEquals('Display none','inline',MyWidget.MyElement.Style.getPropertyValue('display'));
+end;
+
+procedure TTestWidgetBasicOperations.TestGetData;
+begin
+ SetupElement.Dataset['name']:='me';
+ AssertEquals('Correctly read','me',MyWidget.Data['name']);
+end;
+
+procedure TTestWidgetBasicOperations.TestSetData;
+
+var
+ el : TJSHTMLElement;
+begin
+ El:=SetupElement;
+ MyWidget.Data['name']:='me';
+ AssertEquals('Correctly set','me',String(el.Dataset['name']));
+end;
+
+procedure TTestWidgetBasicOperations.TestGetDataNotRendered;
+begin
+ AssertEquals('Correctly read','',MyWidget.Data['name']);
+end;
+
+procedure TTestWidgetBasicOperations.TestSetDataNotRendered;
+begin
+ AssertException('Cannot write',EWidgets,@DoSetDataset);
+end;
+
+function TTestWidgetBasicOperations.SetupElement: TJSHTMLElement;
+
+begin
+ Result:=CreateMyElement(False);
+ MyWidget.ElementID:=SMyChildID;
+end;
+
+procedure TTestWidgetBasicOperations.SetupParentElement;
+begin
+ CreateParentElement;
+ MyParentWidget.ElementID:=SMyParentID;
+end;
+
+procedure TTestWidgetBasicOperations.TestEvent(aName : String);
+
+begin
+ TriggerEvent(aName);
+ AssertEvent(aName,Fmy);
+end;
+
+
+procedure TTestWidgetBasicOperations.TestEventClick;
+begin
+ SetupElement;
+ MyWidget.OnClick:=@MyTestEventHandler;
+ TestEvent('click');
+end;
+
+{ ---------------------------------------------------------------------
+ TBaseTestWidget
+ ---------------------------------------------------------------------}
+procedure TBaseTestWidget.SetUp;
+begin
+ inherited SetUp;
+ FBaseWindow:=GetElement(BaseID);
+end;
+
+procedure TBaseTestWidget.TearDown;
+begin
+ if Assigned(FBaseWindow) then
+ FBaseWindow.InnerHTML:='';
+ FBaseWindow:=Nil;
+ inherited TearDown;
+end;
+
+procedure TBaseTestWidget.AssertEvent(aName: String; aElement : TCustomWebWidget = Nil);
+begin
+ AssertEquals('Event handler called',1,FEventCount);
+ AssertSame('Event handler sender',aElement,FEventSender);
+ AssertEquals('Event name',aName,FEventName);
+end;
+
+procedure TBaseTestWidget.MyTestEventHandler(Sender: TObject; Event: TJSEvent);
+begin
+ Inc(FEventCount);
+ FEventSender:=Sender;
+ FEventName:=Event._type;
+end;
+
+function TBaseTestWidget.FindElement(aID: String): TJSHTMLElement;
+begin
+ Result:=TJSHTMLElement(Document.GetElementById(aID));
+end;
+
+function TBaseTestWidget.GetElement(aID: String): TJSHTMLElement;
+begin
+ Result:=FindElement(aID);
+ if Result=Nil then
+ Fail('No such element : '+aID);
+end;
+
+Function TBaseTestWidget.AssertTree(aParent: TJSHTmlElement; aTree: String) : TJSHTMLElement;
+
+Var
+ S,aTag,aID : String;
+ P : integer;
+ T : TJSHTMLElement;
+
+begin
+ P:=Pos('/',aTree);
+ if P=0 then
+ P:=Length(aTree)+1;
+ aTag:=Copy(aTree,1,P-1);
+ aTree:=Copy(aTree,P+1,Length(aTree)-P);
+ P:=Pos('(',aTag);
+ if P=0 then
+ P:=Length(aTag)+1;
+ aID:=Copy(aTag,P+1,Length(aTag)-P);
+ aTag:=Copy(aTag,1,P-1);
+ P:=Pos(')',aID);
+ if P>0 then
+ aID:=Copy(aID,1,P-1);
+ // Writeln('Look for <',aTag,'>(',aID,')');
+ T:=TJSHTMLElement(aParent.firstElementChild);
+ While (T<>Nil) and Not (SameText(T.tagName,aTag) and ((aID='') or SameText(T.ID,aID))) do
+ T:=TJSHTMLElement(T.nextElementSibling);
+ if (T=Nil) then
+ begin
+ S:='Could not find <'+aTag+'>';
+ if (aID<>'') then
+ S:=S+'(ID='+aID+')';
+ S:=S+'below element '+aParent.TagName;
+ if (aParent.ID<>'') then
+ S:=S+'(ID='+aParent.ID+')';
+ Fail(S);
+ end;
+ // Writeln('Found: <',T.TagName,'>(iD=',T.ID+')');
+ if aTree<>'' then
+ Result:=AssertTree(T,aTree)
+ else
+ Result:=T;
+end;
+
+Function TBaseTestWidget.AssertTree(aTree: String) : TJSHTMLElement;
+begin
+ Result:=AssertTree(BaseWindow,aTree)
+end;
+
+{ ---------------------------------------------------------------------
+ TTestWidgetBasicOperations
+ ---------------------------------------------------------------------}
+
+
+procedure TTestWidgetBasicOperations.TestEventOnAbort;
+
+begin
+ SetupElement;
+ MyWidget.OnAbort:=@MyTestEventHandler;
+ TestEvent('abort');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnAnimationCancel;
+
+begin
+ SetupElement;
+ MyWidget.OnAnimationCancel:=@MyTestEventHandler;
+ TestEvent('animationcancel');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnAnimationEnd;
+
+begin
+ SetupElement;
+ MyWidget.OnAnimationEnd:=@MyTestEventHandler;
+ TestEvent('animationend');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnAnimationIteration;
+
+begin
+ SetupElement;
+ MyWidget.OnAnimationIteration:=@MyTestEventHandler;
+ TestEvent('animationiteration');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnAnimationStart;
+
+begin
+ SetupElement;
+ MyWidget.OnAnimationStart:=@MyTestEventHandler;
+ TestEvent('animationstart');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnAuxClick;
+
+begin
+ SetupElement;
+ MyWidget.OnAuxClick:=@MyTestEventHandler;
+ TestEvent('auxclick');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnBlur;
+
+begin
+ SetupElement;
+ MyWidget.OnBlur:=@MyTestEventHandler;
+ TestEvent('blur');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnCancel;
+
+begin
+ SetupElement;
+ MyWidget.OnCancel:=@MyTestEventHandler;
+ TestEvent('cancel');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnCanPlay;
+
+begin
+ SetupElement;
+ MyWidget.OnCanPlay:=@MyTestEventHandler;
+ TestEvent('canplay');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnCanPlayThrough;
+
+begin
+ SetupElement;
+ MyWidget.OnCanPlayThrough:=@MyTestEventHandler;
+ TestEvent('canplaythrough');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnChange;
+
+begin
+ SetupElement;
+ MyWidget.OnChange:=@MyTestEventHandler;
+ TestEvent('change');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnClick;
+
+begin
+ SetupElement;
+ MyWidget.OnClick:=@MyTestEventHandler;
+ TestEvent('click');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnCompositionEnd;
+
+begin
+ SetupElement;
+ MyWidget.OnCompositionEnd:=@MyTestEventHandler;
+ TestEvent('compositionend');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnCompositionStart;
+
+begin
+ SetupElement;
+ MyWidget.OnCompositionStart:=@MyTestEventHandler;
+ TestEvent('compositionstart');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnCompositionUpdate;
+
+begin
+ SetupElement;
+ MyWidget.OnCompositionUpdate:=@MyTestEventHandler;
+ TestEvent('compositionupdate');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnContextMenu;
+
+begin
+ SetupElement;
+ MyWidget.OnContextMenu:=@MyTestEventHandler;
+ TestEvent('contextmenu');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnCopy;
+
+begin
+ SetupElement;
+ MyWidget.OnCopy:=@MyTestEventHandler;
+ TestEvent('copy');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnCut;
+
+begin
+ SetupElement;
+ MyWidget.OnCut:=@MyTestEventHandler;
+ TestEvent('cut');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnCueChange;
+
+begin
+ SetupElement;
+ MyWidget.OnCueChange:=@MyTestEventHandler;
+ TestEvent('cuechange');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnDblClick;
+
+begin
+ SetupElement;
+ MyWidget.OnDblClick:=@MyTestEventHandler;
+ TestEvent('dblclick');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnDurationChange;
+
+begin
+ SetupElement;
+ MyWidget.OnDurationChange:=@MyTestEventHandler;
+ TestEvent('durationchange');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnEnded;
+
+begin
+ SetupElement;
+ MyWidget.OnEnded :=@MyTestEventHandler;
+ TestEvent('ended');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnError;
+
+begin
+ SetupElement;
+ MyWidget.OnError :=@MyTestEventHandler;
+ TestEvent('error');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnFocus;
+
+begin
+ SetupElement;
+ MyWidget.OnFocus:=@MyTestEventHandler;
+ TestEvent('focus');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnFocusIn;
+
+begin
+ SetupElement;
+ MyWidget.OnFocusIn :=@MyTestEventHandler;
+ TestEvent('focusin');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnFocusOut;
+
+begin
+ SetupElement;
+ MyWidget.OnFocusOut :=@MyTestEventHandler;
+ TestEvent('focusout');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnGotPointerCapture;
+
+begin
+ SetupElement;
+ MyWidget.OnGotPointerCapture:=@MyTestEventHandler;
+ TestEvent('gotpointercapture');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnInput;
+
+begin
+ SetupElement;
+ MyWidget.OnInput:=@MyTestEventHandler;
+ TestEvent('input');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnInvalid;
+
+begin
+ SetupElement;
+ MyWidget.OnInvalid:=@MyTestEventHandler;
+ TestEvent('invalid');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnKeyDown;
+
+begin
+ SetupElement;
+ MyWidget.OnKeyDown:=@MyTestEventHandler;
+ TestEvent('keydown');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnKeyPress;
+
+begin
+ SetupElement;
+ MyWidget.OnKeyPress:=@MyTestEventHandler;
+ TestEvent('keypress');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnKeyUp;
+
+begin
+ SetupElement;
+ MyWidget.OnKeyUp:=@MyTestEventHandler;
+ TestEvent('keyup');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnLoad;
+
+begin
+ SetupElement;
+ MyWidget.OnLoad:=@MyTestEventHandler;
+ TestEvent('load');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnLoadedData;
+
+begin
+ SetupElement;
+ MyWidget.OnLoadedData:=@MyTestEventHandler;
+ TestEvent('loadeddata');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnLoadedMetaData;
+
+begin
+ SetupElement;
+ MyWidget.OnLoadedMetaData:=@MyTestEventHandler;
+ TestEvent('loadedmetadata');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnLoadend;
+
+begin
+ SetupElement;
+ MyWidget.OnLoadend:=@MyTestEventHandler;
+ TestEvent('loadend');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnLoadStart;
+
+begin
+ SetupElement;
+ MyWidget.OnLoadStart:=@MyTestEventHandler;
+ TestEvent('loadstart');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnLostPointerCapture;
+
+begin
+ SetupElement;
+ MyWidget.OnLostPointerCapture:=@MyTestEventHandler;
+ TestEvent('lostpointercapture');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnMouseDown;
+
+begin
+ SetupElement;
+ MyWidget.OnMouseDown:=@MyTestEventHandler;
+ TestEvent('mousedown');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnMouseEnter;
+
+begin
+ SetupElement;
+ MyWidget.OnMouseEnter:=@MyTestEventHandler;
+ TestEvent('mouseenter');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnMouseLeave;
+
+begin
+ SetupElement;
+ MyWidget.OnMouseLeave:=@MyTestEventHandler;
+ TestEvent('mouseleave');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnMouseMove;
+
+begin
+ SetupElement;
+ MyWidget.OnMouseMove:=@MyTestEventHandler;
+ TestEvent('mousemove');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnMouseOut;
+
+begin
+ SetupElement;
+ MyWidget.OnMouseOut:=@MyTestEventHandler;
+ TestEvent('mouseout');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnMouseUp;
+
+begin
+ SetupElement;
+ MyWidget.OnMouseUp:=@MyTestEventHandler;
+ TestEvent('mouseup');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnOverFlow;
+
+begin
+ SetupElement;
+ MyWidget.OnOverFlow:=@MyTestEventHandler;
+ TestEvent('overflow');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnPaste;
+
+begin
+ SetupElement;
+ MyWidget.OnPaste:=@MyTestEventHandler;
+ TestEvent('paste');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnPause;
+
+begin
+ SetupElement;
+ MyWidget.OnPause:=@MyTestEventHandler;
+ TestEvent('pause');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnPlay;
+
+begin
+ SetupElement;
+ MyWidget.OnPlay:=@MyTestEventHandler;
+ TestEvent('play');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnPointerCancel;
+
+begin
+ SetupElement;
+ MyWidget.OnPointerCancel:=@MyTestEventHandler;
+ TestEvent('pointercancel');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnPointerDown;
+
+begin
+ SetupElement;
+ MyWidget.OnPointerDown:=@MyTestEventHandler;
+ TestEvent('pointerdown');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnPointerEnter;
+
+begin
+ SetupElement;
+ MyWidget.OnPointerEnter:=@MyTestEventHandler;
+ TestEvent('pointerenter');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnPointerLeave;
+
+begin
+ SetupElement;
+ MyWidget.OnPointerLeave:=@MyTestEventHandler;
+ TestEvent('pointerleave');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnPointerMove;
+
+begin
+ SetupElement;
+ MyWidget.OnPointerMove:=@MyTestEventHandler;
+ TestEvent('pointermove');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnPointerOut;
+
+begin
+ SetupElement;
+ MyWidget.OnPointerOut:=@MyTestEventHandler;
+ TestEvent('pointerout');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnPointerOver;
+
+begin
+ SetupElement;
+ MyWidget.OnPointerOver:=@MyTestEventHandler;
+ TestEvent('pointerover');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnPointerUp;
+
+begin
+ SetupElement;
+ MyWidget.OnPointerUp:=@MyTestEventHandler;
+ TestEvent('pointerup');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnReset;
+
+begin
+ SetupElement;
+ MyWidget.OnReset:=@MyTestEventHandler;
+ TestEvent('reset');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnResize;
+
+begin
+ SetupElement;
+ MyWidget.OnResize:=@MyTestEventHandler;
+ TestEvent('resize');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnScroll;
+
+begin
+ SetupElement;
+ MyWidget.OnScroll:=@MyTestEventHandler;
+ TestEvent('scroll');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnSelect;
+
+begin
+ SetupElement;
+ MyWidget.OnSelect:=@MyTestEventHandler;
+ TestEvent('select');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnSubmit;
+
+begin
+ SetupElement;
+ MyWidget.OnSubmit:=@MyTestEventHandler;
+ TestEvent('submit');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnTouchStart;
+
+begin
+ SetupElement;
+ MyWidget.OnTouchStart:=@MyTestEventHandler;
+ TestEvent('touchstart');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnTransitionCancel;
+
+begin
+ SetupElement;
+ MyWidget.OnTransitionCancel:=@MyTestEventHandler;
+ TestEvent('transitioncancel');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnTransitionEnd;
+
+begin
+ SetupElement;
+ MyWidget.OnTransitionEnd:=@MyTestEventHandler;
+ TestEvent('transitionend');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnTransitionRun;
+
+begin
+ SetupElement;
+ MyWidget.OnTransitionRun:=@MyTestEventHandler;
+ TestEvent('transitionrun');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnTransitionStart;
+
+begin
+ SetupElement;
+ MyWidget.OnTransitionStart:=@MyTestEventHandler;
+ TestEvent('transitionstart');
+end;
+
+procedure TTestWidgetBasicOperations.TestEventOnWheel;
+
+begin
+ SetupElement;
+ MyWidget.OnWheel:=@MyTestEventHandler;
+ TestEvent('wheel');
+end;
+
+initialization
+ RegisterTests([TTestWidgetBasicOperations,TTestWebWidgetStyles]);
+end.
+
diff --git a/packages/webwidget/tests/testwidgets.html b/packages/webwidget/tests/testwidgets.html
new file mode 100644
index 0000000..d3ec484
--- /dev/null
+++ b/packages/webwidget/tests/testwidgets.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Test widgets
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/webwidget/tests/testwidgets.lpi b/packages/webwidget/tests/testwidgets.lpi
new file mode 100644
index 0000000..a350984
--- /dev/null
+++ b/packages/webwidget/tests/testwidgets.lpi
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/webwidget/tests/testwidgets.lpr b/packages/webwidget/tests/testwidgets.lpr
new file mode 100644
index 0000000..0ee110c
--- /dev/null
+++ b/packages/webwidget/tests/testwidgets.lpr
@@ -0,0 +1,16 @@
+program testwidgets;
+
+{$mode objfpc}
+
+uses
+ browserconsole, {browsertestrunner} consoletestrunner, JS, Classes, SysUtils, Web, btnrun, tcWidget, tchtmlwidgets;
+
+var
+ Application : TTestRunner;
+
+begin
+ Application:=TTestRunner.Create(nil);
+ Application.RunFormClass:=TConsoleRunner;
+ Application.Initialize;
+ Application.Run;
+end.
diff --git a/packages/webwidget/webwidget.pas b/packages/webwidget/webwidget.pas
new file mode 100644
index 0000000..314110f
--- /dev/null
+++ b/packages/webwidget/webwidget.pas
@@ -0,0 +1,1977 @@
+unit webwidget;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, JS, Web;
+
+Const
+
+ SElementData = 'wwElement';
+ STopElementData = SElementData+'Top';
+ SContentElementData = SElementData+'Content';
+ SElementClass = 'wwClass';
+
+ sEventAbort = 'abort';
+ SEventAnimationCancel = 'animationcancel';
+ SEventAnimationEnd = 'animationend';
+ SEventAnimationIteration = 'animationiteration';
+ SEventAnimationStart = 'animationstart';
+ sEventAuxClick = 'auxclick';
+ sEventBlur = 'blur';
+ SEventCancel = 'cancel';
+ SEventCanPlay = 'canplay';
+ SEventCanPlayThrough = 'canplaythrough';
+ SEventChange = 'change';
+ sEventClick = 'click';
+ sEventCompositionEnd = 'compositionend';
+ sEventCompositionStart = 'compositionstart';
+ sEventCompositionUpdate = 'compositionupdate';
+ sEventContextMenu = 'contextmenu';
+ sEventCopy = 'copy';
+ sEventCut = 'cut';
+ sEventCueChange = 'cuechange';
+ sEventDblClick = 'dblclick';
+ sEventDurationChange = 'durationchange';
+ sEventEnded = 'ended';
+ sEventError = 'error';
+ sEventFocus = 'focus';
+ sEventFocusIn = 'focusin';
+ sEventFocusOut = 'focusout';
+ SEventGotPointerCapture = 'gotpointercapture';
+ SEventInput = 'input';
+ SEventInvalid = 'invalid';
+ sEventKeyDown = 'keydown';
+ sEventKeyPress = 'keypress';
+ sEventKeyUp = 'keyup';
+ sEventLoad = 'load';
+ sEventLoadedData = 'loadeddata';
+ sEventLoadedMetaData = 'loadedmetadata';
+ sEventLoadend = 'loadend';
+ sEventLoadStart = 'loadstart';
+ SEventLostPointerCapture = 'lostpointercapture';
+ sEventMouseDown = 'mousedown';
+ sEventMouseEnter = 'mouseenter';
+ sEventMouseLeave = 'mouseleave';
+ sEventMouseMove = 'mousemove';
+ sEventMouseOut = 'mouseout';
+ sEventMouseUp = 'mouseup';
+ sEventOverFlow = 'overflow';
+ sEventPaste = 'paste';
+ sEventPause = 'pause';
+ sEventPlay = 'play';
+ SEventPointerCancel = 'pointercancel';
+ SEventPointerDown = 'pointerdown';
+ SEventPointerEnter = 'pointerenter';
+ SEventPointerLeave = 'pointerleave';
+ SEventPointerMove = 'pointermove';
+ SEventPointerOut = 'pointerout';
+ SEventPointerOver = 'pointerover';
+ SEventPointerUp = 'pointerup';
+ sEventReset = 'reset';
+ sEventResize = 'resize';
+ sEventScroll = 'scroll';
+ sEventSelect = 'select';
+ sEventSubmit = 'submit';
+ sEventTouchStart = 'touchstart';
+ SEventTransitionCancel = 'transitioncancel';
+ SEventTransitionEnd = 'transitionend';
+ SEventTransitionRun = 'transitionrun';
+ SEventTransitionStart = 'transitionstart';
+ SEventWheel = 'wheel';
+ SErrNotRendered = 'Cannot perform this operation: Widget not rendered';
+
+
+Type
+ EWidgets = Class(Exception);
+ TCustomWebWidget = Class;
+
+ THTMLNotifyEvent = Procedure (Sender : TObject; Event : TJSEvent) of object;
+
+ TEventDispatch = Record
+ MsgStr : String;
+ HTMLEvent : TJSEvent;
+ EventHandler : THTMLNotifyEvent;
+ end;
+
+ { TStyleItem }
+ TStylePriority = (spNone,spImportant);
+
+ TStyleItem = Class(TCollectionItem)
+ private
+ FPriority: TStylePriority;
+ FName: String;
+ FValue: String;
+ FImported : Boolean;
+ procedure SetPriority(AValue: TStylePriority);
+ procedure SetValue(AValue: String);
+ procedure SetName(AValue: String);
+ Protected
+ procedure MarkDirty;
+ Public
+ Property Imported : Boolean read FImported;
+ Procedure Assign(Source : TPersistent) ; override;
+ Published
+ Property Name : String Read FName Write SetName;
+ Property Value : String Read FValue Write SetValue;
+ Property Priority : TStylePriority Read FPriority Write SetPriority;
+ end;
+
+ { TWebWidgetStyles }
+
+ TWebWidgetStyles = Class(TOwnedCollection)
+ private
+ Function GetStyleItem(aIndex : Integer): TStyleItem;
+ procedure SetItem(aIndex : Integer; AValue: TStyleItem);
+ Protected
+ Procedure MarkDirty(aItem : TStyleItem);
+ Procedure ApplyToDOM(aElement : TJSHTMlElement;aItem : TStyleItem); virtual; overload;
+ Public
+ Function Widget : TCustomWebWidget;
+ // Manipulate
+ Function Add(Const aName : String; const aValue : String= '') : TStyleItem; overload;
+ Function EnsureStyle(Const aName : String; const aValue : String= '') : TStyleItem;
+ Function IndexOfStyle(Const aName : String) : integer;
+ Function FindStyle(Const aName : String) : TStyleItem;
+ Function GetStyle(Const aName : String) : TStyleItem;
+ Function RemoveStyle(Const aName : String) : String;
+ Procedure RefreshFromDOM(aElement : TJSHTMlElement = Nil;DoClear : Boolean = True);virtual;
+ Procedure ClearImported;
+ Procedure ApplyToDOM(aElement : TJSHTMlElement = Nil); virtual; overload;
+ Property Styles[aIndex : Integer] : TStyleItem Read GetStyleItem Write SetItem; default;
+ end;
+
+
+ TStyleRefresh = (srOnElementID, // Only refresh styles if ElementID was set and we bind to existing element.
+ srAlways, // Always refresh styles
+ srNever); // Never refresh
+ TStyleRefreshes = Set of TStyleRefresh;
+
+ { TReferenceItem }
+ TJSHTMLElementArray = Array of TJSHTMLElement;
+
+ TReferenceItem = Class(TCollectionItem)
+ private
+ FName: String;
+ FSelector: String;
+ procedure SetName(AValue: String);
+ procedure SetSelector(AValue: String);
+ function GetElement: TJSHTMLElement;
+ function GetElements: TJSHTMLElementArray;
+ Protected
+ Procedure MarkDirty; virtual;
+ Public
+ Procedure Refresh;
+ Property Element : TJSHTMLElement Read GetElement;
+ Property Elements : TJSHTMLElementArray Read GetElements;
+ Published
+ Property Selector : String Read FSelector Write SetSelector;
+ Property Name : String Read FName Write SetName;
+ end;
+
+ { TWebWidgetReferences }
+
+ TWebWidgetReferences = Class(TOwnedCollection)
+ Private
+ FRefs : TJSObject; // Arrays of elements, even for single element
+ function GetReferenceItem(aIndex : Integer): TReferenceItem;
+ procedure SetReferenceItem(aIndex : Integer; AValue: TReferenceItem);
+ Protected
+ Procedure MarkDirty(aItem : TReferenceItem);
+ Procedure RefreshFromDOM(aItem : TReferenceItem;aElement : TJSHTMlElement);
+ Property Refs : TJSObject Read FRefs;
+ Public
+ Function Widget : TCustomWebWidget;
+ // Manipulate
+ Function Add(Const aName : String; aSelector : String = '') : TReferenceItem; overload;
+ Function EnsureReference(Const aName : String) : TReferenceItem;
+ Function IndexOfReference(Const aName : String) : TReferenceItem;
+ Function FindReference(Const aName : String) : TReferenceItem;
+ Function GetReference(Const aName : String) : TReferenceItem;
+ Procedure RemoveReference(Const aName : String);
+ Function GetElementByName(Const aName : String) : TJSHTMLElement;
+ Function GetElementsByName(Const aName : String) : TJSHTMLElementArray;
+ Procedure RefreshFromDOM(aElement : TJSHTMlElement = Nil);virtual;
+ Property References[aIndex : Integer] : TReferenceItem Read GetReferenceItem Write SetReferenceItem; default;
+ end;
+
+{$DispatchStrField name}
+ { TCustomWebWidget }
+
+ TCustomWebWidget = Class(TComponent)
+ Private
+ const MaxEvents = 66;
+ Class Var WidgetID : NativeInt;
+ Const FEventNames : Array[0..MaxEvents] of String = (
+ // When adding, only add at the end !!
+ sEventAbort, //0
+ SEventAnimationCancel,
+ SEventAnimationEnd,
+ SEventAnimationIteration,
+ SEventAnimationStart,
+ sEventAuxClick ,
+ sEventBlur ,
+ SEventCancel ,
+ SEventCanPlay ,
+ SEventCanPlayThrough ,
+ SEventChange , // 10
+ sEventClick ,
+ sEventCompositionEnd ,
+ sEventCompositionStart ,
+ sEventCompositionUpdate ,
+ sEventContextMenu ,
+ sEventCopy ,
+ sEventCut ,
+ sEventCueChange ,
+ sEventDblClick ,
+ sEventDurationChange , // 20
+ sEventEnded ,
+ sEventError ,
+ sEventFocus ,
+ sEventFocusIn ,
+ sEventFocusOut ,
+ SEventGotPointerCapture ,
+ SEventInput ,
+ SEventInvalid ,
+ sEventKeyDown ,
+ sEventKeyPress , // 30
+ sEventKeyUp ,
+ sEventLoad ,
+ sEventLoadedData ,
+ sEventLoadedMetaData ,
+ sEventLoadend ,
+ sEventLoadStart ,
+ SEventLostPointerCapture ,
+ sEventMouseDown ,
+ sEventMouseEnter ,
+ sEventMouseLeave , // 40
+ sEventMouseMove ,
+ sEventMouseOut ,
+ sEventMouseUp ,
+ sEventOverFlow ,
+ sEventPaste ,
+ sEventPause ,
+ sEventPlay ,
+ SEventPointerCancel ,
+ SEventPointerDown ,
+ SEventPointerEnter ,
+ SEventPointerLeave ,
+ SEventPointerMove ,
+ SEventPointerOut ,
+ SEventPointerOver ,
+ SEventPointerUp ,
+ sEventReset ,
+ sEventResize ,
+ sEventScroll ,
+ sEventSelect ,
+ sEventSubmit ,
+ sEventTouchStart ,
+ SEventTransitionCancel ,
+ SEventTransitionEnd ,
+ SEventTransitionRun ,
+ SEventTransitionStart ,
+ SEventWheel
+ );
+ private
+ FAfterRenderHTML: TNotifyEvent;
+ FAfterUnRenderHTML: TNotifyEvent;
+ FBeforeRenderHTML: TNotifyEvent;
+ FBeforeUnRenderHTML: TNotifyEvent;
+ FParent : TCustomWebWidget;
+ FMyHook : TJSRawEventHandler;
+ // Set by setting ParentID or Parent
+ FParentElement : TJSHTMLElement;
+ FElement : TJSHTMLElement;
+ FOwnsElement : Boolean;
+ FParentID : String;
+ FElementID: String;
+ FChildren : TJSArray;
+ FClasses : String;
+ FMyEvents : TJSObject;
+ FStyleRefresh: TStyleRefresh;
+ FStyles: TWebWidgetStyles;
+ FVisible : Boolean;
+ FDisplay : String;
+ FReferences : TWebWidgetReferences;
+ function GetChildCount: Integer;
+ function GetChild(aIndex : Integer): TCustomWebWidget;
+ function GetClasses: String;
+ function GetDataset(aName : String): String;
+ function GetElement: TJSHTMLELement;
+ function GetExternalElement: Boolean;
+ function GetHaveReferences: Boolean;
+ function GetHTMLEvent(AIndex: Integer): THTMLNotifyEvent;
+ function GetIsElementDirty: Boolean;
+ function GetParent: TCustomWebWidget;
+ function GetParentElement: TJSHTMLELement;
+ function GetParentID: String;
+ function GetElementID: String;
+ function GetReferences: TWebWidgetReferences;
+ function GetRendered: Boolean;
+ function GetVisible: Boolean;
+ procedure SetClasses(AValue: String);
+ procedure SetDataset(aName : String; AValue: String);
+ procedure SetElementID(AValue: String);
+ procedure SetHTMLEvent(AIndex: Integer; AValue: THTMLNotifyEvent);
+ procedure SetParent(AValue: TCustomWebWidget);
+ procedure SetParentID(AValue: String);
+ Procedure AddChild(aValue : TCustomWebWidget);
+ Procedure RemoveChild(aValue : TCustomWebWidget);
+ procedure SetReferences(AValue: TWebWidgetReferences);
+ procedure SetStyles(AValue: TWebWidgetStyles);
+ procedure SetVisible(AValue: Boolean);
+ // This protected section is not meant to be made public
+ Protected
+ // Events mechanism
+ procedure EventEntry(aEvent: TJSEvent); virtual;
+ // Low level
+ procedure RemoveEvent(aElement: TJSHTMLElement; const aEvent: String);
+ procedure HookupEvent(aElement: TJSHTMLElement; const aEvent: String);
+ Procedure HookupEvents(aElement : TJSHTMLElement); virtual;
+ // Add to internal list, if rendered, calls hookup
+ Procedure AddEvent(aName : String; AHandler : THTMLNotifyEvent);
+ // Remove from internal list, if rendered, calls RemoveEvent
+ Procedure DeleteEvent(aName : String);
+ // Override these if you want somehow to grab a fixed element on a page.
+ Class Function FixedParent : TJSHTMLElement; virtual;
+ Class Function DefaultParentElement : TJSHTMLElement; virtual;
+ Class Function DefaultParent : TCustomWebWidget; virtual;
+ Class Function FixedElement : TJSHTMLElement; virtual;
+ // Generate an ID
+ Class function GenerateID: String;
+ // Find element in DOM tree.
+ Class Function FindElement(aID : String) : TJSHTMLElement;
+ // Create element in DOM tree, set ID if it is nonzero
+ Class function CreateElement (aTag : String; aID : String) : TJSHTMLElement;
+ // override if you want content to go in this sub-element instead of directly below the toplevel element.
+ function GetContentElement: TJSHTMLELement; virtual;
+ // Override this if Element is not the top element of this widget.
+ function GetTopElement: TJSHTMLELement; virtual;
+ // Auxiliary function to create a displayable name of this widget
+ Function DisplayElementName : String;
+ // Make sure there is an element.
+ function EnsureElement: TJSHTMLElement;
+ // Set parent element to nil.
+ Procedure InvalidateParentElement;
+ // Set element to nil, clears styles
+ Procedure InvalidateElement;
+ // Name of the tag to create. Set to '' if you don't want RenderHTML to create one.
+ Function HTMLTag : String; virtual; abstract;
+ // Class names that must always be applied for this widget to work.
+ // They are only added during render, and do not show up in classes property.
+ // e.g. for a bootstrap button widget, this would return "btn"
+ Function WidgetClasses : String; virtual;
+ // Override this if you want to create custom styles collection
+ Function CreateStyles : TWebWidgetStyles; virtual;
+ // Override this if you want to create custom references
+ Function CreateReferences : TWebWidgetReferences; virtual;
+ // Forces apply of visible, sets Visible property
+ procedure ApplyVisible(aElement : TJSHTMLElement; AValue: Boolean); virtual;
+ // Here all properties from the widget are applied to the element.
+ // This is called during RenderHTML, but also when binding ElementID to an Element.
+ Procedure ApplyWidgetSettings(aElement : TJSHTMLElement); virtual;
+ {
+ Actually render element.
+ This gets the element as created by RenderHTML and the parent as received by RenderHTML.
+ If aElement is nil, the DoRenderHTML is responsible for attaching it to the parent element.
+ Must return the value for Element.
+ }
+ Function DoRenderHTML(aParent,aElement : TJSHTMLElement) :TJSHTMLElement; virtual;
+ // Apply data to Element, Top and Content. Can only be called when the 3 are set, i.e. after RenderHTML or when Element is set from ElementID.
+ Procedure ApplyData; virtual;
+ Procedure RemoveData; virtual;
+ // Create html. Creates element below parent, and renders HTML using doRenderHTML
+ Function RenderHTML(aParent : TJSHTMLELement) : TJSHTMLElement;
+ Procedure DoUnRender(aParent : TJSHTMLElement) ; virtual;
+ // Remove HTML, if any. aParent can be nil.
+ Procedure UnRender(aParent : TJSHTMLElement);
+ // Dispatch an event
+ Function DispatchEvent(aName : String; aEvent : TJSEvent = Nil) : Boolean;
+ // the rendered or attached element if ElementID was set. Can be Nil;
+ // This is the "main" widget of the rendered HTML. There can be HTML below or HTML before.
+ Property Element : TJSHTMLELement Read GetElement;
+ // The attached parent element. Obtained through Parent or ParentID. Can be Nil;
+ // Not necessarily the parent element of Element, but definitely the parent of TopElement;
+ Property ParentElement : TJSHTMLELement Read GetParentElement;
+ // Content Element. By default equals Element.
+ Property ContentElement : TJSHTMLELement Read GetContentElement;
+ // Top Element. The parent element is the direct parent of this element.
+ Property TopElement : TJSHTMLELement Read GetTopElement;
+ // Is true if this class created the element (i.e. it was not obtained with ElementID)
+ Property OwnsElement : Boolean Read FOwnsElement;
+ // My Events
+ Property MyEvents : TJSObject Read FMyEvents;
+ // Return true if the ElementID is referring to an existing element.
+ Property ExternalElement : Boolean Read GetExternalElement;
+ // since reading references creates the collection, we want a way to see if there are any without creating them.
+ Property HaveReferences : Boolean Read GetHaveReferences;
+ Public
+ Constructor Create(aOwner : TComponent); override;
+ Destructor Destroy; override;
+ // Does this element allow childern ?
+ Class Function AllowChildren : Boolean; virtual;
+ // Manipulate Classes
+ Class Function RemoveClasses(const Source, aClasses : String; Normalize : Boolean = false) : String;
+ Class Function RemoveClasses(el : TJSHTMLElement; const aClasses : String; Normalize : Boolean = false) : String;
+ Class Function AddClasses(const Source, aClasses : String; Normalize : Boolean = false) : String;
+ Class Function AddClasses(el : TJSHTMLElement; const aClasses : String; Normalize : Boolean = false) : String;
+ // Manipulate styles
+ function EnsureStyle(const aName: String): TStyleItem;
+ function AddStyle(const aName,aValue: String): TStyleItem;
+ function GetStyleValue(const aName : String) : String;
+ function RemoveStyle(const aName: String): String;
+ // Remove data from dataset
+ Procedure RemoveData(const aName : String);
+ // Re-render
+ Procedure Refresh;
+ // These work on the classes property, and on the current element if rendered. Returns the new value of classes.
+ Function RemoveClasses(const aClasses : String; Normalize : Boolean = false) : String;
+ Function AddClasses(const aClasses : String; Normalize : Boolean = false) : String;
+ // Finding widgets
+ Function FindWidgetByID(aElementID : String; Recurse : Boolean = True) : TCustomWebWidget;
+ Function GetWidgetByID(aElementID : String; Recurse : Boolean = True) : TCustomWebWidget;
+
+ // For complex HTML, this is the toplevel element
+ Property Parent : TCustomWebWidget Read GetParent Write SetParent;
+ // Are we rendered, i.e. is Element valid ?
+ Property IsRendered : Boolean Read GetRendered;
+ // Do we need to refresh our internal properties from the element? Currently true if rendered.
+ // Use this when reading properties and you want/need to refresh a property from the element.
+ Property IsElementDirty : Boolean Read GetIsElementDirty;
+ // Child widgets. Note that this can differ significantly from
+ Property ChildCount : Integer Read GetChildCount;
+ Property Children [aIndex : Integer] : TCustomWebWidget Read GetChild;
+ Property Data[aName : String] : String Read GetDataset Write SetDataset;
+ // This works with style Display: none.
+ Property Visible : Boolean Read GetVisible Write SetVisible;
+ // This protected section can be published in descendents
+ Protected
+ // Parent or Element ID: Will be used when determining the HTML element when rendering.
+ // Only one of the Parent or Element ID can be set.
+ Property ParentID : String Read GetParentID Write SetParentID;
+ Property ElementID : String Read GetElementID Write SetElementID;
+ // When reading, returns the actual classes if rendered.
+ // When rendering, these classes are added to any existing ones if the element exists.
+ Property Classes : String Read GetClasses Write SetClasses;
+ // Apply these styles when rendering. Depending on StyleRefresh, styles are imported from actual element.
+ Property Styles: TWebWidgetStyles Read FStyles Write SetStyles;
+ // When rendering, should we refresh styles ?
+ Property StyleRefresh : TStyleRefresh Read FStyleRefresh Write FStyleRefresh;
+ // Possible references to sub widgets, based on CSS selectors
+ Property References : TWebWidgetReferences Read GetReferences write SetReferences;
+ // Events of TWebWidget
+ Property BeforeRenderHTML : TNotifyEvent Read FBeforeRenderHTML Write FBeforeRenderHTML;
+ Property AfterRenderHTML : TNotifyEvent Read FAfterRenderHTML Write FAfterRenderHTML;
+ Property BeforeUnRenderHTML : TNotifyEvent Read FBeforeUnRenderHTML Write FBeforeUnRenderHTML;
+ Property AfterUnRenderHTML : TNotifyEvent Read FAfterUnRenderHTML Write FAfterUnRenderHTML;
+ // HTML DOM events
+ Property OnAbort: THTMLNotifyEvent Index 0 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnAnimationCancel: THTMLNotifyEvent Index 1 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnAnimationEnd: THTMLNotifyEvent Index 2 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnAnimationIteration: THTMLNotifyEvent Index 3 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnAnimationStart: THTMLNotifyEvent Index 4 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnAuxClick : THTMLNotifyEvent Index 5 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnBlur : THTMLNotifyEvent Index 6 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnCancel : THTMLNotifyEvent Index 7 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnCanPlay : THTMLNotifyEvent Index 8 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnCanPlayThrough : THTMLNotifyEvent Index 9 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnChange : THTMLNotifyEvent Index 10 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnClick : THTMLNotifyEvent Index 11 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnCompositionEnd : THTMLNotifyEvent Index 12 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnCompositionStart : THTMLNotifyEvent Index 13 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnCompositionUpdate : THTMLNotifyEvent Index 14 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnContextMenu : THTMLNotifyEvent Index 15 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnCopy : THTMLNotifyEvent Index 16 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnCut : THTMLNotifyEvent Index 17 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnCueChange : THTMLNotifyEvent Index 18 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnDblClick : THTMLNotifyEvent Index 19 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnDurationChange : THTMLNotifyEvent Index 20 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnEnded : THTMLNotifyEvent Index 21 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnError : THTMLNotifyEvent Index 22 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnFocus : THTMLNotifyEvent Index 23 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnFocusIn : THTMLNotifyEvent Index 24 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnFocusOut : THTMLNotifyEvent Index 25 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnGotPointerCapture : THTMLNotifyEvent Index 26 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnInput : THTMLNotifyEvent Index 27 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnInvalid : THTMLNotifyEvent Index 28 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnKeyDown : THTMLNotifyEvent Index 29 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnKeyPress : THTMLNotifyEvent Index 30 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnKeyUp : THTMLNotifyEvent Index 31 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnLoad : THTMLNotifyEvent Index 32 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnLoadedData : THTMLNotifyEvent Index 33 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnLoadedMetaData : THTMLNotifyEvent Index 34 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnLoadend : THTMLNotifyEvent Index 35 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnLoadStart : THTMLNotifyEvent Index 36 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnLostPointerCapture : THTMLNotifyEvent Index 37 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnMouseDown : THTMLNotifyEvent Index 38 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnMouseEnter : THTMLNotifyEvent Index 39 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnMouseLeave : THTMLNotifyEvent Index 40 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnMouseMove : THTMLNotifyEvent Index 41 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnMouseOut : THTMLNotifyEvent Index 42 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnMouseUp : THTMLNotifyEvent Index 43 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnOverFlow : THTMLNotifyEvent Index 44 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnPaste : THTMLNotifyEvent Index 45 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnPause : THTMLNotifyEvent Index 46 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnPlay : THTMLNotifyEvent Index 47 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnPointerCancel : THTMLNotifyEvent Index 48 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnPointerDown : THTMLNotifyEvent Index 49 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnPointerEnter : THTMLNotifyEvent Index 50 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnPointerLeave : THTMLNotifyEvent Index 51 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnPointerMove : THTMLNotifyEvent Index 52 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnPointerOut : THTMLNotifyEvent Index 53 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnPointerOver : THTMLNotifyEvent Index 54 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnPointerUp : THTMLNotifyEvent Index 55 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnReset : THTMLNotifyEvent Index 56 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnResize : THTMLNotifyEvent Index 57 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnScroll : THTMLNotifyEvent Index 58 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnSelect : THTMLNotifyEvent Index 59 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnSubmit : THTMLNotifyEvent Index 60 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnTouchStart : THTMLNotifyEvent Index 61 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnTransitionCancel : THTMLNotifyEvent Index 62 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnTransitionEnd : THTMLNotifyEvent Index 63 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnTransitionRun : THTMLNotifyEvent Index 64 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnTransitionStart : THTMLNotifyEvent Index 65 Read GetHTMLEvent Write SetHTMLEvent;
+ Property OnWheel : THTMLNotifyEvent Index 66 Read GetHTMLEvent Write SetHTMLEvent;
+ end;
+ TCustomWebWidgetClass = Class of TCustomWebWidget;
+
+ { TWebWidget }
+
+ TWebWidget = Class(TCustomWebWidget)
+ Published
+ // Properties
+ Property ParentID;
+ Property ElementID;
+ Property Classes;
+ Property Styles;
+ Property StyleRefresh;
+ Property Visible;
+ // Events
+ Property BeforeRenderHTML;
+ Property AfterRenderHTML;
+ Property OnAbort;
+ Property OnAnimationCancel;
+ Property OnAnimationEnd;
+ Property OnAnimationIteration;
+ Property OnAnimationStart;
+ Property OnAuxClick;
+ Property OnBlur;
+ Property OnCancel;
+ Property OnCanPlay;
+ Property OnCanPlayThrough;
+ Property OnChange;
+ Property OnClick;
+ Property OnCompositionEnd;
+ Property OnCompositionStart;
+ Property OnCompositionUpdate;
+ Property OnContextMenu;
+ Property OnCopy;
+ Property OnCut;
+ Property OnCueChange;
+ Property OnDblClick;
+ Property OnDurationChange;
+ Property OnEnded ;
+ Property OnError ;
+ Property OnFocus;
+ Property OnFocusIn ;
+ Property OnFocusOut ;
+ Property OnGotPointerCapture;
+ Property OnInput;
+ Property OnInvalid;
+ Property OnKeyDown;
+ Property OnKeyPress;
+ Property OnKeyUp;
+ Property OnLoad;
+ Property OnLoadedData;
+ Property OnLoadedMetaData;
+ Property OnLoadend;
+ Property OnLoadStart;
+ Property OnLostPointerCapture;
+ Property OnMouseDown;
+ Property OnMouseEnter;
+ Property OnMouseLeave;
+ Property OnMouseMove;
+ Property OnMouseOut;
+ Property OnMouseUp;
+ Property OnOverFlow;
+ Property OnPaste;
+ Property OnPause;
+ Property OnPlay;
+ Property OnPointerCancel;
+ Property OnPointerDown;
+ Property OnPointerEnter;
+ Property OnPointerLeave;
+ Property OnPointerMove;
+ Property OnPointerOut;
+ Property OnPointerOver;
+ Property OnPointerUp;
+ Property OnReset;
+ Property OnResize;
+ Property OnScroll;
+ Property OnSelect;
+ Property OnSubmit;
+ Property OnTouchStart;
+ Property OnTransitionCancel;
+ Property OnTransitionEnd;
+ Property OnTransitionRun;
+ Property OnTransitionStart;
+ Property OnWheel;
+ end;
+ TWebWidgetClass = Class of TWebWidget;
+
+ { TContainerWidget }
+
+ TContainerWidget = Class(TWebWidget)
+ private
+ const KnownStyleCount = 6;
+ Const KnownStyles : Array[0..KnownStyleCount] of string = ('min-width','max-width','min-height','max-height','display','width','height');
+ function GetKnownStyle(AIndex: Integer): String;
+ procedure SetKnownStyle(AIndex: Integer; AValue: String);
+ Public
+ Function HTMLTag : String; override;
+ Constructor Create(aOwner : TComponent); override;
+ Property MinWidth : String Index 0 Read GetKnownStyle Write SetKnownStyle;
+ Property MaxWidth : String Index 1 Read GetKnownStyle Write SetKnownStyle;
+ Property MinHeight : String Index 2 Read GetKnownStyle Write SetKnownStyle;
+ Property MaxHeight : String Index 3 Read GetKnownStyle Write SetKnownStyle;
+ Property Display : String Index 4 Read GetKnownStyle Write SetKnownStyle;
+ Property Width : String Index 5 Read GetKnownStyle Write SetKnownStyle;
+ Property Height : String Index 6 Read GetKnownStyle Write SetKnownStyle;
+ end;
+
+ { TCustomTemplateWidget }
+
+ TCustomTemplateWidget = Class(TWebWidget)
+ private
+ FContainerTag: String;
+ FTemplate: TStrings;
+ procedure DoTemplateChanged(Sender: TObject);
+ procedure SetContainerTag(AValue: String);
+ procedure SetTemplate(AValue: TStrings);
+ Protected
+ function GetTemplateHTML: String; virtual;
+ Procedure ApplyTemplate(aElement : TJSHTMLElement); virtual;
+ Function DoRenderHTML(aParent, aElement: TJSHTMLElement) : TJSHTMLElement; override;
+ // The template.
+ Property Template : TStrings Read FTemplate Write SetTemplate;
+ // When set, a tag will be created and the template will be rendered below this tag.
+ Property ContainerTag : String Read FContainerTag Write SetContainerTag;
+ Public
+ Function HTMLTag : String; override;
+ Constructor Create(aOwner : TComponent); override;
+ Destructor Destroy; override;
+ end;
+
+implementation
+
+ResourceString
+ SErrCannotSetParentAndElementID = 'ElementID and ParentID cannot be set at the same time.';
+ SErrCannotRenderWithoutParent = 'Cannot render without parent';
+ SErrInvalidChildIndex = 'Invalid child index: value %d is not in valid range [0..%d]';
+ SErrUnknownStyle = 'Unknown style: %s';
+ SErrElementIDNotAllowed = 'Setting element ID is not allowed';
+ SErrParentIDNotAllowed = 'Setting parent ID is not allowed';
+ SErrParentNotAllowed = 'Setting parent is not allowed';
+ SErrWidgetNotFound = 'Widget with ID "%s" not found.';
+
+{ TWebWidgetReferences }
+
+function TWebWidgetReferences.GetReferenceItem(aIndex : Integer): TReferenceItem;
+begin
+
+end;
+
+procedure TWebWidgetReferences.SetReferenceItem(aIndex : Integer; AValue: TReferenceItem);
+begin
+
+end;
+
+procedure TWebWidgetReferences.MarkDirty(aItem: TReferenceItem);
+begin
+ if Assigned(Widget) and Assigned(Widget.Element) then
+ RefreshFromDOM(aItem,Widget.Element)
+end;
+
+procedure TWebWidgetReferences.RefreshFromDOM(aItem: TReferenceItem; aElement: TJSHTMlElement);
+begin
+
+end;
+
+function TWebWidgetReferences.Widget: TCustomWebWidget;
+begin
+ Result:=TCustomWebWidget(owner);
+end;
+
+function TWebWidgetReferences.Add(const aName: String; aSelector: String): TReferenceItem;
+begin
+ Result:=Add as TReferenceItem;
+ Result.FName:=aName;
+ Result.Selector:=aSelector;
+ if (aSelector<>'') then
+ MarkDirty(Result)
+end;
+
+function TWebWidgetReferences.EnsureReference(const aName: String): TReferenceItem;
+begin
+
+end;
+
+function TWebWidgetReferences.IndexOfReference(const aName: String): TReferenceItem;
+begin
+
+end;
+
+function TWebWidgetReferences.FindReference(const aName: String): TReferenceItem;
+begin
+
+end;
+
+function TWebWidgetReferences.GetReference(const aName: String): TReferenceItem;
+begin
+
+end;
+
+procedure TWebWidgetReferences.RemoveReference(const aName: String);
+begin
+
+end;
+
+function TWebWidgetReferences.GetElementByName(const aName: String): TJSHTMLElement;
+begin
+
+end;
+
+function TWebWidgetReferences.GetElementsByName(const aName: String): TJSHTMLElementArray;
+begin
+
+end;
+
+procedure TWebWidgetReferences.RefreshFromDOM(aElement: TJSHTMlElement);
+begin
+
+end;
+
+{ TReferenceItem }
+
+procedure TReferenceItem.SetName(AValue: String);
+begin
+ if FName=AValue then Exit;
+ FName:=AValue;
+ MarkDirty;
+end;
+
+function TReferenceItem.GetElement: TJSHTMLElement;
+begin
+ if Assigned(Collection) then
+ Result:=(Collection as TWebWidgetReferences).GetElementByName(Name)
+ else
+ Result:=Nil;
+end;
+
+function TReferenceItem.GetElements: TJSHTMLElementArray;
+begin
+ if Assigned(Collection) then
+ Result:=(Collection as TWebWidgetReferences).GetElementsByName(Name)
+ else
+ Result:=Nil;
+end;
+
+procedure TReferenceItem.MarkDirty;
+begin
+ if Assigned(Collection) then
+ (Collection as TWebWidgetReferences).MarkDirty(Self);
+end;
+
+procedure TReferenceItem.Refresh;
+begin
+ MarkDirty;
+end;
+
+procedure TReferenceItem.SetSelector(AValue: String);
+begin
+ if FSelector=AValue then Exit;
+ FSelector:=AValue;
+ MarkDirty;
+end;
+
+{ TCustomTemplateWidget }
+
+procedure TCustomTemplateWidget.SetTemplate(AValue: TStrings);
+begin
+ if FTemplate=AValue then Exit;
+ FTemplate.Assign(AValue);
+end;
+
+function TCustomTemplateWidget.GetTemplateHTML: String;
+begin
+ Result:=FTemplate.Text;
+end;
+
+procedure TCustomTemplateWidget.ApplyTemplate(aElement: TJSHTMLElement);
+begin
+ aElement.InnerHTML:=GetTemplateHTML;
+end;
+
+function TCustomTemplateWidget.DoRenderHTML(aParent, aElement: TJSHTMLElement): TJSHTMLElement;
+begin
+ if (ContainerTag='') then
+ begin
+ ApplyTemplate(AParent);
+ Result:= TJSHTMLElement(aParent.firstElementChild);
+ end
+ else
+ begin
+ ApplyTemplate(aElement);
+ Result:=aElement;
+ end;
+end;
+
+function TCustomTemplateWidget.HTMLTag: String;
+begin
+ Result:=ContainerTag;
+end;
+
+procedure TCustomTemplateWidget.DoTemplateChanged(Sender: TObject);
+begin
+ if isRendered then
+ Refresh;
+end;
+
+procedure TCustomTemplateWidget.SetContainerTag(AValue: String);
+begin
+ if FContainerTag=AValue then Exit;
+ FContainerTag:=AValue;
+ if IsRendered then
+ Refresh;
+end;
+
+constructor TCustomTemplateWidget.Create(aOwner: TComponent);
+
+begin
+ inherited Create(aOwner);
+ FTemplate:=TStringList.Create;
+ TStringList(FTemplate).OnChange:=@DoTemplateChanged;
+ FContainerTag:='';
+end;
+
+destructor TCustomTemplateWidget.Destroy;
+
+begin
+ FreeAndNil(FTemplate);
+ inherited Destroy;
+end;
+
+{ TContainerWidget }
+
+function TContainerWidget.GetKnownStyle(AIndex: Integer): String;
+
+var
+ S : TStyleItem;
+begin
+ S:=Styles.FindStyle(KnownStyles[aIndex]);
+ if Assigned(S) then
+ Result:=S.Value;
+end;
+
+procedure TContainerWidget.SetKnownStyle(AIndex: Integer; AValue: String);
+
+begin
+ Styles.EnsureStyle(KnownStyles[aIndex]).Value:=aValue;
+end;
+
+function TContainerWidget.HTMLTag: String;
+begin
+ Result:='div';
+end;
+
+constructor TContainerWidget.Create(aOwner: TComponent);
+begin
+ inherited Create(aOwner);
+ MinWidth:='32px';
+ MinHeight:='12px';
+ Width:='50%';
+ Height:='50%';
+end;
+
+
+
+{ TWebWidgetStyles }
+
+function TWebWidgetStyles.GetStyleItem(aIndex: Integer): TStyleItem;
+begin
+ Result:=TStyleItem(Items[Aindex]);
+end;
+
+procedure TWebWidgetStyles.SetItem(aIndex : Integer; AValue: TStyleItem);
+begin
+ Items[aIndex]:=aValue;
+end;
+
+procedure TWebWidgetStyles.MarkDirty(aItem: TStyleItem);
+
+Var
+ El : TJSHTMLElement;
+
+begin
+ If Assigned(Widget) then
+ begin
+ el:=Widget.Element;
+ if Assigned(El) then
+ ApplyToDom(El,aItem);
+ end;
+end;
+
+procedure TWebWidgetStyles.ApplyToDOM(aElement: TJSHTMlElement; aItem: TStyleItem);
+
+Const
+ Prios : Array[TStylePriority] of string = ('','important');
+
+begin
+ With AItem do
+ if (Name<>'') then
+ if (Value<>'') then
+ aElement.Style.setProperty(Name,Value,Prios[Priority])
+ else
+ aElement.Style.removeProperty(name);
+end;
+
+function TWebWidgetStyles.Add(const aName: String; const aValue: String): TStyleItem;
+begin
+ Result:=Add as TStyleItem;
+ // Don't use Name
+ Result.FName:=aName;
+ if aValue<>'' then
+ Result.Value:=aValue; // will trigger markdirty
+end;
+
+function TWebWidgetStyles.EnsureStyle(const aName: String; const aValue: String): TStyleItem;
+begin
+ Result:=FindStyle(aName);
+ if Result=Nil then
+ Result:=Add(aName,aValue)
+ else if AValue<>'' then
+ Result.Value:=aValue
+end;
+
+function TWebWidgetStyles.Widget: TCustomWebWidget;
+begin
+ Result:=TCustomWebWidget(Owner);
+end;
+
+function TWebWidgetStyles.IndexOfStyle(const aName: String): integer;
+
+begin
+ Result:=Count-1;
+ While (Result>=0) and not SameText(aName,GetStyleItem(Result).Name) do
+ Dec(Result);
+end;
+
+function TWebWidgetStyles.FindStyle(const aName: String): TStyleItem;
+
+Var
+ Idx : integer;
+
+begin
+ Idx:=IndexOfStyle(aName);
+ If Idx=-1 then
+ Result:=Nil
+ else
+ Result:=GetStyleItem(Idx)
+end;
+
+function TWebWidgetStyles.GetStyle(const aName: String): TStyleItem;
+begin
+ Result:=FindStyle(aName);
+ if Result=Nil then
+ Raise EWidgets.CreateFmt(SErrUnknownStyle,[aName]);
+end;
+
+function TWebWidgetStyles.RemoveStyle(const aName: String): String;
+
+Var
+ I : Integer;
+ el : TJSHTMLElement;
+
+begin
+ I:=IndexOfStyle(aName);
+ if I<>-1 then
+ begin
+ Result:=Styles[i].Value;
+ Delete(I);
+ end;
+ if Assigned(Widget) then
+ begin
+ el:=Widget.Element;
+ if Assigned(el) then
+ begin
+ if (Result='') then
+ Result:=el.style.getPropertyValue(aName);
+ el.style.removeProperty(aName);
+ end;
+ end;
+end;
+
+procedure TWebWidgetStyles.RefreshFromDOM(aElement : TJSHTMlElement = Nil;DoClear: Boolean = False);
+
+Var
+ S : TJSCSSStyleDeclaration;
+ I : integer;
+ K : String;
+ W : TCustomWebWidget;
+ itm : TStyleItem;
+
+begin
+ if aElement= Nil then
+ begin
+ W:=Widget;
+ if assigned(W) then
+ aElement:=W.Element;
+ if AElement=Nil then exit;
+ end;
+ if DoClear then
+ Clear;
+ S:=aElement.style;
+ For I:=0 to S.length-1 do
+ begin
+ K:=S.Item(I);
+ itm:=FindStyle(K);
+ if Itm=Nil then
+ begin
+ Itm:=Add(K);
+ Itm.FImported:=True;
+ end;
+ Itm.FValue:=S.getPropertyValue(K);
+ Case LowerCase(S.getPropertyPriority(K)) of
+ 'important' : Itm.FPriority:=spImportant;
+ end;
+ end;
+end;
+
+procedure TWebWidgetStyles.ClearImported;
+
+Var
+ I : integer;
+
+begin
+ I:=Count-1;
+ While I>=0 do
+ begin
+ If GetStyleItem(I).Fimported then
+ Delete(I);
+ Dec(I);
+ end;
+end;
+
+procedure TWebWidgetStyles.ApplyToDOM(aElement : TJSHTMlElement = Nil);
+
+Var
+ I : Integer;
+
+begin
+ if (AElement=Nil) and (Widget<>Nil) then
+ aElement:=Widget.Element;
+ if AElement<>Nil then
+ For I:=0 to Count-1 do
+ ApplyToDOM(aElement,GetStyleItem(i));
+end;
+
+{ TStyleItem }
+
+procedure TStyleItem.MarkDirty;
+
+begin
+ If Assigned(Collection) then
+ TWebWidgetStyles(Collection).MarkDirty(Self);
+end;
+
+procedure TStyleItem.SetValue(AValue: String);
+begin
+ if FValue=AValue then Exit;
+ FValue:=aValue;
+ MarkDirty;
+end;
+
+procedure TStyleItem.SetPriority(AValue: TStylePriority);
+begin
+ if FPriority=AValue then Exit;
+ FPriority:=AValue;
+ MarkDirty;
+end;
+
+
+procedure TStyleItem.SetName(AValue: String);
+begin
+ if aValue=FName then Exit;
+ FName:=AValue;
+ MarkDirty;
+end;
+
+procedure TStyleItem.Assign(Source: TPersistent);
+
+Var
+ SI : TStyleItem;
+
+begin
+ if Source is TStyleItem then
+ begin
+ SI:=Source as TStyleItem;
+ FName:=SI.FName;
+ FValue:=SI.FValue;
+ FImported:=SI.FImported;
+ MarkDirty;
+ end
+ else
+ inherited Assign(Source);
+end;
+
+
+{ TCustomWebWidget }
+
+function TCustomWebWidget.DisplayElementName: String;
+
+begin
+ Result:=Name;
+ If Result='' then
+ Result:=' <'+HTMLTag+'>';
+ if Assigned(FElement) then
+ Result:=Result+'#'+FElement.ID;
+ Result:=Result+' (Type: '+ClassName+')';
+end;
+
+function TCustomWebWidget.EnsureElement : TJSHTMLElement;
+
+var
+ P : TJSHTMLElement;
+
+begin
+ Result:=GetElement;
+ if Result=Nil then
+ begin
+ // If we have a parent, make sure it has it's element
+ if Assigned(Parent) then
+ Parent.EnsureElement;
+ P:=ParentElement;
+ if (P=Nil) and (FixedElement=Nil) then
+ Raise EWidgets.CreateFmt(SErrCannotRenderWithoutParent,[DisplayElementName])
+ else
+ begin
+ Result:=RenderHTML(P);
+ FOwnsElement:=True;
+ FElement:=Result;
+ ApplyData;
+ end;
+ end;
+end;
+
+procedure TCustomWebWidget.InvalidateParentElement;
+begin
+ FParentElement:=nil;
+end;
+
+procedure TCustomWebWidget.InvalidateElement;
+begin
+ If FStyles.Count>0 then
+ FStyles.ClearImported;
+ FElement:=nil;
+end;
+
+function TCustomWebWidget.WidgetClasses: String;
+begin
+ Result:='';
+end;
+
+
+function TCustomWebWidget.GetElement: TJSHTMLELement;
+
+Var
+ El : TJSHTMLElement;
+
+
+begin
+ if (FElement=Nil) then
+ begin
+ if (FElementID<>'') then
+ begin
+ El:=FindElement(FElementID);
+ if Assigned(El) then
+ ApplyWidgetSettings(el);
+ FElement:=El;
+ ApplyData;
+ end;
+ end;
+ Result:=FElement;
+end;
+
+function TCustomWebWidget.GetExternalElement: Boolean;
+begin
+ Result:=(FElementID<>'')
+end;
+
+function TCustomWebWidget.GetHaveReferences: Boolean;
+begin
+ Result:=Assigned(FReferences);
+end;
+
+function TCustomWebWidget.GetHTMLEvent(AIndex: Integer): THTMLNotifyEvent;
+
+Var
+ Fun : JSValue;
+begin
+ Result:=nil;
+ if Assigned(FMyEvents) and (aIndex>=0) and (aIndex<=MaxEvents) then
+ begin
+ Fun:=FMyEvents[FEventNames[aindex]];
+ if Not isUndefined(Fun) then
+ Result:=THTMLNotifyEvent(Fun);
+ end;
+end;
+
+function TCustomWebWidget.GetIsElementDirty: Boolean;
+begin
+ Result:=IsRendered;
+end;
+
+function TCustomWebWidget.GetClasses: String;
+begin
+ if IsRendered Then
+ FClasses:=FElement.ClassName;
+ Result:=FClasses;
+end;
+
+function TCustomWebWidget.GetDataset(aName : String): String;
+
+Var
+ el : TJSHTMLElement;
+
+begin
+ el:=Element;
+ if Assigned(El) then
+ Result:=String(El.Dataset[aName])
+ else
+ Result:='';
+end;
+
+function TCustomWebWidget.GetChildCount: Integer;
+begin
+ Result:=FChildren.Length;
+end;
+
+function TCustomWebWidget.GetChild(aIndex : Integer): TCustomWebWidget;
+begin
+ if (aIndex<0) or (aIndex>=FChildren.Length) then
+ Raise EListError.CreateFmt(SErrInvalidChildIndex,[aIndex,FChildren.Length-1]);
+ Result:=TCustomWebWidget(FChildren[aIndex]);
+end;
+
+function TCustomWebWidget.GetContentElement: TJSHTMLELement;
+begin
+ Result:=Element;
+end;
+
+function TCustomWebWidget.GetParent: TCustomWebWidget;
+begin
+ Result:=FParent;
+end;
+
+function TCustomWebWidget.GetParentElement: TJSHTMLELement;
+
+Var
+ El : TJSHTMLElement;
+
+begin
+ if (FParentElement=Nil) then
+ begin
+ El:=TopElement;
+ if Assigned(el) then
+ FParentElement:=TJSHTMLElement(el.parentElement)
+ else if (FParentID<>'') then
+ FParentElement:=FindElement(FParentID)
+ else if Assigned(FParent) then
+ FParentElement:=FParent.ContentElement
+ else
+ FParentElement:=DefaultParentElement;
+ end;
+ Result:=FParentElement;
+end;
+
+function TCustomWebWidget.GetParentID: String;
+
+Var
+ E : TJSHTMLElement;
+
+begin
+ Result:='';
+ E:=ParentElement;
+ if Assigned(E) then
+ Result:=E.ID
+ else
+ Result:=FParentID;
+end;
+
+function TCustomWebWidget.GetElementID: String;
+
+Var
+ El : TJSHTMLElement;
+
+begin
+ El:=Element;
+ If Assigned(El) then
+ Result:=el.ID
+ else
+ Result:=FElementID;
+end;
+
+function TCustomWebWidget.GetReferences: TWebWidgetReferences;
+begin
+ if (FReferences=Nil) then
+ FReferences:=CreateReferences;
+ Result:=FReferences;
+
+end;
+
+function TCustomWebWidget.GetRendered: Boolean;
+begin
+ Result:=(FElement<>Nil)
+end;
+
+function TCustomWebWidget.GetTopElement: TJSHTMLELement;
+begin
+ Result:=Element;
+end;
+
+function TCustomWebWidget.GetVisible: Boolean;
+begin
+ Result:=FVisible;
+end;
+
+procedure TCustomWebWidget.SetClasses(AValue: String);
+begin
+ FClasses:=AddClasses(AValue,WidgetClasses);
+ If IsRendered then
+ FElement.ClassName:=FClasses;
+end;
+
+procedure TCustomWebWidget.SetDataset(aName : String; AValue: String);
+
+Var
+ El : TJSHTMLElement;
+
+begin
+ el:=Element;
+ If (El=Nil) then
+ Raise EWidgets.Create(SErrNotRendered);
+ el.Dataset[aName]:=aValue;
+end;
+
+procedure TCustomWebWidget.SetElementID(AValue: String);
+begin
+ if (FElementID=AValue) then Exit;
+ if (aValue<>'') then
+ begin
+ if (FParentID<>'') then
+ Raise EWidgets.Create(SErrCannotSetParentAndElementID);
+ if FixedElement<>Nil then
+ Raise EWidgets.Create(SErrElementIDNotAllowed);
+ FElementID:=AValue;
+ end
+ else
+ begin
+ FElementID:=AValue;
+ if IsRendered then
+ Unrender(ParentElement);
+ end;
+end;
+
+procedure TCustomWebWidget.SetHTMLEvent(AIndex: Integer; AValue: THTMLNotifyEvent);
+
+Var
+ EventName : String;
+
+begin
+ if (aIndex<0) or (aIndex>MaxEvents) then
+ exit;
+ EventName:=FEventNames[aIndex];
+ if Assigned(aValue) then
+ AddEvent(EventName,AValue)
+ else
+ DeleteEvent(EventName);
+end;
+
+procedure TCustomWebWidget.SetParent(AValue: TCustomWebWidget);
+
+Var
+ ReRender : Boolean;
+begin
+ if (AValue=FParent) then exit;
+ if (FixedParent<>Nil) then
+ Raise EWidgets.Create(SErrParentNotAllowed);
+ If Assigned(FParent) then
+ FParent.RemoveChild(Self);
+ // Unrender
+ ReRender:=IsRendered;
+ if ReRender then
+ UnRender(ParentElement);
+ InvalidateParentElement;
+ If Assigned(aValue) then
+ begin
+ FParentID:='';
+ aValue.AddChild(Self); // Sets FParent
+ end;
+ if ReRender and Assigned(ParentElement) then
+ begin
+ FElement:=RenderHTML(ParentElement);
+ if Assigned(FElement) then
+ ApplyData;
+ end;
+end;
+
+procedure TCustomWebWidget.SetParentID(AValue: String);
+
+Var
+ ReRender : Boolean;
+
+begin
+ if (FParentID=AValue) then exit;
+ if (aValue<>'') then
+ begin
+ if (FElementID<>'') then
+ Raise EWidgets.Create(SErrCannotSetParentAndElementID);
+ if (FixedParent<>Nil) then
+ Raise EWidgets.Create(SErrParentIDNotAllowed);
+ end;
+ ReRender:=IsRendered;
+ if ReRender then
+ UnRender(ParentElement);
+ if (aValue<>'') and Assigned(FParent) then
+ FParent.RemoveChild(Self);
+ FParentID:=aValue;
+ InvalidateParentElement;
+ if ReRender and Assigned(ParentElement) then
+ EnsureElement;
+end;
+
+procedure TCustomWebWidget.AddChild(aValue: TCustomWebWidget);
+begin
+ if AValue=Nil then exit;
+ aValue.FParent:=Self;
+ if FChildren.IndexOf(aValue)=-1 then
+ FChildren.Push(aValue);
+end;
+
+procedure TCustomWebWidget.RemoveChild(aValue: TCustomWebWidget);
+
+Var
+ I : NativeInt;
+
+begin
+ if AValue=Nil then exit;
+ I:=FChildren.indexOf(aValue);
+ if I>=0 then
+ begin
+ FChildren.splice(I,1);
+ aValue.FParent:=Nil;
+ end;
+end;
+
+procedure TCustomWebWidget.SetReferences(AValue: TWebWidgetReferences);
+begin
+ if (aValue=FReferences) then exit;
+ References.Assign(aValue);
+ if IsRendered then
+ References.RefreshFromDOM(FElement);
+end;
+
+procedure TCustomWebWidget.SetStyles(AValue: TWebWidgetStyles);
+begin
+ if FStyles=AValue then Exit;
+ FStyles.Assign(AValue);
+end;
+
+procedure TCustomWebWidget.SetVisible(AValue: Boolean);
+
+Var
+ el : TJSHTMLElement;
+
+begin
+ if aValue=FVisible then
+ Exit;
+ el:=Element;
+ if Assigned(el) then
+ ApplyVisible(el,aValue)
+ else
+ FVisible:=aValue;
+end;
+
+procedure TCustomWebWidget.ApplyVisible(aElement: TJSHTMLElement;AValue: Boolean);
+
+begin
+ if aValue then
+ begin
+ if (FDisplay<>'') then
+ aElement.Style.setProperty('display',FDisplay)
+ else
+ aElement.Style.removeProperty('display');
+ end
+ else
+ begin
+ FDisplay:=aElement.Style.getPropertyValue('display');
+ aElement.Style.setProperty('display','none');
+ end;
+ FVisible:=aValue;
+end;
+
+procedure TCustomWebWidget.EventEntry(aEvent: TJSEvent);
+
+Var
+ R : TEventDispatch;
+ Fun : JSValue;
+
+begin
+ R.MsgStr:=aEvent._type;
+ R.HTMLEvent:=aEvent;
+ if Assigned(FMyEvents) then
+ Fun:=FMyEvents[R.MsgStr]
+ else
+ Fun:=nil;
+ if Not (isUndefined(Fun) or isNull(Fun)) then
+ R.EventHandler:=THTMLNotifyEvent(Fun);
+ DispatchStr(R);
+ if (R.EventHandler<>Nil) then
+ R.EventHandler(Self,R.HTMLEvent);
+end;
+
+function TCustomWebWidget.CreateStyles: TWebWidgetStyles;
+begin
+ Result:=TWebWidgetStyles.Create(Self,TStyleItem);
+end;
+
+function TCustomWebWidget.CreateReferences: TWebWidgetReferences;
+begin
+ Result:=TWebWidgetReferences.Create(Self,TReferenceItem);
+end;
+
+procedure TCustomWebWidget.RemoveEvent(aElement: TJSHTMLElement; const aEvent: String);
+begin
+ aElement.RemoveEventListener(aEvent,FMyHook);
+end;
+
+procedure TCustomWebWidget.HookupEvent(aElement: TJSHTMLElement; const aEvent : String);
+
+begin
+ aElement.addEventListener(aEvent,FMyHook);
+end;
+
+procedure TCustomWebWidget.HookupEvents(aElement: TJSHTMLElement);
+
+Var
+ Event : String;
+
+begin
+ if Assigned(FMyEvents) then
+ for Event in TJSObject.keys(FMyEvents) do
+ HookupEvent(aElement,Event);
+end;
+
+procedure TCustomWebWidget.AddEvent(aName: String; AHandler: THTMLNotifyEvent);
+
+Var
+ el : TJSHTMLElement;
+
+begin
+ if FMyEvents=nil then
+ FMyEvents:=TJSObject.New;
+ FMyEvents[aName]:=aHandler;
+ El:=Element;
+ if Assigned(El) then
+ HookupEvent(el,aName);
+end;
+
+procedure TCustomWebWidget.DeleteEvent(aName: String);
+
+Var
+ el : TJSHTMLElement;
+
+begin
+ if (FMyEvents<>nil) and FMyEvents.hasOwnProperty(aName) then
+ JSDelete(FMyEvents,aName);
+ El:=Element;
+ if Assigned(El) then
+ RemoveEvent(el,aName);
+end;
+
+class function TCustomWebWidget.FixedParent: TJSHTMLElement;
+begin
+ Result:=Nil;
+end;
+
+class function TCustomWebWidget.DefaultParentElement: TJSHTMLElement;
+begin
+ Result:=Nil;
+end;
+
+class function TCustomWebWidget.DefaultParent: TCustomWebWidget;
+begin
+ Result:=nil;
+end;
+
+class function TCustomWebWidget.FixedElement: TJSHTMLElement;
+begin
+ Result:=Nil;
+end;
+
+
+
+class function TCustomWebWidget.FindElement(aID: String): TJSHTMLElement;
+begin
+ Result:=TJSHTMLElement(Document.getElementbyID(aID));
+end;
+
+class function TCustomWebWidget.CreateElement(aTag: String; aID: String
+ ): TJSHTMLElement;
+begin
+ Result:=TJSHTMLElement(Document.createElement(aTag));
+ if aID<>'' then
+ Result.id:=aID;
+end;
+
+procedure TCustomWebWidget.Refresh;
+
+Var
+ P : TJSHTMLElement;
+
+begin
+ P:=Nil;
+ if Assigned(FElement) then
+ begin
+ P:=ParentElement;
+ if Assigned(P) and (FElementID='') then // Do not remove when it's not ours to begin with
+ P.removeChild(FElement);
+ end;
+ InvalidateParentElement;
+ InvalidateElement;
+ EnsureElement;
+end;
+
+procedure TCustomWebWidget.ApplyWidgetSettings(aElement: TJSHTMLElement);
+
+// Normally, this should be called BEFORE FElement is set.
+// But we'll be extra careful, and not rely on getters using FElement.
+
+Var
+ S : String;
+
+begin
+ if aElement.id='' then
+ aElement.id:=GenerateID;
+ // Don't use Classes, it will return FElement.Classname when set
+ S:=AddClasses(FClasses,WidgetClasses);
+ if (S<>'') then
+ AddClasses(aElement,S);
+ if FStyles.Count>0 then
+ FStyles.ApplyToDOM(aElement);
+ if Not FVisible then
+ ApplyVisible(aElement,FVisible);
+ // Maybe we should put this under control of a property or so ?
+ // TStyleRefresh = (srAlways,srOnElementID,srNever)
+ if (StyleRefresh = srAlways)
+ or ((FelementID<>'') and (FElementID<>'')) then
+ FStyles.RefreshFromDom(aElement,False);
+end;
+
+function TCustomWebWidget.DoRenderHTML(aParent, aElement: TJSHTMLElement): TJSHTMLElement;
+begin
+ If AParent=Nil then
+ Console.Log(DisplayElementName+': render without parent!');
+ Result:=aElement;
+end;
+
+
+procedure TCustomWebWidget.ApplyData;
+
+Var
+ AID : String;
+
+ Procedure MaybeSet(El : TJSHTMLElement; AName : String);
+
+ begin
+ if Assigned(el) then
+ el.Dataset[aName]:=AID;
+ end;
+
+begin
+ AID:=ElementID;
+ if assigned(Element) then
+ Element.dataset[SElementClass]:=ClassName;
+ MaybeSet(Element,SElementData);
+ MaybeSet(TopElement,STopElementData);
+ if AllowChildren then
+ MaybeSet(ContentElement,SContentElementData);
+end;
+
+procedure TCustomWebWidget.RemoveData;
+
+ Procedure MaybeUnSet(El : TJSHTMLElement; AName : String);
+
+ begin
+ if Assigned(el) then
+ jsDelete(el.Dataset,aName);
+ end;
+
+begin
+ MaybeUnSet(Element,SElementData);
+ MaybeUnSet(TopElement,STopElementData);
+ MaybeUnSet(ContentElement,SContentElementData);
+end;
+
+class function TCustomWebWidget.GenerateID: String;
+
+begin
+ Inc(WidgetID);
+ Result:='ww-'+intToStr(WidgetID);
+end;
+
+function TCustomWebWidget.RenderHTML(aParent: TJSHTMLELement): TJSHTMLElement;
+
+Var
+ aTag : String;
+
+begin
+ aTag:=HTMLTag;
+ if aTag='' then
+ Result:=Nil
+ else
+ begin
+ Result:=FixedElement;
+ if Result=Nil then
+ Result:=CreateElement(HTMLTag,GenerateID);
+ end;
+ if Assigned(Result) and Assigned(aParent) then
+ aParent.AppendChild(Result);
+ if Assigned(FBeforeRenderHTML) then
+ FBeforeRenderHTML(Self);
+ Result:=DoRenderHTML(aParent,Result);
+ if Assigned(Result) then
+ begin
+ ApplyWidgetSettings(Result);
+ HookupEvents(Result);
+ end;
+ if Assigned(FAfterRenderHTML) then
+ FAfterRenderHTML(Self);
+end;
+
+procedure TCustomWebWidget.DoUnRender(aParent: TJSHTMLElement);
+
+begin
+ if Assigned(aParent) and Assigned(FElement) then
+ begin
+ if FOwnsElement then
+ aParent.removeChild(FElement);
+ FElement:=Nil
+ end;
+end;
+
+procedure TCustomWebWidget.UnRender(aParent: TJSHTMLElement);
+begin
+ if Assigned(FBeforeUnRenderHTML) then
+ FBeforeUnRenderHTML(Self);
+ RemoveData;
+ DoUnRender(aParent);
+ if Assigned(FAfterUnRenderHTML) then
+ FAfterUnRenderHTML(Self);
+end;
+
+function TCustomWebWidget.DispatchEvent(aName: String; aEvent: TJSEvent): Boolean;
+
+begin
+ if not IsRendered then
+ exit;
+ if (aEvent=Nil) then
+ aEvent:=TJSEvent.New(aName);
+ Result:=Element.dispatchEvent(aEvent);
+end;
+
+constructor TCustomWebWidget.Create(aOwner: TComponent);
+begin
+ inherited Create(aOwner);
+ FChildren:=TJSArray.New;
+ FStyles:=CreateStyles;
+ FMyHook:=@EventEntry;
+ FParent:=DefaultParent;
+ FVisible:=True;
+end;
+
+destructor TCustomWebWidget.Destroy;
+
+Var
+ I : integer;
+ C : TCustomWebWidget;
+
+begin
+ For I:=0 to FChildren.Length-1 do
+ begin
+ C:=TCustomWebWidget(FChildren[i]);
+ FChildren[i]:=Nil;
+ C.Free;
+ end;
+ Parent:=Nil;
+ ParentID:='';
+ FChildren:=Nil;
+ FreeAndNil(FStyles);
+ inherited Destroy;
+end;
+
+class function TCustomWebWidget.AllowChildren: Boolean;
+begin
+ Result:=True;
+end;
+
+class function TCustomWebWidget.RemoveClasses(const Source, aClasses: String; Normalize : Boolean = false): String;
+
+var
+ T : TJSStringDynArray;
+ i : integer;
+ S : String;
+begin
+ Result:=Source;
+ if Normalize then
+ Result:=TJSString(Result).replace(TJSRegexp.New('\s\s+','g'),' ');
+ T:=TJSString(Result).split(' ');
+ For S in TJSString(aClasses).split(' ') do
+ if (S<>'') then
+ begin
+ I:=TJSArray(T).indexOf(S);
+ if (I<>-1) then
+ TJSArray(T).splice(i,1);
+ end;
+ Result:=TJSArray(T).join(' ');
+end;
+
+class function TCustomWebWidget.RemoveClasses(el: TJSHTMLElement; const aClasses: String; Normalize : Boolean = false): String;
+
+begin
+ Result:=RemoveClasses(el.ClassName,aClasses,Normalize);
+ el.ClassName:=Result;
+end;
+
+class function TCustomWebWidget.AddClasses(const Source, aClasses: String; Normalize : Boolean = false): String;
+
+var
+ T : TJSStringDynArray;
+ S : String;
+
+begin
+ Result:=Source;
+ if Normalize then
+ Result:=TJSString(Result).replace(TJSRegexp.New('\s\s+','g'),' ');
+ if AClasses='' then exit;
+ T:=TJSString(Result).split(' ');
+ For S in TJSString(aClasses).split(' ') do
+ if (S<>'') then
+ begin
+ if (TJSArray(T).indexOf(S)=-1) then
+ TJSArray(T).Push(S);
+ end;
+ Result:=TJSArray(T).Join(' ');
+end;
+
+class function TCustomWebWidget.AddClasses(el: TJSHTMLElement; const aClasses: String; Normalize : Boolean = false): String;
+
+begin
+ Result:=AddClasses(el.ClassName,aClasses,Normalize);
+ el.ClassName:=Trim(Result);
+end;
+
+function TCustomWebWidget.RemoveClasses(const aClasses: String; Normalize : Boolean = false): String;
+begin
+ Result:=RemoveClasses(FClasses,aClasses,Normalize);
+ if IsRendered then
+ Result:=RemoveClasses(FElement,aClasses,Normalize)
+end;
+
+function TCustomWebWidget.AddClasses(const aClasses: String; Normalize: Boolean): String;
+begin
+ Result:=AddClasses(FClasses,aClasses,Normalize);
+ if IsRendered then
+ Result:=AddClasses(FElement,aClasses,Normalize)
+end;
+
+function TCustomWebWidget.FindWidgetByID(aElementID: String; Recurse: Boolean): TCustomWebWidget;
+
+Var
+ I : Integer;
+
+begin
+ Result:=Nil;
+ if aElementID='' then
+ exit;
+ if (aElementID=elementID) then
+ Exit(Self);
+ I:=ChildCount-1;
+ // First this level. Typical layout is not so nested.
+ While (i>=0) and (Result=Nil) do
+ begin
+ Result:=Children[i];
+ if (Result.ElementID<>aElementID) then
+ Result:=nil;
+ Dec(I);
+ end;
+ If (Result=Nil) and (Recurse) then
+ begin
+ I:=ChildCount-1;
+ While (i>=0) and (Result=Nil) do
+ begin
+ Result:=Children[i].FindWidgetByID(aElementID,True);
+ Dec(I);
+ end;
+ end;
+end;
+
+function TCustomWebWidget.GetWidgetByID(aElementID: String; Recurse: Boolean): TCustomWebWidget;
+begin
+ Result:=FindWidgetByID(aElementID,Recurse);
+ if Result=Nil then
+ Raise EWidgets.CreateFmt(SErrWidgetNotFound,[aElementID]);
+end;
+
+function TCustomWebWidget.EnsureStyle(const aName: String): TStyleItem;
+begin
+ Result:=Styles.EnsureStyle(aName);
+end;
+
+function TCustomWebWidget.AddStyle(const aName, aValue: String): TStyleItem;
+begin
+ Result:=EnsureStyle(aName);
+ Result.Value:=aValue;
+end;
+
+function TCustomWebWidget.GetStyleValue(const aName : String): String;
+
+Var
+ S : TStyleItem;
+
+begin
+ S:=Styles.FindStyle(aName);
+ if Assigned(S) then
+ Result:=S.Value
+ else
+ Result:='';
+end;
+
+function TCustomWebWidget.RemoveStyle(const aName: String): String;
+
+begin
+ Result:=Styles.RemoveStyle(aName);
+end;
+
+procedure TCustomWebWidget.RemoveData(const aName: String);
+begin
+ if IsRendered then
+ jsDelete(Element.Dataset,aName)
+end;
+
+
+
+end.
+