jQuery Download Plugin

jQuery Download Plugin

I was in need of an “export” button to export some data to a CSV file, but the data changes based on some other choices. I added my js to export the file and then – DUH! You cannot use ajax to return a file. Silly me… 🙂

I figured I could wrap a form around the button and set a hidden input and blah blah blah. I googled and found the above, which basically does all that for me and gives me a convenient 1 liner to work much like an ajax call would but allow for a “save as” dialog. It is pretty neat-o!

Trolliminator!

I am a big soccer fan, and I love my MLS. I frequent the site and read up on all the articles. Unfortunately, there are a few faces that are always trolling in the comments. I really don’t even care to read what they have to say, as it doesn’t contribute to the discussion. I decided I’d do something about it.

Trolliminator!

Introducing the Trolliminator! bookmarklet.

javascript: (function () {
  var trolls = [
		'troll', // <-- add troll names here
	];
 
    var el = document.createElement('div'),
        b = document.getElementsByTagName('body')[0];
    otherlib = false, msg = '';
    el.style.position = 'fixed';
    el.style.height = '32px';
    el.style.width = '220px';
    el.style.marginLeft = '-110px';
    el.style.top = '0';
    el.style.left = '50%';
    el.style.padding = '5px 10px 5px 10px';
    el.style.zIndex = 1001;
    el.style.fontSize = '12px';
    el.style.color = '#222';
    el.style.backgroundColor = '#f99';
    if (typeof jQuery != 'undefined') {
        msg = 'This page already using jQuery v' + jQuery.fn.jquery;
        return showMsg();
    } else if (typeof $ == 'function') {
        otherlib = true;
    }
    function getScript(url, success) {
        var script = document.createElement('script');
        script.src = url;
        var head = document.getElementsByTagName('head')[0],
            done = false;
        script.onload = script.onreadystatechange = function () {
            if (!done && (!this.readyState || this.readyState == 'loaded' 
|| this.readyState == 'complete')) {
                done = true;
                success();
            }
        };
        head.appendChild(script);
    }
    getScript('http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js', 
function () {
        if (typeof jQuery == 'undefined') {
            msg = 'Sorry, but jQuery wasn\'t able to load';
        } else {
            msg = 'This page is now jQuerified with v' + jQuery.fn.jquery;
            if (otherlib) {
                msg += ' and noConflict(). Use $jq(), not $().';
            }
        }
        return showMsg();
    });
 
    function showMsg() {
        el.innerHTML = msg;
        b.appendChild(el);
        window.setTimeout(function () {
            if (typeof jQuery == 'undefined') {
                b.removeChild(el);
            } else {
                jQuery(el).fadeOut('slow', function () {
                    jQuery(this).remove();
                });
                if (otherlib) {
                    $jq = jQuery.noConflict();
                }
                trolliminator();
            }
        }, 2500);
    }
 
    function trolliminator() {
    	var $ = jQuery;
    	var matchCounter = 0;
    	$.each(trolls, function(index, value) {
    		$('.gig-comments-comment-username').filter(function() {
    			var match = $(this).text() == value;
    			if (match) {
    				var table = $(this).closest('table');
    				table.hide();
    				table.parent().append(
$('<div>').css({ 'color':'#666', 'font-style':'italic' })
.html('Trolliminated!'));
    				matchCounter++;
    				return match;
    			}
    		});
    	});
    	$('<div>').css({ 
'position':'fixed', 'right':'0', 'bottom':'0', 
'padding':'5px', 'font-weight':'bold', 
'color':'#090', 'z-index':'99999' })
.html(matchCounter + ' trolls eliminated').appendTo('body');
    }
})();

To use it:

  1. Edit the “trolls” using comma-separated, single-quoted names
  2. Save it as a bookmarklet
  3. Hit it up prior to reading any article

It removes posts for matching “trolls” and replaces them with a “Trolliminated!” message. It also puts a counter down in the bottom-right corner. I started to develop a Chrome extension, but I ran into a little trouble and, to be honest, I did not want to put much effort into blocking the trolls – that is just another way they are fed.
🙂

MLSSoccer.com uses Gigya, so this should technically work with any other site that uses Gigya comments.

Backbone.js

We have decided to use backbone.js for a project at work, so I have been soaking up everything I can find about it. Tekpub has a great series (currently in development) that I am really digging, and it has got me pretty excited.

I recently checked out the Peepcode preview and it looks pretty promising. A co-worker also pointed me to a great book hosted on Github. I’d also like to check out the NetTuts backbone/.NET videos. Can you tell I like video learning?
🙂

