Hi,
to avoid rendering blocking script I wrote a actionFilter. the filter put the inline js code and java sources to the bottom of each page and put the css files inline. Also the filter minize HTML, java and css. For minize javascript and css I used Microsoft Ajax Minifier. And this is the code:
//Filter Provider
public class NopFilterProvider : IFilterProvider
{
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
if (controllerContext.IsChildAction==false)
{
if (controllerContext.HttpContext.Request.HttpMethod == "GET")
{
//rendering blocking script should not work for admin area
if (!actionDescriptor.ControllerDescriptor.ControllerType.FullName.Contains("Admin"))
{
return new[]
{
new Filter(new JavaFilterAttribute(), FilterScope.Action, null)
};
}
}
}
return new Filter[] { };
}
}
Filter:
public class JavaFilterAttribute :ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.Response.Filter = new MoveScript(filterContext.HttpContext.Response.Filter);
}
}
MoveScript:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Text.RegularExpressions;
using System.Net;
using System.Web;
using Microsoft.Ajax.Utilities;
namespace Nop.Plugin.Misc.RC.Core.ActionFilter
{
public class MoveFormScriptStream : Stream
{
StringBuilder s = new StringBuilder();
StringBuilder shtml = new StringBuilder();
StringBuilder js = new StringBuilder();
StringBuilder css = new StringBuilder();
public Stream baseStream;
public MoveScript(Stream stream) :base()
{
baseStream = stream;
}
public override bool CanRead { get { return true; } }
public override bool CanSeek { get { return true; } }
public override bool CanWrite { get { return true; } }
public override void Flush() { baseStream.Flush(); }
public override long Length { get { return 0; } }
public override long Position { get; set; }
public override int Read(byte[] buffer, int offset, int count)
{
return baseStream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return baseStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
baseStream.SetLength(value);
}
public override void Close()
{
baseStream.Close();
}
public override void Write(byte[] buffer, int offset, int count)
{
string HTML = Encoding.UTF8.GetString(buffer, offset, count);
shtml.Append(HTML);
HTML = shtml.ToString();
//If Html document has body end tag
if (HTML.Contains("</body>"))
{
//inlinescript with tag <script type="text/javascript">
bool inlineScriptExist = HTML.Contains("<script type=\"text/javascript\">");
while (inlineScriptExist)
{
int startTag = HTML.IndexOf(@"<script type=");
int endTag = HTML.IndexOf(@"</script>", startTag);
string script = HTML.Substring(startTag, endTag - startTag+9);
string inlinescript = script.Replace("<script type=\"text/javascript\">", "").Replace("</script>", "");
s.Append(inlinescript);
HTML = HTML.Replace(script, "");
inlineScriptExist = HTML.Contains("<script type=\"text/javascript\">");
}
//inlinescript with tag <script>
inlineScriptExist = HTML.Contains("<script>");
while (inlineScriptExist)
{
int startTag = HTML.IndexOf(@"<script>");
int endTag = HTML.IndexOf(@"</script>", startTag);
string script = HTML.Substring(startTag, endTag - startTag + 9);
string inlinescript = script.Replace("<script>", "").Replace("</script>", "");
s.Append(inlinescript);
HTML = HTML.Replace(script, "");
inlineScriptExist = HTML.Contains("<script>");
}
inlineScriptExist = HTML.Contains("<script src=");
//put js script src to bottom
while (inlineScriptExist)
{
int startTag = HTML.IndexOf(@"<script src=");
int endTag = HTML.IndexOf(@"</script>", startTag);
string script = HTML.Substring(startTag, endTag - startTag + 9);
js.Append(script);
HTML = HTML.Replace(script, "");
inlineScriptExist = HTML.Contains("<script src=");
}
/// minify HTML
HTML = Regex.Replace(HTML, @"[a-zA-Z]+#", "#");
HTML = Regex.Replace(HTML, @"[\n\r]+\s*", string.Empty);
HTML = Regex.Replace(HTML, @"\s+", " ");
HTML = Regex.Replace(HTML, @"\s?([:,;{}])\s?", "$1");
HTML = HTML.Replace(";}", "}");
HTML = Regex.Replace(HTML, @"([\s:]0)(px|pt|%|em)", "$1");
/// Remove comments
HTML = Regex.Replace(HTML, @"/\*[\d\D]*?\*/", string.Empty);
//minify js
string _js = s.ToString();
Minifier minifier = new Minifier();
_js=minifier.MinifyJavaScript(_js);
if (s.Length > 0)
{
string test = s.ToString();
HTML = HTML.Replace("</body>", js.ToString()+"<script type='text/javascript'>" + _js + "</script></body>");
}
//css
var matches = Regex.Matches(HTML, "<link href=\"(?<url>.*?)\" rel=\"(?<rel>.*?)\" type=\"(?<type>.*?)\" />(?<text>.*?)");
int cssCount=0;
foreach (Match m in matches)
{
if (m.Groups["type"].Value.Contains("text/css"))
{
string url = m.Groups["url"].Value;
cssCount=cssCount+1;
string host = HttpContext.Current.Request.Url.Host;
string scheme = HttpContext.Current.Request.Url.Scheme + "://";
var webClient = new WebClient();
string readHtml = webClient.DownloadString(scheme + host + url);
readHtml = readHtml.Replace("../", "Themes/Tierarzt24/Content/");
css.Append(readHtml);
HTML = HTML.Replace(m.Value, "");
}
}
string cssCompress = minifier.MinifyStyleSheet(css.ToString());
HTML=HTML.Replace("<head>", "</title><style>" + cssCompress + "</style>");
buffer = System.Text.Encoding.UTF8.GetBytes(HTML);
baseStream.Write(buffer, 0, buffer.Length);
}
}
}
}