mirror of
https://gitlab.com/freepascal.org/fpc/pas2js.git
synced 2025-05-02 22:13:37 +02:00
303 lines
6.7 KiB
ObjectPascal
303 lines
6.7 KiB
ObjectPascal
unit hotreloadclient;
|
|
|
|
{$mode objfpc}
|
|
|
|
interface
|
|
|
|
uses sysutils, types, js, web;
|
|
|
|
Type
|
|
|
|
{ THotReloadOptions }
|
|
|
|
THotReloadOptions = Class
|
|
private
|
|
FLog: Boolean;
|
|
FName: string;
|
|
FOverlayElementID: String;
|
|
FPath: String;
|
|
FPollInterval: Cardinal;
|
|
FReload: Boolean;
|
|
FTimeOut: Cardinal;
|
|
FWarn: Boolean;
|
|
Public
|
|
Constructor Create; reintroduce;
|
|
Property Path : String Read FPath Write FPath;
|
|
Property Timeout : Cardinal Read FTimeOut Write FTimeOut;
|
|
Property PollInterval : Cardinal Read FPollInterval Write FPollInterval;
|
|
Property Reload : Boolean Read FReload Write FReload;
|
|
Property Log : Boolean Read FLog Write FLog;
|
|
Property Warn : Boolean Read FWarn Write FWarn;
|
|
property Name : string Read FName write FName;
|
|
Property OverlayElementID : String Read FOverlayElementID Write FOverlayElementID;
|
|
end;
|
|
|
|
THotReload = Class
|
|
private
|
|
FOptions : THotReloadOptions;
|
|
FLastReq : TJSXMLHttpRequest;
|
|
function doAbort(Event: TEventListenerEvent): boolean;
|
|
function doStatus(Event: TEventListenerEvent): boolean;
|
|
function GetLineColor(aLine: String): String;
|
|
function GetLineStyle(aLine: String): String;
|
|
procedure HandleMessage(aData: TJSObject);
|
|
procedure Reload;
|
|
procedure ShowLogOverlay(O: TStringDynArray);
|
|
Protected
|
|
class var Global : THotReload;
|
|
procedure OnTick; virtual;
|
|
public
|
|
Constructor Create; reintroduce;
|
|
Constructor Create(AOptions : THotReloadOptions);
|
|
Class Procedure StartHotReload;
|
|
Class Procedure StopHotReload;
|
|
class function getGlobal : THotReload;
|
|
procedure Initialize;
|
|
Property Options : THotReloadOptions Read FOptions;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{ THotReload }
|
|
|
|
constructor THotReload.Create;
|
|
begin
|
|
Create(THotReloadOptions.Create);
|
|
end;
|
|
|
|
constructor THotReload.Create(AOptions: THotReloadOptions);
|
|
begin
|
|
FOptions:=AOptions;
|
|
Initialize;
|
|
end;
|
|
|
|
class procedure THotReload.StartHotReload;
|
|
begin
|
|
Global:=THotReload.Create;
|
|
end;
|
|
|
|
class procedure THotReload.StopHotReload;
|
|
begin
|
|
FreeAndNil(Global);
|
|
end;
|
|
|
|
class function THotReload.getGlobal: THotReload;
|
|
begin
|
|
result:=Global;
|
|
end;
|
|
|
|
function THotReload.doAbort(Event: TEventListenerEvent): boolean;
|
|
|
|
begin
|
|
if Event=nil then ;
|
|
if Options.log then
|
|
console.warn('Status request aborted');
|
|
FLastReq:=Nil;
|
|
Result:=false;
|
|
end;
|
|
|
|
Procedure THotReload.Reload;
|
|
|
|
begin
|
|
window.location.reload(true);
|
|
end;
|
|
|
|
function THotReload.GetLineColor(aLine : String) : String;
|
|
|
|
var
|
|
P : Integer;
|
|
|
|
begin
|
|
P:=Pos(':',ALine);
|
|
if (P>0) then
|
|
begin
|
|
Aline:=Copy(ALine,1,P-1);
|
|
P:=Pos(' ',ALine);
|
|
if P>0 then
|
|
ALine:=Copy(ALine,P+1,Length(Aline)-P);
|
|
end;
|
|
case lowercase(aline) of
|
|
'error': Result:='E36049';
|
|
'note': Result:='B3CB74';
|
|
'warning': Result:='FFD080';
|
|
'hint': Result:='7CAFC2';
|
|
'info': Result:='7FACCA';
|
|
'fatal': Result:='E36049';
|
|
else
|
|
Result:='EBE7E3';
|
|
end;
|
|
end;
|
|
|
|
function THotReload.GetLineStyle(aLine : String) : String;
|
|
|
|
Var
|
|
Color : String;
|
|
|
|
begin
|
|
color:=getLineColor(aLine);
|
|
Result:='background-color:#' + color + '; color:#fff; padding:2px 4px; border-radius: 2px';
|
|
end;
|
|
|
|
Procedure THotReload.ShowLogOverlay(O : TStringDynArray);
|
|
Const
|
|
DefaultStyle =
|
|
'background: rgba(0,0,0,0.85);'+
|
|
'color: #E8E8E8;'+
|
|
'lineHeight: 1.2;'+
|
|
'fontFamily: Menlo, Consolas, monospace;'+
|
|
'fontSize: 13px;'+
|
|
'left: 0;'+
|
|
'right: 0;'+
|
|
'top: 0;'+
|
|
'bottom: 0;'+
|
|
'dir: ltr;'+
|
|
'textAlign: left';
|
|
|
|
Var
|
|
D,SP,MN : TJSElement;
|
|
N : TJSNode;
|
|
I : Integer;
|
|
begin
|
|
Writeln('Searching for '+Options.OverlayElementID);
|
|
D:=document.getElementById(Options.OverlayElementID);
|
|
if D=Nil then
|
|
exit;
|
|
// Clear
|
|
N:=D.firstElementChild;
|
|
While (N<>Nil) do
|
|
begin
|
|
D.removeChild(N);
|
|
N:=D.firstElementChild;
|
|
end;
|
|
D['style']:=DefaultStyle;
|
|
For I:=0 to Length(O)-1 do
|
|
begin
|
|
MN:=document.createElement('div');
|
|
SP:=Document.createElement('span');
|
|
SP['style']:=GetLineStyle(O[i]);
|
|
SP.appendChild(document.createTextNode(O[i]));
|
|
MN.appendChild(SP);
|
|
D.appendChild(MN);
|
|
end;
|
|
end;
|
|
|
|
Procedure THotReload.HandleMessage(aData : TJSObject);
|
|
|
|
Var
|
|
a : JSValue;
|
|
O : TStringDynArray;
|
|
I : integer;
|
|
|
|
begin
|
|
if Options.Log then
|
|
console.log('Status ',aData);
|
|
if isDefined(aData['ping']) then
|
|
exit;
|
|
a:=aData['action'];
|
|
if isUnDefined(a) or not isString(a) then
|
|
exit;
|
|
case String(a) of
|
|
'building' :
|
|
if Options.Log then
|
|
Console.log(Options.Name+': Server is building job ID '+String(aData['compileID']));
|
|
'built' :
|
|
begin
|
|
if Options.Log then
|
|
Console.log(Options.Name+': Server has built job ID '+String(aData['compileID']));
|
|
if isArray(aData['output']) then
|
|
begin
|
|
O:=TStringDynArray(aData['output']);
|
|
For I:=0 to Length(o)-1 do
|
|
if Pos('Error: ',O[i])>0 then
|
|
Console.Error(O[i])
|
|
else
|
|
Console.Log(O[i]);
|
|
if (Options.OverlayElementID<>'') then
|
|
ShowLogOverlay(O);
|
|
end;
|
|
if isBoolean(aData['success']) and (Boolean(aData['success'])) and Options.reload then
|
|
begin
|
|
if Options.Log then
|
|
Console.log(Options.Name+': Reloading page');
|
|
Reload;
|
|
end;
|
|
end;
|
|
'sync' :
|
|
begin
|
|
if Options.Reload then
|
|
begin
|
|
if Options.Log then
|
|
Console.log(Options.Name+': Resync event. Reloading page');
|
|
Reload;
|
|
end
|
|
else if Options.Log then
|
|
Console.log(Options.Name+': Resync event. Ignoring');
|
|
end;
|
|
else
|
|
if Options.Log then
|
|
console.warn('Unknown status data', TJSJSON.stringify(aData));
|
|
end;
|
|
end;
|
|
|
|
function THotReload.doStatus(Event: TEventListenerEvent): boolean;
|
|
|
|
Var
|
|
Data : TJSObject;
|
|
|
|
begin
|
|
if Event=nil then ;
|
|
if Options.log then
|
|
console.warn('Status received');
|
|
try
|
|
Data:=TJSJSON.parseObject(FLastReq.responseText);
|
|
HandleMessage(Data);
|
|
except
|
|
console.error('Error parsing JSON status text: '+FLastReq.responseText);
|
|
end;
|
|
FLastReq:=Nil;
|
|
Result:=True;
|
|
end;
|
|
|
|
procedure THotReload.OnTick;
|
|
|
|
Var
|
|
Req : TJSXMLHttpRequest;
|
|
|
|
begin
|
|
if Options.log then
|
|
console.log('tick');
|
|
if (FLastReq<>Nil) then
|
|
Exit;
|
|
Req:=TJSXMLHttpRequest.new;
|
|
Req.addEventListener('load',@DoStatus);
|
|
Req.addEventListener('abort',@DoAbort);
|
|
Req.open('GET',Options.Path);
|
|
Req.send;
|
|
FLastReq:=Req;
|
|
end;
|
|
|
|
procedure THotReload.Initialize;
|
|
|
|
begin
|
|
console.log('init');
|
|
if isunDefined(window) then
|
|
exit; // Cannot do anything
|
|
console.log('init 2');
|
|
Window.setInterval(@OnTick,Options.PollInterval);
|
|
end;
|
|
|
|
{ THotReloadOptions }
|
|
|
|
constructor THotReloadOptions.create;
|
|
begin
|
|
FPath:='/$sys/status';
|
|
FTimeOut:=20*1000;
|
|
FPollInterval:=1000;
|
|
FLog:=True;
|
|
FWarn:=True;
|
|
FName:='hotreload';
|
|
end;
|
|
|
|
end.
|
|
|