2009-06-19 07:39:16 +00:00
using System ;
using System.Collections.Generic ;
using System.Text ;
using System.Web.UI ;
using System.Configuration.Provider ;
using System.Web ;
namespace umbraco.presentation.ClientDependency.Providers
{
public abstract class ClientDependencyProvider : ProviderBase
{
protected Control DependantControl { get ; private set ; }
protected ClientDependencyPathCollection FolderPaths { get ; private set ; }
2009-06-19 09:32:18 +00:00
protected List < IClientDependencyFile > AllDependencies { get ; private set ; }
/// <summary>
/// Set to true to disable composite scripts so all scripts/css comes through as individual files.
/// </summary>
public bool IsDebugMode { get ; set ; }
2009-06-19 07:39:16 +00:00
protected abstract void RegisterJsFiles ( List < IClientDependencyFile > jsDependencies ) ;
protected abstract void RegisterCssFiles ( List < IClientDependencyFile > cssDependencies ) ;
2009-06-19 09:32:18 +00:00
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 , List < IClientDependencyFile > dependencies , ClientDependencyPathCollection paths )
2009-06-19 07:39:16 +00:00
{
DependantControl = dependantControl ;
AllDependencies = dependencies ;
FolderPaths = paths ;
UpdateFilePaths ( ) ;
//seperate the types into 2 lists for all dependencies without composite groups
2009-06-19 09:32:18 +00:00
List < IClientDependencyFile > jsDependencies = AllDependencies . FindAll (
delegate ( IClientDependencyFile a )
2009-06-19 07:39:16 +00:00
{
2009-06-19 09:32:18 +00:00
if ( ! IsDebugMode )
return a . DependencyType = = ClientDependencyType . Javascript & & string . IsNullOrEmpty ( a . CompositeGroupName ) ;
else
return a . DependencyType = = ClientDependencyType . Javascript ;
2009-06-19 07:39:16 +00:00
}
) ;
2009-06-19 09:32:18 +00:00
List < IClientDependencyFile > cssDependencies = AllDependencies . FindAll (
delegate ( IClientDependencyFile a )
2009-06-19 07:39:16 +00:00
{
2009-06-19 09:32:18 +00:00
if ( ! IsDebugMode )
return a . DependencyType = = ClientDependencyType . Css & & string . IsNullOrEmpty ( a . CompositeGroupName ) ;
else
return a . DependencyType = = ClientDependencyType . Css ;
2009-06-19 07:39:16 +00:00
}
) ;
// sort by priority
jsDependencies . Sort ( ( a , b ) = > a . Priority . CompareTo ( b . Priority ) ) ;
cssDependencies . Sort ( ( a , b ) = > a . Priority . CompareTo ( b . Priority ) ) ;
2009-06-19 09:32:18 +00:00
if ( ! IsDebugMode ) RegisterCssFiles ( ProcessCompositeGroups ( ClientDependencyType . Css ) ) ;
RegisterCssFiles ( cssDependencies . ConvertAll < IClientDependencyFile > ( a = > { return ( IClientDependencyFile ) a ; } ) ) ;
if ( ! IsDebugMode ) RegisterJsFiles ( ProcessCompositeGroups ( ClientDependencyType . Javascript ) ) ;
2009-06-19 07:39:16 +00:00
RegisterJsFiles ( jsDependencies . ConvertAll < IClientDependencyFile > ( a = > { return ( IClientDependencyFile ) a ; } ) ) ;
2009-06-19 09:32:18 +00:00
2009-06-19 07:39:16 +00:00
}
private List < IClientDependencyFile > ProcessCompositeGroups ( ClientDependencyType type )
{
2009-06-19 09:32:18 +00:00
List < IClientDependencyFile > dependencies = AllDependencies . FindAll (
delegate ( IClientDependencyFile a )
2009-06-19 07:39:16 +00:00
{
return a . DependencyType = = type & & ! string . IsNullOrEmpty ( a . CompositeGroupName ) ;
}
2009-06-19 09:32:18 +00:00
) ;
2009-06-19 07:39:16 +00:00
List < string > groups = new List < string > ( ) ;
List < IClientDependencyFile > files = new List < IClientDependencyFile > ( ) ;
2009-06-19 09:32:18 +00:00
foreach ( IClientDependencyFile a in dependencies )
2009-06-19 07:39:16 +00:00
{
if ( ! groups . Contains ( a . CompositeGroupName ) )
{
string url = ProcessCompositeGroup ( dependencies , a . CompositeGroupName , type ) ;
groups . Add ( a . CompositeGroupName ) ;
files . Add ( new SimpleDependencyFile ( url , type ) ) ;
DependantControl . Page . Trace . Write ( "ClientDependency" , "Composite group " + a . CompositeGroupName + " URL: " + url ) ;
}
}
return files ;
}
/// <summary>
/// Returns a full url with the encoded query strings for the handler which will process the composite group.
/// </summary>
/// <param name="dependencies"></param>
/// <param name="groupName"></param>
/// <returns></returns>
2009-06-19 09:32:18 +00:00
private string ProcessCompositeGroup ( List < IClientDependencyFile > dependencies , string groupName , ClientDependencyType type )
2009-06-19 07:39:16 +00:00
{
string handler = "{0}?s={1}&t={2}" ;
StringBuilder files = new StringBuilder ( ) ;
2009-06-19 09:32:18 +00:00
List < IClientDependencyFile > byGroup = dependencies . FindAll (
delegate ( IClientDependencyFile a )
2009-06-19 07:39:16 +00:00
{
return a . CompositeGroupName = = groupName ;
}
) ;
2009-06-19 09:32:18 +00:00
byGroup . Sort ( ( a , b ) = > a . Priority . CompareTo ( b . Priority ) ) ;
foreach ( IClientDependencyFile a in byGroup )
2009-06-19 07:39:16 +00:00
{
files . Append ( a . FilePath + ";" ) ;
}
string url = string . Format ( handler , CompositeDependencyHandler . HandlerFileName , HttpContext . Current . Server . UrlEncode ( EncodeTo64 ( files . ToString ( ) ) ) , type . ToString ( ) ) ;
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 url ;
}
private string EncodeTo64 ( string toEncode )
{
byte [ ] toEncodeAsBytes = System . Text . ASCIIEncoding . ASCII . GetBytes ( toEncode ) ;
string returnValue = System . Convert . ToBase64String ( toEncodeAsBytes ) ;
return returnValue ;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="dependencies"></param>
/// <param name="paths"></param>
/// <param name="control"></param>
private void UpdateFilePaths ( )
{
2009-06-19 09:32:18 +00:00
foreach ( IClientDependencyFile dependency in AllDependencies )
2009-06-19 07:39:16 +00:00
{
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 ) ;
}
}
}
internal class SimpleDependencyFile : IClientDependencyFile
{
public SimpleDependencyFile ( string filePath , ClientDependencyType type )
{
FilePath = filePath ;
DependencyType = type ;
}
public string FilePath { get ; set ; }
public ClientDependencyType DependencyType { get ; set ; }
public string InvokeJavascriptMethodOnLoad { get ; set ; }
public int Priority { get ; set ; }
2009-06-19 09:32:18 +00:00
public string CompositeGroupName { get ; set ; }
public string PathNameAlias { get ; set ; }
2009-06-19 07:39:16 +00:00
}
}
}