Exemple #1
0
        /// <summary>
        /// Takes the url of a combined file, and returns its content,
        /// ready to be sent to the browser.
        /// The url does not relate to an actual file. The combined content
        /// only lives in cache. If it is not in cache, this method
        /// finds out which files are associated with the fileUrl,
        /// reads them, compresses the content and stores that in cache
        /// (as well as returning it).
        /// </summary>
        /// <param name="context"></param>
        /// <param name="combinedFileUrl"></param>
        /// <returns></returns>
        public static string Content(
            HttpContext context, string combinedFileUrl,
            bool minifyCSS, bool minifyJavaScript,
            UrlProcessor urlProcessor,
            out FileTypeUtilities.FileType fileType)
        {
            // Get the urlsHash from the combined file url.
            string urlsHash = UrlVersioner.UnversionedFilename(combinedFileUrl);

            // Based on that hash, get the compressed content of the combined file.
            string combinedFileContent = null;
            string newVersionId = null;
            GetContentVersion(
                context, urlsHash, urlProcessor, null,
                minifyCSS, minifyJavaScript,
                out combinedFileContent, out newVersionId, out fileType);

            if (combinedFileContent == null)
            {
                // combinedFileUrl matches an actual file on the server. Load that file
                // and return its content to the browser. Because this situation normally
                // only happens when a (already minified) library file could not be loaded
                // from a CDN (a rare event), or if we are in debug mode, there is no need 
                // to minify the file.

                combinedFileContent = "";
                string filePath = MapPath(combinedFileUrl, urlProcessor.ThrowExceptionOnMissingFile);
                if (filePath != null)
                {
                    combinedFileContent = File.ReadAllText(filePath);
                }

                fileType = FileTypeUtilities.FileTypeOfUrl(combinedFileUrl);
            }

            return combinedFileContent;
        }
Exemple #2
0
		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);
		}
Exemple #3
0
		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);
				}
			}
		}
Exemple #4
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="headHtml">
        /// the current contents of the head
        /// </param>
        /// <param name="totalFileNames">
        /// 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).
        /// </param>
        /// <param name="combineCSSFiles">
        /// If true, the CSS files in the group are combined into a single file.
        /// </param>
        /// <param name="combineJavaScriptFiles">
        /// If true, the JavaScript files in the group are combined into a single file.
        /// </param>
        /// <param name="urlProcessor">
        /// Use this UrlProcessor to for example insert version ids.
        /// The ProcessedImageUrls property of this UrlProcessor has already been loaded with the 
        /// urls of the images on the page.
        /// </param>
        /// <returns>
        /// New content of the head
        /// </returns>
        public HeadAnalysis(
            string headHtml, List<string> totalFileNames,
            ConfigSection.CombineOption combineCSSFiles, ConfigSection.CombineOption combineJavaScriptFiles,
            bool minifyCSS, bool minifyJavaScript,
            UrlProcessor urlProcessor)
        {
            // Find groups of script or link tags that load a script or css file from
            // the local site. That is, their source does not start with
            // http:// or https://
            //
            // A script tag that has code between the <script> and </script>
            // has inline script, so we're not interested in that either.
            //
            // The files within each group will be served up as a single file
            // (which only exists in cache).
            // By combining js and css files only within the groups, we make sure
            // that the order of the script files is not changed.
            // 
            // An improvement would be to provide an option to 
            // move all css link tags above the script tags, so you could
            // combine them all. Move them above, in case any javascript depends on the css.

            const string regexpScriptGroup =
                @"(?:<script[^>]*?src=(?:""|')(?<url>(?!http://)(?!https://)[^""']*?)(?:""|')[^>]*?>[\s\n\r]*</script>[\s\n\r]*)+";

            const string tagTemplateSript = "<script type=\"text/javascript\" src=\"{0}\"></script>";

            const string regexpCssGroup =
                @"(?:<link[^>]*?href=(?:""|')(?<url>(?!http://)(?!https://)[^""']*?\.css)(?:""|')[^>]*?>[\s\n\r]*)+";

            const string tagTemplateCss = "<link rel=\"stylesheet\" type=\"text/css\" href=\"{0}\" />";

            // ProcessFileType adds records to the Replacements list

            Replacements = new List<Replacement>();

            ProcessFileType(
                headHtml,
                regexpScriptGroup,
                FileTypeUtilities.FileType.JavaScript,
                tagTemplateSript,
                totalFileNames,
                combineJavaScriptFiles,
                true,
                minifyCSS, minifyJavaScript,
                urlProcessor);

            ProcessFileType(
                headHtml,
                regexpCssGroup,
                FileTypeUtilities.FileType.CSS,
                tagTemplateCss,
                totalFileNames,
                combineCSSFiles,
                false,
                minifyCSS, minifyJavaScript,
                urlProcessor);

            // The urlProcessor now contains all image urls contained in CSS files.
            // Copy those urls to this.ProcessedImageUrls.
            ProcessedImageUrls = new List<string>(urlProcessor.ProcessedImageUrls);
            urlProcessor.ProcessedImageUrls.Clear();
        }