I started thinking that WebAPI would probably work really well with backbone rather than a full-blwon MVC site. I started searching and I have been finding some great links, so I thought I’d compile a list of them here:

I am (obviously) a little late to jump on the backbone bandwagon, so there is a ton of great content out there. I would really like to find a project that can allow C# code to be converted into backbone models, but that may be a bit difficult. It would be great to use the strongly-typed models and then add some spice for backbone and have some Nuget-able library that can convert the C# model/spice to backbone for you to save some of the javascript work. Don’t get me wrong – I am not afraid of javascript. Quite the contrary – I have grown to really love javascript. What I don’t like is doing things twice. Writing the backbone views and then writing a lot of the same type of code on the server-side (for the data persistence) just doesn’t sound appealing.

One thing that could maybe make it all a little easier is to use Massive. I recently swapped out some data access code that was using SubSonic to use Massive (eliminating some other DLLs along the way too) and it is pretty cool. Of course, it uses the dreaded dynamic keyword, so no fancy-pants Intellisense, but that is okay. Methinks that a properly coded backbone app using WebAPI would result in very little server-side code anyway, so some dynamic objects pushing and pulling to and from a db would be no big deal.

SVG CSS Injection

Through some random occurence I stumbled upon PJ Onori’s blog. I noticed a post titled “SVG CSS Injection” – that seemed pretty interesting, so I clicked. I was pleasantly surprised.
🙂

I have always thought vector graphics were really cool. I like the concept of SVG, but I haven’t gotten much into it. I still don’t know what tool to use to make the SVG graphics, but once made, PJ’s technique seems like a pretty cool concept. Basically, he “injects” the SVG graphics in the page where he can then manipulate them with javascript. He can change the color on-the-fly, etc. There is a nice demo page to show off his work.

The code is available on Github, so you can fork it if you think it is awesome. I hope to have a chance to play with it. I may not get to use it at work, but maybe I can sprinkle some SVG goodness on this blog. We’ll see…

Restrict form access and set a hidden input

There was a question I just ran across on Stack Overflow asking how to set a hidden input with js, and also to restrict access to one form from another. Setting the hidden input value is pretty simple, and I figured this would be a breeze. Overall it was, but I scrapped the guys code and rewrote it from scratch. It was easier for me to do that than to figure out his code. I wanted to share what I did here:

It isn’t the most robust thing, but it works. Once you select one answer on form A or B, you have to select only from that form. Annoying alerts are used, but it is just a prototype – the real deal would need to be much slicker.

I added simple validation; if there aren’t 3 checked radio buttons the form is not complete. Also, the validation triggers the submit button enable, so it’s a two-fer
🙂

Here is the code (in case jsFiddle ever blows up):

<h2>Table A</h2>
<table id="tableA" border="1">
    <tr>
        <td>
            A1
        </td>
        <td>
            <input type="radio" name="a1" value="1" /> Yes
            <input type="radio" name="a1" value="0" /> No
        </td>
    </tr>
    <tr>
        <td>
            A2
        </td>
        <td>
            <input type="radio" name="a2" value="1" /> Yes
            <input type="radio" name="a2" value="0" /> No
        </td>
    </tr>
    <tr>
        <td>
            A3
        </td>
        <td>
            <input type="radio" name="a3" value="1" /> Yes
            <input type="radio" name="a3" value="0" /> No
        </td>
    </tr>
</table>

<h2>Table B</h2>
<table id="tableB" border="1">
    <tr>
        <td>
            B1
        </td>
        <td>
            <input type="radio" name="b1" value="1" /> Yes
            <input type="radio" name="b1" value="0" /> No
        </td>
    </tr>
    <tr>
        <td>
            B2
        </td>
        <td>
            <input type="radio" name="b2" value="1" /> Yes
            <input type="radio" name="b2" value="0" /> No
        </td>
    </tr>
    <tr>
        <td>
            B3
        </td>
        <td>
            <input type="radio" name="b3" value="1" /> Yes
            <input type="radio" name="b3" value="0" /> No
        </td>
    </tr>
</table>

<p>
    <button id="submit">Submit</button>
    <button id="reset">Reset</button>
</p>

<input type="hidden" id="activeTable" />
​
window.ns = {};
ns.activeTable = null;
ns.validate = function() {
    // Simple validation
    // If we don't have 3 checked radio buttons, it is invalid
    var checked = $("#" + ns.activeTable).find("input[type=radio]:checked");
    var valid = (checked || []).length === 3;
    $("#submit").prop("disabled", !valid);
    return valid;
};

