Commit 535abaf8 authored by Tebjan Halm's avatar Tebjan Halm
Browse files

Merge pull request #90 from erdomke/master

Initial Work on W3C Test Compliance
parents 4b1ff3d4 bd05ecbc

// ReSharper disable once CheckNamespace
namespace ExCSS
{
internal abstract class NthChildSelector : BaseSelector, IToString
{
public int Step;
public int Offset;
internal string FunctionText { get; set; }
internal string FormatSelector(string functionName)
{
var format = Offset < 0
? ":{0}({1}n{2})"
: ":{0}({1}n+{2})";
return string.IsNullOrEmpty(FunctionText)
? string.Format(format, functionName, Step, Offset)
: string.Format(":{0}({1})", functionName, FunctionText);
}
public abstract override string ToString(bool friendlyFormat, int indentation = 0);
}
}
\ No newline at end of file

// ReSharper disable once CheckNamespace
namespace ExCSS
{
internal sealed class NthFirstChildSelector : NthChildSelector, IToString
{
public override string ToString(bool friendlyFormat, int indentation = 0)
{
return FormatSelector(PseudoSelectorPrefix.PseudoFunctionNthchild);
}
}
}
\ No newline at end of file
using System;
// ReSharper disable once CheckNamespace
namespace ExCSS
{
internal sealed class NthLastChildSelector : NthChildSelector, IToString
{
public override string ToString(bool friendlyFormat, int indentation = 0)
{
return FormatSelector(PseudoSelectorPrefix.PseudoFunctionNthlastchild);
}
}
}
\ No newline at end of file

// ReSharper disable once CheckNamespace
namespace ExCSS
{
internal sealed class NthLastOfTypeSelector : NthChildSelector, IToString
{
public override string ToString(bool friendlyFormat, int indentation = 0)
{
return FormatSelector(PseudoSelectorPrefix.PseudoFunctionNthLastOfType);
}
}
}
\ No newline at end of file

