/// <summary> /// Generates composite Javascript from a list of scripts /// </summary> /// <param name="webConnection"></param> /// <param name="scriptUrls"></param> /// <returns></returns> private string GenerateCompositeJavascript(IWebConnection webConnection, IEnumerable<string> scriptUrls) { StringBuilder scriptBuilder = new StringBuilder(); foreach (string scriptUrl in scriptUrls) { try { IWebResults webResult = webConnection.ShellTo(scriptUrl); int statusCode = (int)webResult.Status; if ((statusCode >= 200) && (statusCode < 300)) { string script = webResult.ResultsAsString; script = JavaScriptMinifier.Instance.Minify(script); scriptBuilder.AppendFormat("\n// {0}\n", scriptUrl); scriptBuilder.Append("try {"); scriptBuilder.Append(script); scriptBuilder.Append("} catch (exception) { }"); // Note: exceptions are swallowed // This form of compression shouldn't be used when a developer is trying to debug, instead, they should // turn on Javascript debug mode, which disables this compression and allows them to use the browser's // debugger } } catch (Exception e) { log.Error("Error loading script in GenerateCompositeJavascript for script " + scriptUrl, e); } } return scriptBuilder.ToString(); }
/// <summary> /// Given an enumeration of script names, this returns ALL of the scripts that are needed, and their MD5s for caching. This /// recursively inspects scripts to find dependant scripts underneath /// </summary> /// <param name="requestedScripts"></param> /// <param name="addedScripts">Scripts that have already been added through script tags</param> /// <returns></returns> public IEnumerable<ScriptAndMD5> DetermineDependantScripts(IEnumerable<string> requestedScripts, IWebConnection webConnection) { List<string> uninspected = new List<string>(); // resolve user variables like [blahblah] foreach (string requestedScript in requestedScripts) uninspected.Add(webConnection.ResolveUserVariables(requestedScript)); requestedScripts = uninspected.ToArray(); // Each needed script, and the scripts that it depends on Dictionary<string, List<string>> dependancies = new Dictionary<string, List<string>>(); // Cache of loaded scripts Dictionary<string, string> loadedScriptCache = new Dictionary<string, string>(); // Find all of the needed scripts while (uninspected.Count > 0) { // These are all of the scripts that this script depends on List<string> scriptDependancies = new List<string>(); string scriptToInspect = uninspected[0]; uninspected.RemoveAt(0); if (!webConnection.Scripts.Contains(scriptToInspect)) { IWebResults shelledScript = webConnection.ShellTo(scriptToInspect); if (null != shelledScript) { string script = shelledScript.ResultsAsString; loadedScriptCache[scriptToInspect] = script; string firstLine = script.Split('\n', '\r')[0]; if (firstLine.StartsWith("// Scripts:")) { string unbrokenScripts = firstLine.Substring(11).Trim(); // (The list of scripts must be all on the first line, seperated by commas) foreach (string dependantScriptUntrimmed in unbrokenScripts.Split(',')) { string dependantScript = webConnection.ResolveUserVariables(dependantScriptUntrimmed.Trim()); // If the script hasn't been scanned, queue it for scanning if (!uninspected.Contains(dependantScript)) if (!dependancies.ContainsKey(dependantScript)) uninspected.Add(dependantScript); scriptDependancies.Add(dependantScript); } } dependancies[scriptToInspect] = scriptDependancies; } } } // Now that all of the needed scripts, and what depends on what are known, create a complete list of needed // scripts, sorted so that the lowest-level dependancy comes first List<string> sortedDependancies = new List<string>(); SortDependantScriptsHelper(requestedScripts, dependancies, sortedDependancies); // Calculate the results to return List<ScriptAndMD5> toReturn = new List<ScriptAndMD5>(); foreach (string scriptName in sortedDependancies) if (!webConnection.Scripts.Contains(scriptName)) { ScriptAndMD5 scriptAndMD5 = new ScriptAndMD5(); scriptAndMD5.ScriptName = scriptName; byte[] scriptBytes = System.Text.Encoding.UTF8.GetBytes(loadedScriptCache[scriptName]); // Get a free hash calculator MD5CryptoServiceProvider hashAlgorithm = StaticRecycler<MD5CryptoServiceProvider>.Get(); byte[] scriptHash; try { scriptHash = hashAlgorithm.ComputeHash(scriptBytes); } finally { // Save the hash calculator for reuse StaticRecycler<MD5CryptoServiceProvider>.Recycle(hashAlgorithm); } scriptAndMD5.MD5 = Convert.ToBase64String(scriptHash); toReturn.Add(scriptAndMD5); webConnection.Scripts.Add(scriptName); } return toReturn; }