Generating/registering client-side scripts on ASP/VB.NET - javascript

I am currently in the process of converting old "Web 1.0" code to meet current standards.
Is there a better way to generate and append a client-side script other than appending a plethora of lines to a StringBuilder and then registering it to the page via ClientScript.RegisterStartupScript(Me.GetType(), "startUpScript", strScript)?
Is there any other way (besides putting all of this into a global .js file) that this example can be improved? If including this into the main .js file is the "best practice" alternative, then why?
Dim lsbScript As New Text.StringBuilder
lsbScript.Append(vbCrLf)
lsbScript.Append("<script language=""javascript>""" & vbCrLf)
lsbScript.Append("<!--" & vbCrLf)
...
lsbScript.Append("//-->" & vbCrLf)
lsbScript.Append("</SCRIPT>" & vbCrLf)
If Not ClientScript.IsStartupScriptRegistered("someScript") Then
ClientScript.RegisterStartupScript(Me.GetType(), "someScript", lsbScript.ToString)
End If

a good middle ground might be offloading the script contents to a new js file, and including a script node from codebehind.
protected void Page_Load(object sender, EventArgs e) {
bool someCondition = Whatever();
if (someCondition)
{
System.Web.UI.HtmlControls.HtmlGenericControl script;
script = new System.Web.UI.HtmlControls.HtmlGenericControl("script");
script.Attributes["src"] = "myscript.js";
this.Controls.Add(script);
}
}
this will just drop the node at the end of the page. alternatively, you could put a placeholder control whereever you like on the page, and add the new HtmlGenericControl to that.
(sorry, vb is not my native language).

Yes you can include a .js file in your project. Set it's Build Action (right click the file in your project explorer) to Embedded Resource
For example: common.js
Above the namespace of your server control (or whereever you need it):
[assembly: System.Web.UI.WebResource(
"MyFullNameSpace.Common.js",
"text/javascript", PerformSubstitution = true)]
OnPreRender event:
if (!Page.ClientScript.IsClientScriptIncludeRegistered("Common"))
{
string url = Page.ClientScript.GetWebResourceUrl(this.GetType(), "MyFullNameSpace.Common.js");
Page.ClientScript.RegisterClientScriptInclude("Common", url);
}
Beware to replace MyFullNameSpace with the complete and exact namespace of where .js is located. If it doesn't work probably .net can't find it and you can use reflector to open your dll and find the .js as embedded resource so you will know the exact namespace it's in.
I always do this from Server Controls but i image it could be done in a web app project as well.

You can get a little mileage out of using AppendLine() instead of concatenating vbCrLf.

Related

Android - How to load external javascript files within at runtime generated HTML data?

