development, keyboard, mouse

As a developer and gamer I've spent a lot of my life sat in front of a computer. As expected this resulted in me developing issues with my wrists. Now I've had wrist issues for at least 5 years now and it's at a point that having to use a regular mechanical keyboard fills me with dread. 20 minutes into using one of these I will most likely be in agony and not really wanting to write any more code at this point. I tried wrist strengthening exercises, supports and various keyboards.

These are some of the better keyboards I ended up trying:

Microsoft Natural Ergonomic 4000

I had one of these for a few years, I managed to loot one of these at work when someone left and it really seemed to help. I was using one at home and at work until it just started to hurt again. I think the elevated wrists and slightly awkward position was too much on my wrists. Also, the thing was huge.

Microsoft Arc Keyboard

I realised that as I'd never really had any pain issues using a laptop that the low profile and distance to the mouse was really crucial for me. I bought one of these immediately and ended up using at home and work for a long time.

I never developed any issues using this keyboard. The biggest problem here is the keyboard design itself is very poor for developers. The lack of real F6-F12 keys and Insert keys really slow you down inside Visual Studio.

Sculpt Ergonomic Desktop

I just recently discovered the existence of this one. It sits somewhere between the previous two keyboards. Keeping the low profile and laptop style keys which seem to really agree with my wrists and having an ergonomic shape similar to the 4000. This time it also dropped the pointless (for me) num pad from the main keyboard.

Sculpt

I've been using this one for a few days now and I am very happy with it. I think it's replacing the Arc Keyboard as my keyboard of choice for development. Typing on this feels just as comfortable as the Arc Keyboard and has a better selection of keys. The mouse that comes in the set is also very easy on the wrists if a little awkward to use at first.

So after introducing the new minification process the first issue I see is that there's no way to tell it to include comments. Some comments are required, such as jQuery licences.

A little digging and I found out that Bundling in MVC just uses the Microsoft.Ajax.Utilities Minifier and I know that has more configuration options. I then had a look at the System.Web.Optimizations dll to see what the ScriptBundle was doing internally which turns out is pretty simple. So I created this little replacement which does pretty much the same thing except passing more options to the Minifier.

This is the new ScriptBundle, creates a bundle using our new Minifier instead of the default JsMinify.

internal sealed class CommentedScriptBundle : Bundle  
{
    public CommentedScriptBundle(string path)
        : base(path, (string)null, (IBundleTransform)new LicencedMinify())
    {
    }
}

Our new minifer to replace JsMinify, based on what JsMinify actually does. We can easilly add more options to this with CodeSettings to have far greater control over the minification process.

internal class LicencedMinify : IBundleTransform  
{
    private const string JsContentType = "text/javascript";

    private static void GenerateErrorResponse(
        BundleResponse bundle, 
        IEnumerable<object> errors)
    {
        var stringBuilder = new StringBuilder();
        stringBuilder.Append("//* ");
        stringBuilder.Append("Minification Error").Append("\r\n");
        foreach (var obj in errors)
            stringBuilder.Append(obj).Append("\r\n");
        stringBuilder.Append(" *//\r\n");
        stringBuilder.Append(bundle.Content);
        bundle.Content = stringBuilder.ToString();
    }

    public void Process(BundleContext context, BundleResponse response)
    {
        if (context == null)
            throw new ArgumentNullException("context");
        if (response == null)
            throw new ArgumentNullException("response");
        if (!context.EnableInstrumentation)
        {
            var minifier = new Minifier();
            var content = response.Content;
            var codeSettings = new CodeSettings
                {
                    EvalTreatment = EvalTreatment.MakeAllSafe,
                    RemoveUnneededCode = true,
                    PreserveImportantComments = true,
                    TermSemicolons = true
                };
            var str = minifier.MinifyJavaScript(content, codeSettings);
            if (minifier.ErrorList.Count > 0)
                GenerateErrorResponse(response, minifier.ErrorList);
            else
                response.Content = str;
        }
        response.ContentType = JsContentType;
    }
}

We now create our bundles like this:

bundles.Add(  
    new CommentedScriptBundle("~/Bundles/Master").Include(
        new[]
            {
                "~/Script/jquery-uncompressed.js"
                ...
            }));

Now with these, when we create a bundle the important script comments are left intact and we avoid any potential nasty legal issues. Yay

Working on http://hotelscombined.com.au we have a JS bundling process in place which is pretty interesting. There's an executable that runs as part of the TFS build that combines and minifies the JS files into specified a bundle file. It's a pretty old solution so obviously has legacy implications to changing anything.

Firstly we have to deal with affiliates and the white label version of the site. We must maintain backwards compatibility with these sites at all cost and we have a rather interesting http handler which serves a JS file bundle based on a requested file name.

So we have to maintain this lookup of legacy filenames to bundles but I really want to switch to the newer MVC bundling process, then I can look at introducing JSLint/JSHint locally and get build time errors.

Using the MVC bundling creating the bundles is pretty straight forward, I then replaced the links to our legacy JavaScript manager.

bundles.Add(  
    new ScriptBundle("~/Bundles/Master").Include(
        new[]
            {
                "~/Script/jquery-uncompressed.js"
                ...
            }));

Next step is the more complicated one. I created a legacy lookup dictionary containing the old file names and the newer bundle names so we maintain backwards compatibility. We then need to dynamically load the contents of a bundle and serve it via the handler, this also needs to serve minified and unminified versions depending on the environment.

This is the code that I came up with to handle this. It's still in proof of concept stage right now so may change.

var bundle = BundleTable.Bundles.FirstOrDefault(b => b.Path == bundleName);  
if (bundle != null)  
{

    var bundleContext = new BundleContext(
        context, BundleTable.Bundles, bundleName)
        {
            EnableOptimizations = true
        };
    var defaultBundleBuilder = new DefaultBundleBuilder();
    var bundleContent = defaultBundleBuilder.BuildBundleContent(
        bundle, bundleContext,
        bundle.EnumerateFiles(bundleContext));
    var response = new BundleResponse(
        bundleContent, bundle.EnumerateFiles(bundleContext))
        { ContentType = "application/javascript" };

    if (minify)
    {
        var minifier = new JsMinify();
        minifier.Process(bundleContext, response);
    }

    return response;
}