Example #1
0
        /// <summary>
        /// Check if the given css block is assignable to the given css box by validating the selector.<br/>
        /// </summary>
        /// <param name="box">the box to check assign to</param>
        /// <param name="block">the block to check assign of</param>
        /// <returns>true - the block is assignable to the box, false - otherwise</returns>
        private static bool IsBlockAssignableToBoxWithSelector(CssBox box, CssBlock block)
        {
            foreach (var selector in block.Selectors)
            {
                bool matched = false;
                while (!matched)
                {
                    box = box.ParentBox;
                    while (box != null && box.HtmlTag == null)
                    {
                        box = box.ParentBox;
                    }

                    if (box == null)
                    {
                        return(false);
                    }

                    if (box.HtmlTag.Name.Equals(selector.Class, StringComparison.InvariantCultureIgnoreCase))
                    {
                        matched = true;
                    }

                    if (!matched && box.HtmlTag.HasAttribute("class"))
                    {
                        var className = box.HtmlTag.TryGetAttribute("class");
                        if (selector.Class.Equals("." + className, StringComparison.InvariantCultureIgnoreCase) || selector.Class.Equals(box.HtmlTag.Name + "." + className, StringComparison.InvariantCultureIgnoreCase))
                        {
                            matched = true;
                        }
                    }

                    if (!matched && box.HtmlTag.HasAttribute("id"))
                    {
                        var id = box.HtmlTag.TryGetAttribute("id");
                        if (selector.Class.Equals("#" + id, StringComparison.InvariantCultureIgnoreCase))
                        {
                            matched = true;
                        }
                    }

                    if (!matched && selector.DirectParent)
                    {
                        return(false);
                    }
                }
            }
            return(true);
        }
Example #2
0
            /*
             * NOTE: Each ConsumeXXX method consumes the token that is current as of when the
             * method is called and consumes all subsequent tokens that are read from the tokenizer
             * during method execution. ConsumeXXX operations must only call other ConsumeYYY
             * operations and not call any ParseXXX methods.
             */

            /// <summary>
            /// Reads current token and all subsequent tokens that compose into a <see cref="CssQualifiedRule"/> instance.
            /// </summary>
            /// <returns></returns>
            private CssQualifiedRule ConsumeQualifiedRule(CssToken token)
            {
                var preludeFragmentParser = new QualifiedRulePreludeScope(this, _grammar);
                var prelude = _grammar.ParseQualifiedRulePrelude(preludeFragmentParser);

                if (token.TokenType == CssTokenType.LeftCurlyBracket && TryRead(out token))
                {
                    var blockFragmentParser = new RuleBlockScope(this, _grammar);
                    var block = new CssBlock(CssBlockType.CurlyBrackets, _grammar.ParseQualifiedRuleBlock(blockFragmentParser));
                    return(_grammar.CreateQualifiedRule(prelude, block));
                }

                // Parse error
                return(null);
            }
Example #3
0
 /// <summary>
 /// Assigns the given css style block properties to the given css box.
 /// </summary>
 /// <param name="box">the css box to assign css to</param>
 /// <param name="block">the css block to assign</param>
 private static void AssignCssBlock(CssBox box, CssBlock block)
 {
     foreach (var prop in block.Properties)
     {
         var value = prop.Value;
         if (prop.Value == CssConstants.Inherit && box.ParentBox != null)
         {
             value = CssUtils.GetPropertyValue(box.ParentBox, prop.Key);
         }
         if (IsStyleOnElementAllowed(box, prop.Key, value))
         {
             CssUtils.SetPropertyValue(box, prop.Key, value);
         }
     }
 }