Exemple #5
0
        /// <summary>
        /// 
        /// </summary>
        /// <param name="headHtmlSb"></param>
        /// <param name="groupRegexp"></param>
        /// <param name="fileType"></param>
        /// <param name="tagTemplate"></param>
        /// <param name="totalFileNames"></param>
        /// <param name="combineFiles"></param>
        /// <param name="placeCombinedFilesAtEnd">
        /// This is only relevant if combineFiles equals All.
        /// If placeCombinedFilesAtEnd is true, the tag loading the combined file
        /// replaces the very last file group (important if you're loading js, because it means that if any
        /// js is dependent on a library loaded from a CDN, all the js will load after that library.
        /// 
        /// If placeCombinedFilesAtEnd is false, the tag replaces the very first file group.
        /// You'd use this with CSS, to get it load possibly sooner than the js.
        /// </param>
        /// <param name="urlProcessor"></param>
        private void ProcessFileType(
            string headHtml,
            string groupRegexp,
            FileTypeUtilities.FileType fileType,
            string tagTemplate,
            List<string> totalFileNames,
            ConfigSection.CombineOption combineFiles,
            bool placeCombinedFilesAtEnd,
            bool minifyCSS, bool minifyJavaScript,
            UrlProcessor urlProcessor)
        {
            List<groupInfo> allGroups = new List<groupInfo>();
            List<Uri> totalFileUrlsList = new List<Uri>();

            Regex r = new Regex(groupRegexp, RegexOptions.IgnoreCase);
            Match m = r.Match(headHtml);

            // Visit each group of script or link tags. Record the html of each file group
            // and a list of the urls in the tags in that file group in allGroups.
            while (m.Success)
            {
                string fileGroup = m.Value;
                CaptureCollection fileUrls = m.Groups["url"].Captures;

                // Visit the src or href of each individual script or link tag in the group,
                // and add to a list of urls.

                List<Uri> fileUrlsList = new List<Uri>();
                for (int j = 0; j < fileUrls.Count; j++)
                {
                    Uri fileUrl = new Uri(HttpContext.Current.Request.Url, fileUrls[j].Value);
                    fileUrlsList.Add(fileUrl);
                    totalFileUrlsList.Add(fileUrl);
                }

                allGroups.Add(new groupInfo() { fileGroup = fileGroup, fileUrlsList = fileUrlsList });
                m = m.NextMatch();
            }

            // Process each file group in allGroups
            switch (combineFiles)
            {
                case ConfigSection.CombineOption.None:
                    // In each group, process all URLs individually into tags.
                    // Note that CombinedFile.Url not only has the ability to combine urls, but also
                    // to insert version info - and we still want that to be able to use far future cache expiry,
                    // even if not combining files.
                    // Concatenate the tags and replace the group with the concatenated tags.
                    foreach (groupInfo g in allGroups)
                    {
                        StringBuilder tagsInGroup = new StringBuilder();

                        foreach (Uri u in g.fileUrlsList)
                        {
                            string versionedUrl = CombinedFile.Url(
                                HttpContext.Current, new List<Uri>(new Uri[] { u }), fileType,
                                minifyCSS, minifyJavaScript,
                                urlProcessor, totalFileNames);
                            string versionedFileTag =
                                string.Format(tagTemplate, versionedUrl);
                            tagsInGroup.Append(versionedFileTag);
                        }

                        // Be sure to trim the group before storing it (that is, remove space at the front and end).
                        // If you don't, you may store a group with white space at either end, that then doesn't match
                        // a group in some other file that is exactly the same, except for the white space at either end.
                        Replacements.Add(new Replacement { original = g.fileGroup.Trim(), replacement = tagsInGroup.ToString() });
                    }

                    break;

                case ConfigSection.CombineOption.PerGroup:
                    // In each group, process all URLs together into a combined tag.
                    // Replace the group with that one tag.
                    foreach (groupInfo g in allGroups)
                    {
                        string combinedFileUrl = CombinedFile.Url(
                            HttpContext.Current, g.fileUrlsList, fileType, minifyCSS, minifyJavaScript, urlProcessor, totalFileNames);
                        string combinedFileTag =
                            string.Format(tagTemplate, combinedFileUrl);

                        Replacements.Add(new Replacement { original = g.fileGroup.Trim(), replacement = combinedFileTag });
                    }

                    break;

                case ConfigSection.CombineOption.All:
                    // Combine all urls into a single tag. Then insert that tag in the head.
                    // Also, remove all groups.
                    {
                        string combinedFileUrl = CombinedFile.Url(
                            HttpContext.Current, totalFileUrlsList, fileType, minifyCSS, minifyJavaScript, urlProcessor, totalFileNames);
                        string combinedFileTag =
                            string.Format(tagTemplate, combinedFileUrl);

                        int idxFileGroupToReplace = placeCombinedFilesAtEnd ? (allGroups.Count - 1) : 0;

                        Replacements.Add(
                            new Replacement { original = allGroups[idxFileGroupToReplace].fileGroup.Trim(), replacement = combinedFileTag });

                        // Replace all file groups with empty string, except for the one
                        // we just replaced with the tag.
                        allGroups.RemoveAt(idxFileGroupToReplace);
                        foreach (groupInfo g in allGroups)
                        {
                            Replacements.Add(
                                new Replacement { original = g.fileGroup.Trim(), replacement = "" });
                        }
                    }
                    break;

                default:
                    throw new ArgumentException("ProcessFileType - combineFiles=" + combineFiles.ToString());
            }
        }
