protected override void Render (HtmlTextWriter writer) { ConfigSection cs = ConfigSection.CurrentConfigSection (); // If we are not active, render the head section to the writer as is. if (!ConfigSection.OptionIsActive (cs.Active)) { base.Render (writer); return; } // -------------- UrlProcessor urlProcessor = new UrlProcessor ( cs.CookielessDomains, cs.MakeImageUrlsLowercase, cs.InsertVersionIdInImageUrls, ConfigSection.OptionIsActive (cs.EnableCookielessDomains), cs.PreloadAllImages, ConfigSection.OptionIsActive (cs.ExceptionOnMissingFile)); // -------------- // headHtml holds the html on the page making up the // head element, including the <head> tag itself. StringBuilder headHtmlSb = new StringBuilder (); base.Render (new HtmlTextWriter (new StringWriter (headHtmlSb))); // ------------ HeadAnalysis headAnalysis = null; if (cs.HeadCaching == ConfigSection.HeadCachingOption.None) { headAnalysis = new HeadAnalysis ( headHtmlSb.ToString (), null, cs.CombineCSSFiles, cs.CombineJavaScriptFiles, cs.MinifyCSS, cs.MinifyJavaScript, urlProcessor); } else { string headCacheKey = HeadCacheKey (HttpContext.Current.Request.Url, cs.HeadCaching); headAnalysis = (HeadAnalysis)HttpContext.Current.Cache [headCacheKey]; if (headAnalysis == null) { // The urls of the combined CSS and JavaScript files in the new head // are dependent on the versions of the actual files (because they contain the version // ids). // totalFileNames will be filled with a list of the names of all // CSS and JavaScript files loaded in the head (that is, those // that get combined and/or minified). List<string> totalFileNames = new List<string> (); headAnalysis = new HeadAnalysis ( headHtmlSb.ToString (), totalFileNames, cs.CombineCSSFiles, cs.CombineJavaScriptFiles, cs.MinifyCSS, cs.MinifyJavaScript, urlProcessor); AddPageFilePaths (totalFileNames); CacheDependency cd = new CacheDependency (totalFileNames.ToArray ()); HttpContext.Current.Cache.Insert (headCacheKey, headAnalysis, cd); } } // ------------ // Do all replacements in the head specified in headAnalysis foreach (HeadAnalysis.Replacement r in headAnalysis.Replacements) { headHtmlSb.Replace (r.original, r.replacement); } // ------------ // Process all images in the page if needed. if (cs.RemoveWhitespace || urlProcessor.ImagesNeedProcessing ()) { ProcessAllImages (Control.Page.Controls, urlProcessor, cs.RemoveWhitespace); } // ------------ string headHtml = headHtmlSb.ToString (); // At this point, urlProcessor and headAnalysis contains all image urls. // Build the JavaScript to preload any images that need to be preloaded, // and insert it at the start of the head, just after the initial head tag. string preloadJS1 = PreloadJS (cs.PreloadAllImages, cs.PrioritizedImages, headAnalysis.ProcessedImageUrls); string preloadJS2 = PreloadJS (cs.PreloadAllImages, cs.PrioritizedImages, urlProcessor.ProcessedImageUrls); string preloadJS = preloadJS1 + preloadJS2; // If any urls need to be preloaded, insert the JavaScript block after the first > (that is, after the // head tag). if (!string.IsNullOrEmpty (preloadJS)) { headHtml = InsertedAfterFirstTag (headHtml, preloadJS); } writer.Write (headHtml); }
private void ProcessAllImages (ControlCollection cc, UrlProcessor urlProcessor, bool removeWhitespace) { bool imagesNeedProcessing = urlProcessor.ImagesNeedProcessing (); foreach (Control c in cc) { if (c is LiteralControl) { LiteralControl lit = (LiteralControl)c; string literalContent = lit.Text; string newLiteralContent = literalContent; if (imagesNeedProcessing) { // The "src" group in this regexp doesn't just contain the image url, but also the src= and the quotes. // That allows us to replace the entire src="...", instead of the url. // If you only replace the old url with the new url, than if you have a tag with url "images/ball3.png" after a tag with "/images/ball3.png" // when the second url ("images/ball3.png") gets replaced, it alsos replace part of the first tag "/images/ball3.png" (because the first tag // contains the second tag). const string regexpImgGroup = @"<img[^>]*?(?<src>src[^=]*?=[^""']*?(?:""|')(?<url>[^""']*?)(?:""|'))[^>]*?>"; Regex r = new Regex (regexpImgGroup, RegexOptions.IgnoreCase); Match m = r.Match (literalContent); while (m.Success) { string oldSrc = m.Groups ["src"].Value; string oldUrl = m.Groups ["url"].Value; string newUrl = urlProcessor.ProcessedUrl (oldUrl, true, false, Control.Page.Request.Url, null); string newSrc = @"src=""" + newUrl + @""""; newLiteralContent = newLiteralContent.Replace (oldSrc, newSrc); m = m.NextMatch (); } } if (removeWhitespace) { newLiteralContent = CollapsedWhitespace (newLiteralContent); } lit.Text = newLiteralContent; } else if ((c is HtmlImage) && imagesNeedProcessing) { HtmlImage hi = (HtmlImage)c; string versionId = LastUpdateTimeImageControl (hi.Src, hi, urlProcessor.ThrowExceptionOnMissingFile); hi.Src = urlProcessor.ProcessedUrl (hi.Src, true, false, Control.Page.Request.Url, versionId); } else if ((c is HyperLink) && imagesNeedProcessing) { HyperLink hl = (HyperLink)c; string versionId = LastUpdateTimeImageControl (hl.ImageUrl, hl, urlProcessor.ThrowExceptionOnMissingFile); hl.ImageUrl = urlProcessor.ProcessedUrl (hl.ImageUrl, true, false, Control.Page.Request.Url, versionId); } else if ((c is Image) && imagesNeedProcessing) { Image i = (Image)c; string versionId = LastUpdateTimeImageControl (i.ImageUrl, i, urlProcessor.ThrowExceptionOnMissingFile); i.ImageUrl = urlProcessor.ProcessedUrl (i.ImageUrl, true, false, Control.Page.Request.Url, versionId); } else { ProcessAllImages (c.Controls, urlProcessor, removeWhitespace); } } }