Added rating and indicator in "instant_search_with_htmx"

This commit is contained in:
Daniele Teti 2024-09-20 00:17:30 +02:00
parent 4b8b89d4da
commit 34238a48ee
9 changed files with 178 additions and 139 deletions

Binary file not shown.

View File

@ -3,5 +3,10 @@ CREATE TABLE books (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
book_name TEXT NOT NULL,
author_name TEXT NOT NULL,
genre TEXT NOT NULL
);
genre TEXT NOT NULL,
rating INTEGER DEFAULT (0) NOT NULL);
/* fill data and then generate rating using the following update */
update books set rating = abs(random() % 6)

View File

@ -62,8 +62,6 @@ begin
// When MVCSerializeNulls = False empty nullables and nil are not serialized at all.
MVCSerializeNulls := True;
MVCUseTemplatesCache := False;
try
if WebRequestHandler <> nil then
WebRequestHandler.WebModuleClass := WebModuleClass;

View File

@ -1,7 +1,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{563921D8-AB80-4E14-AD4E-36870C7E2008}</ProjectGuid>
<ProjectVersion>20.1</ProjectVersion>
<ProjectVersion>20.2</ProjectVersion>
<FrameworkType>VCL</FrameworkType>
<MainSource>htmx_templatepro.dpr</MainSource>
<Base>True</Base>
@ -125,14 +125,9 @@
<Excluded_Packages Name="$(BDSBIN)\bcbofficexp290.bpl">Embarcadero C++Builder Office XP Servers Package</Excluded_Packages>
</Excluded_Packages>
</Delphi.Personality>
<Deployment Version="4">
<Deployment Version="5">
<DeployFile LocalName="bin\ServerSideViews.exe" Configuration="Debug" Class="ProjectOutput"/>
<DeployFile LocalName="bin\htmx_templatepro.exe" Configuration="Debug" Class="ProjectOutput">
<Platform Name="Win32">
<RemoteName>htmx_templatepro.exe</RemoteName>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="bin\htmx_templatepro.exe" Configuration="Debug" Class="ProjectOutput"/>
<DeployClass Name="AdditionalDebugSymbols">
<Platform Name="OSX32">
<Operation>1</Operation>
@ -141,16 +136,6 @@
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidClasses">
<Platform Name="Android">
<RemoteDir>classes</RemoteDir>
<Operation>64</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>classes</RemoteDir>
<Operation>64</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidFileProvider">
<Platform Name="Android">
<RemoteDir>res\xml</RemoteDir>
@ -161,12 +146,6 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidGDBServer">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidLibnativeArmeabiFile">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi</RemoteDir>

View File

