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