htmlactions.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. unit htmlactions;
  2. {$mode ObjFPC}
  3. {$H+}
  4. interface
  5. uses
  6. {$ifdef pas2js}
  7. web,
  8. {$endif}
  9. htmleventnames, Classes, SysUtils;
  10. Type
  11. {$ifndef pas2js}
  12. TJSEvent = Class(TObject);
  13. TJSElement = class(TObject);
  14. TJSHTMLElement = class(TJSElement);
  15. TJSHTMLElementArray = array of TJSHTMLElement;
  16. {$endif}
  17. THTMLNotifyEvent = Procedure (Sender : TObject; Event : TJSEvent) of object;
  18. THTMLCustomElementActionList = Class;
  19. { THTMLElementAction }
  20. { THTMLCustomElementAction }
  21. TForeachHTMLElementDataEx = {$ifdef pas2js}reference to {$endif} procedure (aElement : TJSHTMLElement; aData : TObject);
  22. TForeachHTMLElementData = {$ifdef pas2js}reference to {$endif} procedure (aElement : TJSHTMLElement);
  23. THTMLCustomElementAction = class(TComponent)
  24. private
  25. FActionList: THTMLCustomElementActionList;
  26. FCSSSelector: String;
  27. FCustomEvents: String;
  28. FElementID: String;
  29. FElement : TJSHTMLElement;
  30. FElements: TJSHTMLElementArray;
  31. FEvents: THTMLEvents;
  32. FOnExecute: THTMLNotifyEvent;
  33. FPreventDefault: Boolean;
  34. FStopPropagation: Boolean;
  35. FBeforeBind : TNotifyEvent;
  36. FAfterBind : TNotifyEvent;
  37. function GetIndex: Integer;
  38. procedure SetActionList(AValue: THTMLCustomElementActionList);
  39. procedure SetCSSSelector(AValue: String);
  40. procedure SetCustomEvents(AValue: String);
  41. procedure SetElementID(AValue: String);
  42. procedure SetIndex(AValue: Integer);
  43. Protected
  44. function GetParentComponent: TComponent; override;
  45. procedure SetParentComponent(AParent: TComponent); override;
  46. procedure ReadState(Reader: TReader); override;
  47. function HasParent: Boolean; override;
  48. Procedure BindElementEvents; virtual;
  49. Procedure DoBeforeBind;
  50. Procedure DoAfterBind;
  51. Public
  52. Destructor Destroy; override;
  53. Procedure Bind; virtual;
  54. Procedure BindEvents(aEl : TJSElement); virtual;
  55. procedure HandleEvent(Event: TJSEvent); virtual;
  56. Procedure ForEach(aCallback : TForeachHTMLElementDataEx; aData : TObject); overload;
  57. Procedure ForEach(aCallback : TForeachHTMLElementData); overload;
  58. Procedure AddClass(const aClass : String);
  59. Procedure RemoveClass(const aClass : String);
  60. Procedure ToggleClass(const aClass : String);
  61. Property ActionList : THTMLCustomElementActionList Read FActionList Write SetActionList;
  62. Property Element : TJSHTMLElement Read FElement;
  63. Property Elements : TJSHTMLElementArray Read FElements;
  64. Property Index : Integer Read GetIndex Write SetIndex;
  65. Public
  66. // These can be published in descendents
  67. Property Events : THTMLEvents Read FEvents Write FEvents;
  68. Property CustomEvents : String Read FCustomEvents Write SetCustomEvents;
  69. Property ElementID : String Read FElementID Write SetElementID;
  70. Property CSSSelector : String Read FCSSSelector Write SetCSSSelector;
  71. Property OnExecute : THTMLNotifyEvent Read FOnExecute Write FOnExecute;
  72. Property PreventDefault : Boolean Read FPreventDefault Write FPreventDefault default false;
  73. Property StopPropagation : Boolean Read FStopPropagation Write FStopPropagation default false;
  74. property BeforeBind : TNotifyEvent Read FBeforeBind Write FAfterBind;
  75. Property AfterBind : TNotifyEvent Read FAfterBind Write FAfterBind;
  76. end;
  77. THTMLCustomElementActionClass = Class of THTMLCustomElementAction;
  78. THTMLCustomElementActionArray = Array of THTMLCustomElementAction;
  79. THTMLElementAction = Class(THTMLCustomElementAction)
  80. Published
  81. Property Events;
  82. Property CustomEvents;
  83. Property ElementID;
  84. Property CSSSelector;
  85. Property PreventDefault;
  86. Property StopPropagation;
  87. Property OnExecute;
  88. Property BeforeBind;
  89. Property AfterBind;
  90. end;
  91. THTMLElementActionClass = class of THTMLElementAction;
  92. THTMLGLobalNotifyEvent = Procedure (Sender : TObject; Event : TJSEvent; var Handled: Boolean) of object;
  93. { THTMLCustomElementActionList }
  94. THTMLCustomElementActionList = class(TComponent)
  95. private
  96. FList : TFPList;
  97. FOnExecute: THTMLGLobalNotifyEvent;
  98. function GetAction(aIndex: Integer): THTMLCustomElementAction;
  99. function GetActionsCount: Integer;
  100. Protected
  101. class function CreateAction(aOwner : TComponent) : THTMLCustomElementAction; virtual;
  102. function GetActionIndex(aAction : THTMLCustomElementAction) : Integer;
  103. Procedure SetActionIndex(aAction : THTMLCustomElementAction; aValue : Integer);
  104. procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
  105. Procedure AddAction(aAction: THTMLCustomElementAction); virtual;
  106. Procedure RemoveAction(aAction: THTMLCustomElementAction); virtual;
  107. Function ExecuteAction(aAction: THTMLCustomElementAction; aEvent : TJSEvent) : Boolean; virtual;
  108. Public
  109. Constructor Create(aOwner : TComponent); override;
  110. Destructor Destroy; override;
  111. Procedure Clear;
  112. Function IndexOfElementID(aID : String; StartAt : Integer = 0) : Integer;
  113. Function FindActionByElementID(aID : String; StartAt : Integer = 0) : THTMLCustomElementAction;
  114. Function GetActionsForElementID(aID : String) : THTMLCustomElementActionArray;
  115. Function NewAction(aOwner: TComponent) : THTMLCustomElementAction;
  116. Function ActionByName(aName : String) : THTMLCustomElementAction;
  117. Property Actions[aIndex: Integer] : THTMLCustomElementAction Read GetAction;
  118. Property ActionCount : Integer Read GetActionsCount;
  119. Protected
  120. Property OnExecute : THTMLGLobalNotifyEvent Read FOnExecute Write FOnExecute;
  121. end;
  122. THTMLElementActionList = Class(THTMLCustomElementActionList)
  123. Published
  124. Property OnExecute;
  125. end;
  126. implementation
  127. uses strutils;
  128. { ----------------------------------------------------------------------
  129. THTMLCustomElementActionList
  130. ----------------------------------------------------------------------}
  131. function THTMLCustomElementActionList.GetAction(aIndex: Integer
  132. ): THTMLCustomElementAction;
  133. begin
  134. Result:=THTMLCustomElementAction(FList[aIndex])
  135. end;
  136. function THTMLCustomElementActionList.GetActionsCount: Integer;
  137. begin
  138. Result:=FList.Count;
  139. end;
  140. function THTMLCustomElementActionList.GetActionIndex(
  141. aAction: THTMLCustomElementAction): Integer;
  142. begin
  143. Result:=FList.IndexOf(aAction);
  144. end;
  145. procedure THTMLCustomElementActionList.SetActionIndex(
  146. aAction: THTMLCustomElementAction; aValue: Integer);
  147. Var
  148. Old : Integer;
  149. begin
  150. Old:=GetActionIndex(aAction);
  151. if Old<>aValue then
  152. FList.Move(Old,aValue);
  153. end;
  154. procedure THTMLCustomElementActionList.GetChildren(Proc: TGetChildProc;
  155. Root: TComponent);
  156. Var
  157. I : Integer;
  158. aAction : THTMLCustomElementAction;
  159. begin
  160. If Proc=Nil then
  161. exit;
  162. for I := 0 to ActionCount - 1 do
  163. begin
  164. aAction:=Actions[I];
  165. if (aAction.Owner=Root) then
  166. Proc(aAction);
  167. end;
  168. end;
  169. procedure THTMLCustomElementActionList.AddAction(
  170. aAction: THTMLCustomElementAction);
  171. begin
  172. FList.Add(aAction);
  173. end;
  174. procedure THTMLCustomElementActionList.RemoveAction(
  175. aAction: THTMLCustomElementAction);
  176. begin
  177. FList.Remove(aAction);
  178. end;
  179. function THTMLCustomElementActionList.ExecuteAction(
  180. aAction: THTMLCustomElementAction; aEvent: TJSEvent): Boolean;
  181. begin
  182. Result:=False;
  183. if Assigned(FOnExecute) then
  184. FOnExecute(aAction,aEvent,Result);
  185. end;
  186. constructor THTMLCustomElementActionList.Create(aOwner: TComponent);
  187. begin
  188. inherited Create(aOwner);
  189. FList:=TFPList.Create;
  190. end;
  191. destructor THTMLCustomElementActionList.Destroy;
  192. begin
  193. Clear;
  194. FreeAndNil(FList);
  195. inherited Destroy;
  196. end;
  197. procedure THTMLCustomElementActionList.Clear;
  198. Var
  199. A : THTMLCustomElementAction;
  200. begin
  201. While ActionCount>0 do
  202. begin
  203. A:=Actions[ActionCount-1];
  204. A.Free;
  205. end;
  206. end;
  207. function THTMLCustomElementActionList.IndexOfElementID(aID: String;
  208. StartAt: Integer): Integer;
  209. begin
  210. Result:=StartAt;
  211. if Result<0 then
  212. Result:=0;
  213. While (Result<ActionCount) and (GetAction(Result).ElementID<>aID) do
  214. Inc(Result);
  215. If Result>=ActionCount then
  216. Result:=-1;
  217. end;
  218. function THTMLCustomElementActionList.FindActionByElementID(aID: String;
  219. StartAt: Integer): THTMLCustomElementAction;
  220. Var
  221. Idx : Integer;
  222. begin
  223. Idx:=IndexOfElementID(aID,StartAt);
  224. if Idx=-1 then
  225. Result:=Nil
  226. else
  227. Result:=GetAction(Idx);
  228. end;
  229. function THTMLCustomElementActionList.GetActionsForElementID(aID: String): THTMLCustomElementActionArray;
  230. Var
  231. Idx,aCount : Integer;
  232. begin
  233. SetLength(Result,10);
  234. Idx:=IndexOfElementID(aID,0);
  235. aCount:=0;
  236. While (Idx<>-1) do
  237. begin
  238. if Length(Result)<=aCount then
  239. SetLength(Result,Length(Result)+10);
  240. Result[aCount]:=GetAction(Idx);
  241. Inc(aCount);
  242. Idx:=IndexOfElementID(aID,Idx+1);
  243. end;
  244. SetLength(Result,aCount);
  245. end;
  246. function THTMLCustomElementActionList.NewAction(aOwner: TComponent
  247. ): THTMLCustomElementAction;
  248. begin
  249. Result:=CreateAction(aOwner);
  250. Result.ActionList:=Self;
  251. end;
  252. class function THTMLCustomElementActionList.CreateAction(aOwner: TComponent
  253. ): THTMLCustomElementAction;
  254. begin
  255. Result:=THTMLElementAction.Create(aOwner);
  256. end;
  257. function THTMLCustomElementActionList.ActionByName(aName: String
  258. ): THTMLCustomElementAction;
  259. Var
  260. I : Integer;
  261. begin
  262. Result:=Nil;
  263. I:=ActionCount-1;
  264. While (Result=Nil) and (I>=0) do
  265. begin
  266. Result:=Actions[i];
  267. If Not SameText(Result.Name,aName) then
  268. Result:=Nil;
  269. Dec(I);
  270. end;
  271. end;
  272. { ----------------------------------------------------------------------
  273. THTMLCustomElementAction
  274. ----------------------------------------------------------------------}
  275. procedure THTMLCustomElementAction.SetActionList(AValue: THTMLCustomElementActionList);
  276. begin
  277. if (aValue=FActionList) then exit;
  278. if Assigned(FActionList) then
  279. FActionList.RemoveAction(Self);
  280. FActionList:=aValue;
  281. if Assigned(FActionList) then
  282. FActionList.AddAction(Self);
  283. end;
  284. function THTMLCustomElementAction.GetIndex: Integer;
  285. begin
  286. if Assigned(FActionList) then
  287. Result:=FActionList.GetActionIndex(Self)
  288. else
  289. Result:=-1;
  290. end;
  291. procedure THTMLCustomElementAction.SetIndex(AValue: Integer);
  292. begin
  293. FActionList.SetActionIndex(Self,aValue);
  294. end;
  295. function THTMLCustomElementAction.GetParentComponent: TComponent;
  296. begin
  297. if ActionList <> nil then
  298. Result := ActionList
  299. else
  300. Result := inherited GetParentComponent;
  301. end;
  302. destructor THTMLCustomElementAction.Destroy;
  303. begin
  304. if Assigned(ActionList) then
  305. ActionList.RemoveAction(Self);
  306. Inherited;
  307. end;
  308. procedure THTMLCustomElementAction.SetCSSSelector(AValue: String);
  309. begin
  310. if (FCSSSelector=aValue) then exit;
  311. FCSSSelector:=aValue;
  312. If Not (csDesigning in ComponentState) then
  313. Bind;
  314. end;
  315. procedure THTMLCustomElementAction.SetCustomEvents(AValue: String);
  316. begin
  317. if (FCustomEvents=aValue) then exit;
  318. FCustomEvents:=aValue;
  319. If Not (csDesigning in ComponentState) then
  320. BindElementEvents;
  321. end;
  322. procedure THTMLCustomElementAction.SetElementID(AValue: String);
  323. begin
  324. if (FElementID=aValue) then exit;
  325. FElementID:=aValue;
  326. If Not (csDesigning in ComponentState) then
  327. Bind;
  328. end;
  329. Procedure THTMLCustomElementAction.DoBeforeBind;
  330. begin
  331. If Assigned(FBeforeBind) then
  332. FBeforeBind(Self);
  333. end;
  334. Procedure THTMLCustomElementAction.DoAfterBind;
  335. begin
  336. If Assigned(FAfterBind) then
  337. FAfterBind(Self);
  338. end;
  339. procedure THTMLCustomElementAction.ForEach(
  340. aCallback: TForeachHTMLElementDataEx; aData: TObject);
  341. Var
  342. El : TJSHTMLElement;
  343. begin
  344. For el in FElements do
  345. if El<>Nil then
  346. aCallBack(El,aData);
  347. end;
  348. procedure THTMLCustomElementAction.ForEach(aCallback: TForeachHTMLElementData);
  349. Var
  350. El : TJSHTMLElement;
  351. begin
  352. For el in FElements do
  353. if El<>Nil then
  354. aCallBack(El);
  355. end;
  356. procedure THTMLCustomElementAction.SetParentComponent(AParent: TComponent);
  357. begin
  358. if not(csLoading in ComponentState) and (AParent is THTMLCustomElementActionList) then
  359. ActionList := THTMLCustomElementActionList(AParent);
  360. end;
  361. procedure THTMLCustomElementAction.ReadState(Reader: TReader);
  362. begin
  363. inherited ReadState(Reader);
  364. if Reader.Parent is THTMLCustomElementActionList then
  365. ActionList := THTMLCustomElementActionList(Reader.Parent);
  366. end;
  367. function THTMLCustomElementAction.HasParent: Boolean;
  368. begin
  369. if Assigned(ActionList) then
  370. Result:=True
  371. else
  372. Result:=inherited HasParent;
  373. end;
  374. { ----------------------------------------------------------------------
  375. The methods in this last part are either empty or implemented,
  376. depending on whether the unit is used in FPC (IDE) or pas2js
  377. ----------------------------------------------------------------------}
  378. {$ifdef pas2js}
  379. procedure THTMLCustomElementAction.BindElementEvents;
  380. Var
  381. El : TJSHTMLElement;
  382. begin
  383. For el in FElements do
  384. if Assigned(El) then
  385. BindEvents(El);
  386. end;
  387. procedure THTMLCustomElementAction.Bind;
  388. Var
  389. Nodes : TJSNodeList;
  390. I : Integer;
  391. begin
  392. DoBeforeBind;
  393. FElement:=Nil;
  394. FElements:=Nil;
  395. if ElementID<>'' then
  396. begin
  397. FElement:=TJSHTMLElement(document.getElementById(ElementID));
  398. FElements:=[FElement];
  399. end
  400. else if CSSSelector<>'' then
  401. begin
  402. Nodes:=document.querySelectorAll(CSSSelector);
  403. SetLength(FElements,Nodes.length);
  404. For I:=0 to Nodes.length-1 do
  405. Felements[I]:=TJSHTMLElement(Nodes.item(I));
  406. end;
  407. BindElementEvents;
  408. DoAfterBind;
  409. end;
  410. procedure THTMLCustomElementAction.HandleEvent(Event: TJSEvent);
  411. Var
  412. isHandled : Boolean;
  413. begin
  414. isHandled:=False;
  415. if Assigned(ActionList) then
  416. IsHandled:=ActionList.ExecuteAction(Self,Event);
  417. If (Not IsHandled) and Assigned(FOnExecute) then
  418. FonExecute(Self,Event);
  419. if StopPropagation then
  420. Event.stopPropagation;
  421. if PreventDefault then
  422. Event.preventDefault;
  423. end;
  424. procedure THTMLCustomElementAction.AddClass(const aClass: String);
  425. begin
  426. ForEach(procedure (aEl : TJSHTMLElement)
  427. begin
  428. aEl.classList.add(aClass);
  429. end
  430. );
  431. end;
  432. procedure THTMLCustomElementAction.RemoveClass(const aClass: String);
  433. begin
  434. ForEach(procedure (aEl : TJSHTMLElement)
  435. begin
  436. aEl.classList.Remove(aClass);
  437. end
  438. );
  439. end;
  440. procedure THTMLCustomElementAction.ToggleClass(const aClass: String);
  441. begin
  442. ForEach(procedure (aEl : TJSHTMLElement)
  443. begin
  444. aEl.classList.toggle(aClass);
  445. end
  446. );
  447. end;
  448. procedure THTMLCustomElementAction.BindEvents(aEl: TJSElement);
  449. Const
  450. Delims = [',',' '];
  451. var
  452. H : THTMLEvent;
  453. I,aCount : Integer;
  454. S : String;
  455. begin
  456. For h in THTMLEvent do
  457. if H in Events then
  458. aEl.addEventListener(HTMLEventNameArray[H],@HandleEvent)
  459. else
  460. aEl.removeEventListener(HTMLEventNameArray[H],@HandleEvent);
  461. aCount:=WordCount(CustomEvents,Delims);
  462. For I:=1 to aCount do
  463. begin
  464. S:=ExtractWord(I,CustomEvents,Delims);
  465. aEl.removeEventListener(HTMLEventNameArray[H],@HandleEvent);
  466. end;
  467. end;
  468. {$else}
  469. procedure THTMLCustomElementAction.BindElementEvents;
  470. begin
  471. end;
  472. procedure THTMLCustomElementAction.Bind;
  473. begin
  474. end;
  475. procedure THTMLCustomElementAction.HandleEvent(Event: TJSEvent);
  476. begin
  477. end;
  478. procedure THTMLCustomElementAction.AddClass(const aClass: String);
  479. begin
  480. end;
  481. procedure THTMLCustomElementAction.RemoveClass(const aClass: String);
  482. begin
  483. end;
  484. procedure THTMLCustomElementAction.ToggleClass(const aClass: String);
  485. begin
  486. end;
  487. procedure THTMLCustomElementAction.BindEvents(aEl: TJSElement);
  488. begin
  489. end;
  490. {$endif}
  491. end.