using System; using System.Collections.Generic; using System.Text; using System.Web.UI; using System.Web; using System.Linq; using umbraco.presentation.ClientDependency.Providers; using umbraco.presentation.ClientDependency.Config; namespace umbraco.presentation.ClientDependency.Controls { [ParseChildren(typeof(ClientDependencyPath), ChildrenAsProperties = true)] public class ClientDependencyLoader : Control { /// /// Constructor sets the defaults. /// public ClientDependencyLoader() { Paths = new ClientDependencyPathCollection(); IsDebugMode = false; //add this object to the context and validate the context type if (HttpContext.Current != null) { if (HttpContext.Current.Items.Contains(ContextKey)) throw new InvalidOperationException("Only one ClientDependencyLoader may exist on a page"); //The context needs to be a page Page page = HttpContext.Current.Handler as Page; if (page == null) throw new InvalidOperationException("ClientDependencyLoader only works with Page based handlers."); HttpContext.Current.Items[ContextKey] = this; } else throw new InvalidOperationException("ClientDependencyLoader requires an HttpContext"); } private const string ContextKey = "ClientDependencyLoader"; /// /// Singleton per request instance. /// /// /// If no ClientDependencyLoader control exists on the current page, an exception is thrown. /// public static ClientDependencyLoader Instance { get { if (!HttpContext.Current.Items.Contains(ContextKey)) return null; return HttpContext.Current.Items[ContextKey] as ClientDependencyLoader; } } /// /// Tracks all dependencies and maintains a deduplicated list /// private List m_Dependencies = new List(); /// /// Tracks all paths and maintains a deduplicated list /// private HashSet m_Paths = new HashSet(); /// /// Need to set the container for each of the paths to support databinding. /// protected override void CreateChildControls() { base.CreateChildControls(); foreach (ClientDependencyPath path in Paths) { path.Parent = this; } } /// /// Need to bind all children paths. /// /// protected override void OnDataBinding(EventArgs e) { base.OnDataBinding(e); foreach (ClientDependencyPath path in Paths) { path.DataBind(); } } protected override void OnPreRender(EventArgs e) { base.OnPreRender(e); m_Paths.UnionWith(Paths.Cast()); Provider.IsDebugMode = IsDebugMode; RegisterClientDependencies(Provider, this.Page, m_Paths); RenderDependencies(); } private void RenderDependencies() { m_Dependencies.ForEach(x => { x.Provider.RegisterDependencies(this.Page, x.Dependencies, m_Paths); }); } [PersistenceMode(PersistenceMode.InnerProperty)] public ClientDependencyPathCollection Paths { get; private set; } public ClientDependencyEmbedType EmbedType { get { return m_EmbedType; } set { m_EmbedType = value; switch (m_EmbedType) { case ClientDependencyEmbedType.Header: Provider = ClientDependencySettings.Instance.ProviderCollection[PageHeaderProvider.DefaultName]; break; case ClientDependencyEmbedType.ClientSideRegistration: Provider = ClientDependencySettings.Instance.ProviderCollection[ClientSideRegistrationProvider.DefaultName]; break; } } } private ClientDependencyEmbedType m_EmbedType = ClientDependencyEmbedType.Header; public bool IsDebugMode { get; set; } public ClientDependencyProvider Provider { get; set; } //TODO: Implement this and remove 'EmbedType' public string ProviderName { get; set; } #region Static Helper methods /// /// Checks if a loader already exists, if it does, it returns it, otherwise it will /// create a new one in the control specified. /// isNew will be true if a loader was created, otherwise false if it already existed. /// /// /// /// public static ClientDependencyLoader TryCreate(Control parent, out bool isNew) { if (ClientDependencyLoader.Instance == null) { ClientDependencyLoader loader = new ClientDependencyLoader(); parent.Controls.Add(loader); isNew = true; return loader; } else { isNew = false; return ClientDependencyLoader.Instance; } } #endregion /// /// Registers a file dependency /// /// /// public ClientDependencyLoader RegisterDependency(string filePath, ClientDependencyType type) { RegisterDependency(filePath, "", type); return this; } /// /// Registers a file dependency /// /// /// /// public ClientDependencyLoader RegisterDependency(string filePath, string pathNameAlias, ClientDependencyType type) { ClientDependencyLoader.Instance.RegisterDependency(ClientDependencyInclude.DefaultPriority, false, filePath, pathNameAlias, type, ""); return this; } /// /// Adds a path to the current loader /// /// /// public ClientDependencyLoader AddPath(string pathNameAlias, string path) { AddPath(new BasicClientDependencyPath() { Name = pathNameAlias, Path = path }); return this; } /// /// Adds a path to the current loader /// /// /// public void AddPath(IClientDependencyPath path) { m_Paths.Add(path); } /// /// Dynamically registers a dependency into the loader at runtime. /// This is similar to ScriptManager.RegisterClientScriptInclude. /// Registers a file dependency with the default provider. /// /// public void RegisterDependency(int priority, bool doNotOptimize, string filePath, string pathNameAlias, ClientDependencyType type, string invokeJavascriptMethodOnLoad) { BasicClientDependencyFile file = new BasicClientDependencyFile(type); file.DoNotOptimize = doNotOptimize; file.Priority = priority; file.FilePath = filePath; file.PathNameAlias = pathNameAlias; file.InvokeJavascriptMethodOnLoad = invokeJavascriptMethodOnLoad; RegisterClientDependencies(new ClientDependencyCollection() { file }, new List()); //send an empty paths collection } /// /// Registers dependencies with the specified provider. /// /// /// /// /// /// This is the top most overloaded method /// public void RegisterClientDependencies(ClientDependencyProvider provider, ClientDependencyCollection dependencies, IEnumerable paths) { //find or create the ProviderDependencyList for the provider passed in ProviderDependencyList currList = m_Dependencies .Where(x => x.Contains(provider)) .DefaultIfEmpty(new ProviderDependencyList(provider)) .SingleOrDefault(); //add the dependencies currList.AddDependencies(dependencies); //add the list if it is new if (!m_Dependencies.Contains(currList)) m_Dependencies.Add(currList); //add the paths, ensure no dups m_Paths.UnionWith(paths); } public void RegisterClientDependencies(ClientDependencyCollection dependencies, IEnumerable paths) { //RegisterClientDependencies(ClientDependencySettings.Instance.DefaultProvider, dependencies, paths); RegisterClientDependencies(Provider, dependencies, paths); } /// /// Registers dependencies /// /// /// public void RegisterClientDependencies(Control control, ClientDependencyPathCollection paths) { //RegisterClientDependencies(ClientDependencySettings.Instance.DefaultProvider, control, paths.Cast()); RegisterClientDependencies(Provider, control, paths.Cast()); } /// /// Registers dependencies with the provider name specified /// /// /// /// public void RegisterClientDependencies(string providerName, Control control, IEnumerable paths) { RegisterClientDependencies(ClientDependencySettings.Instance.ProviderCollection[providerName], control, paths); } /// /// Registers dependencies with the provider specified by T /// public void RegisterClientDependencies(Control control, List paths) where T : ClientDependencyProvider { //need to find the provider with the type ClientDependencyProvider found = null; foreach (ClientDependencyProvider p in ClientDependencySettings.Instance.ProviderCollection) { if (p.GetType().Equals(typeof(T))) { found = p; break; } } if (found == null) throw new ArgumentException("Could not find the ClientDependencyProvider specified by T"); RegisterClientDependencies(found, control, paths); } public void RegisterClientDependencies(ClientDependencyProvider provider, Control control, IEnumerable paths) { ClientDependencyCollection dependencies = FindDependencies(control); RegisterClientDependencies(provider, dependencies, paths); } /// /// Recursively find all dependencies of this control and it's entire child control heirarchy. /// /// /// private ClientDependencyCollection FindDependencies(Control control) { // find dependencies Type controlType = control.GetType(); ClientDependencyCollection dependencies = new ClientDependencyCollection(); foreach (Attribute attribute in Attribute.GetCustomAttributes(controlType)) { if (attribute is ClientDependencyAttribute) { dependencies.Add((ClientDependencyAttribute)attribute); } } // add child dependencies Type iClientDependency = typeof(IClientDependencyFile); foreach (Control child in control.Controls) { if (iClientDependency.IsAssignableFrom(child.GetType())) { IClientDependencyFile include = (IClientDependencyFile)child; dependencies.Add(include); } else { //recurse and de-duplicate! dependencies.UnionWith(FindDependencies(child)); } } return dependencies; } } }