Example #4
0
        /// <summary>
        /// Add the given css block to the css data, merging to existing block if required.
        /// </summary>
        /// <remarks>
        /// If there is no css blocks for the same class it will be added to data collection.<br/>
        /// If there is already css blocks for the same class it will check for each existing block
        /// if the hierarchical selectors match (or not exists). if do the two css blocks will be merged into
        /// one where the new block properties overwrite existing if needed. if the new block doesn't mach any
        /// existing it will be added either to the beginning of the list if it has no  hierarchical selectors or at the end.<br/>
        /// Css block without hierarchical selectors must be added to the beginning of the list so more specific block
        /// can overwrite it when the style is applied.
        /// </remarks>
        /// <param name="media">the media type to add the CSS to</param>
        /// <param name="cssBlock">the css block to add</param>
        public void AddCssBlock(string media, CssBlock cssBlock)
        {
            Dictionary <string, List <CssBlock> > mid;

            if (!_mediaBlocks.TryGetValue(media, out mid))
            {
                mid = new Dictionary <string, List <CssBlock> >(StringComparer.InvariantCultureIgnoreCase);
                _mediaBlocks.Add(media, mid);
            }

            if (!mid.ContainsKey(cssBlock.Class))
            {
                var list = new List <CssBlock>();
                list.Add(cssBlock);
                mid[cssBlock.Class] = list;
            }
            else
            {
                bool merged = false;
                var  list   = mid[cssBlock.Class];
                foreach (var block in list)
                {
                    if (block.EqualsSelector(cssBlock))
                    {
                        merged = true;
                        block.Merge(cssBlock);
                        break;
                    }
                }

                if (!merged)
                {
                    // general block must be first
                    if (cssBlock.Selectors == null)
                    {
                        list.Insert(0, cssBlock);
                    }
                    else
                    {
                        list.Add(cssBlock);
                    }
                }
            }
        }
Example #5
0
        /// <summary>
        /// Check if the given css block is assignable to the given css box.<br/>
        /// the block is assignable if it has no hierarchical selectors or if the hierarchy matches.<br/>
        /// Special handling for ":hover" pseudo-class.<br/>
        /// </summary>
        /// <param name="box">the box to check assign to</param>
        /// <param name="block">the block to check assign of</param>
        /// <returns>true - the block is assignable to the box, false - otherwise</returns>
        private static bool IsBlockAssignableToBox(CssBox box, CssBlock block)
        {
            bool assignable = true;

            if (block.Selectors != null)
            {
                assignable = IsBlockAssignableToBoxWithSelector(box, block);
            }
            else if (box.HtmlTag.Name.Equals("a", StringComparison.OrdinalIgnoreCase) && block.Class.Equals("a", StringComparison.OrdinalIgnoreCase) && !box.HtmlTag.HasAttribute("href"))
            {
                assignable = false;
            }

            if (assignable && block.Hover)
            {
                box.HtmlContainer.AddHoverBox(box, block);
                assignable = false;
            }

            return(assignable);
        }
Example #6
0
        /// <summary>
        /// Feeds the style with a block about the specific media.<br/>
        /// When no media is specified, "all" will be used.
        /// </summary>
        /// <param name="cssData"> </param>
        /// <param name="block">the CSS block to handle</param>
        /// <param name="media">optional: the media (default - all)</param>
        private static void FeedStyleBlock(CssData cssData, string block, string media = "all")
        {
            int startIdx = block.IndexOf("{", StringComparison.Ordinal);
            int endIdx   = startIdx > -1 ? block.IndexOf("}", startIdx) : -1;

            if (startIdx > -1 && endIdx > -1)
            {
                string   blockSource = block.Substring(startIdx + 1, endIdx - startIdx - 1);
                string[] classes     = block.Substring(0, startIdx).Split(',');

                foreach (string cls in classes)
                {
                    string className = cls.Trim();
                    if (!String.IsNullOrEmpty(className))
                    {
                        CssBlock newblock = ParseCssBlockImp(className, blockSource);
                        if (newblock != null)
                        {
                            cssData.AddCssBlock(media, newblock);
                        }
                    }
                }
            }
        }
