Hugoware

The product of a web developer with a little too much caffeine

Archive for January 2010

WebControls In MVC… again…

with 11 comments

I’m a big fan of MVC so far but there are certainly parts that I don’t like. For example, who is responsible for logical changes to a HTML markup in a view? The Controller? The Model? Personally, I don’t think either of them should which leaves the View to pick up the slack.

But the problem is that I end up with a bunch of crud in my View — which to me looks a lot like a Classic ASP website.

It doesn’t take a lot — Just look at a fairly reasonable example…

<div class="<% if (user.IsAdmin) { %>admin-icon<% } else if (user.IsEditor) { %>editor-icon<% } else { %>user-icon<% } %>" >
<!--snip...-->

I realize that you always have access to RenderPartial and you can also start using a templating engine but I still feel this is something that a WebControl could handle perfectly.

Using A WebControl For Markup

Now if your first thought was “You can’t do anything to WebControls in the Render stage of the page lifecycle” then you’re absolutely right. However, it is possible to gain access to earlier than that and use it with models.

This next bit of code is pretty daunting… I’d recommend you send any children out of the room and shield your eyes…

<!-- Old Sample...
<mvc:MyControl SomeProperty='<% #DataBinder.GetPropertyValue(this.Model, "TheProperty") %>' runat="server" />-->

<!-- As it turns out, you can simply call it like... -->
<mvc:MyControl SomeProperty="<% #this.Model.TheProperty %>" runat="server" />

And that is about it! Really!

The second step requires that you call the DataBind function for the page… but you can’t call it as part of the render (otherwise it is too late). You could use a server side script tag at the top of your page, but I recommend you just create your own ViewPage class that takes care of it for you. Below is the class that use in later examples. Again, there is an insane amount of code so be warned…

/// <summary>
/// ViewPage with Model access for WebControls
/// </summary>
public class MyControl : UserControl {

    //some point the the control lifetime call DataBind
    public override void OnLoad(object sender, EventArgs e) {
        this.DataBind();
    }

}

How About A Real Example Then?

So this sounds interesting, but how could it really be used? I’ve include a Visual Studio solution file with some examples that go over possible ways to use WebControls in your MVC applications.

  • Twitter Feed: Writes all of the logic and rendering of a simple Twitter feed. Uses the BoundedViewPage to attach a user name to the Property of the control. Also allows normal access (assignment from a string) so you could reuse the code anywhere.
  • Calendar: Example that uses a Calendar control and a Grid View to create a a simple page of content but uses binding to the Model to get information for all of the controls.
  • Task List: Simple task list that shows how you can use a UserControl with button click events to update the Model and then show the results on the page.
  • Crash Example: Using RenderPartial on a page that has a server form does not work correctly. However, if your WebControl doesn’t use a server form (which is better anyways) then you can use all MVC features without a problem (as far as I know, that is)

Of course, I’m sure a lot of people will be against this idea but I think it is worth exploring. If you think about all of the new features that ASP.NET 4 is going to introduce (especially better control over the client side markup) then this might end up being a perfect combination!

Source Code

WebFormAccess.zip (Solution Files)

Previous Posts On WebControls In MVC

Here are some earlier posts messing around with this same concept — Admittedly, these are geared to using WebControls and the entire WebForms model.

Advertisements

Written by hugoware

January 31, 2010 at 10:54 pm

Yet Another jsshell Update

with 4 comments

I’m pretty psyched about jsshell right now. The user count is growing, I’ve had a lot of good reviews — I’ve even noticed that my blog is being hit with search for it.

I’ve also been getting feedback of ideas and suggestions which I’ve implemented and uploaded to the extension website (so you can go download the new version now).

More Intellisense

The intellisense now includes jQuery commands, jLinq commands and jsshell commands, each labeled with their own color. Additionally, there is now a slight delay before the intellisense is polled. This time limit is 100 milliseconds (so really if you aren’t typing a fast or even pause for a moment you should see it).

Automatic jQuery Help

I received this interesting suggestion from a user. Automatically open the jQuery website with the correct help information for the word that the caret is currently over. Brilliant!

Now, when you press CTRL+? or CTRL+F1, whatever word you have the caret will be opened on the jQuery website. If you don’t have the whole thing typed out, it will take a guess at what you meant. If it can’t even find a command by that name in jQuery then no window is opened.

Import and Export Settings