Exemple #6
0
        public void ProcessRequest(HttpContext context)
        {
            ConfigSection cs = ConfigSection.CurrentConfigSection();

            // --------------

            Uri url = context.Request.Url;
            string path = url.PathAndQuery;
            FileTypeUtilities.FileType fileType = FileTypeUtilities.FileTypeOfUrl(path);

            // --------------

            context.Response.AddHeader(
                "Content-Type",
                FileTypeUtilities.FileTypeToContentType(fileType));

            const int yearInSeconds = 60 * 60 * 24 * 365;

            int maxAge = yearInSeconds;
            if (context.IsDebuggingEnabled || ((!cs.InsertVersionIdInImageUrls) && FileTypeUtilities.FileTypeIsImage(fileType)))
            {
                maxAge = 0;
            }

            // --------------

            if ((fileType == FileTypeUtilities.FileType.JavaScript) ||
                (fileType == FileTypeUtilities.FileType.CSS))
            {
                UrlProcessor urlProcessor =
                    new UrlProcessor(
                        cs.CookielessDomains, cs.MakeImageUrlsLowercase, cs.InsertVersionIdInImageUrls,
                        ConfigSection.OptionIsActive(cs.EnableCookielessDomains), cs.PreloadAllImages,
                        ConfigSection.OptionIsActive(cs.ExceptionOnMissingFile));

                string content =
                    CombinedFile.Content(
                        context, path,
                        cs.MinifyCSS, cs.MinifyJavaScript,
                        urlProcessor, out fileType);

                context.Response.AddHeader("Cache-Control", "public,max-age=" + maxAge.ToString());
                context.Response.Write(content);
            }
            else
            {
                // The file type is not JavaScript or CSS, so it is an image.
                // In case the image has a version id in it, deversion the path
                string deversionedPath = path;

                if (cs.InsertVersionIdInImageUrls)
                {
                    bool deversioned;
                    deversionedPath = UrlVersioner.DeversionedImageUrl(path, out deversioned);
                }

                string fileName =
                    CombinedFile.MapPath(deversionedPath, ConfigSection.OptionIsActive(cs.ExceptionOnMissingFile));

                if (!String.IsNullOrEmpty(fileName))
                {
                    context.Response.AddHeader("Cache-Control", "public,max-age=" + maxAge.ToString());
                    context.Response.WriteFile(fileName);
                }
            }
        }
