// niklas@protocol7.com
// 80
using System;
using System.Text;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace SharpVectors.Dom.Css
{
///
/// The CSSStyleDeclaration interface represents a single CSS declaration block. This interface may be used to determine the style properties currently set in a block or to set style properties explicitly within the block.
/// While an implementation may not recognize all CSS properties within a CSS declaration block, it is expected to provide access to all specified properties in the style sheet through the CSSStyleDeclaration interface. Furthermore, implementations that support a specific level of CSS should correctly handle CSS shorthand properties for that level. For a further discussion of shorthand properties, see the CSS2Properties interface.
/// This interface is also used to provide a read-only access to the computed values of an element. See also the ViewCSS interface.
/// Note: The CSS Object Model doesn't provide an access to the specified or actual values of the CSS cascade
///
public class CssStyleDeclaration : ICssStyleDeclaration
{
#region Static Members
private static Regex styleRegex = new Regex(
@"^(?[A-Za-z\-0-9]+)\s*:(?[^;\}!]+)(!\s?(?important))?;?");
#endregion
#region Private Fields
private bool _readOnly;
private CssStyleSheetType _origin;
private Dictionary styles = new Dictionary();
#endregion
#region Constructors
///
/// The constructor used internally when collecting styles for a specified element
///
internal CssStyleDeclaration()
{
_origin = CssStyleSheetType.Collector;
_readOnly = true;
_parentRule = null;
}
///
/// The constructor for CssStyleDeclaration
///
/// The string to parse for CSS
/// The parent rule or parent stylesheet
/// True if this instance is readonly
/// The type of CssStyleSheet
public CssStyleDeclaration(ref string css, CssRule parentRule, bool readOnly, CssStyleSheetType origin)
{
_origin = origin;
_readOnly = readOnly;
_parentRule = parentRule;
css = parseString(css);
}
public CssStyleDeclaration(string css, CssRule parentRule, bool readOnly, CssStyleSheetType origin)
{
_origin = origin;
_readOnly = readOnly;
_parentRule = parentRule;
parseString(css);
}
#endregion
#region Public Properties
public bool ReadOnly
{
get
{
return _readOnly;
}
}
public CssStyleSheetType Origin
{
get
{
return _origin;
}
}
#endregion
#region Public Methods
///
/// Used to find matching style rules in the cascading order
///
internal void GetStylesForElement(CssCollectedStyleDeclaration csd, int specificity)
{
foreach (KeyValuePair de in styles)
{
CssStyleBlock scs = de.Value;
csd.CollectProperty(scs.Name, specificity,
(CssValue)GetPropertyCssValue(scs.Name), scs.Origin, scs.Priority);
}
}
#endregion
#region Private Methods
private string parseString(string cssText)
{
bool startedWithABracket = false;
cssText = cssText.Trim();
if (cssText.StartsWith("{"))
{
cssText = cssText.Substring(1).Trim();
startedWithABracket = true;
}
Match match = styleRegex.Match(cssText);
while (match.Success)
{
string name = match.Groups["name"].Value;
string value = match.Groups["value"].Value;
if (_parentRule != null)
{
value = ((CssRule)_parentRule).DeReplaceStrings(value);
}
string prio = match.Groups["priority"].Value;
CssStyleBlock style = new CssStyleBlock(name, value, prio, _origin);
bool addStyle = false;
if (styles.ContainsKey(name))
{
string existingPrio = ((CssStyleBlock)styles[name]).Priority;
if (existingPrio != "important" || prio == "important")
{
styles.Remove(name);
addStyle = true;
}
}
else
{
addStyle = true;
}
if (addStyle)
{
styles.Add(name, style);
}
cssText = cssText.Substring(match.Length).Trim();
match = styleRegex.Match(cssText);
}
cssText = cssText.Trim();
if (cssText.StartsWith("}"))
{
cssText = cssText.Substring(1);
}
else if (startedWithABracket)
{
throw new DomException(DomExceptionType.SyntaxErr, "Style declaration ending bracket missing");
}
return cssText;
}
#endregion
#region ICssStyleDeclaration Members
///
/// Used to set a property value and priority within this declaration block
///
/// The name of the CSS property. See the CSS property index.
/// The new value of the property.
/// The new priority of the property (e.g. "important").
/// SYNTAX_ERR: Raised if the specified value has a syntax error and is unparsable.
/// NO_MODIFICATION_ALLOWED_ERR: Raised if this declaration is readonly or the property is readonly.
public void SetProperty(string propertyName, string value, string priority)
{
if (_readOnly)
throw new DomException(DomExceptionType.NoModificationAllowedErr);
styles[propertyName] = new CssStyleBlock(propertyName, value, priority, _origin);
}
///
/// Used to retrieve the priority of a CSS property (e.g. the "important" qualifier) if the property has been explicitly set in this declaration block.
///
/// The name of the CSS property. See the CSS property index.
/// A string representing the priority (e.g. "important") if one exists. The empty string if none exists.
public virtual string GetPropertyPriority(string propertyName)
{
return (styles.ContainsKey(propertyName)) ? styles[propertyName].Priority : String.Empty;
}
///
/// Used to remove a CSS property if it has been explicitly set within this declaration block.
///
/// The name of the CSS property. See the CSS property index.
/// Returns the value of the property if it has been explicitly set for this declaration block. Returns the empty string if the property has not been set or the property name does not correspond to a known CSS property.
/// NO_MODIFICATION_ALLOWED_ERR: Raised if this declaration is readonly or the property is readonly.
public string RemoveProperty(string propertyName)
{
if (_readOnly)
throw new DomException(DomExceptionType.NoModificationAllowedErr);
if (styles.ContainsKey(propertyName))
{
CssStyleBlock s = styles[propertyName];
styles.Remove(propertyName);
return s.Value;
}
return String.Empty;
}
///
/// Used to retrieve the object representation of the value of a CSS property if it has been explicitly set within this declaration block. This method returns null if the property is a shorthand property. Shorthand property values can only be accessed and modified as strings, using the getPropertyValue and setProperty methods.
///
/// The name of the CSS property. See the CSS property index.
/// Returns the value of the property if it has been explicitly set for this declaration block. Returns null if the property has not been set.
public virtual ICssValue GetPropertyCssValue(string propertyName)
{
if (styles.ContainsKey(propertyName))
{
CssStyleBlock scs = styles[propertyName];
if (scs.CssValue == null)
{
scs.CssValue = CssValue.GetCssValue(scs.Value, ReadOnly);
}
return scs.CssValue;
}
else
{
return null;
}
}
///
/// Used to retrieve the value of a CSS property if it has been explicitly set within this declaration block.
///
/// The name of the CSS property. See the CSS property index.
/// Returns the value of the property if it has been explicitly set for this declaration block. Returns the empty string if the property has not been set.
public virtual string GetPropertyValue(string propertyName)
{
return (styles.ContainsKey(propertyName)) ? styles[propertyName].Value : String.Empty;
}
private ICssRule _parentRule;
///
/// The CSS rule that contains this declaration block or null if this CSSStyleDeclaration is not attached to a CSSRule.
///
public ICssRule ParentRule
{
get
{
return _parentRule;
}
}
///
/// The number of properties that have been explicitly set in this declaration block. The range of valid indices is 0 to length-1 inclusive.
///
public virtual ulong Length
{
get
{
return (ulong)styles.Count;
}
}
///
/// The parsable textual representation of the declaration block (excluding the surrounding curly braces). Setting this attribute will result in the parsing of the new value and resetting of all the properties in the declaration block including the removal or addition of properties.
///
/// SYNTAX_ERR: Raised if the specified CSS string value has a syntax error and is unparsable.
/// NO_MODIFICATION_ALLOWED_ERR: Raised if this declaration is readonly or a property is readonly.
public virtual string CssText
{
get
{
StringBuilder builder = new StringBuilder();
//string ret = String.Empty;
IEnumerator> enu = styles.GetEnumerator();
while (enu.MoveNext())
{
CssStyleBlock style = enu.Current.Value;
builder.Append(style.CssText);
builder.Append(";");
//ret += style.CssText + ";";
}
return builder.ToString();
}
set
{
throw new NotImplementedException();
}
}
///
/// Used to retrieve the properties that have been explicitly set in this declaration block. The order of the properties retrieved using this method does not have to be the order in which they were set. This method can be used to iterate over all properties in this declaration block.
/// The name of the property at this ordinal position. The empty string if no property exists at this position.
///
public virtual string this[ulong index]
{
get
{
if(index>=Length) return String.Empty;
else
{
int ind = (int)index;
IEnumerator> iterator = styles.GetEnumerator();
iterator.MoveNext();
KeyValuePair enu = iterator.Current;
for (int i = 0; i < ind; i++)
{
iterator.MoveNext();
enu = iterator.Current;
}
return (string)enu.Key;
}
}
}
#endregion
}
}