I’m not entirely certain what happens from version to version for Chrome extensions, I’ve added an export and import option for settings. Right now it just returns a string that you can import back into jsshell. In the future I’ll be adding an interface to do that for you. For now you might want to save any built in commands before you upgrade versions.

If you have any other feedback, please let me know!

Written by hugoware

January 27, 2010 at 11:03 pm

Simple External jQuery Templates (Update)

with 5 comments

In my previous post I made some code available that would allow you to access external templates all in the same jQuery chain. Unfortunately, I discovered a few bone-head mistakes in the code the next day while I was using it.

The new code has been uploaded and the bugs (the ones I found) have been fixed. If you have anymore feedback, please let me know.

Written by hugoware

January 26, 2010 at 11:09 pm

Simple External jQuery Templates – Part 2

with 4 comments

In my last blog post I wrote about an interesting way to work with server side templates and avoid using callbacks to work with the elements you created. I worked on the concept some more and added some simple templating capabilities along with the ability to apply inline or external data. I’m posting this plugin to the jQuery website for downloading (along with parts of this blog post)

If you remember in the last post, the main problem was having to define callback methods to handle making changes to your jQuery object after it had been created. Instead, we used method forwarding and method queueing to allow direct access to the final object even before it had been created.

So instead of using callbacks your jQuery would look something like…

//loads an external resource but returns a placeholder to
//capture additional jQuery commands
$.template("/resource.html")
    .css({ color: "#f00" })
    .animate({ height: 400 })
    .click(function() { alert('clicked'); });

Interesting concept but requires some more work…

Important: This plugin simply helps avoid using callbacks to keep track of external templates.

What Is New?

Activation and Replacement Commands
The previous method worked pretty well if you’re adding to a page but if you needed to replace something then you were back to using callbacks to handle removing content from the page. This version adds a couple extra new functions to work with the document after the template has finished loading

  • fill(selector): Empties the target and then fills the container with the current jQuery object. (accepts a string or a jQuery object as a selector)
  • replace(selector): Removes the target and then puts the current jQuery object in its place. (accepts a string or a jQuery object as a selector)
  • ready(delegate(element) { ... }): Simple way to perform any action that you want immediately following the external resources being loaded and processed.

All of these commands can be used along with the rest of the existing jQuery commands (like styles, animations, events, etc) because they follow the same queueing rules that the rest of the methods use. So for example, you could write the following command…

$.template("/resource.html")
    .ready(function() { $(document.body).css({ background: "#00f" }); })
    .fill("#placeholder")
    .fadeIn();

Templating Engine

Another thing that was missing from before was a quick, automated way to populate the template after it was loaded. This version introduces a simple engine that allows you to pass in an object or a URL to the source to JSON data you want to use and then populate the template automatically.

Here is a simple example of using a template with a data source…

[template.html]

<div class="customer-list">
	<a href="javascript:void(0);" class="refresh" >Refresh</a>
	<h2>%%title%%</h2>
	<p>%%description%%</p>
	<ul>
	    <li each="%%customers%%" class="%%odd:'alt-row'%%" >
	        <h4 class="%%first:'top-customer'%%" >%%name%%</h4>
	        <ul>
	            <li each="contacts" class="%%first:'preferred'%% %%odd:'alt'%% contact-%%type%%">%%value%%</li>
	        </ul>
	        <hr if="%%!last%%" />
	    </li>
	</ul>
</div>

[data.json]

{"title":"Customer List",
"description":"Customers and their contact methods.",
"customers":[
    { "name":"Mark", "contacts":[
        { "type":"email", "value":"mark@example.com" },
        { "type":"email", "value":"supermark@example.com" },
        { "type":"phone", "value":"555-1254" }
    ]},
    { "name":"Susan", "contacts":[
        { "type":"email", "value":"susan@example.com" },
        { "type":"phone", "value":"555-9811" }
    ]},
    { "name":"Jamie", "contacts":[
        { "type":"email", "value":"jamie@example.com" },
        { "type":"phone", "value":"555-4322" },
        { "type":"phone", "value":"555-1077" }
    ]}
]}

[script.js]

var list = $.template({
	template: "template.html",
	externalData: "data.json"
})
.ready(function() {
	$(document.body).css({ background: "#ffc" })
})
.appendTo("#target")
.find(".preferred")
	.css({ color: "#f00" })
	.end()
.find("h4")
	.css({ color: "#00f" });

The final rendered result would look like the following sample…