Exemple #7
0
        /// <summary>
        /// Takes the urls of a series of files (taken from the src or href
        /// attribute of their script or link tags), and returns the url
        /// of the combined file. That url will be placed in 
        /// single script or link tag that replaces the individual script or link tags.
        ///
        /// When the browser sends a request for this url, get the content
        /// to return by calling CombinedFileContent.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="fileUrls"></param>
        /// <param name="totalFileNames">
        /// The method adds the physical file names of the files making up the combined
        /// file to this parameter. If this is null, nothing is done.
        /// </param>
        /// <returns></returns>
        public static string Url(
            HttpContext context, 
            List<Uri> fileUrls, 
            FileTypeUtilities.FileType fileType,
            bool minifyCSS, bool minifyJavaScript,
            UrlProcessor urlProcessor, 
            List<string> totalFileNames)
        {
            string urlsHash = UrlsHash(fileUrls);

            // Store the urls of the files, so GetContentVersion can retrieve
            // the urls if needed.
            StoreFileUrls(context, urlsHash, fileUrls, fileType);

            string combinedFileContent = null;
            string versionId = null;
            GetContentVersion(
                context, urlsHash, urlProcessor, totalFileNames, 
                minifyCSS, minifyJavaScript,
                out combinedFileContent, out versionId, out fileType);

            string combinedFileUrl = CombinedFileUrl(urlsHash, versionId, fileType, urlProcessor);
            return combinedFileUrl;
        }
Exemple #8
0
        /// <summary>
        /// Takes the content of a CSS file and the original absolute url of that
        /// file, and changes all url() properties to absolute urls.
        /// This way, if the CSS file has been combined with other files, the 
        /// images specified in the url() properties will still show.
        /// </summary>
        /// <param name="fileContent"></param>
        /// <param name="fileUrl"></param>
        private static void FixUrlProperties(
            ref string fileContent, Uri fileUrl, UrlProcessor urlProcessor)
        {
            StringBuilder fileContentSb = new StringBuilder(fileContent);

            const string regexpUrlProperty =
                @"url\([\s\n\r]*(?<url>(?!http://)(?!https://)[^)]*?)[\s\n\r]*\)";

            Regex r = new Regex(regexpUrlProperty, RegexOptions.IgnoreCase);
            Match m = r.Match(fileContent);

            // Visit each url property
            while (m.Success)
            {
                string urlProperty = m.Value;
                CaptureCollection urlProperties = m.Groups["url"].Captures;

                if (urlProperties.Count > 0)
                {
                    string relativeUrl = urlProperties[0].Value;
                    string absoluteUrl = urlProcessor.ProcessedUrl(relativeUrl, true, true, fileUrl, null);

                    fileContentSb.Replace(urlProperty, "url(" + absoluteUrl + ")");
                }

                m = m.NextMatch();
            }

            fileContent = fileContentSb.ToString();
        }