// ReSharper disable once CheckNamespace
namespace ExCSS
{
internal sealed class NthOfTypeSelector : NthChildSelector, IToString
{
public override string ToString(bool friendlyFormat, int indentation = 0)
{
return FormatSelector(PseudoSelectorPrefix.PseudoFunctionNthOfType);
}
}
}
\ No newline at end of file
using System;
using System.Globalization;
using ExCSS.Model;
using ExCSS.Model.TextBlocks;
// ReSharper disable once CheckNamespace
namespace ExCSS
{
internal sealed class SelectorFactory
{
private SelectorOperation _selectorOperation;
private BaseSelector _currentSelector;
private AggregateSelectorList _aggregateSelectorList;
private ComplexSelector _complexSelector;
private bool _hasCombinator;
private Combinator _combinator;
private SelectorFactory _nestedSelectorFactory;
private string _attributeName;
private string _attributeValue;
private string _attributeOperator;
internal SelectorFactory()
{
ResetFactory();
}
internal BaseSelector GetSelector()
{
if (_complexSelector != null)
{
_complexSelector.ConcludeSelector(_currentSelector);
_currentSelector = _complexSelector;
}
if (_aggregateSelectorList == null || _aggregateSelectorList.Length == 0)
{
return _currentSelector ?? SimpleSelector.All;
}
if (_currentSelector == null && _aggregateSelectorList.Length == 1)
{
return _aggregateSelectorList[0];
}
if (_currentSelector == null)
{
return _aggregateSelectorList;
}
_aggregateSelectorList.AppendSelector(_currentSelector);
_currentSelector = null;
return _aggregateSelectorList;
}
internal void Apply(Block token)
{
switch (_selectorOperation)
{
case SelectorOperation.Data:
ParseSymbol(token);
break;
case SelectorOperation.Class:
PraseClass(token);
break;
case SelectorOperation.Attribute:
ParseAttribute(token);
break;
case SelectorOperation.AttributeOperator:
ParseAttributeOperator(token);
break;
case SelectorOperation.AttributeValue:
ParseAttributeValue(token);
break;
case SelectorOperation.AttributeEnd:
ParseAttributeEnd(token);
break;
case SelectorOperation.PseudoClass:
ParsePseudoClass(token);
break;
case SelectorOperation.PseudoClassFunction:
ParsePseudoClassFunction(token);
break;
case SelectorOperation.PseudoClassFunctionEnd:
PrasePseudoClassFunctionEnd(token);
break;
case SelectorOperation.PseudoElement:
ParsePseudoElement(token);
break;
}
}
internal SelectorFactory ResetFactory()
{
_attributeName = null;
_attributeValue = null;
_attributeOperator = string.Empty;
_selectorOperation = SelectorOperation.Data;
_combinator = Combinator.Descendent;
_hasCombinator = false;
_currentSelector = null;
_aggregateSelectorList = null;
_complexSelector = null;
return this;
}
private void ParseSymbol(Block token)
{
switch (token.GrammarSegment)
{
// Attribute [A]
case GrammarSegment.SquareBraceOpen:
_attributeName = null;
_attributeValue = null;
_attributeOperator = string.Empty;
_selectorOperation = SelectorOperation.Attribute;
return;
// Pseudo :P
case GrammarSegment.Colon:
_selectorOperation = SelectorOperation.PseudoClass;
return;
// ID #I
case GrammarSegment.Hash:
Insert(SimpleSelector.Id(((SymbolBlock)token).Value));
return;
// Type E
case GrammarSegment.Ident:
Insert(SimpleSelector.Type(((SymbolBlock)token).Value));
return;
// Whitespace
case GrammarSegment.Whitespace:
Insert(Combinator.Descendent);
return;
case GrammarSegment.Delimiter:
ParseDelimiter(token);
return;
case GrammarSegment.Comma:
InsertCommaDelimited();
return;
}
}
private void ParseAttribute(Block token)
{
if (token.GrammarSegment == GrammarSegment.Whitespace)
{
return;
}
_selectorOperation = SelectorOperation.AttributeOperator;
switch (token.GrammarSegment)
{
case GrammarSegment.Ident:
_attributeName = ((SymbolBlock)token).Value;
break;
case GrammarSegment.String:
_attributeName = ((StringBlock)token).Value;
break;
default:
_selectorOperation = SelectorOperation.Data;
break;
}
}
private void ParseAttributeOperator(Block token)
{
if (token.GrammarSegment == GrammarSegment.Whitespace)
{
return;
}
_selectorOperation = SelectorOperation.AttributeValue;
if (token.GrammarSegment == GrammarSegment.SquareBracketClose)
{
ParseAttributeEnd(token);
}
else if (token is MatchBlock || token.GrammarSegment == GrammarSegment.Delimiter)
{
_attributeOperator = token.ToString();
}
else
{
_selectorOperation = SelectorOperation.AttributeEnd;
}
}
private void ParseAttributeValue(Block token)
{
if (token.GrammarSegment == GrammarSegment.Whitespace)
{
return;
}
_selectorOperation = SelectorOperation.AttributeEnd;
switch (token.GrammarSegment)
{
case GrammarSegment.Ident:
_attributeValue = ((SymbolBlock)token).Value;
break;
case GrammarSegment.String:
_attributeValue = ((StringBlock)token).Value;
break;
case GrammarSegment.Number:
_attributeValue = ((NumericBlock)token).Value.ToString(CultureInfo.InvariantCulture);
break;
default:
_selectorOperation = SelectorOperation.Data;
break;
}
}
private void ParseAttributeEnd(Block token)
{
if (token.GrammarSegment == GrammarSegment.Whitespace)
{
return;
}
_selectorOperation = SelectorOperation.Data;
if (token.GrammarSegment != GrammarSegment.SquareBracketClose)
{
return;
}
switch (_attributeOperator)
{
case "=":
Insert(SimpleSelector.AttributeMatch(_attributeName, _attributeValue));
break;
case "~=":
Insert(SimpleSelector.AttributeSpaceSeparated(_attributeName, _attributeValue));
break;
case "|=":
Insert(SimpleSelector.AttributeDashSeparated(_attributeName, _attributeValue));
break;
case "^=":
Insert(SimpleSelector.AttributeStartsWith(_attributeName, _attributeValue));
break;
case "$=":
Insert(SimpleSelector.AttributeEndsWith(_attributeName, _attributeValue));
break;
case "*=":
Insert(SimpleSelector.AttributeContains(_attributeName, _attributeValue));
break;
case "!=":
Insert(SimpleSelector.AttributeNegatedMatch(_attributeName, _attributeValue));
break;
default:
Insert(SimpleSelector.AttributeUnmatched(_attributeName));
break;
}
}
private void ParsePseudoClass(Block token)
{
_selectorOperation = SelectorOperation.Data;
switch (token.GrammarSegment)
{
case GrammarSegment.Colon:
_selectorOperation = SelectorOperation.PseudoElement;
break;
case GrammarSegment.Function:
_attributeName = ((SymbolBlock)token).Value;
_attributeValue = string.Empty;
_selectorOperation = SelectorOperation.PseudoClassFunction;
if (_nestedSelectorFactory != null)
{
_nestedSelectorFactory.ResetFactory();
}
break;
case GrammarSegment.Ident:
var pseudoSelector = GetPseudoSelector(token);
if (pseudoSelector != null)
{
Insert(pseudoSelector);
}
break;
}
}
private void ParsePseudoElement(Block token)
{
if (token.GrammarSegment != GrammarSegment.Ident)
{
return;
}
var data = ((SymbolBlock)token).Value;
switch (data)
{
case PseudoSelectorPrefix.PseudoElementBefore:
Insert(SimpleSelector.PseudoElement(PseudoSelectorPrefix.PseudoElementBefore));
break;
case PseudoSelectorPrefix.PseudoElementAfter:
Insert(SimpleSelector.PseudoElement(PseudoSelectorPrefix.PseudoElementAfter));
break;
case PseudoSelectorPrefix.PseudoElementSelection:
Insert(SimpleSelector.PseudoElement(PseudoSelectorPrefix.PseudoElementSelection));
break;
case PseudoSelectorPrefix.PseudoElementFirstline:
Insert(SimpleSelector.PseudoElement(PseudoSelectorPrefix.PseudoElementFirstline));
break;
case PseudoSelectorPrefix.PseudoElementFirstletter:
Insert(SimpleSelector.PseudoElement(PseudoSelectorPrefix.PseudoElementFirstletter));
break;
default:
Insert(SimpleSelector.PseudoElement(data));
break;
}
}
private void PraseClass(Block token)
{
_selectorOperation = SelectorOperation.Data;
if (token.GrammarSegment == GrammarSegment.Ident)
{
Insert(SimpleSelector.Class(((SymbolBlock)token).Value));
}
}
private void ParsePseudoClassFunction(Block token)
{
if (token.GrammarSegment == GrammarSegment.Whitespace)
{
return;
}
switch (_attributeName)
{
case PseudoSelectorPrefix.PseudoFunctionNthchild:
case PseudoSelectorPrefix.PseudoFunctionNthlastchild:
case PseudoSelectorPrefix.PseudoFunctionNthOfType:
case PseudoSelectorPrefix.PseudoFunctionNthLastOfType:
{
switch (token.GrammarSegment)
{
case GrammarSegment.Ident:
case GrammarSegment.Number:
case GrammarSegment.Dimension:
_attributeValue += token.ToString();
return;
case GrammarSegment.Delimiter:
var chr = ((DelimiterBlock)token).Value;
if (chr == Specification.PlusSign || chr == Specification.MinusSign)
{
_attributeValue += chr;
return;
}
break;
}
break;
}
case PseudoSelectorPrefix.PseudoFunctionNot:
{
if (_nestedSelectorFactory == null)
{
_nestedSelectorFactory = new SelectorFactory();
}
if (token.GrammarSegment != GrammarSegment.ParenClose || _nestedSelectorFactory._selectorOperation != SelectorOperation.Data)
{
_nestedSelectorFactory.Apply(token);
return;
}
break;
}
case PseudoSelectorPrefix.PseudoFunctionDir:
{
if (token.GrammarSegment == GrammarSegment.Ident)
{
_attributeValue = ((SymbolBlock)token).Value;
}
_selectorOperation = SelectorOperation.PseudoClassFunctionEnd;
return;
}
case PseudoSelectorPrefix.PseudoFunctionLang:
{
if (token.GrammarSegment == GrammarSegment.Ident)
{
_attributeValue = ((SymbolBlock)token).Value;
}
_selectorOperation = SelectorOperation.PseudoClassFunctionEnd;
return;
}
case PseudoSelectorPrefix.PseudoFunctionContains:
{
switch (token.GrammarSegment)
{
case GrammarSegment.String:
_attributeValue = ((StringBlock)token).Value;
break;
case GrammarSegment.Ident:
_attributeValue = ((SymbolBlock)token).Value;
break;
}
_selectorOperation = SelectorOperation.PseudoClassFunctionEnd;
return;
}
}
PrasePseudoClassFunctionEnd(token);
}
private void PrasePseudoClassFunctionEnd(Block token)
{
_selectorOperation = SelectorOperation.Data;
if (token.GrammarSegment != GrammarSegment.ParenClose)
{
return;
}
switch (_attributeName)
{
case PseudoSelectorPrefix.PseudoFunctionNthchild:
Insert(GetChildSelector<NthFirstChildSelector>());
break;
case PseudoSelectorPrefix.PseudoFunctionNthlastchild:
Insert(GetChildSelector<NthLastChildSelector>());
break;
case PseudoSelectorPrefix.PseudoFunctionNthOfType:
Insert(GetChildSelector<NthOfTypeSelector>());
break;
case PseudoSelectorPrefix.PseudoFunctionNthLastOfType:
Insert(GetChildSelector<NthLastOfTypeSelector>());
break;
case PseudoSelectorPrefix.PseudoFunctionNot:
{
var selector = _nestedSelectorFactory.GetSelector();
var code = string.Format("{0}({1})", PseudoSelectorPrefix.PseudoFunctionNot, selector);
Insert(SimpleSelector.PseudoClass(code));
break;
}
case PseudoSelectorPrefix.PseudoFunctionDir:
{
var code = string.Format("{0}({1})", PseudoSelectorPrefix.PseudoFunctionDir, _attributeValue);
Insert(SimpleSelector.PseudoClass(code));
break;
}
case PseudoSelectorPrefix.PseudoFunctionLang:
{
var code = string.Format("{0}({1})", PseudoSelectorPrefix.PseudoFunctionLang, _attributeValue);
Insert(SimpleSelector.PseudoClass(code));
break;
}
case PseudoSelectorPrefix.PseudoFunctionContains:
{
var code = string.Format("{0}({1})", PseudoSelectorPrefix.PseudoFunctionContains, _attributeValue);
Insert(SimpleSelector.PseudoClass(code));
break;
}
}
}
private void InsertCommaDelimited()
{
if (_currentSelector == null)
{
return;
}
if (_aggregateSelectorList == null)
{
_aggregateSelectorList = new AggregateSelectorList(",");
}
if (_complexSelector != null)
{
_complexSelector.ConcludeSelector(_currentSelector);
_aggregateSelectorList.AppendSelector(_complexSelector);
_complexSelector = null;
}
else
{
_aggregateSelectorList.AppendSelector(_currentSelector);
}
_currentSelector = null;
}
private void Insert(BaseSelector selector)
{
if (_currentSelector != null)
{
if (!_hasCombinator)
{
var compound = _currentSelector as AggregateSelectorList;
if (compound == null)
{
compound = new AggregateSelectorList("");
compound.AppendSelector(_currentSelector);
}
compound.AppendSelector(selector);
_currentSelector = compound;
}
else
{
if (_complexSelector == null)
{
_complexSelector = new ComplexSelector();
}
_complexSelector.AppendSelector(_currentSelector, _combinator);
_combinator = Combinator.Descendent;
_hasCombinator = false;
_currentSelector = selector;
}
}
else
{
if (_currentSelector == null && _complexSelector == null && _combinator == Combinator.Namespace)
{
_complexSelector = new ComplexSelector();
_complexSelector.AppendSelector(SimpleSelector.Type(""), _combinator);
_currentSelector = selector;
}
else
{
_combinator = Combinator.Descendent;
_hasCombinator = false;
_currentSelector = selector;
}
}
}
private void Insert(Combinator combinator)
{
_hasCombinator = true;
if (combinator != Combinator.Descendent)
{
_combinator = combinator;
}
}
private void ParseDelimiter(Block token)
{
switch (((DelimiterBlock)token).Value)
{
case Specification.Comma:
InsertCommaDelimited();
return;
case Specification.GreaterThan:
Insert(Combinator.Child);
return;
case Specification.PlusSign:
Insert(Combinator.AdjacentSibling);
return;
case Specification.Tilde:
Insert(Combinator.Sibling);
return;
case Specification.Asterisk:
Insert(SimpleSelector.All);
return;
case Specification.Period:
_selectorOperation = SelectorOperation.Class;
return;
case Specification.Pipe:
Insert(Combinator.Namespace);
return;
}
}
private BaseSelector GetChildSelector<T>() where T : NthChildSelector, new()
{
var selector = new T();
if (_attributeValue.Equals(PseudoSelectorPrefix.NthChildOdd, StringComparison.OrdinalIgnoreCase))
{
selector.Step = 2;
selector.Offset = 1;
selector.FunctionText = PseudoSelectorPrefix.NthChildOdd;
}
else if (_attributeValue.Equals(PseudoSelectorPrefix.NthChildEven, StringComparison.OrdinalIgnoreCase))
{
selector.Step = 2;
selector.Offset = 0;
selector.FunctionText = PseudoSelectorPrefix.NthChildEven;
}
else if (!int.TryParse(_attributeValue, out selector.Offset))
{
var index = _attributeValue.IndexOf(PseudoSelectorPrefix.NthChildN, StringComparison.OrdinalIgnoreCase);
if (_attributeValue.Length <= 0 || index == -1)
{
return selector;
}
var first = _attributeValue.Substring(0, index).Replace(" ", "");
var second = "";
if (_attributeValue.Length > index + 1)
{
second = _attributeValue.Substring(index + 1).Replace(" ", "");
}
if (first == string.Empty || (first.Length == 1 && first[0] == Specification.PlusSign))
{
selector.Step = 1;
}
else if (first.Length == 1 && first[0] == Specification.MinusSign)
{
selector.Step = -1;
}
else
{
int step;
if (int.TryParse(first, out step))
{
selector.Step = step;
}
}
if (second == string.Empty)
{
selector.Offset = 0;
}
else
{
int offset;
if (int.TryParse(second, out offset))
{
selector.Offset = offset;
}
}
}
return selector;
}
private static BaseSelector GetPseudoSelector(Block token)
{
switch (((SymbolBlock)token).Value)
{
case PseudoSelectorPrefix.PseudoRoot:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoRoot);
case PseudoSelectorPrefix.PseudoFirstOfType:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoFirstOfType);
case PseudoSelectorPrefix.PseudoLastoftype:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoLastoftype);
case PseudoSelectorPrefix.PseudoOnlychild:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoOnlychild);
case PseudoSelectorPrefix.PseudoOnlyOfType:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoOnlyOfType);
case PseudoSelectorPrefix.PseudoFirstchild:
return FirstChildSelector.Instance;
case PseudoSelectorPrefix.PseudoLastchild:
return LastChildSelector.Instance;
case PseudoSelectorPrefix.PseudoEmpty:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoEmpty);
case PseudoSelectorPrefix.PseudoLink:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoLink);
case PseudoSelectorPrefix.PseudoVisited:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoVisited);
case PseudoSelectorPrefix.PseudoActive:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoActive);
case PseudoSelectorPrefix.PseudoHover:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoHover);
case PseudoSelectorPrefix.PseudoFocus:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoFocus);
case PseudoSelectorPrefix.PseudoTarget:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoTarget);
case PseudoSelectorPrefix.PseudoEnabled:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoEnabled);
case PseudoSelectorPrefix.PseudoDisabled:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoDisabled);
case PseudoSelectorPrefix.PseudoDefault:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoDefault);
case PseudoSelectorPrefix.PseudoChecked:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoChecked);
case PseudoSelectorPrefix.PseudoIndeterminate:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoIndeterminate);
case PseudoSelectorPrefix.PseudoUnchecked:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoUnchecked);
case PseudoSelectorPrefix.PseudoValid:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoValid);
case PseudoSelectorPrefix.PseudoInvalid:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoInvalid);
case PseudoSelectorPrefix.PseudoRequired:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoRequired);
case PseudoSelectorPrefix.PseudoReadonly:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoReadonly);
case PseudoSelectorPrefix.PseudoReadwrite:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoReadwrite);
case PseudoSelectorPrefix.PseudoInrange:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoInrange);
case PseudoSelectorPrefix.PseudoOutofrange:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoOutofrange);
case PseudoSelectorPrefix.PseudoOptional:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoOptional);
case PseudoSelectorPrefix.PseudoElementBefore:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoElementBefore);
case PseudoSelectorPrefix.PseudoElementAfter:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoElementAfter);
case PseudoSelectorPrefix.PseudoElementFirstline:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoElementFirstline);
case PseudoSelectorPrefix.PseudoElementFirstletter:
return SimpleSelector.PseudoClass(PseudoSelectorPrefix.PseudoElementFirstletter);
default:
return SimpleSelector.PseudoClass(token.ToString());
}
}
}
}
using System.Collections;
using System.Collections.Generic;
// ReSharper disable once CheckNamespace
namespace ExCSS
{
public abstract class SelectorList : BaseSelector, IEnumerable<BaseSelector>
{
protected List<BaseSelector> Selectors;
protected SelectorList()
{
Selectors = new List<BaseSelector>();
}
public int Length
{
get { return Selectors.Count; }
}
public BaseSelector this[int index]
{
get { return Selectors[index]; }
set { Selectors[index] = value; }
}
public SelectorList AppendSelector(BaseSelector selector)
{
Selectors.Add(selector);
return this;
}
public SelectorList RemoveSelector(SimpleSelector selector)
{
Selectors.Remove(selector);
return this;
}
public SelectorList ClearSelectors()
{
Selectors.Clear();
return this;
}
public IEnumerator<BaseSelector> GetEnumerator()
{
return Selectors.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)Selectors).GetEnumerator();
}
public override abstract string ToString(bool friendlyFormat, int indentation = 0);
}
}
using System;
using ExCSS.Model;
// ReSharper disable once CheckNamespace
namespace ExCSS
{
public sealed class SimpleSelector : BaseSelector
{
private readonly string _code;
internal static readonly SimpleSelector All = new SimpleSelector("*");
public SimpleSelector(string selectorText)
{
_code = selectorText;
}
internal static SimpleSelector PseudoElement(string pseudoElement)
{
return new SimpleSelector("::" + pseudoElement);
}
internal static SimpleSelector PseudoClass(string pseudoClass)
{
return new SimpleSelector(":" + pseudoClass);
}
internal static SimpleSelector Class(string match)
{
return new SimpleSelector("." + match);
}
internal static SimpleSelector Id(string match)
{
return new SimpleSelector("#" + match);
}
internal static SimpleSelector AttributeUnmatched(string match)
{
return new SimpleSelector("[" + match + "]");
}
internal static SimpleSelector AttributeMatch(string match, string value)
{
var code = string.Format("[{0}=\"{1}\"]", match, GetValueAsString(value));
return new SimpleSelector(code);
}
internal static SimpleSelector AttributeNegatedMatch(string match, string value)
{
var code = string.Format("[{0}!=\"{1}\"]", match, GetValueAsString(value));
return new SimpleSelector(code);
}
internal static SimpleSelector AttributeSpaceSeparated(string match, string value)
{
var code = string.Format("[{0}~=\"{1}\"]", match, GetValueAsString(value));
return new SimpleSelector(code);
}
internal static SimpleSelector AttributeStartsWith(string match, string value)
{
var code = string.Format("[{0}^=\"{1}\"]", match, GetValueAsString(value));
return new SimpleSelector(code);
}
internal static SimpleSelector AttributeEndsWith(string match, string value)
{
var code = string.Format("[{0}$=\"{1}\"]", match, GetValueAsString(value));
return new SimpleSelector(code);
}
internal static SimpleSelector AttributeContains(string match, string value)
{
var code = string.Format("[{0}*=\"{1}\"]", match, GetValueAsString(value));
return new SimpleSelector(code);
}
internal static SimpleSelector AttributeDashSeparated(string match, string value)
{
var code = string.Format("[{0}|=\"{1}\"]", match, GetValueAsString(value));
return new SimpleSelector(code);
}
internal static SimpleSelector Type(string match)
{
return new SimpleSelector(match);
}
private static string GetValueAsString(string value)
{
var containsSpace = false;
for (var i = 0; i < value.Length; i++)
{
if (!value[i].IsSpaceCharacter())
{
continue;
}
containsSpace = true;
break;
}
if (!containsSpace)
{
return value;
}
if (value.IndexOf(Specification.SingleQuote) != -1)
{
return '"' + value + '"';
}
return "'" + value + "'";
}
public override string ToString(bool friendlyFormat, int indentation = 0)
{
return _code;
}
}
}
namespace ExCSS.Model
{
internal static class Specification
{
internal const char EndOfFile = (char)0x1a;
internal const char Tilde = (char)0x7e;
internal const char Pipe = (char)0x7c;
internal const char Null = (char)0x0;
internal const char Ampersand = (char)0x26;
internal const char Hash = (char)0x23;
internal const char DollarSign = (char)0x24;
internal const char Simicolon = (char)0x3b;
internal const char Asterisk = (char)0x2a;
internal const char EqualSign = (char)0x3d;
internal const char PlusSign = (char)0x2b;
internal const char Comma = (char)0x2c;
internal const char Period = (char)0x2e;
internal const char Accent = (char)0x5e;
internal const char At = (char)0x40;
internal const char LessThan = (char)0x3c;
internal const char GreaterThan = (char)0x3e;
internal const char SingleQuote = (char)0x27;
internal const char DoubleQuote = (char)0x22;
internal const char QuestionMark = (char)0x3f;
internal const char Tab = (char)0x09;
internal const char LineFeed = (char)0x0a;
internal const char CarriageReturn = (char)0x0d;
internal const char FormFeed = (char)0x0c;
internal const char Space = (char)0x20;
internal const char Solidus = (char)0x2f;
internal const char ReverseSolidus = (char)0x5c;
internal const char Colon = (char)0x3a;
internal const char Em = (char)0x21;
internal const char MinusSign = (char)0x2d;
internal const char Replacement = (char)0xfffd;
internal const char Underscore = (char)0x5f;
internal const char ParenOpen = (char)0x28;
internal const char ParenClose = (char)0x29;
internal const char Percent = (char)0x25;
internal const char SquareBracketOpen =(char)0x5b;
internal const char SquareBracketClose = (char)0x5d;
internal const char CurlyBraceOpen = (char)0x7b;
internal const char CurlyBraceClose = (char)0x7d;
internal const int MaxPoint = 0x10FFFF;/// The maximum allowed codepoint (defined in Unicode).
internal static bool IsNonPrintable(this char c)
{
return (c >= 0x0 && c <= 0x8) || (c >= 0xe && c <= 0x1f) || (c >= 0x7f && c <= 0x9f);
}
internal static bool IsLetter(this char c)
{
return IsUppercaseAscii(c) || IsLowercaseAscii(c);
}
internal static bool IsName(this char c)
{
return c >= 0x80 || c.IsLetter() || c == Underscore || c == MinusSign || IsDigit(c);
}
internal static bool IsNameStart(this char c)
{
return c >= 0x80 || IsUppercaseAscii(c) || IsLowercaseAscii(c) || c == Underscore;
}
internal static bool IsLineBreak(this char c)
{
//line feed, carriage return
return c == LineFeed || c == CarriageReturn;
}
internal static bool IsSpaceCharacter(this char c)
{
//white space, tab, line feed, form feed, carriage return
return c == Space || c == Tab || c == LineFeed || c == FormFeed || c == CarriageReturn;
}
internal static bool IsDigit(this char c)
{
return c >= 0x30 && c <= 0x39;
}
internal static bool IsUppercaseAscii(this char c)
{
return c >= 0x41 && c <= 0x5a;
}
internal static bool IsLowercaseAscii(this char c)
{
return c >= 0x61 && c <= 0x7a;
}
internal static bool IsHex(this char c)
{
return IsDigit(c) || (c >= 0x41 && c <= 0x46) || (c >= 0x61 && c <= 0x66);
}
}
}
\ No newline at end of file