@ -14,6 +14,16 @@
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Android' and '$(Base)'=='true') or '$(Base_Android)'!=''">
<Base_Android>true</Base_Android>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Android64' and '$(Base)'=='true') or '$(Base_Android64)'!=''">
<Base_Android64>true</Base_Android64>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Linux64' and '$(Base)'=='true') or '$(Base_Linux64)'!=''">
<Base_Linux64>true</Base_Linux64>
<CfgParent>Base</CfgParent>
@ -63,6 +73,16 @@
<VerInfo_Locale>1040</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Android)'!=''">
<VerInfo_Keys>package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=;minSdkVersion=23;targetSdkVersion=34</VerInfo_Keys>
<BT_BuildType>Debug</BT_BuildType>
<EnabledSysJars>activity-1.7.2.dex.jar;annotation-experimental-1.3.0.dex.jar;annotation-jvm-1.6.0.dex.jar;annotations-13.0.dex.jar;appcompat-1.2.0.dex.jar;appcompat-resources-1.2.0.dex.jar;billing-6.0.1.dex.jar;biometric-1.1.0.dex.jar;browser-1.4.0.dex.jar;cloud-messaging.dex.jar;collection-1.1.0.dex.jar;concurrent-futures-1.1.0.dex.jar;core-1.10.1.dex.jar;core-common-2.2.0.dex.jar;core-ktx-1.10.1.dex.jar;core-runtime-2.2.0.dex.jar;cursoradapter-1.0.0.dex.jar;customview-1.0.0.dex.jar;documentfile-1.0.0.dex.jar;drawerlayout-1.0.0.dex.jar;error_prone_annotations-2.9.0.dex.jar;exifinterface-1.3.6.dex.jar;firebase-annotations-16.2.0.dex.jar;firebase-common-20.3.1.dex.jar;firebase-components-17.1.0.dex.jar;firebase-datatransport-18.1.7.dex.jar;firebase-encoders-17.0.0.dex.jar;firebase-encoders-json-18.0.0.dex.jar;firebase-encoders-proto-16.0.0.dex.jar;firebase-iid-interop-17.1.0.dex.jar;firebase-installations-17.1.3.dex.jar;firebase-installations-interop-17.1.0.dex.jar;firebase-measurement-connector-19.0.0.dex.jar;firebase-messaging-23.1.2.dex.jar;fmx.dex.jar;fragment-1.2.5.dex.jar;google-play-licensing.dex.jar;interpolator-1.0.0.dex.jar;javax.inject-1.dex.jar;kotlin-stdlib-1.8.22.dex.jar;kotlin-stdlib-common-1.8.22.dex.jar;kotlin-stdlib-jdk7-1.8.22.dex.jar;kotlin-stdlib-jdk8-1.8.22.dex.jar;kotlinx-coroutines-android-1.6.4.dex.jar;kotlinx-coroutines-core-jvm-1.6.4.dex.jar;legacy-support-core-utils-1.0.0.dex.jar;lifecycle-common-2.6.1.dex.jar;lifecycle-livedata-2.6.1.dex.jar;lifecycle-livedata-core-2.6.1.dex.jar;lifecycle-runtime-2.6.1.dex.jar;lifecycle-service-2.6.1.dex.jar;lifecycle-viewmodel-2.6.1.dex.jar;lifecycle-viewmodel-savedstate-2.6.1.dex.jar;listenablefuture-1.0.dex.jar;loader-1.0.0.dex.jar;localbroadcastmanager-1.0.0.dex.jar;okio-jvm-3.4.0.dex.jar;play-services-ads-22.2.0.dex.jar;play-services-ads-base-22.2.0.dex.jar;play-services-ads-identifier-18.0.0.dex.jar;play-services-ads-lite-22.2.0.dex.jar;play-services-appset-16.0.1.dex.jar;play-services-base-18.1.0.dex.jar;play-services-basement-18.1.0.dex.jar;play-services-cloud-messaging-17.0.1.dex.jar;play-services-location-21.0.1.dex.jar;play-services-maps-18.1.0.dex.jar;play-services-measurement-base-20.1.2.dex.jar;play-services-measurement-sdk-api-20.1.2.dex.jar;play-services-stats-17.0.2.dex.jar;play-services-tasks-18.0.2.dex.jar;print-1.0.0.dex.jar;profileinstaller-1.3.0.dex.jar;room-common-2.2.5.dex.jar;room-runtime-2.2.5.dex.jar;savedstate-1.2.1.dex.jar;sqlite-2.1.0.dex.jar;sqlite-framework-2.1.0.dex.jar;startup-runtime-1.1.1.dex.jar;tracing-1.0.0.dex.jar;transport-api-3.0.0.dex.jar;transport-backend-cct-3.1.8.dex.jar;transport-runtime-3.1.8.dex.jar;user-messaging-platform-2.0.0.dex.jar;vectordrawable-1.1.0.dex.jar;vectordrawable-animated-1.1.0.dex.jar;versionedparcelable-1.1.1.dex.jar;viewpager-1.0.0.dex.jar;work-runtime-2.7.0.dex.jar</EnabledSysJars>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Android64)'!=''">
<VerInfo_Keys>package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=;minSdkVersion=23;targetSdkVersion=34</VerInfo_Keys>
<BT_BuildType>Debug</BT_BuildType>
<EnabledSysJars>activity-1.7.2.dex.jar;annotation-experimental-1.3.0.dex.jar;annotation-jvm-1.6.0.dex.jar;annotations-13.0.dex.jar;appcompat-1.2.0.dex.jar;appcompat-resources-1.2.0.dex.jar;billing-6.0.1.dex.jar;biometric-1.1.0.dex.jar;browser-1.4.0.dex.jar;cloud-messaging.dex.jar;collection-1.1.0.dex.jar;concurrent-futures-1.1.0.dex.jar;core-1.10.1.dex.jar;core-common-2.2.0.dex.jar;core-ktx-1.10.1.dex.jar;core-runtime-2.2.0.dex.jar;cursoradapter-1.0.0.dex.jar;customview-1.0.0.dex.jar;documentfile-1.0.0.dex.jar;drawerlayout-1.0.0.dex.jar;error_prone_annotations-2.9.0.dex.jar;exifinterface-1.3.6.dex.jar;firebase-annotations-16.2.0.dex.jar;firebase-common-20.3.1.dex.jar;firebase-components-17.1.0.dex.jar;firebase-datatransport-18.1.7.dex.jar;firebase-encoders-17.0.0.dex.jar;firebase-encoders-json-18.0.0.dex.jar;firebase-encoders-proto-16.0.0.dex.jar;firebase-iid-interop-17.1.0.dex.jar;firebase-installations-17.1.3.dex.jar;firebase-installations-interop-17.1.0.dex.jar;firebase-measurement-connector-19.0.0.dex.jar;firebase-messaging-23.1.2.dex.jar;fmx.dex.jar;fragment-1.2.5.dex.jar;google-play-licensing.dex.jar;interpolator-1.0.0.dex.jar;javax.inject-1.dex.jar;kotlin-stdlib-1.8.22.dex.jar;kotlin-stdlib-common-1.8.22.dex.jar;kotlin-stdlib-jdk7-1.8.22.dex.jar;kotlin-stdlib-jdk8-1.8.22.dex.jar;kotlinx-coroutines-android-1.6.4.dex.jar;kotlinx-coroutines-core-jvm-1.6.4.dex.jar;legacy-support-core-utils-1.0.0.dex.jar;lifecycle-common-2.6.1.dex.jar;lifecycle-livedata-2.6.1.dex.jar;lifecycle-livedata-core-2.6.1.dex.jar;lifecycle-runtime-2.6.1.dex.jar;lifecycle-service-2.6.1.dex.jar;lifecycle-viewmodel-2.6.1.dex.jar;lifecycle-viewmodel-savedstate-2.6.1.dex.jar;listenablefuture-1.0.dex.jar;loader-1.0.0.dex.jar;localbroadcastmanager-1.0.0.dex.jar;okio-jvm-3.4.0.dex.jar;play-services-ads-22.2.0.dex.jar;play-services-ads-base-22.2.0.dex.jar;play-services-ads-identifier-18.0.0.dex.jar;play-services-ads-lite-22.2.0.dex.jar;play-services-appset-16.0.1.dex.jar;play-services-base-18.1.0.dex.jar;play-services-basement-18.1.0.dex.jar;play-services-cloud-messaging-17.0.1.dex.jar;play-services-location-21.0.1.dex.jar;play-services-maps-18.1.0.dex.jar;play-services-measurement-base-20.1.2.dex.jar;play-services-measurement-sdk-api-20.1.2.dex.jar;play-services-stats-17.0.2.dex.jar;play-services-tasks-18.0.2.dex.jar;print-1.0.0.dex.jar;profileinstaller-1.3.0.dex.jar;room-common-2.2.5.dex.jar;room-runtime-2.2.5.dex.jar;savedstate-1.2.1.dex.jar;sqlite-2.1.0.dex.jar;sqlite-framework-2.1.0.dex.jar;startup-runtime-1.1.1.dex.jar;tracing-1.0.0.dex.jar;transport-api-3.0.0.dex.jar;transport-backend-cct-3.1.8.dex.jar;transport-runtime-3.1.8.dex.jar;user-messaging-platform-2.0.0.dex.jar;vectordrawable-1.1.0.dex.jar;vectordrawable-animated-1.1.0.dex.jar;versionedparcelable-1.1.1.dex.jar;viewpager-1.0.0.dex.jar;work-runtime-2.7.0.dex.jar</EnabledSysJars>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Linux64)'!=''">
<DCC_UsePackage>DataSnapServer;fmx;emshosting;DbxCommonDriver;bindengine;FireDACCommonODBC;emsclient;FireDACCommonDriver;IndyProtocols;dbxcds;RadiantShapesFmx_Design;emsedge;inetdb;FireDACSqliteDriver;DbxClientDriver;FireDACASADriver;soapmidas;dbexpress;Python;FireDACInfxDriver;inet;DataSnapCommon;dbrtl;FireDACOracleDriver;CustomIPTransport;FireDACMSSQLDriver;DataSnapIndy10ServerTransport;DataSnapConnectors;FireDACMongoDBDriver;IndySystem;RadiantShapesFmx;FireDACTDataDriver;bindcomp;FireDACCommon;DataSnapServerMidas;FireDACODBCDriver;emsserverresource;IndyCore;RESTBackendComponents;rtl;FireDACMySQLDriver;FireDACADSDriver;RESTComponents;dsnapxml;DataSnapClient;DataSnapFireDAC;emsclientfiredac;FireDACPgDriver;FireDAC;xmlrtl;dsnap;CloudService;FireDACDb2Driver;DataSnapNativeClient;DatasnapConnectorsFreePascal;soaprtl;soapserver;FireDACIBDriver;$(DCC_UsePackage)</DCC_UsePackage>
</PropertyGroup>
@ -995,6 +1015,8 @@
<ProjectRoot Platform="Win64x" Name="$(PROJECTNAME)"/>
</Deployment>
<Platforms>
<Platform value="Android">False</Platform>
<Platform value="Android64">False</Platform>
<Platform value="Linux64">False</Platform>
<Platform value="Win32">True</Platform>
<Platform value="Win64">False</Platform>

