initial commit
This commit is contained in:
parent
e19df88193
commit
a0dcc72d8c
65
dotnet/Applications/Mocha.Compiler/Mocha.Compiler.csproj
Executable file
65
dotnet/Applications/Mocha.Compiler/Mocha.Compiler.csproj
Executable file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{DBEE1BAC-D658-420D-96D6-417523326650}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>Mocha.Compiler</RootNamespace>
|
||||
<AssemblyName>mcc</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\..\Output\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\UniversalEditor\Libraries\UniversalEditor.Core\UniversalEditor.Core.csproj">
|
||||
<Project>{2D4737E6-6D95-408A-90DB-8DFF38147E85}</Project>
|
||||
<Name>UniversalEditor.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Libraries\UniversalEditor.Plugins.Mocha\UniversalEditor.Plugins.Mocha.csproj">
|
||||
<Project>{3D0893DC-9961-4EAA-BF6C-604686068D09}</Project>
|
||||
<Name>UniversalEditor.Plugins.Mocha</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\UniversalEditor\Libraries\UniversalEditor.Essential\UniversalEditor.Essential.csproj">
|
||||
<Project>{30467E5C-05BC-4856-AADC-13906EF4CADD}</Project>
|
||||
<Name>UniversalEditor.Essential</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\MBS.Framework\MBS.Framework\MBS.Framework.csproj">
|
||||
<Project>{00266B21-35C9-4A7F-A6BA-D54D7FDCC25C}</Project>
|
||||
<Name>MBS.Framework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Libraries\Mocha.MSBuild.Tasks\Mocha.MSBuild.Tasks.csproj">
|
||||
<Project>{1D5668E8-4540-426B-922A-E3A739D16713}</Project>
|
||||
<Name>Mocha.MSBuild.Tasks</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Libraries\Mocha.Core\Mocha.Core.csproj">
|
||||
<Project>{8D3211A6-B2D6-4A26-ABE3-5B57636A4196}</Project>
|
||||
<Name>Mocha.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="test.xml" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
203
dotnet/Applications/Mocha.Compiler/Program.cs
Executable file
203
dotnet/Applications/Mocha.Compiler/Program.cs
Executable file
@ -0,0 +1,203 @@
|
||||
//
|
||||
// Program.cs - main entry point for the ZeQuaL compiler (zq)
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Mocha.Core;
|
||||
using UniversalEditor;
|
||||
using UniversalEditor.Accessors;
|
||||
using UniversalEditor.Plugins.Mocha.DataFormats.MochaBinary;
|
||||
using UniversalEditor.Plugins.Mocha.DataFormats.MochaXML;
|
||||
using UniversalEditor.Plugins.Mocha.ObjectModels.MochaClassLibrary;
|
||||
|
||||
namespace Mocha.Compiler
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
List<string> listFileNames = new List<string>();
|
||||
|
||||
string outputFileName = "output.mcx";
|
||||
bool foundFileName = false;
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (args[i].StartsWith("/") && !foundFileName)
|
||||
{
|
||||
if (args[i].StartsWith("/out:"))
|
||||
{
|
||||
outputFileName = args[i].Substring(5);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// is file name
|
||||
foundFileName = true;
|
||||
|
||||
listFileNames.Add(args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
listFileNames.Sort();
|
||||
|
||||
MochaClassLibraryObjectModel mcl = new MochaClassLibraryObjectModel();
|
||||
MochaBinaryDataFormat mcx = new MochaBinaryDataFormat();
|
||||
MochaXMLDataFormat xml = new MochaXMLDataFormat();
|
||||
|
||||
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||||
int totalInstances = 0, totalRelationships = 0;
|
||||
for (int i = 0; i < listFileNames.Count; i++)
|
||||
{
|
||||
Console.Error.WriteLine("reading {0}", listFileNames[i]);
|
||||
|
||||
if (!System.IO.File.Exists(listFileNames[i]))
|
||||
{
|
||||
MBS.Framework.ConsoleExtensions.LogMSBuildMessage(MBS.Framework.MessageSeverity.Error, "File not found", "MCX0003", listFileNames[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
MochaClassLibraryObjectModel mcl1 = new MochaClassLibraryObjectModel();
|
||||
FileAccessor fain = new FileAccessor(listFileNames[i]);
|
||||
Document.Load(mcl1, xml, fain);
|
||||
|
||||
for (int j = 0; j < mcl1.Libraries.Count; j++)
|
||||
{
|
||||
mcl.Libraries.Merge(mcl1.Libraries[j]);
|
||||
totalInstances += mcl1.Libraries[j].Instances.Count;
|
||||
totalRelationships += mcl1.Libraries[j].Relationships.Count;
|
||||
}
|
||||
for (int j = 0; j < mcl1.Tenants.Count; j++)
|
||||
{
|
||||
Console.Error.WriteLine("registered tenant {0}", mcl1.Tenants[j].Name);
|
||||
mcl.Tenants.Merge(mcl1.Tenants[j]);
|
||||
}
|
||||
}
|
||||
|
||||
ZqLinkRelationships(mcl);
|
||||
|
||||
Console.Error.WriteLine("wrote {0} libraries with {1} instances and {2} relationships total", mcl.Libraries.Count, totalInstances, totalRelationships);
|
||||
|
||||
/*
|
||||
// LINKER SANITY CHECK
|
||||
// we need to load all the referenced MCX libraries in a testing
|
||||
// environment and check MCX relationships for existing instances
|
||||
foreach (MochaLibrary library in mcl.Libraries)
|
||||
{
|
||||
CheckInstancesForMochaStore(mcl, library);
|
||||
}
|
||||
foreach (MochaTenant tenant in mcl.Tenants)
|
||||
{
|
||||
CheckInstancesForMochaStore(mcl, tenant);
|
||||
}
|
||||
*/
|
||||
|
||||
FileAccessor faout = new FileAccessor(outputFileName, true, true);
|
||||
Document.Save(mcl, mcx, faout);
|
||||
}
|
||||
|
||||
private static void ZqLinkRelationships(MochaClassLibraryObjectModel mcl)
|
||||
{
|
||||
for (int i = 0; i < mcl.Libraries.Count; i++)
|
||||
{
|
||||
ZqLinkRelationships(mcl, mcl.Libraries[i]);
|
||||
}
|
||||
for (int i = 0; i < mcl.Tenants.Count; i++)
|
||||
{
|
||||
ZqLinkRelationships(mcl, mcl.Tenants[i]);
|
||||
}
|
||||
}
|
||||
private static void ZqLinkRelationships(MochaClassLibraryObjectModel mcl, IMochaStore store)
|
||||
{
|
||||
for (int j = 0; j < store.Relationships.Count; j++)
|
||||
{
|
||||
MochaInstance instRel = mcl.FindInstance(store.Relationships[j].RelationshipInstanceID);
|
||||
if (instRel != null)
|
||||
{
|
||||
MochaRelationship relSiblingRel = mcl.FindRelationship(new RelationshipKey(instRel.ID, KnownRelationshipGuids.Relationship__has_sibling__Relationship));
|
||||
if (relSiblingRel != null)
|
||||
{
|
||||
MochaInstance instSibling = mcl.FindInstance(relSiblingRel.DestinationInstanceIDs[0]);
|
||||
if (instSibling != null)
|
||||
{
|
||||
for (int k = 0; k < store.Relationships[j].DestinationInstanceIDs.Count; k++)
|
||||
{
|
||||
RelationshipKey rkSibling = new RelationshipKey(store.Relationships[j].DestinationInstanceIDs[k], instSibling.ID);
|
||||
MochaRelationship relSibling = store.Relationships[rkSibling];
|
||||
if (relSibling == null)
|
||||
{
|
||||
Console.Error.WriteLine("zq2: linking relationship {0}", instSibling.ID.ToString("B"));
|
||||
relSibling = new MochaRelationship() { SourceInstanceID = store.Relationships[j].DestinationInstanceIDs[k], RelationshipInstanceID = instSibling.ID };
|
||||
store.Relationships.Add(relSibling);
|
||||
}
|
||||
|
||||
if (!relSibling.DestinationInstanceIDs.Contains(store.Relationships[j].SourceInstanceID))
|
||||
{
|
||||
relSibling.DestinationInstanceIDs.Add(store.Relationships[j].SourceInstanceID);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error.WriteLine("zq2: no sibling relationship '{0}' found", relSiblingRel.DestinationInstanceIDs[0].ToString("B"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckInstancesForMochaStore(MochaClassLibraryObjectModel mcl, IMochaStore library)
|
||||
{
|
||||
foreach (MochaRelationship rel in library.Relationships)
|
||||
{
|
||||
if (FindInstance(mcl, library, rel.SourceInstanceID) == null)
|
||||
{
|
||||
MBS.Framework.ConsoleExtensions.LogMSBuildMessage(MBS.Framework.MessageSeverity.Error, String.Format("relationship references nonexistent sourceInstanceId '{0}'", rel.SourceInstanceID));
|
||||
}
|
||||
if (FindInstance(mcl, library, rel.RelationshipInstanceID) == null)
|
||||
{
|
||||
MBS.Framework.ConsoleExtensions.LogMSBuildMessage(MBS.Framework.MessageSeverity.Error, String.Format("relationship references nonexistent relationshipInstanceId '{0}'", rel.RelationshipInstanceID));
|
||||
}
|
||||
foreach (Guid id in rel.DestinationInstanceIDs)
|
||||
{
|
||||
if (FindInstance(mcl, library, id) == null)
|
||||
{
|
||||
MBS.Framework.ConsoleExtensions.LogMSBuildMessage(MBS.Framework.MessageSeverity.Error, String.Format("relationship references nonexistent target instanceReference '{0}'", id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static MochaInstance FindInstance(MochaClassLibraryObjectModel mcl, IMochaStore store, Guid id)
|
||||
{
|
||||
MochaInstance inst = store.FindInstance(id);
|
||||
if (inst != null)
|
||||
{
|
||||
return inst;
|
||||
}
|
||||
foreach (Guid libraryId in store.LibraryReferences)
|
||||
{
|
||||
inst = mcl.Libraries[libraryId].FindInstance(id);
|
||||
if (inst != null)
|
||||
return inst;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
dotnet/Applications/Mocha.Compiler/Properties/AssemblyInfo.cs
Executable file
46
dotnet/Applications/Mocha.Compiler/Properties/AssemblyInfo.cs
Executable file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// AssemblyInfo.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Information about this assembly is defined by the following attributes.
|
||||
// Change them to the values specific to your project.
|
||||
|
||||
[assembly: AssemblyTitle("Mocha.Compiler")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Mike Becker's Software")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("Mike Becker's Software")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
||||
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
||||
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
// The following attributes are used to specify the signing key for the assembly,
|
||||
// if desired. See the Mono documentation for more information about signing.
|
||||
|
||||
//[assembly: AssemblyDelaySign(false)]
|
||||
//[assembly: AssemblyKeyFile("")]
|
||||
@ -0,0 +1,4 @@
|
||||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.0", FrameworkDisplayName = ".NET Framework 4")]
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
5cdeef7b70eda6eae969492610195a3ac3d59ee6
|
||||
@ -0,0 +1,7 @@
|
||||
/home/beckermj/Documents/Projects/Mocha/Output/Debug/mcc.exe
|
||||
/home/beckermj/Documents/Projects/Mocha/Output/Debug/mcc.pdb
|
||||
/home/beckermj/Documents/Projects/Mocha/Applications/Mocha.Compiler/obj/Debug/Mocha.Compiler.csproj.AssemblyReference.cache
|
||||
/home/beckermj/Documents/Projects/Mocha/Applications/Mocha.Compiler/obj/Debug/Mocha.Compiler.csproj.CoreCompileInputs.cache
|
||||
/home/beckermj/Documents/Projects/Mocha/Applications/Mocha.Compiler/obj/Debug/Mocha.Compiler.csproj.CopyComplete
|
||||
/home/beckermj/Documents/Projects/Mocha/Applications/Mocha.Compiler/obj/Debug/mcc.exe
|
||||
/home/beckermj/Documents/Projects/Mocha/Applications/Mocha.Compiler/obj/Debug/mcc.pdb
|
||||
BIN
dotnet/Applications/Mocha.Compiler/obj/Debug/mcc.exe
Normal file
BIN
dotnet/Applications/Mocha.Compiler/obj/Debug/mcc.exe
Normal file
Binary file not shown.
BIN
dotnet/Applications/Mocha.Compiler/obj/Debug/mcc.pdb
Normal file
BIN
dotnet/Applications/Mocha.Compiler/obj/Debug/mcc.pdb
Normal file
Binary file not shown.
369
dotnet/Applications/Mocha.Compiler/test.xml
Executable file
369
dotnet/Applications/Mocha.Compiler/test.xml
Executable file
@ -0,0 +1,369 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
//
|
||||
// LoginPage.xml - XML definition for the initial Mocha `Login Page` system page
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2020 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<mocha xmlns="urn:net.alcetech.schemas.Mocha">
|
||||
<libraries>
|
||||
<library id="{1c8cfb35-24a8-4b1f-9b44-7cde72a7168e}">
|
||||
<instances>
|
||||
<instance id="{E753D577-90AA-44EE-90A9-99D002B43367}" classInstanceId="{C269A1F3-E014-4230-B78D-38EAF6EA8A81}">
|
||||
<!-- Style Rule -->
|
||||
<relationships>
|
||||
<relationship relationshipInstanceId="{B69C2708-E78D-413A-B491-ABB6F1D2A6E0}">
|
||||
<!-- Style Rule.has Style Property -->
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{1AE4BE55-312D-461C-9A4B-5F3F13CCF7F9}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
</relationships>
|
||||
<attributeValues>
|
||||
<attributeValue attributeInstanceId="&IDA_Value;" value="center" />
|
||||
</attributeValues>
|
||||
</instance>
|
||||
<instance id="{372D78D0-ED49-4C16-9841-084AC3224A19}" classInstanceId="{C269A1F3-E014-4230-B78D-38EAF6EA8A81}">
|
||||
<!-- Style Rule -->
|
||||
<relationships>
|
||||
<relationship relationshipInstanceId="{B69C2708-E78D-413A-B491-ABB6F1D2A6E0}">
|
||||
<!-- Style Rule.has Style Property -->
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{F936088C-8D65-4476-BDBC-421EE8BC41A9}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
</relationships>
|
||||
<attributeValues>
|
||||
<attributeValue attributeInstanceId="&IDA_Value;" value="14pt" />
|
||||
</attributeValues>
|
||||
</instance>
|
||||
<instance id="{C7C4790B-4D81-4E1E-965F-1DD1A7D788F0}" classInstanceId="{C269A1F3-E014-4230-B78D-38EAF6EA8A81}">
|
||||
<!-- Style Rule -->
|
||||
<relationships>
|
||||
<relationship relationshipInstanceId="{B69C2708-E78D-413A-B491-ABB6F1D2A6E0}">
|
||||
<!-- Style Rule.has Style Property -->
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{354D0A44-6816-4E65-BC84-4910CE0CE6F5}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
</relationships>
|
||||
<attributeValues>
|
||||
<attributeValue attributeInstanceId="&IDA_Value;" value="16px" />
|
||||
</attributeValues>
|
||||
</instance>
|
||||
|
||||
<instance id="{EED15C64-0279-4219-A920-E9635AA84468}" classInstanceId="{A48C843A-B24B-4BC3-BE6F-E2D069229B0A}">
|
||||
<!-- Style: Login Page Header Style -->
|
||||
<translations>
|
||||
<translation relationshipInstanceId="{963233D5-88F6-41F0-9DE0-63BAB95FA228}">
|
||||
<!-- Style.has title Translation -->
|
||||
<translationValues>
|
||||
<translationValue languageInstanceId="&IDI_Language_English;" value="Login Header Text Style" />
|
||||
</translationValues>
|
||||
</translation>
|
||||
</translations>
|
||||
<relationships>
|
||||
<relationship relationshipInstanceId="{B69C2708-E78D-413A-B491-ABB6F1D2A6E0}">
|
||||
<!-- Style.has Style Property -->
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{E753D577-90AA-44EE-90A9-99D002B43367}" />
|
||||
<instanceReference instanceId="{372D78D0-ED49-4C16-9841-084AC3224A19}" />
|
||||
<instanceReference instanceId="{C7C4790B-4D81-4E1E-965F-1DD1A7D788F0}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
</relationships>
|
||||
</instance>
|
||||
|
||||
<instance id="{F0C52DD4-B074-49AE-A96F-5F907359DB6D}" classInstanceId="{ADFF93CE-9E85-4168-A7D4-5239B99BE36D}">
|
||||
<!-- Paragraph Page Component: -->
|
||||
<relationships>
|
||||
<relationship relationshipInstanceId="{818CFF50-7D42-43B2-B6A7-92C3C54D450D}">
|
||||
<!-- Page Component.has Style -->
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{EED15C64-0279-4219-A920-E9635AA84468}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
<relationship relationshipInstanceId="{0E002E6F-AA79-457C-93B8-2CCE1AEF5F7E}">
|
||||
<!-- Text Page Component.gets content from Method -->
|
||||
<targetInstances>
|
||||
<!-- Tenant@get Login Page Header Text -->
|
||||
<instanceReference instanceId="{781e25f0-28cc-4590-9fe9-54f449494d44}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
</relationships>
|
||||
</instance>
|
||||
|
||||
<instance id="{A77F672B-7F9C-4F20-B5CE-1990529407F4}" classInstanceId="{798B67FA-D4BE-42B9-B4BD-6F8E02C953C0}">
|
||||
<relationships>
|
||||
<relationship relationshipInstanceId="{481E3FBE-B82A-4C76-9DDF-D66C6BA8C590}">
|
||||
<targetInstances>
|
||||
<!--
|
||||
this USED TO BE a hardcoded reference to a File on a particular Tenant
|
||||
in other words, a GSI - Get Specific Instance method returning File
|
||||
{E6A5D3B3-7E81-41FD-9F5C-1D460175C60E}
|
||||
|
||||
this IS NOW a reference to a method, Tenant@get Logo Image File
|
||||
which is a GRS - Get Referenced Instance Set using relationship
|
||||
`Tenant.has logo image File` for the instance `Current Tenant`
|
||||
-->
|
||||
<instanceReference instanceId="{4f0ecfdd-e991-4d9c-9ac9-5afe218637a1}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
</relationships>
|
||||
</instance>
|
||||
|
||||
<instance id="{c5027dc2-53ee-4fc0-9ba6-f2b883f7dad8}" classInstanceId="&IDC_Translation;">
|
||||
<relationships>
|
||||
<relationship relationshipInstanceId="&IDR_Translation__has__Translation_Value;">
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{29C02384-57B0-45F5-9C15-747F9DFD2C69}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
</relationships>
|
||||
</instance>
|
||||
<instance id="{29C02384-57B0-45F5-9C15-747F9DFD2C69}" classInstanceId="&IDC_TranslationValue;">
|
||||
<attributeValues>
|
||||
<attributeValue attributeInstanceId="&IDA_Value;" value="Please log in to continue" />
|
||||
</attributeValues>
|
||||
<relationships>
|
||||
<relationship relationshipInstanceId="&IDR_Translation_Value__has__Language;">
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="&IDI_Language_English;" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
</relationships>
|
||||
</instance>
|
||||
|
||||
<instance id="{ed1093d2-3522-4687-869b-d84d99b70aab}" classInstanceId="&IDC_ReturnInstanceSetMethodBinding;">
|
||||
</instance>
|
||||
<instance id="{de0650d4-06a4-4be7-a2ba-8fd8bb26b917}" classInstanceId="&IDC_GetSpecifiedInstancesMethod;">
|
||||
<attributeValues>
|
||||
<attributeValue attributeInstanceId="&IDA_Verb;" value="get" />
|
||||
</attributeValues>
|
||||
<translations>
|
||||
<translation relationshipInstanceId="{52B65829-4A3F-44FB-BEE8-D9A240F1E9C9}">
|
||||
<translationValues>
|
||||
<translationValue languageInstanceId="&IDI_Language_English;" value="Default Login Page Welcome Text" />
|
||||
</translationValues>
|
||||
</translation>
|
||||
</translations>
|
||||
<relationships>
|
||||
<relationship relationshipInstanceId="{dea1aa0b-2bef-4bac-b4f9-0ce8cf7006fc}">
|
||||
<!-- GSI Method.has Instance -->
|
||||
<targetInstances>
|
||||
<!-- build in TTC "Please log in to continue" -->
|
||||
<instanceReference instanceId="{c5027dc2-53ee-4fc0-9ba6-f2b883f7dad8}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
<relationship relationshipInstanceId="&IDR_Method__has__Method_Binding;">
|
||||
<!-- Method.has Method Binding return instance set method binding [rsmb] -->
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{ed1093d2-3522-4687-869b-d84d99b70aab}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
<relationship relationshipInstanceId="&IDR_Method__for__Class;">
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{34241fb3-ca55-4e23-80c2-5e0058311657}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
</relationships>
|
||||
</instance>
|
||||
|
||||
<instance id="{23164023-03A8-4DB2-9C15-DB8321F3486B}" classInstanceId="{FD86551E-E4CE-4B8B-95CB-BEC1E6A0EE2B}">
|
||||
<attributeValues>
|
||||
<!-- Level [Numeric Attribute] -->
|
||||
<attributeValue attributeInstanceId="{8C528FB0-4063-47B0-BC56-85E387A41BD2}" value="4" />
|
||||
</attributeValues>
|
||||
<relationships>
|
||||
<relationship relationshipInstanceId="{0E002E6F-AA79-457C-93B8-2CCE1AEF5F7E}">
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{de0650d4-06a4-4be7-a2ba-8fd8bb26b917}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
</relationships>
|
||||
<translations>
|
||||
<translation relationshipInstanceId="{C5027DC2-53EE-4FC0-9BA6-F2B883F7DAD8}">
|
||||
<translationValues>
|
||||
<translationValue languageInstanceId="&IDI_Language_English;" value="Please sign in to access this feature PAGEBUILDER TEST" />
|
||||
</translationValues>
|
||||
</translation>
|
||||
</translations>
|
||||
</instance>
|
||||
|
||||
<instance id="{4f416621-fb60-4349-988e-7c876e38c6a8}" classInstanceId="{5EBA7BD6-BA0A-45B2-835C-C92489FD7E74}">
|
||||
<!-- SummaryPageComponent, Editable=True -->
|
||||
<attributeValues>
|
||||
<!-- Editable (Boolean Attribute) -->
|
||||
<attributeValue attributeInstanceId="{957fd8b3-fdc4-4f35-87d6-db1c0682f53c}" value="true" />
|
||||
</attributeValues>
|
||||
</instance>
|
||||
|
||||
<instance id="{DF0FCBC5-E1E5-40AD-B1FF-FD001FB680F6}" classInstanceId="{ADFF93CE-9E85-4168-A7D4-5239B99BE36D}">
|
||||
<relationships>
|
||||
<relationship relationshipInstanceId="{0E002E6F-AA79-457C-93B8-2CCE1AEF5F7E}">
|
||||
<!-- Text Page Component.gets content from Method -->
|
||||
<targetInstances>
|
||||
<!-- Tenant@get Login Page Footer Text -->
|
||||
<instanceReference instanceId="{52bc7c5a-359a-4843-b807-809e3763c56a}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
</relationships>
|
||||
</instance>
|
||||
|
||||
<instance id="{10C3078C-F2FC-4B8E-9875-450449549351}" classInstanceId="{A48C843A-B24B-4BC3-BE6F-E2D069229B0A}">
|
||||
<!-- Style: Login Page Header Style -->
|
||||
<translations>
|
||||
<translation relationshipInstanceId="{963233D5-88F6-41F0-9DE0-63BAB95FA228}">
|
||||
<!-- Style.has title Translation -->
|
||||
<translationValues>
|
||||
<translationValue languageInstanceId="&IDI_Language_English;" value="Primary Theme Color Style" />
|
||||
</translationValues>
|
||||
</translation>
|
||||
</translations>
|
||||
<integrationIDs>
|
||||
<integrationID name="StyleName" value="LoginPage" />
|
||||
</integrationIDs>
|
||||
<attributeValues>
|
||||
<attributeValue attributeInstanceId="{0F42BCB6-549A-4407-BB91-DD147443F68F}" value="Primary" />
|
||||
</attributeValues>
|
||||
</instance>
|
||||
|
||||
|
||||
<instance id="{3A52305F-4D96-44F9-B50C-4AC1A75DB1B8}" classInstanceId="{F480787D-F51E-498A-8972-72128D808AEB}">
|
||||
<translations>
|
||||
<translation relationshipInstanceId="{C25230B1-4D23-4CFE-8B75-56C33E8293AF}">
|
||||
<translationValues>
|
||||
<translationValue languageInstanceId="&IDI_Language_English;" value="Log In" />
|
||||
</translationValues>
|
||||
</translation>
|
||||
</translations>
|
||||
</instance>
|
||||
<instance id="{4DEEAB31-9A57-4CDB-9770-6AFDF19F153C}" classInstanceId="{F480787D-F51E-498A-8972-72128D808AEB}">
|
||||
<translations>
|
||||
<translation relationshipInstanceId="{C25230B1-4D23-4CFE-8B75-56C33E8293AF}">
|
||||
<translationValues>
|
||||
<translationValue languageInstanceId="&IDI_Language_English;" value="Cancel" />
|
||||
</translationValues>
|
||||
</translation>
|
||||
</translations>
|
||||
</instance>
|
||||
|
||||
<instance id="{8F9C429D-2FF5-46E1-B906-CD1D3DFF7BDE}" classInstanceId="{D349C489-9684-4A5A-9843-B906A7F803BC}">
|
||||
<!-- Panel Page Component: -->
|
||||
<relationships>
|
||||
<relationship relationshipInstanceId="{AD8C5FAE-2444-4700-896E-C5F968C0F85B}">
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{23164023-03A8-4DB2-9C15-DB8321F3486B}" />
|
||||
<instanceReference instanceId="{4f416621-fb60-4349-988e-7c876e38c6a8}" />
|
||||
<instanceReference instanceId="{DF0FCBC5-E1E5-40AD-B1FF-FD001FB680F6}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
<relationship relationshipInstanceId="{56E339BD-6189-4BAC-AB83-999543FB8060}">
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{3A52305F-4D96-44F9-B50C-4AC1A75DB1B8}" />
|
||||
<instanceReference instanceId="{4DEEAB31-9A57-4CDB-9770-6AFDF19F153C}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
<relationship relationshipInstanceId="{818CFF50-7D42-43B2-B6A7-92C3C54D450D}">
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{10C3078C-F2FC-4B8E-9875-450449549351}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
</relationships>
|
||||
</instance>
|
||||
|
||||
<instance id="{E7C69CF3-CB71-43D8-91AB-B8F5030350AB}" classInstanceId="{A66D9AE2-3BEC-4083-A5CB-7DE3B03A9CC7}">
|
||||
<!-- Sequential Container Page Component: -->
|
||||
<relationships>
|
||||
<relationship relationshipInstanceId="&IDR_Sequential_Container_Page_Component__has__Sequential_Container_Orientation;">
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{9B7B7F14-0925-456D-98E6-E3FFEFDC272C}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
<relationship relationshipInstanceId="{CB7B8162-1C9E-4E72-BBB8-C1C37CA69CD5}">
|
||||
<!-- Container Page Component.has Page Component -->
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{A77F672B-7F9C-4F20-B5CE-1990529407F4}" />
|
||||
<instanceReference instanceId="{F0C52DD4-B074-49AE-A96F-5F907359DB6D}" />
|
||||
<instanceReference instanceId="{8F9C429D-2FF5-46E1-B906-CD1D3DFF7BDE}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
</relationships>
|
||||
</instance>
|
||||
|
||||
<instance id="{96B81CE8-DEDA-4EFF-BF5F-0F988ABCC3EC}" classInstanceId="{A48C843A-B24B-4BC3-BE6F-E2D069229B0A}">
|
||||
<integrationIDs>
|
||||
<integrationID name="StyleName" value="LoginPage" />
|
||||
</integrationIDs>
|
||||
<attributeValues>
|
||||
<attributeValue attributeInstanceId="{0F42BCB6-549A-4407-BB91-DD147443F68F}" value="LoginPage" />
|
||||
</attributeValues>
|
||||
<translations>
|
||||
<translation relationshipInstanceId="{963233D5-88F6-41F0-9DE0-63BAB95FA228}">
|
||||
<translationValues>
|
||||
<translationValue languageInstanceId="&IDI_Language_English;" value="Login Page Style" />
|
||||
</translationValues>
|
||||
</translation>
|
||||
</translations>
|
||||
</instance>
|
||||
|
||||
<instance id="{9E272BC3-0358-4EB7-8B3B-581964A59634}" classInstanceId="{D9626359-48E3-4840-A089-CD8DA6731690}">
|
||||
<integrationIDs>
|
||||
<integrationID name="PageName" value="LoginPage" />
|
||||
</integrationIDs>
|
||||
<translations>
|
||||
<translation relationshipInstanceId="{7BE6522A-4BE8-4CD3-8701-C8353F7DF630}">
|
||||
<!-- Page.has title Translation -->
|
||||
<translationValues>
|
||||
<translationValue languageInstanceId="&IDI_Language_English;" value="Login Page" />
|
||||
</translationValues>
|
||||
</translation>
|
||||
</translations>
|
||||
<relationships>
|
||||
<relationship relationshipInstanceId="{6E6E1A85-3EA9-4939-B13E-CBF645CB8B59}">
|
||||
<!-- Page.has Style -->
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{96B81CE8-DEDA-4EFF-BF5F-0F988ABCC3EC}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
<relationship relationshipInstanceId="{24F6C596-D77D-4754-B023-00321DEBA924}">
|
||||
<!-- Page.has Page Component -->
|
||||
<targetInstances>
|
||||
<instanceReference instanceId="{E7C69CF3-CB71-43D8-91AB-B8F5030350AB}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
<relationship relationshipInstanceId="{15199c49-9595-4288-846d-13b0ad5dcd4b}">
|
||||
<!-- Securable Object.secured by domains from Method -->
|
||||
<targetInstances>
|
||||
<!-- Anyone: {01a86f06-9f1e-45ce-ad0b-24655baa936a} -->
|
||||
<!-- Authenticated Users: {843dcb86-be54-4c81-9ff7-8235215623ba} -->
|
||||
|
||||
<!-- Security Domain@get Anyone instance -->
|
||||
<instanceReference instanceId="{01a86f06-9f1e-45ce-ad0b-24655baa936a}" />
|
||||
</targetInstances>
|
||||
</relationship>
|
||||
</relationships>
|
||||
<attributeValues>
|
||||
<attributeValue attributeInstanceId="{970F79A0-9EFE-4E7D-9286-9908C6F06A67}" value="account/login" />
|
||||
</attributeValues>
|
||||
</instance>
|
||||
</instances>
|
||||
</library>
|
||||
</libraries>
|
||||
</mocha>
|
||||
69
dotnet/Applications/Mocha.Debugger/Mocha.Debugger.csproj
Executable file
69
dotnet/Applications/Mocha.Debugger/Mocha.Debugger.csproj
Executable file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{FF61C373-835F-4160-8499-30324E8B9909}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>Mocha.Debugger</RootNamespace>
|
||||
<AssemblyName>Mocha.Debugger</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\Output\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ExternalConsole>true</ExternalConsole>
|
||||
<AssemblyName>mcxdebug</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ExternalConsole>true</ExternalConsole>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Web" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Libraries\Mocha.OMS\Mocha.OMS.csproj">
|
||||
<Project>{8588928F-2868-4248-8993-533307A05C0C}</Project>
|
||||
<Name>Mocha.OMS</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Libraries\Mocha.Storage\Mocha.Storage.csproj">
|
||||
<Project>{C9A3AE1D-0658-4A70-BC48-9D8DEF9C205B}</Project>
|
||||
<Name>Mocha.Storage</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Libraries\Mocha.Storage.Local\Mocha.Storage.Local.csproj">
|
||||
<Project>{A4B8D6D8-3365-49FC-8123-58B3749A5427}</Project>
|
||||
<Name>Mocha.Storage.Local</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\MBS.Networking\Plugins\MBS.Networking.Plugins.HyperTextTransfer\MBS.Networking.Plugins.HyperTextTransfer.csproj">
|
||||
<Project>{5743EE32-F3ED-4162-A0E3-B105503BF139}</Project>
|
||||
<Name>MBS.Networking.Plugins.HyperTextTransfer</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\MBS.Networking\Libraries\MBS.Networking\MBS.Networking.csproj">
|
||||
<Project>{DBD65B3F-81C8-4E44-B268-3FAB3B12AA1E}</Project>
|
||||
<Name>MBS.Networking</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\MBS.Framework\MBS.Framework\MBS.Framework.csproj">
|
||||
<Project>{00266B21-35C9-4A7F-A6BA-D54D7FDCC25C}</Project>
|
||||
<Name>MBS.Framework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Libraries\Mocha.Core\Mocha.Core.csproj">
|
||||
<Project>{8D3211A6-B2D6-4A26-ABE3-5B57636A4196}</Project>
|
||||
<Name>Mocha.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
175
dotnet/Applications/Mocha.Debugger/Program.cs
Executable file
175
dotnet/Applications/Mocha.Debugger/Program.cs
Executable file
@ -0,0 +1,175 @@
|
||||
//
|
||||
// Program.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2022 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using Mocha.Core;
|
||||
using Mocha.OMS;
|
||||
|
||||
namespace Mocha.Debugger
|
||||
{
|
||||
class MainClass
|
||||
{
|
||||
private static Oms _CurrentOms = null;
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
_CurrentOms = InitializeOms();
|
||||
_CurrentOms.TenantName = "default";
|
||||
|
||||
System.Net.Sockets.TcpListener listener = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Any, 63320);
|
||||
listener.Start();
|
||||
|
||||
while (true)
|
||||
{
|
||||
System.Net.Sockets.TcpClient client = listener.AcceptTcpClient();
|
||||
|
||||
System.Threading.Thread t = new System.Threading.Thread(t_ParameterizedThreadStart);
|
||||
t.Start(client);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Oms InitializeOms()
|
||||
{
|
||||
Oms oms = null;
|
||||
bool useLocalOms = true;
|
||||
if (useLocalOms)
|
||||
{
|
||||
oms = new LocalOms();
|
||||
(oms as LocalOms).Environment = new OmsEnvironment(new Storage.Local.LocalStorageProvider("/usr/share/mocha/system"));
|
||||
oms.Environment.Initialize();
|
||||
oms.Environment.StorageProvider.DefaultTenantName = oms.TenantName;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
// attempt to connect
|
||||
oms = new OMSClient();
|
||||
|
||||
string serverName = Mocha.Web.Configuration.ConfigurationSections.OMS.OMSConfigurationSection.Settings.Server.HostName;
|
||||
int portNumber = Mocha.Web.Configuration.ConfigurationSections.OMS.OMSConfigurationSection.Settings.Server.PortNumber;
|
||||
|
||||
System.Net.IPHostEntry entry = System.Net.Dns.GetHostEntry(serverName);
|
||||
if (entry != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
oms.Connect(entry.AddressList[0], portNumber);
|
||||
}
|
||||
catch (System.Net.Sockets.SocketException ex)
|
||||
{
|
||||
// OMSUnavailable.Visible = true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
return oms;
|
||||
}
|
||||
|
||||
private static void t_ParameterizedThreadStart(object parm)
|
||||
{
|
||||
System.Net.Sockets.TcpClient client = (System.Net.Sockets.TcpClient)parm;
|
||||
|
||||
System.IO.StreamReader sr = new System.IO.StreamReader(client.GetStream());
|
||||
if (sr.EndOfStream)
|
||||
return;
|
||||
|
||||
|
||||
string firstline = sr.ReadLine();
|
||||
if (String.IsNullOrEmpty(firstline))
|
||||
return;
|
||||
|
||||
MBS.Networking.Protocols.HyperTextTransfer.Request req = MBS.Networking.Protocols.HyperTextTransfer.Request.Parse(firstline);
|
||||
if (req.Method == "GET")
|
||||
{
|
||||
Uri uri = new Uri(String.Format("http://localhost:63320{0}", req.Path));
|
||||
System.Collections.Specialized.NameValueCollection nvc = System.Web.HttpUtility.ParseQueryString(uri.Query);
|
||||
|
||||
string reff = nvc["ref"];
|
||||
if (!String.IsNullOrEmpty(reff))
|
||||
{
|
||||
if (Guid.TryParse(reff, out Guid guid))
|
||||
{
|
||||
|
||||
lock (_CurrentOms)
|
||||
{
|
||||
if (nvc["tenant"] != null)
|
||||
{
|
||||
_CurrentOms.TenantName = nvc["tenant"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_CurrentOms.TenantName = "default";
|
||||
}
|
||||
|
||||
Instance inst = _CurrentOms.GetInstance(guid);
|
||||
if (inst != null)
|
||||
{
|
||||
Instance instDefinition = _CurrentOms.GetRelatedInstance(inst, KnownRelationshipGuids.Instance__has__Instance_Definition);
|
||||
|
||||
// FIXME: we don't know how to pass the project name into this
|
||||
string rootpath = "/home/beckermj/Documents/Projects/Mocha.0/Content/Mocha.System";
|
||||
string filename = _CurrentOms.GetAttributeValue<string>(instDefinition, KnownAttributeGuids.Text.DebugDefinitionFileName);
|
||||
decimal linenum = _CurrentOms.GetAttributeValue<decimal>(instDefinition, KnownAttributeGuids.Numeric.DebugDefinitionLineNumber);
|
||||
decimal colnum = _CurrentOms.GetAttributeValue<decimal>(instDefinition, KnownAttributeGuids.Numeric.DebugDefinitionColumnNumber);
|
||||
|
||||
string args = String.Format("\"{0}/{1}\";{2};{3}", rootpath, filename, linenum, colnum);
|
||||
Console.WriteLine("mcxdebug: launching monodevelop {0}", args);
|
||||
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo("monodevelop", args);
|
||||
psi.UseShellExecute = true;
|
||||
System.Diagnostics.Process.Start(psi);
|
||||
|
||||
RespondWith(client, _CurrentOms.GetInstanceText(inst), _CurrentOms.GetInstanceText(_CurrentOms.GetRelatedInstance(inst, KnownRelationshipGuids.Instance__for__Module)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
string nextline = sr.ReadLine();
|
||||
if (String.IsNullOrEmpty(nextline))
|
||||
break;
|
||||
|
||||
string[] nextparts = nextline.Split(new char[] { ':' }, 2);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RespondWith(TcpClient client, string nodename, string projname)
|
||||
{
|
||||
StringBuilder resp = new StringBuilder();
|
||||
resp.AppendLine("HTTP/1.1 200 OK");
|
||||
resp.AppendLine();
|
||||
resp.AppendLine("<html><head><title>Mocha HTTP Debugger Plugin</title></head><body>");
|
||||
resp.AppendLine("<strong>Mocha HTTP Debugger Plugin</strong>");
|
||||
resp.AppendLine("<p>The requested node has been opened in your IDE</p>");
|
||||
resp.AppendLine(String.Format("<p><strong>Node:</strong> {0}</p><p><strong>Project:</strong> {1}</p>", nodename, projname));
|
||||
resp.AppendLine("</body></html>");
|
||||
resp.AppendLine();
|
||||
byte[] respdata = Encoding.UTF8.GetBytes(resp.ToString());
|
||||
client.GetStream().Write(respdata, 0, respdata.Length);
|
||||
client.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
46
dotnet/Applications/Mocha.Debugger/Properties/AssemblyInfo.cs
Executable file
46
dotnet/Applications/Mocha.Debugger/Properties/AssemblyInfo.cs
Executable file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// AssemblyInfo.cs
|
||||
//
|
||||
// Author:
|
||||
// Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2022 Mike Becker's Software
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Information about this assembly is defined by the following attributes.
|
||||
// Change them to the values specific to your project.
|
||||
|
||||
[assembly: AssemblyTitle("Mocha.Debugger")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Mike Becker's Software")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("Mike Becker's Software")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
||||
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
||||
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
// The following attributes are used to specify the signing key for the assembly,
|
||||
// if desired. See the Mono documentation for more information about signing.
|
||||
|
||||
//[assembly: AssemblyDelaySign(false)]
|
||||
//[assembly: AssemblyKeyFile("")]
|
||||
@ -0,0 +1,4 @@
|
||||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7", FrameworkDisplayName = ".NET Framework 4.7")]
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
65bc769232bbd0a8cc10bbb13b101907578febc4
|
||||
@ -0,0 +1,7 @@
|
||||
/home/beckermj/Documents/Projects/Mocha/Output/Debug/mcxdebug.exe
|
||||
/home/beckermj/Documents/Projects/Mocha/Output/Debug/mcxdebug.pdb
|
||||
/home/beckermj/Documents/Projects/Mocha/Applications/Mocha.Debugger/obj/Debug/Mocha.Debugger.csproj.AssemblyReference.cache
|
||||
/home/beckermj/Documents/Projects/Mocha/Applications/Mocha.Debugger/obj/Debug/Mocha.Debugger.csproj.CoreCompileInputs.cache
|
||||
/home/beckermj/Documents/Projects/Mocha/Applications/Mocha.Debugger/obj/Debug/Mocha.Debugger.csproj.CopyComplete
|
||||
/home/beckermj/Documents/Projects/Mocha/Applications/Mocha.Debugger/obj/Debug/mcxdebug.exe
|
||||
/home/beckermj/Documents/Projects/Mocha/Applications/Mocha.Debugger/obj/Debug/mcxdebug.pdb
|
||||
BIN
dotnet/Applications/Mocha.Debugger/obj/Debug/mcxdebug.exe
Normal file
BIN
dotnet/Applications/Mocha.Debugger/obj/Debug/mcxdebug.exe
Normal file
Binary file not shown.
BIN
dotnet/Applications/Mocha.Debugger/obj/Debug/mcxdebug.pdb
Normal file
BIN
dotnet/Applications/Mocha.Debugger/obj/Debug/mcxdebug.pdb
Normal file
Binary file not shown.
11
dotnet/Applications/Mocha.Web.Server/Core/Cache/ICache.cs
Executable file
11
dotnet/Applications/Mocha.Web.Server/Core/Cache/ICache.cs
Executable file
@ -0,0 +1,11 @@
|
||||
namespace dotless.Core.Cache
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
|
||||
public interface ICache
|
||||
{
|
||||
void Insert(string cacheKey, IEnumerable<string> fileDependancies, string css);
|
||||
bool Exists(string cacheKey);
|
||||
string Retrieve(string cacheKey);
|
||||
}
|
||||
}
|
||||
32
dotnet/Applications/Mocha.Web.Server/Core/Cache/InMemoryCache.cs
Executable file
32
dotnet/Applications/Mocha.Web.Server/Core/Cache/InMemoryCache.cs
Executable file
@ -0,0 +1,32 @@
|
||||
namespace dotless.Core.Cache
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class InMemoryCache : ICache
|
||||
{
|
||||
private readonly Dictionary<string, string> _cache;
|
||||
|
||||
public InMemoryCache()
|
||||
{
|
||||
_cache = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public void Insert(string fileName, IEnumerable<string> imports, string css)
|
||||
{
|
||||
_cache[fileName] = css;
|
||||
}
|
||||
|
||||
public bool Exists(string filename)
|
||||
{
|
||||
return _cache.ContainsKey(filename);
|
||||
}
|
||||
|
||||
public string Retrieve(string filename)
|
||||
{
|
||||
if (_cache.ContainsKey(filename))
|
||||
return _cache[filename];
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
78
dotnet/Applications/Mocha.Web.Server/Core/Engine/CacheDecorator.cs
Executable file
78
dotnet/Applications/Mocha.Web.Server/Core/Engine/CacheDecorator.cs
Executable file
@ -0,0 +1,78 @@
|
||||
namespace dotless.Core
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Cache;
|
||||
using Loggers;
|
||||
|
||||
public class CacheDecorator : ILessEngine
|
||||
{
|
||||
public readonly ILessEngine Underlying;
|
||||
public readonly ICache Cache;
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public CacheDecorator(ILessEngine underlying, ICache cache) : this(underlying, cache, NullLogger.Instance)
|
||||
{}
|
||||
|
||||
public CacheDecorator(ILessEngine underlying, ICache cache, ILogger logger)
|
||||
{
|
||||
Underlying = underlying;
|
||||
Cache = cache;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
public string TransformToCss(string source, string fileName)
|
||||
{
|
||||
//Compute Cache Key
|
||||
var hash = ComputeContentHash(source);
|
||||
var cacheKey = fileName + hash;
|
||||
if (!Cache.Exists(cacheKey))
|
||||
{
|
||||
Logger.Debug(String.Format("Inserting cache entry for {0}", cacheKey));
|
||||
|
||||
var css = Underlying.TransformToCss(source, fileName);
|
||||
var dependancies = new[] { fileName }.Concat(GetImports());
|
||||
|
||||
Cache.Insert(cacheKey, dependancies, css);
|
||||
|
||||
return css;
|
||||
}
|
||||
Logger.Debug(String.Format("Retrieving cache entry {0}", cacheKey));
|
||||
return Cache.Retrieve(cacheKey);
|
||||
}
|
||||
|
||||
private string ComputeContentHash(string source)
|
||||
{
|
||||
SHA1 sha1 = SHA1.Create();
|
||||
byte[] computeHash = sha1.ComputeHash(Encoding.Default.GetBytes(source));
|
||||
return Convert.ToBase64String(computeHash);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetImports()
|
||||
{
|
||||
return Underlying.GetImports();
|
||||
}
|
||||
|
||||
public void ResetImports()
|
||||
{
|
||||
Underlying.ResetImports();
|
||||
}
|
||||
|
||||
public bool LastTransformationSuccessful
|
||||
{
|
||||
get
|
||||
{
|
||||
return Underlying.LastTransformationSuccessful;
|
||||
}
|
||||
}
|
||||
|
||||
public string CurrentDirectory
|
||||
{
|
||||
get { return Underlying.CurrentDirectory; }
|
||||
set { Underlying.CurrentDirectory = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
15
dotnet/Applications/Mocha.Web.Server/Core/Engine/ILessEngine.cs
Executable file
15
dotnet/Applications/Mocha.Web.Server/Core/Engine/ILessEngine.cs
Executable file
@ -0,0 +1,15 @@
|
||||
namespace dotless.Core
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
public interface ILessEngine
|
||||
{
|
||||
string TransformToCss(string source, string fileName);
|
||||
void ResetImports();
|
||||
IEnumerable<string> GetImports();
|
||||
bool LastTransformationSuccessful { get; }
|
||||
|
||||
string CurrentDirectory { get; set; }
|
||||
}
|
||||
}
|
||||
145
dotnet/Applications/Mocha.Web.Server/Core/Engine/LessEngine.cs
Executable file
145
dotnet/Applications/Mocha.Web.Server/Core/Engine/LessEngine.cs
Executable file
@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using dotless.Core.Parser;
|
||||
using dotless.Core.Parser.Tree;
|
||||
using dotless.Core.Plugins;
|
||||
using dotless.Core.Stylizers;
|
||||
|
||||
namespace dotless.Core
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Exceptions;
|
||||
using Loggers;
|
||||
using Parser.Infrastructure;
|
||||
|
||||
public class LessEngine : ILessEngine
|
||||
{
|
||||
public Parser.Parser Parser { get; set; }
|
||||
public ILogger Logger { get; set; }
|
||||
public bool Compress { get; set; }
|
||||
public bool Debug { get; set; }
|
||||
[Obsolete("The Variable Redefines feature has been removed to align with less.js")]
|
||||
public bool DisableVariableRedefines { get; set; }
|
||||
[Obsolete("The Color Compression feature has been removed to align with less.js")]
|
||||
public bool DisableColorCompression { get; set; }
|
||||
public bool KeepFirstSpecialComment { get; set; }
|
||||
public bool StrictMath { get; set; }
|
||||
public Env Env { get; set; }
|
||||
public IEnumerable<IPluginConfigurator> Plugins { get; set; }
|
||||
public bool LastTransformationSuccessful { get; private set; }
|
||||
|
||||
public string CurrentDirectory
|
||||
{
|
||||
get { return Parser.CurrentDirectory; }
|
||||
set { Parser.CurrentDirectory = value; }
|
||||
}
|
||||
|
||||
public LessEngine(Parser.Parser parser, ILogger logger, dotless.Core.configuration.DotlessConfiguration config)
|
||||
: this(parser, logger, config.MinifyOutput, config.Debug, config.DisableVariableRedefines, config.DisableColorCompression, config.KeepFirstSpecialComment, config.Plugins)
|
||||
{
|
||||
}
|
||||
|
||||
public LessEngine(Parser.Parser parser, ILogger logger, bool compress, bool debug, bool disableVariableRedefines, bool disableColorCompression, bool keepFirstSpecialComment, bool strictMath, IEnumerable<IPluginConfigurator> plugins)
|
||||
{
|
||||
Parser = parser;
|
||||
Logger = logger;
|
||||
Compress = compress;
|
||||
Debug = debug;
|
||||
Plugins = plugins;
|
||||
KeepFirstSpecialComment = keepFirstSpecialComment;
|
||||
StrictMath = strictMath;
|
||||
}
|
||||
|
||||
public LessEngine(Parser.Parser parser, ILogger logger, bool compress, bool debug, bool disableVariableRedefines, bool disableColorCompression, bool keepFirstSpecialComment, IEnumerable<IPluginConfigurator> plugins)
|
||||
: this(parser, logger, compress, debug, disableVariableRedefines, disableColorCompression, keepFirstSpecialComment, false, plugins)
|
||||
{
|
||||
}
|
||||
|
||||
public LessEngine(Parser.Parser parser, ILogger logger, bool compress, bool debug)
|
||||
: this(parser, logger, compress, debug, false, false, false, null)
|
||||
{
|
||||
}
|
||||
|
||||
public LessEngine(Parser.Parser parser, ILogger logger, bool compress, bool debug, bool disableVariableRedefines)
|
||||
: this(parser, logger, compress, debug, disableVariableRedefines, false, false, null)
|
||||
{
|
||||
}
|
||||
|
||||
public LessEngine(Parser.Parser parser)
|
||||
: this(parser, new ConsoleLogger(LogLevel.Error), false, false, false, false, false, null)
|
||||
{
|
||||
}
|
||||
|
||||
public LessEngine()
|
||||
: this(new Parser.Parser())
|
||||
{
|
||||
}
|
||||
|
||||
public string TransformToCss(string source, string fileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
Parser.StrictMath = StrictMath;
|
||||
var tree = Parser.Parse(source, fileName);
|
||||
|
||||
var env = Env ??
|
||||
new Env(Parser)
|
||||
{
|
||||
Compress = Compress,
|
||||
Debug = Debug,
|
||||
KeepFirstSpecialComment = KeepFirstSpecialComment,
|
||||
};
|
||||
|
||||
if (Plugins != null)
|
||||
{
|
||||
foreach (IPluginConfigurator configurator in Plugins)
|
||||
{
|
||||
env.AddPlugin(configurator.CreatePlugin());
|
||||
}
|
||||
}
|
||||
|
||||
var css = tree.ToCSS(env);
|
||||
|
||||
var stylizer = new PlainStylizer();
|
||||
|
||||
foreach (var unmatchedExtension in env.FindUnmatchedExtensions()) {
|
||||
Logger.Warn("Warning: extend '{0}' has no matches {1}\n",
|
||||
unmatchedExtension.BaseSelector.ToCSS(env).Trim(),
|
||||
stylizer.Stylize(new Zone(unmatchedExtension.Extend.Location)).Trim());
|
||||
}
|
||||
|
||||
tree.Accept(DelegateVisitor.For<Media>(m => {
|
||||
foreach (var unmatchedExtension in m.FindUnmatchedExtensions()) {
|
||||
Logger.Warn("Warning: extend '{0}' has no matches {1}\n",
|
||||
unmatchedExtension.BaseSelector.ToCSS(env).Trim(),
|
||||
stylizer.Stylize(new Zone(unmatchedExtension.Extend.Location)).Trim());
|
||||
}
|
||||
}));
|
||||
|
||||
LastTransformationSuccessful = true;
|
||||
return css;
|
||||
}
|
||||
catch (ParserException e)
|
||||
{
|
||||
LastTransformationSuccessful = false;
|
||||
LastTransformationError = e;
|
||||
Logger.Error(e.Message);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public ParserException LastTransformationError { get; set; }
|
||||
|
||||
public IEnumerable<string> GetImports()
|
||||
{
|
||||
return Parser.Importer.GetImports();
|
||||
}
|
||||
|
||||
public void ResetImports()
|
||||
{
|
||||
Parser.Importer.ResetImports();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
81
dotnet/Applications/Mocha.Web.Server/Core/Engine/ParameterDecorator.cs
Executable file
81
dotnet/Applications/Mocha.Web.Server/Core/Engine/ParameterDecorator.cs
Executable file
@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using dotless.Core.Exceptions;
|
||||
using dotless.Core.Parser.Infrastructure;
|
||||
|
||||
namespace dotless.Core
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Parameters;
|
||||
|
||||
public class ParameterDecorator : ILessEngine
|
||||
{
|
||||
public readonly ILessEngine Underlying;
|
||||
private readonly IParameterSource parameterSource;
|
||||
|
||||
public ParameterDecorator(ILessEngine underlying, IParameterSource parameterSource)
|
||||
{
|
||||
this.Underlying = underlying;
|
||||
this.parameterSource = parameterSource;
|
||||
}
|
||||
|
||||
public string TransformToCss(string source, string fileName)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var parameters = parameterSource.GetParameters()
|
||||
.Where(ValueIsNotNullOrEmpty);
|
||||
|
||||
var parser = new Parser.Parser();
|
||||
sb.Append(source);
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
sb.AppendLine();
|
||||
var variableDeclaration = string.Format("@{0}: {1};", parameter.Key, parameter.Value);
|
||||
|
||||
try
|
||||
{
|
||||
// Attempt to evaluate the generated variable to see if it's OK
|
||||
parser.Parse(variableDeclaration, "").ToCSS(new Env());
|
||||
sb.Append(variableDeclaration);
|
||||
}
|
||||
catch (ParserException)
|
||||
{
|
||||
// Result wasn't valid LESS, output a comment instead
|
||||
sb.AppendFormat("/* Omitting variable '{0}'. The expression '{1}' is not valid. */", parameter.Key,
|
||||
parameter.Value);
|
||||
}
|
||||
}
|
||||
return Underlying.TransformToCss(sb.ToString(), fileName);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetImports()
|
||||
{
|
||||
return Underlying.GetImports();
|
||||
}
|
||||
|
||||
public void ResetImports()
|
||||
{
|
||||
Underlying.ResetImports();
|
||||
}
|
||||
|
||||
public bool LastTransformationSuccessful
|
||||
{
|
||||
get
|
||||
{
|
||||
return Underlying.LastTransformationSuccessful;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ValueIsNotNullOrEmpty(KeyValuePair<string, string> kvp)
|
||||
{
|
||||
return !string.IsNullOrEmpty(kvp.Value);
|
||||
}
|
||||
|
||||
public string CurrentDirectory
|
||||
{
|
||||
get { return Underlying.CurrentDirectory; }
|
||||
set { Underlying.CurrentDirectory = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
26
dotnet/Applications/Mocha.Web.Server/Core/Exceptions/ParserException.cs
Executable file
26
dotnet/Applications/Mocha.Web.Server/Core/Exceptions/ParserException.cs
Executable file
@ -0,0 +1,26 @@
|
||||
using dotless.Core.Parser;
|
||||
|
||||
namespace dotless.Core.Exceptions
|
||||
{
|
||||
using System;
|
||||
|
||||
public class ParserException : Exception
|
||||
{
|
||||
public ParserException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ParserException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
public ParserException(string message, Exception innerException, Zone errorLocation)
|
||||
: base(message, innerException) {
|
||||
ErrorLocation = errorLocation;
|
||||
}
|
||||
|
||||
public Zone ErrorLocation { get; set; }
|
||||
}
|
||||
}
|
||||
28
dotnet/Applications/Mocha.Web.Server/Core/Exceptions/ParsingException.cs
Executable file
28
dotnet/Applications/Mocha.Web.Server/Core/Exceptions/ParsingException.cs
Executable file
@ -0,0 +1,28 @@
|
||||
namespace dotless.Core.Exceptions
|
||||
{
|
||||
using System;
|
||||
using dotless.Core.Parser;
|
||||
|
||||
public class ParsingException : Exception
|
||||
{
|
||||
public NodeLocation Location { get; set; }
|
||||
public NodeLocation CallLocation { get; set; }
|
||||
|
||||
public ParsingException(string message, NodeLocation location) : this(message, null, location, null) { }
|
||||
|
||||
public ParsingException(string message, NodeLocation location, NodeLocation callLocation) : this(message, null, location, callLocation) { }
|
||||
|
||||
public ParsingException(Exception innerException, NodeLocation location) : this(innerException, location, null) { }
|
||||
|
||||
public ParsingException(Exception innerException, NodeLocation location, NodeLocation callLocation) : this(innerException.Message, innerException, location, callLocation) { }
|
||||
|
||||
public ParsingException(string message, Exception innerException, NodeLocation location) : this(message, innerException, location, null) { }
|
||||
|
||||
public ParsingException(string message, Exception innerException, NodeLocation location, NodeLocation callLocation)
|
||||
: base(message, innerException)
|
||||
{
|
||||
Location = location;
|
||||
CallLocation = callLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2015 Kristian Hellang
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace dotless.Core.Extensions
|
||||
{
|
||||
public class MissingTypeRegistrationException : InvalidOperationException
|
||||
{
|
||||
public MissingTypeRegistrationException(Type serviceType)
|
||||
: base($"Could not find any registered services for type '{GetFriendlyName(serviceType)}'.")
|
||||
{
|
||||
ServiceType = serviceType;
|
||||
}
|
||||
|
||||
public Type ServiceType { get; }
|
||||
private static string GetFriendlyName(Type type)
|
||||
{
|
||||
if (type == typeof(int)) return "int";
|
||||
if (type == typeof(short)) return "short";
|
||||
if (type == typeof(byte)) return "byte";
|
||||
if (type == typeof(bool)) return "bool";
|
||||
if (type == typeof(char)) return "char";
|
||||
if (type == typeof(long)) return "long";
|
||||
if (type == typeof(float)) return "float";
|
||||
if (type == typeof(double)) return "double";
|
||||
if (type == typeof(decimal)) return "decimal";
|
||||
if (type == typeof(string)) return "string";
|
||||
if (type == typeof(object)) return "object";
|
||||
// var typeInfo = type.GetTypeInfo();
|
||||
if (type.IsGenericType) return GetGenericFriendlyName(type);
|
||||
return type.Name;
|
||||
}
|
||||
|
||||
private static string GetGenericFriendlyName(Type typeInfo)
|
||||
{
|
||||
var argumentNames = typeInfo.GetGenericArguments().Select(GetFriendlyName).ToArray();
|
||||
var baseName = typeInfo.Name.Split('`').First();
|
||||
return $"{baseName}<{string.Join(", ", argumentNames)}>";
|
||||
}
|
||||
}
|
||||
}
|
||||
72
dotnet/Applications/Mocha.Web.Server/Core/Importers/IImporter.cs
Executable file
72
dotnet/Applications/Mocha.Web.Server/Core/Importers/IImporter.cs
Executable file
@ -0,0 +1,72 @@
|
||||
namespace dotless.Core.Importers
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Parser;
|
||||
using Parser.Tree;
|
||||
|
||||
public interface IImporter
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a list of the current paths, used to pass back in to alter url's after evaluation
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
List<string> GetCurrentPathsClone();
|
||||
|
||||
/// <summary>
|
||||
/// Imports an import and return true if successful
|
||||
/// </summary>
|
||||
ImportAction Import(Import import);
|
||||
|
||||
/// <summary>
|
||||
/// A method set by the parser implementation in order to get a new parser for use in importing
|
||||
/// </summary>
|
||||
Func<Parser> Parser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Called for every Url and allows the importer to adjust relative url's to be relative to the
|
||||
/// primary url
|
||||
/// </summary>
|
||||
string AlterUrl(string url, List<string> pathList);
|
||||
|
||||
string CurrentDirectory { get; set; }
|
||||
|
||||
IDisposable BeginScope(Import parent);
|
||||
|
||||
/// <summary>
|
||||
/// Resets the imports.
|
||||
/// </summary>
|
||||
void ResetImports();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the already imported files
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IEnumerable<string> GetImports();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The action to do with the @import statement
|
||||
/// </summary>
|
||||
public enum ImportAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Import as less (process the file and include)
|
||||
/// </summary>
|
||||
ImportLess,
|
||||
/// <summary>
|
||||
/// Import verbatim as CSS
|
||||
/// </summary>
|
||||
ImportCss,
|
||||
/// <summary>
|
||||
/// Leave a @import statement
|
||||
/// </summary>
|
||||
LeaveImport,
|
||||
/// <summary>
|
||||
/// Do nothing (e.g. when it is an import-once and has already been imported)
|
||||
/// </summary>
|
||||
ImportNothing,
|
||||
}
|
||||
}
|
||||
488
dotnet/Applications/Mocha.Web.Server/Core/Importers/Importer.cs
Executable file
488
dotnet/Applications/Mocha.Web.Server/Core/Importers/Importer.cs
Executable file
@ -0,0 +1,488 @@
|
||||
namespace dotless.Core.Importers
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Input;
|
||||
using Parser;
|
||||
using Parser.Tree;
|
||||
using Utils;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Reflection;
|
||||
|
||||
public class Importer : IImporter
|
||||
{
|
||||
private static readonly Regex _embeddedResourceRegex = new Regex(@"^dll://(?<Assembly>.+?)#(?<Resource>.+)$");
|
||||
|
||||
public static Regex EmbeddedResourceRegex { get { return _embeddedResourceRegex; } }
|
||||
public IFileReader FileReader { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of successful imports
|
||||
/// </summary>
|
||||
public List<string> Imports { get; set; }
|
||||
|
||||
public Func<Parser> Parser { get; set; }
|
||||
private readonly List<string> _paths = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// The raw imports of every @import node, for use with @import
|
||||
/// </summary>
|
||||
protected readonly List<string> _rawImports = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Duplicates of reference imports should be ignored just like normal imports
|
||||
/// but a reference import must not interfere with a regular import, hence a different list
|
||||
/// </summary>
|
||||
private readonly List<string> _referenceImports = new List<string>();
|
||||
|
||||
public virtual string CurrentDirectory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the importer should alter urls
|
||||
/// </summary>
|
||||
public bool IsUrlRewritingDisabled { get; set; }
|
||||
|
||||
public string RootPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Import all files as if they are less regardless of file extension
|
||||
/// </summary>
|
||||
public bool ImportAllFilesAsLess { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Import the css and include inline
|
||||
/// </summary>
|
||||
public bool InlineCssFiles { get; set; }
|
||||
|
||||
|
||||
public Importer() : this(new FileReader())
|
||||
{
|
||||
}
|
||||
|
||||
public Importer(IFileReader fileReader) : this(fileReader, false, "", false, false)
|
||||
{
|
||||
}
|
||||
|
||||
public Importer(IFileReader fileReader, bool disableUrlReWriting, string rootPath, bool inlineCssFiles, bool importAllFilesAsLess)
|
||||
{
|
||||
FileReader = fileReader;
|
||||
IsUrlRewritingDisabled = disableUrlReWriting;
|
||||
RootPath = rootPath;
|
||||
InlineCssFiles = inlineCssFiles;
|
||||
ImportAllFilesAsLess = importAllFilesAsLess;
|
||||
Imports = new List<string>();
|
||||
CurrentDirectory = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether a url has a protocol on it
|
||||
/// </summary>
|
||||
private static bool IsProtocolUrl(string url)
|
||||
{
|
||||
return Regex.IsMatch(url, @"^([a-zA-Z]{2,}:)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether a url has a protocol on it
|
||||
/// </summary>
|
||||
private static bool IsNonRelativeUrl(string url)
|
||||
{
|
||||
return url.StartsWith(@"/") || url.StartsWith(@"~/") || Regex.IsMatch(url, @"^[a-zA-Z]:");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether a url represents an embedded resource
|
||||
/// </summary>
|
||||
private static bool IsEmbeddedResource(string path)
|
||||
{
|
||||
return _embeddedResourceRegex.IsMatch(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of the current paths, used to pass back in to alter url's after evaluation
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<string> GetCurrentPathsClone()
|
||||
{
|
||||
return new List<string>(_paths);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns true if the import should be ignored because it is a duplicate and import-once was used
|
||||
/// </summary>
|
||||
/// <param name="import"></param>
|
||||
/// <returns></returns>
|
||||
protected bool CheckIgnoreImport(Import import)
|
||||
{
|
||||
return CheckIgnoreImport(import, import.Path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns true if the import should be ignored because it is a duplicate and import-once was used
|
||||
/// </summary>
|
||||
/// <param name="import"></param>
|
||||
/// <returns></returns>
|
||||
protected bool CheckIgnoreImport(Import import, string path)
|
||||
{
|
||||
if (IsOptionSet(import.ImportOptions, ImportOptions.Multiple))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// The import option Reference is set at parse time,
|
||||
// but the IsReference bit is set at evaluation time (inherited from parent)
|
||||
// so we check both.
|
||||
if (import.IsReference || IsOptionSet(import.ImportOptions, ImportOptions.Reference))
|
||||
{
|
||||
if (_rawImports.Contains(path, StringComparer.InvariantCultureIgnoreCase))
|
||||
{
|
||||
// Already imported as a regular import, so the reference import is redundant
|
||||
return true;
|
||||
}
|
||||
|
||||
return CheckIgnoreImport(_referenceImports, path);
|
||||
}
|
||||
|
||||
return CheckIgnoreImport(_rawImports, path);
|
||||
}
|
||||
|
||||
private bool CheckIgnoreImport(List<string> importList, string path) {
|
||||
if (importList.Contains(path, StringComparer.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
importList.Add(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imports the file inside the import as a dot-less file.
|
||||
/// </summary>
|
||||
/// <param name="import"></param>
|
||||
/// <returns> The action for the import node to process</returns>
|
||||
public virtual ImportAction Import(Import import)
|
||||
{
|
||||
// if the import is protocol'd (e.g. http://www.opencss.com/css?blah) then leave the import alone
|
||||
if (IsProtocolUrl(import.Path) && !IsEmbeddedResource(import.Path))
|
||||
{
|
||||
if (import.Path.EndsWith(".less"))
|
||||
{
|
||||
throw new FileNotFoundException(string.Format(".less cannot import non local less files [{0}].", import.Path), import.Path);
|
||||
}
|
||||
|
||||
if (CheckIgnoreImport(import))
|
||||
{
|
||||
return ImportAction.ImportNothing;
|
||||
}
|
||||
|
||||
return ImportAction.LeaveImport;
|
||||
}
|
||||
|
||||
var file = import.Path;
|
||||
|
||||
if (!IsNonRelativeUrl(file))
|
||||
{
|
||||
file = GetAdjustedFilePath(import.Path, _paths);
|
||||
}
|
||||
|
||||
if (CheckIgnoreImport(import, file))
|
||||
{
|
||||
return ImportAction.ImportNothing;
|
||||
}
|
||||
|
||||
bool importAsless = ImportAllFilesAsLess || IsOptionSet(import.ImportOptions, ImportOptions.Less);
|
||||
|
||||
if (!importAsless && import.Path.EndsWith(".css") && !import.Path.EndsWith(".less.css"))
|
||||
{
|
||||
if (InlineCssFiles || IsOptionSet(import.ImportOptions, ImportOptions.Inline))
|
||||
{
|
||||
if (IsEmbeddedResource(import.Path) && ImportEmbeddedCssContents(file, import))
|
||||
return ImportAction.ImportCss;
|
||||
if (ImportCssFileContents(file, import))
|
||||
return ImportAction.ImportCss;
|
||||
}
|
||||
|
||||
return ImportAction.LeaveImport;
|
||||
}
|
||||
|
||||
if (Parser == null)
|
||||
throw new InvalidOperationException("Parser cannot be null.");
|
||||
|
||||
string fullPath = String.Empty;
|
||||
if (!ImportLessFile(file, import, out fullPath))
|
||||
{
|
||||
if (IsOptionSet(import.ImportOptions, ImportOptions.Optional))
|
||||
{
|
||||
return ImportAction.ImportNothing;
|
||||
}
|
||||
|
||||
if (IsOptionSet(import.ImportOptions, ImportOptions.Css))
|
||||
{
|
||||
return ImportAction.LeaveImport;
|
||||
}
|
||||
|
||||
if (import.Path.EndsWith(".less", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
throw new FileNotFoundException(string.Format("You are importing a file ending in .less that cannot be found [{0}].", fullPath), file);
|
||||
}
|
||||
return ImportAction.LeaveImport;
|
||||
}
|
||||
|
||||
return ImportAction.ImportLess;
|
||||
}
|
||||
|
||||
public IDisposable BeginScope(Import parentScope) {
|
||||
return new ImportScope(this, Path.GetDirectoryName(parentScope.Path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the paths to adjust the file path
|
||||
/// </summary>
|
||||
protected string GetAdjustedFilePath(string path, IEnumerable<string> pathList)
|
||||
{
|
||||
return pathList.Concat(new[] { path }).AggregatePaths(CurrentDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imports a less file and puts the root into the import node
|
||||
/// </summary>
|
||||
protected bool ImportLessFile(string lessPath, Import import, out string fullName)
|
||||
{
|
||||
string contents, file = null;
|
||||
if (IsEmbeddedResource(lessPath))
|
||||
{
|
||||
contents = ResourceLoader.GetResource(lessPath, FileReader, out file);
|
||||
if (contents == null)
|
||||
{
|
||||
fullName = lessPath;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fullName = RootPath + '/' + lessPath;
|
||||
if (Path.IsPathRooted(lessPath))
|
||||
{
|
||||
fullName = lessPath;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(CurrentDirectory)) {
|
||||
fullName = CurrentDirectory.Replace(@"\", "/").TrimEnd('/') + '/' + lessPath;
|
||||
}
|
||||
|
||||
bool fileExists = FileReader.DoesFileExist(fullName);
|
||||
if (!fileExists && !fullName.EndsWith(".less"))
|
||||
{
|
||||
fullName += ".less";
|
||||
fileExists = FileReader.DoesFileExist(fullName);
|
||||
}
|
||||
|
||||
if (!fileExists) return false;
|
||||
|
||||
contents = FileReader.GetFileContents(fullName);
|
||||
|
||||
file = fullName;
|
||||
}
|
||||
|
||||
_paths.Add(Path.GetDirectoryName(import.Path));
|
||||
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(file))
|
||||
{
|
||||
Imports.Add(file);
|
||||
}
|
||||
import.InnerRoot = Parser().Parse(contents, lessPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Imports.Remove(file);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_paths.RemoveAt(_paths.Count - 1);
|
||||
}
|
||||
|
||||
fullName = file;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imports a css file from an embedded resource and puts the contents into the import node
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <param name="import"></param>
|
||||
/// <returns></returns>
|
||||
private bool ImportEmbeddedCssContents(string file, Import import)
|
||||
{
|
||||
string content = ResourceLoader.GetResource(file, FileReader, out file);
|
||||
if (content == null) return false;
|
||||
import.InnerContent = content;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imports a css file and puts the contents into the import node
|
||||
/// </summary>
|
||||
protected bool ImportCssFileContents(string file, Import import)
|
||||
{
|
||||
if (!FileReader.DoesFileExist(file))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
import.InnerContent = FileReader.GetFileContents(file);
|
||||
Imports.Add(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called for every Url and allows the importer to adjust relative url's to be relative to the
|
||||
/// primary url
|
||||
/// </summary>
|
||||
public string AlterUrl(string url, List<string> pathList)
|
||||
{
|
||||
if (!IsProtocolUrl (url) && !IsNonRelativeUrl (url))
|
||||
{
|
||||
if (pathList.Any() && !IsUrlRewritingDisabled)
|
||||
{
|
||||
url = GetAdjustedFilePath(url, pathList);
|
||||
}
|
||||
return RootPath + url;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public void ResetImports()
|
||||
{
|
||||
Imports.Clear();
|
||||
_rawImports.Clear();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetImports()
|
||||
{
|
||||
return Imports.Distinct();
|
||||
}
|
||||
|
||||
private class ImportScope : IDisposable {
|
||||
private readonly Importer importer;
|
||||
|
||||
public ImportScope(Importer importer, string path) {
|
||||
this.importer = importer;
|
||||
this.importer._paths.Add(path);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.importer._paths.RemoveAt(this.importer._paths.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsOptionSet(ImportOptions options, ImportOptions test)
|
||||
{
|
||||
return (options & test) == test;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility class used to retrieve the content of an embedded resource using a separate app domain in order to unload the assembly when done.
|
||||
/// </summary>
|
||||
class ResourceLoader : MarshalByRefObject
|
||||
{
|
||||
private byte[] _fileContents;
|
||||
private string _resourceName;
|
||||
private string _resourceContent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text content of an embedded resource.
|
||||
/// </summary>
|
||||
/// <param name="file">The path in the form: dll://AssemblyName#ResourceName</param>
|
||||
/// <returns>The content of the resource</returns>
|
||||
public static string GetResource(string file, IFileReader fileReader, out string fileDependency)
|
||||
{
|
||||
fileDependency = null;
|
||||
|
||||
var match = Importer.EmbeddedResourceRegex.Match(file);
|
||||
if (!match.Success) return null;
|
||||
|
||||
var loader = new ResourceLoader
|
||||
{
|
||||
_resourceName = match.Groups["Resource"].Value
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
fileDependency = match.Groups["Assembly"].Value;
|
||||
|
||||
LoadFromCurrentAppDomain(loader, fileDependency);
|
||||
|
||||
if (String.IsNullOrEmpty(loader._resourceContent))
|
||||
LoadFromNewAppDomain(loader, fileReader, fileDependency);
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new FileNotFoundException("Unable to load resource [" + loader._resourceName + "] in assembly [" + fileDependency + "]");
|
||||
}
|
||||
finally
|
||||
{
|
||||
loader._fileContents = null;
|
||||
}
|
||||
|
||||
return loader._resourceContent;
|
||||
}
|
||||
|
||||
private static void LoadFromCurrentAppDomain(ResourceLoader loader, String assemblyName)
|
||||
{
|
||||
foreach (var assembly in AppDomain.CurrentDomain
|
||||
.GetAssemblies()
|
||||
.Where(x => !IsDynamicAssembly(x) && x.Location.EndsWith(assemblyName, StringComparison.InvariantCultureIgnoreCase)))
|
||||
{
|
||||
if (assembly.GetManifestResourceNames().Contains(loader._resourceName))
|
||||
{
|
||||
using (var stream = assembly.GetManifestResourceStream(loader._resourceName))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
loader._resourceContent = reader.ReadToEnd();
|
||||
|
||||
if (!String.IsNullOrEmpty(loader._resourceContent))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsDynamicAssembly(Assembly assembly) {
|
||||
try {
|
||||
string loc = assembly.Location;
|
||||
return false;
|
||||
} catch (NotSupportedException) {
|
||||
// Location is not supported for dynamic assemblies, so it will throw a NotSupportedException
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadFromNewAppDomain(ResourceLoader loader, IFileReader fileReader, String assemblyName)
|
||||
{
|
||||
if (!fileReader.DoesFileExist(assemblyName))
|
||||
{
|
||||
throw new FileNotFoundException("Unable to locate assembly file [" + assemblyName + "]");
|
||||
}
|
||||
|
||||
loader._fileContents = fileReader.GetBinaryFileContents(assemblyName);
|
||||
|
||||
var domain = AppDomain.CreateDomain("LoaderDomain");
|
||||
var assembly = domain.Load(loader._fileContents);
|
||||
|
||||
using (var stream = assembly.GetManifestResourceStream(loader._resourceName))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
loader._resourceContent = reader.ReadToEnd();
|
||||
}
|
||||
|
||||
AppDomain.Unload(domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
41
dotnet/Applications/Mocha.Web.Server/Core/Input/FileReader.cs
Executable file
41
dotnet/Applications/Mocha.Web.Server/Core/Input/FileReader.cs
Executable file
@ -0,0 +1,41 @@
|
||||
namespace dotless.Core.Input
|
||||
{
|
||||
using System.IO;
|
||||
|
||||
public class FileReader : IFileReader
|
||||
{
|
||||
public IPathResolver PathResolver { get; set; }
|
||||
|
||||
public FileReader() : this(new RelativePathResolver())
|
||||
{
|
||||
}
|
||||
|
||||
public FileReader(IPathResolver pathResolver)
|
||||
{
|
||||
PathResolver = pathResolver;
|
||||
}
|
||||
|
||||
public byte[] GetBinaryFileContents(string fileName)
|
||||
{
|
||||
fileName = PathResolver.GetFullPath(fileName);
|
||||
|
||||
return File.ReadAllBytes(fileName);
|
||||
}
|
||||
|
||||
public string GetFileContents(string fileName)
|
||||
{
|
||||
fileName = PathResolver.GetFullPath(fileName);
|
||||
|
||||
return File.ReadAllText(fileName);
|
||||
}
|
||||
|
||||
public bool DoesFileExist(string fileName)
|
||||
{
|
||||
fileName = PathResolver.GetFullPath(fileName);
|
||||
|
||||
return File.Exists(fileName);
|
||||
}
|
||||
|
||||
public bool UseCacheDependencies { get { return true; } }
|
||||
}
|
||||
}
|
||||
13
dotnet/Applications/Mocha.Web.Server/Core/Input/IFileReader.cs
Executable file
13
dotnet/Applications/Mocha.Web.Server/Core/Input/IFileReader.cs
Executable file
@ -0,0 +1,13 @@
|
||||
namespace dotless.Core.Input
|
||||
{
|
||||
public interface IFileReader
|
||||
{
|
||||
byte[] GetBinaryFileContents(string fileName);
|
||||
|
||||
string GetFileContents(string fileName);
|
||||
|
||||
bool DoesFileExist(string fileName);
|
||||
|
||||
bool UseCacheDependencies { get; }
|
||||
}
|
||||
}
|
||||
7
dotnet/Applications/Mocha.Web.Server/Core/Input/IPathResolver.cs
Executable file
7
dotnet/Applications/Mocha.Web.Server/Core/Input/IPathResolver.cs
Executable file
@ -0,0 +1,7 @@
|
||||
namespace dotless.Core.Input
|
||||
{
|
||||
public interface IPathResolver
|
||||
{
|
||||
string GetFullPath(string path);
|
||||
}
|
||||
}
|
||||
10
dotnet/Applications/Mocha.Web.Server/Core/Input/RelativePathResolver.cs
Executable file
10
dotnet/Applications/Mocha.Web.Server/Core/Input/RelativePathResolver.cs
Executable file
@ -0,0 +1,10 @@
|
||||
namespace dotless.Core.Input
|
||||
{
|
||||
public class RelativePathResolver : IPathResolver
|
||||
{
|
||||
public string GetFullPath(string path)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
dotnet/Applications/Mocha.Web.Server/Core/Less.cs
Executable file
25
dotnet/Applications/Mocha.Web.Server/Core/Less.cs
Executable file
@ -0,0 +1,25 @@
|
||||
namespace dotless.Core
|
||||
{
|
||||
using configuration;
|
||||
using System;
|
||||
|
||||
public static class Less
|
||||
{
|
||||
public static string Parse(string less)
|
||||
{
|
||||
return Parse(less, DotlessConfiguration.GetDefault());
|
||||
}
|
||||
|
||||
public static string Parse(string less, DotlessConfiguration config)
|
||||
{
|
||||
if (config.Web)
|
||||
{
|
||||
// throw new Exception("Please use dotless.Core.LessWeb.Parse for web applications. This makes sure all web features are available.");
|
||||
}
|
||||
|
||||
LessEngine engine = new LessEngine();
|
||||
engine.CurrentDirectory = config.RootPath;
|
||||
return engine.TransformToCss(less, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
dotnet/Applications/Mocha.Web.Server/Core/Loggers/ConsoleLogger.cs
Executable file
20
dotnet/Applications/Mocha.Web.Server/Core/Loggers/ConsoleLogger.cs
Executable file
@ -0,0 +1,20 @@
|
||||
namespace dotless.Core.Loggers
|
||||
{
|
||||
using dotless.Core.configuration;
|
||||
using System;
|
||||
|
||||
public class ConsoleLogger : Logger
|
||||
{
|
||||
public ConsoleLogger(LogLevel level) : base(level) { }
|
||||
|
||||
public ConsoleLogger(DotlessConfiguration config) : this(config.LogLevel)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void Log(string message)
|
||||
{
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
dotnet/Applications/Mocha.Web.Server/Core/Loggers/DiagnosticsLogger.cs
Executable file
14
dotnet/Applications/Mocha.Web.Server/Core/Loggers/DiagnosticsLogger.cs
Executable file
@ -0,0 +1,14 @@
|
||||
namespace dotless.Core.Loggers
|
||||
{
|
||||
public class DiagnosticsLogger : Logger
|
||||
{
|
||||
public DiagnosticsLogger(LogLevel level) : base(level)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Log(string message)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
dotnet/Applications/Mocha.Web.Server/Core/Loggers/ILogger.cs
Executable file
23
dotnet/Applications/Mocha.Web.Server/Core/Loggers/ILogger.cs
Executable file
@ -0,0 +1,23 @@
|
||||
namespace dotless.Core.Loggers
|
||||
{
|
||||
public interface ILogger
|
||||
{
|
||||
void Log(LogLevel level, string message);
|
||||
void Info(string message);
|
||||
void Info(string message, params object[] args);
|
||||
void Debug(string message);
|
||||
void Debug(string message, params object[] args);
|
||||
void Warn(string message);
|
||||
void Warn(string message, params object[] args);
|
||||
void Error(string message);
|
||||
void Error(string message, params object[] args);
|
||||
}
|
||||
|
||||
public enum LogLevel
|
||||
{
|
||||
Info = 1,
|
||||
Debug,
|
||||
Warn,
|
||||
Error
|
||||
}
|
||||
}
|
||||
30
dotnet/Applications/Mocha.Web.Server/Core/Loggers/Logger.cs
Executable file
30
dotnet/Applications/Mocha.Web.Server/Core/Loggers/Logger.cs
Executable file
@ -0,0 +1,30 @@
|
||||
namespace dotless.Core.Loggers
|
||||
{
|
||||
public abstract class Logger : ILogger
|
||||
{
|
||||
public LogLevel Level { get; set; }
|
||||
|
||||
protected Logger(LogLevel level)
|
||||
{
|
||||
Level = level;
|
||||
}
|
||||
|
||||
public void Log(LogLevel level, string message)
|
||||
{
|
||||
if (Level <= level)
|
||||
Log(message);
|
||||
}
|
||||
|
||||
protected abstract void Log(string message);
|
||||
|
||||
public void Info(string message) { Log(LogLevel.Info, message); }
|
||||
public void Debug(string message) { Log(LogLevel.Debug, message); }
|
||||
public void Warn(string message) { Log(LogLevel.Warn, message); }
|
||||
public void Error(string message) { Log(LogLevel.Error, message); }
|
||||
public void Info(string message, params object[] args) { Log(LogLevel.Info, string.Format(message, args)); }
|
||||
public void Debug(string message, params object[] args) { Log(LogLevel.Debug, string.Format(message, args)); }
|
||||
public void Warn(string message, params object[] args) { Log(LogLevel.Warn, string.Format(message, args)); }
|
||||
public void Error(string message, params object[] args) { Log(LogLevel.Error, string.Format(message, args)); }
|
||||
|
||||
}
|
||||
}
|
||||
23
dotnet/Applications/Mocha.Web.Server/Core/Loggers/NullLogger.cs
Executable file
23
dotnet/Applications/Mocha.Web.Server/Core/Loggers/NullLogger.cs
Executable file
@ -0,0 +1,23 @@
|
||||
namespace dotless.Core.Loggers
|
||||
{
|
||||
public class NullLogger : Logger
|
||||
{
|
||||
public NullLogger(LogLevel level) : base(level)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Log(string message)
|
||||
{
|
||||
//Swallow
|
||||
}
|
||||
|
||||
private static readonly NullLogger instance = new NullLogger(LogLevel.Warn);
|
||||
public static NullLogger Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
namespace dotless.Core.Parameters
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class ConsoleArgumentParameterSource : IParameterSource
|
||||
{
|
||||
public static IDictionary<string, string> ConsoleArguments = new Dictionary<string, string>();
|
||||
public IDictionary<string, string> GetParameters()
|
||||
{
|
||||
return ConsoleArguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
dotnet/Applications/Mocha.Web.Server/Core/Parameters/IParameterSource.cs
Executable file
9
dotnet/Applications/Mocha.Web.Server/Core/Parameters/IParameterSource.cs
Executable file
@ -0,0 +1,9 @@
|
||||
namespace dotless.Core.Parameters
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
|
||||
public interface IParameterSource
|
||||
{
|
||||
IDictionary<string, string> GetParameters();
|
||||
}
|
||||
}
|
||||
11
dotnet/Applications/Mocha.Web.Server/Core/Parameters/NullParameterSource.cs
Executable file
11
dotnet/Applications/Mocha.Web.Server/Core/Parameters/NullParameterSource.cs
Executable file
@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace dotless.Core.Parameters {
|
||||
public class NullParameterSource : IParameterSource
|
||||
{
|
||||
public IDictionary<string, string> GetParameters()
|
||||
{
|
||||
return new Dictionary<string, string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
17
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AbsFunction.cs
Executable file
17
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AbsFunction.cs
Executable file
@ -0,0 +1,17 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
|
||||
public class AbsFunction : NumberFunctionBase
|
||||
{
|
||||
protected override Node Eval(Env env, Number number, Node[] args)
|
||||
{
|
||||
WarnNotSupportedByLessJS("abs(number)");
|
||||
|
||||
return new Number(Math.Abs(number.Value), number.Unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AddFunction.cs
Executable file
20
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AddFunction.cs
Executable file
@ -0,0 +1,20 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System.Linq;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class AddFunction : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectAllNodes<Number>(Arguments, this, Location);
|
||||
|
||||
var value = Arguments.Cast<Number>().Select(d => d.Value).Aggregate(0d, (a, b) => a + b);
|
||||
|
||||
return new Number(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
51
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AlphaFunction.cs
Executable file
51
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AlphaFunction.cs
Executable file
@ -0,0 +1,51 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
|
||||
public class FadeInFunction : ColorFunctionBase
|
||||
{
|
||||
protected override Node Eval(Color color)
|
||||
{
|
||||
return new Number(color.Alpha);
|
||||
}
|
||||
|
||||
protected override Node EditColor(Color color, Number number)
|
||||
{
|
||||
var alpha = number.Value/100d;
|
||||
|
||||
return new Color(color.R, color.G, color.B, ProcessAlpha( color.Alpha, alpha));
|
||||
}
|
||||
|
||||
protected virtual double ProcessAlpha(double originalAlpha, double newAlpha)
|
||||
{
|
||||
return originalAlpha + newAlpha;
|
||||
}
|
||||
}
|
||||
|
||||
public class AlphaFunction : FadeInFunction
|
||||
{
|
||||
protected override Node EditColor(Color color, Number number)
|
||||
{
|
||||
WarnNotSupportedByLessJS("alpha(color, number)", "fadein(color, number) or the opposite fadeout(color, number),");
|
||||
|
||||
return base.EditColor(color, number);
|
||||
}
|
||||
}
|
||||
|
||||
public class FadeOutFunction : AlphaFunction
|
||||
{
|
||||
protected override Node EditColor(Color color, Number number)
|
||||
{
|
||||
return base.EditColor(color, -number);
|
||||
}
|
||||
}
|
||||
|
||||
public class FadeFunction : AlphaFunction
|
||||
{
|
||||
protected override double ProcessAlpha(double originalAlpha, double newAlpha)
|
||||
{
|
||||
return newAlpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ArgbFunction.cs
Executable file
18
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ArgbFunction.cs
Executable file
@ -0,0 +1,18 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class ArgbFunction : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectNumArguments(1, Arguments.Count, this, Location);
|
||||
var color = Guard.ExpectNode<Color>(Arguments[0], this, Location);
|
||||
|
||||
return new TextNode(color.ToArgb());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
public class AverageFunction : ColorMixFunction
|
||||
{
|
||||
protected override double Operate(double a, double b)
|
||||
{
|
||||
return (a + b) / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/BlueFunction.cs
Executable file
25
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/BlueFunction.cs
Executable file
@ -0,0 +1,25 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
|
||||
public class BlueFunction : ColorFunctionBase
|
||||
{
|
||||
protected override Node Eval(Color color)
|
||||
{
|
||||
return new Number(color.B);
|
||||
}
|
||||
|
||||
protected override Node EditColor(Color color, Number number)
|
||||
{
|
||||
WarnNotSupportedByLessJS("blue(color, number)");
|
||||
|
||||
var value = number.Value;
|
||||
|
||||
if (number.Unit == "%")
|
||||
value = (value*255)/100d;
|
||||
|
||||
return new Color(color.R, color.G, color.B + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/CeilFunction.cs
Executable file
15
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/CeilFunction.cs
Executable file
@ -0,0 +1,15 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
|
||||
public class CeilFunction : NumberFunctionBase
|
||||
{
|
||||
protected override Node Eval(Env env, Number number, Node[] args)
|
||||
{
|
||||
return new Number(Math.Ceiling(number.Value), number.Unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ColorFunction.cs
Executable file
28
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ColorFunction.cs
Executable file
@ -0,0 +1,28 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
using System;
|
||||
using Exceptions;
|
||||
|
||||
public class ColorFunction : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectNumArguments(1, Arguments.Count, this, Location);
|
||||
var node = Guard.ExpectNode<TextNode>(Arguments[0], this, Location);
|
||||
|
||||
try
|
||||
{
|
||||
return Color.From(node.Value);
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
var message = string.Format("Invalid RGB color string '{0}'", node.Value);
|
||||
throw new ParsingException(message, ex, Location, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System.Linq;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public abstract class ColorFunctionBase : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectMinArguments(1, Arguments.Count(), this, Location);
|
||||
Guard.ExpectNode<Color>(Arguments[0], this, Arguments[0].Location);
|
||||
|
||||
var color = Arguments[0] as Color;
|
||||
|
||||
if (Arguments.Count == 2)
|
||||
{
|
||||
Guard.ExpectNode<Number>(Arguments[1], this, Arguments[1].Location);
|
||||
|
||||
var number = Arguments[1] as Number;
|
||||
var edit = EditColor(color, number);
|
||||
|
||||
if (edit != null)
|
||||
return edit;
|
||||
}
|
||||
|
||||
return Eval(color);
|
||||
}
|
||||
|
||||
protected abstract Node Eval(Color color);
|
||||
|
||||
protected virtual Node EditColor(Color color, Number number)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public abstract class ColorMixFunction : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectNumArguments(2, Arguments.Count, this, Location);
|
||||
Guard.ExpectAllNodes<Color>(Arguments, this, Location);
|
||||
|
||||
var color1 = (Color) Arguments[0];
|
||||
var color2 = (Color) Arguments[1];
|
||||
|
||||
var resultAlpha = color2.Alpha + color1.Alpha*(1 - color2.Alpha);
|
||||
|
||||
return new Color(
|
||||
Compose(color1, color2, resultAlpha, c => c.R),
|
||||
Compose(color1, color2, resultAlpha, c => c.G),
|
||||
Compose(color1, color2, resultAlpha, c => c.B),
|
||||
resultAlpha);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Color composition rules from http://www.w3.org/TR/compositing-1/
|
||||
/// (translated from the less.js version
|
||||
/// </summary>
|
||||
private double Compose(Color backdrop, Color source, double ar, Func<Color, double> channel) {
|
||||
var cb = channel(backdrop);
|
||||
var cs = channel(source);
|
||||
var ab = backdrop.Alpha;
|
||||
var @as = source.Alpha;
|
||||
double result = Operate(cb, cs);
|
||||
if (ar > 0)
|
||||
{
|
||||
result = (@as * cs + ab * (cb - @as * (cb + cs - result))) / ar;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract double Operate(double a, double b);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class ComplementFunction : HslColorFunctionBase
|
||||
{
|
||||
protected override Node EvalHsl(HslColor color)
|
||||
{
|
||||
WarnNotSupportedByLessJS("complement(color)");
|
||||
|
||||
color.Hue += 0.5;
|
||||
return color.ToRgbColor();
|
||||
}
|
||||
|
||||
protected override Node EditHsl(HslColor color, Number number)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class ContrastFunction : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectMinArguments(1, Arguments.Count, this, Location);
|
||||
Guard.ExpectMaxArguments(4, Arguments.Count, this, Location);
|
||||
Guard.ExpectNode<Color>(Arguments[0], this, Location);
|
||||
|
||||
var color = (Color) Arguments[0];
|
||||
|
||||
if (Arguments.Count > 1)
|
||||
Guard.ExpectNode<Color>(Arguments[1], this, Location);
|
||||
if (Arguments.Count > 2)
|
||||
Guard.ExpectNode<Color>(Arguments[2], this, Location);
|
||||
if (Arguments.Count > 3)
|
||||
Guard.ExpectNode<Number>(Arguments[3], this, Location);
|
||||
|
||||
var lightColor = Arguments.Count > 1 ? (Color)Arguments[1] : new Color(255d, 255d, 255d);
|
||||
var darkColor = Arguments.Count > 2 ? (Color)Arguments[2] : new Color(0d, 0d, 0d);
|
||||
var threshold = Arguments.Count > 3 ? ((Number) Arguments[3]).ToNumber() : 0.43d;
|
||||
|
||||
if (darkColor.Luma > lightColor.Luma)
|
||||
{
|
||||
var tempColor = lightColor;
|
||||
lightColor = darkColor;
|
||||
darkColor = tempColor;
|
||||
}
|
||||
|
||||
return (color.Luma < threshold) ? lightColor : darkColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using Exceptions;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class DataUriFunction : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
var filename = GetDataUriFilename();
|
||||
string base64 = ConvertFileToBase64(filename);
|
||||
string mimeType = GetMimeType(filename);
|
||||
|
||||
return new TextNode(string.Format("url(\"data:{0};base64,{1}\")", mimeType, base64));
|
||||
}
|
||||
|
||||
private string GetDataUriFilename()
|
||||
{
|
||||
Guard.ExpectMinArguments(1, Arguments.Count, this, Location);
|
||||
|
||||
// Thanks a lot LESS for putting the optional parameter first!
|
||||
var filenameNode = Arguments[0];
|
||||
if (Arguments.Count > 1)
|
||||
filenameNode = Arguments[1];
|
||||
|
||||
Guard.ExpectNode<Quoted>(filenameNode, this, Location);
|
||||
var filename = ((Quoted)filenameNode).Value;
|
||||
|
||||
Guard.Expect(!(filename.StartsWith("http://") || filename.StartsWith("https://")),
|
||||
string.Format("Invalid filename passed to data-uri '{0}'. Filename must be a local file", filename), Location);
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
private string ConvertFileToBase64(string filename)
|
||||
{
|
||||
string base64;
|
||||
try
|
||||
{
|
||||
base64 = Convert.ToBase64String(File.ReadAllBytes(filename));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// this is more general than just a check to see whether the file exists
|
||||
// it could fail for other reasons like security permissions
|
||||
throw new ParsingException(String.Format("Data-uri function could not read file '{0}'", filename), e, Location);
|
||||
}
|
||||
return base64;
|
||||
}
|
||||
|
||||
private string GetMimeType(string filename)
|
||||
{
|
||||
if (Arguments.Count > 1)
|
||||
{
|
||||
Guard.ExpectNode<Quoted>(Arguments[0], this, Location);
|
||||
var mimeType = ((Quoted) Arguments[0]).Value;
|
||||
|
||||
if (mimeType.IndexOf(';') > -1)
|
||||
mimeType = mimeType.Split(';')[0];
|
||||
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
return new MimeTypeLookup().ByFilename(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
using dotless.Core.Parser.Infrastructure;
|
||||
using dotless.Core.Parser.Infrastructure.Nodes;
|
||||
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
public class DefaultFunction : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
return new TextNode("default()");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System;
|
||||
public class DifferenceFunction : ColorMixFunction
|
||||
{
|
||||
protected override double Operate(double a, double b)
|
||||
{
|
||||
return Math.Abs(a - b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
public class ExclusionFunction : ColorMixFunction
|
||||
{
|
||||
protected override double Operate(double a, double b)
|
||||
{
|
||||
return a + b * (255 - a - a) / 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
using dotless.Core.Parser.Infrastructure;
|
||||
using dotless.Core.Parser.Infrastructure.Nodes;
|
||||
using dotless.Core.Parser.Tree;
|
||||
using dotless.Core.Utils;
|
||||
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
public class ExtractFunction : ListFunctionBase
|
||||
{
|
||||
protected override Node Eval(Env env, Node[] list, Node[] args)
|
||||
{
|
||||
Guard.ExpectNumArguments(1, args.Length, this, Location);
|
||||
Guard.ExpectNode<Number>(args[0], this, args[0].Location);
|
||||
|
||||
var index = (int)(args[0] as Number).Value;
|
||||
|
||||
// Extract function indecies are 1-based
|
||||
return list[index-1];
|
||||
}
|
||||
}
|
||||
}
|
||||
15
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/FloorFunction.cs
Executable file
15
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/FloorFunction.cs
Executable file
@ -0,0 +1,15 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
|
||||
public class FloorFunction : NumberFunctionBase
|
||||
{
|
||||
protected override Node Eval(Env env, Number number, Node[] args)
|
||||
{
|
||||
return new Number(Math.Floor(number.Value), number.Unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using dotless.Core.Exceptions;
|
||||
|
||||
public class FormatStringFunction : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
WarnNotSupportedByLessJS("formatstring(string, args...)", null, @" You may want to consider using string interpolation (""@{variable}"") which does the same thing and is supported.");
|
||||
|
||||
if (Arguments.Count == 0)
|
||||
return new Quoted("", false);
|
||||
|
||||
Func<Node, string> unescape = n => n is Quoted ? ((Quoted) n).UnescapeContents() : n.ToCSS(env);
|
||||
|
||||
var format = unescape(Arguments[0]);
|
||||
|
||||
var args = Arguments.Skip(1).Select(unescape).ToArray();
|
||||
|
||||
string result;
|
||||
|
||||
try
|
||||
{
|
||||
result = string.Format(format, args);
|
||||
}
|
||||
catch (FormatException e)
|
||||
{
|
||||
throw new ParserException(string.Format("Error in formatString :{0}", e.Message), e);
|
||||
}
|
||||
|
||||
return new Quoted(result, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
70
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/Function.cs
Executable file
70
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/Function.cs
Executable file
@ -0,0 +1,70 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using dotless.Core.Loggers;
|
||||
|
||||
public abstract class Function
|
||||
{
|
||||
public string Name { get; set; }
|
||||
protected List<Node> Arguments { get; set; }
|
||||
public ILogger Logger { get; set; }
|
||||
public NodeLocation Location { get; set; }
|
||||
|
||||
public Node Call(Env env, IEnumerable<Node> arguments)
|
||||
{
|
||||
Arguments = arguments.ToList();
|
||||
|
||||
var node = Evaluate(env);
|
||||
node.Location = Location;
|
||||
return node;
|
||||
}
|
||||
|
||||
protected abstract Node Evaluate(Env env);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("function '{0}'", Name.ToLowerInvariant());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Warns that a function is not supported by less.js
|
||||
/// </summary>
|
||||
/// <param name="functionPattern">unsupported pattern function call e.g. alpha(color number)</param>
|
||||
protected void WarnNotSupportedByLessJS(string functionPattern)
|
||||
{
|
||||
WarnNotSupportedByLessJS(functionPattern, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Warns that a function is not supported by less.js
|
||||
/// </summary>
|
||||
/// <param name="functionPattern">unsupported pattern function call e.g. alpha(color number)</param>
|
||||
/// <param name="replacementPattern">Replacement pattern function call e.g. fadein(color, number)</param>
|
||||
protected void WarnNotSupportedByLessJS(string functionPattern, string replacementPattern)
|
||||
{
|
||||
WarnNotSupportedByLessJS(functionPattern, replacementPattern, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Warns that a function is not supported by less.js
|
||||
/// </summary>
|
||||
/// <param name="functionPattern">unsupported pattern function call e.g. alpha(color number)</param>
|
||||
/// <param name="replacementPattern">Replacement pattern function call e.g. fadein(color, number)</param>
|
||||
/// <param name="extraInfo">Extra information to put on the end.</param>
|
||||
protected void WarnNotSupportedByLessJS(string functionPattern, string replacementPattern, string extraInfo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(replacementPattern))
|
||||
{
|
||||
Logger.Info("{0} is not supported by less.js, so this will work but not compile with other less implementations.{1}", functionPattern, extraInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("{0} is not supported by less.js, so this will work but not compile with other less implementations." +
|
||||
" You may want to consider using {1} which does the same thing and is supported.{2}", functionPattern, replacementPattern, extraInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,220 @@
|
||||
using System.Threading;
|
||||
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Exceptions;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
using Color = Tree.Color;
|
||||
|
||||
/// <summary>
|
||||
/// Outputs <see cref="Url"/> node with gradient image implemented as described in RFC-2397 (The "data" URL scheme, http://tools.ietf.org/html/rfc2397)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Acepts color point definitions and outputs image data URL. Usage:
|
||||
/// <c>gradient(#color1, #color2[, position2][, #color3[, position3]]...)</c>
|
||||
/// Position is a zero-based offset of the color stop. If omitted, it is increased by 50 (px) or by the previous offset, if any.
|
||||
///
|
||||
/// Currently only vertical 1px width gradients with many color stops are supported.
|
||||
/// Image data URLs are not supported by IE7 and below. IE8 restricts the size of URL to 32k.
|
||||
/// See details here: http://en.wikipedia.org/wiki/Data_URI_scheme.
|
||||
/// <example>
|
||||
/// The following example shows how to render a gradient (.less file):
|
||||
/// <code>
|
||||
/// .ui-widget-header { background: @headerBgColor gradientImage(@headerBgColor, desaturate(lighten(@headerBgColor, 30), 30)) 50% 50% repeat-x; }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// </remarks>
|
||||
public class GradientImageFunction : Function
|
||||
{
|
||||
/*
|
||||
* TODO:
|
||||
* 1. Add URI length check for IE8: max 0x8000 - needs access to config and Context.Request.Browser
|
||||
* 2. Add fallback for IE7 and lower - dump the image to disk and refer it as ordinal url(...) - needs access to config and Context.Request.Browser + disk write permissions.
|
||||
* 3. Implement horisontal gradients (1px height)
|
||||
*
|
||||
* Open questions:
|
||||
* 1. PNG 32bpp - is it ok for all cases?
|
||||
*/
|
||||
|
||||
#region Nested classes
|
||||
private class ColorPoint
|
||||
{
|
||||
public ColorPoint(System.Drawing.Color color, int position)
|
||||
{
|
||||
Color = color;
|
||||
Position = position;
|
||||
}
|
||||
|
||||
public static string Stringify(IEnumerable<ColorPoint> points)
|
||||
{
|
||||
return points.Aggregate(
|
||||
"", (s, point) => string.Format("{0}{1}#{2:X2}{3:X2}{4:X2}{5:X2},{6}", s, s == "" ? "" : ",",
|
||||
point.Color.A, point.Color.R, point.Color.G, point.Color.B, point.Position));
|
||||
}
|
||||
|
||||
public System.Drawing.Color Color { get; private set; }
|
||||
public int Position { get; private set; }
|
||||
}
|
||||
|
||||
private class CacheItem
|
||||
{
|
||||
public readonly string _def;
|
||||
public readonly string _url;
|
||||
|
||||
public CacheItem(string def, string url)
|
||||
{
|
||||
_def = def;
|
||||
_url = url;
|
||||
}
|
||||
}
|
||||
#endregion Nested classes
|
||||
|
||||
#region Fields
|
||||
public const int DEFAULT_COLOR_OFFSET = 50;
|
||||
|
||||
private const int CACHE_LIMIT = 50;
|
||||
private static readonly ReaderWriterLockSlim _cacheLock = new ReaderWriterLockSlim();
|
||||
private static readonly List<CacheItem> _cache = new List<CacheItem>();
|
||||
#endregion Fields
|
||||
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
ColorPoint[] points = GetColorPoints();
|
||||
|
||||
WarnNotSupportedByLessJS("gradientImage(color, color[, position])");
|
||||
|
||||
string colorDefs = ColorPoint.Stringify(points);
|
||||
string imageUrl = GetFromCache(colorDefs);
|
||||
if (imageUrl == null)
|
||||
{
|
||||
imageUrl = "data:image/png;base64," + Convert.ToBase64String(GetImageData(points));
|
||||
AddToCache(colorDefs, imageUrl);
|
||||
}
|
||||
|
||||
return new Url(new TextNode(imageUrl));
|
||||
}
|
||||
|
||||
private byte[] GetImageData(ColorPoint[] points)
|
||||
{
|
||||
ColorPoint last = points.Last();
|
||||
int size = last.Position + 1;
|
||||
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
using (var bmp = new Bitmap(1, size, PixelFormat.Format32bppArgb))
|
||||
{
|
||||
using (Graphics g = Graphics.FromImage(bmp))
|
||||
{
|
||||
for (int i = 1; i < points.Length; i++)
|
||||
{
|
||||
var rect = new Rectangle(0, points[i - 1].Position, 1, points[i].Position);
|
||||
var brush = new LinearGradientBrush(
|
||||
rect,
|
||||
points[i - 1].Color,
|
||||
points[i].Color,
|
||||
LinearGradientMode.Vertical);
|
||||
g.FillRectangle(brush, rect);
|
||||
}
|
||||
|
||||
bmp.SetPixel(0, last.Position, last.Color);
|
||||
bmp.Save(ms, ImageFormat.Png);
|
||||
}
|
||||
}
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private ColorPoint[] GetColorPoints()
|
||||
{
|
||||
int argCount = Arguments.Count;
|
||||
Guard.ExpectMinArguments(2, argCount, this, Location);
|
||||
Guard.ExpectAllNodes<Color>(Arguments.Take(2), this, Location);
|
||||
var first = (Color) Arguments[0];
|
||||
var points = new List<ColorPoint>
|
||||
{
|
||||
new ColorPoint((System.Drawing.Color) first, 0)
|
||||
};
|
||||
|
||||
int prevPos = 0;
|
||||
int prevOffset = DEFAULT_COLOR_OFFSET;
|
||||
|
||||
for (int i = 1; i < argCount; i++)
|
||||
{
|
||||
Node arg = Arguments[i];
|
||||
Guard.ExpectNode<Color>(arg, this, Location);
|
||||
var color = arg as Color;
|
||||
int pos = prevPos + prevOffset;
|
||||
if (i < argCount - 1)
|
||||
{
|
||||
var numberArg = Arguments[i + 1] as Number;
|
||||
if (numberArg)
|
||||
{
|
||||
pos = Convert.ToInt32(numberArg.Value);
|
||||
if (pos <= prevPos)
|
||||
{
|
||||
throw new ParsingException(
|
||||
string.Format("Incrementing color point position expected, at least {0}, found {1}", prevPos + 1, numberArg.Value), Location);
|
||||
}
|
||||
prevOffset = pos - prevPos;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
points.Add(new ColorPoint((System.Drawing.Color) color, pos));
|
||||
prevPos = pos;
|
||||
}
|
||||
|
||||
return points.ToArray();
|
||||
}
|
||||
|
||||
private static string GetFromCache(string colorDefs)
|
||||
{
|
||||
_cacheLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
var cached = _cache.FirstOrDefault(item => item._def == colorDefs);
|
||||
return cached != null ? cached._url : null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cacheLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddToCache(string colorDefs, string imageUrl)
|
||||
{
|
||||
_cacheLock.EnterUpgradeableReadLock();
|
||||
try
|
||||
{
|
||||
if (_cache.All(item => item._def != colorDefs))
|
||||
{
|
||||
_cacheLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
if (_cache.Count >= CACHE_LIMIT)
|
||||
_cache.RemoveRange(0, CACHE_LIMIT / 2);
|
||||
var item = new CacheItem(colorDefs, imageUrl);
|
||||
_cache.Add(item);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cacheLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cacheLock.ExitUpgradeableReadLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System.Linq;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
|
||||
public class GreyscaleFunction : ColorFunctionBase
|
||||
{
|
||||
protected override Node Eval(Color color)
|
||||
{
|
||||
var grey = (color.RGB.Max() + color.RGB.Min())/2;
|
||||
|
||||
return new Color(grey, grey, grey);
|
||||
}
|
||||
}
|
||||
|
||||
public class GrayscaleFunction : GreyscaleFunction
|
||||
{
|
||||
protected override Node Eval(Color color)
|
||||
{
|
||||
WarnNotSupportedByLessJS("grayscale(color)", "greyscale(color)");
|
||||
return base.Eval(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/GreenFunction.cs
Executable file
25
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/GreenFunction.cs
Executable file
@ -0,0 +1,25 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
|
||||
public class GreenFunction : ColorFunctionBase
|
||||
{
|
||||
protected override Node Eval(Color color)
|
||||
{
|
||||
return new Number(color.G);
|
||||
}
|
||||
|
||||
protected override Node EditColor(Color color, Number number)
|
||||
{
|
||||
WarnNotSupportedByLessJS("green(color, number)");
|
||||
|
||||
var value = number.Value;
|
||||
|
||||
if (number.Unit == "%")
|
||||
value = (value*255)/100d;
|
||||
|
||||
return new Color(color.R, color.G + value, color.B);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
public class HardlightFunction : ColorMixFunction
|
||||
{
|
||||
protected override double Operate(double a, double b)
|
||||
{
|
||||
return b < 128 ? 2 * b * a / 255 : 255 - 2 * (255 - b) * (255 - a) / 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HexFunction.cs
Executable file
29
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HexFunction.cs
Executable file
@ -0,0 +1,29 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Exceptions;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
|
||||
public class HexFunction : NumberFunctionBase
|
||||
{
|
||||
protected override Node Eval(Env env, Number number, Node[] args)
|
||||
{
|
||||
WarnNotSupportedByLessJS("hex(number)");
|
||||
|
||||
if (!string.IsNullOrEmpty(number.Unit))
|
||||
throw new ParsingException(string.Format("Expected unitless number in function 'hex', found {0}", number.ToCSS(env)), number.Location);
|
||||
|
||||
number.Value = Clamp(number.Value, 255, 0);
|
||||
|
||||
return new TextNode(((int)number.Value).ToString("X2"));
|
||||
}
|
||||
|
||||
private static double Clamp(double value, double max, double min)
|
||||
{
|
||||
if (value < min) value = min;
|
||||
if (value > max) value = max;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public abstract class HslColorFunctionBase : ColorFunctionBase
|
||||
{
|
||||
protected override Node Eval(Color color)
|
||||
{
|
||||
var hsl = HslColor.FromRgbColor(color);
|
||||
|
||||
return EvalHsl(hsl);
|
||||
}
|
||||
|
||||
protected override Node EditColor(Color color, Number number)
|
||||
{
|
||||
var hsl = HslColor.FromRgbColor(color);
|
||||
|
||||
return EditHsl(hsl, number);
|
||||
}
|
||||
|
||||
protected abstract Node EvalHsl(HslColor color);
|
||||
|
||||
protected abstract Node EditHsl(HslColor color, Number number);
|
||||
}
|
||||
}
|
||||
19
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HslFunction.cs
Executable file
19
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HslFunction.cs
Executable file
@ -0,0 +1,19 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class HslFunction : HslaFunction
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectNumArguments(3, Arguments.Count, this, Location);
|
||||
|
||||
Arguments.Add(new Number(1d, ""));
|
||||
|
||||
return base.Evaluate(env);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HslaFunction.cs
Executable file
21
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HslaFunction.cs
Executable file
@ -0,0 +1,21 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System.Linq;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class HslaFunction : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectNumArguments(4, Arguments.Count, this, Location);
|
||||
Guard.ExpectAllNodes<Number>(Arguments, this, Location);
|
||||
|
||||
var args = Arguments.Cast<Number>().ToArray();
|
||||
|
||||
return new HslColor(args[0], args[1], args[2], args[3]).ToRgbColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HueFunction.cs
Executable file
24
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HueFunction.cs
Executable file
@ -0,0 +1,24 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class HueFunction : HslColorFunctionBase
|
||||
{
|
||||
protected override Node EvalHsl(HslColor color)
|
||||
{
|
||||
return color.GetHueInDegrees();
|
||||
}
|
||||
|
||||
protected override Node EditHsl(HslColor color, Number number)
|
||||
{
|
||||
WarnNotSupportedByLessJS("hue(color, number)");
|
||||
|
||||
color.Hue += number.Value/360d;
|
||||
return color.ToRgbColor();
|
||||
}
|
||||
}
|
||||
|
||||
public class SpinFunction : HueFunction { }
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
|
||||
public class IncrementFunction : NumberFunctionBase
|
||||
{
|
||||
protected override Node Eval(Env env, Number number, Node[] args)
|
||||
{
|
||||
return new Number(number.Value + 1, number.Unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
101
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/IsFunctions.cs
Executable file
101
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/IsFunctions.cs
Executable file
@ -0,0 +1,101 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using dotless.Core.Utils;
|
||||
using dotless.Core.Parser.Infrastructure.Nodes;
|
||||
using dotless.Core.Parser.Tree;
|
||||
|
||||
public abstract class IsFunction : Function
|
||||
{
|
||||
protected override Infrastructure.Nodes.Node Evaluate(Infrastructure.Env env)
|
||||
{
|
||||
Guard.ExpectNumArguments(1, Arguments.Count, this, Location);
|
||||
|
||||
return new Keyword(IsEvaluator(Arguments[0]) ? "true" : "false");
|
||||
}
|
||||
|
||||
protected abstract bool IsEvaluator(Node node);
|
||||
}
|
||||
|
||||
public abstract class IsTypeFunction<T> : IsFunction where T:Node
|
||||
{
|
||||
protected override bool IsEvaluator(Node node)
|
||||
{
|
||||
return node is T;
|
||||
}
|
||||
}
|
||||
|
||||
public class IsColorFunction : IsTypeFunction<Color>
|
||||
{
|
||||
}
|
||||
|
||||
public class IsNumber : IsTypeFunction<Number>
|
||||
{
|
||||
}
|
||||
|
||||
public class IsString : IsTypeFunction<Quoted>
|
||||
{
|
||||
}
|
||||
|
||||
public class IsKeyword : IsTypeFunction<Keyword>
|
||||
{
|
||||
}
|
||||
|
||||
public class IsUrl : IsTypeFunction<Url>
|
||||
{
|
||||
}
|
||||
|
||||
public abstract class IsDimensionUnitFunction : IsTypeFunction<Number>
|
||||
{
|
||||
protected abstract string Unit { get; }
|
||||
|
||||
protected override bool IsEvaluator(Node node)
|
||||
{
|
||||
bool isNumber = base.IsEvaluator(node);
|
||||
if (isNumber)
|
||||
{
|
||||
if (((Number)node).Unit == Unit)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class IsEm : IsDimensionUnitFunction
|
||||
{
|
||||
protected override string Unit
|
||||
{
|
||||
get
|
||||
{
|
||||
return "em";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class IsPercentage : IsDimensionUnitFunction
|
||||
{
|
||||
protected override string Unit
|
||||
{
|
||||
get
|
||||
{
|
||||
return "%";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class IsPixel : IsDimensionUnitFunction
|
||||
{
|
||||
protected override string Unit
|
||||
{
|
||||
get
|
||||
{
|
||||
return "px";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/LengthFunction.cs
Executable file
14
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/LengthFunction.cs
Executable file
@ -0,0 +1,14 @@
|
||||
using dotless.Core.Parser.Infrastructure;
|
||||
using dotless.Core.Parser.Infrastructure.Nodes;
|
||||
using dotless.Core.Parser.Tree;
|
||||
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
public class LengthFunction : ListFunctionBase
|
||||
{
|
||||
protected override Node Eval(Env env, Node[] list, Node[] args)
|
||||
{
|
||||
return new Number(list.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class LightenFunction : HslColorFunctionBase
|
||||
{
|
||||
protected override Node EvalHsl(HslColor color)
|
||||
{
|
||||
return color.GetLightness();
|
||||
}
|
||||
|
||||
protected override Node EditHsl(HslColor color, Number number)
|
||||
{
|
||||
color.Lightness += number.Value/100;
|
||||
return color.ToRgbColor();
|
||||
}
|
||||
}
|
||||
|
||||
public class DarkenFunction : LightenFunction
|
||||
{
|
||||
protected override Node EditHsl(HslColor color, Number number)
|
||||
{
|
||||
return base.EditHsl(color, -number);
|
||||
}
|
||||
}
|
||||
|
||||
public class LightnessFunction : LightenFunction
|
||||
{
|
||||
protected override Node EditHsl(HslColor color, Number number)
|
||||
{
|
||||
WarnNotSupportedByLessJS("lightness(color, number)", "lighten(color, number) or its opposite darken(color, number),");
|
||||
|
||||
return base.EditHsl(color, number);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using dotless.Core.Exceptions;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public abstract class ListFunctionBase : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectMinArguments(1, Arguments.Count, this, Location);
|
||||
Guard.ExpectNodeToBeOneOf<Expression, Value>(Arguments[0], this, Arguments[0].Location);
|
||||
|
||||
if(Arguments[0] is Expression)
|
||||
{
|
||||
var list = Arguments[0] as Expression;
|
||||
var args = Arguments.Skip(1).ToArray();
|
||||
return Eval(env, list.Value.ToArray(), args);
|
||||
}
|
||||
|
||||
if(Arguments[0] is Value)
|
||||
{
|
||||
var list = Arguments[0] as Value;
|
||||
var args = Arguments.Skip(1).ToArray();
|
||||
return Eval(env, list.Values.ToArray(), args);
|
||||
}
|
||||
|
||||
// We should never get here due to the type guard...
|
||||
throw new ParsingException(string.Format("First argument to the list function was a {0}", Arguments[0].GetType().Name.ToLowerInvariant()), Location);
|
||||
}
|
||||
|
||||
protected abstract Node Eval(Env env, Node[] list, Node[] args);
|
||||
}
|
||||
}
|
||||
77
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/MixFunction.cs
Executable file
77
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/MixFunction.cs
Executable file
@ -0,0 +1,77 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System.Linq;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class MixFunction : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectMinArguments(2, Arguments.Count, this, Location);
|
||||
Guard.ExpectMaxArguments(3, Arguments.Count, this, Location);
|
||||
Guard.ExpectAllNodes<Color>(Arguments.Take(2), this, Location);
|
||||
|
||||
double weight = 50;
|
||||
if (Arguments.Count == 3)
|
||||
{
|
||||
Guard.ExpectNode<Number>(Arguments[2], this, Location);
|
||||
|
||||
weight = ((Number)Arguments[2]).Value;
|
||||
}
|
||||
|
||||
var colors = Arguments.Take(2).Cast<Color>().ToArray();
|
||||
|
||||
return Mix(colors[0], colors[1], weight);
|
||||
}
|
||||
|
||||
protected Color Mix(Color color1, Color color2, double weight)
|
||||
{
|
||||
// Note: this algorithm taken from http://github.com/nex3/haml/blob/0e249c844f66bd0872ed68d99de22b774794e967/lib/sass/script/functions.rb
|
||||
|
||||
var p = weight / 100.0;
|
||||
var w = p * 2 - 1;
|
||||
var a = color1.Alpha - color2.Alpha;
|
||||
|
||||
var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
|
||||
var w2 = 1 - w1;
|
||||
|
||||
var rgb = color1.RGB.Select((x, i) => x * w1 + color2.RGB[i] * w2).ToArray();
|
||||
|
||||
var alpha = color1.Alpha * p + color2.Alpha * (1 - p);
|
||||
|
||||
var color = new Color(rgb[0], rgb[1], rgb[2], alpha);
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
public class TintFunction : MixFunction
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectNumArguments(2, Arguments.Count, this, Location);
|
||||
Guard.ExpectNode<Color>(Arguments[0], this, Location);
|
||||
Guard.ExpectNode<Number>(Arguments[1], this, Location);
|
||||
|
||||
double weight = ((Number)Arguments[1]).Value;
|
||||
|
||||
return Mix(new Color(255, 255, 255),(Color)Arguments[0], weight);
|
||||
}
|
||||
}
|
||||
|
||||
public class ShadeFunction : MixFunction
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectNumArguments(2, Arguments.Count, this, Location);
|
||||
Guard.ExpectNode<Color>(Arguments[0], this, Location);
|
||||
Guard.ExpectNode<Number>(Arguments[1], this, Location);
|
||||
|
||||
double weight = ((Number)Arguments[1]).Value;
|
||||
|
||||
return Mix(new Color(0, 0, 0), (Color)Arguments[0], weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
public class MultiplyFunction : ColorMixFunction
|
||||
{
|
||||
protected override double Operate(double a, double b)
|
||||
{
|
||||
return a * b / 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System;
|
||||
public class NegationFunction : ColorMixFunction
|
||||
{
|
||||
protected override double Operate(double a, double b)
|
||||
{
|
||||
return 255 - Math.Abs(255 - b - a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System.Linq;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public abstract class NumberFunctionBase : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectMinArguments(1, Arguments.Count, this, Location);
|
||||
Guard.ExpectNode<Number>(Arguments[0], this, Arguments[0].Location);
|
||||
|
||||
var number = Arguments[0] as Number;
|
||||
var args = Arguments.Skip(1).ToArray();
|
||||
|
||||
return Eval(env, number, args);
|
||||
}
|
||||
|
||||
protected abstract Node Eval(Env env, Number number, Node[] args);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
public class OverlayFunction : ColorMixFunction
|
||||
{
|
||||
protected override double Operate(double a, double b)
|
||||
{
|
||||
return a < 128 ? 2 * a * b / 255 : 255 - 2 * (255 - a) * (255 - b) / 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Exceptions;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
|
||||
public class PercentageFunction : NumberFunctionBase
|
||||
{
|
||||
protected override Node Eval(Env env, Number number, Node[] args)
|
||||
{
|
||||
return new Number(number.Value * 100, "%");
|
||||
}
|
||||
}
|
||||
}
|
||||
27
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/PowFunction.cs
Executable file
27
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/PowFunction.cs
Executable file
@ -0,0 +1,27 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class PowFunction : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectMinArguments(2, Arguments.Count, this, Location);
|
||||
Guard.ExpectMaxArguments(2, Arguments.Count, this, Location);
|
||||
Guard.ExpectAllNodes<Number>(Arguments, this, Location);
|
||||
|
||||
WarnNotSupportedByLessJS("pow(number, number)");
|
||||
|
||||
var first = Arguments.Cast<Number>().First();
|
||||
var second = Arguments.Cast<Number>().ElementAt(1);
|
||||
var value = Math.Pow(first.Value, second.Value);
|
||||
|
||||
return new Number(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RedFunction.cs
Executable file
25
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RedFunction.cs
Executable file
@ -0,0 +1,25 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
|
||||
public class RedFunction : ColorFunctionBase
|
||||
{
|
||||
protected override Node Eval(Color color)
|
||||
{
|
||||
return new Number(color.RGB[0]);
|
||||
}
|
||||
|
||||
protected override Node EditColor(Color color, Number number)
|
||||
{
|
||||
WarnNotSupportedByLessJS("red(color, number)");
|
||||
|
||||
var value = number.Value;
|
||||
|
||||
if (number.Unit == "%")
|
||||
value = (value*255)/100d;
|
||||
|
||||
return new Color(color.R + value, color.G, color.B);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RgbFunction.cs
Executable file
19
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RgbFunction.cs
Executable file
@ -0,0 +1,19 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class RgbFunction : RgbaFunction
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectNumArguments(3, Arguments.Count, this, Location);
|
||||
|
||||
Arguments.Add(new Number(1d, ""));
|
||||
|
||||
return base.Evaluate(env);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RgbaFunction.cs
Executable file
30
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RgbaFunction.cs
Executable file
@ -0,0 +1,30 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System.Linq;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class RgbaFunction : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
if (Arguments.Count == 2)
|
||||
{
|
||||
var color = Guard.ExpectNode<Color>(Arguments[0], this, Location);
|
||||
var number = Guard.ExpectNode<Number>(Arguments[1], this, Location);
|
||||
|
||||
return new Color(color.RGB, number.Value);
|
||||
}
|
||||
|
||||
Guard.ExpectNumArguments(4, Arguments.Count, this, Location);
|
||||
var args = Guard.ExpectAllNodes<Number>(Arguments, this, Location);
|
||||
|
||||
var rgb = args.Take(3).Select(n => n.ToNumber(255.0)).ToArray();
|
||||
var alpha = args[3].ToNumber(1.0);
|
||||
|
||||
return new Color(rgb, alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RoundFunction.cs
Executable file
24
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RoundFunction.cs
Executable file
@ -0,0 +1,24 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using dotless.Core.Utils;
|
||||
|
||||
public class RoundFunction : NumberFunctionBase
|
||||
{
|
||||
protected override Node Eval(Env env, Number number, Node[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
return new Number(Math.Round(number.Value, MidpointRounding.AwayFromZero), number.Unit);
|
||||
}
|
||||
else
|
||||
{
|
||||
Guard.ExpectNode<Number>(args[0], this, args[0].Location);
|
||||
return new Number(Math.Round(number.Value, (int)((Number)args[0]).Value, MidpointRounding.AwayFromZero), number.Unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class SaturateFunction : HslColorFunctionBase
|
||||
{
|
||||
protected override Node EvalHsl(HslColor color)
|
||||
{
|
||||
return color.GetSaturation();
|
||||
}
|
||||
|
||||
protected override Node EditHsl(HslColor color, Number number)
|
||||
{
|
||||
color.Saturation += number.Value/100;
|
||||
return color.ToRgbColor();
|
||||
}
|
||||
}
|
||||
|
||||
public class DesaturateFunction : SaturateFunction
|
||||
{
|
||||
protected override Node EditHsl(HslColor color, Number number)
|
||||
{
|
||||
return base.EditHsl(color, -number);
|
||||
}
|
||||
}
|
||||
|
||||
public class SaturationFunction : SaturateFunction
|
||||
{
|
||||
protected override Node EditHsl(HslColor color, Number number)
|
||||
{
|
||||
WarnNotSupportedByLessJS("saturation(color, number)", "saturate(color, number) or its opposite desaturate(color, number),");
|
||||
|
||||
return base.EditHsl(color, number);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
10
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ScreenFunction.cs
Executable file
10
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ScreenFunction.cs
Executable file
@ -0,0 +1,10 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
public class ScreenFunction : ColorMixFunction
|
||||
{
|
||||
protected override double Operate(double a, double b)
|
||||
{
|
||||
return 255 - (255 - a) * (255 - b) / 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
public class SoftlightFunction : ColorMixFunction
|
||||
{
|
||||
protected override double Operate(double a, double b)
|
||||
{
|
||||
double t = b * a / 255;
|
||||
return t + a * (255 - (255 - a) * (255 - b) / 255 - t) / 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class EFunction : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectMaxArguments(1, Arguments.Count, this, Location);
|
||||
|
||||
WarnNotSupportedByLessJS("e(string)", @"~""""");
|
||||
|
||||
if (Arguments.Count == 0)
|
||||
return new TextNode("");
|
||||
|
||||
var str = Arguments[0];
|
||||
if (str is Quoted)
|
||||
return new TextNode((str as Quoted).UnescapeContents());
|
||||
|
||||
return new TextNode(str.ToCSS(env));
|
||||
}
|
||||
}
|
||||
|
||||
public class CFormatString : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
WarnNotSupportedByLessJS("%(string, args...)", @"~"""" and string interpolation");
|
||||
|
||||
if (Arguments.Count == 0)
|
||||
return new Quoted("", false);
|
||||
|
||||
Func<Node, string> stringValue = n => n is Quoted ? ((Quoted)n).Value : n.ToCSS(env);
|
||||
|
||||
var str = stringValue(Arguments[0]);
|
||||
|
||||
var args = Arguments.Skip(1).ToList();
|
||||
var i = 0;
|
||||
|
||||
MatchEvaluator replacement = m =>
|
||||
{
|
||||
var value = (m.Value == "%s") ?
|
||||
stringValue(args[i++]) :
|
||||
args[i++].ToCSS(env);
|
||||
|
||||
return char.IsUpper(m.Value[1]) ?
|
||||
Uri.EscapeDataString(value) :
|
||||
value;
|
||||
};
|
||||
|
||||
str = Regex.Replace(str, "%[sda]", replacement, RegexOptions.IgnoreCase);
|
||||
|
||||
var quote = Arguments[0] is Quoted ? (Arguments[0] as Quoted).Quote : null;
|
||||
|
||||
return new Quoted(str, quote);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/UnitFunction.cs
Executable file
33
dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/UnitFunction.cs
Executable file
@ -0,0 +1,33 @@
|
||||
namespace dotless.Core.Parser.Functions
|
||||
{
|
||||
using Infrastructure;
|
||||
using Infrastructure.Nodes;
|
||||
using Tree;
|
||||
using Utils;
|
||||
|
||||
public class UnitFunction : Function
|
||||
{
|
||||
protected override Node Evaluate(Env env)
|
||||
{
|
||||
Guard.ExpectMaxArguments(2, Arguments.Count, this, Location);
|
||||
Guard.ExpectNode<Number>(Arguments[0], this, Location);
|
||||
|
||||
var number = Arguments[0] as Number;
|
||||
|
||||
var unit = string.Empty;
|
||||
if (Arguments.Count == 2)
|
||||
{
|
||||
if (Arguments[1] is Keyword)
|
||||
{
|
||||
unit = ((Keyword)Arguments[1]).Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
unit = Arguments[1].ToCSS(env);
|
||||
}
|
||||
}
|
||||
|
||||
return new Number(number.Value, unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Closure.cs
Executable file
11
dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Closure.cs
Executable file
@ -0,0 +1,11 @@
|
||||
namespace dotless.Core.Parser.Infrastructure
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Tree;
|
||||
|
||||
public class Closure
|
||||
{
|
||||
public Ruleset Ruleset { get; set; }
|
||||
public List<Ruleset> Context { get; set; }
|
||||
}
|
||||
}
|
||||
263
dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Context.cs
Executable file
263
dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Context.cs
Executable file
@ -0,0 +1,263 @@
|
||||
namespace dotless.Core.Parser.Infrastructure
|
||||
{
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Tree;
|
||||
using Utils;
|
||||
using dotless.Core.Parser.Infrastructure.Nodes;
|
||||
|
||||
public class Context : IEnumerable<IEnumerable<Selector>>
|
||||
{
|
||||
private List<List<Selector>> Paths { get; set; }
|
||||
|
||||
public Context()
|
||||
{
|
||||
Paths = new List<List<Selector>>();
|
||||
}
|
||||
|
||||
public Context Clone()
|
||||
{
|
||||
var newPathList = new List<List<Selector>>();
|
||||
return new Context {Paths = newPathList};
|
||||
}
|
||||
|
||||
public void AppendSelectors(Context context, IEnumerable<Selector> selectors)
|
||||
{
|
||||
foreach (var selector in selectors)
|
||||
{
|
||||
AppendSelector(context, selector);
|
||||
}
|
||||
}
|
||||
|
||||
private void AppendSelector(Context context, Selector selector)
|
||||
{
|
||||
if (!selector.Elements.Any(e => e.Value == "&"))
|
||||
{
|
||||
if (context != null && context.Paths.Count > 0)
|
||||
{
|
||||
Paths.AddRange(context.Paths.Select(path => path.Concat(new[] { selector }).ToList()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Paths.Add(new List<Selector>() { selector });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// The paths are List<List<Selector>>
|
||||
// The first list is a list of comma seperated selectors
|
||||
// The inner list is a list of inheritance seperated selectors
|
||||
// e.g.
|
||||
// .a, .b {
|
||||
// .c {
|
||||
// }
|
||||
// }
|
||||
// == {{.a} {.c}} {{.b} {.c}}
|
||||
//
|
||||
|
||||
// the elements from the current selector so far
|
||||
var currentElements = new NodeList<Element>();
|
||||
// the current list of new selectors to add to the path.
|
||||
// We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
|
||||
// by the parents
|
||||
var newSelectors = new List<List<Selector>>() { new List<Selector>() };
|
||||
|
||||
foreach (Element el in selector.Elements)
|
||||
{
|
||||
// non parent reference elements just get added
|
||||
if (el.Value != "&")
|
||||
{
|
||||
currentElements.Add(el);
|
||||
} else
|
||||
{
|
||||
// the new list of selectors to add
|
||||
var selectorsMultiplied = new List<List<Selector>>();
|
||||
|
||||
// merge the current list of non parent selector elements
|
||||
// on to the current list of selectors to add
|
||||
if (currentElements.Count > 0)
|
||||
{
|
||||
MergeElementsOnToSelectors(currentElements, newSelectors);
|
||||
}
|
||||
|
||||
// loop through our current selectors
|
||||
foreach(List<Selector> sel in newSelectors)
|
||||
{
|
||||
// if we don't have any parent paths, the & might be in a mixin so that it can be used
|
||||
// whether there are parents or not
|
||||
if (context.Paths.Count == 0)
|
||||
{
|
||||
// the combinator used on el should now be applied to the next element instead so that
|
||||
// it is not lost
|
||||
if (sel.Count > 0)
|
||||
{
|
||||
sel[0].Elements = new NodeList<Element>(sel[0].Elements);
|
||||
sel[0].Elements.Add(new Element(el.Combinator, ""));
|
||||
}
|
||||
selectorsMultiplied.Add(sel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// and the parent selectors
|
||||
foreach (List<Selector> parentSel in context.Paths)
|
||||
{
|
||||
// We need to put the current selectors
|
||||
// then join the last selector's elements on to the parents selectors
|
||||
|
||||
// our new selector path
|
||||
List<Selector> newSelectorPath = new List<Selector>();
|
||||
// selectors from the parent after the join
|
||||
List<Selector> afterParentJoin = new List<Selector>();
|
||||
Selector newJoinedSelector;
|
||||
bool newJoinedSelectorEmpty = true;
|
||||
|
||||
//construct the joined selector - if & is the first thing this will be empty,
|
||||
// if not newJoinedSelector will be the last set of elements in the selector
|
||||
if (sel.Count > 0)
|
||||
{
|
||||
newJoinedSelector = new Selector(new NodeList<Element>(sel[sel.Count - 1].Elements));
|
||||
newSelectorPath.AddRange(sel.Take(sel.Count - 1));
|
||||
newJoinedSelectorEmpty = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
newJoinedSelector = new Selector(new NodeList<Element>());
|
||||
}
|
||||
|
||||
//put together the parent selectors after the join
|
||||
if (parentSel.Count > 1)
|
||||
{
|
||||
afterParentJoin.AddRange(parentSel.Skip(1));
|
||||
}
|
||||
|
||||
if (parentSel.Count > 0)
|
||||
{
|
||||
newJoinedSelectorEmpty = false;
|
||||
|
||||
// join the elements so far with the first part of the parent
|
||||
if (parentSel[0].Elements[0].Value == null)
|
||||
{
|
||||
newJoinedSelector.Elements.Add(new Element(el.Combinator, parentSel[0].Elements[0].NodeValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
newJoinedSelector.Elements.Add(new Element(el.Combinator, parentSel[0].Elements[0].Value));
|
||||
}
|
||||
newJoinedSelector.Elements.AddRange(parentSel[0].Elements.Skip(1));
|
||||
}
|
||||
|
||||
if (!newJoinedSelectorEmpty)
|
||||
{
|
||||
// now add the joined selector
|
||||
newSelectorPath.Add(newJoinedSelector);
|
||||
}
|
||||
|
||||
// and the rest of the parent
|
||||
newSelectorPath.AddRange(afterParentJoin);
|
||||
|
||||
// add that to our new set of selectors
|
||||
selectorsMultiplied.Add(newSelectorPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// our new selectors has been multiplied, so reset the state
|
||||
newSelectors = selectorsMultiplied;
|
||||
currentElements = new NodeList<Element>();
|
||||
}
|
||||
}
|
||||
|
||||
// if we have any elements left over (e.g. .a& .b == .b)
|
||||
// add them on to all the current selectors
|
||||
if (currentElements.Count > 0)
|
||||
{
|
||||
MergeElementsOnToSelectors(currentElements, newSelectors);
|
||||
}
|
||||
|
||||
Paths.AddRange(newSelectors.Select(sel => sel.Select(MergeJoinedElements).ToList()));
|
||||
}
|
||||
|
||||
private void MergeElementsOnToSelectors(NodeList<Element> elements, List<List<Selector>> selectors)
|
||||
{
|
||||
if (selectors.Count == 0)
|
||||
{
|
||||
selectors.Add(new List<Selector>() { new Selector(elements) });
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (List<Selector> sel in selectors)
|
||||
{
|
||||
// if the previous thing in sel is a parent this needs to join on to it?
|
||||
if (sel.Count > 0)
|
||||
{
|
||||
sel[sel.Count - 1] = new Selector(sel[sel.Count - 1].Elements.Concat(elements));
|
||||
}
|
||||
else
|
||||
{
|
||||
sel.Add(new Selector(elements));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly char[] LeaveUnmerged = {'.', '#', ':'};
|
||||
|
||||
private Selector MergeJoinedElements(Selector selector) {
|
||||
var elements = selector.Elements
|
||||
.Select(e => e.Clone())
|
||||
.ToList();
|
||||
|
||||
for (int i = 1; i < elements.Count; i++) {
|
||||
var previousElement = elements[i - 1];
|
||||
var currentElement = elements[i];
|
||||
|
||||
if (string.IsNullOrEmpty(currentElement.Value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (LeaveUnmerged.Contains(currentElement.Value[0])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentElement.Combinator.Value != "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
elements[i - 1] = new Element(previousElement.Combinator, previousElement.Value += currentElement.Value);
|
||||
elements.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
|
||||
return new Selector(elements);
|
||||
}
|
||||
|
||||
public void AppendCSS(Env env) {
|
||||
var selectors = Paths
|
||||
.Where(p => p.Any(s => !s.IsReference))
|
||||
.Select(path => path.Select(p => p.ToCSS(env)).JoinStrings("").Trim())
|
||||
.Distinct();
|
||||
|
||||
env.Output.AppendMany(selectors, env.Compress ? "," : ",\n");
|
||||
}
|
||||
|
||||
public string ToCss(Env env)
|
||||
{
|
||||
return string.Join(env.Compress ? "," : ",\n",Paths.Select(path => path.Select(p => p.ToCSS(env)).JoinStrings("").Trim()).ToArray());
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return Paths.Count; }
|
||||
}
|
||||
|
||||
public IEnumerator<IEnumerable<Selector>> GetEnumerator()
|
||||
{
|
||||
return Paths.Cast<IEnumerable<Selector>>().GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,198 @@
|
||||
namespace dotless.Core.Parser.Infrastructure
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Importers;
|
||||
using Nodes;
|
||||
using Tree;
|
||||
|
||||
public class DefaultNodeProvider : INodeProvider
|
||||
{
|
||||
public Element Element(Combinator combinator, Node value, NodeLocation location)
|
||||
{
|
||||
return new Element(combinator, value) { Location = location };
|
||||
}
|
||||
|
||||
public Combinator Combinator(string value, NodeLocation location)
|
||||
{
|
||||
return new Combinator(value) { Location = location };
|
||||
}
|
||||
|
||||
public Selector Selector(NodeList<Element> elements, NodeLocation location)
|
||||
{
|
||||
return new Selector(elements) { Location = location };
|
||||
}
|
||||
|
||||
public Rule Rule(string name, Node value, NodeLocation location)
|
||||
{
|
||||
return new Rule(name, value) { Location = location };
|
||||
}
|
||||
|
||||
public Rule Rule(string name, Node value, bool variadic, NodeLocation location)
|
||||
{
|
||||
return new Rule(name, value, variadic) { Location = location };
|
||||
}
|
||||
|
||||
public Ruleset Ruleset(NodeList<Selector> selectors, NodeList rules, NodeLocation location)
|
||||
{
|
||||
return new Ruleset(selectors, rules) { Location = location };
|
||||
}
|
||||
|
||||
public CssFunction CssFunction(string name, Node value, NodeLocation location)
|
||||
{
|
||||
return new CssFunction() { Name = name, Value = value, Location = location };
|
||||
}
|
||||
|
||||
public Alpha Alpha(Node value, NodeLocation location)
|
||||
{
|
||||
return new Alpha(value) { Location = location };
|
||||
}
|
||||
|
||||
public Call Call(string name, NodeList<Node> arguments, NodeLocation location)
|
||||
{
|
||||
return new Call(name, arguments) { Location = location };
|
||||
}
|
||||
|
||||
public Color Color(string rgb, NodeLocation location)
|
||||
{
|
||||
var color = Tree.Color.FromHex(rgb);
|
||||
color.Location = location;
|
||||
return color;
|
||||
}
|
||||
|
||||
public Keyword Keyword(string value, NodeLocation location)
|
||||
{
|
||||
return new Keyword(value) { Location = location };
|
||||
}
|
||||
|
||||
public Number Number(string value, string unit, NodeLocation location)
|
||||
{
|
||||
return new Number(value, unit) { Location = location };
|
||||
}
|
||||
|
||||
public Shorthand Shorthand(Node first, Node second, NodeLocation location)
|
||||
{
|
||||
return new Shorthand(first, second) { Location = location };
|
||||
}
|
||||
|
||||
public Variable Variable(string name, NodeLocation location)
|
||||
{
|
||||
return new Variable(name) { Location = location };
|
||||
}
|
||||
|
||||
public Url Url(Node value, IImporter importer, NodeLocation location)
|
||||
{
|
||||
return new Url(value, importer) { Location = location };
|
||||
}
|
||||
|
||||
public Script Script(string script, NodeLocation location)
|
||||
{
|
||||
return new Script(script) { Location = location };
|
||||
}
|
||||
|
||||
public GuardedRuleset GuardedRuleset(NodeList<Selector> selectors, NodeList rules, Condition condition, NodeLocation location)
|
||||
{
|
||||
return new GuardedRuleset(selectors, rules, condition) { Location = location };
|
||||
}
|
||||
|
||||
public MixinCall MixinCall(NodeList<Element> elements, List<NamedArgument> arguments, bool important, NodeLocation location)
|
||||
{
|
||||
return new MixinCall(elements, arguments, important) { Location = location };
|
||||
}
|
||||
|
||||
public MixinDefinition MixinDefinition(string name, NodeList<Rule> parameters, NodeList rules, Condition condition, bool variadic, NodeLocation location)
|
||||
{
|
||||
return new MixinDefinition(name, parameters, rules, condition, variadic) { Location = location };
|
||||
}
|
||||
|
||||
public Import Import(Url path, Value features, ImportOptions option, NodeLocation location)
|
||||
{
|
||||
return new Import(path, features, option) { Location = location };
|
||||
}
|
||||
|
||||
public Import Import(Quoted path, Value features, ImportOptions option, NodeLocation location)
|
||||
{
|
||||
return new Import(path, features, option) { Location = location };
|
||||
}
|
||||
|
||||
public Directive Directive(string name, string identifier, NodeList rules, NodeLocation location)
|
||||
{
|
||||
return new Directive(name, identifier, rules) { Location = location };
|
||||
}
|
||||
|
||||
public Media Media(NodeList rules, Value features, NodeLocation location)
|
||||
{
|
||||
return new Media(features, rules) { Location = location };
|
||||
}
|
||||
|
||||
public KeyFrame KeyFrame(NodeList identifier, NodeList rules, NodeLocation location)
|
||||
{
|
||||
return new KeyFrame(identifier, rules) { Location = location };
|
||||
}
|
||||
|
||||
public Directive Directive(string name, Node value, NodeLocation location)
|
||||
{
|
||||
return new Directive(name, value) { Location = location };
|
||||
}
|
||||
|
||||
public Expression Expression(NodeList expression, NodeLocation location)
|
||||
{
|
||||
return new Expression(expression) { Location = location };
|
||||
}
|
||||
|
||||
public Value Value(IEnumerable<Node> values, string important, NodeLocation location)
|
||||
{
|
||||
return new Value(values, important) { Location = location };
|
||||
}
|
||||
|
||||
public Operation Operation(string operation, Node left, Node right, NodeLocation location)
|
||||
{
|
||||
return new Operation(operation, left, right) { Location = location };
|
||||
}
|
||||
|
||||
public Assignment Assignment(string key, Node value, NodeLocation location)
|
||||
{
|
||||
return new Assignment(key, value) {Location = location};
|
||||
}
|
||||
|
||||
public Comment Comment(string value, NodeLocation location)
|
||||
{
|
||||
return new Comment(value) { Location = location };
|
||||
}
|
||||
|
||||
public TextNode TextNode(string contents, NodeLocation location)
|
||||
{
|
||||
return new TextNode(contents) { Location = location };
|
||||
}
|
||||
|
||||
public Quoted Quoted(string value, string contents, bool escaped, NodeLocation location)
|
||||
{
|
||||
return new Quoted(value, contents, escaped) { Location = location };
|
||||
}
|
||||
|
||||
public Extend Extend(List<Selector> exact, List<Selector> partial, NodeLocation location)
|
||||
{
|
||||
return new Extend(exact,partial) { Location = location };
|
||||
}
|
||||
|
||||
public Node Attribute(Node key, Node op, Node val, NodeLocation location)
|
||||
{
|
||||
return new Attribute(key, op, val) { Location = location };
|
||||
}
|
||||
|
||||
public Paren Paren(Node value, NodeLocation location)
|
||||
{
|
||||
return new Paren(value) { Location = location };
|
||||
}
|
||||
|
||||
public Condition Condition(Node left, string operation, Node right, bool negate, NodeLocation location)
|
||||
{
|
||||
return new Condition(left, operation, right, negate) { Location = location };
|
||||
}
|
||||
#if CSS3EXPERIMENTAL
|
||||
public RepeatEntity RepeatEntity(Node value, Node repeatCount, int index)
|
||||
{
|
||||
return new RepeatEntity(value, repeatCount) { Location = location };
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
446
dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Env.cs
Executable file
446
dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Env.cs
Executable file
@ -0,0 +1,446 @@
|
||||
using dotless.Core.Utils;
|
||||
|
||||
namespace dotless.Core.Parser.Infrastructure
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using Functions;
|
||||
using Nodes;
|
||||
using Plugins;
|
||||
using Tree;
|
||||
using dotless.Core.Loggers;
|
||||
|
||||
public class Env
|
||||
{
|
||||
private Dictionary<string, Type> _functionTypes;
|
||||
private readonly List<IPlugin> _plugins;
|
||||
private readonly List<Extender> _extensions;
|
||||
|
||||
public Stack<Ruleset> Frames { get; protected set; }
|
||||
public bool Compress { get; set; }
|
||||
public bool Debug { get; set; }
|
||||
public Node Rule { get; set; }
|
||||
public ILogger Logger { get; set; }
|
||||
public Output Output { get; private set; }
|
||||
public Stack<Media> MediaPath { get; private set; }
|
||||
public List<Media> MediaBlocks { get; private set; }
|
||||
[Obsolete("The Variable Redefines feature has been removed to align with less.js")]
|
||||
public bool DisableVariableRedefines { get; set; }
|
||||
[Obsolete("The Color Compression feature has been removed to align with less.js")]
|
||||
public bool DisableColorCompression { get; set; }
|
||||
public bool KeepFirstSpecialComment { get; set; }
|
||||
public bool IsFirstSpecialCommentOutput { get; set; }
|
||||
public Parser Parser { get; set; }
|
||||
|
||||
public Env() : this(new Parser())
|
||||
{
|
||||
}
|
||||
|
||||
public Env(Parser parser) : this(parser, null, null)
|
||||
{
|
||||
}
|
||||
|
||||
protected Env(Parser parser, Stack<Ruleset> frames, Dictionary<string, Type> functions) : this(frames, functions) {
|
||||
Parser = parser;
|
||||
}
|
||||
|
||||
protected Env(Stack<Ruleset> frames, Dictionary<string, Type> functions) {
|
||||
Frames = frames ?? new Stack<Ruleset>();
|
||||
Output = new Output(this);
|
||||
MediaPath = new Stack<Media>();
|
||||
MediaBlocks = new List<Media>();
|
||||
Logger = new NullLogger(LogLevel.Info);
|
||||
|
||||
_plugins = new List<IPlugin>();
|
||||
_functionTypes = functions ?? new Dictionary<string, Type>();
|
||||
_extensions = new List<Extender>();
|
||||
ExtendMediaScope = new Stack<Media>();
|
||||
|
||||
if (_functionTypes.Count == 0)
|
||||
AddCoreFunctions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Env variable for the purposes of scope
|
||||
/// </summary>
|
||||
[Obsolete("Argument is ignored as of version 1.4.3.0. Use the parameterless overload of CreateChildEnv instead.", false)]
|
||||
public virtual Env CreateChildEnv(Stack<Ruleset> ruleset)
|
||||
{
|
||||
return CreateChildEnv();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Env variable for the purposes of scope
|
||||
/// </summary>
|
||||
public virtual Env CreateChildEnv()
|
||||
{
|
||||
return new Env(null, _functionTypes)
|
||||
{
|
||||
Parser = Parser,
|
||||
Parent = this,
|
||||
Debug = Debug,
|
||||
Compress = Compress,
|
||||
DisableVariableRedefines = DisableVariableRedefines
|
||||
};
|
||||
}
|
||||
|
||||
private Env Parent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Env variable for the purposes of scope
|
||||
/// </summary>
|
||||
public virtual Env CreateVariableEvaluationEnv(string variableName) {
|
||||
var env = CreateChildEnv();
|
||||
env.EvaluatingVariable = variableName;
|
||||
return env;
|
||||
}
|
||||
|
||||
private string EvaluatingVariable { get; set; }
|
||||
|
||||
public bool IsEvaluatingVariable(string variableName) {
|
||||
if (string.Equals(variableName, EvaluatingVariable, StringComparison.InvariantCulture)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Parent != null) {
|
||||
return Parent.IsEvaluatingVariable(variableName);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual Env CreateChildEnvWithClosure(Closure closure) {
|
||||
var env = CreateChildEnv();
|
||||
env.Rule = Rule;
|
||||
env.ClosureEnvironment = new Env(new Stack<Ruleset>(closure.Context), this._functionTypes);
|
||||
return env;
|
||||
}
|
||||
|
||||
private Env ClosureEnvironment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a plugin to this Env
|
||||
/// </summary>
|
||||
public void AddPlugin(IPlugin plugin)
|
||||
{
|
||||
if (plugin == null) throw new ArgumentNullException("plugin");
|
||||
|
||||
_plugins.Add(plugin);
|
||||
|
||||
IFunctionPlugin functionPlugin = plugin as IFunctionPlugin;
|
||||
if (functionPlugin != null)
|
||||
{
|
||||
foreach(KeyValuePair<string, Type> function in functionPlugin.GetFunctions())
|
||||
{
|
||||
string functionName = function.Key.ToLowerInvariant();
|
||||
|
||||
if (_functionTypes.ContainsKey(functionName))
|
||||
{
|
||||
string message = string.Format("Function '{0}' already exists in environment but is added by plugin {1}",
|
||||
functionName, plugin.GetName());
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
AddFunction(functionName, function.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All the visitor plugins to use
|
||||
/// </summary>
|
||||
public IEnumerable<IVisitorPlugin> VisitorPlugins
|
||||
{
|
||||
get
|
||||
{
|
||||
return _plugins.OfType<IVisitorPlugin>();
|
||||
}
|
||||
}
|
||||
|
||||
//Keep track of media scoping for extenders
|
||||
public Stack<Media> ExtendMediaScope { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the comment should be silent
|
||||
/// </summary>
|
||||
/// <param name="isDoubleStarComment"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsCommentSilent(bool isValidCss, bool isCssHack, bool isSpecialComment)
|
||||
{
|
||||
if (!isValidCss)
|
||||
return true;
|
||||
|
||||
if (isCssHack)
|
||||
return false;
|
||||
|
||||
if (Compress && KeepFirstSpecialComment && !IsFirstSpecialCommentOutput && isSpecialComment)
|
||||
{
|
||||
IsFirstSpecialCommentOutput = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return Compress;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the first scoped variable with this name
|
||||
/// </summary>
|
||||
public Rule FindVariable(string name)
|
||||
{
|
||||
return FindVariable(name, Rule);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the first scoped variable matching the name, using Rule as the current rule to work backwards from
|
||||
/// </summary>
|
||||
public Rule FindVariable(string name, Node rule)
|
||||
{
|
||||
var previousNode = rule;
|
||||
foreach (var frame in Frames)
|
||||
{
|
||||
var v = frame.Variable(name, null);
|
||||
if (v)
|
||||
return v;
|
||||
previousNode = frame;
|
||||
}
|
||||
|
||||
Rule result = null;
|
||||
if (Parent != null) {
|
||||
result = Parent.FindVariable(name, rule);
|
||||
}
|
||||
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (ClosureEnvironment != null) {
|
||||
return ClosureEnvironment.FindVariable(name, rule);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[Obsolete("This method will be removed in a future release.", false)]
|
||||
public IEnumerable<Closure> FindRulesets<TRuleset>(Selector selector) where TRuleset : Ruleset
|
||||
{
|
||||
return FindRulesets(selector).Where(c => c.Ruleset is TRuleset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the first Ruleset matching the selector argument that inherits from or is of type TRuleset (pass this as Ruleset if
|
||||
/// you are trying to find ANY Ruleset that matches the selector)
|
||||
/// </summary>
|
||||
public IEnumerable<Closure> FindRulesets(Selector selector)
|
||||
{
|
||||
var matchingRuleSets = Frames
|
||||
.Reverse()
|
||||
.SelectMany(frame => frame.Find<Ruleset>(this, selector, null))
|
||||
.Where(matchedClosure => {
|
||||
if (!Frames.Any(frame => frame.IsEqualOrClonedFrom(matchedClosure.Ruleset)))
|
||||
return true;
|
||||
|
||||
var mixinDef = matchedClosure.Ruleset as MixinDefinition;
|
||||
if (mixinDef != null)
|
||||
return mixinDef.Condition != null;
|
||||
|
||||
return false;
|
||||
}).ToList();
|
||||
|
||||
if (matchingRuleSets.Any())
|
||||
{
|
||||
return matchingRuleSets;
|
||||
}
|
||||
|
||||
if (Parent != null)
|
||||
{
|
||||
var parentRulesets = Parent.FindRulesets(selector);
|
||||
if (parentRulesets != null)
|
||||
{
|
||||
return parentRulesets;
|
||||
}
|
||||
}
|
||||
|
||||
if (ClosureEnvironment != null)
|
||||
{
|
||||
return ClosureEnvironment.FindRulesets(selector);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Function to this Env object
|
||||
/// </summary>
|
||||
public void AddFunction(string name, Type type)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException("name");
|
||||
if (type == null) throw new ArgumentNullException("type");
|
||||
|
||||
_functionTypes[name] = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given an assembly, adds all the dotless Functions in that assembly into this Env.
|
||||
/// </summary>
|
||||
public void AddFunctionsFromAssembly(Assembly assembly)
|
||||
{
|
||||
if (assembly == null) throw new ArgumentNullException("assembly");
|
||||
|
||||
var functions = GetFunctionsFromAssembly(assembly);
|
||||
|
||||
AddFunctionsToRegistry(functions);
|
||||
}
|
||||
|
||||
private void AddFunctionsToRegistry(IEnumerable<KeyValuePair<string, Type>> functions) {
|
||||
foreach (var func in functions) {
|
||||
AddFunction(func.Key, func.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, Type> GetFunctionsFromAssembly(Assembly assembly) {
|
||||
var functionType = typeof (Function);
|
||||
|
||||
return assembly
|
||||
.GetTypes()
|
||||
.Where(t => functionType.IsAssignableFrom(t) && t != functionType)
|
||||
.Where(t => !t.IsAbstract)
|
||||
.SelectMany(GetFunctionNames)
|
||||
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||
}
|
||||
|
||||
private static Dictionary<string, Type> GetCoreFunctions() {
|
||||
var functions = GetFunctionsFromAssembly(Assembly.GetExecutingAssembly());
|
||||
functions["%"] = typeof (CFormatString);
|
||||
return functions;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, Type> CoreFunctions = GetCoreFunctions();
|
||||
|
||||
private void AddCoreFunctions() {
|
||||
_functionTypes = new Dictionary<string, Type>(CoreFunctions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a function name, returns a new Function matching that name.
|
||||
/// </summary>
|
||||
public virtual Function GetFunction(string name)
|
||||
{
|
||||
Function function = null;
|
||||
name = name.ToLowerInvariant();
|
||||
|
||||
if (_functionTypes.ContainsKey(name))
|
||||
{
|
||||
function = (Function)Activator.CreateInstance(_functionTypes[name]);
|
||||
function.Logger = Logger;
|
||||
}
|
||||
|
||||
return function;
|
||||
}
|
||||
|
||||
private static IEnumerable<KeyValuePair<string, Type>> GetFunctionNames(Type t)
|
||||
{
|
||||
var name = t.Name;
|
||||
|
||||
if (name.EndsWith("function", StringComparison.InvariantCultureIgnoreCase))
|
||||
name = name.Substring(0, name.Length - 8);
|
||||
|
||||
name = Regex.Replace(name, @"\B[A-Z]", "-$0");
|
||||
|
||||
name = name.ToLowerInvariant();
|
||||
|
||||
yield return new KeyValuePair<string, Type>(name, t);
|
||||
|
||||
if(name.Contains("-"))
|
||||
yield return new KeyValuePair<string, Type>(name.Replace("-", ""), t);
|
||||
}
|
||||
|
||||
public void AddExtension(Selector selector, Extend extends, Env env)
|
||||
{
|
||||
foreach (var extending in extends.Exact)
|
||||
{
|
||||
Extender match = null;
|
||||
if ((match = _extensions.OfType<ExactExtender>().FirstOrDefault(e => e.BaseSelector.ToString().Trim() == extending.ToString().Trim())) == null)
|
||||
{
|
||||
match = new ExactExtender(extending, extends);
|
||||
_extensions.Add(match);
|
||||
}
|
||||
|
||||
match.AddExtension(selector,env);
|
||||
}
|
||||
|
||||
foreach (var extending in extends.Partial)
|
||||
{
|
||||
Extender match = null;
|
||||
if ((match = _extensions.OfType<PartialExtender>().FirstOrDefault(e => e.BaseSelector.ToString().Trim() == extending.ToString().Trim())) == null)
|
||||
{
|
||||
match = new PartialExtender(extending, extends);
|
||||
_extensions.Add(match);
|
||||
}
|
||||
|
||||
match.AddExtension(selector,env);
|
||||
}
|
||||
|
||||
if (Parent != null) {
|
||||
Parent.AddExtension(selector, extends, env);
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterExtensionsFrom(Env child) {
|
||||
_extensions.AddRange(child._extensions);
|
||||
}
|
||||
|
||||
public IEnumerable<Extender> FindUnmatchedExtensions() {
|
||||
return _extensions.Where(e => !e.IsMatched);
|
||||
}
|
||||
|
||||
public ExactExtender FindExactExtension(string selection)
|
||||
{
|
||||
if (ExtendMediaScope.Any())
|
||||
{
|
||||
var mediaScopedExtensions = ExtendMediaScope.Select(media => media.FindExactExtension(selection)).FirstOrDefault(result => result != null);
|
||||
if (mediaScopedExtensions != null) {
|
||||
return mediaScopedExtensions;
|
||||
}
|
||||
}
|
||||
|
||||
return _extensions.OfType<ExactExtender>().FirstOrDefault(e => e.BaseSelector.ToString().Trim() == selection);
|
||||
}
|
||||
|
||||
public PartialExtender[] FindPartialExtensions(Context selection)
|
||||
{
|
||||
if (ExtendMediaScope.Any())
|
||||
{
|
||||
var mediaScopedExtensions = ExtendMediaScope.Select(media => media.FindPartialExtensions(selection)).FirstOrDefault(result => result.Any());
|
||||
if (mediaScopedExtensions != null) {
|
||||
return mediaScopedExtensions;
|
||||
}
|
||||
}
|
||||
|
||||
return _extensions.OfType<PartialExtender>()
|
||||
.WhereExtenderMatches(selection)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
[Obsolete("This method doesn't return the correct results. Use FindPartialExtensions(Context) instead.", false)]
|
||||
public PartialExtender[] FindPartialExtensions(string selection)
|
||||
{
|
||||
if (ExtendMediaScope.Any())
|
||||
{
|
||||
var mediaScopedExtensions = ExtendMediaScope.Select(media => media.FindPartialExtensions(selection)).FirstOrDefault(result => result.Any());
|
||||
if (mediaScopedExtensions != null) {
|
||||
return mediaScopedExtensions;
|
||||
}
|
||||
}
|
||||
|
||||
return _extensions.OfType<PartialExtender>().Where(e => selection.Contains(e.BaseSelector.ToString().Trim())).ToArray();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Frames.Select(f => f.ToString()).JoinStrings(" <- ");
|
||||
}
|
||||
}
|
||||
}
|
||||
139
dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Extender.cs
Executable file
139
dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Extender.cs
Executable file
@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using dotless.Core.Parser.Tree;
|
||||
using dotless.Core.Utils;
|
||||
|
||||
namespace dotless.Core.Parser.Infrastructure
|
||||
{
|
||||
public class ExactExtender : Extender
|
||||
{
|
||||
[Obsolete("Use the overload that accepts the Extend node")]
|
||||
public ExactExtender(Selector baseSelector) : this(baseSelector, null)
|
||||
{
|
||||
|
||||
}
|
||||
public ExactExtender(Selector baseSelector, Extend extend) : base(baseSelector, extend)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class PartialExtender : Extender
|
||||
{
|
||||
[Obsolete("Use the overload that accepts the Extend node")]
|
||||
public PartialExtender(Selector baseSelector) : this(baseSelector, null)
|
||||
{
|
||||
|
||||
}
|
||||
public PartialExtender(Selector baseSelector, Extend extend) : base(baseSelector, extend)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public IEnumerable<Selector> Replacements(string selection)
|
||||
{
|
||||
foreach (var ex in ExtendedBy)
|
||||
{
|
||||
yield return new Selector(new []{new Element(null, selection.Replace(BaseSelector.ToString().Trim(), ex.ToString().Trim()))});
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static class ExtenderMatcherExtensions {
|
||||
internal static IEnumerable<PartialExtender> WhereExtenderMatches(this IEnumerable<PartialExtender> extenders, Context selection) {
|
||||
var selectionElements = selection.SelectMany(selectors => selectors.SelectMany(s => s.Elements)).ToList();
|
||||
|
||||
return extenders.Where(e => e.ElementListMatches(selectionElements));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether or not this extender matches the selector elements in <paramref name="list"/>
|
||||
/// by checking if the elements in <see cref="Extender.BaseSelector"/> are a subsequence of the ones in
|
||||
/// <paramref name="list"/>.
|
||||
///
|
||||
/// The special case in the comparison is that if the subsequence element we are comparing is the last one in its
|
||||
/// sequence, we don't compare combinators.
|
||||
///
|
||||
/// A practical example of why we do that is an extendee with the selector:
|
||||
///
|
||||
/// .test .a
|
||||
///
|
||||
/// and an extender with the selector
|
||||
///
|
||||
/// .test
|
||||
///
|
||||
/// The extender should match, even though the .test element in the extendee will have the descendant combinator " "
|
||||
/// and the .test element in the extender won't.
|
||||
/// </summary>
|
||||
private static bool ElementListMatches(this PartialExtender extender, IList<Element> list) {
|
||||
int count = extender.BaseSelector.Elements.Count;
|
||||
|
||||
return extender.BaseSelector.Elements.IsSubsequenceOf(list, (subIndex, subElement, index, seqelement) => {
|
||||
if (subIndex < count - 1) {
|
||||
return Equals(subElement.Combinator, seqelement.Combinator) &&
|
||||
string.Equals(subElement.Value, seqelement.Value) &&
|
||||
Equals(subElement.NodeValue, seqelement.NodeValue);
|
||||
}
|
||||
|
||||
return string.Equals(subElement.Value, seqelement.Value) &&
|
||||
Equals(subElement.NodeValue, seqelement.NodeValue);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class Extender
|
||||
{
|
||||
public Selector BaseSelector { get; private set; }
|
||||
public List<Selector> ExtendedBy { get; private set; }
|
||||
public bool IsReference { get; set; }
|
||||
public bool IsMatched { get; set; }
|
||||
public Extend Extend { get; private set; }
|
||||
|
||||
[Obsolete("Use the overload that accepts the Extend node")]
|
||||
public Extender(Selector baseSelector)
|
||||
{
|
||||
BaseSelector = baseSelector;
|
||||
ExtendedBy = new List<Selector>();
|
||||
IsReference = baseSelector.IsReference;
|
||||
}
|
||||
|
||||
public Extender(Selector baseSelector, Extend extend) : this(baseSelector)
|
||||
{
|
||||
Extend = extend;
|
||||
}
|
||||
|
||||
public static string FullPathSelector()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void AddExtension(Selector selector, Env env)
|
||||
{
|
||||
var selectorPath = new List<IEnumerable<Selector>> {new [] {selector} };
|
||||
selectorPath.AddRange(env.Frames.Skip(1).Select(f => f.Selectors.Where(partialSelector => partialSelector != null)));
|
||||
|
||||
ExtendedBy.Add(GenerateExtenderSelector(env, selectorPath));
|
||||
}
|
||||
|
||||
private Selector GenerateExtenderSelector(Env env, List<IEnumerable<Selector>> selectorPath) {
|
||||
var context = GenerateExtenderSelector(selectorPath);
|
||||
return new Selector(new[] {new Element(null, context.ToCss(env)) }) { IsReference = IsReference };
|
||||
}
|
||||
|
||||
private Context GenerateExtenderSelector(List<IEnumerable<Selector>> selectorStack) {
|
||||
if (!selectorStack.Any()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var parentContext = GenerateExtenderSelector(selectorStack.Skip(1).ToList());
|
||||
|
||||
var childContext = new Context();
|
||||
childContext.AppendSelectors(parentContext, selectorStack.First());
|
||||
return childContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user