namespace ExCSS.Model.TextBlocks
{
internal abstract class Block
{
internal GrammarSegment GrammarSegment { get;set; }
internal static PipeBlock Column
{
get { return PipeBlock.Token; }
}
internal static DelimiterBlock Delim(char value)
{
return new DelimiterBlock(value);
}
internal static NumericBlock Number(string value)
{
return new NumericBlock(value);
}
internal static RangeBlock Range(string start, string end)
{
return new RangeBlock().SetRange(start, end);
}
}
}

namespace ExCSS.Model.TextBlocks
{
internal class BracketBlock : Block
{
private readonly static BracketBlock RoundOpen= new BracketBlock { GrammarSegment = GrammarSegment.ParenOpen, _mirror = GrammarSegment.ParenClose };
private readonly static BracketBlock RoundClose = new BracketBlock { GrammarSegment = GrammarSegment.ParenClose, _mirror = GrammarSegment.ParenOpen };
private readonly static BracketBlock CurlyOpen = new BracketBlock { GrammarSegment = GrammarSegment.CurlyBraceOpen, _mirror = GrammarSegment.CurlyBracketClose };
private readonly static BracketBlock CurlyClose = new BracketBlock { GrammarSegment = GrammarSegment.CurlyBracketClose, _mirror = GrammarSegment.CurlyBraceOpen };
private readonly static BracketBlock SquareOpen = new BracketBlock { GrammarSegment = GrammarSegment.SquareBraceOpen, _mirror = GrammarSegment.SquareBracketClose };
private readonly static BracketBlock SquareClose = new BracketBlock { GrammarSegment = GrammarSegment.SquareBracketClose, _mirror = GrammarSegment.SquareBraceOpen };
private GrammarSegment _mirror;
BracketBlock()
{
}
internal char Open
{
get
{
switch (GrammarSegment)
{
case GrammarSegment.ParenOpen:
return '(';
case GrammarSegment.SquareBraceOpen:
return '[';
default:
return '{';
}
}
}
internal char Close
{
get
{
switch (GrammarSegment)
{
case GrammarSegment.ParenOpen:
return ')';
case GrammarSegment.SquareBraceOpen:
return ']';
default:
return '}';
}
}
}
internal GrammarSegment Mirror
{
get { return _mirror; }
}
internal static BracketBlock OpenRound
{
get { return RoundOpen; }
}
internal static BracketBlock CloseRound
{
get { return RoundClose; }
}
internal static BracketBlock OpenCurly
{
get { return CurlyOpen; }
}
internal static BracketBlock CloseCurly
{
get { return CurlyClose; }
}
internal static BracketBlock OpenSquare
{
get { return SquareOpen; }
}
internal static BracketBlock CloseSquare
{
get { return SquareClose; }
}
public override string ToString()
{
return ToString(false);
}
public string ToString(bool friendlyFormat, int indentation = 0)
{
switch (GrammarSegment)
{
case GrammarSegment.CurlyBraceOpen:
return "{";
case GrammarSegment.CurlyBracketClose:
return "}";
case GrammarSegment.ParenClose:
return ")";
case GrammarSegment.ParenOpen:
return "(";
case GrammarSegment.SquareBracketClose:
return "]";
case GrammarSegment.SquareBraceOpen:
return "[";
}
return string.Empty;
}
}
}