Example #7
0
        public void WriteBlock(CssBlock block, int level)
        {
            var prevScope = scope;

            writer.Write("{"); // Block start

            var condenced = false;
            var count = 0;

            // Write the declarations
            foreach (var node in block.Children) // TODO: Change to an immutable list?
            {
                if (node.Kind == NodeKind.Include)
                {
                    var b2 = new CssBlock(NodeKind.Block);

                    b2.Add(node);

                    scope = ExpandInclude((IncludeNode)node, b2);

                    foreach (var rule in b2.OfType<CssRule>())
                    {
                        writer.WriteLine();

                        WriteRule(rule, level + 1);

                        count++;
                    }
                }
                else if (node.Kind == NodeKind.Declaration)
                {
                    var declaration = (CssDeclaration)node;

                    if (block.Children.Count == 1 && !declaration.Info.NeedsExpansion(declaration, browserSupport))
                    {
                        condenced = true;

                        writer.Write(" ");

                        WriteDeclaration(declaration, 0);
                    }
                    else
                    {
                        if (count == 0) writer.WriteLine();

                        WritePatchedDeclaration(declaration, level + 1);
                    }
                }
                else if (node.Kind == NodeKind.Rule)  // Nested rule
                {
                    if (count == 0) writer.WriteLine();

                    var childRule = (CssRule)node;

                    WriteRule(childRule, level + 1);
                }
                else if (node.Kind == NodeKind.If)
                {
                    EvaluateIf((IfBlock)node, level + 1);
                }

                if (!condenced)
                {
                    writer.WriteLine();
                }

                count++;
            }

            // Limit to declaration
            if (condenced)
            {
                writer.Write(" ");
            }
            else
            {
                Indent(level);
            }

            writer.Write("}"); // Block end

            prevScope = scope;
        }
Example #8
0
 /// <summary>
 /// Init.
 /// </summary>
 public HoverBoxBlock(CssBox cssBox, CssBlock cssBlock)
 {
     _cssBox   = cssBox;
     _cssBlock = cssBlock;
 }
 private List<CssBlock> ParseBlocks(ScintillaControl sci)
 {
     List<CssBlock> blocks = new List<CssBlock>();
     blocks.Clear();
     int lines = sci.LineCount;
     int inString = 0;
     bool inComment = false;
     CssBlock block = null;
     for (int i = 0; i < lines; i++)
     {
         string line = sci.GetLine(i);
         int len = line.Length;
         int safeLen = len - 1;
         for (int j = 0; j < len; j++)
         {
             char c = line[j];
             if (inComment)
             {
                 if (c == '*' && j < safeLen && line[j + 1] == '/') inComment = false;
                 else continue;
             }
             else if (inString > 0)
             {
                 if (inString == 1 && c == '\'') inString = 0;
                 else if (inString == 2 && c == '"') inString = 0;
                 else continue;
             }
             else if (c == '\'') inString = 1;
             else if (c == '"') inString = 2;
             else if (c == '/' && j < safeLen && line[j + 1] == '/')
                 break;
             else if (c == '/' && j < safeLen && line[j + 1] == '*')
                 inComment = true;
             else if (c == '{')
             {
                 CssBlock parent = block;
                 block = new CssBlock();
                 block.LineFrom = i;
                 block.ColFrom = j;
                 if (parent != null)
                 {
                     block.Parent = parent;
                     parent.Children.Add(block);
                 }
                 else blocks.Add(block);
             }
             else if (c == '}')
             {
                 if (block != null)
                 {
                     block.LineTo = i;
                     block.ColTo = j;
                     block = block.Parent;
                     if (block != null)
                     {
                         block.LineTo = i;
                         block.ColTo = j;
                     }
                 }
             }
         }
     }
     return blocks;
 }
 private bool CursorInBlock(CssBlock block, int line, int col)
 {
     if (line < block.LineFrom || line > block.LineTo) return false;
     if (line == block.LineFrom && col <= block.ColFrom) return false;
     if (line == block.LineTo && col > block.ColTo) return false;
     return true;
 }
 private CssBlock LookupBlock(List<CssBlock> blocks, CssBlock parent, int line, int col)
 {
     foreach (CssBlock block in blocks)
     {
         if (CursorInBlock(block, line, col))
             return LookupBlock(block.Children, block, line, col);
     }
     return parent;
 }
