/// <summary> /// Asigns 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); } CssUtils.SetPropertyValue(box, prop.Key, value); } }
/// <summary> /// Check if the selectors of the css blocks is the same. /// </summary> /// <param name="other">the other block to compare to</param> /// <returns>true - the selectors on blocks are the same, false - otherwise</returns> public bool EqualsSelector(CssBlock other) { if (ReferenceEquals(null, other)) { return(false); } if (ReferenceEquals(this, other)) { return(true); } if (other.Hover != Hover) { return(false); } if (other._selectors == null && _selectors != null) { return(false); } if (other._selectors != null && _selectors == null) { return(false); } if (other._selectors != null && _selectors != null) { if (!Equals(other._selectors.Count, _selectors.Count)) { return(false); } for (int i = 0; i < _selectors.Count; i++) { if (!Equals(other._selectors[i].Class, _selectors[i].Class)) { return(false); } if (!Equals(other._selectors[i].DirectParent, _selectors[i].DirectParent)) { return(false); } } } return(true); }
/// <summary> /// Check if the two css blocks are the same (same class, selectors and properties). /// </summary> /// <param name="other">the other block to compare to</param> /// <returns>true - the two blocks are the same, false - otherwise</returns> public bool Equals(CssBlock other) { if (ReferenceEquals(null, other)) { return(false); } if (ReferenceEquals(this, other)) { return(true); } if (!Equals(other._class, _class)) { return(false); } if (!Equals(other._properties.Count, _properties.Count)) { return(false); } foreach (var property in _properties) { if (!other._properties.ContainsKey(property.Key)) { return(false); } if (!Equals(other._properties[property.Key], property.Value)) { return(false); } } if (!EqualsSelector(other)) { return(false); } return(true); }
/// <summary> /// Check if the given css block is assignable to the given css box.<br/> /// the block is assignable if it has no hierarchial selectors or if the hierarchy matches.<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) { if (block.Selectors != null) { 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 == selector.Class) matched = true; if (!matched && box.HtmlTag.HasAttribute("class")) { var className = box.HtmlTag.Attributes["class"]; if (selector.Class == "." + className || selector.Class == box.HtmlTag.Name + "." + className) matched = true; } if (!matched && box.HtmlTag.HasAttribute("id")) { var id = box.HtmlTag.Attributes["id"]; if (selector.Class == "#" + id) matched = true; } if (!matched && selector.DirectParent) return false; } } } return true; }
/// <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 beggining 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>>(); _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); } } }
/// <summary> /// Merge the other block properties into this css block.<br/> /// Other block properties can overwrite this block properties. /// </summary> /// <param name="other">the css block to merge with</param> public void Merge(CssBlock other) { ArgChecker.AssertArgNotNull(other, "other"); foreach (var prop in other._properties.Keys) { _properties[prop] = other._properties[prop]; } }
/// <summary> /// Check if the selectors of the css blocks is the same. /// </summary> /// <param name="other">the other block to compare to</param> /// <returns>true - the selectors on blocks are the same, false - otherwise</returns> public bool EqualsSelector(CssBlock other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; if( other.Hover != Hover ) return false; if (other._selectors == null && _selectors != null) return false; if (other._selectors != null && _selectors == null) return false; if (other._selectors != null && _selectors != null) { if (!Equals(other._selectors.Count,_selectors.Count)) return false; for (int i = 0; i < _selectors.Count; i++) { if (!Equals(other._selectors[i].Class, _selectors[i].Class)) return false; if (!Equals(other._selectors[i].DirectParent, _selectors[i].DirectParent)) return false; } } return true; }
/// <summary> /// Check if the two css blocks are the same (same class, selectors and properties). /// </summary> /// <param name="other">the other block to compare to</param> /// <returns>true - the two blocks are the same, false - otherwise</returns> public bool Equals(CssBlock other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; if (!Equals(other._class, _class)) return false; if (!Equals(other._properties.Count, _properties.Count)) return false; foreach (var property in _properties) { if (!other._properties.ContainsKey(property.Key)) return false; if (!Equals(other._properties[property.Key], property.Value)) return false; } if (!EqualsSelector(other)) return false; return true; }
/// <summary> /// Add css box that has ":hover" selector to be handled on mouse hover. /// </summary> /// <param name="box">the box that has the hover selector</param> /// <param name="block">the css block with the css data with the selector</param> internal void AddHoverBox(CssBox box, CssBlock block) { ArgChecker.AssertArgNotNull(box, "box"); ArgChecker.AssertArgNotNull(block, "block"); if(_hoverBoxes == null) _hoverBoxes = new List<Tupler<CssBox, CssBlock>>(); _hoverBoxes.Add(new Tupler<CssBox, CssBlock>(box, block)); }
/// <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; }
/// <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; }