namespace ExCSS.Model.TextBlocks
{
internal abstract class CharacterBlock : Block
{
private readonly char _value;
protected CharacterBlock()
{
_value = Specification.Null;
}
protected CharacterBlock(char value)
{
_value = value;
}
internal char Value
{
get { return _value; }
}
}
}

namespace ExCSS.Model.TextBlocks
{
internal class CommentBlock : Block
{
private readonly static CommentBlock OpenBlock;
private readonly static CommentBlock CloseBlock;
static CommentBlock()
{
OpenBlock = new CommentBlock { GrammarSegment = GrammarSegment.CommentOpen };
CloseBlock = new CommentBlock { GrammarSegment = GrammarSegment.CommentClose };
}
CommentBlock()
{
}
internal static CommentBlock Open
{
get { return OpenBlock; }
}
internal static CommentBlock Close
{
get { return CloseBlock; }
}
public override string ToString()
{
return GrammarSegment == GrammarSegment.CommentOpen ? "<!--" : "-->";
}
}
}

using System.Globalization;
namespace ExCSS.Model.TextBlocks
{
internal class DelimiterBlock : CharacterBlock
{
internal DelimiterBlock()
{
GrammarSegment = GrammarSegment.Delimiter;
}
internal DelimiterBlock(char value) : base(value)
{
GrammarSegment = GrammarSegment.Delimiter;
}
public override string ToString()
{
return Value.ToString(CultureInfo.InvariantCulture);
}
}
}

