public override bool OnReadValue(Style styleBlock, Css.Value value, int start, out int size) { // Check if it's inherit or initial: if (value is Css.Keywords.Initial || value is Css.Keywords.Inherit) { // Apply it! size = 1; if (SetInfo != null) { // Indicate that we've been set: SetInfo.Set = true; } styleBlock[RawProperty] = value; return(true); } Css.Spec.Value spec = RawProperty.Specification; if (spec.OnReadValue(styleBlock, value, start, out size)) { // Apply it! if (SetInfo != null) { // Indicate that we've been set: SetInfo.Set = true; } if (size == 1) { styleBlock[RawProperty] = value[start]; } else { // Chop out a segment: Css.ValueSet set = new Css.ValueSet(new Css.Value[size]); for (int i = 0; i < size; i++) { set[i] = value[start + i]; } // Apply: styleBlock[RawProperty] = set; } return(true); } size = 0; return(false); }
/// <summary>Loads the given segment of the given value.</summary> private static SupportsQuery LoadSection(Value value, ref int i, int endInclusive) { Value currentValue = value[i]; if (currentValue == null || currentValue == Css.Value.Empty) { return(null); } // Is it an expression? ValueSet expr = currentValue as ValueSet; SupportsQuery current; if (expr == null) { // Always textual here: string token = currentValue.Text; if (token == "not") { i++; current = new SupportsQueryNot(LoadSection(value, ref i, endInclusive)); } else { current = null; } } else { if (expr.Count == 2 && expr[1] is ValueSet) { // Nested functions: int nestedI = 0; current = LoadSection(expr, ref nestedI, 1); } else { // Got e.g. (min-width:400px) etc. // Essentially it's a feature in brackets. current = LoadPropertyExpression(expr); } } // Check for 'and'/'or' if ((i + 1) <= endInclusive) { currentValue = value[i + 1]; if (currentValue != null && currentValue != Css.Value.Empty) { if (currentValue.Text == "and") { // Got the 'and' keyword! i += 2; return(new SupportsQueryAnd(current, LoadSection(value, ref i, endInclusive))); } else if (currentValue.Text == "or") { // Got the 'or' keyword! i += 2; return(new SupportsQueryList(new SupportsQuery[] { current, LoadSection(value, ref i, endInclusive) })); } } } return(current); }
/// <summary>Reads a function with the given lowercase name. There must be a bracket at the current read head.</summary> public Css.Value ReadFunction(string name) { // Read off the open bracket: Read(); // Get the global instance for the given function name (lowercase): CssFunction globalInstance = name == "" ? null : CssFunctions.Get(name); // Read the args: Css.Value parameters = null; if (globalInstance == null) { // Unsupported function. parameters = ReadValue(); } else { // Set literal value: if (globalInstance.LiteralValue) { parameters = ReadLiteralValue(); } else { parameters = ReadValue(); } } // Skip any junk (which may be e.g. after a quote): SkipJunk(); // Read off the close bracket: Read(); if (name == "") { // This occurs with nested brackets - it's just a set. return(parameters); } if (globalInstance == null) { // Don't know what this function is - act like it's not even there. return(null); } // Copy the global instance: CssFunction result = globalInstance.Copy() as CssFunction; // Make sure params are a set: ValueSet set = parameters as ValueSet; if (set == null) { // It's a single value, or null if (parameters != null) { // Push the single value: result.Values = new Css.Value[] { parameters }; } } else { // Apply the parameters: result.Values = set.Values; } // Tell it that it's ready: result.OnValueReady(this); // Ok: return(result); }
/// <summary>Converts a value to a rule. Note that it can be a set of rules (RuleSet).</summary> public static Rule ConvertToRule(Rule parentRule, Css.Value value, StyleSheet sheet, out Rule[] ruleSet) { ruleSet = null; if (value == null) { // Nothing else. return(null); } // Is it an @ rule? Note that they can actually be 'nested' inside an,array,of,selectors (happens with @media) AtRuleUnit atRule = value[0] as AtRuleUnit; if (atRule != null) { // Let the @ rule handle the value: return(atRule.AtRule.LoadRule(parentRule, sheet, value)); } // One or more selectors followed by a block unit. // Get the block: int max = value.Count - 1; SelectorBlockUnit block = value[max] as SelectorBlockUnit; if (block == null) { // Try as a set instead: ValueSet set = value[max] as ValueSet; if (set == null) { // Invalid/ unrecognised selector block. Ignore it. return(null); } // Get last one in the set: block = set[set.Count - 1] as SelectorBlockUnit; // still null? if (block == null) { // Invalid/ unrecognised selector block. Ignore it. return(null); } // Check again for an @rule: Css.Value v0 = value[0]; if (v0 != null && v0[0] is AtRuleUnit) { // Got an at rule! atRule = v0[0] as AtRuleUnit; return(atRule.AtRule.LoadRule(parentRule, sheet, value)); } // Clear last one: set[set.Count - 1] = null; } else { // Clear the block from the value: value[max] = null; } // Get the style object: Style style = block.Style; // Read the selector(s): List <Selector> into = new List <Selector>(); ReadSelectors(sheet, value, into); if (into.Count == 0) { return(null); } if (into.Count != 1) { ruleSet = new Rule[into.Count]; } // Got a specifity override? -spark-specifity:x Css.Value specifityOverride = null; int specOverride = -1; if (style.Properties.TryGetValue(Css.Properties.SparkSpecifity.GlobalProperty, out specifityOverride)) { // Yep! Pull the integer value: specOverride = specifityOverride.GetInteger(null, null); } for (int i = 0; i < into.Count; i++) { // Get it: Selector s = into[i]; // Create the rule: StyleRule rule = new StyleRule(style); rule.ParentStyleSheet = sheet; rule.Selector = s; s.Rule = rule; // Must use a copy of the style if its a secondary selector. // This is because the 'specifity' of CSS properties is defined by how specific // a selector is. So, when there's multiple selectors, we have to clone the style. Style currentStyle = i == 0?style:style.Clone(); // Apply the selectors specifity to the style: int specifity = s.Specifity; if (specOverride != -1) { specifity = specOverride; } foreach (KeyValuePair <CssProperty, Css.Value> kvp in currentStyle.Properties) { // Apply: kvp.Value.SetSpecifity(specifity); } if (ruleSet == null) { return(rule); } ruleSet[i] = rule; } return(null); }
/// <summary>The lexer must be in literal mode for this. Reads a value from the stream. /// May read a whole set or null if there's a ) or ; at the read head.</summary> public Css.Value ReadValue() { Value result = null; List <Value> values = null; ValueSet bottomSet = null; List <Value> bottomValues = null; bool important = false; // Skip junk: SkipJunk(); char current = Peek(); while (current != ')' && current != '}' && current != '\0' && current != ';') { if (current == ',') { // read it off: Read(); // Setup bottom set, if we need to: if (bottomSet == null) { bottomSet = new ValueSet(); bottomSet.Spacer = ","; bottomValues = new List <Value>(); } //"Push" values into bottom set. if (result != null) { if (values != null) { // Result is a set. ApplyList(result, values); values = null; } bottomValues.Add(result); result = null; } } else if (current == '!') { // Special lookout for "important". // We don't want it to create a set. Read(); // Skip whitespaces. SkipJunk(); // Read: Css.Value importantTest = ReadSingleValue(); // Skip whitespaces. SkipJunk(); // Got important? if (importantTest is Css.Keywords.ImportantKeyword) { // Important! important = true; } else { // Act like we added both ! and importantTest. // Note that there might have been no spaces before it; they could be one value (but it's unlikely). if (result == null) { result = new TextUnit("!"); } else if (values == null) { values = new List <Value>(); values.Add(result); result = new ValueSet(); values.Add(new TextUnit("!")); } else { values.Add(new TextUnit("!")); } if (importantTest != null) { if (values == null) { values = new List <Value>(); values.Add(result); result = new ValueSet(); values.Add(importantTest); } else { values.Add(importantTest); } } } // Update current: current = Peek(); continue; } if (result == null) { result = ReadSingleValue(); if (result == null) { // Invalid rule // Skip whitespaces. SkipJunk(); // Update current: current = Peek(); if (current == '}' || current == ';') { AtRuleMode = false; Read(); } return(null); } } else if (values == null) { values = new List <Value>(); values.Add(result); result = new ValueSet(); values.Add(ReadSingleValue()); } else { values.Add(ReadSingleValue()); } if (SelectorMode && !AtRuleMode) { // Check if we have a whitespace: if (Peek() == ' ') { if (values == null) { values = new List <Value>(); values.Add(result); result = new ValueSet(); values.Add(new TextUnit(" ")); } else { values.Add(new TextUnit(" ")); } } } // Skip whitespaces. SkipJunk(); // Update current: current = Peek(); } if (current == '}' || current == ';') { AtRuleMode = false; Read(); } if (values != null) { // Update result with the values set: ApplyList(result, values); } if (bottomSet != null) { // Push result into it: bottomValues.Add(result); ApplyList(bottomSet, bottomValues); result = bottomSet; } if (important && result != null) { result.SetImportant(true); } return(result); }