Exemple #9
0
        /// <summary>
        /// Takes the hash identifying the urls of the files that make up a combined file.
        /// Returns the compressed content of the combined files, and the version ID
        /// of the combined files. The version ID is based on the last modified time of the last
        /// modified file file that goes into the combined file.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="urlsHash"></param>
        /// <param name="totalFileNames">
        /// The file names of the files read by this method get added to this list.
        /// If this is null, nothing is done with this parameter.
        /// </param>
        /// <param name="combinedFileContent">
        /// Content to be sent back to the browser. 
        /// Will be null if the content could not be retrieved, because the hash was not found in
        /// the Application object. This means that the file tag that caused the browser to 
        /// request this file was generated in JavaScript or appeared outside the head tag
        /// on the page. This will also happen in debug mode. 
        /// In this case, the name of the requested file matches an actual
        /// file on the server.
        /// </param>
        /// <param name="versionId"></param>
        private static void GetContentVersion(
            HttpContext context, 
            string urlsHash,
            UrlProcessor urlProcessor,
            List<string> totalFileNames,
            bool minifyCSS, bool minifyJavaScript,
            out string combinedFileContent,
            out string versionId, 
            out FileTypeUtilities.FileType fileType)
        {
            combinedFileContent = null;
            versionId = null;

            List<Uri> fileUrls;
            RetrieveFileUrls(context, urlsHash, out fileUrls, out fileType);
            if (fileUrls == null)
            {
                return;
            }

            CacheElement cacheElement = (CacheElement)context.Cache[urlsHash];
            if (cacheElement == null)
            {
                StringBuilder combinedContentSb = new StringBuilder();
                DateTime mostRecentModifiedTime = DateTime.MinValue;
                List<string> fileNames = new List<string>();
                bool fileMissing = false;

                foreach (Uri fileUrl in fileUrls)
                {
                    string filePath = MapPath(fileUrl.AbsolutePath, urlProcessor.ThrowExceptionOnMissingFile);
                    string fileContent = null;

                    if (filePath != null)
                    {
                        fileContent = File.ReadAllText(filePath);
                        if (fileType == FileTypeUtilities.FileType.CSS)
                        {
                            FixUrlProperties(ref fileContent, fileUrl, urlProcessor);
                        }

                        DateTime lastModifiedTime = File.GetLastWriteTime(filePath);

                        mostRecentModifiedTime =
                            (mostRecentModifiedTime > lastModifiedTime) ?
                                mostRecentModifiedTime : lastModifiedTime;

                        fileNames.Add(filePath);
                        if (totalFileNames != null) { totalFileNames.Add(filePath); }
                    }
                    else
                    {
                        // A comment starting with /*! doesn't get removed by the minifier
                        fileContent = string.Format("\n/*!\n** Does not exist: {0}\n*/\n", fileUrl);

                        fileMissing = true;
                    }

                    combinedContentSb.Append(fileContent);
                }

                string combinedContent = combinedContentSb.ToString();
                if (!string.IsNullOrEmpty(combinedContent))
                {
                    cacheElement = new CacheElement();

                    cacheElement.CombinedFileContent = combinedContent;
                    if (fileType == FileTypeUtilities.FileType.JavaScript)
                    {
                        if (minifyJavaScript)
                        {
                            cacheElement.CombinedFileContent = JavaScriptCompressor.Compress(combinedContent);
                        }
                    }
                    else
                    {
                        if (minifyCSS)
                        {
                            cacheElement.CombinedFileContent = CssCompressor.Compress(combinedContent);
                        }
                    }

                    cacheElement.VersionId = VersionId(mostRecentModifiedTime);

                    // Cache the newly created cacheElement
                    // 
                    // Do not cache the cacheElement if one of the files couldn't be found.
                    // That way, the package will keep checking the missing file, and pick it up
                    // when someone puts the file there.
                    if (!fileMissing)
                    {
                        CacheDependency cd = new CacheDependency(fileNames.ToArray());
                        context.Cache.Insert(urlsHash, cacheElement, cd);
                    }
                }
            }

            if (cacheElement == null)
            {
                if (context.IsDebuggingEnabled) { throw new Exception("cacheElement == null"); }

                combinedFileContent = "";
                versionId = "";
            }
            else
            {
                combinedFileContent = cacheElement.CombinedFileContent;
                versionId = cacheElement.VersionId;
            }
        }
Exemple #10
0
        /// <summary>
        /// Returns a combined file url.
        /// </summary>
        /// <param name="urlsHash">
        /// Hash based on the urls of the files that make up the combined file.
        /// </param>
        /// <param name="versionId">
        /// A string that is different for each version of the files that make up 
        /// the combined file. This is used to make sure that a browser doesn't
        /// pick up an outdated version from its internal browser cache.
        /// </param>
        /// <param name="fileType">
        /// </param>
        /// <param name="urlDomain">
        /// Domain to be used for the url.
        /// Make null or empty if you don't want a domain used in the url.
        /// </param>
        /// <returns></returns>
        private static string CombinedFileUrl(
            string urlsHash, string versionId, FileTypeUtilities.FileType fileType, UrlProcessor urlProcessor)
        {
            string url = "/" + urlsHash + FileTypeUtilities.FileTypeToExtension(fileType);

            return urlProcessor.ProcessedUrl(url, false, false, null, versionId);
        }