namespace ExCSS.Model.TextBlocks
{
internal class MatchBlock : Block
{
internal readonly static MatchBlock Include = new MatchBlock { GrammarSegment = GrammarSegment.IncludeMatch };
internal readonly static MatchBlock Dash = new MatchBlock { GrammarSegment = GrammarSegment.DashMatch };
internal readonly static Block Prefix = new MatchBlock { GrammarSegment = GrammarSegment.PrefixMatch };
internal readonly static Block Substring = new MatchBlock { GrammarSegment = GrammarSegment.SubstringMatch };
internal readonly static Block Suffix = new MatchBlock { GrammarSegment = GrammarSegment.SuffixMatch };
internal readonly static Block Not = new MatchBlock { GrammarSegment = GrammarSegment.NegationMatch };
public override string ToString()
{
switch (GrammarSegment)
{
case GrammarSegment.SubstringMatch:
return "*=";
case GrammarSegment.SuffixMatch:
return "$=";
case GrammarSegment.PrefixMatch:
return "^=";
case GrammarSegment.IncludeMatch:
return "~=";
case GrammarSegment.DashMatch:
return "|=";
case GrammarSegment.NegationMatch:
return "!=";
}
return string.Empty;
}
}
}
using System;
using System.Globalization;
namespace ExCSS.Model.TextBlocks
{
internal class NumericBlock : Block
{
private readonly string _data;
internal NumericBlock(string number)
{
_data = number;
GrammarSegment = GrammarSegment.Number;
}
public Single Value
{
get { return Single.Parse(_data, CultureInfo.InvariantCulture); }
}
public override string ToString()
{
return _data;
}
}
}

