Refactored some class namespaces, re-implemented v-include to support <slot name=""> and <... v-slot="slotname">...</...>
parent
fb412cd3b9
commit
2a981cd155
|
@ -0,0 +1,13 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Rider ignored files
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
/.idea.ln.templates.iml
|
||||||
|
/modules.xml
|
||||||
|
/contentModel.xml
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
2
build.ln
2
build.ln
|
@ -3,7 +3,7 @@
|
||||||
"dotnet"
|
"dotnet"
|
||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"NUGET_SOURCE": "https://nexus.niclas-thobaben.de/repository/l--n.de/",
|
"NUGET_SOURCE": "https://nexus.l--n.de/repository/ln.net/",
|
||||||
"CONFIGURATION": "Release"
|
"CONFIGURATION": "Release"
|
||||||
},
|
},
|
||||||
"stages": [
|
"stages": [
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
using ln.http.route;
|
||||||
|
using ln.templates;
|
||||||
|
using ln.templates.html;
|
||||||
|
|
||||||
|
namespace ln.http.templates
|
||||||
|
{
|
||||||
|
public class FileSystemTemplateRouter : HttpRoute, IDisposable
|
||||||
|
{
|
||||||
|
private HttpRouter _parentRouter;
|
||||||
|
private FileSystemTemplateSource _fileSystemTemplateSource;
|
||||||
|
|
||||||
|
public FileSystemTemplateRouter(HttpRouter parentRouter, string mapPath, string fileSystemPath)
|
||||||
|
:this(parentRouter, mapPath, new FileSystemTemplateSource(fileSystemPath))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileSystemTemplateRouter(HttpRouter parentRouter, string mapPath, FileSystemTemplateSource fileSystemTemplateSource)
|
||||||
|
:base(HttpMethod.GET, mapPath)
|
||||||
|
{
|
||||||
|
_routerDelegate = RouteTemplate;
|
||||||
|
_fileSystemTemplateSource = fileSystemTemplateSource;
|
||||||
|
|
||||||
|
_parentRouter = parentRouter;
|
||||||
|
_parentRouter?.Map(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RouteTemplate(HttpRequestContext requestContext, string routePath)
|
||||||
|
{
|
||||||
|
if (routePath.EndsWith("/"))
|
||||||
|
routePath = string.Format("{0}index.html", routePath);
|
||||||
|
|
||||||
|
if (!routePath.EndsWith(".html"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TemplateDocument templateDocument = _fileSystemTemplateSource.GetTemplateByPath(routePath.Substring(1));
|
||||||
|
if (templateDocument is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
StreamedContent streamedContent = new StreamedContent("text/html");
|
||||||
|
requestContext.Response = HttpResponse.OK().Content(streamedContent);
|
||||||
|
|
||||||
|
RenderContext renderContext = new RenderContext(
|
||||||
|
new StreamWriter(streamedContent.ContentStream),
|
||||||
|
_fileSystemTemplateSource, new[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, object>("request", requestContext.Request),
|
||||||
|
new KeyValuePair<string, object>("response", requestContext.Response),
|
||||||
|
new KeyValuePair<string, object>("context", requestContext)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
templateDocument.RenderTemplate(renderContext);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_parentRouter?.UnMap(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<PackageVersion>0.9.2</PackageVersion>
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
<TargetFrameworks>net5.0;net6.0</TargetFrameworks>
|
||||||
|
<LangVersion>default</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ln.http" Version="0.9.1-test01" />
|
||||||
|
<PackageReference Include="ln.templates" Version="0.4.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.templates", "ln.template
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.templates.test", "ln.templates.test\ln.templates.test.csproj", "{36593773-B517-4B62-BAD2-8AA632AA159E}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.templates.test", "ln.templates.test\ln.templates.test.csproj", "{36593773-B517-4B62-BAD2-8AA632AA159E}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.http.templates", "ln.http.templates\ln.http.templates.csproj", "{011E8B81-DF87-4AFB-BEA8-ADA6FD3F3665}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -44,5 +46,17 @@ Global
|
||||||
{36593773-B517-4B62-BAD2-8AA632AA159E}.Release|x64.Build.0 = Release|Any CPU
|
{36593773-B517-4B62-BAD2-8AA632AA159E}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{36593773-B517-4B62-BAD2-8AA632AA159E}.Release|x86.ActiveCfg = Release|Any CPU
|
{36593773-B517-4B62-BAD2-8AA632AA159E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{36593773-B517-4B62-BAD2-8AA632AA159E}.Release|x86.Build.0 = Release|Any CPU
|
{36593773-B517-4B62-BAD2-8AA632AA159E}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{011E8B81-DF87-4AFB-BEA8-ADA6FD3F3665}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{011E8B81-DF87-4AFB-BEA8-ADA6FD3F3665}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{011E8B81-DF87-4AFB-BEA8-ADA6FD3F3665}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{011E8B81-DF87-4AFB-BEA8-ADA6FD3F3665}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{011E8B81-DF87-4AFB-BEA8-ADA6FD3F3665}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{011E8B81-DF87-4AFB-BEA8-ADA6FD3F3665}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{011E8B81-DF87-4AFB-BEA8-ADA6FD3F3665}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{011E8B81-DF87-4AFB-BEA8-ADA6FD3F3665}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{011E8B81-DF87-4AFB-BEA8-ADA6FD3F3665}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{011E8B81-DF87-4AFB-BEA8-ADA6FD3F3665}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{011E8B81-DF87-4AFB-BEA8-ADA6FD3F3665}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{011E8B81-DF87-4AFB-BEA8-ADA6FD3F3665}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
@ -17,56 +17,35 @@ namespace ln.templates.test
|
||||||
[TestFixture()]
|
[TestFixture()]
|
||||||
public class HtmlTests
|
public class HtmlTests
|
||||||
{
|
{
|
||||||
[Test()]
|
|
||||||
public void TestCase()
|
[TestCase()]
|
||||||
|
public void Test_Base()
|
||||||
{
|
{
|
||||||
html.TemplateReader templateReader = new html.TemplateReader();
|
Template template = new Template("tests/test_base.html", null);
|
||||||
templateReader.Read("<!DOCTYPE HTML>\n<html>\n<head>\n <title>Hello</title>\n</head>\n<body>\n <p>Welcome to this example.</p>\n <p>{{ 'Ich bin ein ScriptText!' }}</p>\n <p>DateTime: {{ Date }}</p>\n <p>Ein bisschen JavaScript: {{ 5 + ' mal ' + 5 + ' = ' + (5*5) }}</p>\n</body>\n</html>");
|
Console.WriteLine("Template Source:\n{0}", template.Document.ToString());
|
||||||
|
|
||||||
Console.WriteLine("Source rendered:\n{0}", templateReader.Document.ToString());
|
StringWriter targetWriter = new StringWriter();
|
||||||
|
template.Render(targetWriter);
|
||||||
StringWriter stringWriter = new StringWriter();
|
|
||||||
RenderContext renderContext = new RenderContext(stringWriter);
|
Console.WriteLine("Rendered Document:\n{0}", targetWriter.ToString());
|
||||||
|
|
||||||
renderContext.SetScriptObject("Date", DateTime.Now);
|
|
||||||
|
|
||||||
templateReader.TemplateDocument.RenderTemplate(renderContext);
|
|
||||||
|
|
||||||
Console.WriteLine("Template rendered:\n{0}", stringWriter.ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test()]
|
[TestCase()]
|
||||||
public void TestFileSystemTemplateSource()
|
public void Test_Slots()
|
||||||
{
|
{
|
||||||
if (File.Exists("templates.tst/frame.html"))
|
FileSystemTemplateSource templateSource = new FileSystemTemplateSource("tests");
|
||||||
File.Delete("templates.tst/frame.html");
|
Template template = templateSource.GetTemplateByPath("test_slots.html");
|
||||||
if (File.Exists("templates.tst/head.html"))
|
|
||||||
File.Delete("templates.tst/head.html");
|
Console.WriteLine("Template Source:\n{0}", template.Document.ToString());
|
||||||
|
|
||||||
Directory.CreateDirectory("templates.tst");
|
StringWriter targetWriter = new StringWriter();
|
||||||
using (FileStream fs = new FileStream("templates.tst/frame.html", FileMode.CreateNew))
|
template.Render(targetWriter);
|
||||||
using (TextWriter tw = new StreamWriter(fs))
|
|
||||||
{
|
using (StreamWriter sw = new StreamWriter("test.out.slots.html"))
|
||||||
tw.Write("<!DOCTYPE html><html><head v-include=\"'head.html'\"/></html>");
|
sw.Write(targetWriter.ToString());
|
||||||
tw.Flush();
|
|
||||||
}
|
Console.WriteLine("Rendered Document:\n{0}", targetWriter.ToString());
|
||||||
|
|
||||||
using (FileStream fs = new FileStream("templates.tst/head.html", FileMode.CreateNew))
|
|
||||||
using (TextWriter tw = new StreamWriter(fs))
|
|
||||||
{
|
|
||||||
tw.Write("<head><title v-if=\"title != 'Schwurbel'\" v-for:title=\"['One HTML Template Test','Second Title','Schwurbel','Last Title']\">{{ title }}</title></head>");
|
|
||||||
tw.Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSystemTemplateSource fileSystemTemplateSource = new FileSystemTemplateSource("templates.tst");
|
|
||||||
TemplateDocument templateDocument = fileSystemTemplateSource.GetTemplateByPath("frame.html");
|
|
||||||
|
|
||||||
StringWriter stringWriter = new StringWriter();
|
|
||||||
RenderContext renderContext = new RenderContext(stringWriter,fileSystemTemplateSource);
|
|
||||||
|
|
||||||
templateDocument.RenderTemplate(renderContext);
|
|
||||||
|
|
||||||
Console.WriteLine("Document rendered to: {0}", stringWriter.ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
|
||||||
|
<TargetFrameworks>net5.0;net6.0</TargetFrameworks>
|
||||||
|
|
||||||
|
<LangVersion>default</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -16,4 +19,16 @@
|
||||||
<ProjectReference Include="..\ln.templates\ln.templates.csproj" />
|
<ProjectReference Include="..\ln.templates\ln.templates.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="tests\test_base.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="tests\test_slot_a.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="tests\test_slots.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Basic Test Template</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Let's check for iterations...</h1>
|
||||||
|
<p v-for:i="[0,1,2,3,4,5,6,7,8,9]">This is paragraph #{{i}}</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div style="border: 1px solid black;">
|
||||||
|
<slot name="title"><h2>Default Title</h2></slot>
|
||||||
|
</div>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1>Slot Test</h1>
|
||||||
|
<div>
|
||||||
|
<template v-include="test_slot_a.html">
|
||||||
|
</template>
|
||||||
|
<template v-include="test_slot_a.html">
|
||||||
|
<div v-slot="title"><h2>Title A1</h2></div>
|
||||||
|
</template>
|
||||||
|
<template v-include="test_slot_a.html">
|
||||||
|
<div v-slot="title"><h2>Title A2</h2></div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,53 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Jint;
|
||||||
|
using ln.templates.html;
|
||||||
|
|
||||||
|
namespace ln.templates;
|
||||||
|
|
||||||
|
|
||||||
|
public class Context
|
||||||
|
{
|
||||||
|
public Context(ITemplateResolver resolver, IEnumerable<KeyValuePair<string, object>> scriptObjects, TextWriter targetWriter)
|
||||||
|
{
|
||||||
|
Resolver = resolver;
|
||||||
|
Engine = new Engine();
|
||||||
|
foreach (KeyValuePair<string,object> pair in scriptObjects)
|
||||||
|
Engine.SetValue(pair.Key, pair.Value);
|
||||||
|
TargetWriter = targetWriter;
|
||||||
|
}
|
||||||
|
public Context(ITemplateResolver resolver, Engine engine, TextWriter targetWriter)
|
||||||
|
{
|
||||||
|
Resolver = resolver;
|
||||||
|
Engine = engine;
|
||||||
|
TargetWriter = targetWriter;
|
||||||
|
}
|
||||||
|
public Context(Context source)
|
||||||
|
{
|
||||||
|
Resolver = source.Resolver;
|
||||||
|
Engine = source.Engine;
|
||||||
|
TargetWriter = source.TargetWriter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Engine Engine { get; private set; }
|
||||||
|
public ITemplateResolver Resolver { get; }
|
||||||
|
|
||||||
|
public TextWriter TargetWriter { get; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slots to be used for v-include and v-frame
|
||||||
|
*/
|
||||||
|
private Dictionary<string, Element> _slots = new Dictionary<string, Element>();
|
||||||
|
public bool TryGetSlot(string slotName, out Element slot) => _slots.TryGetValue(slotName, out slot);
|
||||||
|
public void SetSlot(string slotName, Element slot) => _slots[slotName] = slot;
|
||||||
|
public void ClearSlot(string slotName) => _slots.Remove(slotName);
|
||||||
|
public void ClearSlots() => _slots.Clear();
|
||||||
|
|
||||||
|
public void SetSlots(IEnumerable<KeyValuePair<string, Element>> slots)
|
||||||
|
{
|
||||||
|
_slots.Clear();
|
||||||
|
foreach (var pair in slots)
|
||||||
|
_slots.Add(pair.Key, pair.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,559 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Linq;
|
|
||||||
namespace ln.templates
|
|
||||||
{
|
|
||||||
delegate bool ConditionDelegate(char ch);
|
|
||||||
|
|
||||||
public class Expression
|
|
||||||
{
|
|
||||||
public String Source { get; }
|
|
||||||
private Eval TopEval { get; set; }
|
|
||||||
|
|
||||||
public Expression(String expr)
|
|
||||||
{
|
|
||||||
Source = expr;
|
|
||||||
|
|
||||||
Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
public object Evaluate(object o)
|
|
||||||
{
|
|
||||||
Context context = new Context(o, null);
|
|
||||||
return TopEval.Evaluate(context);
|
|
||||||
}
|
|
||||||
public object Evaluate(Context context)
|
|
||||||
{
|
|
||||||
return TopEval.Evaluate(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsTrue(Context context)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
object v = Evaluate(context);
|
|
||||||
if ((v is bool) && ((bool)v))
|
|
||||||
return true;
|
|
||||||
if ((v is string) && (!String.Empty.Equals(v)))
|
|
||||||
return true;
|
|
||||||
if ((v is int) && ((int)v != 0))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void Compile()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
StringReader reader = new StringReader(Source);
|
|
||||||
List<Token> tokens = new List<Token>();
|
|
||||||
while (reader.Peek() != -1)
|
|
||||||
ReadToken(reader, tokens);
|
|
||||||
|
|
||||||
Queue<Token> qtokens = new Queue<Token>(tokens);
|
|
||||||
|
|
||||||
TopEval = BuildExpression(qtokens);
|
|
||||||
} catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new Exception(String.Format("Failed to compile expression: {0}", Source), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Eval BuildName(Token nameToken,Eval parent,Queue<Token> tokens)
|
|
||||||
{
|
|
||||||
if ((tokens.Count > 0) && (tokens.Peek().TokenType == TokenType.OPENLIST))
|
|
||||||
{
|
|
||||||
tokens.Dequeue();
|
|
||||||
|
|
||||||
List<Eval> pl = new List<Eval>();
|
|
||||||
while (tokens.Peek().TokenType != TokenType.ENDLIST)
|
|
||||||
{
|
|
||||||
pl.Add(BuildExpression(tokens));
|
|
||||||
|
|
||||||
if (tokens.Peek().TokenType == TokenType.ENDLIST)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (tokens.Peek().TokenType != TokenType.COMMA)
|
|
||||||
throw new FormatException("Expected ',' between parameters to call statement");
|
|
||||||
|
|
||||||
tokens.Dequeue();
|
|
||||||
}
|
|
||||||
|
|
||||||
tokens.Dequeue();
|
|
||||||
|
|
||||||
return new MethodCall(parent, nameToken.Value, pl.ToArray());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new Name(parent, nameToken.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Eval BuildPath(Queue<Token> tokens)
|
|
||||||
{
|
|
||||||
Eval currentEval = BuildName(tokens.Dequeue(), null, tokens);
|
|
||||||
|
|
||||||
while (tokens.Count > 0)
|
|
||||||
{
|
|
||||||
Token token = tokens.Peek();
|
|
||||||
switch (token.TokenType)
|
|
||||||
{
|
|
||||||
case TokenType.DOT:
|
|
||||||
tokens.Dequeue();
|
|
||||||
currentEval = BuildName(tokens.Dequeue(), currentEval, tokens);
|
|
||||||
break;
|
|
||||||
case TokenType.OPENINDEXER:
|
|
||||||
tokens.Dequeue();
|
|
||||||
Eval index = BuildExpression(tokens);
|
|
||||||
token = tokens.Dequeue();
|
|
||||||
if (token.TokenType != TokenType.ENDINDEXER)
|
|
||||||
throw new FormatException("Expected ']'");
|
|
||||||
currentEval = new Indexer(currentEval, index);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return currentEval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return currentEval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Eval BuildExpression(Queue<Token> tokens)
|
|
||||||
{
|
|
||||||
Token next = tokens.Peek();
|
|
||||||
|
|
||||||
switch (next.TokenType)
|
|
||||||
{
|
|
||||||
case TokenType.NAME:
|
|
||||||
return BuildPath(tokens);
|
|
||||||
case TokenType.NUMBER:
|
|
||||||
return new Number(tokens.Dequeue().Value);
|
|
||||||
case TokenType.STRING:
|
|
||||||
return new CString(tokens.Dequeue().Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new FormatException(String.Format("unexpected Token: {0}", next.Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReadToken(TextReader reader, List<Token> tokens)
|
|
||||||
{
|
|
||||||
if (reader.Peek() == -1)
|
|
||||||
return;
|
|
||||||
char ch = (char)reader.Peek();
|
|
||||||
if (char.IsWhiteSpace(ch))
|
|
||||||
{
|
|
||||||
reader.Read();
|
|
||||||
}
|
|
||||||
else if (char.IsLetter(ch) || (ch == '_'))
|
|
||||||
{
|
|
||||||
ReadName(reader, tokens);
|
|
||||||
}
|
|
||||||
else if (char.IsDigit(ch))
|
|
||||||
{
|
|
||||||
ReadNumber(reader, tokens);
|
|
||||||
}
|
|
||||||
else if (ch == '"')
|
|
||||||
{
|
|
||||||
ReadString(reader, tokens);
|
|
||||||
}
|
|
||||||
else if (ch == '.')
|
|
||||||
{
|
|
||||||
reader.Read();
|
|
||||||
tokens.Add(new Token(TokenType.DOT, "."));
|
|
||||||
}
|
|
||||||
else if (ch == '[')
|
|
||||||
{
|
|
||||||
reader.Read();
|
|
||||||
tokens.Add(new Token(TokenType.OPENINDEXER, new string(new char[] { ch })));
|
|
||||||
}
|
|
||||||
else if (ch == ']')
|
|
||||||
{
|
|
||||||
reader.Read();
|
|
||||||
tokens.Add(new Token(TokenType.ENDINDEXER, new string(new char[] { ch })));
|
|
||||||
}
|
|
||||||
else if (ch == '(')
|
|
||||||
{
|
|
||||||
reader.Read();
|
|
||||||
tokens.Add(new Token(TokenType.OPENLIST, new string(new char[] { ch })));
|
|
||||||
}
|
|
||||||
else if (ch == ')')
|
|
||||||
{
|
|
||||||
reader.Read();
|
|
||||||
tokens.Add(new Token(TokenType.ENDLIST, new string(new char[] { ch })));
|
|
||||||
}
|
|
||||||
else if (ch == ',')
|
|
||||||
{
|
|
||||||
reader.Read();
|
|
||||||
tokens.Add(new Token(TokenType.COMMA, new string(new char[] { ch })));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new FormatException(String.Format("Unexpected character: {0}", (char)reader.Peek()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void ReadName(TextReader reader, List<Token> tokens)
|
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
int ch = 0;
|
|
||||||
while (char.IsLetterOrDigit((char)(ch = reader.Peek())) || (ch == '_'))
|
|
||||||
{
|
|
||||||
sb.Append((char)reader.Read());
|
|
||||||
}
|
|
||||||
tokens.Add(new Token(TokenType.NAME, sb.ToString()));
|
|
||||||
}
|
|
||||||
private void ReadNumber(TextReader reader, List<Token> tokens)
|
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
int ch = 0;
|
|
||||||
while (char.IsDigit((char)(ch = reader.Peek())) | (ch == '.'))
|
|
||||||
{
|
|
||||||
sb.Append((char)reader.Read());
|
|
||||||
}
|
|
||||||
tokens.Add(new Token(TokenType.NUMBER, sb.ToString()));
|
|
||||||
}
|
|
||||||
private void ReadString(TextReader reader, List<Token> tokens)
|
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
int ch = 0;
|
|
||||||
reader.Read();
|
|
||||||
while ((ch = reader.Peek())!='"')
|
|
||||||
{
|
|
||||||
if (ch == '\\')
|
|
||||||
{
|
|
||||||
reader.Read();
|
|
||||||
char sch = (char)reader.Read();
|
|
||||||
switch (sch)
|
|
||||||
{
|
|
||||||
case 'n':
|
|
||||||
sb.Append('\n');
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
sb.Append('\r');
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
sb.Append('\t');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sb.Append(sch);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.Append((char)reader.Read());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reader.Read();
|
|
||||||
|
|
||||||
tokens.Add(new Token(TokenType.STRING, sb.ToString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum TokenType { NAME, DOT, NUMBER, STRING, OPENINDEXER, ENDINDEXER, OPENLIST, ENDLIST, COMMA }
|
|
||||||
class Token
|
|
||||||
{
|
|
||||||
public TokenType TokenType;
|
|
||||||
public string Value;
|
|
||||||
|
|
||||||
public Token(TokenType tokenType,String value)
|
|
||||||
{
|
|
||||||
this.TokenType = tokenType;
|
|
||||||
this.Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return String.Format("[Token TokenType={0:8} Value={1}]", TokenType, Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Context
|
|
||||||
{
|
|
||||||
public object This { get; }
|
|
||||||
public Dictionary<string,object> MappedValues { get; }
|
|
||||||
|
|
||||||
public Context(Context source)
|
|
||||||
: this(source.This, source.MappedValues)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public Context(object o,IEnumerable<KeyValuePair<string,object>> mappedValues)
|
|
||||||
{
|
|
||||||
this.This = o;
|
|
||||||
this.MappedValues = new Dictionary<string, object>();
|
|
||||||
|
|
||||||
if (mappedValues != null)
|
|
||||||
foreach (KeyValuePair<string, object> kvp in mappedValues)
|
|
||||||
MappedValues.Add(kvp.Key,kvp.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddMappedValue(string name, object value)
|
|
||||||
{
|
|
||||||
MappedValues.Add(name, value);
|
|
||||||
}
|
|
||||||
public void SetMappedValue(string name, object value)
|
|
||||||
{
|
|
||||||
MappedValues[name] = value;
|
|
||||||
}
|
|
||||||
public void RemoveMappedValue(string name)
|
|
||||||
{
|
|
||||||
MappedValues.Remove(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public object Evaluate(string name)
|
|
||||||
{
|
|
||||||
if (MappedValues.ContainsKey(name))
|
|
||||||
return MappedValues[name];
|
|
||||||
|
|
||||||
if (This != null)
|
|
||||||
{
|
|
||||||
FieldInfo fieldInfo = This.GetType().GetField(name);
|
|
||||||
if (fieldInfo != null)
|
|
||||||
{
|
|
||||||
return fieldInfo.GetValue(This);
|
|
||||||
}
|
|
||||||
PropertyInfo propertyInfo = This.GetType().GetProperty(name);
|
|
||||||
if (propertyInfo != null)
|
|
||||||
{
|
|
||||||
return propertyInfo.GetValue(This);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new KeyNotFoundException(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class Eval
|
|
||||||
{
|
|
||||||
public Eval Parent { get; }
|
|
||||||
|
|
||||||
public Eval(Eval parent)
|
|
||||||
{
|
|
||||||
this.Parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract object Evaluate(Context context);
|
|
||||||
}
|
|
||||||
|
|
||||||
class Name : Eval
|
|
||||||
{
|
|
||||||
public string name;
|
|
||||||
|
|
||||||
public Name(Eval parent,String name)
|
|
||||||
:base(parent)
|
|
||||||
{
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Evaluate(Context context)
|
|
||||||
{
|
|
||||||
if (Parent == null)
|
|
||||||
{
|
|
||||||
return context.Evaluate(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
object p = Parent.Evaluate(context);
|
|
||||||
if (p != null)
|
|
||||||
{
|
|
||||||
FieldInfo fieldInfo = p.GetType().GetField(name);
|
|
||||||
if (fieldInfo != null)
|
|
||||||
{
|
|
||||||
return fieldInfo.GetValue(p);
|
|
||||||
}
|
|
||||||
PropertyInfo propertyInfo = p.GetType().GetProperty(name);
|
|
||||||
if (propertyInfo != null)
|
|
||||||
{
|
|
||||||
return propertyInfo.GetValue(p);
|
|
||||||
}
|
|
||||||
throw new KeyNotFoundException(name);
|
|
||||||
}
|
|
||||||
throw new NullReferenceException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Indexer : Eval
|
|
||||||
{
|
|
||||||
Eval index;
|
|
||||||
|
|
||||||
public Indexer(Eval parent,Eval index)
|
|
||||||
:base(parent)
|
|
||||||
{
|
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Evaluate(Context context)
|
|
||||||
{
|
|
||||||
object i = index.Evaluate(context);
|
|
||||||
Type itype = i.GetType();
|
|
||||||
|
|
||||||
object p = Parent.Evaluate(context);
|
|
||||||
|
|
||||||
if (p is Array)
|
|
||||||
{
|
|
||||||
Array pa = p as Array;
|
|
||||||
return pa.GetValue((int)i);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (PropertyInfo pi in p.GetType().GetProperties())
|
|
||||||
{
|
|
||||||
ParameterInfo[] infos = pi.GetIndexParameters();
|
|
||||||
if (infos.Length == 1)
|
|
||||||
{
|
|
||||||
if (infos[0].ParameterType.IsAssignableFrom(itype))
|
|
||||||
{
|
|
||||||
return pi.GetValue(p, new object[] { i });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new KeyNotFoundException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MethodCall : Eval
|
|
||||||
{
|
|
||||||
Eval[] Parameters;
|
|
||||||
String Name;
|
|
||||||
|
|
||||||
public MethodCall(Eval parent, String name,params Eval[] p)
|
|
||||||
: base(parent)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Parameters = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Evaluate(Context context)
|
|
||||||
{
|
|
||||||
object p = Parent.Evaluate(context);
|
|
||||||
|
|
||||||
object[] pvalues = new object[Parameters.Length];
|
|
||||||
Type[] ptypes = new Type[Parameters.Length];
|
|
||||||
|
|
||||||
for (int i=0;i<pvalues.Length;i++)
|
|
||||||
{
|
|
||||||
pvalues[i] = Parameters[i].Evaluate(context);
|
|
||||||
if (pvalues[i] == null)
|
|
||||||
ptypes[i] = null;
|
|
||||||
else
|
|
||||||
ptypes[i] = pvalues[i].GetType();
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodInfo methodInfo = p.GetType().GetRuntimeMethod(Name, ptypes);
|
|
||||||
|
|
||||||
return methodInfo.Invoke(p, pvalues);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Number : Eval
|
|
||||||
{
|
|
||||||
object value;
|
|
||||||
|
|
||||||
public Number(String sourceValue)
|
|
||||||
: base(null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
value = int.Parse(sourceValue);
|
|
||||||
} catch
|
|
||||||
{
|
|
||||||
value = float.Parse(sourceValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Evaluate(Context context)
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class CString : Eval
|
|
||||||
{
|
|
||||||
string value;
|
|
||||||
|
|
||||||
public CString(String sourceValue)
|
|
||||||
: base(null)
|
|
||||||
{
|
|
||||||
this.value = sourceValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Evaluate(Context context)
|
|
||||||
{
|
|
||||||
return this.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//private String read(TextReader reader,ConditionDelegate condition)
|
|
||||||
//{
|
|
||||||
// StringBuilder sb = new StringBuilder();
|
|
||||||
// int ch;
|
|
||||||
// while (((ch = reader.Peek())!=-1)&&condition((char)ch))
|
|
||||||
// {
|
|
||||||
// sb.Append((char)ch);
|
|
||||||
// }
|
|
||||||
// return sb.ToString();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//private Token ReadToken(TextReader reader)
|
|
||||||
//{
|
|
||||||
// int ch = reader.Peek();
|
|
||||||
// if (char.IsLetter((char)ch))
|
|
||||||
// return ReadFieldName(reader);
|
|
||||||
|
|
||||||
// throw new FormatException();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//private Token ReadPath(TextReader reader)
|
|
||||||
//{
|
|
||||||
// String read(reader, (ch) => char.IsLetterOrDigit(ch));
|
|
||||||
//}
|
|
||||||
|
|
||||||
//abstract class Token
|
|
||||||
//{
|
|
||||||
// public abstract object Evaluate();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//class Field : Token
|
|
||||||
//{
|
|
||||||
// Token owner;
|
|
||||||
// String fieldName;
|
|
||||||
|
|
||||||
// public Field(Token owner,String fieldName)
|
|
||||||
// {
|
|
||||||
// this.owner = owner;
|
|
||||||
// this.fieldName = fieldName;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public override object Evaluate()
|
|
||||||
// {
|
|
||||||
// if (o == null)
|
|
||||||
// return null;
|
|
||||||
|
|
||||||
// Type t = o.GetType();
|
|
||||||
// FieldInfo fieldInfo = t.GetField(this.fieldName);
|
|
||||||
// if (fieldInfo != null)
|
|
||||||
// {
|
|
||||||
// return fieldInfo.GetValue(owner.Evaluate());
|
|
||||||
// }
|
|
||||||
// PropertyInfo propertyInfo = t.GetProperty(this.fieldName);
|
|
||||||
// if (propertyInfo != null)
|
|
||||||
// {
|
|
||||||
// return propertyInfo.GetValue(owner.Evaluate());
|
|
||||||
// }
|
|
||||||
// throw new MissingFieldException(t.FullName, this.fieldName);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using ln.templates.html;
|
||||||
|
|
||||||
|
namespace ln.templates
|
||||||
|
{
|
||||||
|
public class FileSystemTemplateSource : ITemplateResolver
|
||||||
|
{
|
||||||
|
public String BasePath { get; }
|
||||||
|
public bool Throws { get; }
|
||||||
|
|
||||||
|
Dictionary<string, Template> _templates = new Dictionary<string, Template>();
|
||||||
|
|
||||||
|
public FileSystemTemplateSource(String path,bool throws)
|
||||||
|
:this(path)
|
||||||
|
{
|
||||||
|
Throws = throws;
|
||||||
|
}
|
||||||
|
public FileSystemTemplateSource(String path)
|
||||||
|
{
|
||||||
|
path = Path.GetFullPath(path);
|
||||||
|
|
||||||
|
if (!Directory.Exists(path))
|
||||||
|
throw new FileNotFoundException();
|
||||||
|
BasePath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Template GetTemplateByPath(string path) => GetTemplateByPath(path, Throws);
|
||||||
|
public Template GetTemplateByPath(string path,bool _throw)
|
||||||
|
{
|
||||||
|
string templatePath = Path.Combine(BasePath, path);
|
||||||
|
if (!File.Exists(templatePath))
|
||||||
|
{
|
||||||
|
if (_templates.ContainsKey(path))
|
||||||
|
_templates.Remove(path);
|
||||||
|
|
||||||
|
if (_throw)
|
||||||
|
throw new FileNotFoundException();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_templates.TryGetValue(path,out Template template))
|
||||||
|
{
|
||||||
|
template = new Template(templatePath, this);
|
||||||
|
_templates[path] = template;
|
||||||
|
}
|
||||||
|
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,254 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Linq;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Resources;
|
|
||||||
|
|
||||||
namespace ln.templates
|
|
||||||
{
|
|
||||||
//public class FormContext
|
|
||||||
//{
|
|
||||||
//private Dictionary<string, object> vars = new Dictionary<string, object>();
|
|
||||||
//protected MemoryStream contentStream, headStream;
|
|
||||||
|
|
||||||
//public Template SharpForm { get; }
|
|
||||||
//public Request Request { get; }
|
|
||||||
|
|
||||||
//public Stream ContentStream => contentStream;
|
|
||||||
//public TextWriter ContentWriter { get; protected set; }
|
|
||||||
|
|
||||||
//public Stream HeadStream => headStream;
|
|
||||||
//public TextWriter HeadWriter { get; protected set; }
|
|
||||||
|
|
||||||
//public FormContext Parent { get; }
|
|
||||||
//public bool UsesClonedVars { get; }
|
|
||||||
|
|
||||||
//public bool IsRootContext => Parent == null;
|
|
||||||
|
|
||||||
//public FormContext SubFrameContext { get; protected set; }
|
|
||||||
|
|
||||||
//public FormContext(Template sharpForm,Request request,object o)
|
|
||||||
//{
|
|
||||||
// Parent = null;
|
|
||||||
|
|
||||||
// SharpForm = sharpForm;
|
|
||||||
// Request = request;
|
|
||||||
|
|
||||||
// contentStream = new MemoryStream();
|
|
||||||
// ContentWriter = new StreamWriter(contentStream);
|
|
||||||
|
|
||||||
// headStream = new MemoryStream();
|
|
||||||
// HeadWriter = new StreamWriter(headStream);
|
|
||||||
|
|
||||||
// vars["o"] = o;
|
|
||||||
// vars["this"] = o;
|
|
||||||
// vars["StrMax"] = GetType().GetMethod("StrMax");
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public FormContext(FormContext parent,bool cloneVars = false,bool independentContent = false,FormContext subFrameContext = null)
|
|
||||||
//{
|
|
||||||
// Parent = parent;
|
|
||||||
|
|
||||||
// SharpForm = parent.SharpForm;
|
|
||||||
// Request = parent.Request;
|
|
||||||
|
|
||||||
// contentStream = independentContent ? new MemoryStream() : parent.contentStream;
|
|
||||||
// ContentWriter = new StreamWriter(ContentStream);
|
|
||||||
|
|
||||||
// headStream = parent.headStream;
|
|
||||||
// HeadWriter = parent.HeadWriter;
|
|
||||||
|
|
||||||
// UsesClonedVars = cloneVars;
|
|
||||||
|
|
||||||
// SubFrameContext = subFrameContext == null ? parent.SubFrameContext : subFrameContext;
|
|
||||||
|
|
||||||
// if (cloneVars){
|
|
||||||
// foreach (KeyValuePair<string,object> var in parent.vars)
|
|
||||||
// {
|
|
||||||
// this.vars.Add(var.Key, var.Value);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// parent.ContentWriter.Flush();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public byte[] ContentBytes
|
|
||||||
//{
|
|
||||||
// get
|
|
||||||
// {
|
|
||||||
// ContentWriter.Flush();
|
|
||||||
// return contentStream.ToArray();
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//public byte[] HeadBytes
|
|
||||||
//{
|
|
||||||
// get
|
|
||||||
// {
|
|
||||||
// HeadWriter.Flush();
|
|
||||||
// return headStream.ToArray();
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//public String Content => Encoding.UTF8.GetString(ContentBytes);
|
|
||||||
//public String Head => Encoding.UTF8.GetString(HeadBytes);
|
|
||||||
|
|
||||||
//public void Insert(FormContext context, bool allToHead = false)
|
|
||||||
//{
|
|
||||||
// if (context.headStream != headStream)
|
|
||||||
// {
|
|
||||||
// HeadWriter.Flush();
|
|
||||||
// byte[] headBytes = context.HeadBytes;
|
|
||||||
// headStream.Write(headBytes, 0, headBytes.Length);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ContentWriter.Flush();
|
|
||||||
// byte[] contentBytes = context.ContentBytes;
|
|
||||||
|
|
||||||
// if (allToHead)
|
|
||||||
// headStream.Write(contentBytes, 0, contentBytes.Length);
|
|
||||||
// else
|
|
||||||
// contentStream.Write(contentBytes, 0, contentBytes.Length);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public bool HasVar(String name)
|
|
||||||
//{
|
|
||||||
// if (vars.ContainsKey(name))
|
|
||||||
// return true;
|
|
||||||
// if (!UsesClonedVars && (Parent != null))
|
|
||||||
// return Parent.HasVar(name);
|
|
||||||
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public object Get(String name){
|
|
||||||
// if (vars.ContainsKey(name))
|
|
||||||
// return vars[name];
|
|
||||||
// else if (Parent != null)
|
|
||||||
// return Parent.Get(name);
|
|
||||||
// else
|
|
||||||
// return null;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public void Set(String name,object value){
|
|
||||||
// if (!UsesClonedVars && (Parent != null) && (Parent.HasVar(name)))
|
|
||||||
// {
|
|
||||||
// Parent.Set(name, value);
|
|
||||||
// } else {
|
|
||||||
// this.vars[name] = value;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
//public object EvaluateExpression(Token[] tokens)
|
|
||||||
//{
|
|
||||||
// if (tokens.Length == 0)
|
|
||||||
// {
|
|
||||||
// return "";
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Stack<Token> tokenStack = new Stack<Token>(tokens);
|
|
||||||
// object currentValue = null;
|
|
||||||
// Token currentToken = null;
|
|
||||||
|
|
||||||
// while (tokenStack.Count > 0){
|
|
||||||
// currentToken = tokenStack.Pop();
|
|
||||||
|
|
||||||
// if (currentToken is PathToken)
|
|
||||||
// {
|
|
||||||
// PathToken pathToken = (PathToken)currentToken;
|
|
||||||
// Response response = Request.SubRequest(pathToken.Path);
|
|
||||||
// if (response.Reference != null)
|
|
||||||
// currentValue = response.Reference;
|
|
||||||
// else
|
|
||||||
// currentValue = Encoding.UTF8.GetString(response.ContentBytes);
|
|
||||||
// } else if (currentToken is KeywordToken)
|
|
||||||
// {
|
|
||||||
// KeywordToken keywordToken = (KeywordToken)currentToken;
|
|
||||||
// currentValue = keywordToken.Evaluate(this);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (tokenStack.Count > 0){
|
|
||||||
// currentToken = tokenStack.Pop();
|
|
||||||
// throw new NotImplementedException("Currently no expression operators are implemented, sorry");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
|
||||||
// return currentValue;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public object EvaluateValue(String sValue)
|
|
||||||
//{
|
|
||||||
// Stack<String> vpath = new Stack<string>(sValue.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries).Reverse());
|
|
||||||
|
|
||||||
// if (vpath.Count > 0)
|
|
||||||
// {
|
|
||||||
// object value = null;
|
|
||||||
// string key = vpath.Pop();
|
|
||||||
|
|
||||||
// value = Get(key);
|
|
||||||
|
|
||||||
// while (vpath.Count > 0)
|
|
||||||
// {
|
|
||||||
// key = vpath.Pop();
|
|
||||||
// value = GetObjectFieldOrProperty(value, key);
|
|
||||||
// }
|
|
||||||
// return value;
|
|
||||||
// }
|
|
||||||
// return null;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public bool EvaluateBoolean(String expression)
|
|
||||||
//{
|
|
||||||
// bool invert = (expression[0] == '!');
|
|
||||||
// if (invert)
|
|
||||||
// expression = expression.Substring(1);
|
|
||||||
|
|
||||||
// object value = EvaluateValue(expression);
|
|
||||||
// if (value != null)
|
|
||||||
// {
|
|
||||||
// if (
|
|
||||||
// ((value is bool) && (((bool)value))) ||
|
|
||||||
// ((value is string) && (!String.Empty.Equals(value))) ||
|
|
||||||
// ((value is int) && (((int)value) != 0)) ||
|
|
||||||
// ((value is long) && (((long)value) != 0)) ||
|
|
||||||
// ((value is float) && (((float)value) != 0)) ||
|
|
||||||
// ((value is double) && (((double)value) != 0))
|
|
||||||
// )
|
|
||||||
// {
|
|
||||||
// return !invert;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return invert;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//private object GetObjectFieldOrProperty(object o, string name)
|
|
||||||
//{
|
|
||||||
// if (o == null)
|
|
||||||
// {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// FieldInfo fieldInfo = o.GetType().GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
|
||||||
// if (fieldInfo != null)
|
|
||||||
// {
|
|
||||||
// return fieldInfo.GetValue(o);
|
|
||||||
// }
|
|
||||||
// PropertyInfo propertyInfo = o.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
|
||||||
// if (propertyInfo != null)
|
|
||||||
// {
|
|
||||||
// return propertyInfo.GetValue(o);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// throw new KeyNotFoundException(String.Format("object of type {0} has no field/property named {1}", o.GetType().FullName, name));
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public static string StrMax(string s,int len,string suffix = "")
|
|
||||||
//{
|
|
||||||
// if (len < s.Length)
|
|
||||||
// return s.Substring(0, len) + suffix;
|
|
||||||
// return s;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// }
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Xml;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
namespace ln.templates
|
|
||||||
{
|
|
||||||
//public abstract class FormElement
|
|
||||||
//{
|
|
||||||
// /* Instance Members */
|
|
||||||
// FormElement Container { get; }
|
|
||||||
// List<FormElement> children = new List<FormElement>();
|
|
||||||
|
|
||||||
// public FormElement(FormElement container)
|
|
||||||
// {
|
|
||||||
// Container = container;
|
|
||||||
// if (container != null)
|
|
||||||
// {
|
|
||||||
// container.children.Add(this);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public abstract void Run(FormContext context);
|
|
||||||
|
|
||||||
// public FormElement[] Children => children.ToArray();
|
|
||||||
// public virtual Template Form => Container.Form;
|
|
||||||
|
|
||||||
// public void RunChildren(FormContext context)
|
|
||||||
// {
|
|
||||||
// foreach (FormElement child in children)
|
|
||||||
// {
|
|
||||||
// child.Run(context);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
//}
|
|
||||||
}
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using ln.templates.html;
|
||||||
|
|
||||||
|
namespace ln.templates
|
||||||
|
{
|
||||||
|
public interface ITemplateResolver
|
||||||
|
{
|
||||||
|
Template GetTemplateByPath(string path);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Esprima.Ast;
|
||||||
|
using ln.templates.html;
|
||||||
|
|
||||||
|
namespace ln.templates;
|
||||||
|
|
||||||
|
public class RecursiveResolver : ITemplateResolver
|
||||||
|
{
|
||||||
|
public RecursiveResolver(string path)
|
||||||
|
:this(null, path)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private RecursiveResolver(RecursiveResolver parent, string path)
|
||||||
|
{
|
||||||
|
Parent = parent;
|
||||||
|
Path = path;
|
||||||
|
if (!Directory.Exists(path))
|
||||||
|
throw new DirectoryNotFoundException(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecursiveResolver Root => Parent?.Root ?? this;
|
||||||
|
public RecursiveResolver Parent { get; private set; }
|
||||||
|
public string Path { get; private set; }
|
||||||
|
|
||||||
|
private bool IsAlive => Directory.Exists(Path);
|
||||||
|
|
||||||
|
private Dictionary<string, RecursiveResolver> _children = new Dictionary<string, RecursiveResolver>();
|
||||||
|
private Dictionary<string, Template> _templates = new Dictionary<string, Template>();
|
||||||
|
|
||||||
|
|
||||||
|
public RecursiveResolver FindResolver(string path) => FindResolver(
|
||||||
|
System.IO.Path.GetDirectoryName(path)?.Split(System.IO.Path.PathSeparator) ?? Array.Empty<string>());
|
||||||
|
private RecursiveResolver FindResolver(Span<string> pathElements)
|
||||||
|
{
|
||||||
|
if (pathElements.Length == 0)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(pathElements));
|
||||||
|
|
||||||
|
if (_children.TryGetValue(pathElements[0], out RecursiveResolver child))
|
||||||
|
{
|
||||||
|
if (!child.IsAlive)
|
||||||
|
{
|
||||||
|
_children.Remove(pathElements[0]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (pathElements.Length > 1)
|
||||||
|
return child.FindResolver(pathElements.Slice(1));
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Template GetTemplateByPath(string templatePath)
|
||||||
|
{
|
||||||
|
Span<string> pathElements = templatePath.Split(System.IO.Path.DirectorySeparatorChar);
|
||||||
|
RecursiveResolver resolver = FindResolver(pathElements.Slice(0, pathElements.Length - 1));
|
||||||
|
return resolver?.GetTemplate(pathElements[^1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Template GetTemplate(string name)
|
||||||
|
{
|
||||||
|
string filename = System.IO.Path.Combine(Path, name);
|
||||||
|
if (File.Exists(filename))
|
||||||
|
{
|
||||||
|
if (!_templates.TryGetValue(name, out Template template))
|
||||||
|
{
|
||||||
|
template = new Template(filename, this);
|
||||||
|
_templates.Add(name, template);
|
||||||
|
}
|
||||||
|
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,132 +1,56 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using ln.templates.html;
|
||||||
using ln.templates.elements;
|
|
||||||
|
|
||||||
namespace ln.templates
|
namespace ln.templates;
|
||||||
|
|
||||||
|
public class Template
|
||||||
{
|
{
|
||||||
public class Template
|
public Template(string filename, ITemplateResolver resolver)
|
||||||
|
:this(filename, resolver, null)
|
||||||
|
{}
|
||||||
|
|
||||||
|
private Template(string filename, ITemplateResolver resolver, string source)
|
||||||
{
|
{
|
||||||
public TemplateProvider Provider { get; private set; }
|
FileName = filename;
|
||||||
public String SourceFilename { get; private set; }
|
Resolver = resolver;
|
||||||
public String PseudoFilename { get; private set; }
|
|
||||||
public DateTime SourceTimeStamp { get; private set; }
|
|
||||||
|
|
||||||
public Element RootElement { get; private set; }
|
if (source is null)
|
||||||
|
|
||||||
public Expression FrameExpression { get; private set; }
|
|
||||||
|
|
||||||
public Template(String sourceFilename)
|
|
||||||
{
|
{
|
||||||
SourceFilename = sourceFilename;
|
Load();
|
||||||
PseudoFilename = sourceFilename;
|
|
||||||
|
|
||||||
LoadSource(null);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
public Template(String sourceFilename, TemplateProvider provider)
|
|
||||||
{
|
{
|
||||||
Provider = provider;
|
Document = TemplateReader.Parse(source);
|
||||||
SourceFilename = sourceFilename;
|
|
||||||
PseudoFilename = sourceFilename;
|
|
||||||
|
|
||||||
LoadSource(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Template(String source,String pseudoFilename,TemplateProvider provider)
|
|
||||||
{
|
|
||||||
Provider = provider;
|
|
||||||
PseudoFilename = pseudoFilename;
|
|
||||||
|
|
||||||
LoadSource(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadSource(String source)
|
|
||||||
{
|
|
||||||
if (source == null)
|
|
||||||
{
|
|
||||||
using (FileStream fileStream = new FileStream(SourceFilename, FileMode.Open))
|
|
||||||
{
|
|
||||||
byte[] loadBuffer = new byte[fileStream.Length];
|
|
||||||
fileStream.Read(loadBuffer, 0, loadBuffer.Length);
|
|
||||||
fileStream.Close();
|
|
||||||
source = Encoding.UTF8.GetString(loadBuffer);
|
|
||||||
|
|
||||||
SourceTimeStamp = File.GetLastWriteTimeUtc(SourceFilename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TemplateReader templateReader = new TemplateReader(source);
|
|
||||||
|
|
||||||
RootElement = templateReader.RootElement;
|
|
||||||
FrameExpression = templateReader.FrameExpression;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String Generate()
|
|
||||||
{
|
|
||||||
return Generate(false);
|
|
||||||
}
|
|
||||||
public String Generate(bool unframed)
|
|
||||||
{
|
|
||||||
Context context = new Context(this);
|
|
||||||
return Generate(context);
|
|
||||||
}
|
|
||||||
public String Generate(Context context)
|
|
||||||
{
|
|
||||||
return Generate(context, false);
|
|
||||||
}
|
|
||||||
public String Generate(Context context,bool unframed)
|
|
||||||
{
|
|
||||||
if (SourceFilename != null)
|
|
||||||
{
|
|
||||||
DateTime ts = File.GetLastWriteTimeUtc(SourceFilename);
|
|
||||||
if (ts > SourceTimeStamp)
|
|
||||||
LoadSource(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringWriter writer = new StringWriter();
|
|
||||||
RootElement.Generate(writer, context);
|
|
||||||
|
|
||||||
if (!unframed && (FrameExpression != null))
|
|
||||||
{
|
|
||||||
Template frame = context.Provider.FindTemplate(FrameExpression.Evaluate(context.ExpressionContext) as string);
|
|
||||||
if (frame == null)
|
|
||||||
{
|
|
||||||
throw new NullReferenceException(String.Format("FindTemplate() returned null for {0}",FrameExpression.Source));
|
|
||||||
}
|
|
||||||
return frame.Generate(new Context(context, writer.ToString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return writer.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class Context
|
|
||||||
{
|
|
||||||
public Context Parent { get; }
|
|
||||||
public Template Template { get; }
|
|
||||||
public TemplateProvider Provider => Template.Provider;
|
|
||||||
|
|
||||||
public String FramedContent { get; }
|
|
||||||
public Expression.Context ExpressionContext { get; }
|
|
||||||
|
|
||||||
public Context(Template template)
|
|
||||||
{
|
|
||||||
Template = template;
|
|
||||||
ExpressionContext = new Expression.Context(null, null);
|
|
||||||
ExpressionContext.AddMappedValue("__template__", template.SourceFilename);
|
|
||||||
ExpressionContext.AddMappedValue("__provider__", template.Provider);
|
|
||||||
}
|
|
||||||
public Context(Context parent,String framedContent)
|
|
||||||
{
|
|
||||||
Parent = parent;
|
|
||||||
Template = parent.Template;
|
|
||||||
FramedContent = framedContent;
|
|
||||||
ExpressionContext = new Expression.Context(parent.ExpressionContext);
|
|
||||||
if (framedContent != null)
|
|
||||||
ExpressionContext.SetMappedValue("__frame__", framedContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private void Load()
|
||||||
|
{
|
||||||
|
Document = TemplateReader.ReadTemplate(FileName);
|
||||||
|
LastWriteTime = File.GetLastWriteTime(FileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ITemplateResolver Resolver { get; }
|
||||||
|
public string FileName { get; }
|
||||||
|
public TemplateDocument Document { get; private set; }
|
||||||
|
public DateTime LastWriteTime { get; private set; }
|
||||||
|
|
||||||
|
public void Render(TextWriter target) => Render(target, Array.Empty<KeyValuePair<string, object>>());
|
||||||
|
public void Render(TextWriter target, IEnumerable<KeyValuePair<string, object>> scriptObjects)
|
||||||
|
{
|
||||||
|
Context context = new Context(Resolver, scriptObjects, target);
|
||||||
|
Render(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Render(Context context)
|
||||||
|
{
|
||||||
|
DateTime currentLastWriteTime = File.GetLastWriteTime(FileName);
|
||||||
|
if (currentLastWriteTime > LastWriteTime)
|
||||||
|
Load();
|
||||||
|
|
||||||
|
Document.RenderTemplate(context);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
using System;
|
|
||||||
namespace ln.templates
|
|
||||||
{
|
|
||||||
public interface TemplateProvider
|
|
||||||
{
|
|
||||||
Template FindTemplate(string templatePath);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,151 +1,104 @@
|
||||||
using System;
|
// /**
|
||||||
using System.Collections.Generic;
|
// * File: TemplateReader.cs
|
||||||
using ln.templates.elements;
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Net;
|
||||||
|
using ln.templates.html;
|
||||||
|
|
||||||
namespace ln.templates
|
namespace ln.templates
|
||||||
{
|
{
|
||||||
public class TemplateReader
|
public class TemplateReader : ElementReader
|
||||||
{
|
{
|
||||||
public ContainerElement RootElement { get; private set; } = new ContainerElement(null);
|
|
||||||
public ContainerElement CurrentElement { get; private set; }
|
|
||||||
|
|
||||||
public Expression FrameExpression { get; private set; }
|
public virtual TemplateDocument TemplateDocument => Document as TemplateDocument;
|
||||||
|
|
||||||
public String Source { get; private set; }
|
protected TemplateReader()
|
||||||
|
|
||||||
public TemplateReader(String source)
|
|
||||||
{
|
{
|
||||||
Source = source;
|
|
||||||
Parse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Parse()
|
public override DocumentElement CreateDocument() => new TemplateDocument();
|
||||||
|
public override Element CreateElement(string tagName) => TemplateElement.Create(tagName);
|
||||||
|
|
||||||
|
public override void CloseTag(string tagName)
|
||||||
{
|
{
|
||||||
CurrentElement = RootElement;
|
Element saveCurrentElement = CurrentElement;
|
||||||
|
base.CloseTag(tagName);
|
||||||
while (Source.Length > 0)
|
|
||||||
|
if (saveCurrentElement.HasAttribute("v-slot") && (saveCurrentElement.Parent is TemplateElement parentElement))
|
||||||
{
|
{
|
||||||
String t, op;
|
parentElement.SetSlot(saveCurrentElement.GetAttribute("v-slot"), saveCurrentElement);
|
||||||
int i, j;
|
parentElement.RemoveChild(saveCurrentElement);
|
||||||
|
saveCurrentElement.RemoveAttribute("v-slot");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
i = Source.IndexOf("<%");
|
public override void Text(string text)
|
||||||
if (i == -1)
|
{
|
||||||
|
if (text.Contains("{{"))
|
||||||
|
{
|
||||||
|
int pOpen = 0;
|
||||||
|
int pClose = 0;
|
||||||
|
|
||||||
|
while (pOpen < text.Length)
|
||||||
{
|
{
|
||||||
t = Source;
|
pOpen = text.IndexOf("{{", pClose);
|
||||||
op = null;
|
if (pOpen == -1)
|
||||||
Source = "";
|
pOpen = text.Length;
|
||||||
}
|
|
||||||
else
|
string preText = text.Substring(pClose, pOpen - pClose);
|
||||||
{
|
if (preText.Length > 0)
|
||||||
t = Source.Substring(0, i);
|
|
||||||
j = Source.IndexOf("%>", i + 1);
|
|
||||||
if (j == -1)
|
|
||||||
{
|
{
|
||||||
throw new FormatException(String.Format("missing '%>' in {0}", Source.Substring(i, 64)));
|
CurrentElement.AppendChild(CreateTextElement(preText));
|
||||||
}
|
}
|
||||||
op = Source.Substring(i + 2, j - i - 2);
|
|
||||||
Source = Source.Substring(j + 2);
|
if (pOpen == text.Length)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pOpen += 2;
|
||||||
|
|
||||||
|
pClose = text.IndexOf("}}", pOpen);
|
||||||
|
if (pClose == -1)
|
||||||
|
throw new FormatException("missing }}");
|
||||||
|
|
||||||
|
string expr = text.Substring(pOpen, pClose - pOpen);
|
||||||
|
|
||||||
|
pClose += 2;
|
||||||
|
|
||||||
|
CurrentElement.AppendChild(new ExpressionElement(expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
new TextElement(CurrentElement, t);
|
} else
|
||||||
if (op != null)
|
{
|
||||||
ParseOP(op);
|
CurrentElement.AppendChild(
|
||||||
|
CreateTextElement(text)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ParseOP(String op)
|
|
||||||
|
public static TemplateDocument Parse(string source)
|
||||||
{
|
{
|
||||||
StringReader reader = new StringReader(op);
|
using (StringReader reader = new StringReader(source))
|
||||||
String opkey;
|
return ReadTemplate(reader);
|
||||||
|
|
||||||
if (reader.Peek() == '=')
|
|
||||||
{
|
|
||||||
reader.Read();
|
|
||||||
opkey = "=";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
opkey = reader.PopWord();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (opkey)
|
|
||||||
{
|
|
||||||
case "=":
|
|
||||||
new ExpressionElement(CurrentElement, new Expression(reader.ReadToEnd()));
|
|
||||||
break;
|
|
||||||
case "frame":
|
|
||||||
FrameExpression = new Expression(reader.ReadToEnd());
|
|
||||||
break;
|
|
||||||
case "iterate":
|
|
||||||
String iterName = reader.PopWord();
|
|
||||||
CurrentElement = new IteratorElement(CurrentElement, iterName, new Expression(reader.ReadToEnd()));
|
|
||||||
break;
|
|
||||||
case "if":
|
|
||||||
CurrentElement = new ConditionalElement(CurrentElement, new Expression(reader.ReadToEnd()));
|
|
||||||
break;
|
|
||||||
case "else":
|
|
||||||
if (CurrentElement is ConditionalElement)
|
|
||||||
{
|
|
||||||
ConditionalElement ce = CurrentElement as ConditionalElement;
|
|
||||||
CurrentElement = ce.AlternativeContainer;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "elseif":
|
|
||||||
if (CurrentElement is ConditionalElement)
|
|
||||||
{
|
|
||||||
ConditionalElement ce = CurrentElement as ConditionalElement;
|
|
||||||
CurrentElement = new ConditionalElement(null, new Expression(reader.ReadToEnd()));
|
|
||||||
ce.AlternativeContainer = CurrentElement;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "include":
|
|
||||||
new IncludeElement(CurrentElement, new Expression(reader.ReadToEnd()));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "set":
|
|
||||||
String setName = reader.PopWord();
|
|
||||||
new SetElement(CurrentElement, setName, new Expression(reader.ReadToEnd()));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "end":
|
|
||||||
CurrentElement = CurrentElement.Container;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException(opkey);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
public static TemplateDocument ReadTemplate(string filename)
|
||||||
|
|
||||||
static class StringHelper
|
|
||||||
{
|
|
||||||
public static int IndexOf(this String s,Func<char,bool> predicate)
|
|
||||||
{
|
{
|
||||||
for (int i=0;i<s.Length;i++)
|
using (StreamReader reader = new StreamReader(filename))
|
||||||
{
|
return ReadTemplate(reader);
|
||||||
if (predicate(s[i]))
|
}
|
||||||
return i;
|
public static TemplateDocument ReadTemplate(TextReader reader)
|
||||||
}
|
{
|
||||||
return -1;
|
var templateReader = new TemplateReader();
|
||||||
|
templateReader.Read(reader);
|
||||||
|
return templateReader.TemplateDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SkipWhiteSpace(this TextReader reader)
|
|
||||||
{
|
|
||||||
while (char.IsWhiteSpace((char)reader.Peek()))
|
|
||||||
{
|
|
||||||
reader.Read();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static String PopWord(this TextReader reader)
|
|
||||||
{
|
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
while ((reader.Peek()!=-1) && !char.IsWhiteSpace((char)reader.Peek()))
|
|
||||||
{
|
|
||||||
stringBuilder.Append((char)reader.Read());
|
|
||||||
}
|
|
||||||
reader.SkipWhiteSpace();
|
|
||||||
return stringBuilder.ToString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,99 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Collections;
|
|
||||||
|
|
||||||
namespace ln.templates.elements
|
|
||||||
{
|
|
||||||
public class ConditionalElement : ContainerElement
|
|
||||||
{
|
|
||||||
private ContainerElement alternativeContainer;
|
|
||||||
|
|
||||||
public Expression Expression { get; private set; }
|
|
||||||
|
|
||||||
public ConditionalElement(ContainerElement container,Expression expression)
|
|
||||||
:base(container)
|
|
||||||
{
|
|
||||||
Expression = expression;
|
|
||||||
AlternativeContainer = new ContainerElement(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsTrue(object value)
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
(value != null) &&
|
|
||||||
((value is int) && ((int)value != 0)) ||
|
|
||||||
((value is double) && ((double)value != 0.0)) ||
|
|
||||||
((value is bool) && ((bool)value)) ||
|
|
||||||
((value is string) && (!String.Empty.Equals(value)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Generate(TextWriter writer, Template.Context context)
|
|
||||||
{
|
|
||||||
if (Expression.IsTrue(context.ExpressionContext))
|
|
||||||
{
|
|
||||||
GenerateChildren(writer, context);
|
|
||||||
}
|
|
||||||
else if (AlternativeContainer != null)
|
|
||||||
{
|
|
||||||
AlternativeContainer.Generate(writer, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContainerElement AlternativeContainer {
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return alternativeContainer;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (alternativeContainer != null)
|
|
||||||
{
|
|
||||||
alternativeContainer.Container = null;
|
|
||||||
}
|
|
||||||
if (value != null)
|
|
||||||
{
|
|
||||||
value.Container = Container;
|
|
||||||
}
|
|
||||||
alternativeContainer = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class IteratorElement : ConditionalElement
|
|
||||||
{
|
|
||||||
public String IterName { get; private set; }
|
|
||||||
|
|
||||||
public IteratorElement(ContainerElement container,String iterName,Expression expression)
|
|
||||||
:base(container, expression)
|
|
||||||
{
|
|
||||||
IterName = iterName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Generate(TextWriter writer, Template.Context context)
|
|
||||||
{
|
|
||||||
object v = Expression.Evaluate(context.ExpressionContext);
|
|
||||||
|
|
||||||
if (v == null)
|
|
||||||
{
|
|
||||||
AlternativeContainer.Generate(writer, context);
|
|
||||||
}
|
|
||||||
else if (v is IEnumerable)
|
|
||||||
{
|
|
||||||
Template.Context icontext = new Template.Context(context,null);
|
|
||||||
|
|
||||||
bool b = false;
|
|
||||||
IEnumerable en = v as IEnumerable;
|
|
||||||
foreach (object o in en)
|
|
||||||
{
|
|
||||||
b = true;
|
|
||||||
icontext.ExpressionContext.SetMappedValue(IterName, o);
|
|
||||||
GenerateChildren(writer, icontext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace ln.templates.elements
|
|
||||||
{
|
|
||||||
public abstract class Element
|
|
||||||
{
|
|
||||||
public ContainerElement Container { get; internal set; }
|
|
||||||
|
|
||||||
protected Element(ContainerElement container)
|
|
||||||
{
|
|
||||||
Container = container;
|
|
||||||
if (Container != null)
|
|
||||||
Container.children.Add(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void Generate(TextWriter writer, Template.Context context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ContainerElement : Element
|
|
||||||
{
|
|
||||||
internal List<Element> children { get; } = new List<Element>();
|
|
||||||
|
|
||||||
public ContainerElement(ContainerElement container)
|
|
||||||
:base(container)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GenerateChildren(TextWriter writer, Template.Context context)
|
|
||||||
{
|
|
||||||
foreach (Element c in children)
|
|
||||||
c.Generate(writer, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Generate(TextWriter writer, Template.Context context)
|
|
||||||
{
|
|
||||||
GenerateChildren(writer, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Element[] Children
|
|
||||||
{
|
|
||||||
get => children.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TextElement : Element
|
|
||||||
{
|
|
||||||
public String Text { get; }
|
|
||||||
public TextElement(ContainerElement container,String text)
|
|
||||||
:base(container)
|
|
||||||
{
|
|
||||||
Text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Generate(TextWriter writer, Template.Context context)
|
|
||||||
{
|
|
||||||
writer.Write(Text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ExpressionElement : Element
|
|
||||||
{
|
|
||||||
public Expression Expression { get; }
|
|
||||||
|
|
||||||
public ExpressionElement(ContainerElement container,Expression expression)
|
|
||||||
:base(container)
|
|
||||||
{
|
|
||||||
Expression = expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Generate(TextWriter writer, Template.Context context)
|
|
||||||
{
|
|
||||||
object v = Expression.Evaluate(context.ExpressionContext);
|
|
||||||
if (v != null)
|
|
||||||
writer.Write(v.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public class SetElement : Element
|
|
||||||
{
|
|
||||||
public String SetName { get; }
|
|
||||||
public Expression Expression { get; }
|
|
||||||
|
|
||||||
public SetElement(ContainerElement container, String setName,Expression expression)
|
|
||||||
: base(container)
|
|
||||||
{
|
|
||||||
SetName = setName;
|
|
||||||
Expression = expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Generate(TextWriter writer, Template.Context context)
|
|
||||||
{
|
|
||||||
object v = Expression.Evaluate(context.ExpressionContext);
|
|
||||||
context.ExpressionContext.SetMappedValue(SetName, v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace ln.templates.elements
|
|
||||||
{
|
|
||||||
public class IncludeElement : Element
|
|
||||||
{
|
|
||||||
Expression TargetExpression { get; set; }
|
|
||||||
|
|
||||||
public IncludeElement(ContainerElement container,Expression targetExpression)
|
|
||||||
:base(container)
|
|
||||||
{
|
|
||||||
TargetExpression = targetExpression;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Generate(TextWriter writer, Template.Context context,String templatePath)
|
|
||||||
{
|
|
||||||
Template iTemplate = context.Provider.FindTemplate(templatePath);
|
|
||||||
Template.Context iContext = new Template.Context(context, null);
|
|
||||||
String iContent = iTemplate.Generate(iContext);
|
|
||||||
|
|
||||||
writer.Write(iContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Generate(TextWriter writer, Template.Context context)
|
|
||||||
{
|
|
||||||
object target = TargetExpression.Evaluate(context.ExpressionContext);
|
|
||||||
|
|
||||||
if (target is IEnumerable<String>)
|
|
||||||
{
|
|
||||||
IEnumerable<String> targets = (IEnumerable<String>)target;
|
|
||||||
foreach (String t in targets)
|
|
||||||
{
|
|
||||||
Generate(writer, context, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Generate(writer, context, target as String);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,9 +19,12 @@ namespace ln.templates.html
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string DocType { get; set; }
|
||||||
|
|
||||||
public override void Render(TextWriter writer)
|
public override void Render(TextWriter writer)
|
||||||
{
|
{
|
||||||
writer.Write("<!DOCTYPE html>");
|
if (DocType is not null)
|
||||||
|
writer.Write("<!DOCTYPE {0}>", DocType);
|
||||||
|
|
||||||
foreach (Element element in Children)
|
foreach (Element element in Children)
|
||||||
element.Render(writer);
|
element.Render(writer);
|
||||||
|
|
|
@ -38,9 +38,17 @@ namespace ln.templates.html
|
||||||
public virtual bool HasAttribute(string attributeName) => attributes.ContainsKey(attributeName);
|
public virtual bool HasAttribute(string attributeName) => attributes.ContainsKey(attributeName);
|
||||||
public virtual string GetAttribute(string attributeName) => attributes[attributeName];
|
public virtual string GetAttribute(string attributeName) => attributes[attributeName];
|
||||||
public virtual void SetAttribute(string attributeName, string attributeValue) => attributes[attributeName] = attributeValue;
|
public virtual void SetAttribute(string attributeName, string attributeValue) => attributes[attributeName] = attributeValue;
|
||||||
|
public virtual void RemoveAttribute(string attributeName) => attributes.Remove(attributeName);
|
||||||
|
|
||||||
|
public virtual string GetAttribute(string attributeName, string defaultValue)
|
||||||
|
{
|
||||||
|
if (attributes.TryGetValue(attributeName, out string value))
|
||||||
|
return value;
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<Element> Children => children;
|
public IEnumerable<Element> Children => children;
|
||||||
public void AppendChild(Element element)
|
public virtual void AppendChild(Element element)
|
||||||
{
|
{
|
||||||
if (element.Parent != null)
|
if (element.Parent != null)
|
||||||
element.Parent.RemoveChild(element);
|
element.Parent.RemoveChild(element);
|
||||||
|
@ -55,7 +63,7 @@ namespace ln.templates.html
|
||||||
element.Parent = this;
|
element.Parent = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void RemoveChild(Element element)
|
public virtual void RemoveChild(Element element)
|
||||||
{
|
{
|
||||||
if (element.Parent != this)
|
if (element.Parent != this)
|
||||||
throw new KeyNotFoundException();
|
throw new KeyNotFoundException();
|
||||||
|
|
|
@ -61,6 +61,12 @@ namespace ln.templates.html
|
||||||
{
|
{
|
||||||
Pop();
|
Pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void DOCTYPE(string type)
|
||||||
|
{
|
||||||
|
Document.DocType = type;
|
||||||
|
}
|
||||||
|
|
||||||
public override void Attribute(string attributeName, string attributeValue)
|
public override void Attribute(string attributeName, string attributeValue)
|
||||||
{
|
{
|
||||||
CurrentElement.SetAttribute(attributeName, attributeValue);
|
CurrentElement.SetAttribute(attributeName, attributeValue);
|
||||||
|
|
|
@ -29,10 +29,10 @@ namespace ln.templates.html
|
||||||
writer.Write("{{{{{0}}}}}", ExpressionText);
|
writer.Write("{{{{{0}}}}}", ExpressionText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void RenderTemplate(RenderContext renderContext)
|
public override void RenderTemplate(Context renderContext)
|
||||||
{
|
{
|
||||||
object o = Expression.Resolve(renderContext);
|
object o = Expression.Resolve(renderContext.Engine);
|
||||||
renderContext.ContentWriter.Write(o?.ToString());
|
renderContext.TargetWriter.Write(o?.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace ln.templates.html
|
|
||||||
{
|
|
||||||
public class FileSystemTemplateSource : ITemplateSource
|
|
||||||
{
|
|
||||||
public String BasePath { get; }
|
|
||||||
public bool Throws { get; }
|
|
||||||
|
|
||||||
Dictionary<string, TemplateDocument> templateDocuments = new Dictionary<string, TemplateDocument>();
|
|
||||||
|
|
||||||
public FileSystemTemplateSource(String path,bool throws)
|
|
||||||
:this(path)
|
|
||||||
{
|
|
||||||
Throws = throws;
|
|
||||||
}
|
|
||||||
public FileSystemTemplateSource(String path)
|
|
||||||
{
|
|
||||||
if (!Directory.Exists(path))
|
|
||||||
throw new FileNotFoundException();
|
|
||||||
BasePath = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TemplateDocument GetTemplateByPath(string path) => GetTemplateByPath(path, Throws);
|
|
||||||
public TemplateDocument GetTemplateByPath(string path,bool _throw)
|
|
||||||
{
|
|
||||||
string templatePath = Path.Combine(BasePath, path);
|
|
||||||
if (!File.Exists(templatePath))
|
|
||||||
{
|
|
||||||
if (templateDocuments.ContainsKey(path))
|
|
||||||
templateDocuments.Remove(path);
|
|
||||||
|
|
||||||
if (_throw)
|
|
||||||
throw new FileNotFoundException();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!templateDocuments.TryGetValue(path,out TemplateDocument templateDocument))
|
|
||||||
{
|
|
||||||
templateDocument = ReadTemplateDocument(path);
|
|
||||||
templateDocuments[path] = templateDocument;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
DateTime templateDateTime = File.GetLastWriteTime(templatePath);
|
|
||||||
if (templateDateTime.Ticks != templateDocument.TemplateVersion)
|
|
||||||
{
|
|
||||||
templateDocument = ReadTemplateDocument(path);
|
|
||||||
templateDocuments[path] = templateDocument;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return templateDocument;
|
|
||||||
}
|
|
||||||
|
|
||||||
TemplateDocument ReadTemplateDocument(string path)
|
|
||||||
{
|
|
||||||
string templatePath = Path.Combine(BasePath, path);
|
|
||||||
|
|
||||||
TemplateReader templateReader = new TemplateReader();
|
|
||||||
using (StreamReader sr = new StreamReader(templatePath))
|
|
||||||
{
|
|
||||||
templateReader.Read(sr);
|
|
||||||
}
|
|
||||||
templateReader.TemplateDocument.TemplateVersion = File.GetLastWriteTime(templatePath).Ticks;
|
|
||||||
|
|
||||||
return templateReader.TemplateDocument;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,7 +13,7 @@ using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
namespace ln.templates.html
|
namespace ln.templates.html
|
||||||
{
|
{
|
||||||
public class HtmlReader
|
public class HtmlReader
|
||||||
{
|
{
|
||||||
public HtmlReader()
|
public HtmlReader()
|
||||||
{
|
{
|
||||||
|
@ -124,7 +124,7 @@ namespace ln.templates.html
|
||||||
}
|
}
|
||||||
void ReadLWS(TextReader textReader) => ReadToken(textReader, char.IsWhiteSpace);
|
void ReadLWS(TextReader textReader) => ReadToken(textReader, char.IsWhiteSpace);
|
||||||
|
|
||||||
public string ReadTagName(TextReader textReader) => ReadTokenLWS(textReader, (ch) => char.IsLetterOrDigit(ch));
|
public string ReadTagName(TextReader textReader) => ReadTokenLWS(textReader, (ch) => char.IsLetterOrDigit(ch) || (ch == '-'));
|
||||||
public string ReadAttributeName(TextReader textReader) => ReadTokenLWS(textReader, (ch) => !char.IsWhiteSpace(ch) && (ch != '\0') && (ch != '"') && (ch != '\'') && (ch != '>') && (ch != '/') && (ch != '='));
|
public string ReadAttributeName(TextReader textReader) => ReadTokenLWS(textReader, (ch) => !char.IsWhiteSpace(ch) && (ch != '\0') && (ch != '"') && (ch != '\'') && (ch != '>') && (ch != '/') && (ch != '='));
|
||||||
public string ReadAttributeValue(TextReader textReader)
|
public string ReadAttributeValue(TextReader textReader)
|
||||||
{
|
{
|
||||||
|
@ -147,7 +147,7 @@ namespace ln.templates.html
|
||||||
public void ReadText(TextReader textReader)
|
public void ReadText(TextReader textReader)
|
||||||
{
|
{
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
while (textReader.Peek() != '<')
|
while ((textReader.Peek() >= 0) && (textReader.Peek() != '<'))
|
||||||
stringBuilder.Append((char)textReader.Read());
|
stringBuilder.Append((char)textReader.Read());
|
||||||
|
|
||||||
Text(stringBuilder.ToString());
|
Text(stringBuilder.ToString());
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace ln.templates.html
|
|
||||||
{
|
|
||||||
public interface ITemplateSource
|
|
||||||
{
|
|
||||||
TemplateDocument GetTemplateByPath(string path);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
// /**
|
|
||||||
// * File: RenderContext.cs
|
|
||||||
// * Author: haraldwolff
|
|
||||||
// *
|
|
||||||
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
|
||||||
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
|
||||||
// *
|
|
||||||
// *
|
|
||||||
// **/
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using ln.templates.script;
|
|
||||||
namespace ln.templates.html
|
|
||||||
{
|
|
||||||
public class RenderContext : IScriptContext
|
|
||||||
{
|
|
||||||
public TextWriter ContentWriter { get; }
|
|
||||||
public ITemplateSource TemplateSource { get; set; }
|
|
||||||
|
|
||||||
Dictionary<string, object> scriptObjects = new Dictionary<string, object>();
|
|
||||||
public IEnumerable<string> ScriptObjectNames => scriptObjects.Keys;
|
|
||||||
|
|
||||||
public RenderContext(TextWriter contentWriter)
|
|
||||||
{
|
|
||||||
ContentWriter = contentWriter;
|
|
||||||
}
|
|
||||||
public RenderContext(TextWriter contentWriter,ITemplateSource templateSource)
|
|
||||||
:this(contentWriter)
|
|
||||||
{
|
|
||||||
TemplateSource = templateSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dictionary<string, object> values = new Dictionary<string, object>();
|
|
||||||
|
|
||||||
public object GetScriptObject(string itemName) => scriptObjects[itemName];
|
|
||||||
public void SetScriptObject(string itemName, object value) => scriptObjects[itemName] = value;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,30 +13,22 @@ namespace ln.templates.html
|
||||||
{
|
{
|
||||||
public class TemplateDocument : DocumentElement
|
public class TemplateDocument : DocumentElement
|
||||||
{
|
{
|
||||||
public long TemplateVersion { get; set; }
|
|
||||||
|
|
||||||
public TemplateDocument()
|
public TemplateDocument()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RenderTemplate(TextWriter contentWriter) => RenderTemplate(new RenderContext(contentWriter));
|
public void RenderTemplate(Context renderContext)
|
||||||
public void RenderTemplate(RenderContext renderContext,bool withoutDocType = false)
|
|
||||||
{
|
{
|
||||||
if (!withoutDocType)
|
if (DocType is not null)
|
||||||
renderContext.ContentWriter.Write("<!DOCTYPE html>");
|
renderContext.TargetWriter.Write("<!DOCTYPE {0}>", DocType);
|
||||||
|
|
||||||
foreach (Element element in Children)
|
foreach (var child in Children)
|
||||||
{
|
{
|
||||||
if (element is TemplateElement templateElement)
|
if (child is TemplateElement templateElement)
|
||||||
{
|
|
||||||
templateElement.RenderTemplate(renderContext);
|
templateElement.RenderTemplate(renderContext);
|
||||||
} else
|
else
|
||||||
{
|
child.Render(renderContext.TargetWriter);
|
||||||
renderContext.ContentWriter.Write(element.ToString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderContext.ContentWriter.Flush();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
namespace ln.templates.html;
|
||||||
|
|
||||||
|
public partial class TemplateElement
|
||||||
|
{
|
||||||
|
private class SlotElement : TemplateElement
|
||||||
|
{
|
||||||
|
public SlotElement(string tagName)
|
||||||
|
:base(tagName){}
|
||||||
|
|
||||||
|
public string Name => GetAttribute("name", "");
|
||||||
|
public override void RenderElement(Context renderContext)
|
||||||
|
{
|
||||||
|
if (renderContext.TryGetSlot(Name, out Element slot))
|
||||||
|
{
|
||||||
|
RenderElements(new Context(renderContext), new []{ slot });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RenderElements(new Context(renderContext), Children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,24 +10,33 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Jint.Native;
|
||||||
|
using Jint.Native.Object;
|
||||||
using ln.templates.script;
|
using ln.templates.script;
|
||||||
|
|
||||||
namespace ln.templates.html
|
namespace ln.templates.html
|
||||||
{
|
{
|
||||||
public class TemplateElement : Element
|
public partial class TemplateElement : Element
|
||||||
{
|
{
|
||||||
|
private static string[] hiddenTags = new[] { "template", "slot" };
|
||||||
|
|
||||||
Dictionary<string, NewExpression> expressions = new Dictionary<string, NewExpression>();
|
Dictionary<string, NewExpression> expressions = new Dictionary<string, NewExpression>();
|
||||||
Dictionary<string, NewExpression> loops = new Dictionary<string, NewExpression>();
|
Dictionary<string, NewExpression> loops = new Dictionary<string, NewExpression>();
|
||||||
List<NewExpression> conditions = new List<NewExpression>();
|
List<NewExpression> conditions = new List<NewExpression>();
|
||||||
|
|
||||||
NewExpression includeExpression;
|
private Dictionary<string, Element> _slots = new Dictionary<string, Element>();
|
||||||
|
|
||||||
|
private NewExpression classExpression;
|
||||||
|
|
||||||
bool hideTag;
|
bool hideTag;
|
||||||
|
|
||||||
public TemplateElement(string tagName)
|
public TemplateElement(string tagName)
|
||||||
:base(tagName)
|
:base(tagName)
|
||||||
{
|
{
|
||||||
hideTag = "template".Equals(tagName.ToLower());
|
hideTag = hiddenTags.Contains(tagName.ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetAttribute(string attributeName, string attributeValue)
|
public override void SetAttribute(string attributeName, string attributeValue)
|
||||||
|
@ -36,27 +45,50 @@ namespace ln.templates.html
|
||||||
loops.Add(attributeName.Substring(6), new NewExpression(attributeValue));
|
loops.Add(attributeName.Substring(6), new NewExpression(attributeValue));
|
||||||
else if (attributeName.Equals("v-if", StringComparison.InvariantCultureIgnoreCase))
|
else if (attributeName.Equals("v-if", StringComparison.InvariantCultureIgnoreCase))
|
||||||
conditions.Add(new NewExpression(attributeValue));
|
conditions.Add(new NewExpression(attributeValue));
|
||||||
else if (attributeName.Equals("v-include", StringComparison.InvariantCultureIgnoreCase))
|
else if (attributeName.Equals(":class", StringComparison.InvariantCultureIgnoreCase))
|
||||||
includeExpression = new NewExpression(attributeValue);
|
classExpression = new NewExpression(attributeValue);
|
||||||
|
else if (attributeName.StartsWith("::"))
|
||||||
|
base.SetAttribute(attributeName, attributeValue);
|
||||||
else if (attributeName[0] == ':')
|
else if (attributeName[0] == ':')
|
||||||
expressions.Add(attributeName.Substring(1), new NewExpression(attributeValue));
|
expressions.Add(attributeName.Substring(1), new NewExpression(attributeValue));
|
||||||
else
|
else
|
||||||
base.SetAttribute(attributeName, attributeValue);
|
base.SetAttribute(attributeName, attributeValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void RenderTemplate(RenderContext renderContext)
|
public void SetSlot(string slotName, Element slotElement)
|
||||||
{
|
{
|
||||||
if (loops.Count == 0)
|
_slots.Add(slotName, slotElement);
|
||||||
{
|
}
|
||||||
RenderElement(renderContext);
|
|
||||||
} else
|
/**
|
||||||
{
|
* This is the render method to be called from external callers
|
||||||
Stack<KeyValuePair<string, NewExpression>> loopStack = new Stack<KeyValuePair<string, NewExpression>>(loops);
|
*/
|
||||||
DoLoop(renderContext, loopStack);
|
public virtual void RenderTemplate(Context renderContext)
|
||||||
}
|
{
|
||||||
|
if (loops.Count == 0)
|
||||||
|
{
|
||||||
|
RenderElement(renderContext);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Stack<KeyValuePair<string, NewExpression>> loopStack =
|
||||||
|
new Stack<KeyValuePair<string, NewExpression>>(loops);
|
||||||
|
DoLoop(renderContext, loopStack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoLoop(RenderContext renderContext, Stack<KeyValuePair<string, NewExpression>> loopStack)
|
private Template TemplateFromExpression(Context renderContext, NewExpression expression)
|
||||||
|
{
|
||||||
|
object templateValue = expression.Resolve(renderContext.Engine);
|
||||||
|
if (!(templateValue is Template template))
|
||||||
|
{
|
||||||
|
template = renderContext.Resolver.GetTemplateByPath((string)templateValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void DoLoop(Context renderContext, Stack<KeyValuePair<string, NewExpression>> loopStack)
|
||||||
{
|
{
|
||||||
if (loopStack.Count == 0)
|
if (loopStack.Count == 0)
|
||||||
{
|
{
|
||||||
|
@ -64,77 +96,193 @@ namespace ln.templates.html
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
KeyValuePair<string, NewExpression> loop = loopStack.Pop();
|
KeyValuePair<string, NewExpression> loop = loopStack.Pop();
|
||||||
IEnumerable enumerable = (IEnumerable)loop.Value.Resolve(renderContext);
|
IEnumerable enumerable = (IEnumerable)loop.Value.Resolve(renderContext.Engine);
|
||||||
foreach (object o in enumerable)
|
if (enumerable != null)
|
||||||
{
|
{
|
||||||
renderContext.SetScriptObject(loop.Key, o);
|
bool hadBinding = renderContext.Engine.Realm.GlobalEnv.HasBinding(loop.Key);
|
||||||
DoLoop(renderContext, loopStack);
|
JsValue _save = null;
|
||||||
|
if (hadBinding)
|
||||||
|
_save = renderContext.Engine.Realm.GlobalEnv.GetBindingValue(loop.Key, false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
foreach (object o in enumerable)
|
||||||
|
{
|
||||||
|
renderContext.Engine.SetValue(loop.Key, o);
|
||||||
|
DoLoop(renderContext, loopStack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (hadBinding)
|
||||||
|
renderContext.Engine.Realm.GlobalEnv.SetMutableBinding(loop.Key, _save, false);
|
||||||
|
else
|
||||||
|
renderContext.Engine.Realm.GlobalEnv.DeleteBinding(loop.Key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
loopStack.Push(loop);
|
loopStack.Push(loop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderElement(RenderContext renderContext)
|
/**
|
||||||
|
* This is the render method to be called to render the element itself (tag, attributes, children, ... )
|
||||||
|
*/
|
||||||
|
public virtual void RenderElement(Context renderContext)
|
||||||
{
|
{
|
||||||
if (checkConditions(renderContext))
|
if (checkConditions(renderContext))
|
||||||
{
|
{
|
||||||
if (includeExpression != null)
|
if (TryGetAttribute(renderContext, "v-include", out string templatePath))
|
||||||
{
|
{
|
||||||
object includeValue = includeExpression.Resolve(renderContext);
|
Template template = renderContext.Resolver.GetTemplateByPath(templatePath);
|
||||||
if (!(includeValue is TemplateDocument includeTemplate))
|
Context context = new Context(renderContext);
|
||||||
{
|
if (this._slots.Count == 0)
|
||||||
includeTemplate = renderContext.TemplateSource.GetTemplateByPath((string)includeValue);
|
context.SetSlot("", this);
|
||||||
}
|
else
|
||||||
includeTemplate.RenderTemplate(renderContext, true);
|
context.SetSlots(this._slots);
|
||||||
|
|
||||||
|
template.Render(context);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!hideTag)
|
if (!hideTag)
|
||||||
{
|
{
|
||||||
renderContext.ContentWriter.Write("<{0}", Name);
|
object classObject = null;
|
||||||
foreach (KeyValuePair<String, String> attributePair in Attributes)
|
if (classExpression is not null)
|
||||||
renderContext.ContentWriter.Write(" {0}=\"{1}\"", attributePair.Key, attributePair.Value);
|
|
||||||
|
|
||||||
foreach (KeyValuePair<string,NewExpression> keyValuePair in expressions)
|
|
||||||
{
|
{
|
||||||
object value = keyValuePair.Value.Resolve(renderContext);
|
classObject = renderContext.Engine.Evaluate("(function(){ return " + classExpression
|
||||||
if (value != null)
|
.ExpressionText + ";})()");
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContext.TargetWriter.Write("<{0}", Name);
|
||||||
|
foreach (KeyValuePair<String, String> attributePair in Attributes)
|
||||||
|
{
|
||||||
|
if (attributePair.Key.Equals("class") && (classObject is ObjectInstance classObjectInstance))
|
||||||
{
|
{
|
||||||
renderContext.ContentWriter.Write(" {0}=\"{1}\"", keyValuePair.Key, value);
|
StringBuilder valueBuilder = new StringBuilder(attributePair.Value);
|
||||||
|
foreach (var property in classObjectInstance.GetOwnProperties())
|
||||||
|
{
|
||||||
|
if ((property.Value.Value is JsBoolean jsBooleanValue) &&
|
||||||
|
jsBooleanValue.Equals(JsBoolean.True))
|
||||||
|
valueBuilder.AppendFormat(" {0}", property.Key.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContext.TargetWriter.Write(" {0}=\"{1}\"", attributePair.Key,
|
||||||
|
valueBuilder.ToString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
renderContext.TargetWriter.Write(" {0}=\"{1}\"", attributePair.Key,
|
||||||
|
attributePair.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderContext.ContentWriter.Write(">");
|
foreach (KeyValuePair<string,NewExpression> keyValuePair in expressions)
|
||||||
|
{
|
||||||
|
object value = keyValuePair.Value.Resolve(renderContext.Engine);
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
renderContext.TargetWriter.Write(" {0}=\"{1}\"", keyValuePair.Key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContext.TargetWriter.Write(">");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsVoid)
|
if (!IsVoid)
|
||||||
{
|
{
|
||||||
foreach (Element element in Children)
|
RenderElements(renderContext, Children);
|
||||||
{
|
|
||||||
if (element is TemplateElement templateElement)
|
|
||||||
{
|
|
||||||
templateElement.RenderTemplate(renderContext);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
renderContext.ContentWriter.Write(element.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hideTag)
|
if (!hideTag)
|
||||||
renderContext.ContentWriter.Write("</{0}>", Name);
|
renderContext.TargetWriter.Write("</{0}>", Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkConditions(RenderContext renderContext)
|
public string GetAttribute(Context context, string attributeName)
|
||||||
|
{
|
||||||
|
if (expressions.TryGetValue(attributeName, out NewExpression expression))
|
||||||
|
return expression.Resolve(context.Engine).ToString();
|
||||||
|
return GetAttribute(attributeName);
|
||||||
|
}
|
||||||
|
public bool TryGetAttribute(Context context, string attributeName, out string attributeValue)
|
||||||
|
{
|
||||||
|
if (expressions.TryGetValue(attributeName, out NewExpression expression))
|
||||||
|
{
|
||||||
|
attributeValue = expression.Resolve(context.Engine).ToString();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (HasAttribute(attributeName))
|
||||||
|
{
|
||||||
|
attributeValue = GetAttribute(attributeName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeValue = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void RenderElements(Context renderContext, IEnumerable<Element> elements)
|
||||||
|
{
|
||||||
|
foreach (Element element in elements)
|
||||||
|
{
|
||||||
|
if (element is TemplateElement templateElement)
|
||||||
|
{
|
||||||
|
templateElement.RenderTemplate(renderContext);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
renderContext.TargetWriter.Write(element.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkConditions(Context renderContext)
|
||||||
{
|
{
|
||||||
foreach (NewExpression condition in conditions)
|
foreach (NewExpression condition in conditions)
|
||||||
if (!condition.IsTrue(renderContext))
|
if (!condition.IsTrue(renderContext.Engine))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TemplateScriptElement : TemplateElement
|
||||||
|
{
|
||||||
|
private StringBuilder scriptBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
public TemplateScriptElement()
|
||||||
|
: base("template-script")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void AppendChild(Element element)
|
||||||
|
{
|
||||||
|
if (element is TextElement textElement)
|
||||||
|
{
|
||||||
|
scriptBuilder.Append(textElement.Text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void RenderElement(Context renderContext) =>
|
||||||
|
renderContext.Engine.Execute(scriptBuilder.ToString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TemplateElement Create(string tagName)
|
||||||
|
{
|
||||||
|
switch (tagName)
|
||||||
|
{
|
||||||
|
case "slot":
|
||||||
|
return new SlotElement(tagName);
|
||||||
|
case "template-script":
|
||||||
|
return new TemplateScriptElement();
|
||||||
|
default:
|
||||||
|
return new TemplateElement(tagName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
// /**
|
|
||||||
// * File: TemplateReader.cs
|
|
||||||
// * Author: haraldwolff
|
|
||||||
// *
|
|
||||||
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
|
||||||
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
|
||||||
// *
|
|
||||||
// *
|
|
||||||
// **/
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
namespace ln.templates.html
|
|
||||||
{
|
|
||||||
public class TemplateReader : ElementReader
|
|
||||||
{
|
|
||||||
|
|
||||||
public virtual TemplateDocument TemplateDocument => Document as TemplateDocument;
|
|
||||||
|
|
||||||
public TemplateReader()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override DocumentElement CreateDocument() => new TemplateDocument();
|
|
||||||
public override Element CreateElement(string tagName) => new TemplateElement(tagName);
|
|
||||||
|
|
||||||
public override void Text(string text)
|
|
||||||
{
|
|
||||||
if (text.Contains("{{"))
|
|
||||||
{
|
|
||||||
int pOpen = 0;
|
|
||||||
int pClose = 0;
|
|
||||||
|
|
||||||
while (pOpen < text.Length)
|
|
||||||
{
|
|
||||||
pOpen = text.IndexOf("{{", pClose);
|
|
||||||
if (pOpen == -1)
|
|
||||||
pOpen = text.Length;
|
|
||||||
|
|
||||||
string preText = text.Substring(pClose, pOpen - pClose);
|
|
||||||
if (preText.Length > 0)
|
|
||||||
{
|
|
||||||
CurrentElement.AppendChild(CreateTextElement(preText));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pOpen == text.Length)
|
|
||||||
break;
|
|
||||||
|
|
||||||
pOpen += 2;
|
|
||||||
|
|
||||||
pClose = text.IndexOf("}}", pOpen);
|
|
||||||
if (pClose == -1)
|
|
||||||
throw new FormatException("missing }}");
|
|
||||||
|
|
||||||
string expr = text.Substring(pOpen, pClose - pOpen - 1);
|
|
||||||
|
|
||||||
pClose += 2;
|
|
||||||
|
|
||||||
CurrentElement.AppendChild(new ExpressionElement(expr));
|
|
||||||
}
|
|
||||||
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
CurrentElement.AppendChild(
|
|
||||||
CreateTextElement(text)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,10 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<PackageVersion>0.4.2</PackageVersion>
|
||||||
|
<TargetFrameworks>net5.0;net6.0</TargetFrameworks>
|
||||||
|
<LangVersion>default</LangVersion>
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>0.2.1-ci</Version>
|
<Version>0.2.1-ci</Version>
|
||||||
|
@ -13,7 +16,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Jint" Version="2.11.58" />
|
<PackageReference Include="Jint" Version="3.0.0-preview-288" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -9,12 +9,12 @@
|
||||||
// **/
|
// **/
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Jint;
|
||||||
|
|
||||||
namespace ln.templates.script
|
namespace ln.templates.script
|
||||||
{
|
{
|
||||||
public interface IScriptContext
|
public interface IScriptContext
|
||||||
{
|
{
|
||||||
IEnumerable<string> ScriptObjectNames { get; }
|
Engine GetEngine();
|
||||||
object GetScriptObject(string itemName);
|
|
||||||
void SetScriptObject(string itemName, object value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
// **/
|
// **/
|
||||||
using System;
|
using System;
|
||||||
using Jint;
|
using Jint;
|
||||||
using Jint.Native;
|
|
||||||
using Jint.Native.String;
|
|
||||||
|
|
||||||
namespace ln.templates.script
|
namespace ln.templates.script
|
||||||
{
|
{
|
||||||
|
@ -18,26 +16,20 @@ namespace ln.templates.script
|
||||||
{
|
{
|
||||||
public string ExpressionText { get; }
|
public string ExpressionText { get; }
|
||||||
|
|
||||||
|
|
||||||
public NewExpression(string expression)
|
public NewExpression(string expression)
|
||||||
{
|
{
|
||||||
ExpressionText = expression;
|
ExpressionText = expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public virtual object Resolve(IScriptContext scriptContext)
|
public virtual object Resolve() => Resolve(new Engine());
|
||||||
|
public virtual object Resolve(Engine engine) =>
|
||||||
|
engine.Evaluate(ExpressionText).ToObject();
|
||||||
|
|
||||||
|
public virtual bool IsTrue() => IsTrue(new Engine());
|
||||||
|
public virtual bool IsTrue(Engine engine)
|
||||||
{
|
{
|
||||||
Engine engine = new Engine();
|
object resolved = Resolve(engine);
|
||||||
foreach (string itemName in scriptContext.ScriptObjectNames)
|
|
||||||
engine.SetValue(itemName, scriptContext.GetScriptObject(itemName));
|
|
||||||
|
|
||||||
JsValue jsv = engine.Eval.Call(null, new Jint.Native.JsValue[] { ExpressionText });
|
|
||||||
return jsv.ToObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual bool IsTrue(IScriptContext scriptContext)
|
|
||||||
{
|
|
||||||
object resolved = Resolve(scriptContext);
|
|
||||||
if (resolved == null)
|
if (resolved == null)
|
||||||
return false;
|
return false;
|
||||||
if (resolved is bool b)
|
if (resolved is bool b)
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Text;
|
|
||||||
namespace ln.templates.streams
|
|
||||||
{
|
|
||||||
public class CharStream : PeekAbleStream<char>
|
|
||||||
{
|
|
||||||
public CharStream(char[] buffer)
|
|
||||||
:base(buffer)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
public CharStream(byte[] byteBuffer, Encoding encoding)
|
|
||||||
: this(encoding.GetChars(byteBuffer))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
public CharStream(byte[] byteBuffer)
|
|
||||||
:this(Encoding.UTF8.GetChars(byteBuffer))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Text;
|
|
||||||
namespace ln.templates.streams
|
|
||||||
{
|
|
||||||
public class PeekAbleStream<T>
|
|
||||||
{
|
|
||||||
private T[] buffer;
|
|
||||||
private int position;
|
|
||||||
|
|
||||||
public int Position => position;
|
|
||||||
public bool EndOfBuffer => (position >= buffer.Length);
|
|
||||||
|
|
||||||
public int MarkedPosition { get; set; }
|
|
||||||
|
|
||||||
public PeekAbleStream(T[] buffer)
|
|
||||||
{
|
|
||||||
this.buffer = buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Read(){
|
|
||||||
if (position < buffer.Length)
|
|
||||||
return buffer[position++];
|
|
||||||
|
|
||||||
throw new IndexOutOfRangeException("Tried to read after end of buffer");
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Peek(int shift = 0)
|
|
||||||
{
|
|
||||||
if ((position + shift) < buffer.Length)
|
|
||||||
return buffer[position + shift];
|
|
||||||
|
|
||||||
throw new IndexOutOfRangeException("Tried to peek after end of buffer");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Mark()
|
|
||||||
{
|
|
||||||
MarkedPosition = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T[] GetMarkedIntervall()
|
|
||||||
{
|
|
||||||
T[] intervall = new T[position - MarkedPosition];
|
|
||||||
Array.Copy(buffer, MarkedPosition, intervall, 0, intervall.Length);
|
|
||||||
return intervall;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Remaining => buffer.Length - position;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue