/// <summary> /// Merge a ScriptLoadGroup into this group /// </summary> /// <param name="group">The group to merge into this one</param> public void Merge(ScriptLoadGroup group) { System.Diagnostics.Debug.Assert(this != group, "Can't merge ScriptLoadGroup into itself!"); if (group.Parent != null && this.Parent != null && group.Parent != this.Parent) { // Both groups have parents, meaning the merge needs to // start there, not this low in the tree. this.Parent.Merge(group.Parent); return; } foreach (ScriptLoadItem script in group.Scripts) { this.AddScript(script); } if (group.ChildGroup != null) { if (ChildGroup == null) ChildGroup = group.ChildGroup; else // Recurse down children if both groups have them { // Detach the child group before merging, or we'll trigger an infinite // loop where it comes back to this already merged parent group to try // to merge it group.ChildGroup.Parent = null; ChildGroup.Merge(group.ChildGroup); } } }
public ScriptLoadGroup(ScriptLoadGroup parent) { Parent = parent; }
public HashSet<ScriptLoadGroup> MapScripts(HashSet<ScriptResource> pageScripts) { // TODO: A setup like this: // jquery -> site -> loggedin -> shipmenttotals -> dash // jquery -> site -> rsswidget // Causes this to lose scripts - shipmenttotals and dash are thrown out // TODO: A setup like this: // jquery -> site -> loggedin -> shipmenttotals -> dash // jquery, excanvas -> flot -> dash // Causes an infinite loop - presumably from jquery being too deep to properly merge // Just stupidly map it all out every time - in the future we should // use caching and better data structures to make this smart // The global list of ScriptResource objects declared in Global.asax Dictionary<string, ScriptResource> appScripts = AppScripts.Current.List; foreach (ScriptResource resource in pageScripts) { if (ProcessedScripts.ContainsKey(resource)) { // This script has already been put into a group, so we // can just skip it. continue; } ScriptLoadItem script = processScript(resource); if (script.Script.ParentScripts.Length == 0) { simpleScriptGroup.AddScript(script); } else { // Look for its dependency already added // If it's not there, add the dependency to a new root ScriptGroup, // then add this as its only child // If it is there, add this to the list of children for that ScriptGroup // If there are more than one dependency, find all dependencies, // make new ones for ones that don't exist yet, and merge them all together ScriptLoadGroup group = new ScriptLoadGroup(); group.AddScript(script); ScriptLoadGroup parentGroup = new ScriptLoadGroup(); group.Parent = parentGroup; parentGroup = buildParentScriptGroup(script.Script.ParentScripts, parentGroup); // Find the highest level parent, and add it to root while (parentGroup.Parent != null) parentGroup = parentGroup.Parent; root.Add(parentGroup); } } if (simpleScriptGroup.Scripts.Count > 0) root.Add(simpleScriptGroup); return root; }
public void MoveScript(ScriptLoadGroup to, ScriptLoadItem script) { Scripts.Remove(script); to.AddScript(script); }
/// <summary> /// Recursively inject parents up the tree of dependencies /// Eventually we don't have any dependencies (parents), so the /// ultimate parent group has its Parent set to null, meaning it /// sits at the root /// </summary> /// <param name="scripts"></param> /// <returns></returns> protected ScriptLoadGroup buildParentScriptGroup(IEnumerable<ScriptResource> scripts, ScriptLoadGroup mergeGroup) { ScriptLoadGroup group; // If no group to merge into was passed, we're building a new one if (mergeGroup == null) group = new ScriptLoadGroup(); else group = mergeGroup; List<ScriptResource> parents = new List<ScriptResource>(); foreach (ScriptResource script in scripts) { if (ProcessedScripts.ContainsKey(script)) { // Already processed. ScriptLoadItem existingScriptItem = ProcessedScripts[script]; ScriptLoadGroup existingGroup = existingScriptItem.Group; if (existingGroup == simpleScriptGroup) { // The script is in the simpleScriptGroup, which we want to // keep simple. Move this script out of it and into our new // group. existingGroup.MoveScript(group, existingScriptItem); } else if (existingGroup != group) { // We need to merge our current group with the // group this script already lives in. // But, if the group this script lives in actually has this script as a // dependency, we're going to cause an infinite loop by merging. Check the // members for dependencies that include this one. If there are, remove it // from the existingGroup. The group we built above will now simply be the // remaining scripts' parent. ScriptLoadGroup siblingDependentGroup = null; ScriptLoadGroup checkGroup = existingGroup; while (checkGroup != null) { foreach (ScriptLoadItem sibling in checkGroup.Scripts) { if (sibling.Script.ParentScripts.Contains(script)) { siblingDependentGroup = checkGroup; break; } } checkGroup = checkGroup.Parent; } if (siblingDependentGroup != null) { existingGroup.Scripts.Remove(existingScriptItem); group.AddScript(existingScriptItem); } else { existingGroup.Merge(group); group = existingGroup; } } // else // { // No work needed. This script has already been placed in this // group (the group passed in as mergeGroup above). // } } else { // Ensure this is in the map for looking up during dependency searches ScriptLoadItem loadItem = processScript(script); // Dump the script into the group group.AddScript(loadItem); } // Build a parent list for all new scripts in this group foreach (ScriptResource resource in script.ParentScripts) parents.Add(resource); } // If there were any new parents, we need to recurse to build a // parent group if (parents.Count > 0) { // If the mergeGroup had a null parent, it was passed in already added // to the root; in this case we need to pull it from the root, as we're // about to make it a child of a new parent group. if (mergeGroup != null && mergeGroup.Parent == null) root.Remove(mergeGroup); if (group.Parent == null) // Build a new parent group.Parent = buildParentScriptGroup(parents); else // Merge parents into the passed-in group's existing parent buildParentScriptGroup(parents, group.Parent); } return group; }