Skip to content

Commit 9916ad1

Browse files
author
Tyler Brinks
committed
Page rule pseudo class and margin updates
1 parent 38c35ae commit 9916ad1

16 files changed

Lines changed: 279 additions & 167 deletions

src/ExCSS.Tests/Cases.cs

Lines changed: 12 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -823,65 +823,23 @@ tobi loki jane {
823823
}
824824
}
825825

826-
[Fact]
827-
public void StyleSheetPageLinebreak()
828-
{
829-
var sheet = ParseSheet(@"@page
830-
toc
831-
{
832-
color: black;
833-
}");
834-
Assert.Equal(1, sheet.Rules.Length);
835-
}
836-
837826
[Fact]
838-
public void StyleSheetPageAtRules()
827+
public void StyleSheetPageAtRulesAndProperties()
839828
{
840-
var sheet = ParseSheet(@"@page {
841-
size: A4;
842-
margin: 30mm 15mm;
843-
844-
@bottom-right {
845-
content: counter(page);
846-
}
847-
}");
848-
var x = sheet.ToCss();
829+
var sheet = ParseSheet(@"@page :left{size:A4;margin:30mm 15mm;@bottom-right{color:black;}}");
830+
Assert.Equal(1, sheet.Rules.Length);
831+
Assert.Equal(RuleType.Page, sheet.Rules[0].Type);
849832
Assert.Equal(1, sheet.Rules.Length);
850-
}
851-
852-
[Fact]
853-
public void StyleSheetPagedMedia()
854-
{
855-
var sheet = ParseSheet(@"/* toc above */
856-
@page toc, index:blank {
857-
/* toc inside */
858-
color: green;
859-
}
860-
861-
@page {
862-
font-size: 16pt;
863-
color: #f00;
864-
}
865-
866-
@page :left {
867-
margin-left: 5cm;
868-
}");
869-
Assert.Equal(3, sheet.Rules.Length);
870-
871-
var page1 = sheet.Rules[0] as PageRule;
872-
var page2 = sheet.Rules[1] as PageRule;
873-
var page3 = sheet.Rules[2] as PageRule;
874-
875-
Assert.Equal(1, page1.Style.Length);
876-
Assert.Equal("green", page1.Style["color"]);
877833

878-
Assert.Equal(2, page2.Style.Length);
879-
Assert.Equal("16pt", page2.Style["font-size"]);
880-
Assert.Equal("#f00", page2.Style["color"]);
834+
var pageRule = sheet.Rules[0] as PageRule;
835+
Assert.Equal(RuleType.Page, pageRule.Type);
836+
Assert.Equal(":left", pageRule.SelectorText);
881837

882-
Assert.Equal(1, page3.Style.Length);
883-
Assert.Equal("5cm", page3.Style["margin-left"]);
884-
}
838+
var marginRule = pageRule.Children.Last() as MarginStyleRule;
839+
Assert.Equal("@bottom-right", marginRule.SelectorText);
840+
Assert.Equal(1, marginRule.Style.Children.Count());
841+
Assert.Equal("color: black", (marginRule.Style.Children.First() as UnknownProperty).CssText);
842+
}
885843

886844
[Fact]
887845
public void StyleSheetProps()

src/ExCSS/Model/ISelector.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{
33
public interface ISelector : IStylesheetNode
44
{
5-
Priority Specifity { get; }
5+
Priority Specificity { get; }
66
string Text { get; }
77
}
88
}

src/ExCSS/Model/StyleDeclaration.cs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,13 @@ internal Property GetProperty(string name)
214214
internal void SetProperty(Property property)
215215
{
216216
if (property is ShorthandProperty shorthand)
217+
{
217218
SetShorthand(shorthand);
219+
}
218220
else
221+
{
219222
SetLonghand(property);
223+
}
220224
}
221225

222226
internal void SetDeclarations(IEnumerable<Property> declarations)
@@ -233,33 +237,36 @@ private void ChangeDeclarations(IEnumerable<Property> declarations, Predicate<Pr
233237
Func<Property, Property, bool> removeExisting)
234238
{
235239
var propertyList = new List<Property>();
236-
foreach (var newdecl in declarations)
240+
foreach (var newDeclaration in declarations)
237241
{
238-
var skip = defaultSkip(newdecl);
239-
foreach (var olddecl in Declarations)
242+
var skip = defaultSkip(newDeclaration);
243+
foreach (var oldDeclaration in Declarations)
240244
{
241-
if (!olddecl.Name.Is(newdecl.Name)) continue;
245+
if (!oldDeclaration.Name.Is(newDeclaration.Name)) continue;
242246

243-
if (removeExisting(olddecl, newdecl))
244-
RemoveChild(olddecl);
247+
if (removeExisting(oldDeclaration, newDeclaration))
248+
RemoveChild(oldDeclaration);
245249
else
246250
skip = true;
247251
break;
248252
}
249253

250-
if (!skip) propertyList.Add(newdecl);
254+
if (!skip) propertyList.Add(newDeclaration);
251255
}
252256

253257
foreach (var declaration in propertyList) AppendChild(declaration);
254258
}
255259

256260
private void SetLonghand(Property property)
257261
{
258-
foreach (var declaration in Declarations)
262+
if (!_parser.Options.PreserveDuplicateProperties)
259263
{
260-
if (!declaration.Name.Is(property.Name)) continue;
261-
RemoveChild(declaration);
262-
break;
264+
foreach (var declaration in Declarations)
265+
{
266+
if (!declaration.Name.Is(property.Name)) continue;
267+
RemoveChild(declaration);
268+
break;
269+
}
263270
}
264271

265272
AppendChild(property);

src/ExCSS/Parser/StylesheetComposer.cs

Lines changed: 115 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -245,31 +245,17 @@ public Rule CreatePage(Token current)
245245
var token = NextToken();
246246
_nodes.Push(rule);
247247
ParseComments(ref token);
248-
rule.Selector = CreateSelector(ref token);
249-
ParseComments(ref token);
250248

251-
if (token.Type == TokenType.CurlyBracketOpen)
249+
if (token.Type != TokenType.CurlyBracketOpen)
252250
{
253-
var end = FillDeclarations(rule.Style);
254-
rule.StylesheetText = CreateView(start, end);
255-
_nodes.Pop();
256-
return rule;
251+
// A pseudo-selector exists. Parse it prior
252+
// to declarations
253+
// e.g. @page :left{...}
254+
rule.Selector = CreatePageSelector(ref token);
255+
ParseComments(ref token);
256+
token = NextToken();
257257
}
258258

259-
_nodes.Pop();
260-
return SkipDeclarations(token);
261-
}
262-
263-
public Rule CreateMarginRule(ref Token token)
264-
{
265-
var rule = new MarginRule(_parser, token.Data);
266-
var start = token.Position;
267-
token = NextToken();
268-
_nodes.Push(rule);
269-
ParseComments(ref token);
270-
rule.Selector = CreateSelector(ref token);
271-
ParseComments(ref token);
272-
273259
if (token.Type == TokenType.CurlyBracketOpen)
274260
{
275261
var end = FillDeclarations(rule.Style);
@@ -317,6 +303,19 @@ public Rule CreateStyle(Token current)
317303
return rule.Selector != null ? rule : null;
318304
}
319305

306+
public Rule CreateMarginStyle(ref Token current)
307+
{
308+
var rule = new MarginStyleRule(_parser);
309+
var start = current.Position;
310+
_nodes.Push(rule);
311+
ParseComments(ref current);
312+
rule.Selector = CreateMarginSelector(ref current);
313+
var end = FillDeclarations(rule.Style);
314+
rule.StylesheetText = CreateView(start, end);
315+
_nodes.Pop();
316+
return rule.Selector != null ? rule : null;
317+
}
318+
320319
public KeyframeRule CreateKeyframeRule(Token current)
321320
{
322321
var rule = new KeyframeRule(_parser);
@@ -445,14 +444,21 @@ public KeyframeSelector CreateKeyframeSelector(ref Token token)
445444
ParseComments(ref token);
446445
}
447446

448-
if (token.Type == TokenType.Percentage)
449-
keys.Add(new Percent(((UnitToken) token).Value));
450-
else if (token.Type == TokenType.Ident && token.Data.Is(Keywords.From))
451-
keys.Add(Percent.Zero);
452-
else if (token.Type == TokenType.Ident && token.Data.Is(Keywords.To))
453-
keys.Add(Percent.Hundred);
454-
else
455-
valid = false;
447+
switch (token.Type)
448+
{
449+
case TokenType.Percentage:
450+
keys.Add(new Percent(((UnitToken) token).Value));
451+
break;
452+
case TokenType.Ident when token.Data.Is(Keywords.From):
453+
keys.Add(Percent.Zero);
454+
break;
455+
case TokenType.Ident when token.Data.Is(Keywords.To):
456+
keys.Add(Percent.Hundred);
457+
break;
458+
default:
459+
valid = false;
460+
break;
461+
}
456462

457463
token = NextToken();
458464
ParseComments(ref token);
@@ -463,6 +469,48 @@ public KeyframeSelector CreateKeyframeSelector(ref Token token)
463469
return new KeyframeSelector(keys);
464470
}
465471

472+
private PageSelector CreatePageSelector(ref Token token)
473+
{
474+
PageSelector selector;
475+
476+
if (token.Type == TokenType.Colon)
477+
{
478+
// Add the pseudo class selector
479+
token = NextToken();
480+
selector = token.Type == TokenType.Ident ? new PageSelector(token.Data) : new PageSelector();
481+
}
482+
else
483+
{
484+
selector = new PageSelector();
485+
}
486+
487+
//var start = token.Position;
488+
489+
//while (token.IsNot(TokenType.EndOfFile, TokenType.CurlyBracketOpen, TokenType.CurlyBracketClose))
490+
//{
491+
// var a = 1;
492+
// token = NextToken();
493+
//}
494+
495+
//var result = selector.ToPool();
496+
497+
//if (result is StylesheetNode node)
498+
//{
499+
// var end = token.Position.Shift(-1);
500+
//node.StylesheetText = CreateView(start, end);
501+
//}
502+
503+
//if (!selectorIsValid && !_parser.Options.AllowInvalidValues)
504+
//{
505+
// RaiseErrorOccurred(ParseError.InvalidSelector, start);
506+
// result = null;
507+
//}
508+
509+
//return result;
510+
511+
return selector;
512+
}
513+
466514
public List<DocumentFunction> CreateFunctions(ref Token token)
467515
{
468516
var functions = new List<DocumentFunction>();
@@ -492,8 +540,14 @@ public TextPosition FillDeclarations(StyleDeclaration style)
492540
var parentPageRule = _nodes.FirstOrDefault(parent => parent is PageRule);
493541
if (parentPageRule != null)
494542
{
495-
var genericAtRule = CreateMarginRule(ref token);
496-
parentPageRule.AppendChild(genericAtRule);
543+
//var genericAtRule = CreateMarginRule(ref token);
544+
//parentPageRule.AppendChild(genericAtRule);
545+
// Rewind to capture the margin's @ symbol
546+
547+
var marginToken = new Token(TokenType.Ident, token.Data, token.Position);
548+
var marginStyle = CreateMarginStyle(ref marginToken);
549+
parentPageRule.AppendChild(marginStyle);
550+
token = marginToken;
497551
}
498552
}
499553
else
@@ -850,8 +904,7 @@ private IConditionFunction DeclarationCondition(ref Token token)
850904
return declaration;
851905
}
852906

