(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("Mocha HTTP Debugger Plugin");
+ resp.AppendLine("Mocha HTTP Debugger Plugin");
+ resp.AppendLine("The requested node has been opened in your IDE
");
+ resp.AppendLine(String.Format("Node: {0}
Project: {1}
", nodename, projname));
+ resp.AppendLine("");
+ resp.AppendLine();
+ byte[] respdata = Encoding.UTF8.GetBytes(resp.ToString());
+ client.GetStream().Write(respdata, 0, respdata.Length);
+ client.Close();
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Debugger/Properties/AssemblyInfo.cs b/dotnet/Applications/Mocha.Debugger/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..f03ca78
--- /dev/null
+++ b/dotnet/Applications/Mocha.Debugger/Properties/AssemblyInfo.cs
@@ -0,0 +1,46 @@
+//
+// AssemblyInfo.cs
+//
+// Author:
+// Michael Becker
+//
+// 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 .
+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("")]
diff --git a/dotnet/Applications/Mocha.Debugger/obj/Debug/.NETFramework,Version=v4.7.AssemblyAttributes.cs b/dotnet/Applications/Mocha.Debugger/obj/Debug/.NETFramework,Version=v4.7.AssemblyAttributes.cs
new file mode 100644
index 0000000..4c82453
--- /dev/null
+++ b/dotnet/Applications/Mocha.Debugger/obj/Debug/.NETFramework,Version=v4.7.AssemblyAttributes.cs
@@ -0,0 +1,4 @@
+//
+using System;
+using System.Reflection;
+[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7", FrameworkDisplayName = ".NET Framework 4.7")]
diff --git a/dotnet/Applications/Mocha.Debugger/obj/Debug/Mocha.Debugger.csproj.AssemblyReference.cache b/dotnet/Applications/Mocha.Debugger/obj/Debug/Mocha.Debugger.csproj.AssemblyReference.cache
new file mode 100644
index 0000000..8c02396
Binary files /dev/null and b/dotnet/Applications/Mocha.Debugger/obj/Debug/Mocha.Debugger.csproj.AssemblyReference.cache differ
diff --git a/dotnet/Applications/Mocha.Debugger/obj/Debug/Mocha.Debugger.csproj.CopyComplete b/dotnet/Applications/Mocha.Debugger/obj/Debug/Mocha.Debugger.csproj.CopyComplete
new file mode 100644
index 0000000..e69de29
diff --git a/dotnet/Applications/Mocha.Debugger/obj/Debug/Mocha.Debugger.csproj.CoreCompileInputs.cache b/dotnet/Applications/Mocha.Debugger/obj/Debug/Mocha.Debugger.csproj.CoreCompileInputs.cache
new file mode 100644
index 0000000..fcace06
--- /dev/null
+++ b/dotnet/Applications/Mocha.Debugger/obj/Debug/Mocha.Debugger.csproj.CoreCompileInputs.cache
@@ -0,0 +1 @@
+65bc769232bbd0a8cc10bbb13b101907578febc4
diff --git a/dotnet/Applications/Mocha.Debugger/obj/Debug/Mocha.Debugger.csproj.FileListAbsolute.txt b/dotnet/Applications/Mocha.Debugger/obj/Debug/Mocha.Debugger.csproj.FileListAbsolute.txt
new file mode 100644
index 0000000..861e534
--- /dev/null
+++ b/dotnet/Applications/Mocha.Debugger/obj/Debug/Mocha.Debugger.csproj.FileListAbsolute.txt
@@ -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
diff --git a/dotnet/Applications/Mocha.Debugger/obj/Debug/mcxdebug.exe b/dotnet/Applications/Mocha.Debugger/obj/Debug/mcxdebug.exe
new file mode 100644
index 0000000..7e226c7
Binary files /dev/null and b/dotnet/Applications/Mocha.Debugger/obj/Debug/mcxdebug.exe differ
diff --git a/dotnet/Applications/Mocha.Debugger/obj/Debug/mcxdebug.pdb b/dotnet/Applications/Mocha.Debugger/obj/Debug/mcxdebug.pdb
new file mode 100644
index 0000000..030a5b6
Binary files /dev/null and b/dotnet/Applications/Mocha.Debugger/obj/Debug/mcxdebug.pdb differ
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Cache/ICache.cs b/dotnet/Applications/Mocha.Web.Server/Core/Cache/ICache.cs
new file mode 100755
index 0000000..c0e874e
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Cache/ICache.cs
@@ -0,0 +1,11 @@
+namespace dotless.Core.Cache
+{
+ using System.Collections.Generic;
+
+ public interface ICache
+ {
+ void Insert(string cacheKey, IEnumerable fileDependancies, string css);
+ bool Exists(string cacheKey);
+ string Retrieve(string cacheKey);
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Cache/InMemoryCache.cs b/dotnet/Applications/Mocha.Web.Server/Core/Cache/InMemoryCache.cs
new file mode 100755
index 0000000..f579aa5
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Cache/InMemoryCache.cs
@@ -0,0 +1,32 @@
+namespace dotless.Core.Cache
+{
+ using System.Collections.Generic;
+
+ public class InMemoryCache : ICache
+ {
+ private readonly Dictionary _cache;
+
+ public InMemoryCache()
+ {
+ _cache = new Dictionary();
+ }
+
+ public void Insert(string fileName, IEnumerable 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 "";
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Engine/CacheDecorator.cs b/dotnet/Applications/Mocha.Web.Server/Core/Engine/CacheDecorator.cs
new file mode 100755
index 0000000..b1bc774
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Engine/CacheDecorator.cs
@@ -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 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; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Engine/ILessEngine.cs b/dotnet/Applications/Mocha.Web.Server/Core/Engine/ILessEngine.cs
new file mode 100755
index 0000000..abc7842
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Engine/ILessEngine.cs
@@ -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 GetImports();
+ bool LastTransformationSuccessful { get; }
+
+ string CurrentDirectory { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Engine/LessEngine.cs b/dotnet/Applications/Mocha.Web.Server/Core/Engine/LessEngine.cs
new file mode 100755
index 0000000..f5be716
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Engine/LessEngine.cs
@@ -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 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 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 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(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 GetImports()
+ {
+ return Parser.Importer.GetImports();
+ }
+
+ public void ResetImports()
+ {
+ Parser.Importer.ResetImports();
+ }
+
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Engine/ParameterDecorator.cs b/dotnet/Applications/Mocha.Web.Server/Core/Engine/ParameterDecorator.cs
new file mode 100755
index 0000000..17926a7
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Engine/ParameterDecorator.cs
@@ -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 GetImports()
+ {
+ return Underlying.GetImports();
+ }
+
+ public void ResetImports()
+ {
+ Underlying.ResetImports();
+ }
+
+ public bool LastTransformationSuccessful
+ {
+ get
+ {
+ return Underlying.LastTransformationSuccessful;
+ }
+ }
+
+ private static bool ValueIsNotNullOrEmpty(KeyValuePair kvp)
+ {
+ return !string.IsNullOrEmpty(kvp.Value);
+ }
+
+ public string CurrentDirectory
+ {
+ get { return Underlying.CurrentDirectory; }
+ set { Underlying.CurrentDirectory = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Exceptions/ParserException.cs b/dotnet/Applications/Mocha.Web.Server/Core/Exceptions/ParserException.cs
new file mode 100755
index 0000000..c374ba4
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Exceptions/ParserException.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Exceptions/ParsingException.cs b/dotnet/Applications/Mocha.Web.Server/Core/Exceptions/ParsingException.cs
new file mode 100755
index 0000000..61bf107
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Exceptions/ParsingException.cs
@@ -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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Extensions/MissingTypeRegistrationException.cs b/dotnet/Applications/Mocha.Web.Server/Core/Extensions/MissingTypeRegistrationException.cs
new file mode 100755
index 0000000..58c095e
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Extensions/MissingTypeRegistrationException.cs
@@ -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)}>";
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Importers/IImporter.cs b/dotnet/Applications/Mocha.Web.Server/Core/Importers/IImporter.cs
new file mode 100755
index 0000000..3b40b68
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Importers/IImporter.cs
@@ -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
+ {
+ ///
+ /// Get a list of the current paths, used to pass back in to alter url's after evaluation
+ ///
+ ///
+ List GetCurrentPathsClone();
+
+ ///
+ /// Imports an import and return true if successful
+ ///
+ ImportAction Import(Import import);
+
+ ///
+ /// A method set by the parser implementation in order to get a new parser for use in importing
+ ///
+ Func Parser { get; set; }
+
+ ///
+ /// Called for every Url and allows the importer to adjust relative url's to be relative to the
+ /// primary url
+ ///
+ string AlterUrl(string url, List pathList);
+
+ string CurrentDirectory { get; set; }
+
+ IDisposable BeginScope(Import parent);
+
+ ///
+ /// Resets the imports.
+ ///
+ void ResetImports();
+
+ ///
+ /// Gets the already imported files
+ ///
+ ///
+ IEnumerable GetImports();
+ }
+
+ ///
+ /// The action to do with the @import statement
+ ///
+ public enum ImportAction
+ {
+ ///
+ /// Import as less (process the file and include)
+ ///
+ ImportLess,
+ ///
+ /// Import verbatim as CSS
+ ///
+ ImportCss,
+ ///
+ /// Leave a @import statement
+ ///
+ LeaveImport,
+ ///
+ /// Do nothing (e.g. when it is an import-once and has already been imported)
+ ///
+ ImportNothing,
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Importers/Importer.cs b/dotnet/Applications/Mocha.Web.Server/Core/Importers/Importer.cs
new file mode 100755
index 0000000..2f5dc3a
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Importers/Importer.cs
@@ -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://(?.+?)#(?.+)$");
+
+ public static Regex EmbeddedResourceRegex { get { return _embeddedResourceRegex; } }
+ public IFileReader FileReader { get; set; }
+
+ ///
+ /// List of successful imports
+ ///
+ public List Imports { get; set; }
+
+ public Func Parser { get; set; }
+ private readonly List _paths = new List();
+
+ ///
+ /// The raw imports of every @import node, for use with @import
+ ///
+ protected readonly List _rawImports = new List();
+
+ ///
+ /// 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
+ ///
+ private readonly List _referenceImports = new List();
+
+ public virtual string CurrentDirectory { get; set; }
+
+ ///
+ /// Whether or not the importer should alter urls
+ ///
+ public bool IsUrlRewritingDisabled { get; set; }
+
+ public string RootPath { get; set; }
+
+ ///
+ /// Import all files as if they are less regardless of file extension
+ ///
+ public bool ImportAllFilesAsLess { get; set; }
+
+ ///
+ /// Import the css and include inline
+ ///
+ 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();
+ CurrentDirectory = "";
+ }
+
+ ///
+ /// Whether a url has a protocol on it
+ ///
+ private static bool IsProtocolUrl(string url)
+ {
+ return Regex.IsMatch(url, @"^([a-zA-Z]{2,}:)");
+ }
+
+ ///
+ /// Whether a url has a protocol on it
+ ///
+ private static bool IsNonRelativeUrl(string url)
+ {
+ return url.StartsWith(@"/") || url.StartsWith(@"~/") || Regex.IsMatch(url, @"^[a-zA-Z]:");
+ }
+
+ ///
+ /// Whether a url represents an embedded resource
+ ///
+ private static bool IsEmbeddedResource(string path)
+ {
+ return _embeddedResourceRegex.IsMatch(path);
+ }
+
+ ///
+ /// Get a list of the current paths, used to pass back in to alter url's after evaluation
+ ///
+ ///
+ public List GetCurrentPathsClone()
+ {
+ return new List(_paths);
+ }
+
+ ///
+ /// returns true if the import should be ignored because it is a duplicate and import-once was used
+ ///
+ ///
+ ///
+ protected bool CheckIgnoreImport(Import import)
+ {
+ return CheckIgnoreImport(import, import.Path);
+ }
+
+ ///
+ /// returns true if the import should be ignored because it is a duplicate and import-once was used
+ ///
+ ///
+ ///
+ 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 importList, string path) {
+ if (importList.Contains(path, StringComparer.InvariantCultureIgnoreCase))
+ {
+ return true;
+ }
+ importList.Add(path);
+ return false;
+ }
+
+ ///
+ /// Imports the file inside the import as a dot-less file.
+ ///
+ ///
+ /// The action for the import node to process
+ 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));
+ }
+
+ ///
+ /// Uses the paths to adjust the file path
+ ///
+ protected string GetAdjustedFilePath(string path, IEnumerable pathList)
+ {
+ return pathList.Concat(new[] { path }).AggregatePaths(CurrentDirectory);
+ }
+
+ ///
+ /// Imports a less file and puts the root into the import node
+ ///
+ 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;
+ }
+
+ ///
+ /// Imports a css file from an embedded resource and puts the contents into the import node
+ ///
+ ///
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// Imports a css file and puts the contents into the import node
+ ///
+ protected bool ImportCssFileContents(string file, Import import)
+ {
+ if (!FileReader.DoesFileExist(file))
+ {
+ return false;
+ }
+
+ import.InnerContent = FileReader.GetFileContents(file);
+ Imports.Add(file);
+
+ return true;
+ }
+
+ ///
+ /// Called for every Url and allows the importer to adjust relative url's to be relative to the
+ /// primary url
+ ///
+ public string AlterUrl(string url, List 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 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;
+ }
+ }
+
+ ///
+ /// Utility class used to retrieve the content of an embedded resource using a separate app domain in order to unload the assembly when done.
+ ///
+ class ResourceLoader : MarshalByRefObject
+ {
+ private byte[] _fileContents;
+ private string _resourceName;
+ private string _resourceContent;
+
+ ///
+ /// Gets the text content of an embedded resource.
+ ///
+ /// The path in the form: dll://AssemblyName#ResourceName
+ /// The content of the resource
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Input/FileReader.cs b/dotnet/Applications/Mocha.Web.Server/Core/Input/FileReader.cs
new file mode 100755
index 0000000..e26eab5
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Input/FileReader.cs
@@ -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; } }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Input/IFileReader.cs b/dotnet/Applications/Mocha.Web.Server/Core/Input/IFileReader.cs
new file mode 100755
index 0000000..b4ee7fd
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Input/IFileReader.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Input/IPathResolver.cs b/dotnet/Applications/Mocha.Web.Server/Core/Input/IPathResolver.cs
new file mode 100755
index 0000000..2b849b3
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Input/IPathResolver.cs
@@ -0,0 +1,7 @@
+namespace dotless.Core.Input
+{
+ public interface IPathResolver
+ {
+ string GetFullPath(string path);
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Input/RelativePathResolver.cs b/dotnet/Applications/Mocha.Web.Server/Core/Input/RelativePathResolver.cs
new file mode 100755
index 0000000..7fa74dd
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Input/RelativePathResolver.cs
@@ -0,0 +1,10 @@
+namespace dotless.Core.Input
+{
+ public class RelativePathResolver : IPathResolver
+ {
+ public string GetFullPath(string path)
+ {
+ return path;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Less.cs b/dotnet/Applications/Mocha.Web.Server/Core/Less.cs
new file mode 100755
index 0000000..0aa2757
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Less.cs
@@ -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);
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Loggers/ConsoleLogger.cs b/dotnet/Applications/Mocha.Web.Server/Core/Loggers/ConsoleLogger.cs
new file mode 100755
index 0000000..3dceb6a
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Loggers/ConsoleLogger.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Loggers/DiagnosticsLogger.cs b/dotnet/Applications/Mocha.Web.Server/Core/Loggers/DiagnosticsLogger.cs
new file mode 100755
index 0000000..443059f
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Loggers/DiagnosticsLogger.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Loggers/ILogger.cs b/dotnet/Applications/Mocha.Web.Server/Core/Loggers/ILogger.cs
new file mode 100755
index 0000000..5211267
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Loggers/ILogger.cs
@@ -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
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Loggers/Logger.cs b/dotnet/Applications/Mocha.Web.Server/Core/Loggers/Logger.cs
new file mode 100755
index 0000000..1991805
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Loggers/Logger.cs
@@ -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)); }
+
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Loggers/NullLogger.cs b/dotnet/Applications/Mocha.Web.Server/Core/Loggers/NullLogger.cs
new file mode 100755
index 0000000..08bd731
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Loggers/NullLogger.cs
@@ -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;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parameters/ConsoleArgumentParameterSource.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parameters/ConsoleArgumentParameterSource.cs
new file mode 100755
index 0000000..793346a
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parameters/ConsoleArgumentParameterSource.cs
@@ -0,0 +1,13 @@
+namespace dotless.Core.Parameters
+{
+ using System.Collections.Generic;
+
+ public class ConsoleArgumentParameterSource : IParameterSource
+ {
+ public static IDictionary ConsoleArguments = new Dictionary();
+ public IDictionary GetParameters()
+ {
+ return ConsoleArguments;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parameters/IParameterSource.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parameters/IParameterSource.cs
new file mode 100755
index 0000000..c50d08d
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parameters/IParameterSource.cs
@@ -0,0 +1,9 @@
+namespace dotless.Core.Parameters
+{
+ using System.Collections.Generic;
+
+ public interface IParameterSource
+ {
+ IDictionary GetParameters();
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parameters/NullParameterSource.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parameters/NullParameterSource.cs
new file mode 100755
index 0000000..693b3af
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parameters/NullParameterSource.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace dotless.Core.Parameters {
+ public class NullParameterSource : IParameterSource
+ {
+ public IDictionary GetParameters()
+ {
+ return new Dictionary();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AbsFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AbsFunction.cs
new file mode 100755
index 0000000..d71fb18
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AbsFunction.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AddFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AddFunction.cs
new file mode 100755
index 0000000..7bfbb89
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AddFunction.cs
@@ -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(Arguments, this, Location);
+
+ var value = Arguments.Cast().Select(d => d.Value).Aggregate(0d, (a, b) => a + b);
+
+ return new Number(value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AlphaFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AlphaFunction.cs
new file mode 100755
index 0000000..96670f2
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AlphaFunction.cs
@@ -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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ArgbFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ArgbFunction.cs
new file mode 100755
index 0000000..88487af
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ArgbFunction.cs
@@ -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(Arguments[0], this, Location);
+
+ return new TextNode(color.ToArgb());
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AverageFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AverageFunction.cs
new file mode 100755
index 0000000..019147c
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/AverageFunction.cs
@@ -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;
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/BlueFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/BlueFunction.cs
new file mode 100755
index 0000000..6b8fe65
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/BlueFunction.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/CeilFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/CeilFunction.cs
new file mode 100755
index 0000000..6530d20
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/CeilFunction.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ColorFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ColorFunction.cs
new file mode 100755
index 0000000..0d23ee7
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ColorFunction.cs
@@ -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(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);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ColorFunctionBase.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ColorFunctionBase.cs
new file mode 100755
index 0000000..3aa368d
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ColorFunctionBase.cs
@@ -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(Arguments[0], this, Arguments[0].Location);
+
+ var color = Arguments[0] as Color;
+
+ if (Arguments.Count == 2)
+ {
+ Guard.ExpectNode(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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ColorMixFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ColorMixFunction.cs
new file mode 100755
index 0000000..d42fa58
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ColorMixFunction.cs
@@ -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(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);
+ }
+
+ ///
+ /// Color composition rules from http://www.w3.org/TR/compositing-1/
+ /// (translated from the less.js version
+ ///
+ private double Compose(Color backdrop, Color source, double ar, Func 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);
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ComplementFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ComplementFunction.cs
new file mode 100755
index 0000000..439b287
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ComplementFunction.cs
@@ -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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ContrastFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ContrastFunction.cs
new file mode 100755
index 0000000..556208c
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ContrastFunction.cs
@@ -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(Arguments[0], this, Location);
+
+ var color = (Color) Arguments[0];
+
+ if (Arguments.Count > 1)
+ Guard.ExpectNode(Arguments[1], this, Location);
+ if (Arguments.Count > 2)
+ Guard.ExpectNode(Arguments[2], this, Location);
+ if (Arguments.Count > 3)
+ Guard.ExpectNode(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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/DataUriFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/DataUriFunction.cs
new file mode 100755
index 0000000..7a3bd3d
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/DataUriFunction.cs
@@ -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(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(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);
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/DefaultFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/DefaultFunction.cs
new file mode 100755
index 0000000..1eb9277
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/DefaultFunction.cs
@@ -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()");
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/DifferenceFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/DifferenceFunction.cs
new file mode 100755
index 0000000..6aac819
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/DifferenceFunction.cs
@@ -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);
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ExclusionFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ExclusionFunction.cs
new file mode 100755
index 0000000..93a60dc
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ExclusionFunction.cs
@@ -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;
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ExtractFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ExtractFunction.cs
new file mode 100755
index 0000000..4c062d3
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ExtractFunction.cs
@@ -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(args[0], this, args[0].Location);
+
+ var index = (int)(args[0] as Number).Value;
+
+ // Extract function indecies are 1-based
+ return list[index-1];
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/FloorFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/FloorFunction.cs
new file mode 100755
index 0000000..6316a36
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/FloorFunction.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/FormatStringFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/FormatStringFunction.cs
new file mode 100755
index 0000000..d2b20b3
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/FormatStringFunction.cs
@@ -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 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/Function.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/Function.cs
new file mode 100755
index 0000000..9837b54
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/Function.cs
@@ -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 Arguments { get; set; }
+ public ILogger Logger { get; set; }
+ public NodeLocation Location { get; set; }
+
+ public Node Call(Env env, IEnumerable 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());
+ }
+
+ ///
+ /// Warns that a function is not supported by less.js
+ ///
+ /// unsupported pattern function call e.g. alpha(color number)
+ protected void WarnNotSupportedByLessJS(string functionPattern)
+ {
+ WarnNotSupportedByLessJS(functionPattern, null, null);
+ }
+
+ ///
+ /// Warns that a function is not supported by less.js
+ ///
+ /// unsupported pattern function call e.g. alpha(color number)
+ /// Replacement pattern function call e.g. fadein(color, number)
+ protected void WarnNotSupportedByLessJS(string functionPattern, string replacementPattern)
+ {
+ WarnNotSupportedByLessJS(functionPattern, replacementPattern, null);
+ }
+
+ ///
+ /// Warns that a function is not supported by less.js
+ ///
+ /// unsupported pattern function call e.g. alpha(color number)
+ /// Replacement pattern function call e.g. fadein(color, number)
+ /// Extra information to put on the end.
+ 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);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/GradientImageFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/GradientImageFunction.cs
new file mode 100755
index 0000000..c74704f
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/GradientImageFunction.cs
@@ -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;
+
+ ///
+ /// Outputs node with gradient image implemented as described in RFC-2397 (The "data" URL scheme, http://tools.ietf.org/html/rfc2397)
+ ///
+ ///
+ /// Acepts color point definitions and outputs image data URL. Usage:
+ /// gradient(#color1, #color2[, position2][, #color3[, position3]]...)
+ /// 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.
+ ///
+ /// The following example shows how to render a gradient (.less file):
+ ///
+ /// .ui-widget-header { background: @headerBgColor gradientImage(@headerBgColor, desaturate(lighten(@headerBgColor, 30), 30)) 50% 50% repeat-x; }
+ ///
+ ///
+ ///
+ 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 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 _cache = new List();
+ #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(Arguments.Take(2), this, Location);
+ var first = (Color) Arguments[0];
+ var points = new List
+ {
+ 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(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();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/GrayscaleFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/GrayscaleFunction.cs
new file mode 100755
index 0000000..709a28e
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/GrayscaleFunction.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/GreenFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/GreenFunction.cs
new file mode 100755
index 0000000..4a9f101
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/GreenFunction.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HardlightFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HardlightFunction.cs
new file mode 100755
index 0000000..e3f7803
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HardlightFunction.cs
@@ -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;
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HexFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HexFunction.cs
new file mode 100755
index 0000000..1035d9b
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HexFunction.cs
@@ -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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HslColorFunctionBase.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HslColorFunctionBase.cs
new file mode 100755
index 0000000..f6fb1d5
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HslColorFunctionBase.cs
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HslFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HslFunction.cs
new file mode 100755
index 0000000..4faf006
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HslFunction.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HslaFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HslaFunction.cs
new file mode 100755
index 0000000..86bf17a
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HslaFunction.cs
@@ -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(Arguments, this, Location);
+
+ var args = Arguments.Cast().ToArray();
+
+ return new HslColor(args[0], args[1], args[2], args[3]).ToRgbColor();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HueFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HueFunction.cs
new file mode 100755
index 0000000..ea3a77c
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/HueFunction.cs
@@ -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 { }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/IncrementFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/IncrementFunction.cs
new file mode 100755
index 0000000..f3316ae
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/IncrementFunction.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/IsFunctions.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/IsFunctions.cs
new file mode 100755
index 0000000..ea9bf6c
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/IsFunctions.cs
@@ -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 : IsFunction where T:Node
+ {
+ protected override bool IsEvaluator(Node node)
+ {
+ return node is T;
+ }
+ }
+
+ public class IsColorFunction : IsTypeFunction
+ {
+ }
+
+ public class IsNumber : IsTypeFunction
+ {
+ }
+
+ public class IsString : IsTypeFunction
+ {
+ }
+
+ public class IsKeyword : IsTypeFunction
+ {
+ }
+
+ public class IsUrl : IsTypeFunction
+ {
+ }
+
+ public abstract class IsDimensionUnitFunction : IsTypeFunction
+ {
+ 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";
+ }
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/LengthFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/LengthFunction.cs
new file mode 100755
index 0000000..b706084
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/LengthFunction.cs
@@ -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);
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/LightnessFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/LightnessFunction.cs
new file mode 100755
index 0000000..c248ca7
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/LightnessFunction.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ListFunctionBase.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ListFunctionBase.cs
new file mode 100755
index 0000000..47e085f
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ListFunctionBase.cs
@@ -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(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);
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/MixFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/MixFunction.cs
new file mode 100755
index 0000000..63ef750
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/MixFunction.cs
@@ -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(Arguments.Take(2), this, Location);
+
+ double weight = 50;
+ if (Arguments.Count == 3)
+ {
+ Guard.ExpectNode(Arguments[2], this, Location);
+
+ weight = ((Number)Arguments[2]).Value;
+ }
+
+ var colors = Arguments.Take(2).Cast().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(Arguments[0], this, Location);
+ Guard.ExpectNode(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(Arguments[0], this, Location);
+ Guard.ExpectNode(Arguments[1], this, Location);
+
+ double weight = ((Number)Arguments[1]).Value;
+
+ return Mix(new Color(0, 0, 0), (Color)Arguments[0], weight);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/MultiplyFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/MultiplyFunction.cs
new file mode 100755
index 0000000..2f32ae4
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/MultiplyFunction.cs
@@ -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;
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/NegationFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/NegationFunction.cs
new file mode 100755
index 0000000..9bdcb4f
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/NegationFunction.cs
@@ -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);
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/NumberFunctionBase.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/NumberFunctionBase.cs
new file mode 100755
index 0000000..868a054
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/NumberFunctionBase.cs
@@ -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(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);
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/OverlayFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/OverlayFunction.cs
new file mode 100755
index 0000000..d77d7c6
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/OverlayFunction.cs
@@ -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;
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/PercentageFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/PercentageFunction.cs
new file mode 100755
index 0000000..6db052b
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/PercentageFunction.cs
@@ -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, "%");
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/PowFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/PowFunction.cs
new file mode 100755
index 0000000..ba2bc60
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/PowFunction.cs
@@ -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(Arguments, this, Location);
+
+ WarnNotSupportedByLessJS("pow(number, number)");
+
+ var first = Arguments.Cast().First();
+ var second = Arguments.Cast().ElementAt(1);
+ var value = Math.Pow(first.Value, second.Value);
+
+ return new Number(value);
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RedFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RedFunction.cs
new file mode 100755
index 0000000..99245c8
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RedFunction.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RgbFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RgbFunction.cs
new file mode 100755
index 0000000..dfde85d
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RgbFunction.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RgbaFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RgbaFunction.cs
new file mode 100755
index 0000000..3da50a2
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RgbaFunction.cs
@@ -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(Arguments[0], this, Location);
+ var number = Guard.ExpectNode(Arguments[1], this, Location);
+
+ return new Color(color.RGB, number.Value);
+ }
+
+ Guard.ExpectNumArguments(4, Arguments.Count, this, Location);
+ var args = Guard.ExpectAllNodes(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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RoundFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RoundFunction.cs
new file mode 100755
index 0000000..6023dcc
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/RoundFunction.cs
@@ -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(args[0], this, args[0].Location);
+ return new Number(Math.Round(number.Value, (int)((Number)args[0]).Value, MidpointRounding.AwayFromZero), number.Unit);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/SaturationFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/SaturationFunction.cs
new file mode 100755
index 0000000..90010cb
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/SaturationFunction.cs
@@ -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);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ScreenFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ScreenFunction.cs
new file mode 100755
index 0000000..5977cae
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/ScreenFunction.cs
@@ -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;
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/SoftlightFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/SoftlightFunction.cs
new file mode 100755
index 0000000..04c03b4
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/SoftlightFunction.cs
@@ -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;
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/StringFunctions.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/StringFunctions.cs
new file mode 100755
index 0000000..b08be65
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/StringFunctions.cs
@@ -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 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/UnitFunction.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/UnitFunction.cs
new file mode 100755
index 0000000..7ea0423
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Functions/UnitFunction.cs
@@ -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(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);
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Closure.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Closure.cs
new file mode 100755
index 0000000..5396bd4
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Closure.cs
@@ -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 Context { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Context.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Context.cs
new file mode 100755
index 0000000..853c928
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Context.cs
@@ -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>
+ {
+ private List> Paths { get; set; }
+
+ public Context()
+ {
+ Paths = new List>();
+ }
+
+ public Context Clone()
+ {
+ var newPathList = new List>();
+ return new Context {Paths = newPathList};
+ }
+
+ public void AppendSelectors(Context context, IEnumerable 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 });
+ }
+ return;
+ }
+
+ // The paths are List>
+ // 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();
+ // 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>() { new List() };
+
+ 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>();
+
+ // 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 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(sel[0].Elements);
+ sel[0].Elements.Add(new Element(el.Combinator, ""));
+ }
+ selectorsMultiplied.Add(sel);
+ }
+ else
+ {
+ // and the parent selectors
+ foreach (List 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 newSelectorPath = new List();
+ // selectors from the parent after the join
+ List afterParentJoin = new List();
+ 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(sel[sel.Count - 1].Elements));
+ newSelectorPath.AddRange(sel.Take(sel.Count - 1));
+ newJoinedSelectorEmpty = false;
+ }
+ else
+ {
+ newJoinedSelector = new Selector(new NodeList());
+ }
+
+ //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();
+ }
+ }
+
+ // 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 elements, List> selectors)
+ {
+ if (selectors.Count == 0)
+ {
+ selectors.Add(new List() { new Selector(elements) });
+ return;
+ }
+
+ foreach (List 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> GetEnumerator()
+ {
+ return Paths.Cast>().GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/DefaultNodeProvider.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/DefaultNodeProvider.cs
new file mode 100755
index 0000000..4933ffc
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/DefaultNodeProvider.cs
@@ -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 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 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 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 selectors, NodeList rules, Condition condition, NodeLocation location)
+ {
+ return new GuardedRuleset(selectors, rules, condition) { Location = location };
+ }
+
+ public MixinCall MixinCall(NodeList elements, List arguments, bool important, NodeLocation location)
+ {
+ return new MixinCall(elements, arguments, important) { Location = location };
+ }
+
+ public MixinDefinition MixinDefinition(string name, NodeList 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 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 exact, List 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
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Env.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Env.cs
new file mode 100755
index 0000000..ac6ee36
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Env.cs
@@ -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 _functionTypes;
+ private readonly List _plugins;
+ private readonly List _extensions;
+
+ public Stack 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 MediaPath { get; private set; }
+ public List 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 frames, Dictionary functions) : this(frames, functions) {
+ Parser = parser;
+ }
+
+ protected Env(Stack frames, Dictionary functions) {
+ Frames = frames ?? new Stack();
+ Output = new Output(this);
+ MediaPath = new Stack();
+ MediaBlocks = new List();
+ Logger = new NullLogger(LogLevel.Info);
+
+ _plugins = new List();
+ _functionTypes = functions ?? new Dictionary();
+ _extensions = new List();
+ ExtendMediaScope = new Stack();
+
+ if (_functionTypes.Count == 0)
+ AddCoreFunctions();
+ }
+
+ ///
+ /// Creates a new Env variable for the purposes of scope
+ ///
+ [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)
+ {
+ return CreateChildEnv();
+ }
+
+ ///
+ /// Creates a new Env variable for the purposes of scope
+ ///
+ public virtual Env CreateChildEnv()
+ {
+ return new Env(null, _functionTypes)
+ {
+ Parser = Parser,
+ Parent = this,
+ Debug = Debug,
+ Compress = Compress,
+ DisableVariableRedefines = DisableVariableRedefines
+ };
+ }
+
+ private Env Parent { get; set; }
+
+ ///
+ /// Creates a new Env variable for the purposes of scope
+ ///
+ 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(closure.Context), this._functionTypes);
+ return env;
+ }
+
+ private Env ClosureEnvironment { get; set; }
+
+ ///
+ /// Adds a plugin to this Env
+ ///
+ 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 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);
+ }
+ }
+ }
+
+ ///
+ /// All the visitor plugins to use
+ ///
+ public IEnumerable VisitorPlugins
+ {
+ get
+ {
+ return _plugins.OfType();
+ }
+ }
+
+ //Keep track of media scoping for extenders
+ public Stack ExtendMediaScope { get; set; }
+
+ ///
+ /// Returns whether the comment should be silent
+ ///
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// Finds the first scoped variable with this name
+ ///
+ public Rule FindVariable(string name)
+ {
+ return FindVariable(name, Rule);
+ }
+
+ ///
+ /// Finds the first scoped variable matching the name, using Rule as the current rule to work backwards from
+ ///
+ 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 FindRulesets(Selector selector) where TRuleset : Ruleset
+ {
+ return FindRulesets(selector).Where(c => c.Ruleset is TRuleset);
+ }
+
+ ///
+ /// 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)
+ ///
+ public IEnumerable FindRulesets(Selector selector)
+ {
+ var matchingRuleSets = Frames
+ .Reverse()
+ .SelectMany(frame => frame.Find(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;
+ }
+
+ ///
+ /// Adds a Function to this Env object
+ ///
+ public void AddFunction(string name, Type type)
+ {
+ if (name == null) throw new ArgumentNullException("name");
+ if (type == null) throw new ArgumentNullException("type");
+
+ _functionTypes[name] = type;
+ }
+
+ ///
+ /// Given an assembly, adds all the dotless Functions in that assembly into this Env.
+ ///
+ public void AddFunctionsFromAssembly(Assembly assembly)
+ {
+ if (assembly == null) throw new ArgumentNullException("assembly");
+
+ var functions = GetFunctionsFromAssembly(assembly);
+
+ AddFunctionsToRegistry(functions);
+ }
+
+ private void AddFunctionsToRegistry(IEnumerable> functions) {
+ foreach (var func in functions) {
+ AddFunction(func.Key, func.Value);
+ }
+ }
+
+ private static Dictionary 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 GetCoreFunctions() {
+ var functions = GetFunctionsFromAssembly(Assembly.GetExecutingAssembly());
+ functions["%"] = typeof (CFormatString);
+ return functions;
+ }
+
+ private static readonly Dictionary CoreFunctions = GetCoreFunctions();
+
+ private void AddCoreFunctions() {
+ _functionTypes = new Dictionary(CoreFunctions);
+ }
+
+ ///
+ /// Given a function name, returns a new Function matching that name.
+ ///
+ 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> 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(name, t);
+
+ if(name.Contains("-"))
+ yield return new KeyValuePair(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().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().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 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().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()
+ .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().Where(e => selection.Contains(e.BaseSelector.ToString().Trim())).ToArray();
+ }
+
+ public override string ToString()
+ {
+ return Frames.Select(f => f.ToString()).JoinStrings(" <- ");
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Extender.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Extender.cs
new file mode 100755
index 0000000..b21164f
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Extender.cs
@@ -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 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 WhereExtenderMatches(this IEnumerable extenders, Context selection) {
+ var selectionElements = selection.SelectMany(selectors => selectors.SelectMany(s => s.Elements)).ToList();
+
+ return extenders.Where(e => e.ElementListMatches(selectionElements));
+ }
+
+ ///
+ /// Tests whether or not this extender matches the selector elements in
+ /// by checking if the elements in are a subsequence of the ones in
+ /// .
+ ///
+ /// 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.
+ ///
+ private static bool ElementListMatches(this PartialExtender extender, IList 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 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();
+ 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> {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> selectorPath) {
+ var context = GenerateExtenderSelector(selectorPath);
+ return new Selector(new[] {new Element(null, context.ToCss(env)) }) { IsReference = IsReference };
+ }
+
+ private Context GenerateExtenderSelector(List> selectorStack) {
+ if (!selectorStack.Any()) {
+ return null;
+ }
+
+ var parentContext = GenerateExtenderSelector(selectorStack.Skip(1).ToList());
+
+ var childContext = new Context();
+ childContext.AppendSelectors(parentContext, selectorStack.First());
+ return childContext;
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/INodeProvider.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/INodeProvider.cs
new file mode 100755
index 0000000..9fdf21b
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/INodeProvider.cs
@@ -0,0 +1,64 @@
+namespace dotless.Core.Parser.Infrastructure
+{
+ using System.Collections.Generic;
+ using Importers;
+ using Nodes;
+ using Tree;
+
+ public interface INodeProvider
+ {
+ Element Element(Combinator combinator, Node Value, NodeLocation location);
+ Combinator Combinator(string value, NodeLocation location);
+ Selector Selector(NodeList elements, NodeLocation location);
+ Rule Rule(string name, Node value, NodeLocation location);
+ Rule Rule(string name, Node value, bool variadic, NodeLocation location);
+ Ruleset Ruleset(NodeList selectors, NodeList rules, NodeLocation location);
+
+ CssFunction CssFunction(string name, Node value, NodeLocation location);
+
+ //entities
+ Alpha Alpha(Node value, NodeLocation location);
+ Call Call(string name, NodeList arguments, NodeLocation location);
+ Color Color(string rgb, NodeLocation location);
+ Keyword Keyword(string value, NodeLocation location);
+ Number Number(string value, string unit, NodeLocation location);
+ Shorthand Shorthand(Node first, Node second, NodeLocation location);
+ Variable Variable(string name, NodeLocation location);
+ Url Url(Node value, IImporter importer, NodeLocation location);
+ Script Script(string script, NodeLocation location);
+ Paren Paren(Node node, NodeLocation location);
+
+ GuardedRuleset GuardedRuleset(NodeList selectors, NodeList rules, Condition condition, NodeLocation location);
+
+ //mixins
+ MixinCall MixinCall(NodeList elements, List arguments, bool important, NodeLocation location);
+ MixinDefinition MixinDefinition(string name, NodeList parameters, NodeList rules, Condition condition, bool variadic, NodeLocation location);
+ Condition Condition(Node left, string operation, Node right, bool negate, NodeLocation location);
+
+ //directives
+ Import Import(Url path, Value features, ImportOptions option, NodeLocation location);
+ Import Import(Quoted path, Value features, ImportOptions option, NodeLocation location);
+ Directive Directive(string name, string identifier, NodeList rules, NodeLocation location);
+ Directive Directive(string name, Node value, NodeLocation location);
+ Media Media(NodeList rules, Value features, NodeLocation location);
+ KeyFrame KeyFrame(NodeList identifier, NodeList rules, NodeLocation location);
+
+ //expressions
+ Expression Expression(NodeList expression, NodeLocation location);
+ #if CSS3EXPERIMENTAL
+ RepeatEntity RepeatEntity(Node value, Node repeatCount, int index);
+#endif
+ Value Value(IEnumerable values, string important, NodeLocation location);
+ Operation Operation(string operation, Node left, Node right, NodeLocation location);
+ Assignment Assignment(string key, Node value, NodeLocation location);
+
+ //text
+ Comment Comment(string value, NodeLocation location);
+ TextNode TextNode(string contents, NodeLocation location);
+ Quoted Quoted(string value, string contents, bool escaped, NodeLocation location);
+
+ //extenders
+ Extend Extend(List exact, List partial, NodeLocation location);
+ Node Attribute(Node key, Node op, Node val, NodeLocation location);
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/IOperable.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/IOperable.cs
new file mode 100755
index 0000000..843c55e
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/IOperable.cs
@@ -0,0 +1,11 @@
+namespace dotless.Core.Parser.Infrastructure
+{
+ using Nodes;
+ using Tree;
+
+ public interface IOperable
+ {
+ Node Operate(Operation op, Node other);
+ Color ToColor();
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/MimeTypeLookup.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/MimeTypeLookup.cs
new file mode 100755
index 0000000..5cd0f7c
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/MimeTypeLookup.cs
@@ -0,0 +1,1302 @@
+using System;
+using System.Collections.Generic;
+
+namespace dotless.Core.Parser.Infrastructure
+{
+ // Source: https://gist.github.com/ChristianWeyer/eea2cb567932e345cdfa
+ // If this project is moved to .NET 4.5, this can be replaced with System.Web.MimeMapping
+ // http://msdn.microsoft.com/en-us/library/system.web.mimemapping.getmimemapping.aspx
+ internal class MimeTypeLookup
+ {
+ public string ByFilename(string filename)
+ {
+ var extension = filename.Substring(filename.LastIndexOf('.'));
+
+ string mimeType;
+ if (!String.IsNullOrEmpty(extension) && _mappings.TryGetValue(extension, out mimeType))
+ return mimeType;
+
+ return null;
+ }
+
+ #region MimeTypes
+ private static readonly Dictionary _mappings = new Dictionary(2000, StringComparer.InvariantCultureIgnoreCase)
+ {
+ {".ez", "application/andrew-inset"},
+ {".aw", "application/applixware"},
+ {".atom", "application/atom+xml"},
+ {".atomcat", "application/atomcat+xml"},
+ {".atomsvc", "application/atomsvc+xml"},
+ {".ccxml", "application/ccxml+xml"},
+ {".cdmia", "application/cdmi-capability"},
+ {".cdmic", "application/cdmi-container"},
+ {".cdmid", "application/cdmi-domain"},
+ {".cdmio", "application/cdmi-object"},
+ {".cdmiq", "application/cdmi-queue"},
+ {".cu", "application/cu-seeme"},
+ {".davmount", "application/davmount+xml"},
+ {".dbk", "application/docbook+xml"},
+ {".dssc", "application/dssc+der"},
+ {".xdssc", "application/dssc+xml"},
+ {".ecma", "application/ecmascript"},
+ {".emma", "application/emma+xml"},
+ {".epub", "application/epub+zip"},
+ {".exi", "application/exi"},
+ {".pfr", "application/font-tdpfr"},
+ {".gml", "application/gml+xml"},
+ {".gpx", "application/gpx+xml"},
+ {".gxf", "application/gxf"},
+ {".stk", "application/hyperstudio"},
+ {".ink", "application/inkml+xml"},
+ {".inkml", "application/inkml+xml"},
+ {".ipfix", "application/ipfix"},
+ {".jar", "application/java-archive"},
+ {".ser", "application/java-serialized-object"},
+ {".class", "application/java-vm"},
+ {".js", "application/javascript"},
+ {".json", "application/json"},
+ {".jsonml", "application/jsonml+json"},
+ {".lostxml", "application/lost+xml"},
+ {".hqx", "application/mac-binhex40"},
+ {".cpt", "application/mac-compactpro"},
+ {".mads", "application/mads+xml"},
+ {".mrc", "application/marc"},
+ {".mrcx", "application/marcxml+xml"},
+ {".ma", "application/mathematica"},
+ {".nb", "application/mathematica"},
+ {".mb", "application/mathematica"},
+ {".mathml", "application/mathml+xml"},
+ {".mbox", "application/mbox"},
+ {".mscml", "application/mediaservercontrol+xml"},
+ {".metalink", "application/metalink+xml"},
+ {".meta4", "application/metalink4+xml"},
+ {".mets", "application/mets+xml"},
+ {".mods", "application/mods+xml"},
+ {".m21", "application/mp21"},
+ {".mp21", "application/mp21"},
+ {".mp4s", "application/mp4"},
+ {".doc", "application/msword"},
+ {".dot", "application/msword"},
+ {".mxf", "application/mxf"},
+ {".bin", "application/octet-stream"},
+ {".dms", "application/octet-stream"},
+ {".lrf", "application/octet-stream"},
+ {".mar", "application/octet-stream"},
+ {".so", "application/octet-stream"},
+ {".dist", "application/octet-stream"},
+ {".distz", "application/octet-stream"},
+ {".pkg", "application/octet-stream"},
+ {".bpk", "application/octet-stream"},
+ {".dump", "application/octet-stream"},
+ {".elc", "application/octet-stream"},
+ {".deploy", "application/octet-stream"},
+ {".oda", "application/oda"},
+ {".opf", "application/oebps-package+xml"},
+ {".ogx", "application/ogg"},
+ {".omdoc", "application/omdoc+xml"},
+ {".onetoc", "application/onenote"},
+ {".onetoc2", "application/onenote"},
+ {".onetmp", "application/onenote"},
+ {".onepkg", "application/onenote"},
+ {".oxps", "application/oxps"},
+ {".xer", "application/patch-ops-error+xml"},
+ {".pdf", "application/pdf"},
+ {".pgp", "application/pgp-encrypted"},
+ {".asc", "application/pgp-signature"},
+ {".sig", "application/pgp-signature"},
+ {".prf", "application/pics-rules"},
+ {".p10", "application/pkcs10"},
+ {".p7m", "application/pkcs7-mime"},
+ {".p7c", "application/pkcs7-mime"},
+ {".p7s", "application/pkcs7-signature"},
+ {".p8", "application/pkcs8"},
+ {".ac", "application/pkix-attr-cert"},
+ {".cer", "application/pkix-cert"},
+ {".crl", "application/pkix-crl"},
+ {".pkipath", "application/pkix-pkipath"},
+ {".pki", "application/pkixcmp"},
+ {".pls", "application/pls+xml"},
+ {".ai", "application/postscript"},
+ {".eps", "application/postscript"},
+ {".ps", "application/postscript"},
+ {".cww", "application/prs.cww"},
+ {".pskcxml", "application/pskc+xml"},
+ {".rdf", "application/rdf+xml"},
+ {".rif", "application/reginfo+xml"},
+ {".rnc", "application/relax-ng-compact-syntax"},
+ {".rl", "application/resource-lists+xml"},
+ {".rld", "application/resource-lists-diff+xml"},
+ {".rs", "application/rls-services+xml"},
+ {".gbr", "application/rpki-ghostbusters"},
+ {".mft", "application/rpki-manifest"},
+ {".roa", "application/rpki-roa"},
+ {".rsd", "application/rsd+xml"},
+ {".rss", "application/rss+xml"},
+ {".rtf", "application/rtf"},
+ {".sbml", "application/sbml+xml"},
+ {".scq", "application/scvp-cv-request"},
+ {".scs", "application/scvp-cv-response"},
+ {".spq", "application/scvp-vp-request"},
+ {".spp", "application/scvp-vp-response"},
+ {".sdp", "application/sdp"},
+ {".setpay", "application/set-payment-initiation"},
+ {".setreg", "application/set-registration-initiation"},
+ {".shf", "application/shf+xml"},
+ {".smi", "application/smil+xml"},
+ {".smil", "application/smil+xml"},
+ {".rq", "application/sparql-query"},
+ {".srx", "application/sparql-results+xml"},
+ {".gram", "application/srgs"},
+ {".grxml", "application/srgs+xml"},
+ {".sru", "application/sru+xml"},
+ {".ssdl", "application/ssdl+xml"},
+ {".ssml", "application/ssml+xml"},
+ {".tei", "application/tei+xml"},
+ {".teicorpus", "application/tei+xml"},
+ {".tfi", "application/thraud+xml"},
+ {".tsd", "application/timestamped-data"},
+ {".plb", "application/vnd.3gpp.pic-bw-large"},
+ {".psb", "application/vnd.3gpp.pic-bw-small"},
+ {".pvb", "application/vnd.3gpp.pic-bw-var"},
+ {".tcap", "application/vnd.3gpp2.tcap"},
+ {".pwn", "application/vnd.3m.post-it-notes"},
+ {".aso", "application/vnd.accpac.simply.aso"},
+ {".imp", "application/vnd.accpac.simply.imp"},
+ {".acu", "application/vnd.acucobol"},
+ {".atc", "application/vnd.acucorp"},
+ {".acutc", "application/vnd.acucorp"},
+ {".air", "application/vnd.adobe.air-application-installer-package+zip"},
+ {".fcdt", "application/vnd.adobe.formscentral.fcdt"},
+ {".fxp", "application/vnd.adobe.fxp"},
+ {".fxpl", "application/vnd.adobe.fxp"},
+ {".xdp", "application/vnd.adobe.xdp+xml"},
+ {".xfdf", "application/vnd.adobe.xfdf"},
+ {".ahead", "application/vnd.ahead.space"},
+ {".azf", "application/vnd.airzip.filesecure.azf"},
+ {".azs", "application/vnd.airzip.filesecure.azs"},
+ {".azw", "application/vnd.amazon.ebook"},
+ {".acc", "application/vnd.americandynamics.acc"},
+ {".ami", "application/vnd.amiga.ami"},
+ {".apk", "application/vnd.android.package-archive"},
+ {".cii", "application/vnd.anser-web-certificate-issue-initiation"},
+ {".fti", "application/vnd.anser-web-funds-transfer-initiation"},
+ {".atx", "application/vnd.antix.game-component"},
+ {".mpkg", "application/vnd.apple.installer+xml"},
+ {".m3u8", "application/vnd.apple.mpegurl"},
+ {".swi", "application/vnd.aristanetworks.swi"},
+ {".iota", "application/vnd.astraea-software.iota"},
+ {".aep", "application/vnd.audiograph"},
+ {".mpm", "application/vnd.blueice.multipass"},
+ {".bmi", "application/vnd.bmi"},
+ {".rep", "application/vnd.businessobjects"},
+ {".cdxml", "application/vnd.chemdraw+xml"},
+ {".mmd", "application/vnd.chipnuts.karaoke-mmd"},
+ {".cdy", "application/vnd.cinderella"},
+ {".cla", "application/vnd.claymore"},
+ {".rp9", "application/vnd.cloanto.rp9"},
+ {".c4g", "application/vnd.clonk.c4group"},
+ {".c4d", "application/vnd.clonk.c4group"},
+ {".c4f", "application/vnd.clonk.c4group"},
+ {".c4p", "application/vnd.clonk.c4group"},
+ {".c4u", "application/vnd.clonk.c4group"},
+ {".c11amc", "application/vnd.cluetrust.cartomobile-config"},
+ {".c11amz", "application/vnd.cluetrust.cartomobile-config-pkg"},
+ {".csp", "application/vnd.commonspace"},
+ {".cdbcmsg", "application/vnd.contact.cmsg"},
+ {".cmc", "application/vnd.cosmocaller"},
+ {".clkx", "application/vnd.crick.clicker"},
+ {".clkk", "application/vnd.crick.clicker.keyboard"},
+ {".clkp", "application/vnd.crick.clicker.palette"},
+ {".clkt", "application/vnd.crick.clicker.template"},
+ {".clkw", "application/vnd.crick.clicker.wordbank"},
+ {".wbs", "application/vnd.criticaltools.wbs+xml"},
+ {".pml", "application/vnd.ctc-posml"},
+ {".ppd", "application/vnd.cups-ppd"},
+ {".car", "application/vnd.curl.car"},
+ {".pcurl", "application/vnd.curl.pcurl"},
+ {".dart", "application/vnd.dart"},
+ {".rdz", "application/vnd.data-vision.rdz"},
+ {".uvf", "application/vnd.dece.data"},
+ {".uvvf", "application/vnd.dece.data"},
+ {".uvd", "application/vnd.dece.data"},
+ {".uvvd", "application/vnd.dece.data"},
+ {".uvt", "application/vnd.dece.ttml+xml"},
+ {".uvvt", "application/vnd.dece.ttml+xml"},
+ {".uvx", "application/vnd.dece.unspecified"},
+ {".uvvx", "application/vnd.dece.unspecified"},
+ {".uvz", "application/vnd.dece.zip"},
+ {".uvvz", "application/vnd.dece.zip"},
+ {".fe_launch", "application/vnd.denovo.fcselayout-link"},
+ {".dna", "application/vnd.dna"},
+ {".mlp", "application/vnd.dolby.mlp"},
+ {".dpg", "application/vnd.dpgraph"},
+ {".dfac", "application/vnd.dreamfactory"},
+ {".kpxx", "application/vnd.ds-keypoint"},
+ {".ait", "application/vnd.dvb.ait"},
+ {".svc", "application/vnd.dvb.service"},
+ {".geo", "application/vnd.dynageo"},
+ {".mag", "application/vnd.ecowin.chart"},
+ {".nml", "application/vnd.enliven"},
+ {".esf", "application/vnd.epson.esf"},
+ {".msf", "application/vnd.epson.msf"},
+ {".qam", "application/vnd.epson.quickanime"},
+ {".slt", "application/vnd.epson.salt"},
+ {".ssf", "application/vnd.epson.ssf"},
+ {".es3", "application/vnd.eszigno3+xml"},
+ {".et3", "application/vnd.eszigno3+xml"},
+ {".ez2", "application/vnd.ezpix-album"},
+ {".ez3", "application/vnd.ezpix-package"},
+ {".fdf", "application/vnd.fdf"},
+ {".mseed", "application/vnd.fdsn.mseed"},
+ {".seed", "application/vnd.fdsn.seed"},
+ {".dataless", "application/vnd.fdsn.seed"},
+ {".gph", "application/vnd.flographit"},
+ {".ftc", "application/vnd.fluxtime.clip"},
+ {".fm", "application/vnd.framemaker"},
+ {".frame", "application/vnd.framemaker"},
+ {".maker", "application/vnd.framemaker"},
+ {".book", "application/vnd.framemaker"},
+ {".fnc", "application/vnd.frogans.fnc"},
+ {".ltf", "application/vnd.frogans.ltf"},
+ {".fsc", "application/vnd.fsc.weblaunch"},
+ {".oas", "application/vnd.fujitsu.oasys"},
+ {".oa2", "application/vnd.fujitsu.oasys2"},
+ {".oa3", "application/vnd.fujitsu.oasys3"},
+ {".fg5", "application/vnd.fujitsu.oasysgp"},
+ {".bh2", "application/vnd.fujitsu.oasysprs"},
+ {".ddd", "application/vnd.fujixerox.ddd"},
+ {".xdw", "application/vnd.fujixerox.docuworks"},
+ {".xbd", "application/vnd.fujixerox.docuworks.binder"},
+ {".fzs", "application/vnd.fuzzysheet"},
+ {".txd", "application/vnd.genomatix.tuxedo"},
+ {".ggb", "application/vnd.geogebra.file"},
+ {".ggt", "application/vnd.geogebra.tool"},
+ {".gex", "application/vnd.geometry-explorer"},
+ {".gre", "application/vnd.geometry-explorer"},
+ {".gxt", "application/vnd.geonext"},
+ {".g2w", "application/vnd.geoplan"},
+ {".g3w", "application/vnd.geospace"},
+ {".gmx", "application/vnd.gmx"},
+ {".kml", "application/vnd.google-earth.kml+xml"},
+ {".kmz", "application/vnd.google-earth.kmz"},
+ {".gqf", "application/vnd.grafeq"},
+ {".gqs", "application/vnd.grafeq"},
+ {".gac", "application/vnd.groove-account"},
+ {".ghf", "application/vnd.groove-help"},
+ {".gim", "application/vnd.groove-identity-message"},
+ {".grv", "application/vnd.groove-injector"},
+ {".gtm", "application/vnd.groove-tool-message"},
+ {".tpl", "application/vnd.groove-tool-template"},
+ {".vcg", "application/vnd.groove-vcard"},
+ {".hal", "application/vnd.hal+xml"},
+ {".zmm", "application/vnd.handheld-entertainment+xml"},
+ {".hbci", "application/vnd.hbci"},
+ {".les", "application/vnd.hhe.lesson-player"},
+ {".hpgl", "application/vnd.hp-hpgl"},
+ {".hpid", "application/vnd.hp-hpid"},
+ {".hps", "application/vnd.hp-hps"},
+ {".jlt", "application/vnd.hp-jlyt"},
+ {".pcl", "application/vnd.hp-pcl"},
+ {".pclxl", "application/vnd.hp-pclxl"},
+ {".sfd-hdstx", "application/vnd.hydrostatix.sof-data"},
+ {".mpy", "application/vnd.ibm.minipay"},
+ {".afp", "application/vnd.ibm.modcap"},
+ {".listafp", "application/vnd.ibm.modcap"},
+ {".list3820", "application/vnd.ibm.modcap"},
+ {".irm", "application/vnd.ibm.rights-management"},
+ {".sc", "application/vnd.ibm.secure-container"},
+ {".icc", "application/vnd.iccprofile"},
+ {".icm", "application/vnd.iccprofile"},
+ {".igl", "application/vnd.igloader"},
+ {".ivp", "application/vnd.immervision-ivp"},
+ {".ivu", "application/vnd.immervision-ivu"},
+ {".igm", "application/vnd.insors.igm"},
+ {".xpw", "application/vnd.intercon.formnet"},
+ {".xpx", "application/vnd.intercon.formnet"},
+ {".i2g", "application/vnd.intergeo"},
+ {".qbo", "application/vnd.intu.qbo"},
+ {".qfx", "application/vnd.intu.qfx"},
+ {".rcprofile", "application/vnd.ipunplugged.rcprofile"},
+ {".irp", "application/vnd.irepository.package+xml"},
+ {".xpr", "application/vnd.is-xpr"},
+ {".fcs", "application/vnd.isac.fcs"},
+ {".jam", "application/vnd.jam"},
+ {".rms", "application/vnd.jcp.javame.midlet-rms"},
+ {".jisp", "application/vnd.jisp"},
+ {".joda", "application/vnd.joost.joda-archive"},
+ {".ktz", "application/vnd.kahootz"},
+ {".ktr", "application/vnd.kahootz"},
+ {".karbon", "application/vnd.kde.karbon"},
+ {".chrt", "application/vnd.kde.kchart"},
+ {".kfo", "application/vnd.kde.kformula"},
+ {".flw", "application/vnd.kde.kivio"},
+ {".kon", "application/vnd.kde.kontour"},
+ {".kpr", "application/vnd.kde.kpresenter"},
+ {".kpt", "application/vnd.kde.kpresenter"},
+ {".ksp", "application/vnd.kde.kspread"},
+ {".kwd", "application/vnd.kde.kword"},
+ {".kwt", "application/vnd.kde.kword"},
+ {".htke", "application/vnd.kenameaapp"},
+ {".kia", "application/vnd.kidspiration"},
+ {".kne", "application/vnd.kinar"},
+ {".knp", "application/vnd.kinar"},
+ {".skp", "application/vnd.koan"},
+ {".skd", "application/vnd.koan"},
+ {".skt", "application/vnd.koan"},
+ {".skm", "application/vnd.koan"},
+ {".sse", "application/vnd.kodak-descriptor"},
+ {".lasxml", "application/vnd.las.las+xml"},
+ {".lbd", "application/vnd.llamagraphics.life-balance.desktop"},
+ {".lbe", "application/vnd.llamagraphics.life-balance.exchange+xml"},
+ {".123", "application/vnd.lotus-1-2-3"},
+ {".apr", "application/vnd.lotus-approach"},
+ {".pre", "application/vnd.lotus-freelance"},
+ {".nsf", "application/vnd.lotus-notes"},
+ {".org", "application/vnd.lotus-organizer"},
+ {".scm", "application/vnd.lotus-screencam"},
+ {".lwp", "application/vnd.lotus-wordpro"},
+ {".portpkg", "application/vnd.macports.portpkg"},
+ {".mcd", "application/vnd.mcd"},
+ {".mc1", "application/vnd.medcalcdata"},
+ {".cdkey", "application/vnd.mediastation.cdkey"},
+ {".mwf", "application/vnd.mfer"},
+ {".mfm", "application/vnd.mfmp"},
+ {".flo", "application/vnd.micrografx.flo"},
+ {".igx", "application/vnd.micrografx.igx"},
+ {".mif", "application/vnd.mif"},
+ {".daf", "application/vnd.mobius.daf"},
+ {".dis", "application/vnd.mobius.dis"},
+ {".mbk", "application/vnd.mobius.mbk"},
+ {".mqy", "application/vnd.mobius.mqy"},
+ {".msl", "application/vnd.mobius.msl"},
+ {".plc", "application/vnd.mobius.plc"},
+ {".txf", "application/vnd.mobius.txf"},
+ {".mpn", "application/vnd.mophun.application"},
+ {".mpc", "application/vnd.mophun.certificate"},
+ {".xul", "application/vnd.mozilla.xul+xml"},
+ {".cil", "application/vnd.ms-artgalry"},
+ {".cab", "application/vnd.ms-cab-compressed"},
+ {".xls", "application/vnd.ms-excel"},
+ {".xlm", "application/vnd.ms-excel"},
+ {".xla", "application/vnd.ms-excel"},
+ {".xlc", "application/vnd.ms-excel"},
+ {".xlt", "application/vnd.ms-excel"},
+ {".xlw", "application/vnd.ms-excel"},
+ {".xlam", "application/vnd.ms-excel.addin.macroenabled.12"},
+ {".xlsb", "application/vnd.ms-excel.sheet.binary.macroenabled.12"},
+ {".xlsm", "application/vnd.ms-excel.sheet.macroenabled.12"},
+ {".xltm", "application/vnd.ms-excel.template.macroenabled.12"},
+ {".eot", "application/vnd.ms-fontobject"},
+ {".chm", "application/vnd.ms-htmlhelp"},
+ {".ims", "application/vnd.ms-ims"},
+ {".lrm", "application/vnd.ms-lrm"},
+ {".thmx", "application/vnd.ms-officetheme"},
+ {".cat", "application/vnd.ms-pki.seccat"},
+ {".stl", "application/vnd.ms-pki.stl"},
+ {".ppt", "application/vnd.ms-powerpoint"},
+ {".pps", "application/vnd.ms-powerpoint"},
+ {".pot", "application/vnd.ms-powerpoint"},
+ {".ppam", "application/vnd.ms-powerpoint.addin.macroenabled.12"},
+ {".pptm", "application/vnd.ms-powerpoint.presentation.macroenabled.12"},
+ {".sldm", "application/vnd.ms-powerpoint.slide.macroenabled.12"},
+ {".ppsm", "application/vnd.ms-powerpoint.slideshow.macroenabled.12"},
+ {".potm", "application/vnd.ms-powerpoint.template.macroenabled.12"},
+ {".mpp", "application/vnd.ms-project"},
+ {".mpt", "application/vnd.ms-project"},
+ {".docm", "application/vnd.ms-word.document.macroenabled.12"},
+ {".dotm", "application/vnd.ms-word.template.macroenabled.12"},
+ {".wps", "application/vnd.ms-works"},
+ {".wks", "application/vnd.ms-works"},
+ {".wcm", "application/vnd.ms-works"},
+ {".wdb", "application/vnd.ms-works"},
+ {".wpl", "application/vnd.ms-wpl"},
+ {".xps", "application/vnd.ms-xpsdocument"},
+ {".mseq", "application/vnd.mseq"},
+ {".mus", "application/vnd.musician"},
+ {".msty", "application/vnd.muvee.style"},
+ {".taglet", "application/vnd.mynfc"},
+ {".nlu", "application/vnd.neurolanguage.nlu"},
+ {".ntf", "application/vnd.nitf"},
+ {".nitf", "application/vnd.nitf"},
+ {".nnd", "application/vnd.noblenet-directory"},
+ {".nns", "application/vnd.noblenet-sealer"},
+ {".nnw", "application/vnd.noblenet-web"},
+ {".ngdat", "application/vnd.nokia.n-gage.data"},
+ {".n-gage", "application/vnd.nokia.n-gage.symbian.install"},
+ {".rpst", "application/vnd.nokia.radio-preset"},
+ {".rpss", "application/vnd.nokia.radio-presets"},
+ {".edm", "application/vnd.novadigm.edm"},
+ {".edx", "application/vnd.novadigm.edx"},
+ {".ext", "application/vnd.novadigm.ext"},
+ {".odc", "application/vnd.oasis.opendocument.chart"},
+ {".otc", "application/vnd.oasis.opendocument.chart-template"},
+ {".odb", "application/vnd.oasis.opendocument.database"},
+ {".odf", "application/vnd.oasis.opendocument.formula"},
+ {".odft", "application/vnd.oasis.opendocument.formula-template"},
+ {".odg", "application/vnd.oasis.opendocument.graphics"},
+ {".otg", "application/vnd.oasis.opendocument.graphics-template"},
+ {".odi", "application/vnd.oasis.opendocument.image"},
+ {".oti", "application/vnd.oasis.opendocument.image-template"},
+ {".odp", "application/vnd.oasis.opendocument.presentation"},
+ {".otp", "application/vnd.oasis.opendocument.presentation-template"},
+ {".ods", "application/vnd.oasis.opendocument.spreadsheet"},
+ {".ots", "application/vnd.oasis.opendocument.spreadsheet-template"},
+ {".odt", "application/vnd.oasis.opendocument.text"},
+ {".odm", "application/vnd.oasis.opendocument.text-master"},
+ {".ott", "application/vnd.oasis.opendocument.text-template"},
+ {".oth", "application/vnd.oasis.opendocument.text-web"},
+ {".xo", "application/vnd.olpc-sugar"},
+ {".dd2", "application/vnd.oma.dd2+xml"},
+ {".oxt", "application/vnd.openofficeorg.extension"},
+ {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
+ {".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"},
+ {".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
+ {".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"},
+ {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
+ {".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"},
+ {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
+ {".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
+ {".mgp", "application/vnd.osgeo.mapguide.package"},
+ {".dp", "application/vnd.osgi.dp"},
+ {".esa", "application/vnd.osgi.subsystem"},
+ {".pdb", "application/vnd.palm"},
+ {".pqa", "application/vnd.palm"},
+ {".oprc", "application/vnd.palm"},
+ {".paw", "application/vnd.pawaafile"},
+ {".str", "application/vnd.pg.format"},
+ {".ei6", "application/vnd.pg.osasli"},
+ {".efif", "application/vnd.picsel"},
+ {".wg", "application/vnd.pmi.widget"},
+ {".plf", "application/vnd.pocketlearn"},
+ {".pbd", "application/vnd.powerbuilder6"},
+ {".box", "application/vnd.previewsystems.box"},
+ {".mgz", "application/vnd.proteus.magazine"},
+ {".qps", "application/vnd.publishare-delta-tree"},
+ {".ptid", "application/vnd.pvi.ptid1"},
+ {".qxd", "application/vnd.quark.quarkxpress"},
+ {".qxt", "application/vnd.quark.quarkxpress"},
+ {".qwd", "application/vnd.quark.quarkxpress"},
+ {".qwt", "application/vnd.quark.quarkxpress"},
+ {".qxl", "application/vnd.quark.quarkxpress"},
+ {".qxb", "application/vnd.quark.quarkxpress"},
+ {".bed", "application/vnd.realvnc.bed"},
+ {".mxl", "application/vnd.recordare.musicxml"},
+ {".musicxml", "application/vnd.recordare.musicxml+xml"},
+ {".cryptonote", "application/vnd.rig.cryptonote"},
+ {".cod", "application/vnd.rim.cod"},
+ {".rm", "application/vnd.rn-realmedia"},
+ {".rmvb", "application/vnd.rn-realmedia-vbr"},
+ {".link66", "application/vnd.route66.link66+xml"},
+ {".st", "application/vnd.sailingtracker.track"},
+ {".see", "application/vnd.seemail"},
+ {".sema", "application/vnd.sema"},
+ {".semd", "application/vnd.semd"},
+ {".semf", "application/vnd.semf"},
+ {".ifm", "application/vnd.shana.informed.formdata"},
+ {".itp", "application/vnd.shana.informed.formtemplate"},
+ {".iif", "application/vnd.shana.informed.interchange"},
+ {".ipk", "application/vnd.shana.informed.package"},
+ {".twd", "application/vnd.simtech-mindmapper"},
+ {".twds", "application/vnd.simtech-mindmapper"},
+ {".mmf", "application/vnd.smaf"},
+ {".teacher", "application/vnd.smart.teacher"},
+ {".sdkm", "application/vnd.solent.sdkm+xml"},
+ {".sdkd", "application/vnd.solent.sdkm+xml"},
+ {".dxp", "application/vnd.spotfire.dxp"},
+ {".sfs", "application/vnd.spotfire.sfs"},
+ {".sdc", "application/vnd.stardivision.calc"},
+ {".sda", "application/vnd.stardivision.draw"},
+ {".sdd", "application/vnd.stardivision.impress"},
+ {".smf", "application/vnd.stardivision.math"},
+ {".sdw", "application/vnd.stardivision.writer"},
+ {".vor", "application/vnd.stardivision.writer"},
+ {".sgl", "application/vnd.stardivision.writer-global"},
+ {".smzip", "application/vnd.stepmania.package"},
+ {".sm", "application/vnd.stepmania.stepchart"},
+ {".sxc", "application/vnd.sun.xml.calc"},
+ {".stc", "application/vnd.sun.xml.calc.template"},
+ {".sxd", "application/vnd.sun.xml.draw"},
+ {".std", "application/vnd.sun.xml.draw.template"},
+ {".sxi", "application/vnd.sun.xml.impress"},
+ {".sti", "application/vnd.sun.xml.impress.template"},
+ {".sxm", "application/vnd.sun.xml.math"},
+ {".sxw", "application/vnd.sun.xml.writer"},
+ {".sxg", "application/vnd.sun.xml.writer.global"},
+ {".stw", "application/vnd.sun.xml.writer.template"},
+ {".sus", "application/vnd.sus-calendar"},
+ {".susp", "application/vnd.sus-calendar"},
+ {".svd", "application/vnd.svd"},
+ {".sis", "application/vnd.symbian.install"},
+ {".sisx", "application/vnd.symbian.install"},
+ {".xsm", "application/vnd.syncml+xml"},
+ {".bdm", "application/vnd.syncml.dm+wbxml"},
+ {".xdm", "application/vnd.syncml.dm+xml"},
+ {".tao", "application/vnd.tao.intent-module-archive"},
+ {".pcap", "application/vnd.tcpdump.pcap"},
+ {".cap", "application/vnd.tcpdump.pcap"},
+ {".dmp", "application/vnd.tcpdump.pcap"},
+ {".tmo", "application/vnd.tmobile-livetv"},
+ {".tpt", "application/vnd.trid.tpt"},
+ {".mxs", "application/vnd.triscape.mxs"},
+ {".tra", "application/vnd.trueapp"},
+ {".ufd", "application/vnd.ufdl"},
+ {".ufdl", "application/vnd.ufdl"},
+ {".utz", "application/vnd.uiq.theme"},
+ {".umj", "application/vnd.umajin"},
+ {".unityweb", "application/vnd.unity"},
+ {".uoml", "application/vnd.uoml+xml"},
+ {".vcx", "application/vnd.vcx"},
+ {".vsd", "application/vnd.visio"},
+ {".vst", "application/vnd.visio"},
+ {".vss", "application/vnd.visio"},
+ {".vsw", "application/vnd.visio"},
+ {".vis", "application/vnd.visionary"},
+ {".vsf", "application/vnd.vsf"},
+ {".wbxml", "application/vnd.wap.wbxml"},
+ {".wmlc", "application/vnd.wap.wmlc"},
+ {".wmlsc", "application/vnd.wap.wmlscriptc"},
+ {".wtb", "application/vnd.webturbo"},
+ {".nbp", "application/vnd.wolfram.player"},
+ {".wpd", "application/vnd.wordperfect"},
+ {".wqd", "application/vnd.wqd"},
+ {".stf", "application/vnd.wt.stf"},
+ {".xar", "application/vnd.xara"},
+ {".xfdl", "application/vnd.xfdl"},
+ {".hvd", "application/vnd.yamaha.hv-dic"},
+ {".hvs", "application/vnd.yamaha.hv-script"},
+ {".hvp", "application/vnd.yamaha.hv-voice"},
+ {".osf", "application/vnd.yamaha.openscoreformat"},
+ {".osfpvg", "application/vnd.yamaha.openscoreformat.osfpvg+xml"},
+ {".saf", "application/vnd.yamaha.smaf-audio"},
+ {".spf", "application/vnd.yamaha.smaf-phrase"},
+ {".cmp", "application/vnd.yellowriver-custom-menu"},
+ {".zir", "application/vnd.zul"},
+ {".zirz", "application/vnd.zul"},
+ {".zaz", "application/vnd.zzazz.deck+xml"},
+ {".vxml", "application/voicexml+xml"},
+ {".wgt", "application/widget"},
+ {".hlp", "application/winhlp"},
+ {".wsdl", "application/wsdl+xml"},
+ {".wspolicy", "application/wspolicy+xml"},
+ {".7z", "application/x-7z-compressed"},
+ {".abw", "application/x-abiword"},
+ {".ace", "application/x-ace-compressed"},
+ {".dmg", "application/x-apple-diskimage"},
+ {".aab", "application/x-authorware-bin"},
+ {".x32", "application/x-authorware-bin"},
+ {".u32", "application/x-authorware-bin"},
+ {".vox", "application/x-authorware-bin"},
+ {".aam", "application/x-authorware-map"},
+ {".aas", "application/x-authorware-seg"},
+ {".bcpio", "application/x-bcpio"},
+ {".torrent", "application/x-bittorrent"},
+ {".blb", "application/x-blorb"},
+ {".blorb", "application/x-blorb"},
+ {".bz", "application/x-bzip"},
+ {".bz2", "application/x-bzip2"},
+ {".boz", "application/x-bzip2"},
+ {".cbr", "application/x-cbr"},
+ {".cba", "application/x-cbr"},
+ {".cbt", "application/x-cbr"},
+ {".cbz", "application/x-cbr"},
+ {".cb7", "application/x-cbr"},
+ {".vcd", "application/x-cdlink"},
+ {".cfs", "application/x-cfs-compressed"},
+ {".chat", "application/x-chat"},
+ {".pgn", "application/x-chess-pgn"},
+ {".nsc", "application/x-conference"},
+ {".cpio", "application/x-cpio"},
+ {".csh", "application/x-csh"},
+ {".deb", "application/x-debian-package"},
+ {".udeb", "application/x-debian-package"},
+ {".dgc", "application/x-dgc-compressed"},
+ {".dir", "application/x-director"},
+ {".dcr", "application/x-director"},
+ {".dxr", "application/x-director"},
+ {".cst", "application/x-director"},
+ {".cct", "application/x-director"},
+ {".cxt", "application/x-director"},
+ {".w3d", "application/x-director"},
+ {".fgd", "application/x-director"},
+ {".swa", "application/x-director"},
+ {".wad", "application/x-doom"},
+ {".ncx", "application/x-dtbncx+xml"},
+ {".dtb", "application/x-dtbook+xml"},
+ {".res", "application/x-dtbresource+xml"},
+ {".dvi", "application/x-dvi"},
+ {".evy", "application/x-envoy"},
+ {".eva", "application/x-eva"},
+ {".bdf", "application/x-font-bdf"},
+ {".gsf", "application/x-font-ghostscript"},
+ {".psf", "application/x-font-linux-psf"},
+ {".otf", "application/x-font-otf"},
+ {".pcf", "application/x-font-pcf"},
+ {".snf", "application/x-font-snf"},
+ {".ttf", "application/x-font-ttf"},
+ {".ttc", "application/x-font-ttf"},
+ {".pfa", "application/x-font-type1"},
+ {".pfb", "application/x-font-type1"},
+ {".pfm", "application/x-font-type1"},
+ {".afm", "application/x-font-type1"},
+ {".woff", "application/font-woff"},
+ {".arc", "application/x-freearc"},
+ {".spl", "application/x-futuresplash"},
+ {".gca", "application/x-gca-compressed"},
+ {".ulx", "application/x-glulx"},
+ {".gnumeric", "application/x-gnumeric"},
+ {".gramps", "application/x-gramps-xml"},
+ {".gtar", "application/x-gtar"},
+ {".hdf", "application/x-hdf"},
+ {".install", "application/x-install-instructions"},
+ {".iso", "application/x-iso9660-image"},
+ {".jnlp", "application/x-java-jnlp-file"},
+ {".latex", "application/x-latex"},
+ {".lzh", "application/x-lzh-compressed"},
+ {".lha", "application/x-lzh-compressed"},
+ {".mie", "application/x-mie"},
+ {".prc", "application/x-mobipocket-ebook"},
+ {".mobi", "application/x-mobipocket-ebook"},
+ {".application", "application/x-ms-application"},
+ {".lnk", "application/x-ms-shortcut"},
+ {".wmd", "application/x-ms-wmd"},
+ {".wmz", "application/x-ms-wmz"},
+ {".xbap", "application/x-ms-xbap"},
+ {".mdb", "application/x-msaccess"},
+ {".obd", "application/x-msbinder"},
+ {".crd", "application/x-mscardfile"},
+ {".clp", "application/x-msclip"},
+ {".exe", "application/x-msdownload"},
+ {".dll", "application/x-msdownload"},
+ {".com", "application/x-msdownload"},
+ {".bat", "application/x-msdownload"},
+ {".msi", "application/x-msdownload"},
+ {".mvb", "application/x-msmediaview"},
+ {".m13", "application/x-msmediaview"},
+ {".m14", "application/x-msmediaview"},
+ {".wmf", "application/x-msmetafile"},
+ {".emf", "application/x-msmetafile"},
+ {".emz", "application/x-msmetafile"},
+ {".mny", "application/x-msmoney"},
+ {".pub", "application/x-mspublisher"},
+ {".scd", "application/x-msschedule"},
+ {".trm", "application/x-msterminal"},
+ {".wri", "application/x-mswrite"},
+ {".nc", "application/x-netcdf"},
+ {".cdf", "application/x-netcdf"},
+ {".nzb", "application/x-nzb"},
+ {".p12", "application/x-pkcs12"},
+ {".pfx", "application/x-pkcs12"},
+ {".p7b", "application/x-pkcs7-certificates"},
+ {".spc", "application/x-pkcs7-certificates"},
+ {".p7r", "application/x-pkcs7-certreqresp"},
+ {".rar", "application/x-rar-compressed"},
+ {".ris", "application/x-research-info-systems"},
+ {".sh", "application/x-sh"},
+ {".shar", "application/x-shar"},
+ {".swf", "application/x-shockwave-flash"},
+ {".xap", "application/x-silverlight-app"},
+ {".sql", "application/x-sql"},
+ {".sit", "application/x-stuffit"},
+ {".sitx", "application/x-stuffitx"},
+ {".srt", "application/x-subrip"},
+ {".sv4cpio", "application/x-sv4cpio"},
+ {".sv4crc", "application/x-sv4crc"},
+ {".t3", "application/x-t3vm-image"},
+ {".gam", "application/x-tads"},
+ {".tar", "application/x-tar"},
+ {".tcl", "application/x-tcl"},
+ {".tex", "application/x-tex"},
+ {".tfm", "application/x-tex-tfm"},
+ {".texinfo", "application/x-texinfo"},
+ {".texi", "application/x-texinfo"},
+ {".obj", "application/x-tgif"},
+ {".ustar", "application/x-ustar"},
+ {".src", "application/x-wais-source"},
+ {".der", "application/x-x509-ca-cert"},
+ {".crt", "application/x-x509-ca-cert"},
+ {".fig", "application/x-xfig"},
+ {".xlf", "application/x-xliff+xml"},
+ {".xpi", "application/x-xpinstall"},
+ {".xz", "application/x-xz"},
+ {".z1", "application/x-zmachine"},
+ {".z2", "application/x-zmachine"},
+ {".z3", "application/x-zmachine"},
+ {".z4", "application/x-zmachine"},
+ {".z5", "application/x-zmachine"},
+ {".z6", "application/x-zmachine"},
+ {".z7", "application/x-zmachine"},
+ {".z8", "application/x-zmachine"},
+ {".xaml", "application/xaml+xml"},
+ {".xdf", "application/xcap-diff+xml"},
+ {".xenc", "application/xenc+xml"},
+ {".xhtml", "application/xhtml+xml"},
+ {".xht", "application/xhtml+xml"},
+ {".xml", "application/xml"},
+ {".xsl", "application/xml"},
+ {".dtd", "application/xml-dtd"},
+ {".xop", "application/xop+xml"},
+ {".xpl", "application/xproc+xml"},
+ {".xslt", "application/xslt+xml"},
+ {".xspf", "application/xspf+xml"},
+ {".mxml", "application/xv+xml"},
+ {".xhvml", "application/xv+xml"},
+ {".xvml", "application/xv+xml"},
+ {".xvm", "application/xv+xml"},
+ {".yang", "application/yang"},
+ {".yin", "application/yin+xml"},
+ {".zip", "application/zip"},
+ {".adp", "audio/adpcm"},
+ {".au", "audio/basic"},
+ {".snd", "audio/basic"},
+ {".mid", "audio/midi"},
+ {".midi", "audio/midi"},
+ {".kar", "audio/midi"},
+ {".rmi", "audio/midi"},
+ {".mp4a", "audio/mp4"},
+ {".mpga", "audio/mpeg"},
+ {".mp2", "audio/mpeg"},
+ {".mp2a", "audio/mpeg"},
+ {".mp3", "audio/mpeg"},
+ {".m2a", "audio/mpeg"},
+ {".m3a", "audio/mpeg"},
+ {".oga", "audio/ogg"},
+ {".ogg", "audio/ogg"},
+ {".spx", "audio/ogg"},
+ {".s3m", "audio/s3m"},
+ {".sil", "audio/silk"},
+ {".uva", "audio/vnd.dece.audio"},
+ {".uvva", "audio/vnd.dece.audio"},
+ {".eol", "audio/vnd.digital-winds"},
+ {".dra", "audio/vnd.dra"},
+ {".dts", "audio/vnd.dts"},
+ {".dtshd", "audio/vnd.dts.hd"},
+ {".lvp", "audio/vnd.lucent.voice"},
+ {".pya", "audio/vnd.ms-playready.media.pya"},
+ {".ecelp4800", "audio/vnd.nuera.ecelp4800"},
+ {".ecelp7470", "audio/vnd.nuera.ecelp7470"},
+ {".ecelp9600", "audio/vnd.nuera.ecelp9600"},
+ {".rip", "audio/vnd.rip"},
+ {".weba", "audio/webm"},
+ {".aac", "audio/x-aac"},
+ {".aif", "audio/x-aiff"},
+ {".aiff", "audio/x-aiff"},
+ {".aifc", "audio/x-aiff"},
+ {".caf", "audio/x-caf"},
+ {".flac", "audio/x-flac"},
+ {".mka", "audio/x-matroska"},
+ {".m3u", "audio/x-mpegurl"},
+ {".wax", "audio/x-ms-wax"},
+ {".wma", "audio/x-ms-wma"},
+ {".ram", "audio/x-pn-realaudio"},
+ {".ra", "audio/x-pn-realaudio"},
+ {".rmp", "audio/x-pn-realaudio-plugin"},
+ {".wav", "audio/x-wav"},
+ {".xm", "audio/xm"},
+ {".cdx", "chemical/x-cdx"},
+ {".cif", "chemical/x-cif"},
+ {".cmdf", "chemical/x-cmdf"},
+ {".cml", "chemical/x-cml"},
+ {".csml", "chemical/x-csml"},
+ {".xyz", "chemical/x-xyz"},
+ {".bmp", "image/bmp"},
+ {".cgm", "image/cgm"},
+ {".g3", "image/g3fax"},
+ {".gif", "image/gif"},
+ {".ief", "image/ief"},
+ {".jpeg", "image/jpeg"},
+ {".jpg", "image/jpeg"},
+ {".jpe", "image/jpeg"},
+ {".ktx", "image/ktx"},
+ {".png", "image/png"},
+ {".btif", "image/prs.btif"},
+ {".sgi", "image/sgi"},
+ {".svg", "image/svg+xml"},
+ {".svgz", "image/svg+xml"},
+ {".tiff", "image/tiff"},
+ {".tif", "image/tiff"},
+ {".psd", "image/vnd.adobe.photoshop"},
+ {".uvi", "image/vnd.dece.graphic"},
+ {".uvvi", "image/vnd.dece.graphic"},
+ {".uvg", "image/vnd.dece.graphic"},
+ {".uvvg", "image/vnd.dece.graphic"},
+ {".sub", "text/vnd.dvb.subtitle"},
+ {".djvu", "image/vnd.djvu"},
+ {".djv", "image/vnd.djvu"},
+ {".dwg", "image/vnd.dwg"},
+ {".dxf", "image/vnd.dxf"},
+ {".fbs", "image/vnd.fastbidsheet"},
+ {".fpx", "image/vnd.fpx"},
+ {".fst", "image/vnd.fst"},
+ {".mmr", "image/vnd.fujixerox.edmics-mmr"},
+ {".rlc", "image/vnd.fujixerox.edmics-rlc"},
+ {".mdi", "image/vnd.ms-modi"},
+ {".wdp", "image/vnd.ms-photo"},
+ {".npx", "image/vnd.net-fpx"},
+ {".wbmp", "image/vnd.wap.wbmp"},
+ {".xif", "image/vnd.xiff"},
+ {".webp", "image/webp"},
+ {".3ds", "image/x-3ds"},
+ {".ras", "image/x-cmu-raster"},
+ {".cmx", "image/x-cmx"},
+ {".fh", "image/x-freehand"},
+ {".fhc", "image/x-freehand"},
+ {".fh4", "image/x-freehand"},
+ {".fh5", "image/x-freehand"},
+ {".fh7", "image/x-freehand"},
+ {".ico", "image/x-icon"},
+ {".sid", "image/x-mrsid-image"},
+ {".pcx", "image/x-pcx"},
+ {".pic", "image/x-pict"},
+ {".pct", "image/x-pict"},
+ {".pnm", "image/x-portable-anymap"},
+ {".pbm", "image/x-portable-bitmap"},
+ {".pgm", "image/x-portable-graymap"},
+ {".ppm", "image/x-portable-pixmap"},
+ {".rgb", "image/x-rgb"},
+ {".tga", "image/x-tga"},
+ {".xbm", "image/x-xbitmap"},
+ {".xpm", "image/x-xpixmap"},
+ {".xwd", "image/x-xwindowdump"},
+ {".eml", "message/rfc822"},
+ {".mime", "message/rfc822"},
+ {".igs", "model/iges"},
+ {".iges", "model/iges"},
+ {".msh", "model/mesh"},
+ {".mesh", "model/mesh"},
+ {".silo", "model/mesh"},
+ {".dae", "model/vnd.collada+xml"},
+ {".dwf", "model/vnd.dwf"},
+ {".gdl", "model/vnd.gdl"},
+ {".gtw", "model/vnd.gtw"},
+ {".mts", "model/vnd.mts"},
+ {".vtu", "model/vnd.vtu"},
+ {".wrl", "model/vrml"},
+ {".vrml", "model/vrml"},
+ {".x3db", "model/x3d+binary"},
+ {".x3dbz", "model/x3d+binary"},
+ {".x3dv", "model/x3d+vrml"},
+ {".x3dvz", "model/x3d+vrml"},
+ {".x3d", "model/x3d+xml"},
+ {".x3dz", "model/x3d+xml"},
+ {".appcache", "text/cache-manifest"},
+ {".ics", "text/calendar"},
+ {".ifb", "text/calendar"},
+ {".css", "text/css"},
+ {".csv", "text/csv"},
+ {".html", "text/html"},
+ {".htm", "text/html"},
+ {".n3", "text/n3"},
+ {".txt", "text/plain"},
+ {".text", "text/plain"},
+ {".conf", "text/plain"},
+ {".def", "text/plain"},
+ {".list", "text/plain"},
+ {".log", "text/plain"},
+ {".in", "text/plain"},
+ {".dsc", "text/prs.lines.tag"},
+ {".rtx", "text/richtext"},
+ {".sgml", "text/sgml"},
+ {".sgm", "text/sgml"},
+ {".tsv", "text/tab-separated-values"},
+ {".t", "text/troff"},
+ {".tr", "text/troff"},
+ {".roff", "text/troff"},
+ {".man", "text/troff"},
+ {".me", "text/troff"},
+ {".ms", "text/troff"},
+ {".ttl", "text/turtle"},
+ {".uri", "text/uri-list"},
+ {".uris", "text/uri-list"},
+ {".urls", "text/uri-list"},
+ {".vcard", "text/vcard"},
+ {".curl", "text/vnd.curl"},
+ {".dcurl", "text/vnd.curl.dcurl"},
+ {".scurl", "text/vnd.curl.scurl"},
+ {".mcurl", "text/vnd.curl.mcurl"},
+ {".fly", "text/vnd.fly"},
+ {".flx", "text/vnd.fmi.flexstor"},
+ {".gv", "text/vnd.graphviz"},
+ {".3dml", "text/vnd.in3d.3dml"},
+ {".spot", "text/vnd.in3d.spot"},
+ {".jad", "text/vnd.sun.j2me.app-descriptor"},
+ {".wml", "text/vnd.wap.wml"},
+ {".wmls", "text/vnd.wap.wmlscript"},
+ {".s", "text/x-asm"},
+ {".asm", "text/x-asm"},
+ {".c", "text/x-c"},
+ {".cc", "text/x-c"},
+ {".cxx", "text/x-c"},
+ {".cpp", "text/x-c"},
+ {".h", "text/x-c"},
+ {".hh", "text/x-c"},
+ {".dic", "text/x-c"},
+ {".f", "text/x-fortran"},
+ {".for", "text/x-fortran"},
+ {".f77", "text/x-fortran"},
+ {".f90", "text/x-fortran"},
+ {".java", "text/x-java-source"},
+ {".opml", "text/x-opml"},
+ {".p", "text/x-pascal"},
+ {".pas", "text/x-pascal"},
+ {".nfo", "text/x-nfo"},
+ {".etx", "text/x-setext"},
+ {".sfv", "text/x-sfv"},
+ {".uu", "text/x-uuencode"},
+ {".vcs", "text/x-vcalendar"},
+ {".vcf", "text/x-vcard"},
+ {".3gp", "video/3gpp"},
+ {".3g2", "video/3gpp2"},
+ {".h261", "video/h261"},
+ {".h263", "video/h263"},
+ {".h264", "video/h264"},
+ {".jpgv", "video/jpeg"},
+ {".jpm", "video/jpm"},
+ {".jpgm", "video/jpm"},
+ {".mj2", "video/mj2"},
+ {".mjp2", "video/mj2"},
+ {".mp4", "video/mp4"},
+ {".mp4v", "video/mp4"},
+ {".mpg4", "video/mp4"},
+ {".mpeg", "video/mpeg"},
+ {".mpg", "video/mpeg"},
+ {".mpe", "video/mpeg"},
+ {".m1v", "video/mpeg"},
+ {".m2v", "video/mpeg"},
+ {".ogv", "video/ogg"},
+ {".qt", "video/quicktime"},
+ {".mov", "video/quicktime"},
+ {".uvh", "video/vnd.dece.hd"},
+ {".uvvh", "video/vnd.dece.hd"},
+ {".uvm", "video/vnd.dece.mobile"},
+ {".uvvm", "video/vnd.dece.mobile"},
+ {".uvp", "video/vnd.dece.pd"},
+ {".uvvp", "video/vnd.dece.pd"},
+ {".uvs", "video/vnd.dece.sd"},
+ {".uvvs", "video/vnd.dece.sd"},
+ {".uvv", "video/vnd.dece.video"},
+ {".uvvv", "video/vnd.dece.video"},
+ {".dvb", "video/vnd.dvb.file"},
+ {".fvt", "video/vnd.fvt"},
+ {".mxu", "video/vnd.mpegurl"},
+ {".m4u", "video/vnd.mpegurl"},
+ {".pyv", "video/vnd.ms-playready.media.pyv"},
+ {".uvu", "video/vnd.uvvu.mp4"},
+ {".uvvu", "video/vnd.uvvu.mp4"},
+ {".viv", "video/vnd.vivo"},
+ {".webm", "video/webm"},
+ {".f4v", "video/x-f4v"},
+ {".fli", "video/x-fli"},
+ {".flv", "video/x-flv"},
+ {".m4v", "video/x-m4v"},
+ {".mkv", "video/x-matroska"},
+ {".mk3d", "video/x-matroska"},
+ {".mks", "video/x-matroska"},
+ {".mng", "video/x-mng"},
+ {".asf", "video/x-ms-asf"},
+ {".asx", "video/x-ms-asf"},
+ {".vob", "video/x-ms-vob"},
+ {".wm", "video/x-ms-wm"},
+ {".wmv", "video/x-ms-wmv"},
+ {".wmx", "video/x-ms-wmx"},
+ {".wvx", "video/x-ms-wvx"},
+ {".avi", "video/x-msvideo"},
+ {".movie", "video/x-sgi-movie"},
+ {".smv", "video/x-smv"},
+ {".ice", "x-conference/x-cooltalk"},
+ {".323", "text/h323"},
+ {".3gp2", "video/3gpp2"},
+ {".3gpp", "video/3gpp"},
+ {".aa", "audio/audible"},
+ {".aaf", "application/octet-stream"},
+ {".aax", "audio/vnd.audible.aax"},
+ {".ac3", "audio/ac3"},
+ {".aca", "application/octet-stream"},
+ {".accda", "application/msaccess.addin"},
+ {".accdb", "application/msaccess"},
+ {".accdc", "application/msaccess.cab"},
+ {".accde", "application/msaccess"},
+ {".accdr", "application/msaccess.runtime"},
+ {".accdt", "application/msaccess"},
+ {".accdw", "application/msaccess.webapplication"},
+ {".accft", "application/msaccess.ftemplate"},
+ {".acx", "application/internet-property-stream"},
+ {".AddIn", "text/xml"},
+ {".ade", "application/msaccess"},
+ {".adobebridge", "application/x-bridge-url"},
+ {".ADT", "audio/vnd.dlna.adts"},
+ {".ADTS", "audio/aac"},
+ {".amc", "application/x-mpeg"},
+ {".art", "image/x-jg"},
+ {".asa", "application/xml"},
+ {".asax", "application/xml"},
+ {".ascx", "application/xml"},
+ {".asd", "application/octet-stream"},
+ {".ashx", "application/xml"},
+ {".asi", "application/octet-stream"},
+ {".asmx", "application/xml"},
+ {".aspx", "application/xml"},
+ {".asr", "video/x-ms-asf"},
+ {".axs", "application/olescript"},
+ {".bas", "text/plain"},
+ {".calx", "application/vnd.ms-office.calx"},
+ {".cd", "text/plain"},
+ {".cdda", "audio/aiff"},
+ {".cnf", "text/plain"},
+ {".config", "application/xml"},
+ {".contact", "text/x-ms-contact"},
+ {".coverage", "application/xml"},
+ {".cs", "text/plain"},
+ {".csdproj", "text/plain"},
+ {".csproj", "text/plain"},
+ {".cur", "application/octet-stream"},
+ {".dat", "application/octet-stream"},
+ {".datasource", "application/xml"},
+ {".dbproj", "text/plain"},
+ {".dgml", "application/xml"},
+ {".dib", "image/bmp"},
+ {".dif", "video/x-dv"},
+ {".disco", "text/xml"},
+ {".dll.config", "text/xml"},
+ {".dlm", "text/dlm"},
+ {".dsp", "application/octet-stream"},
+ {".dsw", "text/plain"},
+ {".dtsConfig", "text/xml"},
+ {".dv", "video/x-dv"},
+ {".dwp", "application/octet-stream"},
+ {".etl", "application/etl"},
+ {".exe.config", "text/xml"},
+ {".fif", "application/fractals"},
+ {".filters", "Application/xml"},
+ {".fla", "application/octet-stream"},
+ {".flr", "x-world/x-vrml"},
+ {".fsscript", "application/fsharp-script"},
+ {".fsx", "application/fsharp-script"},
+ {".generictest", "application/xml"},
+ {".group", "text/x-ms-group"},
+ {".gsm", "audio/x-gsm"},
+ {".gz", "application/x-gzip"},
+ {".hdml", "text/x-hdml"},
+ {".hhc", "application/x-oleobject"},
+ {".hhk", "application/octet-stream"},
+ {".hhp", "application/octet-stream"},
+ {".hpp", "text/plain"},
+ {".hta", "application/hta"},
+ {".htc", "text/x-component"},
+ {".htt", "text/webviewhtml"},
+ {".hxa", "application/xml"},
+ {".hxc", "application/xml"},
+ {".hxd", "application/octet-stream"},
+ {".hxe", "application/xml"},
+ {".hxf", "application/xml"},
+ {".hxh", "application/octet-stream"},
+ {".hxi", "application/octet-stream"},
+ {".hxk", "application/xml"},
+ {".hxq", "application/octet-stream"},
+ {".hxr", "application/octet-stream"},
+ {".hxs", "application/octet-stream"},
+ {".hxt", "text/html"},
+ {".hxv", "application/xml"},
+ {".hxw", "application/octet-stream"},
+ {".hxx", "text/plain"},
+ {".i", "text/plain"},
+ {".idl", "text/plain"},
+ {".iii", "application/x-iphone"},
+ {".inc", "text/plain"},
+ {".inf", "application/octet-stream"},
+ {".inl", "text/plain"},
+ {".ins", "application/x-internet-signup"},
+ {".ipa", "application/x-itunes-ipa"},
+ {".ipg", "application/x-itunes-ipg"},
+ {".ipproj", "text/plain"},
+ {".ipsw", "application/x-itunes-ipsw"},
+ {".iqy", "text/x-ms-iqy"},
+ {".isp", "application/x-internet-signup"},
+ {".ite", "application/x-itunes-ite"},
+ {".itlp", "application/x-itunes-itlp"},
+ {".itms", "application/x-itunes-itms"},
+ {".itpc", "application/x-itunes-itpc"},
+ {".IVF", "video/x-ivf"},
+ {".jck", "application/liquidmotion"},
+ {".jcz", "application/liquidmotion"},
+ {".jfif", "image/pjpeg"},
+ {".jpb", "application/octet-stream"},
+ {".jsx", "text/jscript"},
+ {".jsxbin", "text/plain"},
+ {".library-ms", "application/windows-library+xml"},
+ {".lit", "application/x-ms-reader"},
+ {".loadtest", "application/xml"},
+ {".lpk", "application/octet-stream"},
+ {".lsf", "video/x-la-asf"},
+ {".lst", "text/plain"},
+ {".lsx", "video/x-la-asf"},
+ {".m2t", "video/vnd.dlna.mpeg-tts"},
+ {".m2ts", "video/vnd.dlna.mpeg-tts"},
+ {".m4a", "audio/m4a"},
+ {".m4b", "audio/m4b"},
+ {".m4p", "audio/m4p"},
+ {".m4r", "audio/x-m4r"},
+ {".mac", "image/x-macpaint"},
+ {".mak", "text/plain"},
+ {".manifest", "application/x-ms-manifest"},
+ {".map", "text/plain"},
+ {".master", "application/xml"},
+ {".mda", "application/msaccess"},
+ {".mde", "application/msaccess"},
+ {".mdp", "application/octet-stream"},
+ {".mfp", "application/x-shockwave-flash"},
+ {".mht", "message/rfc822"},
+ {".mhtml", "message/rfc822"},
+ {".mix", "application/octet-stream"},
+ {".mk", "text/plain"},
+ {".mno", "text/xml"},
+ {".mod", "video/mpeg"},
+ {".mp2v", "video/mpeg"},
+ {".mpa", "video/mpeg"},
+ {".mpf", "application/vnd.ms-mediapackage"},
+ {".mpv2", "video/mpeg"},
+ {".mqv", "video/quicktime"},
+ {".mso", "application/octet-stream"},
+ {".mtx", "application/xml"},
+ {".mvc", "application/x-miva-compiled"},
+ {".mxp", "application/x-mmxp"},
+ {".nws", "message/rfc822"},
+ {".ocx", "application/octet-stream"},
+ {".odh", "text/plain"},
+ {".odl", "text/plain"},
+ {".one", "application/onenote"},
+ {".onea", "application/onenote"},
+ {".orderedtest", "application/xml"},
+ {".osdx", "application/opensearchdescription+xml"},
+ {".pcast", "application/x-podcast"},
+ {".pcz", "application/octet-stream"},
+ {".pict", "image/pict"},
+ {".pkgdef", "text/plain"},
+ {".pkgundef", "text/plain"},
+ {".pko", "application/vnd.ms-pki.pko"},
+ {".pma", "application/x-perfmon"},
+ {".pmc", "application/x-perfmon"},
+ {".pmr", "application/x-perfmon"},
+ {".pmw", "application/x-perfmon"},
+ {".pnt", "image/x-macpaint"},
+ {".pntg", "image/x-macpaint"},
+ {".pnz", "image/png"},
+ {".ppa", "application/vnd.ms-powerpoint"},
+ {".prm", "application/octet-stream"},
+ {".prx", "application/octet-stream"},
+ {".psc1", "application/PowerShell"},
+ {".psess", "application/xml"},
+ {".psm", "application/octet-stream"},
+ {".psp", "application/octet-stream"},
+ {".pwz", "application/vnd.ms-powerpoint"},
+ {".qht", "text/x-html-insertion"},
+ {".qhtm", "text/x-html-insertion"},
+ {".qti", "image/x-quicktime"},
+ {".qtif", "image/x-quicktime"},
+ {".qtl", "application/x-quicktimeplayer"},
+ {".rat", "application/rat-file"},
+ {".rc", "text/plain"},
+ {".rc2", "text/plain"},
+ {".rct", "text/plain"},
+ {".rdlc", "application/xml"},
+ {".resx", "application/xml"},
+ {".rf", "image/vnd.rn-realflash"},
+ {".rgs", "text/plain"},
+ {".rpm", "audio/x-pn-realaudio-plugin"},
+ {".rqy", "text/x-ms-rqy"},
+ {".ruleset", "application/xml"},
+ {".safariextz", "application/x-safari-safariextz"},
+ {".sct", "text/scriptlet"},
+ {".sd2", "audio/x-sd2"},
+ {".sea", "application/octet-stream"},
+ {".searchConnector-ms", "application/windows-search-connector+xml"},
+ {".settings", "application/xml"},
+ {".sgimb", "application/x-sgimb"},
+ {".shtml", "text/html"},
+ {".sitemap", "application/xml"},
+ {".skin", "application/xml"},
+ {".slk", "application/vnd.ms-excel"},
+ {".sln", "text/plain"},
+ {".slupkg-ms", "application/x-ms-license"},
+ {".smd", "audio/x-smd"},
+ {".smx", "audio/x-smd"},
+ {".smz", "audio/x-smd"},
+ {".snippet", "application/xml"},
+ {".snp", "application/octet-stream"},
+ {".sol", "text/plain"},
+ {".sor", "text/plain"},
+ {".srf", "text/plain"},
+ {".SSISDeploymentManifest", "text/xml"},
+ {".ssm", "application/streamingmedia"},
+ {".sst", "application/vnd.ms-pki.certstore"},
+ {".testrunconfig", "application/xml"},
+ {".testsettings", "application/xml"},
+ {".tgz", "application/x-compressed"},
+ {".thn", "application/octet-stream"},
+ {".tlh", "text/plain"},
+ {".tli", "text/plain"},
+ {".toc", "application/octet-stream"},
+ {".trx", "application/xml"},
+ {".ts", "video/vnd.dlna.mpeg-tts"},
+ {".tts", "video/vnd.dlna.mpeg-tts"},
+ {".uls", "text/iuls"},
+ {".user", "text/plain"},
+ {".vb", "text/plain"},
+ {".vbdproj", "text/plain"},
+ {".vbk", "video/mpeg"},
+ {".vbproj", "text/plain"},
+ {".vbs", "text/vbscript"},
+ {".vcproj", "Application/xml"},
+ {".vcxproj", "Application/xml"},
+ {".vddproj", "text/plain"},
+ {".vdp", "text/plain"},
+ {".vdproj", "text/plain"},
+ {".vdx", "application/vnd.ms-visio.viewer"},
+ {".vml", "text/xml"},
+ {".vscontent", "application/xml"},
+ {".vsct", "text/xml"},
+ {".vsi", "application/ms-vsi"},
+ {".vsix", "application/vsix"},
+ {".vsixlangpack", "text/xml"},
+ {".vsixmanifest", "text/xml"},
+ {".vsmdi", "application/xml"},
+ {".vspscc", "text/plain"},
+ {".vsscc", "text/plain"},
+ {".vssettings", "text/xml"},
+ {".vssscc", "text/plain"},
+ {".vstemplate", "text/xml"},
+ {".vsto", "application/x-ms-vsto"},
+ {".vsx", "application/vnd.visio"},
+ {".vtx", "application/vnd.visio"},
+ {".wave", "audio/wav"},
+ {".wbk", "application/msword"},
+ {".webarchive", "application/x-safari-webarchive"},
+ {".webtest", "application/xml"},
+ {".wiq", "application/xml"},
+ {".wiz", "application/msword"},
+ {".WLMP", "application/wlmoviemaker"},
+ {".wlpginstall", "application/x-wlpg-detect"},
+ {".wlpginstall3", "application/x-wlpg3-detect"},
+ {".wmp", "video/x-ms-wmp"},
+ {".wrz", "x-world/x-vrml"},
+ {".wsc", "text/scriptlet"},
+ {".x", "application/directx"},
+ {".xaf", "x-world/x-vrml"},
+ {".xdr", "text/plain"},
+ {".xld", "application/vnd.ms-excel"},
+ {".xlk", "application/vnd.ms-excel"},
+ {".xll", "application/vnd.ms-excel"},
+ {".xmta", "application/xml"},
+ {".xof", "x-world/x-vrml"},
+ {".XOML", "text/plain"},
+ {".xrm-ms", "text/xml"},
+ {".xsc", "application/xml"},
+ {".xsd", "text/xml"},
+ {".xsf", "text/xml"},
+ {".xsn", "application/octet-stream"},
+ {".xss", "application/xml"},
+ {".xtp", "application/octet-stream"},
+ {".z", "application/x-compress"},
+ };
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/MixinMatch.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/MixinMatch.cs
new file mode 100755
index 0000000..76f6a17
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/MixinMatch.cs
@@ -0,0 +1,10 @@
+namespace dotless.Core.Parser.Infrastructure
+{
+ public enum MixinMatch
+ {
+ ArgumentMismatch = 1,
+ Pass = 2,
+ GuardFail = 3,
+ Default = 4
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/NamedArgument.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/NamedArgument.cs
new file mode 100755
index 0000000..2d9e2fd
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/NamedArgument.cs
@@ -0,0 +1,10 @@
+namespace dotless.Core.Parser.Infrastructure
+{
+ using Tree;
+
+ public class NamedArgument
+ {
+ public string Name { get; set; }
+ public Expression Value { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/BooleanNode.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/BooleanNode.cs
new file mode 100755
index 0000000..d04d2d0
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/BooleanNode.cs
@@ -0,0 +1,25 @@
+namespace dotless.Core.Parser.Infrastructure.Nodes
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ public class BooleanNode : Node
+ {
+ public bool Value { get; set; }
+
+ public BooleanNode(bool value)
+ {
+ Value = value;
+ }
+
+ protected override Node CloneCore() {
+ return new BooleanNode(Value);
+ }
+
+ public override string ToString() {
+ return Value.ToString().ToLowerInvariant();
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/CharMatchResult.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/CharMatchResult.cs
new file mode 100755
index 0000000..b12dafe
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/CharMatchResult.cs
@@ -0,0 +1,12 @@
+namespace dotless.Core.Parser.Infrastructure.Nodes
+{
+ public class CharMatchResult : TextNode
+ {
+ public char Char { get; set; }
+
+ public CharMatchResult(char c) : base(c.ToString())
+ {
+ Char = c;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/Node.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/Node.cs
new file mode 100755
index 0000000..cf94006
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/Node.cs
@@ -0,0 +1,170 @@
+using dotless.Core.Plugins;
+
+namespace dotless.Core.Parser.Infrastructure.Nodes
+{
+ using System;
+
+ public abstract class Node
+ {
+ private bool isReference;
+ // Should this node be treated as a reference node
+ // i.e. has this been added to the parse tree via
+ // @import (reference)
+ public bool IsReference {
+ get { return isReference; }
+ set { isReference = value; }
+ }
+
+ public NodeLocation Location { get; set; }
+
+ public NodeList PreComments { get; set; }
+ public NodeList PostComments { get; set; }
+
+ #region Boolean Operators
+
+ public static implicit operator bool(Node node)
+ {
+ return node != null;
+ }
+
+ public static bool operator true(Node n)
+ {
+ return n != null;
+ }
+
+ public static bool operator false(Node n)
+ {
+ return n == null;
+ }
+
+ public static bool operator !(Node n)
+ {
+ return n == null;
+ }
+
+ public static Node operator &(Node n1, Node n2)
+ {
+ return n1 != null ? n2 : null;
+ }
+
+ public static Node operator |(Node n1, Node n2)
+ {
+ return n1 ?? n2;
+ }
+
+ #endregion
+
+ ///
+ /// Copies common properties when evaluating multiple nodes into one
+ ///
+ /// Type to return - for convenience
+ /// The nodes this new node is derived from
+ /// The new node
+ public T ReducedFrom(params Node[] nodes) where T : Node
+ {
+ foreach (var node in nodes)
+ {
+ if (node == this)
+ {
+ continue;
+ }
+
+ Location = node.Location;
+
+ if (node.PreComments)
+ {
+ if (PreComments)
+ {
+ PreComments.AddRange(node.PreComments);
+ }
+ else
+ {
+ PreComments = node.PreComments;
+ }
+ }
+
+ if (node.PostComments)
+ {
+ if (PostComments)
+ {
+ PostComments.AddRange(node.PostComments);
+ }
+ else
+ {
+ PostComments = node.PostComments;
+ }
+ }
+
+ IsReference = node.IsReference;
+ }
+
+ return (T)this;
+ }
+
+ public virtual Node Clone() {
+ return CloneCore().ReducedFrom(this);
+ }
+ protected abstract Node CloneCore();
+
+ public virtual void AppendCSS(Env env)
+ {
+ //if there is no implementation then it will be a node that
+ //evalutes to something.
+ //ideally this shouldn't be called, but when creating error message it may be
+ Evaluate(env).AppendCSS(env);
+ }
+
+ public virtual string ToCSS(Env env)
+ {
+ return env.Output
+ .Push()
+ .Append(this)
+ .Pop()
+ .ToString();
+ }
+
+ public virtual Node Evaluate(Env env)
+ {
+ return this;
+ }
+
+ public virtual bool IgnoreOutput()
+ {
+ return
+ this is RegexMatchResult ||
+ this is CharMatchResult;
+ }
+
+ public virtual void Accept(IVisitor visitor) {}
+
+ ///
+ /// Visits the node and throw an exception if the replacement mode isn't the right type, or the replacement is null
+ ///
+ public T VisitAndReplace(T nodeToVisit, IVisitor visitor) where T : Node
+ {
+ return VisitAndReplace(nodeToVisit, visitor, false);
+ }
+
+ ///
+ /// Visits the node and throw an exception if the replacement mode isn't the right type
+ /// The allowNull parameter determines if a null is allowed to be returned
+ ///
+ public T VisitAndReplace(T nodeToVisit, IVisitor visitor, bool allowNull) where T : Node
+ {
+ if (nodeToVisit == null)
+ {
+ return null;
+ }
+
+ Node replacement = visitor.Visit(nodeToVisit);
+
+ T typedReplacement = replacement as T;
+ if (typedReplacement != null || (allowNull && replacement == null))
+ {
+ return typedReplacement;
+ }
+
+ throw new Exception("Not allowed null for node of type "+typeof(T).ToString());
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/NodeList.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/NodeList.cs
new file mode 100755
index 0000000..48d24cc
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/NodeList.cs
@@ -0,0 +1,155 @@
+using System.Linq;
+
+namespace dotless.Core.Parser.Infrastructure.Nodes
+{
+ using System.Collections;
+ using System.Collections.Generic;
+
+ public class NodeList : NodeList
+ {
+ public NodeList()
+ {
+ Inner = new List();
+ }
+
+ public NodeList(params Node[] nodes)
+ : this((IEnumerable) nodes)
+ {
+ }
+
+ public NodeList(IEnumerable nodes)
+ {
+ Inner = new List(nodes);
+ }
+
+ public NodeList(NodeList nodes) : this((IEnumerable)nodes)
+ {
+ IsReference = nodes.IsReference;
+ }
+ }
+
+ public class NodeList : Node, IList
+ where TNode : Node
+ {
+ protected List Inner;
+
+ public NodeList()
+ {
+ Inner = new List();
+ }
+
+ public NodeList(params TNode[] nodes)
+ : this((IEnumerable) nodes)
+ {
+ }
+
+ public NodeList(IEnumerable nodes)
+ {
+ Inner = new List(nodes);
+ }
+
+ protected override Node CloneCore() {
+ return new NodeList(Inner.Select(i => i.Clone()));
+ }
+
+ public override void AppendCSS(Env env)
+ {
+ env.Output.AppendMany(Inner);
+ }
+
+ public override void Accept(Plugins.IVisitor visitor)
+ {
+ List newInner = new List(Inner.Count);
+
+ foreach (TNode inner in Inner)
+ {
+ TNode node = VisitAndReplace(inner, visitor, true);
+
+ if (node == null)
+ {
+ continue;
+ }
+
+ newInner.Add(node);
+ }
+
+ Inner = newInner;
+ }
+
+ public void AddRange(IEnumerable nodes)
+ {
+ Inner.AddRange(nodes);
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return Inner.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public void InsertRange(int index, IEnumerable collection)
+ {
+ Inner.InsertRange(index, collection);
+ }
+
+ public void Add(TNode item)
+ {
+ Inner.Add(item);
+ }
+
+ public void Clear()
+ {
+ Inner.Clear();
+ }
+
+ public bool Contains(TNode item)
+ {
+ return Inner.Contains(item);
+ }
+
+ public void CopyTo(TNode[] array, int arrayIndex)
+ {
+ Inner.CopyTo(array, arrayIndex);
+ }
+
+ public bool Remove(TNode item)
+ {
+ return Inner.Remove(item);
+ }
+
+ public int Count
+ {
+ get { return Inner.Count; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return ((IList) Inner).IsReadOnly; }
+ }
+
+ public int IndexOf(TNode item)
+ {
+ return Inner.IndexOf(item);
+ }
+
+ public void Insert(int index, TNode item)
+ {
+ Inner.Insert(index, item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ Inner.RemoveAt(index);
+ }
+
+ public TNode this[int index]
+ {
+ get { return Inner[index]; }
+ set { Inner[index] = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/RegexMatchResult.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/RegexMatchResult.cs
new file mode 100755
index 0000000..5a62862
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/RegexMatchResult.cs
@@ -0,0 +1,23 @@
+namespace dotless.Core.Parser.Infrastructure.Nodes
+{
+ using System.Text.RegularExpressions;
+
+ public class RegexMatchResult : TextNode
+ {
+ public Match Match { get; set; }
+
+ public RegexMatchResult(Match match) : base(match.Value)
+ {
+ Match = match;
+ }
+
+ public string this[int index]
+ {
+ get
+ {
+ var value = Match.Groups[index].Value;
+ return string.IsNullOrEmpty(value) ? null : value;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/TextNode.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/TextNode.cs
new file mode 100755
index 0000000..f42057d
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Nodes/TextNode.cs
@@ -0,0 +1,47 @@
+using System;
+namespace dotless.Core.Parser.Infrastructure.Nodes
+{
+ public class TextNode : Node, IComparable
+ {
+ public string Value { get; set; }
+
+ public TextNode(string contents)
+ {
+ Value = contents;
+ }
+
+ public static TextNode operator &(TextNode n1, TextNode n2)
+ {
+ return n1 != null ? n2 : null;
+ }
+
+ public static TextNode operator |(TextNode n1, TextNode n2)
+ {
+ return n1 ?? n2;
+ }
+
+ protected override Node CloneCore() {
+ return new TextNode(Value);
+ }
+
+ public override void AppendCSS(Env env)
+ {
+ env.Output.Append(env.Compress ? Value.Trim() : Value);
+ }
+
+ public override string ToString()
+ {
+ return Value;
+ }
+
+ public virtual int CompareTo(object obj)
+ {
+ if (obj == null)
+ {
+ return -1;
+ }
+
+ return obj.ToString().CompareTo(ToString());
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Output.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Output.cs
new file mode 100755
index 0000000..39b57c1
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Infrastructure/Output.cs
@@ -0,0 +1,241 @@
+namespace dotless.Core.Parser.Infrastructure
+{
+ using System;
+ using System.Globalization;
+ using System.Collections.Generic;
+ using System.Text;
+ using Nodes;
+
+ public class Output
+ {
+ private Env Env { get; set; }
+ private StringBuilder Builder { get; set; }
+ private Stack BuilderStack { get; set; }
+
+ public Output(Env env)
+ {
+ Env = env;
+ BuilderStack = new Stack();
+
+ Push();
+ }
+
+ public Output Push()
+ {
+ Builder = new StringBuilder();
+
+ BuilderStack.Push(Builder);
+
+ return this;
+ }
+
+ public StringBuilder Pop()
+ {
+ if (BuilderStack.Count == 1)
+ throw new InvalidOperationException();
+
+ var sb = BuilderStack.Pop();
+
+ Builder = BuilderStack.Peek();
+
+ return sb;
+ }
+
+ public void Reset(string s)
+ {
+ Builder = new StringBuilder(s);
+
+ BuilderStack.Pop();
+ BuilderStack.Push(Builder);
+ }
+
+ public Output PopAndAppend()
+ {
+ return Append(Pop());
+ }
+
+ public Output Append(Node node)
+ {
+ if (node != null)
+ {
+ if (node.PreComments)
+ node.PreComments.AppendCSS(Env);
+
+ node.AppendCSS(Env);
+
+ if (node.PostComments)
+ node.PostComments.AppendCSS(Env);
+ }
+
+ return this;
+ }
+
+ public Output Append(string s)
+ {
+ Builder.Append(s);
+
+ return this;
+ }
+
+ public Output Append(char? s)
+ {
+ Builder.Append(s);
+
+ return this;
+ }
+
+ public Output Append(StringBuilder sb)
+ {
+ Builder.Append(sb);
+
+ return this;
+ }
+
+ public Output AppendMany(IEnumerable nodes)
+ where TNode : Node
+ {
+ return AppendMany(nodes, null);
+ }
+
+ public Output AppendMany(IEnumerable nodes, string join)
+ where TNode : Node
+ {
+ return AppendMany(nodes, n => Env.Output.Append(n), join);
+ }
+
+ public Output AppendMany(IEnumerable list, string join)
+ {
+ return AppendMany(list, (item, sb) => sb.Append(item), join);
+ }
+
+ public Output AppendMany(IEnumerable list, Func toString, string join)
+ {
+ return AppendMany(list, (item, sb) => sb.Append(toString(item)), join);
+ }
+
+ public Output AppendMany(IEnumerable list, Action toString, string join)
+ {
+ return AppendMany(list, (item, sb) => toString(item), join);
+ }
+
+ public Output AppendMany(IEnumerable list, Action toString, string join)
+ {
+ var first = true;
+ var hasJoinString = !string.IsNullOrEmpty(join);
+
+ foreach (var item in list)
+ {
+ if (!first && hasJoinString)
+ Builder.Append(join);
+
+ first = false;
+ toString(item, Builder);
+ }
+
+ return this;
+ }
+
+ public Output AppendMany(IEnumerable buildersToAppend)
+ {
+ return AppendMany(buildersToAppend, null);
+ }
+
+ public Output AppendMany(IEnumerable buildersToAppend, string join)
+ {
+ return AppendMany(buildersToAppend, (b, output) => output.Append(b), join);
+ }
+
+ public Output AppendFormat(string format, params object[] values) {
+ return AppendFormat(CultureInfo.InvariantCulture, format, values);
+ }
+
+ public Output AppendFormat(IFormatProvider formatProvider, string format, params object[] values)
+ {
+ Builder.AppendFormat(formatProvider, format, values);
+
+ return this;
+ }
+
+ public Output Indent(int amount)
+ {
+ if (amount > 0)
+ {
+ var indentation = new string(' ', amount);
+ Builder.Replace("\n", "\n" + indentation);
+ Builder.Insert(0, indentation);
+ }
+
+ return this;
+ }
+
+ ///
+ /// Trims whitespace
+ ///
+ public Output Trim()
+ {
+ return this.TrimLeft(null).TrimRight(null);
+ }
+
+ ///
+ /// Trims the character passed or whitespace if it has no value from the left
+ ///
+ public Output TrimLeft(char? c)
+ {
+ int trimLLength = 0;
+ int length = Builder.Length;
+
+ if (length == 0)
+ {
+ return this;
+ }
+
+ while (trimLLength < length &&
+ (c.HasValue ? Builder[trimLLength] == c.Value :
+ char.IsWhiteSpace(Builder[trimLLength])))
+ {
+ trimLLength++;
+ }
+
+ if (trimLLength > 0)
+ {
+ Builder.Remove(0, trimLLength);
+ }
+
+ return this;
+ }
+
+ ///
+ /// Trims the character passed or whitespace if it has no value from the left
+ ///
+ public Output TrimRight(char? c)
+ {
+ int trimRLength = 0;
+ int length = Builder.Length;
+
+ if (length == 0)
+ {
+ return this;
+ }
+
+ while (trimRLength < length &&
+ (c.HasValue ? Builder[length - (trimRLength + 1)] == c.Value :
+ char.IsWhiteSpace(Builder[length - (trimRLength + 1)])))
+ {
+ trimRLength++;
+ }
+
+ if (trimRLength > 0)
+ {
+ Builder.Remove(length - trimRLength, trimRLength);
+ }
+
+ return this;
+ }
+
+
+ public override string ToString()
+ {
+ return Builder.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Parser.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Parser.cs
new file mode 100755
index 0000000..c33a1ce
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Parser.cs
@@ -0,0 +1,173 @@
+// ReSharper disable InconsistentNaming
+
+namespace dotless.Core.Parser
+{
+ using Exceptions;
+ using Importers;
+ using Infrastructure;
+ using Stylizers;
+ using Tree;
+
+ //
+ // less.js - parser
+ //
+ // A relatively straight-forward predictive parser.
+ // There is no tokenization/lexing stage, the input is parsed
+ // in one sweep.
+ //
+ // To make the parser fast enough to run in the browser, several
+ // optimization had to be made:
+ //
+ // - Instead of the more commonly used technique of slicing the
+ // input string on every match, we use global regexps (/g),
+ // and move the `lastIndex` pointer on match, foregoing `slice()`
+ // completely. This gives us a 3x speed-up.
+ //
+ // - Matching on a huge input is often cause of slowdowns.
+ // The solution to that is to chunkify the input into
+ // smaller strings.
+ //
+ // - In many cases, we don't need to match individual tokens;
+ // for example, if a value doesn't hold any variables, operations
+ // or dynamic references, the parser can effectively 'skip' it,
+ // treating it as a literal.
+ // An example would be '1px solid #000' - which evaluates to itself,
+ // we don't need to know what the individual components are.
+ // The drawback, of course is that you don't get the benefits of
+ // syntax-checking on the CSS. This gives us a 50% speed-up in the parser,
+ // and a smaller speed-up in the code-gen.
+ //
+ //
+ // Token matching is done with the `Match` function, which either takes
+ // a terminal string or regexp, or a non-terminal function to call.
+ // It also takes care of moving all the indices forwards.
+ //
+ //
+ public class Parser
+ {
+ public Tokenizer Tokenizer { get; set; }
+ public IStylizer Stylizer { get; set; }
+ public string FileName { get; set; }
+ public bool Debug { get; set; }
+
+ public string CurrentDirectory
+ {
+ get { return Importer.CurrentDirectory; }
+ set { Importer.CurrentDirectory = value; }
+ }
+
+ private INodeProvider _nodeProvider;
+ public INodeProvider NodeProvider
+ {
+ get { return _nodeProvider ?? (_nodeProvider = new DefaultNodeProvider()); }
+ set { _nodeProvider = value; }
+ }
+
+ private IImporter _importer;
+ public IImporter Importer
+ {
+ get { return _importer; }
+ set
+ {
+ _importer = value;
+ _importer.Parser = () => new Parser(Tokenizer.Optimization, Stylizer, _importer)
+ {
+ NodeProvider = NodeProvider,
+ Debug = Debug,
+ CurrentDirectory = CurrentDirectory,
+ StrictMath = StrictMath
+ };
+ }
+ }
+
+ public bool StrictMath { get; set; }
+
+ private const int defaultOptimization = 1;
+ private const bool defaultDebug = false;
+
+ public Parser()
+ : this(defaultOptimization, defaultDebug)
+ {
+ }
+
+ public Parser(bool debug)
+ : this(defaultOptimization, debug)
+ {
+ }
+
+ public Parser(int optimization)
+ : this(optimization, new PlainStylizer(), new Importer(), defaultDebug)
+ {
+ }
+
+ public Parser(int optimization, bool debug)
+ : this(optimization, new PlainStylizer(), new Importer(), debug)
+ {
+ }
+
+ public Parser(IStylizer stylizer, IImporter importer)
+ : this(defaultOptimization, stylizer, importer, defaultDebug)
+ {
+ }
+
+ public Parser(IStylizer stylizer, IImporter importer, bool debug)
+ : this(defaultOptimization, stylizer, importer, debug)
+ {
+ }
+
+ public Parser(int optimization, IStylizer stylizer, IImporter importer)
+ : this(optimization, stylizer, importer, defaultDebug)
+ {
+ }
+
+ public Parser(int optimization, IStylizer stylizer, IImporter importer, bool debug)
+ {
+ Stylizer = stylizer;
+ Importer = importer;
+ Debug = debug;
+ Tokenizer = new Tokenizer(optimization);
+ }
+
+ public Parser(dotless.Core.configuration.DotlessConfiguration config, IStylizer stylizer, IImporter importer)
+ : this(config.Optimization, stylizer, importer, config.Debug)
+ {
+
+ }
+
+ public Ruleset Parse(string input, string fileName)
+ {
+ Ruleset root;
+ FileName = fileName;
+
+ try
+ {
+ Tokenizer.SetupInput(input, fileName);
+
+ var parsers = new Parsers(NodeProvider);
+ root = new Root(parsers.Primary(this), GenerateParserError);
+ }
+ catch (ParsingException e)
+ {
+ throw GenerateParserError(e);
+ }
+
+ if (!Tokenizer.HasCompletedParsing())
+ throw GenerateParserError(new ParsingException("Content after finishing parsing (missing opening bracket?)", Tokenizer.GetNodeLocation(Tokenizer.Location.Index)));
+
+ return root;
+ }
+
+ private ParserException GenerateParserError(ParsingException parsingException)
+ {
+ var errorLocation = parsingException.Location;
+ var error = parsingException.Message;
+ var call = parsingException.CallLocation;
+
+ var zone = new Zone(errorLocation, error, call != null ? new Zone(call) : null);
+
+ var message = Stylizer.Stylize(zone);
+
+ return new ParserException(message, parsingException, zone);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/ParserLocation.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/ParserLocation.cs
new file mode 100755
index 0000000..5ed47ff
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/ParserLocation.cs
@@ -0,0 +1,18 @@
+namespace dotless.Core.Parser
+{
+ using System;
+
+ public class NodeLocation
+ {
+ public int Index { get; set; }
+ public string Source { get; set; }
+ public string FileName { get; set; }
+
+ public NodeLocation(int index, string source, string filename)
+ {
+ Index = index;
+ Source = source;
+ FileName = filename;
+ }
+ }
+}
diff --git a/dotnet/Applications/Mocha.Web.Server/Core/Parser/Parsers.cs b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Parsers.cs
new file mode 100755
index 0000000..8c6be6d
--- /dev/null
+++ b/dotnet/Applications/Mocha.Web.Server/Core/Parser/Parsers.cs
@@ -0,0 +1,2071 @@
+//
+// Here in, the parsing rules/functions
+//
+// The basic structure of the syntax tree generated is as follows:
+//
+// Ruleset -> Rule -> Value -> Expression -> Entity
+//
+// Here's some LESS code:
+//
+// .class {
+// color: #fff;
+// border: 1px solid #000;
+// width: @w + 4px;
+// > .child {...}
+// }
+//
+// And here's what the parse tree might look like:
+//
+// Ruleset (Selector '.class', [
+// Rule ("color", Value ([Expression [Color #fff]]))
+// Rule ("border", Value ([Expression [Number 1px][Keyword "solid"][Color #000]]))
+// Rule ("width", Value ([Expression [Operation "+" [Variable "@w"][Number 4px]]]))
+// Ruleset (Selector [Element '>', '.child'], [...])
+// ])
+//
+// In general, most rules will try to parse a token with the `$()` function, and if the return
+// value is truly, will return a new node, of the relevant type. Sometimes, we need to check
+// first, before parsing, that's when we use `peek()`.
+//
+
+
+using System;
+using System.Text.RegularExpressions;
+
+#pragma warning disable 665
+// ReSharper disable RedundantNameQualifier
+
+namespace dotless.Core.Parser
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using Exceptions;
+ using Infrastructure;
+ using Infrastructure.Nodes;
+ using Tree;
+
+ public class Parsers
+ {
+ public INodeProvider NodeProvider { get; set; }
+
+ public Parsers(INodeProvider nodeProvider)
+ {
+ NodeProvider = nodeProvider;
+ }
+
+ //
+ // The `primary` rule is the *entry* and *exit* point of the parser.
+ // The rules here can appear at any level of the parse tree.
+ //
+ // The recursive nature of the grammar is an interplay between the `block`
+ // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
+ // as represented by this simplified grammar:
+ //
+ // primary → (ruleset | rule)+
+ // ruleset → selector+ block
+ // block → '{' primary '}'
+ //
+ // Only at one point is the primary rule not called from the
+ // block rule: at the root level.
+ //
+ public NodeList Primary(Parser parser)
+ {
+ Node node;
+ var root = new NodeList();
+
+ GatherComments(parser);
+ while (node = MixinDefinition(parser) || ExtendRule(parser) || Rule(parser) || PullComments() || GuardedRuleset(parser) || Ruleset(parser) ||
+ MixinCall(parser) || Directive(parser))
+ {
+ NodeList comments;
+ if (comments = PullComments())
+ {
+ root.AddRange(comments);
+ }
+
+ comments = node as NodeList;
+ if (comments)
+ {
+ foreach (Comment c in comments)
+ {
+ c.IsPreSelectorComment = true;
+ }
+ root.AddRange(comments);
+ }
+ else
+ root.Add(node);
+
+ GatherComments(parser);
+ }
+ return root;
+ }
+
+ private NodeList CurrentComments { get; set; }
+
+ ///
+ /// Gathers the comments and put them on the stack
+ ///
+ private void GatherComments(Parser parser)
+ {
+ Comment comment;
+ while (comment = Comment(parser))
+ {
+ if (CurrentComments == null)
+ {
+ CurrentComments = new NodeList();
+ }
+ CurrentComments.Add(comment);
+ }
+ }
+
+ ///
+ /// Collects comments from the stack retrived when gathering comments
+ ///
+ private NodeList PullComments()
+ {
+ NodeList comments = CurrentComments;
+ CurrentComments = null;
+ return comments;
+ }
+
+ ///
+ /// The equivalent of gathering any more comments and pulling everything on the stack
+ ///
+ private NodeList GatherAndPullComments(Parser parser)
+ {
+ GatherComments(parser);
+ return PullComments();
+ }
+
+ private Stack CommentsStack = new Stack();
+
+ ///
+ /// Pushes comments on to a stack for later use
+ ///
+ private void PushComments()
+ {
+ CommentsStack.Push(PullComments());
+ }
+
+ ///
+ /// Pops the comments stack
+ ///
+ private void PopComments()
+ {
+ CurrentComments = CommentsStack.Pop();
+ }
+
+ // We create a Comment node for CSS comments `/* */`,
+ // but keep the LeSS comments `//` silent, by just skipping
+ // over them.e
+ public Comment Comment(Parser parser)
+ {
+ var index = parser.Tokenizer.Location.Index;
+ string comment = parser.Tokenizer.GetComment();
+
+ if (comment != null)
+ {
+ return NodeProvider.Comment(comment, parser.Tokenizer.GetNodeLocation(index));
+ }
+
+ return null;
+ }
+
+ //
+ // Entities are tokens which can be found inside an Expression
+ //
+
+ //
+ // A string, which supports escaping " and '
+ //
+ // "milky way" 'he\'s the one!'
+ //
+ public Quoted Quoted(Parser parser)
+ {
+ var index = parser.Tokenizer.Location.Index;
+ var escaped = false;
+ var quote = parser.Tokenizer.CurrentChar;
+
+ if (parser.Tokenizer.CurrentChar == '~')
+ {
+ escaped = true;
+ quote = parser.Tokenizer.NextChar;
+ }
+ if (quote != '"' && quote != '\'')
+ return null;
+
+ if (escaped)
+ parser.Tokenizer.Match('~');
+
+ string str = parser.Tokenizer.GetQuotedString();
+
+ if (str == null)
+ return null;
+
+ return NodeProvider.Quoted(str, str.Substring(1, str.Length - 2), escaped, parser.Tokenizer.GetNodeLocation(index));
+ }
+
+ //
+ // A catch-all word, such as:
+ //
+ // black border-collapse
+ //
+ public Keyword Keyword(Parser parser)
+ {
+ var index = parser.Tokenizer.Location.Index;
+
+ var k = parser.Tokenizer.Match(@"[A-Za-z0-9_-]+");
+ if (k)
+ return NodeProvider.Keyword(k.Value, parser.Tokenizer.GetNodeLocation(index));
+
+ return null;
+ }
+
+ //
+ // A function call
+ //
+ // rgb(255, 0, 255)
+ //
+ // We also try to catch IE's `alpha()`, but let the `alpha` parser
+ // deal with the details.
+ //
+ // The arguments are parsed with the `entities.arguments` parser.
+ //
+ public Call Call(Parser parser)
+ {
+ var memo = Remember(parser);
+ var index = parser.Tokenizer.Location.Index;
+
+ var name = parser.Tokenizer.Match(@"(%|[a-zA-Z0-9_-]+|progid:[\w\.]+)\(");
+
+ if (!name)
+ return null;
+
+ if (name[1].ToLowerInvariant() == "alpha")
+ {
+ var alpha = Alpha(parser);
+ if (alpha != null)
+ return alpha;
+ }
+
+ var args = Arguments(parser);
+
+ if (!parser.Tokenizer.Match(')'))
+ {
+ Recall(parser, memo);
+ return null;
+ }
+
+ return NodeProvider.Call(name[1], args, parser.Tokenizer.GetNodeLocation(index));
+ }
+
+ public NodeList Arguments(Parser parser)
+ {
+ var args = new NodeList();
+ Node arg;
+
+ while ((arg = Assignment(parser)) || (arg = Expression(parser)))
+ {
+ args.Add(arg);
+ if (!parser.Tokenizer.Match(','))
+ break;
+ }
+ return args;
+ }
+
+ // Assignments are argument entities for calls.
+ // They are present in ie filter properties as shown below.
+ //
+ // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
+ //
+ public Assignment Assignment(Parser parser)
+ {
+ var key = parser.Tokenizer.Match(@"\w+(?=\s?=)");
+
+ if (!key || !parser.Tokenizer.Match('='))
+ {
+ return null;
+ }
+
+ var value = Entity(parser);
+
+ if (value) {
+ return NodeProvider.Assignment(key.Value, value, key.Location);
+ }
+
+ return null;
+ }
+
+ public Node Literal(Parser parser)
+ {
+ return Dimension(parser) ||
+ Color(parser) ||
+ Quoted(parser);
+ }
+
+ //
+ // Parse url() tokens
+ //
+ // We use a specific rule for urls, because they don't really behave like
+ // standard function calls. The difference is that the argument doesn't have
+ // to be enclosed within a string, so it can't be parsed as an Expression.
+ //
+ public Url Url(Parser parser)
+ {
+ var index = parser.Tokenizer.Location.Index;
+
+ if (parser.Tokenizer.CurrentChar != 'u' || !parser.Tokenizer.Match(@"url\("))
+ return null;
+
+ GatherComments(parser);
+
+ Node value = Quoted(parser);
+
+ if (!value)
+ {
+ var memo = Remember(parser);
+ value = Expression(parser);
+
+ if (value && !parser.Tokenizer.Peek(')'))
+ {
+ value = null;
+ Recall(parser, memo);
+ }
+ }
+ else
+ {
+ value.PreComments = PullComments();
+ value.PostComments = GatherAndPullComments(parser);
+ }
+
+ if (!value)
+ {
+ value = parser.Tokenizer.MatchAny(@"[^\)""']*") || new TextNode("");
+ }
+
+ Expect(parser, ')');
+
+ return NodeProvider.Url(value, parser.Importer, parser.Tokenizer.GetNodeLocation(index));
+ }
+
+ //
+ // A Variable entity, such as `@fink`, in
+ //
+ // width: @fink + 2px
+ //
+ // We use a different parser for variable definitions,
+ // see `parsers.variable`.
+ //
+ public Variable Variable(Parser parser)
+ {
+ RegexMatchResult name;
+ var index = parser.Tokenizer.Location.Index;
+
+ if (parser.Tokenizer.CurrentChar == '@' && (name = parser.Tokenizer.Match(@"@(@?[a-zA-Z0-9_-]+)")))
+ return NodeProvider.Variable(name.Value, parser.Tokenizer.GetNodeLocation(index));
+
+ return null;
+ }
+
+ //
+ // An interpolated Variable entity, such as `@{foo}`, in
+ //
+ // [@{foo}="value"]
+ //
+ public Variable InterpolatedVariable(Parser parser) {
+ RegexMatchResult name;
+ var index = parser.Tokenizer.Location.Index;
+
+ if (parser.Tokenizer.CurrentChar == '@' && (name = parser.Tokenizer.Match(@"@\{(?@?[a-zA-Z0-9_-]+)\}")))
+ return NodeProvider.Variable("@" + name.Match.Groups["name"].Value, parser.Tokenizer.GetNodeLocation(index));
+
+ return null;
+ }
+
+ //
+ // A Variable entity as like in a selector e.g.
+ //
+ // @{var} {
+ // }
+ //
+ public Variable VariableCurly(Parser parser)
+ {
+ RegexMatchResult name;
+ var index = parser.Tokenizer.Location.Index;
+
+ if (parser.Tokenizer.CurrentChar == '@' && (name = parser.Tokenizer.Match(@"@\{([a-zA-Z0-9_-]+)\}")))
+ return NodeProvider.Variable("@" + name.Match.Groups[1].Value, parser.Tokenizer.GetNodeLocation(index));
+
+ return null;
+ }
+
+ ///
+ /// A guarded ruleset placed inside another e.g.
+ ///
+ /// & when (@x = true) {
+ /// }
+ ///
+ public GuardedRuleset GuardedRuleset(Parser parser)
+ {
+ var selectors = new NodeList();
+
+ var memo = Remember(parser);
+ var index = memo.TokenizerLocation.Index;
+
+ Selector s;
+ while (s = Selector(parser))
+ {
+ selectors.Add(s);
+ if (!parser.Tokenizer.Match(','))
+ break;
+
+ GatherComments(parser);
+ }
+
+ if (parser.Tokenizer.Match(@"when"))
+ {
+ GatherAndPullComments(parser);
+
+ var condition = Expect(Conditions(parser), "Expected conditions after when (guard)", parser);
+ var rules = Block(parser);
+
+ return NodeProvider.GuardedRuleset(selectors, rules, condition, parser.Tokenizer.GetNodeLocation(index));
+ }
+
+ Recall(parser, memo);
+
+ return null;
+ }
+
+ ///
+ /// An extend statement placed at the end of a rule
+ ///
+ ///
+ ///
+ public Extend ExtendRule(Parser parser)
+ {
+ RegexMatchResult extendKeyword;
+ var index = parser.Tokenizer.Location.Index;
+
+ if ((extendKeyword = parser.Tokenizer.Match(@"\&?:extend\(")) != null)
+ {
+ var exact = new List();
+ var partial = new List();
+
+ Selector s;
+ while (s = Selector(parser))
+ {
+ if (s.Elements.Count == 1 && s.Elements.First().Value == null)
+ {
+ continue;
+ }
+
+ if (s.Elements.Count > 1 && s.Elements.Last().Value == "all")
+ {
+ s.Elements.Remove(s.Elements.Last());
+ partial.Add(s);
+ }
+ else
+ {
+ exact.Add(s);
+ }
+
+ if (!parser.Tokenizer.Match(','))
+ {
+ break;
+ }
+ }
+
+ if (!parser.Tokenizer.Match(')'))
+ {
+ throw new ParsingException(@"Extend rule not correctly terminated",parser.Tokenizer.GetNodeLocation(index));
+ }
+
+ if (extendKeyword.Match.Value[0] == '&')
+ {
+ parser.Tokenizer.Match(';');
+ }
+
+ if (partial.Count == 0 && exact.Count == 0)
+ {
+ return null;
+ }
+
+ return NodeProvider.Extend(exact,partial, parser.Tokenizer.GetNodeLocation(index));
+ }
+ return null;
+ }
+
+ //
+ // A Hexadecimal color
+ //
+ // #4F3C2F
+ //
+ // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
+ //
+ public Color Color(Parser parser)
+ {
+ RegexMatchResult hex;
+
+ var index = parser.Tokenizer.Location.Index;
+
+ if (parser.Tokenizer.CurrentChar == '#' &&
+ (hex = parser.Tokenizer.Match(@"#([a-fA-F0-9]{8}|[a-fA-F0-9]{6}|[a-fA-F0-9]{3})")))
+ return NodeProvider.Color(hex[1], parser.Tokenizer.GetNodeLocation(index));
+
+ return null;
+ }
+
+ //
+ // A Dimension, that is, a number and a unit
+ //
+ // 0.5em 95%
+ //
+ public Number Dimension(Parser parser)
+ {
+ var c = parser.Tokenizer.CurrentChar;
+ if (!(char.IsNumber(c) || c == '.' || c == '-' || c == '+'))
+ return null;
+
+ var index = parser.Tokenizer.Location.Index;
+
+ var value = parser.Tokenizer.Match(@"([+-]?[0-9]*\.?[0-9]+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|ch|rem|vw|vh|vmin|vm(ax)?|grad|rad|fr|gr|Hz|kHz|dpi|dpcm|dppx)?", true);
+
+ if (value)
+ return NodeProvider.Number(value[1], value[2], parser.Tokenizer.GetNodeLocation(index));
+
+ return null;
+ }
+
+ //
+ // C# code to be evaluated
+ //
+ // ``
+ //
+ public Script Script(Parser parser)
+ {
+ if (parser.Tokenizer.CurrentChar != '`')
+ return null;
+
+ var index = parser.Tokenizer.Location.Index;
+
+ var script = parser.Tokenizer.MatchAny(@"`[^`]*`");
+
+ if (!script)
+ {
+ return null;
+ }
+
+ return NodeProvider.Script(script.Value, parser.Tokenizer.GetNodeLocation(index));
+ }
+
+
+ //
+ // The variable part of a variable definition. Used in the `rule` parser
+ //
+ // @fink:
+ //
+ public string VariableName(Parser parser)
+ {
+ var variable = Variable(parser);
+
+ if (variable != null)
+ return variable.Name;
+
+ return null;
+ }
+
+ //
+ // A font size/line-height shorthand
+ //
+ // small/12px
+ //
+ // We need to peek first, or we'll match on keywords and dimensions
+ //
+ public Shorthand Shorthand(Parser parser)
+ {
+ if (!parser.Tokenizer.Peek(@"[@%\w.-]+\/[@%\w.-]+"))
+ return null;
+
+ var index = parser.Tokenizer.Location.Index;
+
+ Node a = null;
+ Node b = null;
+ if ((a = Entity(parser)) && parser.Tokenizer.Match('/') && (b = Entity(parser)))
+ return NodeProvider.Shorthand(a, b, parser.Tokenizer.GetNodeLocation(index));
+
+ return null;
+ }
+
+ //
+ // Mixins
+ //
+
+ //
+ // A Mixin call, with an optional argument list
+ //
+ // #mixins > .square(#fff);
+ // .rounded(4px, black);
+ // .button;
+ //
+ // The `while` loop is there because mixins can be
+ // namespaced, but we only support the child and descendant
+ // selector for now.
+ //
+ public MixinCall MixinCall(Parser parser)
+ {
+ var elements = new NodeList();
+ var index = parser.Tokenizer.Location.Index;
+ bool important = false;
+
+ RegexMatchResult e;
+ Combinator c = null;
+
+ PushComments();
+
+ for (var i = parser.Tokenizer.Location.Index; e = parser.Tokenizer.Match(@"[#.][a-zA-Z0-9_-]+"); i = parser.Tokenizer.Location.Index)
+ {
+ elements.Add(NodeProvider.Element(c, e, parser.Tokenizer.GetNodeLocation(index)));
+
+ i = parser.Tokenizer.Location.Index;
+ var match = parser.Tokenizer.Match('>');
+ c = match != null ? NodeProvider.Combinator(match.Value, parser.Tokenizer.GetNodeLocation(index)) : null;
+ }
+
+ if (elements.Count == 0)
+ {
+ PopComments();
+ return null;
+ }
+
+ var args = new List();
+ if (parser.Tokenizer.Peek('(')) {
+ var location = Remember(parser);
+ const string balancedParenthesesRegex = @"\([^()]*(?>(?>(?'open'\()[^()]*)*(?>(?'-open'\))[^()]*)*)+(?(open)(?!))\)";
+
+ var argumentList = parser.Tokenizer.Match(balancedParenthesesRegex);
+ bool argumentListIsSemicolonSeparated = argumentList != null && argumentList.Value.Contains(';');
+
+ char expectedSeparator = argumentListIsSemicolonSeparated ? ';' : ',';
+
+ Recall(parser, location);
+ parser.Tokenizer.Match('(');
+
+ Expression arg;
+ while (arg = Expression(parser, argumentListIsSemicolonSeparated))
+ {
+ var value = arg;
+ string name = null;
+
+ if (arg.Value.Count == 1 && arg.Value[0] is Variable)
+ {
+ if (parser.Tokenizer.Match(':'))
+ {
+ value = Expect(Expression(parser), "expected value", parser);
+ name = (arg.Value[0] as Variable).Name;
+ }
+ }
+
+ args.Add(new NamedArgument { Name = name, Value = value });
+
+ if (!parser.Tokenizer.Match(expectedSeparator))
+ break;
+ }
+ Expect(parser, ')');
+ }
+
+ GatherComments(parser);
+
+ if (!string.IsNullOrEmpty(Important(parser)))
+ {
+ important = true;
+ }
+
+ // if elements then we've picked up chars so don't need to worry about remembering
+ var postComments = GatherAndPullComments(parser);
+
+ if (End(parser))
+ {
+ var mixinCall = NodeProvider.MixinCall(elements, args, important, parser.Tokenizer.GetNodeLocation(index));
+ mixinCall.PostComments = postComments;
+ PopComments();
+ return mixinCall;
+ }
+
+ PopComments();
+ return null;
+ }
+
+ private Expression Expression(Parser parser, bool allowList)
+ {
+ return allowList ? ExpressionOrExpressionList(parser) : Expression(parser);
+ }
+
+ //
+ // A Mixin definition, with a list of parameters
+ //
+ // .rounded (@radius: 2px, @color) {
+ // ...
+ // }
+ //
+ // Until we have a finer grained state-machine, we have to
+ // do a look-ahead, to make sure we don't have a mixin call.
+ // See the `rule` function for more information.
+ //
+ // We start by matching `.rounded (`, and then proceed on to
+ // the argument list, which has optional default values.
+ // We store the parameters in `params`, with a `value` key,
+ // if there is a value, such as in the case of `@radius`.
+ //
+ // Once we've got our params list, and a closing `)`, we parse
+ // the `{...}` block.
+ //
+ public MixinDefinition MixinDefinition(Parser parser)
+ {
+ if ((parser.Tokenizer.CurrentChar != '.' && parser.Tokenizer.CurrentChar != '#') ||
+ parser.Tokenizer.Peek(@"[^{]*}"))
+ return null;
+
+ var index = parser.Tokenizer.Location.Index;
+
+ var memo = Remember(parser);
+
+ var match = parser.Tokenizer.Match(@"([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(");
+ if (!match)
+ return null;
+
+ //mixin definition ignores comments before it - a css hack can't be part of a mixin definition,
+ //so it may as well be a rule before the definition
+ PushComments();
+ GatherAndPullComments(parser); // no store as mixin definition not output
+
+ var name = match[1];
+ bool variadic = false;
+ var parameters = new NodeList();
+ RegexMatchResult param = null;
+ Node param2 = null;
+ Condition condition = null;
+ int i;
+ while (true)
+ {
+ i = parser.Tokenizer.Location.Index;
+ if (parser.Tokenizer.CurrentChar == '.' && parser.Tokenizer.Match("\\.{3}"))
+ {
+ variadic = true;
+ break;
+ }
+
+ if (param = parser.Tokenizer.Match(@"@[a-zA-Z0-9_-]+"))
+ {
+ GatherAndPullComments(parser);
+
+ if (parser.Tokenizer.Match(':'))
+ {
+ GatherComments(parser);
+ var value = Expect(Expression(parser), "Expected value", parser);
+
+ parameters.Add(NodeProvider.Rule(param.Value, value, parser.Tokenizer.GetNodeLocation(i)));
+ }
+ else if (parser.Tokenizer.Match("\\.{3}"))
+ {
+ variadic = true;
+ parameters.Add(NodeProvider.Rule(param.Value, null, true, parser.Tokenizer.GetNodeLocation(i)));
+ break;
+ }
+ else
+ parameters.Add(NodeProvider.Rule(param.Value, null, parser.Tokenizer.GetNodeLocation(i)));
+
+ } else if (param2 = Literal(parser) || Keyword(parser))
+ {
+ parameters.Add(NodeProvider.Rule(null, param2, parser.Tokenizer.GetNodeLocation(i)));
+ } else
+ break;
+
+ GatherAndPullComments(parser);
+
+ if (!(parser.Tokenizer.Match(',') || parser.Tokenizer.Match(';')))
+ break;
+
+ GatherAndPullComments(parser);
+ }
+
+ if (!parser.Tokenizer.Match(')'))
+ {
+ Recall(parser, memo);
+ }
+
+ GatherAndPullComments(parser);
+
+ if (parser.Tokenizer.Match("when"))
+ {
+ GatherAndPullComments(parser);
+
+ condition = Expect(Conditions(parser), "Expected conditions after when (mixin guards)", parser);
+ }
+
+ var rules = Block(parser);
+
+ PopComments();
+
+ if (rules != null)
+ return NodeProvider.MixinDefinition(name, parameters, rules, condition, variadic, parser.Tokenizer.GetNodeLocation(index));
+
+ Recall(parser, memo);
+
+ return null;
+ }
+
+ ///
+ /// a list of , seperated conditions (, == OR)
+ ///
+ public Condition Conditions(Parser parser)
+ {
+ Condition condition, nextCondition;
+
+ if (condition = Condition(parser)) {
+ while(parser.Tokenizer.Match(',')) {
+ nextCondition = Expect(Condition(parser), ", without recognised condition", parser);
+
+ condition = NodeProvider.Condition(condition, "or", nextCondition, false, parser.Tokenizer.GetNodeLocation());
+ }
+ return condition;
+ }
+
+ return null;
+ }
+
+ ///
+ /// A condition is used for mixin definitions and is made up
+ /// of left operation right
+ ///
+ public Condition Condition(Parser parser)
+ {
+ int index = parser.Tokenizer.Location.Index;
+ bool negate = false;
+ Condition condition;
+ //var a, b, c, op, index = i, negate = false;
+
+ if (parser.Tokenizer.Match("not"))
+ {
+ negate = true;
+ }
+
+ Expect(parser, '(');
+
+ Node left = Expect(Operation(parser) || Keyword(parser) || Quoted(parser), "unrecognised condition", parser);
+
+ var op = parser.Tokenizer.Match("(>=|=<|[<=>])");
+
+ if (op)
+ {
+ Node right = Expect(Operation(parser) || Keyword(parser) || Quoted(parser), "unrecognised right hand side condition expression", parser);
+
+ condition = NodeProvider.Condition(left, op.Value, right, negate, parser.Tokenizer.GetNodeLocation(index));
+ }
+ else
+ {
+ condition = NodeProvider.Condition(left, "=", NodeProvider.Keyword("true", parser.Tokenizer.GetNodeLocation(index)), negate, parser.Tokenizer.GetNodeLocation(index));
+ }
+
+ Expect(parser, ')');
+
+ if (parser.Tokenizer.Match("and"))
+ {
+ return NodeProvider.Condition(condition, "and", Condition(parser), false, parser.Tokenizer.GetNodeLocation(index));
+ }
+
+ return condition;
+ }
+
+ //
+ // Entities are the smallest recognized token,
+ // and can be found inside a rule's value.
+ //
+ public Node Entity(Parser parser)
+ {
+ return Literal(parser) || Variable(parser) || Url(parser) ||
+ Call(parser) || Keyword(parser) || Script(parser);
+ }
+
+ private Expression ExpressionOrExpressionList(Parser parser)
+ {
+ var memo = Remember(parser);
+
+ List entities = new List();
+
+ Expression entity;
+ while (entity = Expression(parser))
+ {
+ entities.Add(entity);
+
+ if (!parser.Tokenizer.Match(','))
+ {
+ break;
+ }
+ }
+
+ if (entities.Count == 0)
+ {
+ Recall(parser, memo);
+ return null;
+ }
+
+ if (entities.Count == 1)
+ {
+ return entities[0];
+ }
+
+ return new Expression(entities.Cast