123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- {
- This file is part of the Pas2JS run time library.
- Copyright (c) 2019 by Michael Van Canneyt
- This unit implements a HTML template loader.
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- unit Rtl.TemplateLoader;
- {$mode objfpc}
- interface
- uses
- Classes, SysUtils, JS, web;
- Type
- TFailData = Record
- message : string;
- code : Integer;
- end;
- TTemplateNotifyEvent = Reference to Procedure(Sender : TObject; Const aTemplate : String);
- TTemplateErrorNotifyEvent = Reference to Procedure(Sender : TObject; Const aTemplate,aError : String; aErrorcode : Integer);
- { TCustomTemplateLoader }
- TCustomTemplateLoader = Class(TComponent)
- Private
- FBaseURL: String;
- FOnLoad: TTemplateNotifyEvent;
- FOnLoadFail: TTemplateErrorNotifyEvent;
- FTemplates : TJSObject;
- function GetTemplate(aName : String): String;
- procedure SetTemplate(aName : String; AValue: String);
- Protected
- // Process an url before it is used to fetch data
- Function ProcessURL(const aURL : String) : String;
- Public
- Constructor Create (aOwner : TComponent); override;
- Destructor Destroy; override;
- // Remove a template
- Procedure RemoveRemplate(aName : String);
- // fetch a template using promise. Promise resolves to template name. On fail a TFailData record is passed on.
- // Note that the global OnLoad/OnLoadFail a
- Function FetchTemplate(Const aName,aURL : String) : TJSPromise;
- // procedural API.
- // If the aOnSuccess aOnFail event handlers are specified, they're called as well in addition to global handlers.
- Procedure LoadTemplate(Const aName,aURL : String; aOnSuccess : TTemplateNotifyEvent = Nil; AOnFail : TTemplateErrorNotifyEvent= Nil);
- // procedural API for multiple templates at once.
- // Form = name, URL, name URL.
- // If the aOnSuccess aOnFail event handlers are specified, they're called as well in addition to global handlers.
- Procedure LoadTemplates(Const Templates : Array of String; aOnSuccess : TTemplateNotifyEvent = Nil; AOnFail : TTemplateErrorNotifyEvent= nil);
- // URLs will be relative to this. Take care that you add a / at the end if needed !
- Property BaseURL : String Read FBaseURL Write FBaseURl;
- Property Templates[aName : String] : String Read GetTemplate Write SetTemplate; default;
- // Called when a template was loaded.
- Property OnLoad : TTemplateNotifyEvent Read FOnLoad Write FOnLoad;
- // Called when a template failed to load.
- Property OnLoadFail : TTemplateErrorNotifyEvent Read FOnLoadFail Write FOnLoadFail;
- end;
- TTemplateLoader = Class(TCustomTemplateLoader)
- Published
- Property BaseURL;
- Property OnLoad;
- Property OnLoadFail;
- end;
- // Global instance, for ease of use.
- Function GlobalTemplates : TCustomTemplateLoader;
- implementation
- { TCustomTemplateLoader }
- Var
- _loader : TCustomTemplateLoader;
- Function GlobalTemplates : TCustomTemplateLoader;
- begin
- if _loader=Nil then
- _loader:=TCustomTemplateLoader.Create(Nil);
- Result:=_Loader;
- end;
- Type
- { TURLLoader }
- TURLLoader = Class(TObject)
- private
- FLoader: TCustomTemplateLoader;
- FName: String;
- FURL: String;
- procedure dofetch(resolve, reject: TJSPromiseResolver);
- Public
- Constructor Create(aLoader : TCustomTemplateLoader; aName,aURL : String);
- Function fetch : TJSPromise;
- Property Name : String Read FName;
- Property URL : String Read FURL;
- Property Loader : TCustomTemplateLoader Read FLoader;
- end;
- { TURLLoader }
- constructor TURLLoader.Create(aLoader: TCustomTemplateLoader; aName, aURL: String);
- begin
- FLoader:=aLoader;
- FURL:=aURL;
- FName:=aName;
- end;
- procedure TURLLoader.dofetch(resolve,reject : TJSPromiseResolver);
- function doOK(response : JSValue) : JSValue;
- var
- Res : TJSResponse absolute response;
- F : TFailData;
- begin
- If (Res.status<>200) then
- begin
- F.Message:=res.StatusText;
- F.Code:=Res.Status;
- Result:=Reject(F);
- end
- else
- Res.text._then(
- function (value : JSValue) : JSValue
- begin
- Loader.Templates[FName]:=String(Value);
- if Assigned(Loader.FonLoad) then
- Loader.FOnLoad(FLoader,Name);
- Result:=Resolve(Name);
- end
- );
- end;
- function doFail(response : JSValue) : JSValue;
- Var
- F : TFailData;
- begin
- F.message:='unknown error';
- F.code:=999;
- Result:=Reject(F);
- end;
- begin
- Window.Fetch(URl)._then(@DoOK).catch(@DoFail);
- end;
- function TURLLoader.fetch : TJSPromise;
- begin
- Result:=TJSPromise.New(@Dofetch)
- end;
- function TCustomTemplateLoader.GetTemplate(aName : String): String;
- Var
- V : jsValue;
- begin
- V:=FTemplates[LowerCase(aName)];
- if isString(V) then
- Result:=String(V)
- else
- Result:='';
- end;
- procedure TCustomTemplateLoader.SetTemplate(aName : String; AValue: String);
- begin
- FTemplates[LowerCase(aName)]:=AValue;
- end;
- function TCustomTemplateLoader.ProcessURL(const aURL: String): String;
- Var
- R : TJSRegexp;
- begin
- R:=TJSRegexp.New('^https?://|^/','i');
- if R.Test(aURL) then
- Result:=aURL
- else
- Result:=BaseURL+aURL;
- end;
- constructor TCustomTemplateLoader.Create(aOwner: TComponent);
- begin
- inherited Create(aOwner);
- FTemplates:=TJSObject.New;
- end;
- destructor TCustomTemplateLoader.Destroy;
- begin
- FTemplates:=nil;
- inherited Destroy;
- end;
- procedure TCustomTemplateLoader.RemoveRemplate(aName: String);
- begin
- jsDelete(FTemplates,Lowercase(aName));
- end;
- function TCustomTemplateLoader.FetchTemplate(const aName, aURL: String): TJSPromise;
- begin
- Result:=TURLLoader.Create(Self,aName,ProcessURL(aURL)).fetch;
- end;
- procedure TCustomTemplateLoader.LoadTemplate(const aName, aURL: String; aOnSuccess: TTemplateNotifyEvent;
- AOnFail: TTemplateErrorNotifyEvent);
- function doOK(aValue : JSValue) : JSValue;
- begin
- if Assigned(aOnSuccess) then
- aOnSuccess(Self,aName);
- Result:=nil;
- end;
- function doFail(aValue : JSValue) : JSValue;
- Var
- F : TFailData absolute aValue;
- S : String;
- C : Integer;
- begin
- S:=F.message;
- C:=F.Code;
- if Assigned(FonLoadFail) then
- FOnLoadFail(Self,aName,S,C);
- if Assigned(aOnFail) then
- aOnFail(Self,aName,S,C);
- Result:=nil;
- end;
- begin
- FetchTemplate(aName,aURL)._then(@DoOK).catch(@doFail);
- end;
- procedure TCustomTemplateLoader.LoadTemplates(const Templates: array of String; aOnSuccess: TTemplateNotifyEvent;
- AOnFail: TTemplateErrorNotifyEvent);
- Var
- I,L : Integer;
- begin
- L:=Length(Templates);
- if (L mod 2)<>0 then
- Raise Exception.CreateFmt('Number of arguments (%d) must be even',[L]);
- I:=0;
- While I<L do
- begin
- LoadTemplate(Templates[I],Templates[I+1],aOnsuccess,aOnFail);
- Inc(I,2);
- end;
- end;
- end.
|