Example #12
0
 /// <summary>
 /// Init.
 /// </summary>
 public HoverBoxBlock(CssBox cssBox, CssBlock cssBlock)
 {
     this._cssBox   = cssBox;
     this._cssBlock = cssBlock;
 }
Example #13
0
 /// <summary>
 /// Add the given css block to the css data, merging to existing block if required.
 /// </summary>
 /// <remarks>
 /// If there is no css blocks for the same class it will be added to data collection.<br/>
 /// If there is already css blocks for the same class it will check for each existing block
 /// if the hierarchical selectors match (or not exists). if do the two css blocks will be merged into
 /// one where the new block properties overwrite existing if needed. if the new block doesn't mach any
 /// existing it will be added either to the beginning of the list if it has no  hierarchical selectors or at the end.<br/>
 /// Css block without hierarchical selectors must be added to the beginning of the list so more specific block
 /// can overwrite it when the style is applied.
 /// </remarks>
 /// <param name="media">the media type to add the CSS to</param>
 /// <param name="cssBlock">the css block to add</param>
 public void AddCssBlock(string media, CssBlock cssBlock)
 {
     _mediaRules.AddCssBlock(media, cssBlock);
 }
Example #14
0
 public virtual CssQualifiedRule CreateQualifiedRule(CssComponent prelude, CssBlock block)
 {
     return(new CssQualifiedRule(prelude, block));
 }
Example #15
0
        /// <summary>
        /// Applies style to all boxes in the tree.<br/>
        /// If the html tag has style defined for each apply that style to the css box of the tag.<br/>
        /// If the html tag has "class" attribute and the class name has style defined apply that style on the tag css box.<br/>
        /// If the html tag has "style" attribute parse it and apply the parsed style on the tag css box.<br/>
        /// If the html tag is "style" tag parse it content and add to the css data for all future tags parsing.<br/>
        /// If the html tag is "link" that point to style data parse it content and add to the css data for all future tags parsing.<br/>
        /// </summary>
        /// <param name="box"></param>
        /// <param name="htmlContainer">the html container to use for reference resolve</param>
        /// <param name="cssData"> </param>
        /// <param name="cssDataChanged">check if the css data has been modified by the handled html not to change the base css data</param>
        private static void CascadeStyles(CssBox box, HtmlContainer htmlContainer, ref CssData cssData,
                                          ref bool cssDataChanged)
        {
            box.InheritStyle();

            if (box.HtmlTag != null)
            {
                // try assign style using the html element tag
                AssignCssBlocks(box, cssData, box.HtmlTag.Name);

                // try assign style using the "class" attribute of the html element
                if (box.HtmlTag.HasAttribute("class"))
                {
                    AssignClassCssBlocks(box, cssData);
                }

                // try assign style using the "id" attribute of the html element
                if (box.HtmlTag.HasAttribute("id"))
                {
                    string id = box.HtmlTag.TryGetAttribute("id");
                    AssignCssBlocks(box, cssData, "#" + id);
                }

                TranslateAttributes(box.HtmlTag, box);

                // Check for the style="" attribute
                if (box.HtmlTag.HasAttribute("style"))
                {
                    CssBlock block = CssParser.ParseCssBlock(box.HtmlTag.Name, box.HtmlTag.TryGetAttribute("style"));
                    AssignCssBlock(box, block);
                }

                // Check for the <style> tag
                if (box.HtmlTag.Name.Equals("style", StringComparison.CurrentCultureIgnoreCase) && box.Boxes.Count == 1)
                {
                    CloneCssData(ref cssData, ref cssDataChanged);
                    CssParser.ParseStyleSheet(cssData, box.Boxes[0].Text.CutSubstring());
                }

                // Check for the <link rel=stylesheet> tag
                if (box.HtmlTag.Name.Equals("link", StringComparison.CurrentCultureIgnoreCase) &&
                    box.GetAttribute("rel", string.Empty).Equals("stylesheet", StringComparison.CurrentCultureIgnoreCase))
                {
                    CloneCssData(ref cssData, ref cssDataChanged);
                    string  stylesheet;
                    CssData stylesheetData;
                    StylesheetLoadHandler.LoadStylesheet(htmlContainer, box.GetAttribute("href", string.Empty),
                                                         box.HtmlTag.Attributes, out stylesheet, out stylesheetData);
                    if (stylesheet != null)
                    {
                        CssParser.ParseStyleSheet(cssData, stylesheet);
                    }
                    else if (stylesheetData != null)
                    {
                        cssData.Combine(stylesheetData);
                    }
                }
            }

            // cascade text decoration only to boxes that actually have text so it will be handled correctly.
            if (box.TextDecoration != String.Empty && box.Text == null)
            {
                foreach (CssBox childBox in box.Boxes)
                {
                    childBox.TextDecoration = box.TextDecoration;
                }
                box.TextDecoration = string.Empty;
            }

            foreach (CssBox childBox in box.Boxes)
            {
                CascadeStyles(childBox, htmlContainer, ref cssData, ref cssDataChanged);
            }
        }
