public void WriteSelectorBlock(SelectorAndBlock block) { WriteSelector(block.Selector); StartClass(); foreach (var rule in block.Properties.Cast <NameValueProperty>()) { WriteRule(rule); } EndClass(); }
private static SelectorAndBlock CacheBreakBlock(SelectorAndBlock block) { var ret = new List <Property>(); foreach (var prop in block.Properties.Cast <NameValueProperty>()) { ret.Add(CacheBreakProperty(prop)); } return(new SelectorAndBlock(block.Selector, ret, block.ResetContext, block.Start, block.Stop, block.FilePath)); }
private static void Verify(SelectorAndBlock block) { var nestedBlock = block.Properties.OfType <NestedBlockProperty>().ToList(); var innerMedia = block.Properties.OfType <InnerMediaProperty>().ToList(); if (nestedBlock.Count != 0) { throw new InvalidOperationException("It shouldn't be possible for nested blocks to remain here"); } foreach (var media in innerMedia) { Current.RecordError(ErrorType.Compiler, media, "@media blocks cannot be nested within each other"); } }
private static SelectorAndBlock PrefixBlock(SelectorAndBlock block) { var ret = new List <Property>(); var asNameValue = block.Properties.Cast <NameValueProperty>().ToList(); foreach (var prop in asNameValue) { var possible = PrefixProperty(prop); // No prefix versions, no point in any conflict checking; just put it back and continue if (possible == null) { ret.Add(prop); continue; } var alreadyPresent = asNameValue.Where( w => possible.Any(p => p.Name.Equals(w.Name, StringComparison.InvariantCultureIgnoreCase) && p.Name != prop.Name ) ).ToList(); foreach (var dupe in alreadyPresent) { var dupeOf = possible.Where(w => w.Name == dupe.Name).ToList(); if (dupeOf.All(d => d.Value.Equals(dupe.Value))) { Current.RecordInfo("Prefixed property " + dupe.Name + " in '" + block.Selector + "' could have been generated automatically"); } } possible.RemoveAll(x => alreadyPresent.Any(y => y.Name.Equals(x.Name, StringComparison.InvariantCultureIgnoreCase))); ret.AddRange(possible); } return(new SelectorAndBlock(block.Selector, ret, block.ResetContext, block.Start, block.Stop, block.FilePath)); }
public void WriteSelectorBlock(SelectorAndBlock block) { WriteSelector(block.Selector); StartClass(); var properties = block.Properties.Cast <NameValueProperty>(); var firstN = properties.Take(properties.Count() - 1); var last = properties.LastOrDefault(); foreach (var rule in firstN) { WriteRule(rule, lastRule: false); } if (last != null) { WriteRule(last, lastRule: true); } EndClass(); }
internal static InnerMediaProperty ParseInnerMediaDirective(ParserStream stream) { var start = stream.Position; var media = new StringBuilder(); stream.ScanUntil(media, '{'); var mediaStr = media.ToString().Trim(); if (mediaStr.IsNullOrEmpty()) { Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected media list"); throw new StoppedParsingException(); } var mediaQuery = MediaQueryParser.Parse(mediaStr, Position.Create(start, stream.Position, Current.CurrentFilePath)); var props = ParseCssRules(InvalidSelector.Singleton, stream); var blockEquiv = new SelectorAndBlock(InvalidSelector.Singleton, props, null, start, stream.Position, Current.CurrentFilePath); return(new InnerMediaProperty(mediaQuery, blockEquiv, start, stream.Position, Current.CurrentFilePath)); }
public static List <Block> Task(List <Block> statements) { var ret = new List <Block>(); var mixins = MapAndWarnDupe(statements.OfType <MixinBlock>(), s => s.Name, v => v); var variables = MapAndWarnDupe(statements.OfType <MoreVariable>(), s => s.Name, v => v.Value); var globalScope = new Scope(variables, mixins); Current.SetGlobalScope(globalScope); foreach (var charset in statements.OfType <CssCharset>()) { ret.Add(charset.Bind(globalScope)); } foreach (var import in statements.OfType <Model.Import>()) { ret.Add(import.Bind(globalScope)); } foreach (var block in statements.OfType <SelectorAndBlock>()) { ret.Add(block.BindAndEvaluateMixins()); } foreach (var animation in statements.OfType <KeyFramesBlock>()) { var innerVariables = MapAndWarnDupe(animation.Variables, s => s.Name, s => s.Value); var innerScope = globalScope.Push(innerVariables, new Dictionary <string, MixinBlock>(), animation); var frames = new List <KeyFrame>(); foreach (var frame in animation.Frames) { var blockEquivalent = new SelectorAndBlock(InvalidSelector.Singleton, frame.Properties, null, frame.Start, frame.Stop, frame.FilePath); var bound = blockEquivalent.BindAndEvaluateMixins(innerScope); frames.Add(new KeyFrame(frame.Percentages.ToList(), bound.Properties.ToList(), frame.Start, frame.Stop, frame.FilePath)); } ret.Add(new KeyFramesBlock(animation.Prefix, animation.Name, frames.ToList(), animation.Variables.ToList(), animation.Start, animation.Stop, animation.FilePath)); } foreach (var media in statements.OfType <MediaBlock>()) { var innerRet = new List <Block>(); var innerVariable = MapAndWarnDupe(media.Blocks.OfType <MoreVariable>(), s => s.Name, v => v.Value); var innerScope = globalScope.Push(innerVariable, new Dictionary <string, MixinBlock>(), media); foreach (var block in media.Blocks.OfType <SelectorAndBlock>()) { innerRet.Add(block.BindAndEvaluateMixins(innerScope)); } ret.Add(new MediaBlock(media.MediaQuery.Bind(globalScope), innerRet, media.Start, media.Stop, media.FilePath)); } foreach (var font in statements.OfType <FontFaceBlock>()) { var blockEquiv = new SelectorAndBlock(InvalidSelector.Singleton, font.Properties, null, font.Start, font.Stop, font.FilePath); var bound = blockEquiv.BindAndEvaluateMixins(globalScope); ret.Add(new FontFaceBlock(bound.Properties.ToList(), font.Start, font.Stop, font.FilePath)); } return(ret); }
private static List <Block> Impl(List <Block> blocks, List <SelectorAndBlock> parent = null) { var ret = new List <Block>(); var forLookup = blocks.OfType <SelectorAndBlock>().ToList(); // At this point, unrolled blocks have only NameValue (unevaluated) and Includes foreach (var statement in blocks) { var block = statement as SelectorAndBlock; var media = statement as MediaBlock; var keyframes = statement as KeyFramesBlock; var fontface = statement as FontFaceBlock; if (block == null && media == null && keyframes == null && fontface == null) { ret.Add(statement); continue; } if (block != null) { var other = block.Properties.Where(w => !(w is NameValueProperty || w is IncludeSelectorProperty)).ToList(); var simple = block.Properties.OfType <NameValueProperty>().ToList(); var includes = block.Properties.OfType <IncludeSelectorProperty>(); var @override = new List <NameValueProperty>(); includes.Each(e => { if (e.Overrides) { @override.AddRange(e.LookupMatch(forLookup, parent: parent)); } else { simple.AddRange(e.LookupMatch(forLookup, parent: parent)); } } ); var doubleDefined = false; foreach (var e in @override.GroupBy(g => g.Name).Where(g => g.Count() > 1)) { Current.RecordError(ErrorType.Compiler, block, "After resolving selector includes, the [" + e.Key + "] rule would be included as an override " + e.Count() + " times."); doubleDefined = true; } if (doubleDefined) { throw new StoppedCompilingException(); } foreach (var o in @override) { simple.RemoveAll(e => e.Name == o.Name); simple.Add(o); } other.AddRange(simple); ret.Add(new SelectorAndBlock(block.Selector, other, block.ResetContext, block.Start, block.Stop, block.FilePath)); } if (media != null) { var subBlocks = media.Blocks.ToList(); var copied = Impl(subBlocks, parent: forLookup); ret.Add(new MediaBlock(media.MediaQuery, copied, media.Start, media.Stop, media.FilePath)); } if (keyframes != null) { var frames = new List <KeyFrame>(); foreach (var frame in keyframes.Frames) { var blockEquiv = new SelectorAndBlock(InvalidSelector.Singleton, frame.Properties, null, frame.Start, frame.Stop, frame.FilePath); var copied = Impl(new List <Block>() { blockEquiv }, parent: parent); frames.Add(new KeyFrame(frame.Percentages.ToList(), ((SelectorAndBlock)copied[0]).Properties.ToList(), frame.Start, frame.Stop, frame.FilePath)); } ret.Add(new KeyFramesBlock(keyframes.Prefix, keyframes.Name, frames, keyframes.Variables.ToList(), keyframes.Start, keyframes.Stop, keyframes.FilePath)); } if (fontface != null) { var blockEquiv = new SelectorAndBlock(InvalidSelector.Singleton, fontface.Properties, null, fontface.Start, fontface.Stop, fontface.FilePath); var copied = (SelectorAndBlock)Impl(new List <Block>() { blockEquiv }, parent)[0]; ret.Add(new FontFaceBlock(copied.Properties.ToList(), copied.Start, copied.Stop, copied.FilePath)); } } return(ret); }
public static List <Block> Task(List <Block> blocks) { var ret = new List <Block>(); ret.AddRange(blocks.OfType <CssCharset>()); ret.AddRange(blocks.OfType <Model.Import>().Select(s => new Model.Import(MinifyValue(s.ToImport), ForQuery(s.MediaQuery), s.Start, s.Stop, s.FilePath))); ret.AddRange(blocks.Where(w => w is SelectorAndBlock && ((SelectorAndBlock)w).IsReset)); var remainder = blocks.Where(w => !ret.Contains(w)); foreach (var statement in remainder) { var block = statement as SelectorAndBlock; if (block != null) { var rules = new List <Property>(); foreach (var prop in block.Properties) { rules.Add(MinifyProperty(prop)); } rules = MinifyPropertyList(rules).ToList(); ret.Add(new SelectorAndBlock(block.Selector, rules, block.ResetContext, block.Start, block.Stop, block.FilePath)); continue; } var media = statement as MediaBlock; if (media != null) { var subStatements = Task(media.Blocks.ToList()); ret.Add(new MediaBlock(ForQuery(media.MediaQuery), subStatements, media.Start, media.Stop, media.FilePath)); continue; } var keyframes = statement as KeyFramesBlock; if (keyframes != null) { var frames = new List <KeyFrame>(); // minify each frame foreach (var frame in keyframes.Frames) { var blockEquiv = new SelectorAndBlock(InvalidSelector.Singleton, frame.Properties, null, frame.Start, frame.Stop, frame.FilePath); var mind = Task(new List <Block>() { blockEquiv }); frames.Add(new KeyFrame(frame.Percentages.ToList(), ((SelectorAndBlock)mind[0]).Properties.ToList(), frame.Start, frame.Stop, frame.FilePath)); } // collapse frames if rules are identical var frameMap = frames.ToDictionary( d => { using (var str = new StringWriter()) using (var css = new MinimalCssWriter(str)) { foreach (var rule in d.Properties.Cast <NameValueProperty>()) { css.WriteRule(rule, lastRule: false); } return(str.ToString()); } }, d => d ); frames.Clear(); foreach (var frame in frameMap.GroupBy(k => k.Key)) { var allPercents = new List <decimal>(); foreach (var f in frame) { allPercents.AddRange(f.Value.Percentages); } var urFrame = frame.First().Value; frames.Add(new KeyFrame(allPercents, urFrame.Properties.ToList(), urFrame.Start, urFrame.Stop, urFrame.FilePath)); } ret.Add(new KeyFramesBlock(keyframes.Prefix, keyframes.Name, frames, keyframes.Variables.ToList(), keyframes.Start, keyframes.Stop, keyframes.FilePath)); continue; } ret.Add(statement); } return(ret); }
private static IEnumerable <Block> Unroll(SelectorAndBlock block) { var ret = new List <Block>(); var props = new List <Property>(); foreach (var prop in block.Properties) { var media = prop as InnerMediaProperty; var nested = prop as NestedBlockProperty; if (media == null && nested == null) { props.Add(prop); continue; } if (nested != null) { var inner = Unroll(nested.Block); var innerMedia = inner.OfType <MediaBlock>(); var other = inner.Where(i => !(i is MediaBlock)).Cast <SelectorAndBlock>(); props.AddRange(other.Select(s => new NestedBlockProperty(s, s.Start, s.Stop))); foreach (var m in innerMedia) { var selBlock = new SelectorAndBlock( block.Selector, m.Blocks.Cast <SelectorAndBlock>().Select(s => new NestedBlockProperty(s, s.Start, s.Stop)), null, m.Start, m.Stop, m.FilePath ); var newMedia = new MediaBlock( m.MediaQuery, new List <Block> { selBlock }, m.Start, m.Stop, m.FilePath ); ret.Add(newMedia); } continue; } var unrolled = new MediaBlock( media.MediaQuery, new List <Block> { new SelectorAndBlock( block.Selector, media.Block.Properties, null, -1, -1, media.FilePath ) }, -1, -1, media.FilePath ); ret.Add(unrolled); } ret.Add(new SelectorAndBlock(block.Selector, props, null, block.Start, block.Stop, block.FilePath)); return(ret); }
public static List <Block> Task(List <Block> blocks) { var ret = new List <Block>(); foreach (var statement in blocks) { var block = statement as SelectorAndBlock; var media = statement as MediaBlock; var keyframes = statement as KeyFramesBlock; var fontface = statement as FontFaceBlock; if (block == null && media == null && keyframes == null && fontface == null) { ret.Add(statement); continue; } if (block != null) { var rules = new List <NameValueProperty>(); foreach (var group in block.Properties.Cast <NameValueProperty>().GroupBy(g => g.Name)) { if (group.Count() == 1) { rules.Add(group.Single()); } else { var important = group.SingleOrDefault(g => g.Value.IsImportant()); if (important == null) { Current.RecordWarning(ErrorType.Compiler, block, "More than one definition for [" + group.Key + "], did you mean for one to be !important?"); rules.AddRange(group); } else { rules.Add(important); } } } ret.Add(new SelectorAndBlock(block.Selector, rules, block.ResetContext, block.Start, block.Stop, block.FilePath)); } if (media != null) { var resolved = Task(media.Blocks.ToList()); ret.Add(new MediaBlock(media.MediaQuery, resolved, media.Start, media.Stop, media.FilePath)); } if (keyframes != null) { var frames = new List <KeyFrame>(); foreach (var frame in keyframes.Frames) { var blockEquiv = new SelectorAndBlock(InvalidSelector.Singleton, frame.Properties, null, frame.Start, frame.Stop, frame.FilePath); var resolved = Task(new List <Block>() { blockEquiv }); frames.Add(new KeyFrame(frame.Percentages.ToList(), ((SelectorAndBlock)resolved[0]).Properties.ToList(), frame.Stop, frame.Stop, frame.FilePath)); } ret.Add(new KeyFramesBlock(keyframes.Prefix, keyframes.Name, frames, keyframes.Variables.ToList(), keyframes.Start, keyframes.Stop, keyframes.FilePath)); } if (fontface != null) { var blockEquiv = new SelectorAndBlock(InvalidSelector.Singleton, fontface.Properties, null, fontface.Start, fontface.Stop, fontface.FilePath); var resolved = (SelectorAndBlock)Task(new List <Block>() { blockEquiv })[0]; ret.Add(new FontFaceBlock(resolved.Properties.ToList(), fontface.Start, fontface.Stop, fontface.FilePath)); } } return(ret); }
public static List <Block> Task(List <Block> blocks) { var ret = new List <Block>(); foreach (var statement in blocks) { var block = statement as SelectorAndBlock; var media = statement as MediaBlock; var keyframes = statement as KeyFramesBlock; var fontface = statement as FontFaceBlock; var import = statement as Model.Import; if (block == null && media == null && keyframes == null && fontface == null && import == null) { ret.Add(statement); continue; } if (block != null) { var processedRules = new List <NameValueProperty>(); // At this point, it is an error for any other type of rule to exist foreach (var rule in block.Properties.Cast <NameValueProperty>()) { var value = rule.Value.Evaluate(); while (value.NeedsEvaluate) { value = value.Evaluate(); } if (!(value is ExcludeFromOutputValue)) { processedRules.Add(new NameValueProperty(rule.Name, value, rule.Start, rule.Stop, rule.FilePath)); } } ret.Add(new SelectorAndBlock(block.Selector, processedRules, block.ResetContext, block.Start, block.Stop, block.FilePath)); } if (media != null) { var evaluated = Task(media.Blocks.ToList()); ret.Add(new MediaBlock(media.MediaQuery.Evaluate(), evaluated, media.Start, media.Stop, media.FilePath)); } if (keyframes != null) { var frames = new List <KeyFrame>(); foreach (var frame in keyframes.Frames) { var blockEquiv = new SelectorAndBlock(InvalidSelector.Singleton, frame.Properties, null, frame.Start, frame.Stop, frame.FilePath); var evald = Task(new List <Block>() { blockEquiv }); frames.Add(new KeyFrame(frame.Percentages.ToList(), ((SelectorAndBlock)evald[0]).Properties.ToList(), frame.Start, frame.Stop, frame.FilePath)); } ret.Add(new KeyFramesBlock(keyframes.Prefix, keyframes.Name, frames, keyframes.Variables.ToList(), keyframes.Start, keyframes.Stop, keyframes.FilePath)); } if (fontface != null) { var processedRules = new List <Property>(); foreach (var rule in fontface.Properties.Cast <NameValueProperty>()) { var value = rule.Value.Evaluate(); while (value.NeedsEvaluate) { value = value.Evaluate(); } if (!(value is ExcludeFromOutputValue)) { processedRules.Add(new NameValueProperty(rule.Name, value, rule.Start, rule.Stop, rule.FilePath)); } } ret.Add(new FontFaceBlock(processedRules, fontface.Start, fontface.Stop, fontface.FilePath)); } if (import != null) { ret.Add(import.Evaluate()); } } return(ret); }