PageRenderTime 26ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/WebGrease/WebGrease/Css/ImageAssemblyAnalysis/PropertyModel/BackgroundImage.cs

http://webgrease.codeplex.com
C# | 251 lines | 140 code | 37 blank | 74 comment | 25 complexity | df6229ab55cea7af8f473967c488730a MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // --------------------------------------------------------------------------------------------------------------------
  2. // <copyright file="BackgroundImage.cs" company="Microsoft">
  3. // Copyright Microsoft Corporation, all rights reserved
  4. // </copyright>
  5. // <summary>
  6. // Represents the Css "background-image" declaration
  7. // Example:
  8. // #selector
  9. // {
  10. // background-image: url(../../i/02/3118D8F3781159C8341246BBF2B4CA.gif);
  11. // }
  12. // </summary>
  13. // --------------------------------------------------------------------------------------------------------------------
  14. namespace WebGrease.Css.ImageAssemblyAnalysis.PropertyModel
  15. {
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Globalization;
  19. using System.Text.RegularExpressions;
  20. using Ast;
  21. using Extensions;
  22. using LogModel;
  23. /// <summary>Represents the Css "background-image" declaration
  24. /// Example:
  25. /// #selector
  26. /// {
  27. /// background-image: url(../../i/02/3118D8F3781159C8341246BBF2B4CA.gif);
  28. /// }</summary>
  29. internal sealed class BackgroundImage
  30. {
  31. /// <summary>The url reg ex.</summary>
  32. internal static readonly string UrlRegEx = @"url\((?<quote>[""']?)\s*([-\\:/.\w]+\.[\w]+)\s*\k<quote>\)";
  33. /// <summary>
  34. /// The compiled regular expression for identifying multiple urls
  35. /// </summary>
  36. private static readonly Regex MultipleUrlsRegex = new Regex(UrlRegEx, RegexOptions.IgnoreCase);
  37. /// <summary>
  38. /// The compiled regular expression for identifying exact url
  39. /// We don't want to match the data uri based images for spriting.
  40. /// </summary>
  41. private static readonly Regex UrlRegex = new Regex(string.Format(CultureInfo.InvariantCulture, "^{0}$", UrlRegEx), RegexOptions.IgnoreCase);
  42. /// <summary>
  43. /// Initializes a new instance of the BackgroundImage class
  44. /// </summary>
  45. internal BackgroundImage()
  46. {
  47. }
  48. /// <summary>Initializes a new instance of the BackgroundImage class</summary>
  49. /// <param name="declarationNode">The declaration node</param>
  50. internal BackgroundImage(DeclarationNode declarationNode)
  51. {
  52. if (declarationNode == null)
  53. {
  54. throw new ArgumentNullException("declarationNode");
  55. }
  56. this.DeclarationNode = declarationNode;
  57. var expr = declarationNode.ExprNode;
  58. this.ParseTerm(expr.TermNode);
  59. expr.TermsWithOperators.ForEach(this.ParseTermWithOperator);
  60. }
  61. /// <summary>
  62. /// Gets the declaration node
  63. /// </summary>
  64. public DeclarationNode DeclarationNode { get; private set; }
  65. /// <summary>
  66. /// Gets the url term node
  67. /// </summary>
  68. internal TermNode UrlTermNode { get; private set; }
  69. /// <summary>
  70. /// Gets the image url
  71. /// </summary>
  72. internal string Url { get; private set; }
  73. /// <summary>Determines if there are multiple urls in the declaration.</summary>
  74. /// <param name="text">The declaration text.</param>
  75. /// <returns>True if multiple urls are present.</returns>
  76. internal static bool HasMultipleUrls(string text)
  77. {
  78. if (string.IsNullOrWhiteSpace(text))
  79. {
  80. return false;
  81. }
  82. return MultipleUrlsRegex.Matches(text).Count > 1;
  83. }
  84. /// <summary>Matches the url pattern and returns the value of url term</summary>
  85. /// <param name="termNode">The term node which contains the url pattern</param>
  86. /// <param name="url">The url value which is found in the term node</param>
  87. /// <returns>True if the url is found</returns>
  88. internal static bool TryGetUrl(TermNode termNode, out string url)
  89. {
  90. if (termNode != null &&
  91. !string.IsNullOrWhiteSpace(termNode.StringBasedValue))
  92. {
  93. var termValue = termNode.StringBasedValue;
  94. var match = UrlRegex.Match(termValue);
  95. if (match.Success &&
  96. match.Groups.Count > 2 &&
  97. !string.IsNullOrWhiteSpace(url = match.Groups[1].Value))
  98. {
  99. return true;
  100. }
  101. }
  102. url = null;
  103. return false;
  104. }
  105. /// <summary>Verify that url has some value</summary>
  106. /// <param name="parent">The parent AST node</param>
  107. /// <param name="cssPath">The css path from which AST node was prepared</param>
  108. /// <param name="imageReferencesToIgnore">The image reference to ignore</param>
  109. /// <param name="imageAssemblyAnalysisLog">The logging object</param>
  110. /// <param name="shouldIgnore">The result of scan if we should ignore the image reference</param>
  111. /// <returns>True if px units are used</returns>
  112. internal bool VerifyBackgroundUrl(AstNode parent, string cssPath, HashSet<string> imageReferencesToIgnore, ImageAssemblyAnalysisLog imageAssemblyAnalysisLog, out bool shouldIgnore)
  113. {
  114. shouldIgnore = false;
  115. if (string.IsNullOrWhiteSpace(this.Url))
  116. {
  117. if (imageAssemblyAnalysisLog != null)
  118. {
  119. // Log diagnostics
  120. imageAssemblyAnalysisLog.Add(new ImageAssemblyAnalysis
  121. {
  122. AstNode = parent,
  123. FailureReason = FailureReason.NoUrl
  124. });
  125. }
  126. return false;
  127. }
  128. if (!string.IsNullOrWhiteSpace(cssPath) && imageReferencesToIgnore != null)
  129. {
  130. var fullImageUrl = this.Url.MakeAbsoluteTo(cssPath);
  131. if (imageReferencesToIgnore.Contains(fullImageUrl))
  132. {
  133. // Log diagnostics
  134. if (imageAssemblyAnalysisLog != null)
  135. {
  136. imageAssemblyAnalysisLog.Add(new ImageAssemblyAnalysis
  137. {
  138. AstNode = parent,
  139. FailureReason = FailureReason.IgnoreUrl
  140. });
  141. }
  142. shouldIgnore = true;
  143. return false;
  144. }
  145. }
  146. return true;
  147. }
  148. /// <summary>Parses the term AST node</summary>
  149. /// <param name="termNode">The AST to parse</param>
  150. internal void ParseTerm(TermNode termNode)
  151. {
  152. if (termNode == null)
  153. {
  154. return;
  155. }
  156. string url;
  157. if (!TryGetUrl(termNode, out url))
  158. {
  159. return;
  160. }
  161. // Update the properties
  162. this.UrlTermNode = termNode;
  163. this.Url = url;
  164. }
  165. /// <summary>Parses the termwithoperator AST node</summary>
  166. /// <param name="termWithOperatorNode">The AST to parse</param>
  167. internal void ParseTermWithOperator(TermWithOperatorNode termWithOperatorNode)
  168. {
  169. if (termWithOperatorNode == null)
  170. {
  171. return;
  172. }
  173. this.ParseTerm(termWithOperatorNode.TermNode);
  174. }
  175. /// <summary>Updates the term for url</summary>
  176. /// <param name="originalTermNode">The original term node</param>
  177. /// <param name="updatedTermNode">The new term node</param>
  178. /// <param name="updatedUrl">The new url</param>
  179. /// <returns>True if the term is updated</returns>
  180. internal bool UpdateTermForUrl(TermNode originalTermNode, out TermNode updatedTermNode, string updatedUrl)
  181. {
  182. if (originalTermNode == this.UrlTermNode)
  183. {
  184. updatedUrl = string.Format(CultureInfo.CurrentUICulture, ImageAssembleConstants.UrlTerm, updatedUrl);
  185. // Create a term with new assembled image url
  186. updatedTermNode = new TermNode(originalTermNode.UnaryOperator, originalTermNode.NumberBasedValue, updatedUrl, originalTermNode.Hexcolor, originalTermNode.FunctionNode);
  187. return true;
  188. }
  189. updatedTermNode = originalTermNode;
  190. return false;
  191. }
  192. /// <summary>Updates the background image node with new url</summary>
  193. /// <param name="updatedUrl">The updated url</param>
  194. /// <returns>The new declaration node with updated values</returns>
  195. internal DeclarationNode UpdateBackgroundImageNode(string updatedUrl)
  196. {
  197. if (this.DeclarationNode == null)
  198. {
  199. return null;
  200. }
  201. var originalExpr = this.DeclarationNode.ExprNode;
  202. var originalTermNode = originalExpr.TermNode;
  203. TermNode newBackgroundImageTermNode;
  204. // Try update for url
  205. if (this.UpdateTermForUrl(originalTermNode, out newBackgroundImageTermNode, updatedUrl))
  206. {
  207. // No need to update the term with operators since there is only url element allowed in the expression
  208. // which is primary term.
  209. return new DeclarationNode(this.DeclarationNode.Property, new ExprNode(newBackgroundImageTermNode, originalExpr.TermsWithOperators), this.DeclarationNode.Prio);
  210. }
  211. return this.DeclarationNode;
  212. }
  213. }
  214. }