File: UI\ScriptReference.cs
Assembly: System.Web.Extensions
//------------------------------------------------------------------------------
// <copyright file="ScriptReference.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Web.UI {
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Reflection;
    using System.Web;
    using System.Web.Handlers;
    using System.Web.Resources;
    using System.Web.Util;
    using Debug = System.Diagnostics.Debug;
 
    [
    DefaultProperty("Path"),
    ]
    public class ScriptReference : ScriptReferenceBase {
        // Maps Tuple<string, Assembly>(resource name, assembly) to string (partial script path)
        private static readonly Hashtable _scriptPathCache = Hashtable.Synchronized(new Hashtable());
 
        private string _assembly;
        private bool _ignoreScriptPath;
        private string _name;
        private ScriptEffectiveInfo _scriptInfo;
 
        public ScriptReference() : base() { }
 
        public ScriptReference(string name, string assembly)
            : this() {
            Name = name;
            Assembly = assembly;
        }
 
        public ScriptReference(string path)
            : this() {
            Path = path;
        }
 
        internal ScriptReference(string name, IClientUrlResolver clientUrlResolver, Control containingControl)
            : this() {
            Debug.Assert(!String.IsNullOrEmpty(name), "The script's name must be specified.");
            Debug.Assert(clientUrlResolver != null && clientUrlResolver is ScriptManager, "The clientUrlResolver must be the ScriptManager.");
            Name = name;
            ClientUrlResolver = clientUrlResolver;
            IsStaticReference = true;
            ContainingControl = containingControl;
        }
 
        internal bool IsDirectRegistration {
            // set to true internally to disable checking for adding .debug
            // used when registering a script directly through SM.RegisterClientScriptResource
            get;
            set;
        }
 
        [
        Category("Behavior"),
        DefaultValue(""),
        ResourceDescription("ScriptReference_Assembly")
        ]
        public string Assembly {
            get {
                return (_assembly == null) ? String.Empty : _assembly;
            }
            set {
                _assembly = value;
                _scriptInfo = null;
            }
        }
 
        internal Assembly EffectiveAssembly {
            get {
                return ScriptInfo.Assembly;
            }
        }
 
        internal string EffectivePath {
            get {
                return String.IsNullOrEmpty(Path) ? ScriptInfo.Path : Path;
            }
        }
 
        internal string EffectiveResourceName {
            get {
                return ScriptInfo.ResourceName;
            }
        }
 
        internal ScriptMode EffectiveScriptMode {
            get {
                if (ScriptMode == ScriptMode.Auto) {
                    // - When a mapping.DebugPath exists, ScriptMode.Auto is equivilent to ScriptMode.Inherit,
                    //   since a debug path exists, even though it may not be an assembly based script.
                    // - An explicitly set Path on the ScriptReference effectively ignores a DebugPath.
                    // - When only Path is specified, ScriptMode.Auto is equivalent to ScriptMode.Release.
                    // - When only Name is specified, ScriptMode.Auto is equivalent to ScriptMode.Inherit.
                    // - When Name and Path are both specified, the Path is used instead of the Name, but
                    //   ScriptMode.Auto is still equivalent to ScriptMode.Inherit, since the assumption
                    //   is that if the Assembly contains both release and debug scripts, the Path should
                    //   contain both as well.
                    return ((String.IsNullOrEmpty(EffectiveResourceName) &&
                        (!String.IsNullOrEmpty(Path) || String.IsNullOrEmpty(ScriptInfo.DebugPath))) ?
                        ScriptMode.Release : ScriptMode.Inherit);
                }
                else {
                    return ScriptMode;
                }
            }
        }
 
        [
        Category("Behavior"),
        DefaultValue(false),
        ResourceDescription("ScriptReference_IgnoreScriptPath"),
        Obsolete("This property is obsolete. Instead of using ScriptManager.ScriptPath, set the Path property on each individual ScriptReference.")
        ]
        public bool IgnoreScriptPath {
            get {
                return _ignoreScriptPath;
            }
            set {
                _ignoreScriptPath = value;
            }
        }
 
        [
        Category("Behavior"),
        DefaultValue(""),
        ResourceDescription("ScriptReference_Name")
        ]
        public string Name {
            get {
                return (_name == null) ? String.Empty : _name;
            }
            set {
                _name = value;
                _scriptInfo = null;
            }
        }
 
        internal ScriptEffectiveInfo ScriptInfo {
            get {
                if (_scriptInfo == null) {
                    _scriptInfo = new ScriptEffectiveInfo(this);
                }
                return _scriptInfo;
            }
        }
 
        private string AddCultureName(ScriptManager scriptManager, string resourceName) {
            Debug.Assert(!String.IsNullOrEmpty(resourceName));
            CultureInfo culture = (scriptManager.EnableScriptLocalization ?
                DetermineCulture(scriptManager) : CultureInfo.InvariantCulture);
            if (!culture.Equals(CultureInfo.InvariantCulture)) {
                return AddCultureName(culture, resourceName);
            }
            else {
                return resourceName;
            }
        }
 
        private static string AddCultureName(CultureInfo culture, string resourceName) {
            if (resourceName.EndsWith(".js", StringComparison.OrdinalIgnoreCase)) {
                resourceName = resourceName.Substring(0, resourceName.Length - 2) +
                    culture.Name + ".js";
            }
            return resourceName;
        }
 
        internal bool DetermineResourceNameAndAssembly(ScriptManager scriptManager, bool isDebuggingEnabled, ref string resourceName, ref Assembly assembly) {
            // If the assembly is the AjaxFrameworkAssembly, the resource may come from that assembly
            // or from the fallback assembly (SWE).
            if (assembly == scriptManager.AjaxFrameworkAssembly) {
                assembly = ApplyFallbackResource(assembly, resourceName);
            }
            // ShouldUseDebugScript throws exception if the resource name does not exist in the assembly
            bool isDebug = ShouldUseDebugScript(resourceName, assembly,
                isDebuggingEnabled, scriptManager.AjaxFrameworkAssembly);
            if (isDebug) {
                resourceName = GetDebugName(resourceName);
            }
            // returning true means the debug version is selected
            return isDebug;
        }
 
        internal CultureInfo DetermineCulture(ScriptManager scriptManager) {
            if ((ResourceUICultures == null) || (ResourceUICultures.Length == 0)) {
                // In this case we want to determine available cultures from assembly info if available
                if (!String.IsNullOrEmpty(EffectiveResourceName)) {
                    return ScriptResourceHandler
                        .DetermineNearestAvailableCulture(GetAssembly(scriptManager), EffectiveResourceName, CultureInfo.CurrentUICulture);
                }
                return CultureInfo.InvariantCulture;
            }
            CultureInfo currentCulture = CultureInfo.CurrentUICulture;
            while (!currentCulture.Equals(CultureInfo.InvariantCulture)) {
                string cultureName = currentCulture.ToString();
                foreach (string uiCulture in ResourceUICultures) {
                    if (String.Equals(cultureName, uiCulture.Trim(), StringComparison.OrdinalIgnoreCase)) {
                        return currentCulture;
                    }
                }
                currentCulture = currentCulture.Parent;
            }
            return currentCulture;
        }
 
        internal Assembly GetAssembly() {
            return String.IsNullOrEmpty(Assembly) ? null : AssemblyCache.Load(Assembly);
        }
 
        internal Assembly GetAssembly(ScriptManager scriptManager) {
            // normalizes the effective assembly by redirecting it to the given scriptmanager's
            // ajax framework assembly when it is set to SWE.
            // EffectiveAssembly can't do this since ScriptReference does not have access by itself
            // to the script manager.
            Debug.Assert(scriptManager != null);
            Assembly assembly = EffectiveAssembly;
            if (assembly == null) {
                return scriptManager.AjaxFrameworkAssembly;
            }
            else {
                return ((assembly == AssemblyCache.SystemWebExtensions) ?
                    scriptManager.AjaxFrameworkAssembly :
                    assembly);
            }
        }
 
        // Release: foo.js
        // Debug:   foo.debug.js
        private static string GetDebugName(string releaseName) {
            // Since System.Web.Handlers.AssemblyResourceLoader treats the resource name as case-sensitive,
            // we must do the same when verifying the extension.
            // Ignore trailing whitespace. For example, "MicrosoftAjax.js " is valid (at least from
            // a debug/release naming perspective).
            if (!releaseName.EndsWith(".js", StringComparison.Ordinal)) {
                throw new InvalidOperationException(
                    String.Format(CultureInfo.CurrentUICulture, AtlasWeb.ScriptReference_InvalidReleaseScriptName, releaseName));
            }
 
            return ReplaceExtension(releaseName);
        }
 
        internal string GetPath(ScriptManager scriptManager, string releasePath, string predeterminedDebugPath, bool isDebuggingEnabled) {
            // convert the release path to a debug path if:
            // isDebuggingEnabled && not resource based
            // isDebuggingEnabled && resource based && debug resource exists
            // ShouldUseDebugScript is called even when isDebuggingEnabled=false as it verfies
            // the existence of the resource.
            // applies the culture name to the path if appropriate
            string path;
            if (!String.IsNullOrEmpty(EffectiveResourceName)) {
                Assembly assembly = GetAssembly(scriptManager);
                string resourceName = EffectiveResourceName;
                isDebuggingEnabled = DetermineResourceNameAndAssembly(scriptManager, isDebuggingEnabled, ref resourceName, ref assembly);
            }
 
            if (isDebuggingEnabled) {
                // Just use predeterminedDebugPath if it is provided. This may be because
                // a script mapping has DebugPath set. If it is empty or null, then '.debug' is added
                // to the .js extension of the release path.
                path = String.IsNullOrEmpty(predeterminedDebugPath) ? GetDebugPath(releasePath) : predeterminedDebugPath;
            }
            else {
                path = releasePath;
            }
            return AddCultureName(scriptManager, path);
        }
 
        internal Assembly ApplyFallbackResource(Assembly assembly, string releaseName) {
            // fall back to SWE if the assembly does not contain the requested resource
            if ((assembly != AssemblyCache.SystemWebExtensions) &&
                !WebResourceUtil.AssemblyContainsWebResource(assembly, releaseName)) {
                assembly = AssemblyCache.SystemWebExtensions;
            }
            return assembly;
        }
 
        // Format: <ScriptPath>/<AssemblyName>/<AssemblyVersion>/<ResourceName>
        // This function does not canonicalize the path in any way (i.e. remove duplicate slashes).
        // You must call ResolveClientUrl() on this path before rendering to the page.
        internal static string GetScriptPath(
            string resourceName,
            Assembly assembly,
            CultureInfo culture,
            string scriptPath) {
 
            return scriptPath + "/" + GetScriptPathCached(resourceName, assembly, culture);
        }
 
        // Cache partial script path, since Version.ToString() and HttpUtility.UrlEncode() are expensive.
        // Increases requests/second by 50% in ScriptManagerScriptPath.aspx test.
        private static string GetScriptPathCached(string resourceName, Assembly assembly, CultureInfo culture) {
            Tuple<string, Assembly, CultureInfo> key = Tuple.Create(resourceName, assembly, culture);
            string scriptPath = (string)_scriptPathCache[key];
 
            if (scriptPath == null) {
                // Need to use "new AssemblyName(assembly.FullName)" instead of "assembly.GetName()",
                // since Assembly.GetName() requires FileIOPermission to the path of the assembly.
                // In partial trust, we may not have this permission.
                AssemblyName assemblyName = new AssemblyName(assembly.FullName);
 
                string name = assemblyName.Name;
                string version = assemblyName.Version.ToString();
                string fileVersion = AssemblyUtil.GetAssemblyFileVersion(assembly);
 
                if (!culture.Equals(CultureInfo.InvariantCulture)) {
                    resourceName = AddCultureName(culture, resourceName);
                }
 
                // Assembly name, fileVersion, and resource name may contain invalid URL characters (like '#' or '/'),
                // so they must be url-encoded.
                scriptPath = String.Join("/", new string[] {
                    HttpUtility.UrlEncode(name), version, HttpUtility.UrlEncode(fileVersion), HttpUtility.UrlEncode(resourceName)
                });
 
                _scriptPathCache[key] = scriptPath;
            }
 
            return scriptPath;
        }
 
        [SuppressMessage("Microsoft.Design", "CA1055", Justification = "Consistent with other URL properties in ASP.NET.")]
        protected internal override string GetUrl(ScriptManager scriptManager, bool zip) {
            bool hasName = !String.IsNullOrEmpty(Name);
            bool hasAssembly = !String.IsNullOrEmpty(Assembly);
 
            if (!hasName && String.IsNullOrEmpty(Path)) {
                throw new InvalidOperationException(AtlasWeb.ScriptReference_NameAndPathCannotBeEmpty);
            }
 
            if (hasAssembly && !hasName) {
                throw new InvalidOperationException(AtlasWeb.ScriptReference_AssemblyRequiresName);
            }
 
            return GetUrlInternal(scriptManager, zip);
        }
 
        internal string GetUrlInternal(ScriptManager scriptManager, bool zip) {
            bool enableCdn = scriptManager != null && scriptManager.EnableCdn;
            return GetUrlInternal(scriptManager, zip, useCdnPath: enableCdn);
        }
 
        internal string GetUrlInternal(ScriptManager scriptManager, bool zip, bool useCdnPath) {
            if (!String.IsNullOrEmpty(EffectiveResourceName) && !IsAjaxFrameworkScript(scriptManager) &&
                AssemblyCache.IsAjaxFrameworkAssembly(GetAssembly(scriptManager))) {
                // it isnt an AjaxFrameworkScript but it might be from an assembly that is meant to
                // be an ajax script assembly, in which case we should throw an error.
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
                    AtlasWeb.ScriptReference_ResourceRequiresAjaxAssembly, EffectiveResourceName, GetAssembly(scriptManager)));
            }
 
            if (!String.IsNullOrEmpty(Path)) {
                // if an explicit path is set on the SR (not on a mapping) it always
                // takes precedence, even when EnableCdn=true.
                // Also, even if a script mapping has a DebugPath, the explicitly set path
                // overrides all -- so path.debug.js is used instead of the mapping's DebugPath,
                // hence the null 3rd parameter.
                return GetUrlFromPath(scriptManager, Path, null);
            }
            else if (!String.IsNullOrEmpty(ScriptInfo.Path)) {
                // when only the mapping has a path, CDN takes first priority
                if (useCdnPath) {
                    // first determine the actual resource name and assembly to be used
                    // This is so we can (1) apply fallback logic, where ajax fx scripts can come from the 
                    // current ajax assembly or from SWE, whichever is first, and (2) the .debug resource
                    // name is applied if appropriate.
                    string resourceName = EffectiveResourceName;
                    Assembly assembly = null;
                    bool hasDebugResource = false;
                    if (!String.IsNullOrEmpty(resourceName)) {
                        assembly = GetAssembly(scriptManager);
                        hasDebugResource = DetermineResourceNameAndAssembly(scriptManager,
                            IsDebuggingEnabled(scriptManager), ref resourceName, ref assembly);
                    }
                    string cdnPath = GetUrlForCdn(scriptManager, resourceName, assembly, hasDebugResource);
                    if (!String.IsNullOrEmpty(cdnPath)) {
                        return cdnPath;
                    }
                }
                // the mapping's DebugPath applies if it exists
                return GetUrlFromPath(scriptManager, ScriptInfo.Path, ScriptInfo.DebugPath);
            }
 
            Debug.Assert(!String.IsNullOrEmpty(EffectiveResourceName));
            return GetUrlFromName(scriptManager, scriptManager.Control, zip, useCdnPath);
        }
 
        private string GetUrlForCdn(ScriptManager scriptManager, string resourceName, Assembly assembly, bool hasDebugResource) {
            // if EnableCdn, then url always comes from mapping.Cdn[Debug]Path or WRA.CdnPath, if available.
            // first see if the script description mapping has a cdn path defined
            bool isDebuggingEnabled = IsDebuggingEnabled(scriptManager);
            bool isAssemblyResource = !String.IsNullOrEmpty(resourceName);
            bool secureConnection = scriptManager.IsSecureConnection;
            isDebuggingEnabled = isDebuggingEnabled && (hasDebugResource || !isAssemblyResource);
            string cdnPath = isDebuggingEnabled ?
                (secureConnection ? ScriptInfo.CdnDebugPathSecureConnection : ScriptInfo.CdnDebugPath) :
                (secureConnection ? ScriptInfo.CdnPathSecureConnection : ScriptInfo.CdnPath);
 
            // then see if the WebResourceAttribute for the resource has one
            // EXCEPT when the ScriptInfo has a cdnpath but it wasn't selected due to this being a secure connection
            // and it does not support secure connections. Avoid having the HTTP cdn path come from the mapping and the
            // HTTPS path come from the WRA.
            if (isAssemblyResource && String.IsNullOrEmpty(cdnPath) && String.IsNullOrEmpty(isDebuggingEnabled ? ScriptInfo.CdnDebugPath : ScriptInfo.CdnPath)) {
                ScriptResourceInfo scriptResourceInfo = ScriptResourceInfo.GetInstance(assembly, resourceName);
                if (scriptResourceInfo != null) {
                    cdnPath = secureConnection ? scriptResourceInfo.CdnPathSecureConnection : scriptResourceInfo.CdnPath;
                }
            }
            return String.IsNullOrEmpty(cdnPath) ? null : ClientUrlResolver.ResolveClientUrl(AddCultureName(scriptManager, cdnPath));
        }
 
        private string GetUrlFromName(ScriptManager scriptManager, IControl scriptManagerControl, bool zip, bool useCdnPath) {
            string resourceName = EffectiveResourceName;
            Assembly assembly = GetAssembly(scriptManager);
            bool hasDebugResource = DetermineResourceNameAndAssembly(scriptManager, IsDebuggingEnabled(scriptManager),
                ref resourceName, ref assembly);
 
            if (useCdnPath) {
                string cdnPath = GetUrlForCdn(scriptManager, resourceName, assembly, hasDebugResource);
                if (!String.IsNullOrEmpty(cdnPath)) {
                    return cdnPath;
                }
            }
 
            CultureInfo culture = (scriptManager.EnableScriptLocalization ?
                DetermineCulture(scriptManager) : CultureInfo.InvariantCulture);
#pragma warning disable 618
            // ScriptPath is obsolete but still functional
            if (IgnoreScriptPath || String.IsNullOrEmpty(scriptManager.ScriptPath)) {
                return ScriptResourceHandler.GetScriptResourceUrl(assembly, resourceName, culture, zip);
            }
            else {
                string path = GetScriptPath(resourceName, assembly, culture, scriptManager.ScriptPath);
 
                if (IsBundleReference) {
                    return scriptManager.BundleReflectionHelper.GetBundleUrl(path);
                }
 
                // Always want to resolve ScriptPath urls against the ScriptManager itself,
                // regardless of whether the ScriptReference was declared on the ScriptManager
                // or a ScriptManagerProxy.
                return scriptManagerControl.ResolveClientUrl(path);
            }
#pragma warning restore 618
        }
 
        private string GetUrlFromPath(ScriptManager scriptManager, string releasePath, string predeterminedDebugPath) {
            string path = GetPath(scriptManager, releasePath, predeterminedDebugPath, IsDebuggingEnabled(scriptManager));
            if (IsBundleReference) {
                return scriptManager.BundleReflectionHelper.GetBundleUrl(path);
            }
 
            return ClientUrlResolver.ResolveClientUrl(path);
        }
 
        private bool IsDebuggingEnabled(ScriptManager scriptManager) {
            // Deployment mode retail overrides all values of ScriptReference.ScriptMode.
            if (IsDirectRegistration || scriptManager.DeploymentSectionRetail) {
                return false;
            }
 
            switch (EffectiveScriptMode) {
                case ScriptMode.Inherit:
                    return scriptManager.IsDebuggingEnabled;
                case ScriptMode.Debug:
                    return true;
                case ScriptMode.Release:
                    return false;
                default:
                    Debug.Fail("Invalid value for ScriptReference.EffectiveScriptMode");
                    return false;
            }
        }
 
        protected internal override bool IsAjaxFrameworkScript(ScriptManager scriptManager) {
            return (GetAssembly(scriptManager) == scriptManager.AjaxFrameworkAssembly);
        }
 
        [Obsolete("This method is obsolete. Use IsAjaxFrameworkScript(ScriptManager) instead.")]
        protected internal override bool IsFromSystemWebExtensions() {
            return (EffectiveAssembly == AssemblyCache.SystemWebExtensions);
        }
 
        internal bool IsFromSystemWeb() {
            return (EffectiveAssembly == AssemblyCache.SystemWeb);
        }
 
        internal bool ShouldUseDebugScript(string releaseName, Assembly assembly,
            bool isDebuggingEnabled, Assembly currentAjaxAssembly) {
            bool useDebugScript;
            string debugName = null;
 
            if (isDebuggingEnabled) {
                debugName = GetDebugName(releaseName);
 
                // If an assembly contains a release script but not a corresponding debug script, and we
                // need to register the debug script, we normally throw an exception.  However, we automatically
                // use the release script if ScriptReference.ScriptMode is Auto.  This improves the developer
                // experience when ScriptMode is Auto, yet still gives the developer full control with the
                // other ScriptModes.
                if (ScriptMode == ScriptMode.Auto && !WebResourceUtil.AssemblyContainsWebResource(assembly, debugName)) {
                    useDebugScript = false;
                }
                else {
                    useDebugScript = true;
                }
            }
            else {
                useDebugScript = false;
            }
 
            // Verify that assembly contains required web resources.  Always check for release
            // script before debug script.
            if (!IsDirectRegistration) {
                // Don't check if direct registration, because calls to ScriptManager.RegisterClientScriptResource
                // with resources that do not exist does not throw an exception until the resource is served.
                // This was the existing behavior and matches the behavior with ClientScriptManager.GetWebResourceUrl.
                WebResourceUtil.VerifyAssemblyContainsReleaseWebResource(assembly, releaseName, currentAjaxAssembly);
                if (useDebugScript) {
                    Debug.Assert(debugName != null);
                    WebResourceUtil.VerifyAssemblyContainsDebugWebResource(assembly, debugName);
                }
            }
 
            return useDebugScript;
        }
 
        // Improves the UI in the VS collection editor, by displaying the Name or Path (if available), or
        // the short type name.
        [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
        public override string ToString() {
            if (!String.IsNullOrEmpty(Name)) {
                return Name;
            }
            else if (!String.IsNullOrEmpty(Path)) {
                return Path;
            }
            else {
                return GetType().Name;
            }
        }
 
        internal class ScriptEffectiveInfo {
            private string _resourceName;
            private Assembly _assembly;
            private string _path;
            private string _debugPath;
            private string _cdnPath;
            private string _cdnDebugPath;
            private string _cdnPathSecureConnection;
            private string _cdnDebugPathSecureConnection;
 
            public ScriptEffectiveInfo(ScriptReference scriptReference) {
                ScriptResourceDefinition definition =
                    ScriptManager.ScriptResourceMapping.GetDefinition(scriptReference);
                string name = scriptReference.Name;
                string path = scriptReference.Path;
                Assembly assembly = scriptReference.GetAssembly();
                if (definition != null) {
                    if (String.IsNullOrEmpty(path)) {
                        // only when the SR has no path, the mapping's path and debug path, if any, apply
                        path = definition.Path;
                        _debugPath = definition.DebugPath;
                    }
                    name = definition.ResourceName;
                    assembly = definition.ResourceAssembly;
                    _cdnPath = definition.CdnPath;
                    _cdnDebugPath = definition.CdnDebugPath;
                    _cdnPathSecureConnection = definition.CdnPathSecureConnection;
                    _cdnDebugPathSecureConnection = definition.CdnDebugPathSecureConnection;
                    LoadSuccessExpression = definition.LoadSuccessExpression;
                }
                else if ((assembly == null) && !String.IsNullOrEmpty(name)) {
                    // name is set and there is no mapping, default to SWE for assembly
                    assembly = AssemblyCache.SystemWebExtensions;
                }
                _resourceName = name;
                _assembly = assembly;
                _path = path;
 
                if (assembly != null && !String.IsNullOrEmpty(name) && String.IsNullOrEmpty(LoadSuccessExpression)) {
                    var scriptResourceInfo = ScriptResourceInfo.GetInstance(assembly, name);
                    if (scriptResourceInfo != null) {
                        LoadSuccessExpression = scriptResourceInfo.LoadSuccessExpression;
                    }
                }
            }
 
            public Assembly Assembly {
                get {
                    return _assembly;
                }
            }
 
            public string CdnDebugPath {
                get {
                    return _cdnDebugPath;
                }
            }
 
            public string CdnPath {
                get {
                    return _cdnPath;
                }
            }
 
            public string CdnDebugPathSecureConnection {
                get {
                    return _cdnDebugPathSecureConnection;
                }
            }
 
            public string CdnPathSecureConnection {
                get {
                    return _cdnPathSecureConnection;
                }
            }
 
            public string LoadSuccessExpression {
                get;
                private set;
            }
 
            public string DebugPath {
                get {
                    return _debugPath;
                }
            }
 
            public string Path {
                get {
                    return _path;
                }
            }
 
            public string ResourceName {
                get {
                    return _resourceName;
                }
            }
        }
    }
}