I suspect that a templating engine written in the duration it takes me to finish a Starbucks coffee isn’t going to be high enough quality for everyone so you can easily replace it with a custom one by using the following command.

$.template(function(params) {
    /* perform formatting in this block (or send it to another templating engine)
     * params.html: The text provided by the template from the server
     * params.data: The JSON data provided for rendering (if any)
     */

    //must return a jQuery object
    return $(params.html);
});

This will replace the template formatting method for the duration of the page, however you can replace the formatting on a per request basis (but I’ll explain that later)

How To Use

There are a few ways you can use the template command.

$.template(function(params) { ... });
Replaces the template formatting method with a custom method. The argument passed in contains a html property that contains the string of HTML to use for the template. The argument also contains a data property that contains the JSON data to populate the template with (if any). This should return a jQuery object when finished.

$.template("/path/to/template.html")
Simply downloads the HTML from an external path and then returns the jQuery object after it has finished loading. This option does not support templating.

$.template({ options })
Allows a lot more control over the request. The argument accepts the following properties.

  • template: The URL that points to the template file. You can optionally pass in an object that contains the same parameters as the $.ajax command so you can have full control over the request.
  • externalData [optional]: The URL that points to the JSON content for the template. You can optionally pass in an object that contains the same parameters as the $.ajax command so you can have full control over the request.
  • data [optional]: A JSON object with data to populate the template with.
  • complain [optional]: Determines if an exception should be thrown if the template and the data do not cooperate with each other (such as missing information).
  • error [optional]: A delegate that handles any connection errors. The first argument will tell where the error took place (external data or the template).
  • format [optional]: A delegate that performs formatting for a template. This won’t replace the default formatting. Requires the same delegate type as the example for replacing the default version.

Source Code

For now I’m simply uploading the code for downloading but I’m going to post it to the jQuery website shortly. I’d like to get some feedback and check for bugs before I save anything to the site.

Also — If anyone can come up with a clever, buzz-wordy sort of name of the plugin, please clue me in! I’m terrible at naming applications or plugins.

Download

Download template.js (full source code)

.

Written by hugoware

January 24, 2010 at 10:38 pm

Simple External Templates With jQuery

with 4 comments

New code available: I’ve written a follow up post about this topic with new code and new features.

If you’ve ever tried building a jQuery object from scratch then you’ve probably found that it is a little time consuming and probably a little ugly by the time you’re finished. You could put additional HTML templates as hidden elements on the page but maybe you’d rather keep that extra mark up out of your page.

One option would be load a template from a website address and then make your changes once it arrives. The code would probably look something like this.

//create the container
var element = null;

//make the request for the HTML
$.ajax({
    url:"template.html",
    success:function(html) {
        element = $(html);
        //make changes here
    }
});

There isn’t anything wrong with this code but it does put a bit of separation of your declaration of an element and the actual usage of the element. The problem is that you can’t do the whole process at once since the AJAX call takes a little time and you have to rely on a call back before you have access to the object.

It would be nice if we could have immediate access to our jQuery object on an AJAX call, but not block anything else on the page.

Time For Some Javascript Magic ™

I’m not really sure what you would call the example below, but the general idea is to capture (and queue up) any actions that would be invoked on our jQuery object and then release them once the AJAX call has been finished.

Let’s look at some code and see what it would look like (extra comments to try and explain the process)

/*
 * Arguments
 * $.template(url) - Pass in a string to a url to load 
 * $.template(params) - An object with parameters found below
 *   url: Path to the template resource (required)
 *   data: Same as the 'data' argument in the $.ajax call
 *   error: Same as the 'error' argument in the $.ajax call
 *   complete: Same as the 'complete' argument in the $.ajax call
 *   beforeUpdate: delegate(html) called just before the actual object is created
 *   afterUpdate: delegate(html, actual) called just after the actual object is created
 */