namespace ExCSS.Model.TextBlocks
{
internal class PipeBlock : Block
{
private readonly static PipeBlock TokenBlock;
static PipeBlock()
{
TokenBlock = new PipeBlock();
}
PipeBlock()
{
GrammarSegment = GrammarSegment.Column;
}
internal static PipeBlock Token
{
get { return TokenBlock; }
}
public override string ToString()
{
return "||";
}
}
}
using System.Collections.Generic;
namespace ExCSS.Model.TextBlocks
{
internal class RangeBlock : Block
{
public RangeBlock()
{
GrammarSegment = GrammarSegment.Range;
}
internal bool IsEmpty
{
get { return SelectedRange == null || SelectedRange.Length == 0; }
}
internal string[] SelectedRange { get; private set; }
internal RangeBlock SetRange(string start, string end)
{
var startValue = int.Parse(start, System.Globalization.NumberStyles.HexNumber);
if (startValue > Specification.MaxPoint)
{
return this;
}
if (end == null)
{
SelectedRange = new [] { char.ConvertFromUtf32(startValue) };
}
else
{
var list = new List<string>();
var endValue = int.Parse(end, System.Globalization.NumberStyles.HexNumber);
if (endValue > Specification.MaxPoint)
{
endValue = Specification.MaxPoint;
}
for (; startValue <= endValue; startValue++)
{
list.Add(char.ConvertFromUtf32(startValue));
}
SelectedRange = list.ToArray();
}
return this;
}
public override string ToString()
{
if (IsEmpty)
{
return string.Empty;
}
if (SelectedRange.Length == 1)
{
return "#" + char.ConvertToUtf32(SelectedRange[0], 0).ToString("x");
}
return "#" + char.ConvertToUtf32(SelectedRange[0], 0).ToString("x") + "-#" +
char.ConvertToUtf32(SelectedRange[SelectedRange.Length - 1], 0).ToString("x");
}
}
}