ns.validate();

$("#submit").click(function() {
    var valid = ns.validate;
    if (valid == false) {
        alert("You must complete the form!");
        return;
    }
    
    var results = $("#" + ns.activeTable).find("input[type=radio]:checked");
    var output = ns.activeTable + " Results\n";
    $.each(results, function(idx, data) {
        output += "\t" + $(this).prop("name") + 
            " - " + $(this).val() + "\n";
    });
    alert(output);
    $("#activeTable").val(ns.activeTable);
});

$("#reset").click(function() {
    $("input[type=radio]").prop("checked", false);
    ns.activeTable = null;
    ns.validate();
});

$("input[type=radio]").click(function() {
    var selectedTable = $(this).closest("table");
    if (ns.activeTable != null && 
        selectedTable.prop("id") != ns.activeTable) {
        alert("Invalid form selection. Onlye selections from " + 
              ns.activeTable + " are allowed");
        $(this).prop("checked", false);
    } else {
        ns.activeTable = selectedTable.prop("id");
    }
    ns.validate();
});​
html { margin: 10px; }
table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
table td { border: 3px solid #CCC; padding: 5px; }
table td:nth-child(1) { width: 75%; }
table td:nth-child(2) { text-align: center; }
h2 { font-size: 1.8em; font-weight; bold }
button { padding: 5px; border-radius: 15px; background-color: #CCC; 
         width: 100px; }​

Recent gists

I have saved a couple of code snippets lately and just wanted to add them to the blog too. This one enables “focus out” unobtrusive validation. I use this in a couple of forms on the site. Part of this gist also has a little code where I was messing around with “remote” validation to get it to work with the focus out validation – of course, I wasn’t thinking, just geeking out. The remote stuff is already baked in to jquery validation and I already had code to handle the focus out portion, so it wasn’t really worth the time, but I didn’t spend much on it. Anyway, the code:

window.Em = window.Em || {};

Em = {
    setFocusOutValidation: function (form) {
        var s = $.data(form, "validator").settings;
        s.onfocusout = function (element) {
            if ($(element).val().length > 0) {
                $(element).valid();
            }
        };
        s.showErrors = function (map, list) {
            this.defaultShowErrors();
            if (list && list.length > 0) {
                for (prop in map) {
                    $("#" + prop).focus();
                }
            }
            Em.displayValidationSummaryErrors(list);
        };
        return s;
    },
    displayValidationSummaryErrors: function (list) {
        $("[data-valmsg-summary]")
            .removeClass("validation-summary-valid")
            .addClass("validation-summary-errors");
        $("[data-valmsg-summary] ul").html("");
        $.each(list, function (idx, data) {
            $("[data-valmsg-summary] ul").append($("<li></li>")
                .html(data.message));
        });
    }
}

The focus out validation will check if the field is valid (also remotely) and if not, the focus will stay on the invalid field. Sometimes it can get a little wonky, but for the most part it works well. Typically when it doesn’t seem to “work” it is because the validation is setup backwards, like comparing a “new password” to the “confirm password”, rather than the other way around.

Gist

The next gist is a Telerik MVC grid extension I started messing with to handle filtering. Turns out I was making it harder than necessary, and there was already an extension method in the NopCommerce codebase. Oh well, here it is anyway:

public static IList<T> ApplyFilter<T>(this IList<T> list, 
                                      FilterDescriptor filter) {
 
    // We wont allow filtering on anything but string type properties
    if (filter.MemberType != typeof(string)) 
        throw new ArgumentException(@"Filtering is only allowed 
            for properties with a type of 'string'.");
 
    var value = filter.Value.ToString();
    switch (filter.Operator) {
        case FilterOperator.IsEqualTo:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member)
                    .GetValue(x, null);
                return String.Equals((string)propertyValue, value, 
                    StringComparison.InvariantCultureIgnoreCase);
            }).ToList();
            break;
 
        case FilterOperator.IsNotEqualTo:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member).GetValue(x, null);
                return String.Equals((string)propertyValue, value, 
                    StringComparison.InvariantCultureIgnoreCase) == false;
            }).ToList();
            break;
 
        case FilterOperator.StartsWith:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member).GetValue(x, null);
                return ((string)propertyValue).StartsWith(value, 
                    StringComparison.InvariantCultureIgnoreCase);
            }).ToList();
            break;
 
        case FilterOperator.Contains:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member).GetValue(x, null);
                return ((string)propertyValue).IndexOf(value, 
                    StringComparison.InvariantCultureIgnoreCase) > -1;
            }).ToList();
            break;
 
        case FilterOperator.EndsWith:
            list = list.Where(x => {
                var propertyValue = x.GetType()
                    .GetProperty(filter.Member).GetValue(x, null);
                return ((string)propertyValue).EndsWith(value, 
                    StringComparison.InvariantCultureIgnoreCase);
            }).ToList();
            break;
    }
 
    return list;
}

