formgen.pas 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128
  1. {
  2. This file is part of the Pas2JS toolchain
  3. Copyright (c) 2020 by Michael Van Canneyt
  4. This unit implements a HTML to pascal class converter.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit formgen;
  12. {$mode objfpc}{$H+}
  13. interface
  14. uses
  15. Classes, SysUtils, sax, sax_html, pascodegen, fpjson, jsonparser;
  16. Type
  17. TLogEvent = Procedure (Sender : TObject; Const Msg : String) of object;
  18. { TFormElement }
  19. TFormElement = Class(TCollectionItem)
  20. private
  21. FHTMLID: String;
  22. FName: String;
  23. FType: String;
  24. FEvents : TStrings;
  25. function GetEvents: TStrings;
  26. function getName: String;
  27. procedure SetEvents(AValue: TStrings);
  28. Public
  29. Destructor Destroy; override;
  30. Function HasEvents : Boolean;
  31. Procedure Assign(Source : TPersistent); override;
  32. Published
  33. Property Name : String Read getName Write FName;
  34. Property HTMLID : String Read FHTMLID Write FHTMLID;
  35. Property ElementType : String Read FType Write FType;
  36. Property Events : TStrings Read GetEvents Write SetEvents;
  37. end;
  38. { TFormElementList }
  39. TFormElementList = CLass(TCollection)
  40. private
  41. function GetEl(aIndex : Integer): TFormElement;
  42. Public
  43. Function Add(Const aName : string) : TFormElement;
  44. Function IndexOf(Const aName : string) : Integer;
  45. Function Find(Const aName : string) : TFormElement;
  46. Property Elements[aIndex : Integer] : TFormElement Read GetEl; default;
  47. end;
  48. TAttributeOperation = (aoNotPresent,aoPresent,aoEqual,aoNotEqual,aoContains);
  49. { TAttributeCondition }
  50. TAttributeCondition = Class(TCollectionItem)
  51. private
  52. FAttribute: String;
  53. FOperation: TAttributeOperation;
  54. FValue: String;
  55. Public
  56. Procedure LoadFromJSON(aName : String; aValue: TJSONData);
  57. function IsMatch(aValue: String): Boolean;
  58. Property Attribute : String Read FAttribute Write FAttribute;
  59. Property Operation : TAttributeOperation Read FOperation Write FOperation;
  60. Property Value : String Read FValue Write FValue;
  61. end;
  62. { TAttributeConditionList }
  63. TAttributeConditionList = Class(TCollection)
  64. private
  65. function GetC(aIndex : Integer): TAttributeCondition;
  66. Public
  67. Procedure LoadFromJSON(aJSON : TJSONObject);
  68. Function IsMatch(Attrs: TSAXAttributes): Boolean;
  69. Property Conditions[aIndex : Integer] : TAttributeCondition Read GetC; default;
  70. end;
  71. (* // Structure of accepted JSON
  72. [
  73. {
  74. "class" : "TWebComboBox",
  75. "tag" : "input",
  76. "attrs" : {
  77. name0 : null, // name0 Not present
  78. name1 : "value", // name1 equals value
  79. name2 ; "-value", // name2 does not equal value
  80. name3 : "~value" // name3 contains value
  81. }
  82. }
  83. ]
  84. *)
  85. { THTMLElementMap }
  86. THTMLElementMap = Class(TCollectionItem)
  87. private
  88. FConditionList : TAttributeConditionList;
  89. FControlClass: String;
  90. FTag: String;
  91. function GetAttrConditionList: TAttributeConditionList;
  92. Protected
  93. Function CreateConditionList : TAttributeConditionList; virtual;
  94. Public
  95. Destructor Destroy; override;
  96. Procedure LoadFromJSON(aJSON : TJSONObject);
  97. Function HasConditions : Boolean;
  98. Function IsMatch(aTag: SAXString; Attrs: TSAXAttributes): Boolean;
  99. Property Tag : String Read FTag Write FTag;
  100. Property ControlClass : String Read FControlClass Write FControlClass;
  101. Property Attributes : TAttributeConditionList Read GetAttrConditionList;
  102. end;
  103. { THTMLElementMapList }
  104. THTMLElementMapList = Class(TCollection)
  105. private
  106. function GetM(aIndex : Integer): THTMLElementMap;
  107. Public
  108. Procedure LoadFromFile(Const aFileName : String);
  109. Procedure LoadFromStream(aStream : TStream); virtual;
  110. Procedure LoadFromJSON(aJSON : TJSONArray); virtual;
  111. Function IndexOfMap(aTag: SAXString; Attrs: TSAXAttributes): Integer;
  112. Function FindMap(aTag: SAXString; Attrs: TSAXAttributes): THTMLElementMap;
  113. Property Maps[aIndex : Integer] : THTMLElementMap Read GetM; default;
  114. end;
  115. { THTMLToFormELements }
  116. THTMLToFormELements = class(TComponent)
  117. private
  118. FBelowID: String;
  119. FDefaultElements: Boolean;
  120. FExcludeIDS: TStrings;
  121. FFormElements: TFormElementList;
  122. FLevel : Integer;
  123. FMap: THTMLElementMapList;
  124. FOnLog: TLogEvent;
  125. function MakeValidName(aID: string): string;
  126. procedure SetExcludeIDS(AValue: TStrings);
  127. procedure SetFormElements(AValue: TFormElementList);
  128. protected
  129. Procedure DoLog(Const Msg : String);
  130. Procedure DoLog(Const Fmt : String; Args : Array of const);
  131. function CreateHTMLElementMapList: THTMLElementMapList; virtual;
  132. procedure GetEvents(aEl: TFormElement; Atts: TSAXAttributes); virtual;
  133. procedure DoEndElement(Sender: TObject; const {%H-}NamespaceURI, {%H-}LocalName,
  134. {%H-}QName: SAXString); virtual;
  135. procedure DoStartElement(Sender: TObject; const {%H-}NamespaceURI, LocalName,
  136. {%H-}QName: SAXString; Atts: TSAXAttributes); virtual;
  137. function Maptype(aTag: SAXString; Atts: TSAXAttributes): String; virtual;
  138. Class Function CreateElementList : TFormElementList; virtual;
  139. Property Level : Integer Read FLevel Write FLevel;
  140. Public
  141. Constructor Create(aOwner : TComponent); override;
  142. Destructor Destroy; override;
  143. Procedure Clear;
  144. Procedure LoadFromStream(aInput : TStream);
  145. Procedure LoadFromFile(Const aFileName : String);
  146. Property FormElements : TFormElementList Read FFormElements Write SetFormElements;
  147. Property BelowID : String Read FBelowID Write FBelowID;
  148. Property ExcludeIDS : TStrings Read FExcludeIDS Write SetExcludeIDS;
  149. Property Map : THTMLElementMapList Read FMap;
  150. Property DefaultElements : Boolean Read FDefaultElements Write FDefaultElements;
  151. Property OnLog : TLogEvent Read FOnLog Write FOnLog;
  152. end;
  153. { TFormCodeGen }
  154. TSpecialMethod = (smConstructor,smBindElements,smBindElementEvents);
  155. TSpecialMethods = Set of TSpecialMethod;
  156. TFormOption = (foEvents,foFormFile,foBindInConstructor);
  157. TFormOptions = Set of TFormOption;
  158. { TFormFileCodeGen }
  159. TFormFileCodeGen = Class(TPascalCodeGenerator)
  160. private
  161. FElementHeight: Word;
  162. FElementHSpacing: Word;
  163. FElementVSpacing: Word;
  164. FElementWidth: Word;
  165. FDoEvents: Boolean;
  166. FFormClassName: String;
  167. FFormElements: TFormElementList;
  168. FIDProperty: String;
  169. FLeft: Word;
  170. FMaxHeight: Word;
  171. FMaxWidth: Word;
  172. FTop: Word;
  173. Protected
  174. function GetFormName(const aClassName: string): String; virtual;
  175. procedure GenerateElements; virtual;
  176. procedure EmitElementEvents(El: TFormElement); virtual;
  177. procedure EmitElementProps(El: TFormElement); virtual;
  178. procedure NextPosition; virtual;
  179. Property ELeft : Word Read FLeft Write FLeft;
  180. Property ETop : Word Read FTop Write FTop;
  181. Public
  182. Constructor Create(aOwner : TComponent);override;
  183. Procedure Execute;
  184. Property FormElements: TFormElementList read FFormElements write FFormElements;
  185. Property FormClassName : String read FFormClassName write FFormClassName;
  186. Property DoEvents : Boolean read FDoEvents write FDoEvents;
  187. Property IDProperty : String Read FIDProperty Write FIDProperty;
  188. Property ElementHeight : Word Read FElementHeight Write FElementHeight;
  189. Property ElementWidth : Word Read FElementWidth Write FElementWidth;
  190. Property MaxWidth : Word Read FMaxWidth Write FMaxWidth;
  191. Property MaxHeight : Word Read FMaxHeight Write FMaxHeight;
  192. Property ElementHSpacing : Word Read FElementHSpacing Write FElementHSpacing;
  193. Property ElementVSpacing : Word Read FElementVSpacing Write FElementVSpacing;
  194. end;
  195. TFormCodeGen = Class(TPascalCodeGenerator)
  196. private
  197. FAddMethods: TSpecialMethods;
  198. FConstructorArgs: String;
  199. FEventModifiers: String;
  200. FEventSignature: string;
  201. FFormClassName: string;
  202. FFormElements: TFormElementList;
  203. fFormFileGenerator: TFormFileCodeGen;
  204. FFormSource: Tstrings;
  205. FGetElementFunction: string;
  206. FOptions: TFormOptions;
  207. FOverrideMethods: TSpecialMethods;
  208. FParentClassName: string;
  209. FVirtualMethods: TSpecialMethods;
  210. procedure SetFormElements(AValue: TFormElementList);
  211. Protected
  212. function BaseUnits : String; override;
  213. Function CreateHTMLToFormELements: THTMLToFormELements; virtual;
  214. Class Function CreateElementList : TFormElementList; virtual;
  215. procedure EmitFormFile; virtual;
  216. function CreateFormFileGen : TFormFileCodeGen; virtual;
  217. procedure EmitFormElement(aEL: TFormElement); virtual;
  218. procedure EmitFormEvents(aEL: TFormElement);virtual;
  219. procedure EmitImplementation; virtual;
  220. procedure EmitPublicSection; virtual;
  221. procedure EmitPublishedSection; virtual;
  222. procedure EmitFormBindElements; virtual;
  223. procedure EmitFormBindEvents; virtual;
  224. procedure EmitFormConstructor; virtual;
  225. function VirtualOverride(M: TSpecialMethod; const Decl: String): string; virtual;
  226. Public
  227. Constructor Create(aOwner : TComponent); override;
  228. Destructor Destroy; override;
  229. class function Pretty(const S: String): string; virtual;
  230. class procedure GetEventNameAndHandler(const S,aFieldName: String; out aName, aHandler: string);
  231. Procedure Execute;
  232. Property FormFileGenerator : TFormFileCodeGen Read fFormFileGenerator Write FFormFileGenerator;
  233. Property FormElements : TFormElementList Read FFormElements Write SetFormElements;
  234. Property FormClassName : string Read FFormClassName Write FFormClassName;
  235. Property ParentClassName : string Read FParentClassName Write FParentClassName;
  236. Property GetElementFunction : string Read FGetElementFunction Write FGetElementFunction;
  237. Property EventSignature: string Read FEventSignature Write FEventSignature;
  238. Property EventModifiers : String Read FEventModifiers Write FEventModifiers;
  239. Property ConstructorArgs : String Read FConstructorArgs Write FConstructorArgs;
  240. Property Options : TFormOptions Read FOptions Write FOptions;
  241. Property AddMethods : TSpecialMethods Read FAddMethods Write FAddMethods;
  242. Property OverrideMethods : TSpecialMethods Read FOverrideMethods Write FOverrideMethods;
  243. Property VirtualMethods : TSpecialMethods Read FVirtualMethods Write FVirtualMethods;
  244. Property FormSource : Tstrings Read FFormSource;
  245. end;
  246. implementation
  247. { TFormFileCodeGen }
  248. function TFormFileCodeGen.GetFormName(const aClassName: string): String;
  249. begin
  250. Result:=aClassName;
  251. if SameText(Copy(Result,1,1),'T') then
  252. Delete(Result,1,1);
  253. end;
  254. (*
  255. procedure TFormFileCodeGen.LoadFromStream(const AStream: TStream);
  256. begin
  257. if aStream=Nil then exit;
  258. end;
  259. *)
  260. constructor TFormFileCodeGen.Create(aOwner: TComponent);
  261. begin
  262. inherited Create(aOwner);
  263. IDProperty:='ElementID';
  264. ElementHeight:=24;
  265. ElementWidth:=72;
  266. ElementVSpacing:=8;
  267. ElementHSpacing:=16;
  268. MaxWidth:=800;
  269. MaxHeight:=600;
  270. end;
  271. procedure TFormFileCodeGen.NextPosition;
  272. begin
  273. ELeft:=ELeft+ElementWidth+ElementHSpacing;
  274. if ELeft+ElementWidth>=MaxWidth then
  275. begin
  276. ELeft:=8;
  277. ETop:=ETop+ElementHeight+ElementVSpacing;
  278. end;
  279. end;
  280. procedure TFormFileCodeGen.EmitElementProps(El : TFormElement);
  281. begin
  282. AddLn('Top = %d',[ETop]);
  283. AddLn('Left = %d',[ELeft]);
  284. Addln('Width = %d',[ElementWidth]);
  285. Addln('Height = %d',[ElementHeight]);
  286. addLn('%s = ''%s''',[IDProperty,El.Name]);
  287. end;
  288. procedure TFormFileCodeGen.EmitElementEvents(El : TFormElement);
  289. Var
  290. S,EN,EH : String;
  291. begin
  292. For S in El.Events do
  293. begin
  294. TFormCodeGen.GetEventNameAndHandler(S,El.Name,EN,EH);
  295. Addln('%s = %s',[EN,EH]);
  296. end;
  297. end;
  298. procedure TFormFileCodeGen.GenerateElements;
  299. Var
  300. I : Integer;
  301. El : TFormElement;
  302. begin
  303. For I:=0 to FormElements.Count-1 do
  304. begin
  305. el:=FormElements[i];
  306. With El do
  307. begin
  308. Addln('object %s: %s',[Name,ElementType]);
  309. Indent;
  310. EmitElementProps(EL);
  311. if DoEvents then
  312. EmitElementEvents(El);
  313. Undent;
  314. AddLn('end');
  315. NextPosition;
  316. end;
  317. end;
  318. end;
  319. procedure TFormFileCodeGen.Execute;
  320. begin
  321. ETop:=8;
  322. ELeft:=8;
  323. AddLn('object %s : %s',[GetFormName(FormClassName),FormClassName]);
  324. Indent;
  325. AddLn('Width = %d',[MaxWidth]);
  326. AddLn('Height = %d',[MaxHeight]);
  327. GenerateElements;
  328. Undent;
  329. AddLn('end');
  330. end;
  331. { THTMLElementMapList }
  332. function THTMLElementMapList.GetM(aIndex : Integer): THTMLElementMap;
  333. begin
  334. Result:=Items[aIndex] as THTMLElementMap;
  335. end;
  336. procedure THTMLElementMapList.LoadFromFile(const aFileName: String);
  337. Var
  338. F : TFileStream;
  339. begin
  340. F:=TFileStream.Create(aFileName,fmOpenRead or fmShareDenyWrite);
  341. try
  342. LoadFromStream(F);
  343. finally
  344. F.Free;
  345. end;
  346. end;
  347. procedure THTMLElementMapList.LoadFromStream(aStream: TStream);
  348. Var
  349. D : TJSONData;
  350. begin
  351. D:=GetJSON(aStream);
  352. try
  353. if D is TJSONArray then
  354. LoadFromJSON(D as TJSONArray);
  355. finally
  356. D.Free;
  357. end;
  358. end;
  359. procedure THTMLElementMapList.LoadFromJSON(aJSON: TJSONArray);
  360. Var
  361. E : TJSONEnum;
  362. begin
  363. For E in aJSON do
  364. if E.Value is TJSONObject then
  365. (Add as THTMLElementMap).LoadFromJSON(e.Value as TJSONObject);
  366. end;
  367. function THTMLElementMapList.IndexOfMap(aTag: SAXString; Attrs: TSAXAttributes
  368. ): Integer;
  369. begin
  370. Result:=0;
  371. While (Result<Count) and Not GetM(Result).IsMatch(aTag,Attrs) do
  372. Inc(Result);
  373. if Result=Count then
  374. Result:=-1;
  375. end;
  376. function THTMLElementMapList.FindMap(aTag: SAXString; Attrs: TSAXAttributes
  377. ): THTMLElementMap;
  378. Var
  379. Idx : Integer;
  380. begin
  381. Idx:=IndexOfMap(aTag,Attrs);
  382. If Idx=-1 then
  383. Result:=Nil
  384. else
  385. Result:=GetM(Idx);
  386. end;
  387. { THTMLElementMap }
  388. function THTMLElementMap.GetAttrConditionList: TAttributeConditionList;
  389. begin
  390. If FConditionList=Nil then
  391. FConditionList:=CreateConditionList;
  392. Result:=FConditionList
  393. end;
  394. function THTMLElementMap.CreateConditionList: TAttributeConditionList;
  395. begin
  396. Result:=TAttributeConditionList.Create(TAttributeCondition);
  397. end;
  398. destructor THTMLElementMap.Destroy;
  399. begin
  400. FreeAndNil(FConditionList);
  401. inherited Destroy;
  402. end;
  403. procedure THTMLElementMap.LoadFromJSON(aJSON: TJSONObject);
  404. Var
  405. A : TJSONObject;
  406. begin
  407. FTag:=aJSON.Get('tag','');
  408. ControlClass:=aJSON.Get('class','');
  409. A:=aJSON.Get('attrs',TJSONObject(Nil));
  410. If Assigned(A) then
  411. Attributes.LoadFromJSON(A);
  412. end;
  413. function THTMLElementMap.HasConditions: Boolean;
  414. begin
  415. Result:=Assigned(FConditionList) and (FConditionList.Count>0);
  416. end;
  417. function THTMLElementMap.IsMatch(aTag: SAXString; Attrs: TSAXAttributes): Boolean;
  418. begin
  419. Result:=SameText(UTF8Encode(aTag),FTag);
  420. if Result and HasConditions then
  421. Result:=Attributes.IsMatch(Attrs);
  422. end;
  423. { TAttributeConditionList }
  424. function TAttributeConditionList.GetC(aIndex : Integer): TAttributeCondition;
  425. begin
  426. Result:=TAttributeCondition(Items[aIndex]);
  427. end;
  428. procedure TAttributeConditionList.LoadFromJSON(aJSON: TJSONObject);
  429. Var
  430. E : TJSONEnum;
  431. A : TAttributeCondition;
  432. begin
  433. For E in aJSON do
  434. begin
  435. A:=Add as TAttributeCondition;
  436. A.LoadFromJSON(E.Key,E.Value);
  437. end;
  438. end;
  439. function TAttributeConditionList.IsMatch(Attrs: TSAXAttributes): Boolean;
  440. function GetIndex(const aName: SAXString): Integer;
  441. begin
  442. Result := Attrs.Length-1;
  443. while (Result>=0) and not SameText(UTF8Encode(Attrs.LocalNames[Result]),UTF8Encode(aName)) do
  444. Dec(Result);
  445. end;
  446. Var
  447. I,Idx : Integer;
  448. A : TAttributeCondition;
  449. begin
  450. Result:=True;
  451. I:=0;
  452. While Result and (I<Count) do
  453. begin
  454. A:=GetC(I);
  455. Idx:=GetIndex(UTF8Decode(A.Attribute));
  456. if A.Operation=aoNotPresent then
  457. Result:=Idx<0
  458. else
  459. Result:=A.IsMatch(UTF8Encode(Attrs.GetValue(Idx)));
  460. Inc(I);
  461. end;
  462. end;
  463. { TAttributeCondition }
  464. procedure TAttributeCondition.LoadFromJSON(aName: String; aValue: TJSONData);
  465. Var
  466. S : TJSONStringType;
  467. C : Char;
  468. begin
  469. Attribute:=aName;
  470. if aValue.JSONType=jtNull then
  471. Operation:=aoNotPresent
  472. else if aValue.JSONType=jtBoolean then
  473. begin
  474. if aValue.AsBoolean then
  475. Operation:=aoPresent
  476. else
  477. Operation:=aoNotPresent
  478. end
  479. else
  480. begin
  481. S:=aValue.AsString;
  482. If S<>'' then
  483. C:=S[1]
  484. else
  485. C:=#0;
  486. Case C of
  487. '-' : Operation:=aoNotEqual;
  488. '~' : Operation:=aoContains;
  489. else
  490. Operation:=aoEqual;
  491. Value:=S;
  492. end;
  493. if Operation in [aoNotEqual,aoContains] then
  494. Value:=Copy(S,2,Length(S)-1);
  495. end;
  496. end;
  497. function TAttributeCondition.IsMatch(aValue: String): Boolean;
  498. begin
  499. Case Operation of
  500. aoPresent : Result:=True;
  501. aoNotEqual : Result:=Not SameText(aValue,Value);
  502. aoEqual : Result:=SameText(aValue,Value);
  503. aoContains : Result:=Pos(LowerCase(Value),LowerCase(aValue))>0;
  504. end;
  505. end;
  506. { THTMLToFormELements }
  507. procedure THTMLToFormELements.SetFormElements(AValue: TFormElementList);
  508. begin
  509. if FFormElements=AValue then Exit;
  510. FFormElements:=AValue;
  511. end;
  512. procedure THTMLToFormELements.DoLog(const Msg: String);
  513. begin
  514. if Assigned(FOnLog) then
  515. FOnLog(Self,Msg);
  516. end;
  517. procedure THTMLToFormELements.DoLog(const Fmt: String; Args: array of const);
  518. begin
  519. DoLog(Format(Fmt,Args));
  520. end;
  521. function THTMLToFormELements.Maptype(aTag: SAXString; Atts: TSAXAttributes): String;
  522. var
  523. t : string;
  524. m : THTMLElementMap;
  525. begin
  526. Result:='';
  527. if Map.Count>0 then
  528. begin
  529. M:=Map.FindMap(aTag,Atts);
  530. if Assigned(m) then
  531. Exit(M.ControlClass)
  532. else if not DefaultElements then
  533. begin
  534. DoLog('Could not map tag %s',[aTag]);
  535. Exit;
  536. end;
  537. end;
  538. t:=lowercase(Utf8Encode(aTag));
  539. case t of
  540. 'input' : Result:='TJSHTMLInputElement';
  541. 'button' : Result:='TJSHTMLButtonElement';
  542. 'select' : Result:='TJSHTMLSelectElement';
  543. 'textarea' : Result:='TJSHTMLTextAreaElement';
  544. 'option' : Result:='';
  545. else
  546. Result:='TJSHTMLElement';
  547. end;
  548. end;
  549. function THTMLToFormELements.MakeValidName(aID: string): string;
  550. Var
  551. C : Char;
  552. begin
  553. Result:='';
  554. for C in aID do
  555. if C in ['_','a'..'z','A'..'Z','0'..'9'] then
  556. Result:=Result+C
  557. else
  558. Result:=Result+'_';
  559. end;
  560. procedure THTMLToFormELements.SetExcludeIDS(AValue: TStrings);
  561. begin
  562. if FExcludeIDS=AValue then Exit;
  563. FExcludeIDs.AddStrings(AValue,True);
  564. end;
  565. procedure THTMLToFormELements.DoStartElement(Sender: TObject;
  566. const NamespaceURI, LocalName, QName: SAXString; Atts: TSAXAttributes);
  567. Var
  568. aID,aType : String;
  569. El : TFormElement;
  570. begin
  571. if Not Assigned(atts) then exit;
  572. aID:=UTF8Encode(Atts.GetValue('','id'));
  573. if (aID='') or (FExcludeIDS.IndexOf(aID)>=0) then
  574. exit;
  575. if (Level=0) and (BelowID=aID) then
  576. Level:=1
  577. else if (BelowID<>'') and (Level<=0) then
  578. Exit;
  579. aType:=MapType(LocalName,Atts);
  580. if aType='' then
  581. DoLog('Ignoring tag %s with id %s',[LocalName,aID])
  582. else
  583. begin
  584. El:=FormElements.Add(MakeValidName(aID));
  585. EL.ElementType:=aType;
  586. EL.HTMLID:=aId;
  587. GetEvents(El,Atts);
  588. end
  589. end;
  590. procedure THTMLToFormELements.GetEvents(aEl : TFormElement; Atts : TSAXAttributes);
  591. Var
  592. I,aLen : Integer;
  593. aName : string;
  594. begin
  595. for I:=0 to Atts.Length-1 do
  596. begin
  597. aName:=UTF8Encode(Atts.GetLocalName(i));
  598. aLen:=Length(aName);
  599. if (aLen>3) and (Copy(aName,1,1)='_') and (Copy(aName,aLen,1)='_') then
  600. aEl.Events.Add(Copy(aName,2,aLen-2)+'='+UTF8Encode(Atts.GetValue(i)));
  601. end;
  602. end;
  603. procedure THTMLToFormELements.DoEndElement(Sender: TObject; const NamespaceURI,
  604. LocalName, QName: SAXString);
  605. begin
  606. if Level>0 then
  607. Dec(FLevel);
  608. end;
  609. class function THTMLToFormELements.CreateElementList: TFormElementList;
  610. begin
  611. Result:=TFormElementList.Create(TFormElement);
  612. end;
  613. function THTMLToFormELements.CreateHTMLElementMapList: THTMLElementMapList;
  614. begin
  615. Result:=THTMLElementMapList.Create(THTMLElementMap);
  616. end;
  617. constructor THTMLToFormELements.Create(aOwner: TComponent);
  618. begin
  619. inherited Create(aOwner);
  620. FMap:=CreateHTMLElementMapList;
  621. FFormElements:=CreateElementList;
  622. FExcludeIDS:=TStringList.Create;
  623. TStringList(FExcludeIDS).Sorted:=True;
  624. end;
  625. destructor THTMLToFormELements.Destroy;
  626. begin
  627. FreeAndNil(FMap);
  628. FreeAndNil(FExcludeIDS);
  629. FreeAndNil(FFormElements);
  630. inherited Destroy;
  631. end;
  632. procedure THTMLToFormELements.Clear;
  633. begin
  634. FFormElements.Clear;
  635. end;
  636. procedure THTMLToFormELements.LoadFromStream(aInput: TStream);
  637. var
  638. MyReader : THTMLReader;
  639. begin
  640. MyReader:=THTMLReader.Create;
  641. Try
  642. MyReader.OnStartElement:=@DoStartElement;
  643. MyReader.OnEndElement:=@DoEndElement;
  644. MyReader.ParseStream(aInput);
  645. finally
  646. FreeAndNil(MyReader);
  647. end;
  648. end;
  649. procedure THTMLToFormELements.LoadFromFile(const aFileName: String);
  650. var
  651. F : TFileStream;
  652. begin
  653. F:=TFileStream.Create(aFileName,fmOpenRead or fmShareDenyWrite);
  654. try
  655. LoadFromStream(F);
  656. finally
  657. F.Free;
  658. end;
  659. end;
  660. { TFormCodeGen }
  661. procedure TFormCodeGen.SetFormElements(AValue: TFormElementList);
  662. begin
  663. if FFormElements=AValue then Exit;
  664. FFormElements.Assign(AValue);
  665. end;
  666. function TFormCodeGen.BaseUnits: String;
  667. begin
  668. Result:='js, web';
  669. end;
  670. class function TFormCodeGen.CreateElementList: TFormElementList;
  671. begin
  672. Result:=TFormElementList.Create(TFormElement);
  673. end;
  674. constructor TFormCodeGen.Create(aOwner: TComponent);
  675. begin
  676. inherited Create(aOwner);
  677. // Assume class is TComponent descendant
  678. FConstructorArgs:='aOwner : TComponent';
  679. FFormElements:=CreateElementList;
  680. FormClassName:='TMyForm';
  681. ParentClassName:='TComponent';
  682. EventSignature:='Event : TJSEvent';
  683. EventModifiers:='virtual; abstract;';
  684. GetElementFunction:='document.getelementByID';
  685. AddMethods:=[smConstructor,smBindElements,smBindElementEvents];
  686. VirtualMethods:=[smBindElementEvents,smBindElements];
  687. OverrideMethods:=[smConstructor];
  688. Options:=[foBindInConstructor];
  689. fFormFileGenerator:=CreateFormFileGen;
  690. FFormSource:=TStringList.Create;
  691. end;
  692. destructor TFormCodeGen.Destroy;
  693. begin
  694. FreeAndNil(FFormSource);
  695. FreeAndNil(fFormFileGenerator) ;
  696. FreeAndNil(FFormElements);
  697. inherited Destroy;
  698. end;
  699. procedure TFormCodeGen.EmitFormElement(aEL : TFormElement);
  700. begin
  701. With aEl do
  702. AddLn('%s : %s;',[Name,ElementType]) ;
  703. end;
  704. procedure TFormCodeGen.EmitFormEvents(aEL : TFormElement);
  705. Var
  706. S,EN,EH : String;
  707. begin
  708. if not aEl.HasEvents then
  709. exit;
  710. For S in aEl.Events do
  711. begin
  712. GetEventNameAndHandler(S,aEl.Name,EN,EH);
  713. Addln('Procedure %s(%s); %s',[EH, EventSignature,EventModifiers]);
  714. end;
  715. end;
  716. procedure TFormCodeGen.EmitPublishedSection;
  717. var
  718. I : Integer;
  719. begin
  720. For I:=0 to FormElements.Count-1 do
  721. EmitFormElement(FormElements[i]);
  722. if foEvents in Options then
  723. For I:=0 to FormElements.Count-1 do
  724. EmitFormEvents(FormElements[i]);
  725. end;
  726. function TFormCodeGen.VirtualOverride(M: TSpecialMethod; const Decl: String): string;
  727. begin
  728. Result:=Decl;
  729. if M in OverrideMethods then
  730. Result:=Result+' override;'
  731. else if M in VirtualMethods then
  732. Result:=Result+' virtual;'
  733. end;
  734. procedure TFormCodeGen.EmitPublicSection;
  735. begin
  736. if smConstructor in AddMethods then
  737. Addln(VirtualOverride(smConstructor,'Constructor create('+ConstructorArgs+');'));
  738. if smBindElements in AddMethods then
  739. Addln(VirtualOverride(smBindElements, 'Procedure BindElements;'));
  740. if (smBindElementEvents in AddMethods) and (foEvents in Options) then
  741. Addln(VirtualOverride(smBindElementEvents,'Procedure BindElementEvents;'));
  742. end;
  743. procedure TFormCodeGen.Execute;
  744. begin
  745. Source.Clear;
  746. Addln('unit %s;',[OutputUnitName]);
  747. CreateHeader;
  748. Addln('Type');
  749. Indent;
  750. ClassHeader(FormClassName);
  751. AddLn('%s = class(%s) ',[FormClassName,ParentClassName]);
  752. Addln('Published');
  753. Indent;
  754. EmitPublishedSection;
  755. Undent;
  756. Addln('Public');
  757. Indent;
  758. EmitPublicSection;
  759. Undent;
  760. Addln('end;');
  761. Undent;
  762. Addln('');
  763. Addln('implementation');
  764. AddLn('');
  765. if (foFormFile in Options) then
  766. begin
  767. EmitFormFile;
  768. AddLn('');
  769. AddLn('{$R *.dfm}');
  770. AddLn('');
  771. end;
  772. ClassHeader(FormClassName);
  773. EmitImplementation;
  774. AddLn('');
  775. AddLn('end.');
  776. end;
  777. procedure TFormCodeGen.EmitFormFile;
  778. begin
  779. FormFileGenerator.FormElements:=Self.FormElements;
  780. FormFileGenerator.DoEvents:=foEvents in Options;
  781. FormFileGenerator.FormClassName:=Self.FormClassName;
  782. FormFileGenerator.Execute;
  783. FormSource.Assign(FormFileGenerator.Source);
  784. end;
  785. function TFormCodeGen.CreateFormFileGen: TFormFileCodeGen;
  786. begin
  787. Result:=TFormFileCodeGen.Create(Nil);
  788. end;
  789. function TFormCodeGen.CreateHTMLToFormELements: THTMLToFormELements;
  790. begin
  791. Result:=THTMLToFormELements.Create(Self);
  792. end;
  793. procedure TFormCodeGen.EmitFormConstructor;
  794. begin
  795. Addln('');
  796. Addln('Constructor %s.create(aOwner : TComponent);',[FormClassName]);
  797. if not (foBindInConstructor in Options) then
  798. SimpleMethodBody(['Inherited;'])
  799. else
  800. begin
  801. if foEvents in Options then
  802. SimpleMethodBody(['Inherited;','BindElements;','BindElementEvents;'])
  803. else
  804. SimpleMethodBody(['Inherited;','BindElements;']);
  805. end;
  806. Addln('');
  807. end;
  808. procedure TFormCodeGen.EmitImplementation;
  809. begin
  810. if smConstructor in AddMethods then
  811. EmitFormConstructor;
  812. if (smBindElements in AddMethods) then
  813. EmitFormBindElements;
  814. if (foEvents in Options) and Not (foFormFile in Options) and (smBindElementEvents in AddMethods) then
  815. EmitFormBindEvents;
  816. end;
  817. procedure TFormCodeGen.EmitFormBindElements;
  818. var
  819. I : integer;
  820. El : TFormElement;
  821. begin
  822. Addln('');
  823. Addln('Procedure %s.BindElements;',[FormClassName]);
  824. Addln('');
  825. AddLn('begin');
  826. Indent;
  827. if smBindElements in OverrideMethods then
  828. AddLn('inherited;');
  829. For I:=0 to FormElements.Count-1 do
  830. begin
  831. el:=FormElements[i];
  832. With El do
  833. Addln('%s:=%s(%s(''%s''));',[Name,ElementType,GetElementFunction,HTMLID]);
  834. end;
  835. Undent;
  836. Addln('end;');
  837. Addln('');
  838. end;
  839. class function TFormCodeGen.Pretty(const S: String): string;
  840. begin
  841. Result:=UpperCase(Copy(S,1,1))+LowerCase(Copy(S,2,Length(S)-1));
  842. end;
  843. class procedure TFormCodeGen.GetEventNameAndHandler(const S,
  844. aFieldName: String; out aName, aHandler: string);
  845. Var
  846. P : Integer;
  847. begin
  848. P:=Pos('=',S);
  849. if (P=0) then
  850. P:=Length(S)+1;
  851. aName:=Copy(S,1,P-1);
  852. aHandler:=Copy(S,P+1,Length(S)-P);
  853. if AHandler='' then
  854. aHandler:=aFieldName+Pretty(aName);
  855. // Writeln(aFieldName,': ',S,' -> ',aName,' & ',aHandler);
  856. end;
  857. procedure TFormCodeGen.EmitFormBindEvents;
  858. var
  859. I : integer;
  860. El : TFormElement;
  861. S,EN,EH : String;
  862. begin
  863. Addln('Procedure %s.BindElementEvents;',[FormClassName]);
  864. Addln('');
  865. AddLn('begin');
  866. Indent;
  867. if smBindElementEvents in OverrideMethods then
  868. AddLn('inherited;');
  869. For I:=0 to FormElements.Count-1 do
  870. begin
  871. el:=FormElements[i];
  872. With El do
  873. if HasEvents then
  874. For S in El.Events do
  875. begin
  876. GetEventNameAndHandler(S,Name,EN,EH);
  877. Addln('%s.AddEventListener(''%s'',@%s);',[Name,EN,EH]);
  878. end;
  879. end;
  880. Undent;
  881. Addln('end;');
  882. end;
  883. { TFormElementList }
  884. function TFormElementList.GetEl(aIndex : Integer): TFormElement;
  885. begin
  886. Result:=Items[aIndex] as TFormElement;
  887. end;
  888. function TFormElementList.Add(const aName: string): TFormElement;
  889. begin
  890. if IndexOf(aName)<>-1 then
  891. Raise Exception.CreateFmt('Duplicate name : %s' ,[aName]);
  892. Result:=(Inherited Add) as TFormElement;
  893. Result.Name:=aName;
  894. end;
  895. function TFormElementList.IndexOf(const aName: string): Integer;
  896. begin
  897. Result:=Count-1;
  898. While (Result>=0) and Not SameText(aName,GetEl(Result).Name) do
  899. Dec(Result);
  900. end;
  901. function TFormElementList.Find(const aName: string): TFormElement;
  902. var
  903. Idx : Integer;
  904. begin
  905. Idx:=IndexOf(aName);
  906. if Idx>=0 then
  907. Result:=GetEl(Idx)
  908. else
  909. Result:=Nil;
  910. end;
  911. { TFormElement }
  912. function TFormElement.GetEvents: TStrings;
  913. begin
  914. If (FEvents=Nil) then
  915. FEvents:=TStringList.Create;
  916. Result:=FEvents;
  917. end;
  918. function TFormElement.getName: String;
  919. begin
  920. Result:=FName;
  921. if Result='' then
  922. Result:=HTMLID;
  923. end;
  924. procedure TFormElement.SetEvents(AValue: TStrings);
  925. begin
  926. If AValue=FEVents then exit;
  927. Events.Assign(aValue);
  928. end;
  929. destructor TFormElement.Destroy;
  930. begin
  931. FreeAndNil(FEvents);
  932. inherited Destroy;
  933. end;
  934. function TFormElement.HasEvents: Boolean;
  935. begin
  936. Result:=Assigned(FEvents) and (FEvents.Count>0);
  937. end;
  938. procedure TFormElement.Assign(Source: TPersistent);
  939. Var
  940. FE : TFormElement absolute Source;
  941. begin
  942. if Source is TFormElement then
  943. begin
  944. FHTMLID:=FE.HTMLID;
  945. FName:=FE.FName;
  946. FType:=FE.FType;
  947. if FE.HasEvents then
  948. Events:=FE.Events;
  949. end
  950. else
  951. inherited Assign(Source);
  952. end;
  953. end.