View File

@ -32,7 +32,7 @@ var
lBaseSelect, lOrdering: String;
begin
SearchQueryText := SearchQueryText.ToLower;
lBaseSelect := 'select id, book_name, author_name, genre from books';
lBaseSelect := 'select id, book_name, author_name, genre, rating from books';
lOrdering := 'order by book_name COLLATE NOCASE';
if SearchQueryText.IsEmpty then
begin

View File

@ -7,17 +7,18 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css" />
<script src="https://cdn.jsdelivr.net/npm/htmx.org/dist/htmx.min.js"></script>
</head>
<body>
<section class="section">
<body hx-indicator="#spinner">
<section class="section" >
<div class="columns">
<div class="column is-one-third is-offset-one-third">
<input type="text" class="input" placeholder="Search" name="query" hx-get="/search"
<input type="text" class="input" placeholder="Search" name="query" hx-get="/search"
hx-trigger="keyup changed delay:500ms" hx-target="#results" hx-push-url="true" />
</div>
<div class="column is-one-third">
<p class="subtitle is-6">⭐ DMVCFramework + HTMX :: Instant Search Demo ⭐</p>
</div>
</div>
<progress id="spinner" class="htmx-indicator progress is-large is-info" max="100">ciao</progress>
<div id="results">{{include "search_results.html"}}</div>
</section>
</body>

