using System; using System.Collections.Generic; using System.Text; using System.Web.UI; using System.Configuration.Provider; using System.Web; using System.Linq; using umbraco.presentation.ClientDependency.Controls; namespace umbraco.presentation.ClientDependency.Providers { public abstract class ClientDependencyProvider : ProviderBase { protected Control DependantControl { get; private set; } protected ClientDependencyPathCollection FolderPaths { get; private set; } protected List AllDependencies { get; private set; } /// /// Set to true to disable composite scripts so all scripts/css comes through as individual files. /// public bool IsDebugMode { get; set; } protected abstract void RegisterJsFiles(List jsDependencies); protected abstract void RegisterCssFiles(List cssDependencies); protected abstract void ProcessSingleJsFile(string js); protected abstract void ProcessSingleCssFile(string css); public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { IsDebugMode = false; if (config != null && config["isDebug"] != null) { bool isDebug; if (bool.TryParse(config["isDebug"], out isDebug)) IsDebugMode = isDebug; } base.Initialize(name, config); } public void RegisterDependencies(Control dependantControl, ClientDependencyList dependencies, ClientDependencyPathCollection paths) { DependantControl = dependantControl; AllDependencies = new List(dependencies); FolderPaths = paths; UpdateFilePaths(); //seperate the types into 2 lists for all dependencies without composite groups List jsDependencies = AllDependencies.FindAll( delegate(IClientDependencyFile a) { return a.DependencyType == ClientDependencyType.Javascript; } ); List cssDependencies = AllDependencies.FindAll( delegate(IClientDependencyFile a) { return a.DependencyType == ClientDependencyType.Css; } ); // sort by priority jsDependencies.Sort((a, b) => a.Priority.CompareTo(b.Priority)); cssDependencies.Sort((a, b) => a.Priority.CompareTo(b.Priority)); RegisterCssFiles(cssDependencies.ConvertAll(a => { return (IClientDependencyFile)a; })); RegisterJsFiles(jsDependencies.ConvertAll(a => { return (IClientDependencyFile)a; })); //Register all startup scripts in order RegisterStartupScripts(dependencies .Where(x => !string.IsNullOrEmpty(x.InvokeJavascriptMethodOnLoad)) .OrderBy(x => x.Priority) .ToList()); } /// /// Registers all of the methods/scripts to be invoked that have been set in the InvokeJavaScriptMethodOnLoad propery /// for each dependent js script. /// /// protected virtual void RegisterStartupScripts(List dependencies) { foreach (var js in dependencies) { DependantControl.Page.ClientScript.RegisterStartupScript(this.GetType(), js.GetHashCode().ToString(), js.InvokeJavascriptMethodOnLoad, true); } } /// /// Returns a list of urls. The array will consist of only one entry if /// none of the dependencies are tagged as DoNotOptimize, otherwise, if any of them are, /// this will return the path to the file. /// /// For the optimized files, the full url with the encoded query strings for the handler which will process the composite list /// of dependencies. The handler will compbine, compress, minify (if JS), and output cache the results /// based on a hash key of the base64 encoded string. /// /// /// /// /// If the DoNotOptimize setting has been set for any of the dependencies in the list, then this will ignore them. /// /// protected List ProcessCompositeList(List dependencies, ClientDependencyType type) { List rVal = new List(); if (dependencies.Count == 0) return rVal; //build the combined composite list url string handler = "{0}?s={1}&t={2}"; StringBuilder files = new StringBuilder(); foreach (IClientDependencyFile a in dependencies.Where(x => !x.DoNotOptimize)) { files.Append(a.FilePath + ";"); } string combinedurl = string.Format(handler, CompositeDependencyHandler.HandlerFileName, HttpContext.Current.Server.UrlEncode(EncodeTo64(files.ToString())), type.ToString()); rVal.Add(combinedurl); //add any urls that are not to be optimized foreach (IClientDependencyFile a in dependencies.Where(x => x.DoNotOptimize)) { rVal.Add(a.FilePath); } //if (url.Length > CompositeDependencyHandler.MaxHandlerUrlLength) // throw new ArgumentOutOfRangeException("The number of files in the composite group " + groupName + " creates a url handler address that exceeds the CompositeDependencyHandler MaxHandlerUrlLength. Reducing the amount of files in this composite group should fix the issue"); return rVal; } private string EncodeTo64(string toEncode) { byte[] toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(toEncode); string returnValue = System.Convert.ToBase64String(toEncodeAsBytes); return returnValue; } /// /// Ensures the correctly resolved file path is set for each dependency (i.e. so that ~ are taken care of) and also /// prefixes the file path with the correct base path specified for the PathNameAlias if specified. /// /// /// /// private void UpdateFilePaths() { foreach (IClientDependencyFile dependency in AllDependencies) { if (!string.IsNullOrEmpty(dependency.PathNameAlias)) { ClientDependencyPath path = FolderPaths.Find( delegate(ClientDependencyPath p) { return p.Name == dependency.PathNameAlias; } ); if (path == null) { throw new NullReferenceException("The PathNameAlias specified for dependency " + dependency.FilePath + " does not exist in the ClientDependencyPathCollection"); } string basePath = path.ResolvedPath.EndsWith("/") ? path.ResolvedPath : path.ResolvedPath + "/"; dependency.FilePath = basePath + dependency.FilePath; } else { dependency.FilePath = DependantControl.ResolveUrl(dependency.FilePath); } } } } }