unit htmlactions; {$mode ObjFPC} {$H+} interface uses {$ifdef pas2js} web, {$endif} htmleventnames, Classes, SysUtils; Type {$ifndef pas2js} TJSEvent = Class(TObject); TJSElement = class(TObject); TJSHTMLElement = class(TJSElement); TJSHTMLElementArray = array of TJSHTMLElement; {$endif} THTMLNotifyEvent = Procedure (Sender : TObject; Event : TJSEvent) of object; THTMLCustomElementActionList = Class; { THTMLElementAction } { THTMLCustomElementAction } TForeachHTMLElementDataEx = {$ifdef pas2js}reference to {$endif} procedure (aElement : TJSHTMLElement; aData : TObject); TForeachHTMLElementData = {$ifdef pas2js}reference to {$endif} procedure (aElement : TJSHTMLElement); THTMLCustomElementAction = class(TComponent) private FActionList: THTMLCustomElementActionList; FCSSSelector: String; FCustomEvents: String; FElementID: String; FElement : TJSHTMLElement; FElements: TJSHTMLElementArray; FEvents: THTMLEvents; FOnExecute: THTMLNotifyEvent; FPreventDefault: Boolean; FStopPropagation: Boolean; FBeforeBind : TNotifyEvent; FAfterBind : TNotifyEvent; function GetIndex: Integer; procedure SetActionList(AValue: THTMLCustomElementActionList); procedure SetCSSSelector(AValue: String); procedure SetCustomEvents(AValue: String); procedure SetElementID(AValue: String); procedure SetIndex(AValue: Integer); Protected function GetParentComponent: TComponent; override; procedure SetParentComponent(AParent: TComponent); override; procedure ReadState(Reader: TReader); override; function HasParent: Boolean; override; Procedure BindElementEvents; virtual; Procedure DoBeforeBind; Procedure DoAfterBind; Public Destructor Destroy; override; Procedure Bind; virtual; Procedure BindEvents(aEl : TJSElement); virtual; procedure HandleEvent(Event: TJSEvent); virtual; Procedure ForEach(aCallback : TForeachHTMLElementDataEx; aData : TObject); overload; Procedure ForEach(aCallback : TForeachHTMLElementData); overload; Procedure AddClass(const aClass : String); Procedure RemoveClass(const aClass : String); Procedure ToggleClass(const aClass : String); Property ActionList : THTMLCustomElementActionList Read FActionList Write SetActionList; Property Element : TJSHTMLElement Read FElement; Property Elements : TJSHTMLElementArray Read FElements; Property Index : Integer Read GetIndex Write SetIndex; Public // These can be published in descendents Property Events : THTMLEvents Read FEvents Write FEvents; Property CustomEvents : String Read FCustomEvents Write SetCustomEvents; Property ElementID : String Read FElementID Write SetElementID; Property CSSSelector : String Read FCSSSelector Write SetCSSSelector; Property OnExecute : THTMLNotifyEvent Read FOnExecute Write FOnExecute; Property PreventDefault : Boolean Read FPreventDefault Write FPreventDefault default false; Property StopPropagation : Boolean Read FStopPropagation Write FStopPropagation default false; property BeforeBind : TNotifyEvent Read FBeforeBind Write FAfterBind; Property AfterBind : TNotifyEvent Read FAfterBind Write FAfterBind; end; THTMLCustomElementActionClass = Class of THTMLCustomElementAction; THTMLCustomElementActionArray = Array of THTMLCustomElementAction; THTMLElementAction = Class(THTMLCustomElementAction) Published Property Events; Property CustomEvents; Property ElementID; Property CSSSelector; Property PreventDefault; Property StopPropagation; Property OnExecute; Property BeforeBind; Property AfterBind; end; THTMLElementActionClass = class of THTMLElementAction; THTMLGLobalNotifyEvent = Procedure (Sender : TObject; Event : TJSEvent; var Handled: Boolean) of object; { THTMLCustomElementActionList } THTMLCustomElementActionList = class(TComponent) private FList : TFPList; FOnExecute: THTMLGLobalNotifyEvent; function GetAction(aIndex: Integer): THTMLCustomElementAction; function GetActionsCount: Integer; Protected class function CreateAction(aOwner : TComponent) : THTMLCustomElementAction; virtual; function GetActionIndex(aAction : THTMLCustomElementAction) : Integer; Procedure SetActionIndex(aAction : THTMLCustomElementAction; aValue : Integer); procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override; Procedure AddAction(aAction: THTMLCustomElementAction); virtual; Procedure RemoveAction(aAction: THTMLCustomElementAction); virtual; Function ExecuteAction(aAction: THTMLCustomElementAction; aEvent : TJSEvent) : Boolean; virtual; Public Constructor Create(aOwner : TComponent); override; Destructor Destroy; override; Procedure Clear; Function IndexOfElementID(aID : String; StartAt : Integer = 0) : Integer; Function FindActionByElementID(aID : String; StartAt : Integer = 0) : THTMLCustomElementAction; Function GetActionsForElementID(aID : String) : THTMLCustomElementActionArray; Function NewAction(aOwner: TComponent) : THTMLCustomElementAction; Function ActionByName(aName : String) : THTMLCustomElementAction; Property Actions[aIndex: Integer] : THTMLCustomElementAction Read GetAction; Property ActionCount : Integer Read GetActionsCount; Protected Property OnExecute : THTMLGLobalNotifyEvent Read FOnExecute Write FOnExecute; end; THTMLElementActionList = Class(THTMLCustomElementActionList) Published Property OnExecute; end; implementation uses strutils; { ---------------------------------------------------------------------- THTMLCustomElementActionList ----------------------------------------------------------------------} function THTMLCustomElementActionList.GetAction(aIndex: Integer ): THTMLCustomElementAction; begin Result:=THTMLCustomElementAction(FList[aIndex]) end; function THTMLCustomElementActionList.GetActionsCount: Integer; begin Result:=FList.Count; end; function THTMLCustomElementActionList.GetActionIndex( aAction: THTMLCustomElementAction): Integer; begin Result:=FList.IndexOf(aAction); end; procedure THTMLCustomElementActionList.SetActionIndex( aAction: THTMLCustomElementAction; aValue: Integer); Var Old : Integer; begin Old:=GetActionIndex(aAction); if Old<>aValue then FList.Move(Old,aValue); end; procedure THTMLCustomElementActionList.GetChildren(Proc: TGetChildProc; Root: TComponent); Var I : Integer; aAction : THTMLCustomElementAction; begin If Proc=Nil then exit; for I := 0 to ActionCount - 1 do begin aAction:=Actions[I]; if (aAction.Owner=Root) then Proc(aAction); end; end; procedure THTMLCustomElementActionList.AddAction( aAction: THTMLCustomElementAction); begin FList.Add(aAction); end; procedure THTMLCustomElementActionList.RemoveAction( aAction: THTMLCustomElementAction); begin FList.Remove(aAction); end; function THTMLCustomElementActionList.ExecuteAction( aAction: THTMLCustomElementAction; aEvent: TJSEvent): Boolean; begin Result:=False; if Assigned(FOnExecute) then FOnExecute(aAction,aEvent,Result); end; constructor THTMLCustomElementActionList.Create(aOwner: TComponent); begin inherited Create(aOwner); FList:=TFPList.Create; end; destructor THTMLCustomElementActionList.Destroy; begin Clear; FreeAndNil(FList); inherited Destroy; end; procedure THTMLCustomElementActionList.Clear; Var A : THTMLCustomElementAction; begin While ActionCount>0 do begin A:=Actions[ActionCount-1]; A.Free; end; end; function THTMLCustomElementActionList.IndexOfElementID(aID: String; StartAt: Integer): Integer; begin Result:=StartAt; if Result<0 then Result:=0; While (ResultaID) do Inc(Result); If Result>=ActionCount then Result:=-1; end; function THTMLCustomElementActionList.FindActionByElementID(aID: String; StartAt: Integer): THTMLCustomElementAction; Var Idx : Integer; begin Idx:=IndexOfElementID(aID,StartAt); if Idx=-1 then Result:=Nil else Result:=GetAction(Idx); end; function THTMLCustomElementActionList.GetActionsForElementID(aID: String): THTMLCustomElementActionArray; Var Idx,aCount : Integer; begin SetLength(Result,10); Idx:=IndexOfElementID(aID,0); aCount:=0; While (Idx<>-1) do begin if Length(Result)<=aCount then SetLength(Result,Length(Result)+10); Result[aCount]:=GetAction(Idx); Inc(aCount); Idx:=IndexOfElementID(aID,Idx+1); end; SetLength(Result,aCount); end; function THTMLCustomElementActionList.NewAction(aOwner: TComponent ): THTMLCustomElementAction; begin Result:=CreateAction(aOwner); Result.ActionList:=Self; end; class function THTMLCustomElementActionList.CreateAction(aOwner: TComponent ): THTMLCustomElementAction; begin Result:=THTMLElementAction.Create(aOwner); end; function THTMLCustomElementActionList.ActionByName(aName: String ): THTMLCustomElementAction; Var I : Integer; begin Result:=Nil; I:=ActionCount-1; While (Result=Nil) and (I>=0) do begin Result:=Actions[i]; If Not SameText(Result.Name,aName) then Result:=Nil; Dec(I); end; end; { ---------------------------------------------------------------------- THTMLCustomElementAction ----------------------------------------------------------------------} procedure THTMLCustomElementAction.SetActionList(AValue: THTMLCustomElementActionList); begin if (aValue=FActionList) then exit; if Assigned(FActionList) then FActionList.RemoveAction(Self); FActionList:=aValue; if Assigned(FActionList) then FActionList.AddAction(Self); end; function THTMLCustomElementAction.GetIndex: Integer; begin if Assigned(FActionList) then Result:=FActionList.GetActionIndex(Self) else Result:=-1; end; procedure THTMLCustomElementAction.SetIndex(AValue: Integer); begin FActionList.SetActionIndex(Self,aValue); end; function THTMLCustomElementAction.GetParentComponent: TComponent; begin if ActionList <> nil then Result := ActionList else Result := inherited GetParentComponent; end; destructor THTMLCustomElementAction.Destroy; begin if Assigned(ActionList) then ActionList.RemoveAction(Self); Inherited; end; procedure THTMLCustomElementAction.SetCSSSelector(AValue: String); begin if (FCSSSelector=aValue) then exit; FCSSSelector:=aValue; If Not (csDesigning in ComponentState) then Bind; end; procedure THTMLCustomElementAction.SetCustomEvents(AValue: String); begin if (FCustomEvents=aValue) then exit; FCustomEvents:=aValue; If Not (csDesigning in ComponentState) then BindElementEvents; end; procedure THTMLCustomElementAction.SetElementID(AValue: String); begin if (FElementID=aValue) then exit; FElementID:=aValue; If Not (csDesigning in ComponentState) then Bind; end; Procedure THTMLCustomElementAction.DoBeforeBind; begin If Assigned(FBeforeBind) then FBeforeBind(Self); end; Procedure THTMLCustomElementAction.DoAfterBind; begin If Assigned(FAfterBind) then FAfterBind(Self); end; procedure THTMLCustomElementAction.ForEach( aCallback: TForeachHTMLElementDataEx; aData: TObject); Var El : TJSHTMLElement; begin For el in FElements do if El<>Nil then aCallBack(El,aData); end; procedure THTMLCustomElementAction.ForEach(aCallback: TForeachHTMLElementData); Var El : TJSHTMLElement; begin For el in FElements do if El<>Nil then aCallBack(El); end; procedure THTMLCustomElementAction.SetParentComponent(AParent: TComponent); begin if not(csLoading in ComponentState) and (AParent is THTMLCustomElementActionList) then ActionList := THTMLCustomElementActionList(AParent); end; procedure THTMLCustomElementAction.ReadState(Reader: TReader); begin inherited ReadState(Reader); if Reader.Parent is THTMLCustomElementActionList then ActionList := THTMLCustomElementActionList(Reader.Parent); end; function THTMLCustomElementAction.HasParent: Boolean; begin if Assigned(ActionList) then Result:=True else Result:=inherited HasParent; end; { ---------------------------------------------------------------------- The methods in this last part are either empty or implemented, depending on whether the unit is used in FPC (IDE) or pas2js ----------------------------------------------------------------------} {$ifdef pas2js} procedure THTMLCustomElementAction.BindElementEvents; Var El : TJSHTMLElement; begin For el in FElements do if Assigned(El) then BindEvents(El); end; procedure THTMLCustomElementAction.Bind; Var Nodes : TJSNodeList; I : Integer; begin DoBeforeBind; FElement:=Nil; FElements:=Nil; if ElementID<>'' then begin FElement:=TJSHTMLElement(document.getElementById(ElementID)); FElements:=[FElement]; end else if CSSSelector<>'' then begin Nodes:=document.querySelectorAll(CSSSelector); SetLength(FElements,Nodes.length); For I:=0 to Nodes.length-1 do Felements[I]:=TJSHTMLElement(Nodes.item(I)); end; BindElementEvents; DoAfterBind; end; procedure THTMLCustomElementAction.HandleEvent(Event: TJSEvent); Var isHandled : Boolean; begin isHandled:=False; if Assigned(ActionList) then IsHandled:=ActionList.ExecuteAction(Self,Event); If (Not IsHandled) and Assigned(FOnExecute) then FonExecute(Self,Event); if StopPropagation then Event.stopPropagation; if PreventDefault then Event.preventDefault; end; procedure THTMLCustomElementAction.AddClass(const aClass: String); begin ForEach(procedure (aEl : TJSHTMLElement) begin aEl.classList.add(aClass); end ); end; procedure THTMLCustomElementAction.RemoveClass(const aClass: String); begin ForEach(procedure (aEl : TJSHTMLElement) begin aEl.classList.Remove(aClass); end ); end; procedure THTMLCustomElementAction.ToggleClass(const aClass: String); begin ForEach(procedure (aEl : TJSHTMLElement) begin aEl.classList.toggle(aClass); end ); end; procedure THTMLCustomElementAction.BindEvents(aEl: TJSElement); Const Delims = [',',' ']; var H : THTMLEvent; I,aCount : Integer; S : String; begin For h in THTMLEvent do if H in Events then aEl.addEventListener(HTMLEventNameArray[H],@HandleEvent) else aEl.removeEventListener(HTMLEventNameArray[H],@HandleEvent); aCount:=WordCount(CustomEvents,Delims); For I:=1 to aCount do begin S:=ExtractWord(I,CustomEvents,Delims); aEl.removeEventListener(HTMLEventNameArray[H],@HandleEvent); end; end; {$else} procedure THTMLCustomElementAction.BindElementEvents; begin end; procedure THTMLCustomElementAction.Bind; begin end; procedure THTMLCustomElementAction.HandleEvent(Event: TJSEvent); begin end; procedure THTMLCustomElementAction.AddClass(const aClass: String); begin end; procedure THTMLCustomElementAction.RemoveClass(const aClass: String); begin end; procedure THTMLCustomElementAction.ToggleClass(const aClass: String); begin end; procedure THTMLCustomElementAction.BindEvents(aEl: TJSElement); begin end; {$endif} end.