private void AddParent(ReadFile readFile) { if (!Parents.Contains(readFile)) { Parents.Add(readFile); } }
private static string SafeExpandView(ReadFile readFile, string inputContent, object parameters = null, bool prePostRun = false) { foreach (var e in Expanders) { var tagRegex = GetExpanderRegex(e); if (prePostRun) { //run before expansion e.PreRun(readFile); } //run the expansion inputContent = e.Expand(readFile, tagRegex, inputContent, parameters); if (prePostRun) { //run the post expansion e.PostRun(readFile); } } //run the matchers again if required after expansion foreach (var e in Expanders) { if (e.AssociatedRegEx.Matches(inputContent).Any()) { inputContent = SafeExpandView(readFile, inputContent, parameters); } } return(inputContent); }
public override string Expand(ReadFile readFile, Regex regEx, string inputContent, object parameters = null) { var matches = regEx.Matches(inputContent); if (matches.Count == 0) { return(inputContent); } var paramsAsDict = (IDictionary <string, object>)parameters; if (paramsAsDict == null) { return(inputContent); } var serializer = DependencyResolver.Resolve <IDataSerializer>(); foreach (Match jsonMatch in matches) { ExtractMatch(jsonMatch, out var _, out var keyValuePairs); keyValuePairs.TryGetValue("source", out var source); keyValuePairs.TryGetValue("var", out var targetVar); paramsAsDict.TryGetValue(source, out var sourceObj); var match = jsonMatch.Result("$0"); var json = serializer.Serialize(sourceObj); var scriptedJson = $"<script type='text/javascript'>var {targetVar}={json};</script>"; readFile.Content = readFile.Content.ReplaceFirstOccurance(match, scriptedJson); inputContent = inputContent.ReplaceFirstOccurance(match, scriptedJson); } //remove the global matches readFile.Content = regEx.Replace(readFile.Content, ""); inputContent = regEx.Replace(inputContent, ""); return(inputContent); }
public override string Expand(ReadFile readFile, Regex regEx, string inputContent, object parameters = null) { var componentMatches = regEx.Matches(inputContent); var viewComponentManager = DependencyResolver.Resolve <IViewComponentManager>(); var componentIndexOnPage = 0; if (componentMatches.Count == 0) { foreach (var meta in readFile.GetMeta(nameof(ComponentExpander))) { var componentName = meta.Key; var valueAsArray = (object[])meta.Value; //extract values componentIndexOnPage = (int)valueAsArray[0]; var keyValuePairs = (Dictionary <string, string>)valueAsArray[1]; var componentParameters = GetComponentParameters(keyValuePairs, parameters); viewComponentManager.InvokeViewComponent(componentName, componentParameters, out string _, out object model, out string _, true); MergeModel(parameters, model, componentName, componentIndexOnPage, out string _, out string _); } return(inputContent); } foreach (Match componentMatch in componentMatches) { ExtractMatch(componentMatch, out string[] straightParameters, out Dictionary <string, string> keyValuePairs); if (!straightParameters.Any()) { throw new Exception($"A component must be specified with component tag in view {readFile.FileName}"); } var componentName = straightParameters[0]; //collect the values that are being passed to the component var componentParameters = GetComponentParameters(keyValuePairs, parameters); viewComponentManager.InvokeViewComponent(componentName, componentParameters, out string componentContent, out object model, out string viewPath); if (!viewPath.IsNullEmptyOrWhiteSpace()) { readFile.AddChild(ReadFile.From(viewPath)); } //merge models MergeModel(parameters, model, componentName, componentIndexOnPage, out var assignString, out var resetAssignString); if (!WebHelper.IsAjaxRequest(ApplicationEngine.CurrentHttpContext.Request)) { //add keyvaluepairs as meta along with the index readFile.AddMeta(componentName, new object[] { componentIndexOnPage, keyValuePairs }, nameof(ComponentExpander)); } var match = componentMatch.Result("$0"); //replace only first occurance of the pattern result readFile.Content = readFile.Content.ReplaceFirstOccurance(match, assignString + componentContent + resetAssignString); inputContent = inputContent.ReplaceFirstOccurance(match, assignString + componentContent + resetAssignString); //next component componentIndexOnPage++; } return(inputContent); }
public void AddChild(ReadFile readFile) { if (!Children.Contains(readFile)) { Children.Add(readFile); } //interlink readFile.AddParent(this); }
public override string Expand(ReadFile readFile, Regex regEx, string inputContent, object parameters = null) { var matches = regEx.Matches(inputContent); if (matches.Count == 0) { return(inputContent); } foreach (Match match in matches) { ExtractMatch(match, out string[] straightParameters, out Dictionary <string, string> keyValuePairs); if (!straightParameters.Any()) { if (readFile != null) { throw new Exception($"A route name must be specified in the view {readFile.FileName}"); } else { throw new Exception($"A route name must be specified. See inner exception for content", new Exception(inputContent)); } } var routeName = straightParameters[0]; var absoluteValue = "false"; if (keyValuePairs != null) { keyValuePairs.TryGetValue("absolute", out absoluteValue); if (!absoluteValue.IsNullEmptyOrWhiteSpace()) { keyValuePairs.Remove("absolute"); } } var routeUrl = ApplicationEngine.RouteUrl(routeName, keyValuePairs, absoluteValue == "true"); routeUrl = HttpUtility.UrlDecode(routeUrl); //we are using lower case urls and this causes the liquid parameters to convert to lower case like {{userId}} converts to {{userid}} //we need to fix those otherwise the liquid urls break. if (keyValuePairs != null && !routeUrl.IsNullEmptyOrWhiteSpace()) { foreach (var kp in keyValuePairs) { routeUrl = routeUrl.Replace(kp.Value, kp.Value, StringComparison.InvariantCultureIgnoreCase); } } if (readFile != null) { readFile.Content = readFile.Content.Replace(match.Result("$0"), routeUrl); } inputContent = inputContent.Replace(match.Result("$0"), routeUrl); } return(inputContent); }
public override string Expand(ReadFile readFile, Regex regEx, string inputContent, object parameters = null) { var matches = regEx.Matches(inputContent); if (!matches.Any()) { return(inputContent); } var viewAccountant = DependencyResolver.Resolve <IViewAccountant>(); foreach (Match match in matches) { ExtractMatch(match, out string[] straightParameters, out Dictionary <string, string> keyValuePairs); if (!straightParameters.Any()) { throw new Exception($"No control type specified in the tag in view file {readFile.FileName}"); } var controlType = straightParameters[0]; var viewName = viewAccountant.GetThemeViewPath($"Controls/{controlType}"); if (viewName.IsNullEmptyOrWhiteSpace()) { throw new Exception($"Can't find the view {controlType} in view file {readFile.FileName}"); } var controlFile = ReadFile.From(viewName); readFile.AddChild(controlFile); keyValuePairs = keyValuePairs ?? new Dictionary <string, string>(); var keyValuePairsString = keyValuePairs.Where(x => !NonAttributeNames.Contains(x.Key)) .Select(x => $"{x.Key}=\"{x.Value}\"").ToList(); var attributeString = string.Join(" ", keyValuePairsString); var assigns = string.Join("", keyValuePairsString.Select(x => string.Format(AssignFormat, x))); //replace attribute string var controlText = controlFile.Content.Replace("%attributes%", attributeString); foreach (var kp in keyValuePairs.Where(x => NonAttributeNames.Contains(x.Key) || x.Key.StartsWith("__"))) { var key = kp.Key.StartsWith("__") ? kp.Key.Substring("__".Length) : kp.Key; controlText = controlText.Replace($"%{key}%", kp.Value); } //replace the non attributes which were not passed foreach (var nan in NonAttributeNames) { controlText = controlText.Replace($"%{nan}%", ""); } var resetAssigns = string.Join("", keyValuePairs.Select(x => string.Format(AssignFormat, $"{x.Key}=\"\"")).ToList()); readFile.Content = readFile.Content.Replace(match.Result("$0"), assigns + controlText + resetAssigns); inputContent = inputContent.Replace(match.Result("$0"), assigns + controlText + resetAssigns); } return(inputContent); }
public override string Expand(ReadFile readFile, Regex regEx, string inputContent, object parameters = null) { var widgetMatches = regEx.Matches(inputContent); if (widgetMatches.Count == 0) { return(inputContent); } var pluginSettings = DependencyResolver.Resolve <PluginSettings>(); var widgets = pluginSettings.GetSiteWidgets(true); var viewAccountant = DependencyResolver.Resolve <IViewAccountant>(); var widgetFormat = "{0}"; //do we have a wrapper? var widgetPath = viewAccountant.GetThemeViewPath("Widgets/Index"); if (!widgetPath.IsNullEmptyOrWhiteSpace()) { var widgetFile = ReadFile.From(widgetPath); widgetFormat = widgetFile.Content; } foreach (Match widgetMatch in widgetMatches) { ExtractMatch(widgetMatch, out string[] straightParameters, out Dictionary <string, string> keyValuePairs); if (!straightParameters.Any()) { throw new Exception($"A widget must be specified with zone name in view {readFile.FileName}"); } var zoneName = straightParameters[0]; var widgetBuilder = new StringBuilder(); var paramBuilder = new StringBuilder(); keyValuePairs = keyValuePairs ?? new Dictionary <string, string>(); foreach (var kp in keyValuePairs) { paramBuilder.Append($"{kp.Key}=\"{kp.Value}\" "); } foreach (var widget in widgets.Where(x => x.ZoneName == zoneName)) { var idStr = $"id=\"{widget.Id}\""; var componentStr = $"{{% {string.Format(ComponentFormat, widget.WidgetSystemName, paramBuilder)} {idStr} %}}"; var widgetStr = string.Format(widgetFormat, componentStr); widgetBuilder.Append(widgetStr); widgetBuilder.AppendLine(); } inputContent = inputContent.Replace(widgetMatch.Result("$0"), widgetBuilder.ToString()); readFile.Content = readFile.Content.Replace(widgetMatch.Result("$0"), widgetBuilder.ToString()); } return(inputContent); }
public override string Expand(ReadFile readFile, Regex regEx, string inputContent, object parameters = null) { var matches = regEx.Matches(inputContent); if (matches.Count == 0) { return(inputContent); } var globalBuilder = new StringBuilder(); foreach (Match match in matches) { ExtractMatch(match, out string[] _, out Dictionary <string, string> keyValuePairs); var ifStr = ""; var endifStr = ""; if (keyValuePairs.ContainsKey("if")) { ifStr = $"{{% if {keyValuePairs["if"]} %}}"; endifStr = "{% endif %}"; } keyValuePairs.Remove("if"); foreach (var parameter in keyValuePairs) { var variableName = parameter.Key; var variableValue = parameter.Value; globalBuilder.Append(ifStr); globalBuilder.Append(string.Format(AssignFormat, variableName, variableValue)); globalBuilder.Append(endifStr); globalBuilder.Append(Environment.NewLine); } } //remove the global matches readFile.Content = regEx.Replace(readFile.Content, ""); inputContent = regEx.Replace(inputContent, ""); //and prepend the assignments readFile.Content = globalBuilder + readFile.Content; inputContent = globalBuilder + inputContent; return(inputContent); }
public static ReadFile From(string fileName) { var localFileProvider = DependencyResolver.Resolve <ILocalFileProvider>(); var lastModified = localFileProvider.GetLastModifiedDateTime(fileName); if (!ReadFileCache.TryGetValue(fileName, out ReadFile readFile) || readFile.IsModified()) { if (readFile == null) { readFile = new ReadFile(); ReadFileCache.TryAdd(fileName, readFile); } readFile.Content = localFileProvider.ReadAllText(fileName); readFile.LastModified = lastModified; readFile.FileName = fileName; readFile.IsDirty = false; readFile.Meta?.Clear(); readFile.MarkAllParentsDirty(); } return(readFile); }
public override string Expand(ReadFile readFile, Regex regEx, string inputContent, object parameters = null) { var partialMatches = regEx.Matches(inputContent); if (partialMatches.Count == 0) { return(inputContent); } foreach (Match partialMatch in partialMatches) { ExtractMatch(partialMatch, out string[] straightParameters, out Dictionary <string, string> keyValuePairs); if (!straightParameters.Any()) { throw new Exception($"A partial view must be specified with partial tag in view {readFile.FileName}"); } var partialFile = straightParameters[0]; var includeAll = false; if (keyValuePairs != null) { keyValuePairs.TryGetValue("includeAll", out var iaStr); includeAll = iaStr == "true"; } var viewAccountant = DependencyResolver.Resolve <IViewAccountant>(); Func <string, string> partialReplaceAction = s => { var viewFile = ReadFile.From(s); readFile.AddChild(viewFile); //expand the view file var partialViewExpanded = Expand(viewFile, regEx, viewFile.Content); if (viewFile.Content == partialViewExpanded) { var assignString = ""; var resetAssignString = ""; if (keyValuePairs != null) { //create assigns assignString = string.Join("", keyValuePairs.Select(x => string.Format(AssignFormat, x.Key, x.Value))); resetAssignString = string.Join("", keyValuePairs.Select(x => string.Format(AssignFormat, x.Key, ""))); } return(assignString + partialViewExpanded + resetAssignString); } return(""); }; var partialExpanded = ""; if (includeAll) { var allFiles = viewAccountant.GetAllMatchingViewPaths(partialFile); foreach (var file in allFiles) { partialExpanded = partialExpanded + partialReplaceAction(file); } } else { //read the layout now var viewPath = viewAccountant.GetThemeViewPath(partialFile); if (viewPath.IsNullEmptyOrWhiteSpace()) { throw new Exception($"Can't find partial view {partialFile} in view {readFile.FileName}"); } partialExpanded = partialReplaceAction(viewPath); } inputContent = inputContent.Replace(partialMatch.Result("$0"), partialExpanded); readFile.Content = readFile.Content.Replace(partialMatch.Result("$0"), partialExpanded); } return(inputContent); }
public virtual void PostRun(ReadFile readFile) { //execute anything that needs to be done before expansion }
public abstract string Expand(ReadFile readFile, Regex regEx, string inputContent, object parameters = null);
public static void ExpandView(string viewName, object parameters, out string expandedContent) { var readFile = ReadFile.From(viewName); expandedContent = SafeExpandView(readFile, readFile.Content, parameters, true); }
public override string Expand(ReadFile readFile, Regex regEx, string inputContent, object parameters = null) { var viewAccountant = DependencyResolver.Resolve <IViewAccountant>(); var layoutMatches = regEx.Matches(inputContent); if (layoutMatches.Count == 0) { if (WebHelper.IsAjaxRequest(ApplicationEngine.CurrentHttpContext.Request)) { //return content without layout in case of raw request var layoutLessContent = readFile.GetMeta(nameof(LayoutExpander)) .FirstOrDefault(x => x.Key == ContentWithoutLayoutKey) .Value?.ToString() ?? inputContent; //is there any ajax layout var ajaxLayoutPath = viewAccountant.GetLayoutPath("_LayoutAjax"); if (!ajaxLayoutPath.IsNullEmptyOrWhiteSpace()) { var ajaxLayout = ReadFile.From(ajaxLayoutPath); return(ajaxLayout.Content.Replace("{% bodyContent %}", layoutLessContent)); } return(layoutLessContent); } return(inputContent); } if (layoutMatches.Count > 1) { throw new Exception($"Can't use two layouts in one page"); } ExtractMatch(layoutMatches[0], out string[] straightParameters, out Dictionary <string, string> keyValuePairs); if (!straightParameters.Any()) { throw new Exception($"A layout must be specified with layout tag in view {readFile.FileName}"); } if (keyValuePairs != null && keyValuePairs.Any(x => x.Key.Equals("ignoreForAjax") && x.Value == "true")) { //preserve content without layout readFile.AddMeta(ContentWithoutLayoutKey, regEx.Replace(readFile.Content, ""), nameof(LayoutExpander)); if (WebHelper.IsAjaxRequest(ApplicationEngine.CurrentHttpContext.Request)) { //return content without layout in case of raw request return(readFile.GetMeta(nameof(LayoutExpander)) .FirstOrDefault(x => x.Key == ContentWithoutLayoutKey) .Value.ToString()); } } var layoutValue = straightParameters[0]; //read the layout now var layoutPath = viewAccountant.GetLayoutPath(layoutValue); if (layoutPath.IsNullEmptyOrWhiteSpace()) { throw new Exception($"Can't find layout {layoutValue} in view {readFile.FileName}"); } var layoutFile = ReadFile.From(layoutPath); readFile.AddChild(layoutFile); //expand the layout file var layoutExpanded = Expand(layoutFile, regEx, layoutFile.Content); if (layoutFile.Content == layoutExpanded) { var bodyMatcher = new Regex(@"{%\s+bodyContent\s+%}"); //remove the layout tag readFile.Content = regEx.Replace(readFile.Content, ""); //remove the body content tag readFile.Content = bodyMatcher.Replace(layoutExpanded, readFile.Content); return(readFile.Content); } return(string.Empty); }
public override string Expand(ReadFile readFile, Regex regEx, string inputContent, object parameters = null) { var bundleMatches = regEx.Matches(inputContent); if (!bundleMatches.Any()) { return(inputContent); } var generalSettings = DependencyResolver.Resolve <GeneralSettings>(); foreach (Match match in bundleMatches) { ExtractMatch(match, out _, out var keyValuePairs); if (keyValuePairs == null) { throw new Exception($"The bundle tag must have a render parameter set to either 'css' or 'js' in file " + readFile.FileName); } keyValuePairs.TryGetValue("render", out var render); keyValuePairs.TryGetValue("bundle", out var bundle); keyValuePairs.TryGetValue("gz", out var gz); bundle = bundle ?? ""; render = render?.ToLower(); var gzExt = ""; if (!gz.IsNullEmptyOrWhiteSpace() && gz == "true") { gzExt = ".gz"; } if (render.IsNullEmptyOrWhiteSpace() || (render != "css" && render != "js")) { throw new Exception($"The bundle tag must have a render parameter set to either 'css' or 'js' in file " + readFile.FileName); } if (render == "css") { if (generalSettings.EnableCssBundling) { var cssBundle = readFile.GetMeta(nameof(CssExpander)).FirstOrDefault(x => x.Key == CssExpander.BundleKey); var bundleUrls = (Dictionary <string, string>)cssBundle.Value; if (!bundleUrls.ContainsKey(bundle)) { throw new Exception($"The bundle with name '{bundle}' was not declared. File name " + readFile.FileName); } var bundleUrl = bundleUrls[bundle]; var link = $"<link rel=\"stylesheet\" href=\"{bundleUrl}{gzExt}\" />"; inputContent = inputContent.ReplaceFirstOccurance(match.Result("$0"), link); readFile.Content = readFile.Content.ReplaceFirstOccurance(match.Result("$0"), link); } else { inputContent = inputContent.ReplaceFirstOccurance(match.Result("$0"), ""); readFile.Content = readFile.Content.ReplaceFirstOccurance(match.Result("$0"), ""); } } else { if (generalSettings.EnableJsBundling) { var jsBundle = readFile.GetMeta(nameof(JsExpander)).FirstOrDefault(x => x.Key == JsExpander.BundleKey); var bundleUrls = (Dictionary <string, string>)jsBundle.Value; if (!bundleUrls.ContainsKey(bundle)) { throw new Exception($"The bundle with name '{bundle}' was not declared. File name " + readFile.FileName); } var bundleUrl = bundleUrls[bundle]; var script = $"<script type=\"text/javascript\" src=\"{bundleUrl}{gzExt}\"></script>"; inputContent = inputContent.ReplaceFirstOccurance(match.Result("$0"), script); readFile.Content = readFile.Content.ReplaceFirstOccurance(match.Result("$0"), script); } else { inputContent = inputContent.ReplaceFirstOccurance(match.Result("$0"), ""); readFile.Content = readFile.Content.ReplaceFirstOccurance(match.Result("$0"), ""); } } } return(inputContent); }
public override string Expand(ReadFile readFile, Regex regEx, string inputContent, object parameters = null) { if (!ApplicationEngine.IsAdmin()) { return(inputContent); } var matches = regEx.Matches(inputContent); var paramsAsDict = (IDictionary <string, object>)parameters; if (paramsAsDict == null) { return(inputContent); } if (matches.Count == 0) { var navMeta = readFile.GetMeta(nameof(NavigationExpander)).FirstOrDefault(x => x.Key == NavigationKey); if (navMeta.Key != null && !paramsAsDict.ContainsKey(NavigationKey)) { paramsAsDict.Add(NavigationKey, navMeta.Value); } var groupMeta = readFile.GetMeta(nameof(NavigationExpander)).FirstOrDefault(x => x.Key == NavigationGroupKey); if (navMeta.Key != null && !paramsAsDict.ContainsKey(NavigationGroupKey)) { paramsAsDict.Add(NavigationGroupKey, groupMeta.Value); } return(inputContent); } List <Navigation> menuList = null; if (!paramsAsDict.ContainsKey(NavigationKey)) { menuList = new List <Navigation>(); paramsAsDict.Add(NavigationKey, menuList); } List <NavigationGroup> groupList = null; if (!paramsAsDict.ContainsKey(NavigationGroupKey)) { groupList = new List <NavigationGroup>() { new NavigationGroup() { Name = "", Id = null, DisplayOrder = 0 } }; paramsAsDict.Add(NavigationGroupKey, groupList); } menuList = (List <Navigation>)paramsAsDict[NavigationKey]; groupList = (List <NavigationGroup>)paramsAsDict[NavigationGroupKey]; foreach (Match match in matches) { ExtractMatch(match, out var _, out var keyValuePairs); //first for groups keyValuePairs.TryGetValue("group", out var group); keyValuePairs.TryGetValue("order", out var displayOrderValue); keyValuePairs.TryGetValue("id", out var id); int.TryParse(displayOrderValue, out var displayOrder); if (group != null) { groupList.Add(new NavigationGroup() { Name = group, DisplayOrder = displayOrder, Id = id }); continue; } //do we have route parameter? keyValuePairs.TryGetValue("url", out var url); keyValuePairs.TryGetValue("title", out var title); keyValuePairs.TryGetValue("systemName", out var systemName); keyValuePairs.TryGetValue("groupId", out var groupId); keyValuePairs.TryGetValue("capability", out var capability); keyValuePairs.TryGetValue("iconClass", out var iconClass); keyValuePairs.TryGetValue("parent", out var parent); if (url.IsNullEmptyOrWhiteSpace()) { //use the current url if it's empty url url = ApplicationEngine.CurrentHttpContext.Request.Path + ApplicationEngine.CurrentHttpContext.Request.QueryString; } title = title ?? ""; systemName = systemName ?? ""; menuList.Add(new Navigation() { Title = title, Url = url, SystemName = systemName, DisplayOrder = displayOrder, GroupId = groupId, Capabilities = capability?.Split(" or ", StringSplitOptions.RemoveEmptyEntries), IconClass = iconClass, Type = NavigationType, Parent = parent }); } menuList = menuList.OrderBy(x => x.DisplayOrder).ToList(); paramsAsDict[NavigationKey] = menuList; readFile.AddMeta(NavigationKey, menuList, $"{nameof(NavigationExpander)}"); groupList = groupList.OrderBy(x => x.DisplayOrder).ToList(); paramsAsDict[NavigationGroupKey] = groupList; readFile.AddMeta(NavigationGroupKey, groupList, $"{nameof(NavigationExpander)}"); //remove the tags readFile.Content = regEx.Replace(readFile.Content, ""); inputContent = regEx.Replace(inputContent, ""); return(inputContent); }
public override string Expand(ReadFile readFile, Regex regEx, string inputContent, object parameters = null) { var jsMatches = regEx.Matches(inputContent); if (!jsMatches.Any()) { return(inputContent); } var generalSettings = DependencyResolver.Resolve <GeneralSettings>(); var jsFiles = new Dictionary <string, List <string> >(); foreach (Match match in jsMatches) { ExtractMatch(match, out var parts, out var keyValuePairs); if (!parts.Any()) { throw new Exception("No file provided for bundling"); } var bundle = ""; keyValuePairs?.TryGetValue("bundle", out bundle); if (jsFiles.ContainsKey(bundle)) { jsFiles[bundle] = jsFiles[bundle].Concat(parts).ToList(); } else { jsFiles.Add(bundle, parts.ToList()); } } var canBundle = generalSettings.EnableJsBundling && jsFiles.Any(); string bundleUrl = null; Dictionary <string, string> bundleUrls = null; if (canBundle) { bundleUrls = new Dictionary <string, string>(); var bundleService = DependencyResolver.Resolve <IBundleService>(); foreach (var jsFile in jsFiles) { bundleUrl = bundleService.GenerateJsBundle(jsFile.Value.ToArray()); bundleUrls.Add(jsFile.Key, bundleUrl); } } if (bundleUrls != null) { inputContent = regEx.Replace(inputContent, ""); readFile.Content = regEx.Replace(readFile.Content, ""); //add to readfile to be picked up by the renderer tag readFile.AddMeta(BundleKey, bundleUrls, nameof(JsExpander)); } else { var fileIndex = 0; //render the link tags on page var alljsFiles = jsFiles.Values.SelectMany(x => x).ToArray(); foreach (Match match in jsMatches) { var script = $"<script type=\"text/javascript\" src=\"{alljsFiles[fileIndex++]}\"></script>"; inputContent = inputContent.ReplaceFirstOccurance(match.Result("$0"), script); } } return(inputContent); }