//creates a function to access a web template
jQuery.template = function(params) {
	
	//format the passed in parameters
	//check if this was only the resource url
	if (typeof(params) === "string") {
		params = { url:params };
	}
	
	//prepare the arguments passed into the class
	if (!$.isFunction(params.beforeUpdate)) { params.beforeUpdate = function() {}; }
	if (!$.isFunction(params.afterUpdate)) { params.afterUpdate = function() {}; }
	

	//create the object that handles the work
	var self = {
	
	//Properties
	//---------------------------------------------------------
	
		//handles forwarding methods onto the actual object
		container:null,
		
		//the actual jQuery object being created
		actual:null,
		
		//method calls that are waiting to be called
		queue:[],
		
	//Methods
	//---------------------------------------------------------
		
		//prepares the container for use
		setup:function() {
			
			//apply each of the methods
			self.container = $("<div/>");
			for(var item in self.container) {
				if (!$.isFunction(self.container[item])) { continue; }
				self.register(item);
			}
			
		},
		
		//handles creating method forwarding on the returned object
		register:function(method) {
		
			//create a method that handles routing the original method calls
			self.container[method] = 
				function(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14) {
				
				//if the actual object has been called, just invoke
				if (self.container.actual) {
					return self.container.actual[method](
						p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14
						);
				}
				//otherwise, queue the request
				else {
					self.queue.push({
						action:method,
						params:[p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14]
					});
					
					//then return the temporary object
					return self.container;
				}
			
			};
		
		},
		
		//executes any queued commands and updates the jQuery object
		update:function(html) {
			
			//create the jQuery object
			self.container.actual = $(html);
			
			//then execute all of the waiting commands
			$.each(self.queue, function(i, arg) {
				self.container.actual[arg.action](
					arg.params[0], arg.params[1], arg.params[2], arg.params[3], arg.params[4],
					arg.params[5], arg.params[6], arg.params[7], arg.params[8], arg.params[9],
					arg.params[10], arg.params[11], arg.params[12], arg.params[13], arg.params[14]
					);
			});
		
		},
		
		//starts the ajax request to download the template HTML
		download:function() {
			
			//starts downloading content
			$.ajax({
			
				//parameters for the template request
				url:params.url,
				data:params.data,
				dataType:"html",
				
				//performs the catch up work for the ajax
				success:function(html) {
					//** Optional: Uncomment 'setTimeout' to simulate a delay 
					//setTimeout(function() {
					params.beforeUpdate(html);
					self.update(html);
					params.afterUpdate(html, self.container.actual);
					//}, 2000);
				},
				
				//additional handling of the request
				error:params.error,
				complete:params.complete
			});
		
		},
		
	//Initalization
	//---------------------------------------------------------
		
		//setup the object to work
		init:function() {
		
			//prepare the temporary container
			self.setup();
		
			//start downloading the content
			self.download();
		}
	
	};
	
	//prepare the the template code
	self.init();
	
	//return the custom container to forward method calls
	return self.container;
	
};

This code allows you to have direct access to a jQuery object even before it has finished loading the content from the server. So, instead of using a callback we can write our jQuery like we normally would.

//note: the first command is all one long chain with a comments
//between functions to explain a bit more

//create the object and immediately apply changes
var template = $.template("template.txt")
	.css({"color":"#f00", "width":"200", "background":"#333"})
	.animate({width:800, height:900}, 3000)

	//you can even append and appendTo the object in advance
	.appendTo($("#container"))

	//or search for and update child elements
	.find(".title").text("howdy")

	//and parent elements
	.parent().css({"background":"#00f"});
	
//you can also still access the template from the 
//assigned variable
template.find("div").click(function() {
	alert('clicked');
});

//even if if you call it much later, it still works
setTimeout(function() {
	template.css({"color":"#0f0"});
}, 10000);

What Is Going On Here?

As I mentioned before the real problem is that the jQuery object we create isn’t ready as soon as we want to assign to it. Because of that we have to use a callback to resume the work. However, the cool thing about dynamic languages is that we can override anything we want.

In this example we start by creating a container that has all the same functions as a typical jQuery object but has one slight modification — all the methods are overridden and placed into a queue (man, I love enclosures). Once our AJAX call has completed we run an update command that executes everything we have saved against our new jQuery object instead of the one that received the calls to begin with — madness!

After we’ve caught up and our actual object is created we can stop saving actions to the queue and instead just invoke them immediately. I nicknamed this method forwarding for the sake of having a cool, buzzword sounding sort of name but if anyone knows what this is really called, please tell me 🙂

It is worth noting that this is only going to work with functions that return the jQuery object (at least until the ‘real’ object is created). The reason is that since we’re just capturing methods and saving them for later then we don’t know what the actual return type is. Needless to say, using a property probably won’t be accurate either (since we can’t intercept the request for the property like we would with a method)

In any case, this might simplify the next time you need to download content in jQuery but you don’t want to break up your functionality.

Written by hugoware

January 14, 2010 at 10:59 pm

Using Delegates As Parameters

with 2 comments

I’m not sure the community stance on passing around delegates as arguments but personally I love it. I think there is something magical about the way you can provide instructions on how how to do something and then pass it into another method and let it do its job without needing to think about it. It is almost like writing a code template and then filling in the blanks with the specifics of what you need.

Using delegates as arguments can simplify code and in some cases reduce it. This post goes over a sample scenario where you might use delegates as arguments instead of writing multiple functions to perform different tasks.

Simple Resource Loader

Let’s pretend we have a class that is responsible for loading and saving files from an external resource. We could write a class like the one below.


//handles loading resources for the application
public static class ResourceLoader {

    //returns the bytes for a resource
    public static byte[] LoadBytes(string resource) {
        string path = ResourceLoader._GetResourcePath(resource);
        return File.ReadAllBytes(path);
    }

    //returns a xml document resource
    public static XDocument LoadXmlDocument(string resource) {
        string path = ResourceLoader._GetResourcePath(resource);
        return XDocument.Load(path);
    }

    //returns a bitmap resource
    public static Bitmap LoadBitmap(string resource) {
        string path = ResourceLoader._GetResourcePath(resource);
        return Bitmap.FromFile(path) as Bitmap;
    }

    //returns the string text for a resource
    public static string LoadText(string resource) {
        string path = ResourceLoader._GetResourcePath(resource);
        return File.ReadAllText(path);
    }

    //generates a path to a resource
    private static string _GetResourcePath(string file) {
        return Path.Combine(@"c:\path\to\files\", file);
    }

}

Nothing is really wrong with this code. If we need to add a new type to this class then we simply create a new method and plug it in. Additionally, each of the methods could use some sort of exception handling in case the conversion doesn’t go so well. As you can imagine, the more try catch blocks we add, the larger this class starts to get.

However, if you think about it, all of these resources could be handled roughly the same way. They all need a way to convert bytes to whatever type you’re wanting.

Using A Delegate To Fill In The Blank

So instead, lets address this problem using a delegate to handle the conversion of bytes to the type that we need.


//handles loading resources for the application
public static class ResourceLoader {

    //returns the bytes for a resource
    public static T Load<T>(string resource, Func<byte[], T> convert) {

        //find the correct path
        string path = Path.Combine(@"c:\path\to\files\", resource);
        byte[] bytes = File.ReadAllBytes(path);

        //attempt convert the file
        try {
            return convert(bytes);
        }
        //if it fails, forward the error to the caller
        catch (Exception ex) {
            throw new FormatException(
                string.Format(
                    "Could not load resource '{0}' as a type {1}", 
                    resource, typeof(T).Name
                    ), ex);
        }

    }

}

Great — Now we can provide any method we want to format the bytes and then return the type we’re looking for. So for example, instead of calling ResourceHandler.LoadBitmap we can use our new method as shown below.


Bitmap bitmap = ResourceLoader.Load("test.jpg", (bytes) => {
    using (MemoryStream stream = new MemoryStream(bytes)) {
        return Bitmap.FromStream(stream) as Bitmap;
    }
});

Pretty slick, right? …oh, wait… this code is longer and more difficult to use… This can’t be right!

Bringing The Concept Together

Clearly, the example above isn’t improving anything for us. The code is longer and requires that we write the same functionality for reading a resource in multiple places. Even though this code is more flexible, it also requires more work. So instead, lets write some definitions of common conversions as part of our class.


//handles loading resources for the application
public static class ResourceLoader {

    //Load<T>(resource, convert)
    //snip...

    //converts bytes to a bitmap
    public static readonly Func<byte[], Bitmap> AsBitmap = (bytes) => {
        using (MemoryStream stream = new MemoryStream(bytes)) {
            return Bitmap.FromStream(stream) as Bitmap;
        }
    };

    //converts bytes to a xml document
    public static readonly Func<byte[], XDocument> AsXml = (bytes) => {
        using (MemoryStream stream = new MemoryStream(bytes)) {
            using (StreamReader reader = new StreamReader(stream)) {
                return XDocument.Parse(reader.ReadToEnd());
            }
        }
    };

    //converts bytes to a string
    public static readonly Func<byte[], string> AsText = (bytes) => {
        return Encoding.UTF8.GetString(bytes);
    };