Gist

The last one is just a little quickie example of how to select data attributes with jQuery:

var d = $("input[data-toggle-class]");
$.each(d, function (idx, data) {
    console.log(idx, data);
});

Gist

I was originally looking for a way to select a wildcard in the data attribute, but I couldn’t get it working. Anyway, this works fine – though I did not actually end up using it. 🙂

JS MVC/MVVM Frameworks

My recent dive into MVC has me wanting to do some client-side razzle-dazzle. I have a Tekpub subscription and Rob’s MVC3 vid shows off backbone.js. It looks interesting, but it looks a tab bit complicated and cumbersome. I have to say that the URL router stuff is awesome sauce.

I decided I’d start looking around. Last year on one of the Hanselminutes podcasts he spoke with Steve Sanderson about knockout.js. At the time I wasn’t doing anything with MVC, so I did not think it would be of much value, but I filed it away. (NOTE: I realize now I could use knockout, backbone, whatever with Webforms, but did not realize it at the time.) I must say knockout looks pretty sweet. I watched Steve’s presentation at MIX11 and I really love the two-way binding out of the box (that was the one thing I did not care for much about backbone, though I realize there is a plugin for it).

While both knockout and backbone look really cool, I cannot decide which one to go with? Unfortunately there is not a jQuery-like choice here – there is not a real dominant player that everyone has decided on. So, I decided I’d check out some opinions. There is a great question on StackOverflow (naturally) and it has lots of good info. There are some other options to consider as well:

There is a LOT more out there then I thought. I guess I am going to have to take a little time and weigh the pros/cons of each before I go jumping in to using one – not my strong suit!

ASP.NET MVC Cascading drop down lists

Found this great post on how to create cascading drop downs in ASP.NET MVC.

I created a page for looking up shipping rates by carrier, weight and zip code. For my particular usage, I had 2 lists: Carrier and ShipMethod. I wanted the cascading list to be disabled when the initial list selection had yet to be made. I created functions to populate the target list as well as enable/disable the target list. I made the js into a reusable Razor helper which should allow me to use it anywhere I need this functionality.

Here is the helper code:

@helper DynamicDropDowns() {
<script type="text/javascript">
    function listChanged($list, $target, url) {
        var listId = $list.val();
        if (listId == "") { // User selected first option, 
                            // so clear and disable the list
            $target.empty();
            enableList($target, false);
            return;
        }
        $.getJSON(url, { id: listId }, function (data) {
            $target.empty(); // Clear the list
            $.each(data, function (idx, item) { // Add the data
                $target.append($("<option/>",
                {
                    value: item.Value,
                    text: item.Text
                }));
            });
            enableList($target, true); // Enable the list
        });
    }

    function enableList($list, enabled) {
        $list.attr("disabled", enabled ? null : "disabled");
    }
</script>
}

Gist

And here it is in action:

<script type="text/javascript">
    $(function () {
        var $carrier = $("#Carrier");
        var $ship = $("#ShipMethod");
        enableList($ship, false);
        $carrier.change(function () {
            listChanged($(this), $ship, "/Tools/GetShipMethodsByCarrier");
        });
    });
</script>

jQuery Star Rating Plugin

I am thinking about adding a star rating to one of the sites at work, so I started poking around for what was available in a jQuery plugin. Surprisingly, there are quite a few plugins but none that I totally fell in love with. they either don’t degrade, or use form elements I don’t hink they should, or whatever. I thought it would be a good exercise to try and roll my own plugin.

I started of checking out some how-to’s for jQuery plugin writing, but I usually learn better by doing, so I started with the closest thing I like, Wil Stuckey and John Resig’s rating plugin. It is pretty good, but it just needed a little nudge to be better. I figured this was a good way to start. I would not have to write an entire plugin from scratch, and I’d learn from one of the guys that created jQuery, so how could I go wrong?

It took a few days, with a few hours spent here and there. All in all I feel pretty good about the plugin, though I am sure I could make it better, and I likely forgot something important. If you are interested, you can grade the code from my code section, and there is a demo available too. Let me know what you think in the comments.