Example #16
0
        public CssBlock ReadBlock(CssBlock block)
        {
            var blockStart = Read(TokenKind.BlockStart, LexicalMode.Block); // Read {

            ReadTrivia();

            while(current.Kind != TokenKind.BlockEnd)
            {
                if (isEnd) throw new UnbalancedBlock(LexicalMode.Block, blockStart);

                // A list of delarations or blocks

                if (current.Kind == TokenKind.AtSymbol)
                {
                    Read(); // Read @

                    var name = tokenizer.Read(); // Name

                    switch (name.Text)
                    {
                        case "include" : block.Add(ReadInclude()); continue;
                        case "if"      : block.Add(ReadIfRule());  continue;
                    }
                }

                if (current.Kind == TokenKind.Dollar)
                {
                    block.Add(ReadAssignment());

                    continue;

                }

                var statement = ReadSpan();

                switch (current.Kind)
                {
                    case TokenKind.Colon        : block.Add(ReadDeclarationFromName(statement)); break; // DeclarationName
                    case TokenKind.BlockStart   : block.Add(ReadRuleBlock(new CssSelector(statement))); break;
                    case TokenKind.BlockEnd     : break;

                    // TODO: Figure out where we missed reading the semicolon TEMP
                    case TokenKind.Semicolon    : tokenizer.Read(); break;

                    default: throw new UnexpectedTokenException(LexicalMode.Block, current);
                }
            }

            tokenizer.Read(); // read }

            block.Trailing = ReadTrivia();

            return block;
        }
Example #17
0
 public virtual CssAtRule CreateAtRule(string name, CssComponent prelude, CssBlock block)
 {
     return(new CssAtRule(name, prelude, block));
 }
Example #18
0
        public CssScope ExpandInclude(IncludeNode include, CssBlock rule)
        {
            includeCount++;

            if (includeCount > 1000) throw new Exception("Exceded include limit of 1,000");

            MixinNode mixin;

            if (!context.Mixins.TryGetValue(include.Name, out mixin))
            {
                throw new Exception($"Mixin '{include.Name}' not registered");
            }

            var index = rule.Children.IndexOf(include);

            var childScope = GetScope(mixin.Parameters, include.Args);

            var i = 0;

            foreach (var node in mixin.Children.ToArray())
            {
                // Bind variables

                if (node is IncludeNode)
                {
                    ExpandInclude(
                        (IncludeNode)node,
                        rule
                    );

                    mixin.Children.Remove(node);
                }

                rule.Insert(i + 1, node.CloneNode());

                i++;
            }

            return childScope;
        }
Example #19
0
        /// <summary>
        /// Write the given html tag with all its attributes and styles.
        /// </summary>
        /// <param name="sb">the string builder to write html into</param>
        /// <param name="box">the css box with the html tag to write</param>
        /// <param name="indent">the indent to use for nice formating</param>
        /// <param name="styleGen">Controls the way styles are generated when html is generated</param>
        private static void WriteHtmlTag(StringBuilder sb, CssBox box, int indent, HtmlGenerationStyle styleGen)
        {
            sb.Append(new string(' ', indent * 4));
            sb.AppendFormat("<{0}", box.HtmlTag.Name);

            // collect all element style properties incliding from stylesheet
            var tagStyles = new Dictionary <string, string>();
            IEnumerable <CssBlock> tagCssBlock = box.HtmlContainer.CssData.GetCssBlock(box.HtmlTag.Name);

            if (tagCssBlock != null)
            {
                // atodo: handle selectors
                foreach (CssBlock cssBlock in tagCssBlock)
                {
                    foreach (var prop in cssBlock.Properties)
                    {
                        tagStyles[prop.Key] = prop.Value;
                    }
                }
            }

            if (box.HtmlTag.HasAttributes())
            {
                sb.Append(" ");
                foreach (var att in box.HtmlTag.Attributes)
                {
                    // handle image tags by inserting the image using base64 data
                    if (box.HtmlTag.Name == "img" && att.Key == "src" &&
                        (att.Value.StartsWith("property") || att.Value.StartsWith("method")))
                    {
                        Image img = ((CssBoxImage)box).Image;
                        if (img != null)
                        {
                            using (var buffer = new MemoryStream())
                            {
                                img.Save(buffer, ImageFormat.Png);
                                string base64 = Convert.ToBase64String(buffer.ToArray());
                                sb.AppendFormat("{0}=\"data:image/png;base64, {1}\" ", att.Key, base64);
                            }
                        }
                    }
                    else if (styleGen == HtmlGenerationStyle.Inline && att.Key == HtmlConstants.Style)
                    {
                        // if inline style add the styles to the collection
                        CssBlock block = CssParser.ParseCssBlock(box.HtmlTag.Name, box.HtmlTag.TryGetAttribute("style"));
                        foreach (var prop in block.Properties)
                        {
                            tagStyles[prop.Key] = prop.Value;
                        }
                    }
                    else if (styleGen == HtmlGenerationStyle.Inline && att.Key == HtmlConstants.Class)
                    {
                        // if inline style convert the style class to actual properties and add to collection
                        IEnumerable <CssBlock> cssBlocks = box.HtmlContainer.CssData.GetCssBlock("." + att.Value);
                        if (cssBlocks != null)
                        {
                            // atodo: handle selectors
                            foreach (CssBlock cssBlock in cssBlocks)
                            {
                                foreach (var prop in cssBlock.Properties)
                                {
                                    tagStyles[prop.Key] = prop.Value;
                                }
                            }
                        }
                    }
                    else
                    {
                        sb.AppendFormat("{0}=\"{1}\" ", att.Key, att.Value);
                    }
                }

                sb.Remove(sb.Length - 1, 1);
            }

            // if inline style insert the style tag with all collected style properties
            if (styleGen == HtmlGenerationStyle.Inline && tagStyles.Count > 0)
            {
                sb.Append(" style=\"");
                foreach (var style in tagStyles)
                {
                    sb.AppendFormat("{0}: {1}; ", style.Key, style.Value);
                }
                sb.Remove(sb.Length - 1, 1);
                sb.Append("\"");
            }

            sb.AppendFormat("{0}>", box.HtmlTag.IsSingle ? "/" : "");
            sb.AppendLine();
        }