853-
private List<IConditionFunction> MultipleConditions(IConditionFunction condition, string connector,
854-
ref Token token)
907+
private List<IConditionFunction> MultipleConditions(IConditionFunction condition, string connector, ref Token token)
855908
{
856909
var list = new List<IConditionFunction>();
857910
ParseComments(ref token);
@@ -1004,6 +1057,35 @@ private ISelector CreateSelector(ref Token token)
10041057
return result;
10051058
}
10061059

1060+
private ISelector CreateMarginSelector(ref Token token)
1061+
{
1062+
var selector = _parser.GetSelectorCreator();
1063+
var start = token.Position;
1064+
1065+
while (token.IsNot(TokenType.EndOfFile, TokenType.CurlyBracketOpen, TokenType.CurlyBracketClose))
1066+
{
1067+
selector.Apply(token);
1068+
token = NextToken();
1069+
}
1070+
1071+
var selectorIsValid = selector.IsValid;
1072+
var result = selector.ToPool();
1073+
1074+
if (result is StylesheetNode node)
1075+
{
1076+
var end = token.Position.Shift(-1);
1077+
node.StylesheetText = CreateView(start, end);
1078+
}
1079+
1080+
if (!selectorIsValid && !_parser.Options.AllowInvalidValues)
1081+
{
1082+
RaiseErrorOccurred(ParseError.InvalidSelector, start);
1083+
result = null;
1084+
}
1085+
1086+
return result;
1087+
}
1088+
10071089
private TokenValue CreateValue(TokenType closing, ref Token token, out bool important)
10081090
{
10091091
var value = Pool.NewValueBuilder();

src/ExCSS/Parser/StylesheetParser.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ public StylesheetParser(
1818
bool tolerateInvalidSelectors = false,
1919
bool tolerateInvalidValues = false,
2020
bool tolerateInvalidConstraints = false,
21-
bool preserveComments = false
21+
bool preserveComments = false,
22+
bool preserveDuplicateProperties = false
2223
)
2324
{
2425
Options = new ParserOptions

0 commit comments

Comments
 (0)