namespace ExCSS.Model.TextBlocks
{
internal class SpecialCharacter : CharacterBlock
{
internal static readonly SpecialCharacter Colon = new SpecialCharacter(Specification.Colon, GrammarSegment.Colon);
internal static readonly SpecialCharacter Comma = new SpecialCharacter(Specification.Comma, GrammarSegment.Comma);
internal static readonly SpecialCharacter Semicolon = new SpecialCharacter(Specification.Simicolon, GrammarSegment.Semicolon);
internal static readonly SpecialCharacter Whitespace = new SpecialCharacter(Specification.Space, GrammarSegment.Whitespace);
SpecialCharacter(char specialCharacter, GrammarSegment type) : base(specialCharacter)
{
GrammarSegment = type;
}
public override string ToString()
{
return Value.ToString();
}
}
}

namespace ExCSS.Model.TextBlocks
{
internal class StringBlock : Block
{
StringBlock(GrammarSegment type)
{
GrammarSegment = type;
}
internal static StringBlock Plain(string data, bool bad = false)
{
return new StringBlock(GrammarSegment.String) { Value = data, IsBad = bad };
}
internal static StringBlock Url(string data, bool bad = false)
{
return new StringBlock(GrammarSegment.Url) { Value = data, IsBad = bad };
}
internal string Value { get; private set; }
internal bool IsBad { get; private set; }
public override string ToString()
{
if (GrammarSegment == GrammarSegment.Url)
{
return "url(" + Value + ")";
}
return "'" + Value + "'";
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment