예제 #1
0
        private static Selector LoadSingleSelector(StyleSheet sheet, Css.Value value, List <Selector> all)
        {
            // Create the selector:
            Selector selector = new Selector();

            selector.Value = value;

            if (sheet != null)
            {
                // Default NS:
                selector.Namespace = sheet.Namespace;
            }

            List <LocalMatcher> locals      = null;
            List <RootMatcher>  roots       = new List <RootMatcher>();
            RootMatcher         currentRoot = null;

            // Current state:
            bool            nextTarget    = false;
            bool            wasWhitespace = false;
            int             max           = value.Count;
            SelectorMatcher addMatcher    = null;
            RootMatcher     addRoot       = null;

            // For each selector fragment..
            for (int i = 0; i < max; i++)
            {
                Css.Value current = value[i];

                if (current == null)
                {
                    // Happens right at the end where the block was.
                    continue;
                }

                if (current.GetType() == typeof(SquareBracketUnit))
                {
                    // [attr]
                    SquareBracketUnit attrib = current as SquareBracketUnit;

                    // Make the match object:
                    AttributeMatch match = attrib.GetMatch();

                    addMatcher = match;
                }
                else if (current.Type == ValueType.Text)
                {
                    // Selector fragment.
                    string text = current.Text;

                    if (text == " ")
                    {
                        wasWhitespace = true;
                        continue;
                    }

                    if (text == "#")
                    {
                        // ID selector next.
                        i++;
                        string id = value[i].Text;

                        // Create ID root:
                        addRoot      = new RootIDMatcher();
                        addRoot.Text = id;
                    }
                    else if (text == ".")
                    {
                        // Class selector next.
                        i++;
                        string className = value[i].Text;

                        // Create class root:
                        addRoot      = new RootClassMatcher();
                        addRoot.Text = className;

                        // Note that if you chain multiple class selectors (.a.b)
                        // This is called twice and PreviousMatcher/NextMatcher remain null.
                        // It then simply doesn't change element (which is what we want!).
                    }
                    else if (text == "*")
                    {
                        // Apply last locals:
                        if (currentRoot != null && locals != null)
                        {
                            currentRoot.SetLocals(locals);
                            locals.Clear();
                        }

                        // Everything:
                        currentRoot = CreateUniversal(roots);
                    }
                    else if (text == ":")
                    {
                        // Pseudo class or function next. May also be another colon.
                        // after and before map to actual style properties.

                        i++;

                        current = value[i];

                        if (current != null && !current.IsFunction)
                        {
                            string keywordText = current.Text;

                            // Second colon?
                            if (keywordText == ":")
                            {
                                // Skip the double colon.
                                i++;
                                current     = value[i];
                                keywordText = current.Text;
                            }

                            // Keyword?
                            current = CssKeywords.Get(keywordText.ToLower());
                        }

                        if (current == null)
                        {
                            // Selector failure - return a blank one.
                            if (all != null)
                            {
                                all.Clear();
                            }

                            return(null);
                        }

                        // Convert the value into a matcher:
                        SelectorMatcher sm = current.GetSelectorMatcher();

                        if (sm == null)
                        {
                            // Selector failure - return a blank one.
                            if (all != null)
                            {
                                all.Clear();
                            }

                            return(null);
                        }

                        // Add it!
                        addMatcher = sm;
                    }
                    else if (text == ">")
                    {
                        // Following "thing" being a direct child.
                        addMatcher = new DirectParentMatch();
                    }
                    else if (text == ">>")
                    {
                        // Following "thing" being a descendant (same as space).
                        addMatcher = new ParentMatch();
                    }
                    else if (text == "|")
                    {
                        // Namespace declaration was before.

                        // Selector's namespace name is the latest root:
                        string nsName = currentRoot.Text;

                        // Pop the root:
                        roots.RemoveAt(roots.Count - 1);

                        if (roots.Count > 0)
                        {
                            currentRoot = roots[roots.Count - 1];
                        }
                        else
                        {
                            currentRoot = null;
                        }

                        // Note: It's null for * which we can just ignore.
                        if (nsName != null && sheet != null && sheet.Namespaces != null)
                        {
                            // Get the namespace:
                            CssNamespace ns;
                            sheet.Namespaces.TryGetValue(nsName.ToLower(), out ns);
                            selector.Namespace = ns;
                        }
                    }
                    else if (text == "!")
                    {
                        // CSS selectors level 4 selector target.
                        nextTarget = true;
                        continue;
                    }
                    else if (text == "+")
                    {
                        // Whenever this follows the following "thing" directly.

                        addMatcher = new DirectPreviousSiblingMatch();
                    }
                    else if (text == "~")
                    {
                        // Whenever this follows the following "thing".

                        addMatcher = new PreviousSiblingMatch();
                    }
                    else
                    {
                        // It's just a tag:
                        string tag = text.ToLower();

                        // Create tag root:
                        addRoot      = new RootTagMatcher();
                        addRoot.Text = tag;
                    }
                }

                if (addRoot != null)
                {
                    if (currentRoot != null && locals != null)
                    {
                        currentRoot.SetLocals(locals);
                        locals.Clear();
                    }

                    if (currentRoot != null && wasWhitespace && currentRoot.NextMatcher == null)
                    {
                        // Space before it!

                        // Following "thing" being a descendant.
                        currentRoot.NextMatcher          = new ParentMatch();
                        currentRoot.NextMatcher.Selector = selector;

                        wasWhitespace = false;
                    }

                    // Add to root set:
                    roots.Add(addRoot);
                    currentRoot = addRoot;

                    // Set selector:
                    addRoot.Selector = selector;

                    // Clear:
                    addRoot = null;
                }

                if (addMatcher != null)
                {
                    // Always clear whitespace:
                    wasWhitespace = false;

                    // Create implicit *:
                    if (roots.Count == 0)
                    {
                        if (currentRoot != null && locals != null)
                        {
                            currentRoot.SetLocals(locals);
                            locals.Clear();
                        }

                        currentRoot = CreateUniversal(roots);
                    }

                    // Set selector:
                    addMatcher.Selector = selector;

                    if (addMatcher is StructureMatcher)
                    {
                        // Structural matcher:
                        currentRoot.NextMatcher = addMatcher as StructureMatcher;
                    }
                    else if (addMatcher is PseudoSelectorMatch)
                    {
                        // Pseudo matcher:
                        selector.PseudoElement = addMatcher as PseudoSelectorMatch;
                    }
                    else if (addMatcher is LocalMatcher)
                    {
                        if (locals == null)
                        {
                            locals = new List <LocalMatcher>();
                        }

                        // Local matcher:
                        locals.Add(addMatcher as LocalMatcher);
                    }

                    addMatcher = null;
                }

                if (nextTarget && currentRoot != null)
                {
                    nextTarget = false;

                    // Update target:
                    currentRoot.IsTarget = true;
                    selector.Target      = currentRoot;
                }
            }

            // Apply last locals:
            if (currentRoot != null && locals != null)
            {
                currentRoot.SetLocals(locals);
                locals.Clear();
            }

            // Always ensure at least 1:
            if (roots.Count == 0)
            {
                // *:
                CreateUniversal(roots);
            }

            // Update selector info now:
            selector.Roots     = roots.ToArray();
            selector.RootCount = roots.Count;
            selector.LastRoot  = roots[roots.Count - 1];
            selector.FirstRoot = roots[0];

            // Set target if needed:
            if (selector.Target == null)
            {
                // Last root is the target:
                selector.Target          = selector.LastRoot;
                selector.Target.IsTarget = true;
            }

            // Go through them backwards applying PreviousMatcher too:
            // (Not including 0).
            for (int i = roots.Count - 1; i > 0; i--)
            {
                // Hook up:
                roots[i].PreviousMatcher = roots[i - 1].NextMatcher;
            }

            if (sheet != null)
            {
                // Compute specifity now:
                selector.GetSpecifity(sheet.Priority);
            }

            // Add to all set, if there is one:
            if (all != null && all.Count == 0)
            {
                all.Add(selector);
            }

            return(selector);
        }
예제 #2
0
        /// <summary>Reads a single selector matcher. E.g. :hover or .test</summary>
        public static SelectorMatcher ReadSelectorMatcher(Css.Value value)
        {
            if (value == null)
            {
                return(null);
            }

            if (value.GetType() == typeof(SquareBracketUnit))
            {
                // [attr]. Make the match object:
                return((value as SquareBracketUnit).GetMatch());
            }

            // Selector fragment.
            string      text   = value[0].Text;
            RootMatcher result = null;

            if (text == "#")
            {
                // ID selector next.
                string id = value[1].Text;

                // Create ID root:
                result      = new RootIDMatcher();
                result.Text = id;
                return(result);
            }
            else if (text == ".")
            {
                // Class selector next.
                string className = value[1].Text;

                // Create class root:
                result      = new RootClassMatcher();
                result.Text = className;
                return(result);
            }
            else if (text == "*")
            {
                // Everything:
                return(new RootUniversalMatcher());
            }
            else if (text == ":")
            {
                // Pseudo class or function next. May also be another colon.
                // after and before map to actual style properties.

                Css.Value current = value[1];

                if (current != null && !current.IsFunction)
                {
                    string keywordText = current.Text;

                    // Second colon?
                    if (keywordText == ":")
                    {
                        // Skip the double colon.
                        current     = value[2];
                        keywordText = current.Text;
                    }

                    // Keyword?
                    current = CssKeywords.Get(keywordText.ToLower());
                }

                if (current == null)
                {
                    // Selector failure - return a blank one.
                    return(null);
                }

                // Convert the value into a matcher:
                return(current.GetSelectorMatcher());
            }

            // It's just a tag:
            string tag = text.ToLower();

            // Create tag root:
            result      = new RootTagMatcher();
            result.Text = tag;
            return(result);
        }