In a project using JSF, we have JavaScript files located here:
MyProject/view/javascript/*.js
In the xhtml file, I include these resources as
<script type="text/javascript" src="javascript/#{myBean.jsFileName}.js" />
This works fine, but it's possible that #{myBean.jsFileName}.js doesn't exist, in which case I want to load "Default.js".
In myBean (or in the xhtml file itself), how can I first check for the existence of a js file before setting the #{myBean.jsFileName} value? I've tried variations of this:
File f = new File("javascript/myFile.js");
if (!f.exists){ jsFileName="Default"};
But I don't think that's right. Am I on the right track?
You can use ExternalContext#getResource() to obtain a web resource as URL. It will return null if resource doesn't exist. It takes a path relative to the web root and should preferably always start with /.
So. this should do in bean:
String fullPath = "/view/javascript/myFile.js";
URL resource = FacesContext.getCurrentInstance().getExternalContext().getResource(fullPath);
if (resource == null) {
fullPath = "/view/javascript/default.js";
}
Or, equivalently, this in the view, which is IMO somewhat hacky:
<c:set var="available" value="#{not empty facesContext.externalContext.getResource(bean.fullPath)}" />
<script src="#{available ? bean.fullPath : bean.fullDefaultPath}" />
See also:
getResourceAsStream() vs FileInputStream
Unrelated to the concrete problem, if you were using JSF 2.x (at least Facelets, as implied by being able to use unified EL in template text in your code snippet), you should be using resource libraries with <h:outputScript>. See also How to reference CSS / JS / image resource in Facelets template?
Related
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.
I'd like to inject a couple of local .js files into a webpage. I just mean client side, as in within my browser, I don't need anybody else accessing the page to be able to see it. I just need to take a .js file, and then make it so it's as if that file had been included in the page's html via a <script> tag all along.
It's okay if it takes a second after the page has loaded for the stuff in the local files to be available.
It's okay if I have to be at the computer to do this "by hand" with a console or something.
I've been trying to do this for two days, I've tried Greasemonkey, I've tried manually loading files using a JavaScript console. It amazes me that there isn't (apparently) an established way to do this, it seems like such a simple thing to want to do. I guess simple isn't the same thing as common, though.
If it helps, the reason why I want to do this is to run a chatbot on a JS-based chat client. Some of the bot's code is mixed into the pre-existing chat code -- for that, I have Fiddler intercepting requests to .../chat.js and replacing it with a local file. But I have two .js files which are "independant" of anything on the page itself. There aren't any .js files requested by the page that I can substitute them for, so I can't use Fiddler.
Since your already using a fiddler script, you can do something like this in the OnBeforeResponse(oSession: Session) function
if ( oSession.oResponse.headers.ExistsAndContains("Content-Type", "html") &&
oSession.hostname.Contains("MY.TargetSite.com") ) {
oSession.oResponse.headers.Add("DEBUG1_WE_EDITED_THIS", "HERE");
// Remove any compression or chunking
oSession.utilDecodeResponse();
var oBody = System.Text.Encoding.UTF8.GetString(oSession.responseBodyBytes);
// Find the end of the HEAD script, so you can inject script block there.
var oRegEx = oRegEx = /(<\/head>)/gi
// replace the head-close tag with new-script + head-close
oBody = oBody.replace(oRegEx, "<script type='text/javascript'>console.log('We injected it');</script></head>");
// Set the response body to the changed body string
oSession.utilSetResponseBody(oBody);
}
Working example for www.html5rocks.com :
if ( oSession.oResponse.headers.ExistsAndContains("Content-Type", "html") &&
oSession.hostname.Contains("html5rocks") ) { //goto html5rocks.com
oSession.oResponse.headers.Add("DEBUG1_WE_EDITED_THIS", "HERE");
oSession.utilDecodeResponse();
var oBody = System.Text.Encoding.UTF8.GetString(oSession.responseBodyBytes);
var oRegEx = oRegEx = /(<\/head>)/gi
oBody = oBody.replace(oRegEx, "<script type='text/javascript'>alert('We injected it')</script></head>");
oSession.utilSetResponseBody(oBody);
}
Note, you have to turn streaming off in fiddler : http://www.fiddler2.com/fiddler/help/streaming.asp and I assume you would need to decode HTTPS : http://www.fiddler2.com/fiddler/help/httpsdecryption.asp
I have been using fiddler script less and less, in favor of fiddler .Net Extensions - http://fiddler2.com/fiddler/dev/IFiddlerExtension.asp
If you are using Chrome then check out dotjs.
It will do exactly what you want!
How about just using jquery's jQuery.getScript() method?
http://api.jquery.com/jQuery.getScript/
save the normal html pages to the file system, add the js files manually by hand, and then use fiddler to intercept those calls so you get your version of the html file
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").
How do I get the absolute or site-relative path for an included javascript file.
I know this can be done in PHP, (__file__, I think). Even for an included page, one can check the path (to the included file). Is there any way to have this self awareness in Javascript?
I know I can can get the page URL, but need to get the JS URL.
Eg. Javascript needs to modify the src of an image on the page. I know where the image is relative to the JavaScript file. I don't know where the Javascript is relative to the page.
<body>
<img id="img0" src="">
<script src="js/imgMaker/myscript.js"></script>
</body>
function fixPath(){
$$("#img0")[0].set('src','js/imgMaker/images/main.jpg');
}
Please do not tell me to restructure my function - the example is simplified to explain the need.
In the actual case, a Mootools class is being distributed and people can put it into whatever folder they want.
I would just read the src of the script element, but the class can be part of any number of javascript files, so I can't know what the element looks like.
JavaScript (not JScript) has no concept of file names. It was developed for Netscape back in the days. Therefore there is no __file__ feature or anything similar.
The closest you can come are these two possibilities:
What you already mentioned: Harvest all src attributes of all JS files and try to figure out which one is the right.
Make it a necessary option, that the path to the images must be set in the embedding HTML file. If not set, use a reasonable and well-documented default:
<script type="text/javascript">
var options = {
'path_to_images': '/static/images/' // defaults to '/js/img/'
};
</script>
Based on http://ejohn.org/blog/file-in-javascript/
(function(){
this.__defineGetter__("__FILE__", function() {
return (new Error).stack.split("\n")[2].split("#")[1].split(":").slice(0,-1).join(":");
});
})();
(function(){
this.__defineGetter__("__DIR__", function() {
return __FILE__.substring(0, __FILE__.lastIndexOf('/'));
});
})();
Then later
img.setAttribute('src', __DIR__ + '/' + file);
if you have folders:
/webroot
/images
/scripts
Then images would be an absolute path of /images/whatever.jpg and scripts would be an absolute path of /scripts/js.js
I'm using the following method to get the base URL and using it for loading the other prorotypes, maybe this is what you need. Lets say current script name is 'clone.js'.
/*
* get the base URL using current script
*/
var baseURL = '';
var myName = 'clone.js';
var myPattern = /(^|[\/\\])clone\.js(\?|$)/;
var scripts = document.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
var src;
if (src = scripts[i].getAttribute("src")) {
if (src.match(myPattern)) {
baseURL = src.replace(myName, '');
break;
}
}
}
Var baseURL should contain what you need.
The path to the JS is irrelevant; links in the HTML file are always relative to the HTML file, even if you modify them from external JS.
[EDIT] If you need to build a path relative to the current web page, you can find its path in document.location.pathname. This path is relative to the web root but you should be able to find a known subpath and then work from there.
For example, for this page, it pathname would be /posts/1858724. You can look for posts and then build a relative path from there (for example posts/../images/smiley.png)
I know this question was asked awhile back but I have a similar situation to Sam's.
In my case, I have two reasons for the situation:
The user can access different sub-domains, each with its own index page.
The user can enter a password that causes index.php to adjust the paths.
Most of the references point to the same src locations for the scripts, but some do not. For instance, those at a different level of the tree would require a different path.
I addressed it by assigning an id to the index page's script tag. For example, the head might include...
<script id='scriptLocation' type='text/javascript' language='javascript' src='../scripts.test/script.js'></script>
My JavaScript is then able to read the path...
var myPath = document.getElementById("scriptLocation").src;
Found another approach, perhaps someone with more JS ninja can flush this out.
CSS stylesheet are able to find the node that called them using document.stylesheets.ownernode.
I could not find a similar call for javascript files.
But, in some cases, if one can include a CSS file together with the javascript, and give the first rule some unique identifier.
One can loop through all stylesheets till they find the one with the identifier [if(document.stylsheets[i].cssRules[0] == thisIs:myCSS)], than use ownerNode to get the path of that file, and assume the same for the JS.
Convoluted and not very useful, but its another approach - might trigger a better idea by someone.
I have a Master Page in the root of my project. I have Content Pages throughout my project and in subfolders referencing this Master Page. What is the correct way to reference my .CSS and .JS files if I always want them to be relative to the root?
Here is how I'm doing it now:
link href="/common/css/global.css"
script src="/common/javascript/global.js"
But that breaks the link. I tried without the leading "/" but that didn't work on my pages in the subfolders.
I would use something like
Server.ResolveClientUrl("~/common/css/global.css")
This will get a proper url for you at all times.
Example:
Per the comment this would be full usage.
<link type="text/css" rel="stylesheet"
href='<%= Server.ResolveClientUrl("~/common/css/global.css") %>' />
According to comments, other validated usage, no "error CS1061: 'System.Web.HttpServerUtility' does not contain a definition" error:
<script type="text/javascript"
src="<%= Page.ResolveUrl("~/Scripts/YourScript.js") %>" ></script>
Also is important to always put the closing tag .
You can make the <link> tag to run at server so Asp.Net will resolve the URL for you like this:
<link href="~/common/css/global.css" runat="server" />
(Notice the '~')
I don't know if it can be applied to the <script> tag though, you should try...
EDIT: I discovered recently on a project that you can (and should) use a ScriptManager to hold your scripts (you can only have 1 per page). You can put one in your MasterPage and reference all your scripts. Inside your content page, you then add a ScriptManagerProxy that will 'reference' the scripts on the master page and you can even add other scripts for that content page only.
I do it as simple as this: link href="<%=ResolveUrl("~/common/css/global.css")%>"
The solutions I saw so far didn't work in my project (especially not for .css links). The issues were the following:
inside <link> it didn't resolve the <%=...%> expression
it did not find the Page.ResolveURL in all cases
there was some trouble with ' and " quotes if you embedd <%=...%>
So I'd like to offer this solution: In code behind (your master page's C# class), add the the following 3 methods:
public partial class SiteBasic : System.Web.UI.MasterPage
{
public string ResolveURL(string url)
{
var resolvedURL=this.Page.ResolveClientUrl(url);
return resolvedURL;
}
public string cssLink(string cssURL)
{
return string.Format("<link href='{0}' rel='stylesheet' type='text/css'/>",
ResolveURL(cssURL));
}
public string jsLink(string jsURL)
{
return string.Format("<script src='{0}' type='text/javascript'></script>",
ResolveURL(jsURL));
}
}
For stylsheet references, you can then say:
<%=cssLink("~/css/custom-theme/jquery-ui-1.8.20.custom.css")%>
For JavaScript references, it looks like so:
<%=jsLink("~/Scripts/jquery-1.7.2.js")%>
And for other references, you can use:
<a href='<%=ResolveURL("~/Default.htm")%>'>link</a>
<img title='image' src='<%=ResolveURL("~/Images/logo.png")%>'/>
Note: I found it is better to use single quotes outside and double quotes inside the href or src attribute as shown in the example above. Doing it vice versa caused trouble in some cases as I found.
This solution is simple and it worked well in my case, even if the pages referencing the master page reside in different subdirectories. What it basically does is translating the ~ path (which needs to be absolute from the root of your web site) into a relative path (using as many ../ in the path as needed) based on the page you're currently displaying.
Advanced hint:
If you're using AJAX calls to invoke web service methods, then you'll have the same issue referencing them if you have ASPX pages on different directory levels. I recommend you define something like (assuming that your web services reside in the directory ~/AJAX):
<script type="text/javascript">
function getWebServicePath() { return '<%=ResolveURL("~/AJAX/")%>'; }
</script>
inside the <head> ... </head> section of your master page. This will make the entry path of the web service available in your JavaScript. You can use it like
$.ajax({
type: "POST",
url: getWebServicePath()+"myWebService.asmx/myMethod",
data: $.toJSON({ param: "" }),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
// ... code on success ...
},
error: function (ex) {
// ... code on error ...
}
});