using System; using System.Xml; using SharpVectors.Dom.Stylesheets; namespace SharpVectors.Dom.Css { public delegate void NodeChangeHandler(Object src, XmlNodeChangedEventArgs args); public delegate void CssChangeHandler(); public class CssXmlElement : Element, IElementCssInlineStyle { #region Constructors public CssXmlElement(string prefix, string localname, string ns, CssXmlDocument doc) : base(prefix, localname, ns, doc) { } #endregion #region Style attribute private ICssStyleDeclaration style; public ICssStyleDeclaration Style { get { if(style == null) { style = new CssStyleDeclaration(GetAttribute("style", String.Empty), null, false, CssStyleSheetType.Inline); } return style; } } #endregion #region GetComputedStyle protected ICssStyleDeclaration cachedCSD; public virtual ICssStyleDeclaration GetComputedStyle(string pseudoElt) { if(cachedCSD == null) { CssCollectedStyleDeclaration csd = new CssCollectedStyleDeclaration(this); MediaList currentMedia = OwnerDocument.Media; if(OwnerDocument.UserAgentStyleSheet != null) { OwnerDocument.UserAgentStyleSheet.GetStylesForElement(this, pseudoElt, currentMedia, csd); } ((StyleSheetList)OwnerDocument.StyleSheets).GetStylesForElement(this, pseudoElt, csd); ((CssStyleDeclaration)Style).GetStylesForElement(csd, 0); if(OwnerDocument.UserStyleSheet != null) { OwnerDocument.UserStyleSheet.GetStylesForElement(this, pseudoElt, currentMedia, csd); } cachedCSD = csd; } return cachedCSD; } public virtual string GetComputedStringValue(string propertyName, string pseudoElt) { ICssStyleDeclaration csd = GetComputedStyle(pseudoElt); return csd.GetPropertyValue(propertyName); } public virtual ICssValue GetComputedCssValue(string propertyName, string pseudoElt) { ICssStyleDeclaration csd = GetComputedStyle(pseudoElt); return csd.GetPropertyCssValue(propertyName); } #endregion #region OwnerDocument public new CssXmlDocument OwnerDocument { get { return (CssXmlDocument)base.OwnerDocument; } } #endregion #region Supports public override bool Supports(string feature, string version) { if ((feature == "StyleSheets" || feature == "CSS" ) && version == "2.0") { return true; } return base.Supports(feature, version); } #endregion #region Update handling public virtual void CssInvalidate() { // TODO: why is this being called during load? foreach(XmlNode child in ChildNodes) { if (child is CssXmlElement) ((CssXmlElement)child).CssInvalidate(); } // Kill the cache cachedCSD = null; // Notify FireCssChange(); } /// /// Called when this element is changing in one of the following ways /// /// Text child added/removed/changed /// Element moved in the tree /// /// public virtual void ElementChange(Object src, XmlNodeChangedEventArgs args) { // Invalidate the CSS, the cascade for the CSS heirarchy will need to be recomputed CssInvalidate(); // Notify any listeners FireElementChange(src, args); FireParentNodeChange(src, args, false); FireChildNodeChange(src, args, false); } /// /// Called when any parent element is changing. If an element is moved the CSS heirarchy for that element /// will need to change. /// public virtual void ParentNodeChange(Object src, XmlNodeChangedEventArgs args) { FireParentNodeChange(src, args, true); } /// /// Called when any attribute is changing. This is typically triggered by calls to /// setAttribute() and should only be called from the CssXmlDocument. /// /// public virtual void AttributeChange(Object src, XmlNodeChangedEventArgs args) { // Invalidate the CSS, the cascade for the CSS heirarchy will need to be recomputed // We do this before and after the change because we need to invalidate the old and new locations CssInvalidate(); XmlAttribute attribute = src as XmlAttribute; if(attribute != null) { HandleAttributeChange(attribute); } // Notify any listeners FireAttributeChange(src, args); FireParentNodeChange(src, args, false); FireChildNodeChange(src, args, false); // Invalidate the CSS, the cascade for the CSS heirarchy will need to be recomputed CssInvalidate(); } /// /// This function allows each element to handle it's own behaviors for /// attribute changing. By default, the cached computed style is invalidated /// because most attributes refer to style properties. /// /// The attribute that is changing. public virtual void HandleAttributeChange(XmlAttribute attribute) { if(attribute.NamespaceURI.Length == 0) { switch (attribute.LocalName) { case "style": style = null; break; } } } /// /// Called when any child node is changing. If an element is moved the CSS heirarchy for that element /// will need to change. This is mainly useful when one of the child nodes parent is a /// referenced node (for example in a <use> element. /// public virtual void ChildNodeChange(Object src, XmlNodeChangedEventArgs args) { FireChildNodeChange(src, args, true); } protected void FireCssChange() { if (cssChangeHandler != null) { cssChangeHandler(); } } protected void FireAttributeChange(Object src, XmlNodeChangedEventArgs args) { if (attributeChangeHandler != null) { attributeChangeHandler(src, args); } } protected void FireElementChange(Object src, XmlNodeChangedEventArgs args) { if (elementChangeHandler != null) { elementChangeHandler(src, args); } } protected void FireParentNodeChange(Object src, XmlNodeChangedEventArgs args, bool fireEvent) { if (fireEvent && parentNodeChangeHandler != null) { parentNodeChangeHandler(src, args); } foreach(XmlNode child in ChildNodes) { if (child.NodeType != XmlNodeType.Element) continue; CssXmlElement cssChild = child as CssXmlElement; if(cssChild != null) { cssChild.ParentNodeChange(src, args); } } } protected void FireChildNodeChange(Object src, XmlNodeChangedEventArgs args, bool fireEvent) { if (fireEvent && childNodeChangeHandler != null) { childNodeChangeHandler(src, args); } CssXmlElement cssParent = ParentNode as CssXmlElement; if(cssParent != null) { cssParent.ChildNodeChange(src, args); } } public virtual event NodeChangeHandler attributeChangeHandler; public virtual event NodeChangeHandler elementChangeHandler; public virtual event NodeChangeHandler parentNodeChangeHandler; public virtual event NodeChangeHandler childNodeChangeHandler; public virtual event CssChangeHandler cssChangeHandler; #endregion } }