View File

@ -5,6 +5,7 @@
<th>ID</th>
<th>Book Title</th>
<th>Book Author</th>
<th>Rating</th>
<th>Genre</th>
</tr>
</thead>
@ -30,9 +31,14 @@
<td>{{:book.id|lpad,8,"0"}}</td>
<td>{{:book.book_name}}</td>
<td>{{:book.author_name}}</td>
<td
>
<td>
{{if book.rating|ge,1}}⭐{{endif}}
{{if book.rating|ge,2}}⭐{{endif}}
{{if book.rating|ge,3}}⭐{{endif}}
{{if book.rating|ge,4}}⭐{{endif}}
{{if book.rating|ge,5}}⭐{{endif}}
</td>
<td>
{{:book.genre|uppercase}}
</td>
</tr>

View File

@ -34,7 +34,7 @@ uses
System.RTTI;
const
TEMPLATEPRO_VERSION = '0.6';
TEMPLATEPRO_VERSION = '0.6.1';
type
ETProException = class(Exception)
@ -59,11 +59,11 @@ type
TTokenType = (
ttContent, ttInclude, ttFor, ttEndFor, ttIfThen, ttBoolExpression, ttElse, ttEndIf, ttStartTag, ttComment,
ttLiteralString, ttEndTag, ttValue, ttFilterName, ttFilterParameter, ttLineBreak, ttSystemVersion, ttEOF);
ttContinue, ttLiteralString, ttEndTag, ttValue, ttFilterName, ttFilterParameter, ttLineBreak, ttSystemVersion, ttExit, ttEOF);
const
TOKEN_TYPE_DESCR: array [Low(TTokenType)..High(TTokenType)] of string =
('ttContent', 'ttInclude', 'ttFor', 'ttEndFor', 'ttIfThen', 'ttBoolExpression', 'ttElse', 'ttEndIf', 'ttStartTag', 'ttComment',
'ttLiteralString', 'ttEndTag', 'ttValue', 'ttFilterName', 'ttFilterParameter', 'ttLineBreak', 'ttSystemVersion', 'ttEOF');
'ttContinue', 'ttLiteralString', 'ttEndTag', 'ttValue', 'ttFilterName', 'ttFilterParameter', 'ttLineBreak', 'ttSystemVersion', 'ttExit', 'ttEOF');
type
TToken = packed record
TokenType: TTokenType;
@ -144,7 +144,7 @@ type
function GetVarAsString(const Name: string): string;
function GetTValueVarAsString(const Value: TValue; const VarName: string = ''): String;
function GetVarAsTValue(const aName: string): TValue;
// function EvaluateIfExpression(aIdentifier: string): Boolean;
function GetDataSetFieldAsTValue(const aDataSet: TDataSet; const FieldName: String): TValue;
function EvaluateIfExpressionAt(var Idx: UInt64): Boolean;
function GetVariables: TTProVariables;
procedure SplitVariableName(const VariableWithMember: String; out VarName, VarMembers: String);
@ -289,16 +289,25 @@ begin
fTemplateFunctions.Add(FunctionName.ToLower, FunctionImpl);
end;
function TTProCompiledTemplate.GetDataSetFieldAsTValue(const aDataSet: TDataSet; const FieldName: String): TValue;
var
lField: TField;
begin
lField := aDataSet.FieldByName(FieldName);
case lField.DataType of
ftInteger: Result := lField.AsInteger;
ftLargeint, ftAutoInc: Result := lField.AsLargeInt;
ftString, ftWideString, ftMemo, ftWideMemo: Result := lField.AsWideString;
else
Error('Invalid data type for field "%s": %s', [FieldName, TRttiEnumerationType.GetName<TFieldType>(lField.DataType)]);
end;
end;
function TTProCompiledTemplate.GetOnGetValue: TTProCompiledTemplateGetValueEvent;
begin
Result := fOnGetValue;
end;
//function TTProCompiledTemplate.GetPseudoVariable(const Variable: TVarDataSource; const PseudoVarName: String): TValue;
//begin
// Result := GetPseudoVariable(Variable.VarIterator, PseudoVarName);
//end;
function TTProCompiledTemplate.GetPseudoVariable(const VarIterator: Integer; const PseudoVarName: String): TValue;
begin
if PseudoVarName = '@@index' then
@ -838,6 +847,10 @@ begin
aTokens.Add(TToken.Create(lLastToken, '', ''));
Dec(lForStatementCount);
lStartVerbatim := fCharIndex;
end else if MatchSymbol('continue') then {continue}
begin
lLastToken := ttContinue;
aTokens.Add(TToken.Create(lLastToken, '', ''));
end else if MatchSymbol('endif') then {endif}
begin
if lIfStatementCount = -1 then
@ -951,6 +964,8 @@ begin
end
else if MatchSymbol('exit') then {exit}
begin
lLastToken := ttExit;
aTokens.Add(TToken.Create(lLastToken, '', ''));
lLastToken := ttEOF;
aTokens.Add(TToken.Create(lLastToken, '', ''));
Break;
@ -1020,69 +1035,108 @@ end;
procedure TTProCompiler.FixJumps(const aTokens: TList<TToken>);
var
lForInStack: TStack<UInt64>;
lContinueStack: TStack<UInt64>;
lIfStatementStack: TStack<TIfThenElseIndex>;
I: UInt64;
lToken: TToken;
lForAddress: UInt64;
lIfStackItem: TIfThenElseIndex;
lCheckForUnbalancedPair: Boolean;
lTmpContinueAddress: UInt64;
begin
lCheckForUnbalancedPair := True;
lForInStack := TStack<UInt64>.Create;
try
lIfStatementStack := TStack<TIfThenElseIndex>.Create;
lContinueStack := TStack<UInt64>.Create;
try
for I := 0 to aTokens.Count - 1 do
begin
case aTokens[I].TokenType of
ttFor: begin
lForInStack.Push(I);
end;
ttEndFor: begin
{ttFor.Ref1 --> endfor}
lForAddress := lForInStack.Pop;
lToken := aTokens[lForAddress];
lToken.Ref1 := I;
aTokens[lForAddress] := lToken;
lIfStatementStack := TStack<TIfThenElseIndex>.Create;
try
for I := 0 to aTokens.Count - 1 do
begin
case aTokens[I].TokenType of
ttFor: begin
if lContinueStack.Count > 0 then
begin
Error('Continue stack corrupted');
end;
lForInStack.Push(I);
end;
{ttEndFor.Ref1 --> for}
lToken := aTokens[I];
lToken.Ref1 := lForAddress;
aTokens[I] := lToken;
end;
ttEndFor: begin
{ttFor.Ref1 --> endfor}
lForAddress := lForInStack.Pop;
lToken := aTokens[lForAddress];
lToken.Ref1 := I;
aTokens[lForAddress] := lToken;
{ttIfThen.Ref1 points always to relative else (if present otherwise -1)}
{ttIfThen.Ref2 points always to relative endif}
{ttEndFor.Ref1 --> for}
lToken := aTokens[I];
lToken.Ref1 := lForAddress;
aTokens[I] := lToken;
ttIfThen: begin
lIfStackItem.IfIndex := I;
lIfStackItem.ElseIndex := -1; {-1 means: "there isn't ttElse"}
lIfStatementStack.Push(lIfStackItem);
end;
ttElse: begin
lIfStackItem := lIfStatementStack.Pop;
lIfStackItem.ElseIndex := I;
lIfStatementStack.Push(lIfStackItem);
end;
ttEndIf: begin
lIfStackItem := lIfStatementStack.Pop;
{if there's a ttContinue (or more than one), it must jump to endfor}
while lContinueStack.Count > 0 do
begin
lTmpContinueAddress := lContinueStack.Pop;
lToken := aTokens[lTmpContinueAddress];
lToken.Ref1 := I;
aTokens[lTmpContinueAddress] := lToken;
end;
end;
{fixup ifthen}
lToken := aTokens[lIfStackItem.IfIndex];
lToken.Ref2 := I; {ttIfThen.Ref2 points always to relative endif}
lToken.Ref1 := lIfStackItem.ElseIndex; {ttIfThen.Ref1 points always to relative else (if present, otherwise -1)}
aTokens[lIfStackItem.IfIndex] := lToken;
ttContinue: begin
lContinueStack.Push(I);
end;
{fixup else}
if lIfStackItem.ElseIndex > -1 then
begin
lToken := aTokens[lIfStackItem.ElseIndex];
lToken.Ref2 := I; {ttElse.Ref2 points always to relative endif}
aTokens[lIfStackItem.ElseIndex] := lToken;
{ttIfThen.Ref1 points always to relative else (if present otherwise -1)}
{ttIfThen.Ref2 points always to relative endif}
ttIfThen: begin
lIfStackItem.IfIndex := I;
lIfStackItem.ElseIndex := -1; {-1 means: "there isn't ttElse"}
lIfStatementStack.Push(lIfStackItem);
end;
ttElse: begin
lIfStackItem := lIfStatementStack.Pop;
lIfStackItem.ElseIndex := I;
lIfStatementStack.Push(lIfStackItem);
end;
ttEndIf: begin
lIfStackItem := lIfStatementStack.Pop;
{fixup ifthen}
lToken := aTokens[lIfStackItem.IfIndex];
lToken.Ref2 := I; {ttIfThen.Ref2 points always to relative endif}
lToken.Ref1 := lIfStackItem.ElseIndex; {ttIfThen.Ref1 points always to relative else (if present, otherwise -1)}
aTokens[lIfStackItem.IfIndex] := lToken;
{fixup else}
if lIfStackItem.ElseIndex > -1 then
begin
lToken := aTokens[lIfStackItem.ElseIndex];
lToken.Ref2 := I; {ttElse.Ref2 points always to relative endif}
aTokens[lIfStackItem.ElseIndex] := lToken;
end;
end;
ttExit: begin
lCheckForUnbalancedPair := False;
end;
end;
end; // for
if lCheckForUnbalancedPair and (lIfStatementStack.Count > 0) then
begin
Error('Unbalanced "if" - expected "endif"');
end;
end; // for
if lCheckForUnbalancedPair and (lForInStack.Count > 0) then
begin
Error('Unbalanced "for" - expected "endfor"');
end;
finally
lIfStatementStack.Free;
end;
finally
lIfStatementStack.Free;
lContinueStack.Free;
end;
finally
lForInStack.Free;
@ -1163,6 +1217,18 @@ begin
FunctionError('expected 1 parameter');
Result := aValue.AsInt64 <= StrToInt64(aParameters[0]);
end
else if aFunctionName = 'contains' then
begin
if Length(aParameters) <> 1 then
FunctionError('expected 1 parameter');
Result := aValue.AsString.Contains(aParameters[0]);
end
else if aFunctionName = 'contains_ignore_case' then
begin
if Length(aParameters) <> 1 then
FunctionError('expected 1 parameter');
Result := aValue.AsString.ToLowerInvariant.Contains(aParameters[0].ToLowerInvariant);
end
else if (aFunctionName = 'eq') or (aFunctionName = 'ne') then
begin
if Length(aParameters) <> 1 then
@ -1685,7 +1751,7 @@ end;
procedure TTProCompiledTemplate.Error(const aMessage: String);
begin
raise ETProRenderException.Create(aMessage);
raise ETProRenderException.Create(aMessage) at ReturnAddress;
end;
procedure TTProCompiledTemplate.ForEachToken(
@ -1916,45 +1982,6 @@ begin
end;
ttValue, ttLiteralString: begin
lVarValue := EvaluateValue(lIdx, lMustBeEncoded {must be encoded});
// // Ref1 contains the optional filter parameter number (-1 if there isn't any filter)
// // Ref2 is -1 if the variable must be HTMLEncoded, while contains 1 is the value must not be HTMLEncoded
// lRef2 := fTokens[lIdx].Ref2;
// lCurrTokenType := fTokens[lIdx].TokenType;
// if fTokens[lIdx].Ref1 > -1 {has a filter with Ref1 parameters} then
// begin
// lVarName := fTokens[lIdx].Value1;
// Inc(lIdx);
// lFilterName := fTokens[lIdx].Value1;
// lFilterParCount := fTokens[lIdx].Ref1; // parameter count
// SetLength(lFilterParameters, lFilterParCount);
// for I := 0 to lFilterParCount - 1 do
// begin
// Inc(lIdx);
// Assert(fTokens[lIdx].TokenType = ttFilterParameter);
// lFilterParameters[I] := fTokens[lIdx].Value1;
// end;
// if lCurrTokenType = ttValue then
// begin
// lVarValue := ExecuteFilter(lFilterName, lFilterParameters, GetVarAsTValue(lVarName));
// end
// else
// begin
// lVarValue := ExecuteFilter(lFilterName, lFilterParameters, lVarName);
// end;
// end
// else
// begin
// if lCurrTokenType = ttValue then
// begin
// lVarValue := GetVarAsString(fTokens[lIdx].Value1);
// end
// else
// begin
// lVarValue := fTokens[lIdx].Value1;
// end;
// end;
if lMustBeEncoded {lRef2 = -1 // encoded} then
lBuff.Append(HTMLEncode(lVarValue.ToString))
else
@ -1972,6 +1999,13 @@ begin
begin
Error('Compiled template has been compiled with a different version. Expected ' + TEMPLATEPRO_VERSION + ' got ' + fTokens[lIdx].Value1);
end;
end;
ttContinue: begin
lIdx := fTokens[lIdx].Ref1;
Continue;
end;
ttExit: begin
//do nothing
end
else
begin
@ -1997,7 +2031,6 @@ end;
function TTProCompiledTemplate.GetVarAsTValue(const aName: string): TValue;
var
lVariable: TVarDataSource;
lField: TField;
lHasMember: Boolean;
lJPath: string;
lDataSource: string;
@ -2040,21 +2073,16 @@ begin
begin
Error('Empty field name while reading from iterator "%s"', [lVarName]);
end;
lField := TDataSet(lVariable.VarValue.AsObject).FieldByName(lVarMembers);
case lField.DataType of
ftInteger: Result := lField.AsInteger;
ftLargeint, ftAutoInc: Result := lField.AsLargeInt;
ftString, ftWideString, ftMemo, ftWideMemo: Result := lField.AsWideString;
else
Error('Invalid data type for field "%s": %s', [lVarMembers, TRttiEnumerationType.GetName<TFieldType>(lField.DataType)]);
end;
Result := GetDataSetFieldAsTValue(TDataSet(lVariable.VarValue.AsObject), lVarMembers);
end;
end
else
begin
{ not an interator }
if lHasMember then
Error(lDataSource + ' members can be read only through an iterator')
begin
Result := GetDataSetFieldAsTValue(TDataSet(lVariable.VarValue.AsObject), lVarMembers);
end
else
begin
Result := lVariable.VarValue.AsObject;