I have a WebView containing HTML data. The data is generated at runtime. A main feature of my app is highlighting certain parts of this HTML data. I tried this by using javascript.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_reader_page, container, false);
webview = (WebView) rootView.findViewById(R.id.resultText);
webview.getSettings().setJavaScriptEnabled(true);
String page = makeHTML();
webview.loadUrl("file:///android_asset/jquery-1.8.2.js");
webview.loadUrl("file:///android_asset/src_javascript.js");
webview.loadData(page, "text/html", "UTF-8");
return rootView;
}
private String makeHTML() {
StringBuilder sb = new StringBuilder();
sb.append("<!DOCTYPE html>\n");
sb.append("<html>\n");
sb.append("<head>\n");
sb.append("</head>\n");
sb.append("<body>\n");
sb.append(tokenizedText + "\n");
sb.append("</body>\n");
sb.append("</html>\n");
return sb.toString();
}
tokenizedText is my at runtime generated data with this format:
<YT_SEN id="_YT_SEN_0">This is my first sentence.</YT_SEN>
<YT_SEN id="_YT_SEN_1">This is my second sentence.</YT_SEN>
...
When my data is loaded in the WebView, the user can highlight a particular sentence by giving its number. This method then calls the corresponding javascript function:
public void highlightSentence(int sent_id) {
if (android.os.Build.VERSION.SDK_INT < 19) {
webview.loadUrl("javascript:highlightSentence('_YT_SEN_" +sent_id+ "', " +color+ ")");
} else {
webview.evaluateJavascript("javascript:highlightSentence('_YT_SEN_" +sent_id+ "', " +color+ ")", null);
}
}
The javascript function for highlighting (defined inside file:///android_asset/src_javascript.js):
function highlightSentence(object,color)
{
document.getElementById(object).style.backgroundColor = color;
}
The output of Logcat when I execute the highlightSentence method:
I/chromium﹕ [INFO:CONSOLE(1)] "Uncaught ReferenceError: highlightSentence is not defined", source: (1)
Somehow the WebView can't find the highlightSentence function. I think it's because of the way I load the Javascript and JQuery files. Yet I don't know (and can't find) the proper way to load external js-files within at runtime generated HTML data.
Note: I use the WebView solely for offline use, I have no need for any internet communication whatsoever. WebView seemed like the easiest way to enable dynamic highlighting.
It seems the Javascript same origin policy is the root of the problem. The WebView will only load javascript files which are from the same origin as the html. Since no origin for the html was given, the data scheme is used as default. If, however, the data is loaded with the same base url as the javascript files, no problem arises.
Load the html data (with file:///android_asset/javascript/ being the directory of the javascript files):
webview.loadDataWithBaseURL("file:///android_asset/javascript/", page, "text/html", "UTF-8", null);
Then reference the javascript files like this inside the html:
<script src='jquery-1.8.2.js' type='text/javascript'></script>
<script src='src_javascript.js' type='text/javascript'></script>
Assuming your javascript is in the assets directory point to it with a file url
file://android_asset/<some java script file in assets>
file://android_asset/ points to the assets directory in your apk.
So you can reference the script in your html when you build it for the webview.
<script charset='utf-8' type='text/javascript'
src='file://android_asset/myjavascript.js'></script>

How do I use ASP.NET bundling and minification without recompiling?

Constraints: I'm not using MVC, just regular ol' .aspx files in my web app. Not using master pages either - each page is a different beast, so that solution isn't right for me.
Most examples I've read for bundling and minification require either some special MVC markup or require you to identify the bundled scripts / stylesheets up front and then refer to these bundles. I want to avoid recompiling DLLs every time I add or modify a .js reference in a .aspx page.
I'm a bit stumped from reading the Msft docs.. is there a way (like an ASP.NET control) that I can just wrap a series of script tags (or link tags for CSS) to create and use a bundle dynamically? I don't want to reinvent the wheel, but seriously considering creating my own user control / custom control that handles this. Are there other options?
For example, looking for something like this:
<asp:AdHocScriptBundle id="mypage_bundle" runat="server">
<script type="text/javascript" src="~/scripts/mypage1.js"></script>
<script type="text/javascript" src="~/scripts/mypage2.js"></script>
<script type="text/javascript" src="~/scripts/mypage3.js"></script>
</asp:AdHocScriptBundle>
that, when bundling is enabled, automatically replaces the contents of asp:AdHocScriptBundle with a single script tag that resembles this:
<script type="text/javascript" src="/webappname/bundles/mypage_bundle.js?v=dh120398dh1298dh192d8hd32d"></script>
And when Bundling is disabled, outputs the contents normally like this:
<script type="text/javascript" src="/webappname/scripts/mypage1.js"></script>
<script type="text/javascript" src="/webappname/scripts/mypage2.js"></script>
<script type="text/javascript" src="/webappname/scripts/mypage3.js"></script>
Any thoughts?
About to roll my own anyway, but if there is already a solution for this please share, thanks!
I rolled my own solution and it works great! I created 4 classes that I can use as custom server controls:
ScriptBundle
Script
StyleBundle
Link
These call functions around my custom bundling library which is itself a wrapper for the System.Web.Optimization API.
During Render of ScriptBundle and StyleBundle I then check an internal setting (the same one that I use to set EnableOptimizations in the System.Web.Optimization API) that tells the page to either use bundling, or simply write out the normal script / link tags. If Bundling is enabled it calls this function from my custom bundling library (for Scripts, similar code for Styles tho. Bundler in code below is the class for my custom bundling library - just in case Microsoft changes the System.Web.Optimization API I wanted a layer in-between so that I wouldn't have to change my code as much):
public static void AddScriptBundle(string virtualTargetPath, params string[] virtualSourcePaths)
{
var scriptBundle = new System.Web.Optimization.ScriptBundle(virtualTargetPath);
scriptBundle.Include(virtualSourcePaths);
System.Web.Optimization.BundleTable.Bundles.Add(scriptBundle);
}
To make sure that I only create the Bundle if it does not already exist, I first check for the Bundle using this method (before using the above method):
public static bool BundleExists(string virtualTargetPath)
{
return System.Web.Optimization.BundleTable.Bundles.GetBundleFor(virtualTargetPath) != null;
}
Then I use this function to spit out the URL to the bundle by using System.Web.Optimization:
public static System.Web.IHtmlString GetScriptBundleHTML(string virtualTargetPath)
{
return System.Web.Optimization.Scripts.Render(virtualTargetPath);
}
Within my .aspx files, I do this:
<%# Register TagPrefix="cc1" Namespace="AdHocBundler" Assembly="AdHocBundler" %>
...
<cc1:ScriptBundle name="MyBundle" runat="Server">
<cc1:script src='~/js/script1.js'/>
<cc1:script src='~/js/utils/script2.js'/>
</cc1:ScriptBundle>
The trick for me was figuring out that I had to convert script and link tags to be work as list items within the ScriptBundle and StyleBundle controls, but after that it works great AND it let me use the tilde operator for easy references relative to app root (using Page.ResolveClientUrl(), which is helpful for creating module content).
Thanks go to this SO answer for helping me figure out how to create a custom collection control: How do you build an ASP.NET custom control with a collection property?
UPDATE: In the interest of full disclosure, I got permission to share the code for ScriptBundle (StyleBundle is almost identical, so did not include it):
[DefaultProperty("Name")]
[ParseChildren(true, DefaultProperty = "Scripts")]
public class ScriptBundle : Control
{
public ScriptBundle()
{
this.Enabled = true;
this.Scripts = new List<Script>();
}
[PersistenceMode(PersistenceMode.Attribute)]
public String Name { get; set; }
[PersistenceMode(PersistenceMode.Attribute)]
[DefaultValue(true)]
public Boolean Enabled { get; set; }
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public List<Script> Scripts { get; set; }
protected override void Render(HtmlTextWriter writer)
{
if (String.IsNullOrEmpty(this.Name))
{
// Name is used to generate the bundle; tell dev if he forgot it
throw new Exception("ScriptBundle Name is not defined.");
}
writer.BeginRender();
if (this.Enabled && Bundler.EnableOptimizations)
{
if (this.Scripts.Count > 0)
{
string bundleName = String.Format("~/bundles{0}/{1}.js",
HttpContext.Current.Request.FilePath,
this.Name).ToLower();
// create a bundle if not exists
if (!Bundler.BundleExists(bundleName))
{
string[] scriptPaths = new string[this.Scripts.Count];
int len = scriptPaths.Length;
for (int i = 0; i < len; i++)
{
if (!string.IsNullOrEmpty(this.Scripts[i].Src))
{
// no need for resolve client URL here - bundler already does it for us, so paths like "~/scripts" will already be expanded
scriptPaths[i] = this.Scripts[i].Src;
}
}
Bundler.AddScriptBundle(bundleName, scriptPaths);
}
// spit out a reference to bundle
writer.Write(Bundler.GetScriptBundleHTML(bundleName));
}
}
else
{
// do not use bundling. generate normal script tags for each Script
foreach (Script s in this.Scripts)
{
if (!string.IsNullOrEmpty(s.Src))
{
// render <script type='<type>' src='<src'>/> ... and resolve URL to expand tilde, which lets us use paths relative to app root
// calling writer.Write() directly since it has less overhead than using RenderBeginTag(), etc., assumption is no special/weird chars in the cc1:script attrs
writer.Write(String.Format(Script.TAG_FORMAT_DEFAULT,
s.Type,
Page.ResolveClientUrl(s.Src)));
}
}
}
writer.EndRender();
}
}
public class Script
{
public const String ATTR_TYPE_DEFAULT = "text/javascript";
public const String TAG_FORMAT_DEFAULT = "<script type=\"{0}\" src=\"{1}\"></script>";
public Script()
{
this.Type = ATTR_TYPE_DEFAULT;
this.Src = null;
}
public String Type { get; set; }
public String Src { get; set; }
public String Language { get; set; }
}
This isn't possible with the default Bundling/Minification in ASP.NET. The entire point of bundling is to create one single to file to reduce the number of browser requests to load static files such as .JS and .CSS files.
Rolling your own is your only option. However, please note that each <script> line will result in a browser request. Since most browsers can only handle 6 requests concurrently, you can have wait times just to load these static files.
And FYI, you don't have to recompile DLLs every time you update your .JS files with built-in bundling. You can simply reset the application pool the app is running on. If you're running with an external session persistence model, your users won't notice when this happens.
Your problem here is that you aren't really thinking this problem through. If you were, you would realize that what you are asking for can't work.
Why? Because the script tag ahs to generate an external link reference to a different url. So anything you place in the header of the current file will have no affect on your other URL that actually contains your bundles. As such, there is no way to dynamically change your bundles in the page itself because bundles have to, by definition, be defined in an external resource.
Now, there's nothing that says those bundles have to be compiled into DLL's in your own solution, but they cannot be embedded in the page that's currently being rendered.
You might want to investigate some of the javascript based minification tools out there, since they are typically not compiled.

How to make a popup display a message and then present a download after 5 seconds and then close the popup

In my project, I have a file that I want the user to download. When they click on the link, I want a popup window to display "Your download will shortly, if it doesn't start click here". After a few seconds, it will then close and the actual file download will then display.
I know to achieve the window closing you'll use:
window.setTimeout(function(){window.close()}, 5000);
But I'm not sure how you would call the download once the window has closed?
Cheers for any help!
In simple way, use window.open() to start download file.
Direct link
<script type="text/javascript">
setTimeout(function() {
window.open("myfile.doc");
},3000);
</script>
Okay so I don't know your platform so will give an ASP.NET version. If you are using something else then I have commented so you should be able to adapt to your platform.
EDIT: Now know user is using PHP so added code snippet for PHP (not robust but I'm no PHP dev)...
1) SAME FOR PHP/ASP Are you getting a file that will not be displayed by the browser automatically? i.e. .js will be shown as is but an exe will probably trigger a file download dialog (someone correct me if wrong please and I'll update)
If your files are always going to be i.e. .exe then you could probably just get away with:
$("body").append("<iframe src='http://www.targetsite.com/files/thefilename.exe'></iframe>");
but more likely you will be using a parameter to find the right file (and hide direct download
$("body").append("<iframe src='http://www.targetsite.com/downloader/?file=1234-1234-1234'></iframe>");
in some setTimeout function.
If the filetypes are unknown then I suggest pointing the above code at a script file (.ashx, php, etc) that writes the file byte stream to the http response.
FOR PHP:
<?php // Demo - send a (binary) file
$file = "ireland.jpg";//here you would use the query string parameter of the above
//ajax/iframe request eg file=1234-1234-1234 to find image in db
$fp = fopen($file,"r") ;
header("Content-Type: image/jpeg");//this would need to be modified to either show right content type or you could
//set it to Application/force-download
while (! feof($fp)) {
$buff = fread($fp,4096);
print $buff;
}
?>
WARNING Be careful with the above code. It occurred to me you might pass in filename directly which I'm pretty sure someone could use to get files in other places in your app without careful attention
FOR ASP:
I have included an example ashx (generic handler) solution:
aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Wardle.PdfGenerator;
using System.IO;
public partial class user_account_iframedownloader : System.Web.UI.Page
{
private IInformixRepository _rep;
//this page gets loaded into an iframe so we can do downloads while using ajax
protected void Page_Load(object sender, EventArgs e)
{
//write bytes out here i.e. see after for methods
}
}
Example byte output methods (you just need to do File.getBytes or something - my code is quite complicated so 'excercise for reader'
public static void PdfOutput(byte[] pdfData, string filename)
{
HttpContext.Current.Response.ContentType = "Application/pdf";
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filename);
HttpContext.Current.Response.BinaryWrite(pdfData);
}
public static void PdfZipOutput(byte[] zipData, string filename)
{
HttpContext.Current.Response.ContentType = "Application/zip";
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filename);
HttpContext.Current.Response.BinaryWrite(zipData);
}

Best/efficient way to include JavaScript when using User Controls

I'm to .NET and all the associated cool stuff you can do, but am wondering about efficiency with User Controls and JS includes.
My user controls are mainly made up with an ascx display page and .vb.ascx code behind, as is customary with the code-behind coding style of .NET, which is great for coding simplicity although it does double then bear of files required. However, as I understand it, the server compiles these and returns the HTML efficiently.
Where the Control requires JavaScript, as I'm developing, I am making external JS files for each User Control with the same name, so the user controls consist of 'controlName.ascx, controlName.vb.ascx, controlName.js'
If a page requested by the user contains several User Controls the browser will be requesting multiple JS files, probably a master page JS file, jQuery AND each required file for the respective Controls.
This approach makes sense to me whilst developing and as everything's all kept nice and neat, making problem solving easy, but when it goes live there'll be loads of get requests from the browser, given that each time the browser gets a file, even the process of requesting the file to check if its cached or not must take some time.
Would I be best off including my JS inline in the ascx files, or code behind, directly inserting the script, or what is the 'correct' way to handle these multiple files to reduce get requests from the browser.
I'm using CSS sprites for buttons and stuff for the same reason, so wondering what to do with JS files. In my case CSS is generally handled by classes in the primary pages, so these are not an issue.
We actually built a control on top of ScriptManager that automatically extracts all of the js from all controls on a page, including scriptresource.axd and stores them in a single cached file. This has greatly improved performance and reduced maintenance work since it is automated. We built this starting in .Net 2.0, so I am not sure if ScriptManager now provides the same functionality, but I thought it was worth mentioning.
Here is our implementation of this class:
Option Explicit On
Option Strict On
Imports System.Collections.Generic
Imports System.Web.SessionState
Public Class OurScriptManager
Inherits ScriptManager
Implements IRequiresSessionState
Private m_ScriptBuilder As New StringBuilder
'Private m_sSessionIndex As String = ""
Private m_cScripts As List(Of ScriptReference)
Private m_fIsCached As Boolean
Private m_sScriptName As String = ""
Private m_sScriptFileName As String = ""
Const CACHED_SCRIPTS_DIR As String = "/scriptcache/"
Public Sub New()
' default constructor
End Sub
Public Property ScriptName() As String
Get
Return m_sScriptName
End Get
Set(ByVal value As String)
m_sScriptName = value
End Set
End Property
Private ReadOnly Property ScriptFileName() As String
Get
If String.IsNullOrEmpty(m_sScriptFileName) Then
m_sScriptFileName = "~" & CACHED_SCRIPTS_DIR & Me.ScriptName & ".js"
End If
Return m_sScriptFileName
End Get
End Property
Protected Overrides Sub OnInit(ByVal e As System.EventArgs)
' Exceptions are handled by the caller
MyBase.OnInit(e)
If String.IsNullOrEmpty(Me.ScriptName) Then
Me.ScriptName = Me.Page.ToString
End If
' this compiled script should be cached on the server
' check for the file, if it exists, load that file instead of generating it
If Configuration.HasPageScriptBeenCached(Me.ScriptFileName) AndAlso File.Exists(Me.Page.Server.MapPath(Me.ScriptFileName)) Then
m_fIsCached = True
Else
m_cScripts = New List(Of ScriptReference)
End If
End Sub
Protected Overrides Sub OnResolveScriptReference(ByVal e As System.Web.UI.ScriptReferenceEventArgs)
Try
MyBase.OnResolveScriptReference(e)
If Not m_fIsCached Then
' First, check to make sure this script should be loaded
Dim fIsFound As Boolean
For Each oXref As ScriptReference In m_cScripts
If oXref.Assembly = e.Script.Assembly AndAlso oXref.Name = e.Script.Name AndAlso oXref.Path = e.Script.Path Then
fIsFound = True
Exit For
End If
Next
' If this script is found within the list of scripts that this page uses, add the script to the scripthandler.aspx js output
If Not fIsFound Then
Dim oReference As ScriptReference
Dim oElement As ScriptReference
Dim fIsPathBased As Boolean
oElement = e.Script
If String.IsNullOrEmpty(oElement.Path) AndAlso Not String.IsNullOrEmpty(oElement.Name) AndAlso String.IsNullOrEmpty(oElement.Assembly) Then
' If resource belongs to System.Web.Extensions.dll, it does not
' provide assembly info that's why hard-coded assembly name is
' written to get it in profiler
oElement.Assembly = "System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
End If
'check to see what type of script this is
If Not String.IsNullOrEmpty(oElement.Path) Then
' this script is a physical file
oReference = New ScriptReference(oElement.Path)
fIsPathBased = True
ElseIf Not String.IsNullOrEmpty(oElement.Assembly) AndAlso Not String.IsNullOrEmpty(oElement.Name) Then
' this script is generated by an assembly
oReference = New ScriptReference(oElement.Name, oElement.Assembly)
Else
' Couldn't find script, so bail to allow standard processing to take place.
Return
End If
If Not fIsPathBased Then
Dim sUrl As String
Dim oRequest As HttpRequest
Dim sScriptResourcePath As String
sUrl = GetUrl(oReference)
sScriptResourcePath = String.Format("{0}{1}{2}{3}{4}{5}{6}{7}", Context.Request.Url.Scheme, "://", Context.Request.Url.Host, ":", Context.Request.Url.Port, "/", Context.Request.ApplicationPath, "/ScriptResource.axd")
oRequest = New HttpRequest("scriptresource.axd", sScriptResourcePath, sUrl.Substring(sUrl.IndexOf("?"c) + 1))
Try
Using oWriter As New StringWriter(m_ScriptBuilder)
Dim oHandler As IHttpHandler = New System.Web.Handlers.ScriptResourceHandler
oHandler.ProcessRequest(New HttpContext(oRequest, New HttpResponse(oWriter)))
End Using
Catch theException As Exception
Call ReportError(theException)
' Since we couldn't automatically process this, just bail so that standard processing takes over
Return
End Try
Else
' If this script is from a file, open the file and load the
' contents of the file into the compiled js variable
Dim sAbsolutePath As String
sAbsolutePath = Context.Server.MapPath(oElement.Path)
Try
If System.IO.File.Exists(sAbsolutePath) Then
Using oReader As New StreamReader(sAbsolutePath, True)
m_ScriptBuilder.Append(oReader.ReadToEnd())
End Using
Else
Throw New Exception("File " & sAbsolutePath & " does not exist")
End If
Catch theException As Exception
Call ReportError(theException, New ExtraErrorInformation("File", sAbsolutePath))
' Since we couldn't automatically process this, just bail so that standard processing takes over
Return
End Try
End If
m_ScriptBuilder.AppendLine()
' add this script to the script reference library
Dim oNewElement As New ScriptReference
oNewElement.Name = e.Script.Name.ToString()
oNewElement.Assembly = e.Script.Assembly.ToString()
oNewElement.Path = e.Script.Path.ToString()
m_cScripts.Add(oNewElement)
End If
End If
' a script filename is provided for caching
e.Script.Assembly = String.Empty
e.Script.Name = String.Empty
e.Script.Path = Me.ScriptFileName
Catch theException As Exception
HttpContext.Current.Response.Write(ReportError(theException))
HttpContext.Current.Response.End()
End Try
End Sub
Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
' Exceptions are handled by the caller
MyBase.Render(writer)
If Not m_fIsCached Then
If Not String.IsNullOrEmpty(Me.ScriptName) Then
' Save script to file for caching
Using fsFile As New FileStream(Me.Page.Server.MapPath(Me.ScriptFileName), FileMode.Create, FileAccess.Write, FileShare.Read)
Using oWriter As New StreamWriter(fsFile)
oWriter.Write(m_ScriptBuilder.ToString)
oWriter.Flush()
oWriter.Close()
End Using
fsFile.Close()
End Using
' Record that the script file has been cached
Configuration.RecordPageScriptCached(Me.ScriptFileName)
End If
m_ScriptBuilder = Nothing
End If
End Sub
Private Function GetUrl(ByVal oReference As ScriptReference) As String
' Exceptions are handled by the caller
If String.IsNullOrEmpty(oReference.Path) Then
Try
Dim oMethod As MethodInfo
oMethod = oReference.GetType.GetMethod("GetUrl", BindingFlags.NonPublic Or BindingFlags.Instance)
If oMethod IsNot Nothing Then
Return DirectCast(oMethod.Invoke(oReference, New Object() {Me, False}), String)
Else
Return String.Empty
End If
Catch ex As Exception
Return String.Empty
End Try
Else
Return Me.ResolveClientUrl(oReference.Path)
End If
End Function
End Class
In the above code, ReportError logs the exception to the event log and/or file; you can replace this with your own mechanism.
Here is the Configuration code:
Private Shared m_cCachedPageScripts As Collections.Generic.List(Of String)
''' <summary>
''' This method is used to determine whether or not the script for the page has been cached.
''' This is used for script combining.
''' </summary>
''' <param name="sKey"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function HasPageScriptBeenCached(ByVal sKey As String) As Boolean
' Exceptions are handled by the caller
SyncLock CacheSyncObject
If m_cCachedPageScripts IsNot Nothing AndAlso m_cCachedPageScripts.Contains(sKey) Then
Return True
End If
End SyncLock
End Function
''' <summary>
''' This method is used to record the fact that the page script has been cached.
''' This is used for script combining.
''' </summary>
''' <param name="sKey"></param>
''' <remarks></remarks>
Public Shared Sub RecordPageScriptCached(ByVal sKey As String)
' Exceptions are handled by the caller
SyncLock CacheSyncObject
If m_cCachedPageScripts Is Nothing Then
m_cCachedPageScripts.Add(sKey)
End If
m_cCachedPageScripts.Add(sKey)
End SyncLock
End Sub

Tool / accelerator for improving adding css and script resources to MVC3 Razor pages

Is there any tool to make adding files to MVC3 razor pages faster?
I find myself having to drag script files onto the page to generate:
<script src="../../Scripts/rails.js" type="text/javascript"></script>
Which then i'll copy and paste
<script src="#Url.Content("~/Scripts/")" type="text/javascript"></script>
Then cut/drag the rails.js fragment into the new script statement. Then at some point after this hopefully I remember I need to clean up a whole bunch of duplicated and/or broken script links.
There has to be a better way than this that doesn't involve typing urls out manually.
Came across this blog post earlier today and ASP.NET MVC Best Practices (Part 1) and it's guidance shows
public static string Image(this UrlHelper helper, string fileName)
{
return helper.Content("~/assets/images/{0}".FormatWith(fileName));
}
public static string Stylesheet(this UrlHelper helper, string fileName)
{
return helper.Content("~/assets/stylesheets/{0}".FormatWith(fileName));
}
public static string NoIcon(this UrlHelper helper)
{
return Image(helper, "noIcon.png");
}
This seems like the optimal solution if you use a good layout scheme for your resources.
Edit: FWIW
public static string FormatWith(this string format, params object[] inputs)
{
return string.Format(format, inputs)
}
You can use T4MVC to get a compile-time validation your your links.
2.3. Strongly typed links to script files and static resources
T4MVC generates static helpers for
your content files and script files.
So instead of writing:
<img src="/Content/nerd.jpg" />
You
can write:
<img src="<%= Links.Content.nerd_jpg %>" />
Likewise, instead of
<script src="/Scripts/Map.js" type="text/javascript"></script>
You
can write
<script src="<%= Links.Scripts.Map_js %>" type="text/javascript"></script>
The obvious benefit is that you’ll get
a compile error if you ever move or
rename your static resource, so you’ll
catch it earlier.
Another benefit is that you get a more
versatile reference. When you write
src="/Content/nerd.jpg", your app will
only work when it’s deployed at the
root of the site. But when you use the
helper, it executes some server side
logic that makes sure your reference
is correct wherever your site is
rooted. It does this by calling
VirtualPathUtility.ToAbsolute("~/Content/nerd.jpg").

Categories