    //simply returns the byte array
    public static readonly Func<byte[], byte[]> AsBytes = (bytes) => bytes;

}

Now, instead of needing to manually create a delegate to handle the conversion process we can write code like the example below.

 
//Loads a resource by passing the static delegate that is part of the class we created
Bitmap bitmap = ResourceLoader.Load("test.jpg", ResourceLoader.AsBitmap);
 

By using delegates as an argument we allow our loading function to be flexible enough to accept other methods for performing the conversion (that we can create as we need them) or the common conversion methods that we added as part of the class.

You’ll also notice that we don’t need to declare the generic type we’re wanting to have returned since it can be inferred from the return type of the delegate we pass in! (so cool… to me at least)

Of course, there is about a hundred different ways to can already load resources in .NET but you could apply this same concept many other areas of applications you develop.

How could you use delegates to create “templates” for code?

Written by hugoware

January 13, 2010 at 1:58 am

jsshell 2.0 – The Release

with 17 comments

Today you can download jsshell 2.0. This new version has a lot of new features.

If you’ve never used it before you might want to watch the jsshell screencast to help you get caught up on what it does and how it works.

Basic Usage

Here are the basics of using jsshell.

  • CTRL + Enter: Runs all of the code in the current window. You can also highlight the code you want to run and press the same keys to run just the selected code.
  • CTRL + Delete: Wipes out all of the code currently in the editor.
  • Double Click the Title bar: This will minimize and restore the code window on the page.

You can drag the window around the screen and also resize the text area by clicking the corner of the textarea and dragging it to the correct size.

Settings

You can now adjust settings in jsshell to behave how you want. Below are some commands you can use.

  • js.setting("allowAuto", true|false): Turns on and off the ability for commands to run automatically when browsing to a web page.
  • js.setting("autoDelay", milliseconds): The time to wait before attempting to run a command automatically on a page.
  • js.setting("showHint", true|false): Turns on and off the ‘intellisense’ box with the page elements, classes and ids.
  • js.setting("displayNodes", true|false): Turns on and off is the ‘Growl’ style boxes can appear or not.
  • js.setting("autoShow", true|false): Turns on or off if the box should automatically appear at the bottom of every page (without clicking the jsshell icon)
  • js.setting("onlyTop", true|false): (experimental) Causes the editor window to only appear in the top frame and not any other sub frames.
  • js.color("dialog|editor|font", "#color"): Changes a color setting for the jsshell window.

Data

jsshell now allows settings to persist from session to session and page to page.

  • js.data.list(): Shows a complete list of all data currently saved for the browser.
  • js.data.save("key", {object}): Saves an object to the data container. Uses JSON.stringify so be careful what you save.
  • js.data.load("key"): Returns the object the data found in the container of the key provided.
  • js.data.edit("key", delegate(data) { }): Edits the data found with the method provided. The method must return the data or nothing will be saved.
  • js.data.use("key", delegate(data) { }): Similar to the edit command, but just allows access to the data. Nothing will be saved regardless of what is returned.

Other Built In Commands

jsshell also has several other built in commands that perform a variety of tasks.

  • js.call("command"): Invokes a built in command like nostyle or any custom commands you have created.
  • js.note({message:"message", [title:"title"]}): Displays a ‘Growl’ style popup box with a message inside. The Message accepts HTML so you can include links.
  • js.go("url.com"): Browses to the url provided in the command.
  • js.debug(): Nothing special…

Shortcut Commands

Shortcut commands can be called by simply entering !commandName or with js.call(“commandName”).

  • !nostyle: Removes style information from the page like stylesheets, attributes and classes.
  • !flash: Attempts to move the jsshell window over the top of Flash elements on the page.
  • !save: Manually saves the settings for jsshell, but you shouldn’t ever need to call it. Special for all you paranoid people out there 🙂
  • !reset: Resets all information for jsshell and returns to the default settings.

Custom/Auto Commands

One of the cooler new features is that you can write custom commands to run as soon as you get to a page. You can specify a regular expression to evaluate the URL you navigate to and then have a custom command run right away (or multiple commands). If you don’t want to run them automatically then you can still invoke them by using the Shortcut Command syntax shown above.

  • js.custom.edit("commandName"): Shows the dialog to allow you to make changes to a custom command.
  • js.custom.remove("commandName"): Removes a custom command from your settings.
  • js.custom.list(): Displays a full list of all commands available in your settings file.

New Screencast

You can watch the new screencast to see more of the new features in jsshell.

Download It Now!

You can download jsshell 2.0 at my website. Make sure to send me a message if this is helpful and any other features you might want to see!

Written by hugoware

January 12, 2010 at 12:29 am