예제 #1
0
        /// <summary>
        /// Resolve all script dependencies in the bound CQ document. Scripts that cotain a "data-
        /// location='head'" attribute will be moved to the head.
        /// </summary>
        ///
        /// <param name="doc">
        /// The document to resolve.
        /// </param>

        public void ResolveScriptDependencies(CQ doc)
        {
            string scriptSelector = "script[src][type='text/javascript'], script[src]:not([type]), link[type='text/css']";

            CQ scripts = doc[scriptSelector];

            if (scripts.Length == 0)
            {
                return;
            }

            // move scripts first
            // TODO: Optimize using a query caching mechanism so

            foreach (var item in scripts.Filter("[data-moveto]"))
            {
                var target = doc.Select(item["data-moveto"]);
                if (target.Length > 0)
                {
                    target.First().Append(item);
                    item.RemoveAttribute("data-moveto");
                }
            }


            // resolve dependencies

            ScriptCollection coll = new ScriptCollection(ScriptEnvironment);

            coll.Options = Options;

            // identify the insertion point for the script bundle. AddFromCq returns the first script with dependencies,
            // so scripts should be added right before that one. Otherwise they should be added
            // at the end of head.

            var firstScriptEl = coll.AddFromCq(scripts);
            CQ  firstScript   = null;

            if (firstScriptEl != null)
            {
                firstScript = firstScriptEl.Cq();
            }

            string           bundleUrl;
            List <ScriptRef> dependencies = coll.GetDependencies()
                                            .Where(item => !coll.Contains(item))
                                            .ToList();

            // Now add scripts directly for dependencies marked as NoCombine.


            var inlineScripts = Options.HasFlag(ViewEngineOptions.NoBundle) ?
                                dependencies :
                                dependencies.Where(item => item.NoCombine);


            foreach (var item in inlineScripts)
            {
                var script = GetScriptHtml(item.Path, item.ScriptHash);
                if (firstScript != null)
                {
                    firstScript.Before(script);
                }
                else
                {
                    firstScript = script;
                    doc["body"].Append(script);
                }
            }

            // Before creating the bundle, remove any duplicates of the same script on the page


            if (!Options.HasFlag(ViewEngineOptions.NoBundle))
            {
                bool hasBundle = Bundles.TryGetValue(coll, out bundleUrl);

                if (hasBundle)
                {
                    // when nocache is set, we will regenerate the bundle, but not change the script ID. The v=
                    // flag will be changed by BundleTable.

                    if (Options.HasFlag(ViewEngineOptions.NoCache))
                    {
                        string removeUrl = "~" + bundleUrl.Before("?");
                        BundleTable.Bundles.Remove(BundleTable.Bundles.GetBundleFor(removeUrl));
                        hasBundle = false;
                        ScriptID++;

                        // this code attempts to un-cache the bundle, it doesn't work.
                        // leaving it here until some permanent solution is found as a reminder
                        //
                        // http://stackoverflow.com/questions/12317391/how-to-force-bundlecollection-to-flush-cached-script-bundles-in-mvc4
                        //
                        //var bundleList = BundleTable.Bundles.ToList();
                        //BundleTable.Bundles.Clear();
                        //BundleTable.Bundles.ResetAll();
                        //BundleTable.EnableOptimizations = false;

                        //foreach (var oldBundle in bundleList)
                        //{
                        //    BundleTable.Bundles.Add(oldBundle);
                        //}
                    }
                }
                else
                {
                    ScriptID++;
                }

                if (!hasBundle)
                {
                    var activeDependencies = dependencies.Where(item => !item.NoCombine).ToList();
                    if (activeDependencies.Count > 0)
                    {
                        string bundleAlias = "~/cqbundle" + ScriptID;
                        var    bundle      = GetScriptBundle(bundleAlias);



                        foreach (var item in activeDependencies)
                        {
                            bundle.Include(item.Path);
                        }


                        BundleTable.Bundles.Add(bundle);
                        if (HttpContext.Current != null)
                        {
                            bundleUrl = BundleTable.Bundles.ResolveBundleUrl(bundleAlias, true);
                        }
                        else
                        {
                            bundleUrl = bundleAlias + "_no_http_context";
                        }
                        Bundles[coll] = bundleUrl;
                    }
                }

                var scriptPlaceholder = scripts.First();



                // add bundle after all noncombined scripts
                if (!String.IsNullOrEmpty(bundleUrl))
                {
                    firstScript.Before(GetScriptHtml(bundleUrl));
                }
            }
        }