New caretakers for some of my side projects

I’m trying to pair back some of my side projects so I can focus on the ones I really enjoy the most like RestSharp and some other things not yet public. That combined with a new job that is more time-intensive than my previous one led me to start looking for some new caretakers for a couple of my side projects.

Managed Assembly and @dotnetlinks

I have sold Managed Assembly to DotNetKicks. If anyone can take this site and make something of it, it’s James Avery whom you probably know from The Lounge ad network and TekPub. I don’t know his exact plans for it, but I trust it will be great. Control of @dotnetlinks was transferred in this transaction as well. Looking forward to seeing what comes next for my little link sharing site!

jQuery Snippets for Visual Studio 2010

I’m very happy to announce that Jon Galloway and Rey Bango have taken over maintaining the jQuery Snippets for Visual Studio 2010 project on CodePlex. I can’t think of two better people to take the project and run with it. Rey is a member of the jQuery team and Jon is best known for being my first Twitter follower. Thanks Jon and Rey!

Posted June 22nd, 12:36 AM
Read more posts about .NET, Boring Meta, Managed Assembly, jQuery.

View Comments
Link

jQuery Snippets & jQuery 1.4: What’s your opinion?

jQuery 1.4 was released today and jQuery founder John Resig said in a webcast that 1.4.x would be shipping with Visual Studio 2010. The jQuery Snippets for VS 2010 project I released in December currently has support for jQuery 1.3.2. How do you think the new version should be handled?

  1. Do side-by-side releases targeting 1.3.x and 1.4
  2. Remove support for 1.3.x and only support 1.4.x since it’s the version shipping with VS
  3. Update existing snippets to 1.4, add new 1.4 features and add additional snippets specifically for 1.3 only in areas where it differs

I’m leaning toward #3. There won’t need to be many 1.3 specific snippets (mostly in the CDN snippets). It may be confusing for those using 1.3 if they encounter 1.4 specific snippets and they produce code that doesn’t work with their version of jQuery.

What do you think? Comment below with your choice.

Posted January 14th, 11:42 PM
Read more posts about jQuery.

View Comments
Link

jQuery Snippets for Visual Studio 2010

I love Visual Studio code snippets. I’ve always wished that Visual Studio had them for HTML and JavaScript so I was thrilled to see that feature added in VS2010. I also love jQuery. jQuery is shipping with VS2010, but that’s about the extent of the official support for it. I thought there needed to be code snippets for jQuery and couldn’t find any, so I created 131 of them and started an open source project. You can find links to the project/downloads/docs below.

If you have any suggestions, issues, etc. Please let me know. You can follow me on Twitter for updates on the project. Thanks to Dave Ward, Jonathan Carter, and Rick Schott for testing them out and providing feedback.

jQuery Snippets on CodePlex

Complete List of Snippets

25 Second Demo Video

Download jQuery Snippets 1.0 Installer

Posted November 30th, 10:15 AM
Read more posts about .NET, Programming, jQuery.

View Comments
Link

Building a mostly real-time web-based Twitter client with ASP.NET MVC, jQuery and TweetSharp, Part 2

In Part 1 I showed how the server-side tweet retrieval and caching mechanism works for the .NET Twitter Stream on Managed Assembly. In this post, I’ll show you how the UI pulls the items and updates the list live.

Twitter streams tend to show the newest items at the top of the page which I don’t think is ideal for reading a live stream of tweets. I decided to build an interface that showed the newest tweets at the bottom of a list so that it would read more like a chat window. I wanted the list to automatically scroll to keep up with the new items, but if you scrolled up to start reading older items autoscoll would be disabled so you wouldn’t be constantly losing your place.

When a visitor hits the page a call is made to the /Refresh method described in Part 1. On the first request the last 100 cached tweets on the server are returned along with the ID of the most recent tweet, which is stored in a hidden field. On subsequent requests the value from the hidden field is included in the request to /Refresh so that only new tweets are returned and appended to the list. This minimizes the amount of traffic sent back and forth and the amount of work needed to append items to the list (just loop through the returned items and append the generated HTML).

Let’s start with the HTML needed:

<div id="statusContainer">
    <table id="statuses" style="table-layout: fixed; overflow: hidden;">
    </table>
</div>
<p>
    Updates automatically every 60 seconds. 
    Auto scroll is 
    <span id="autoScrollOn">ON</span> 
    <span id="autoScrollOff" style="display: none;">OFF</span>
</p>
<%=Html.Hidden("lid", "") %>

There’s a couple inline styles but bare with me. There’s a certain amount of “get-it-done”-ness to the code (particularly the HTML generation later) but it’s not important, this is just an example that happens to be running great live :-)

There’s a lot of CSS involved, but the important style definitions to make the autoscrolling work are as follows:

#autoScrollOff { color: #f00; }
#autoScrollOn { color: #0f0; }
#statusContainer
{
    height: 525px;
    overflow: auto;
    border: 1px solid #ccc;
    margin: 5px 0;
}
#statuses
{
    width: 100%;
    border: none;
    border-collapse: collapse;
}

There are two functions that make it happen. The first calls for the JSON and schedules itself to be called again in 60 seconds:

function getUpdates() {
    $.getJSON('<%=Url.Action("Refresh")%>', { lid: $("#lid").val() }, refreshList);
    window.setTimeout(getUpdates, 60000);
}

The other function is called when the JSON is retrieved (again, this should use client-side templating or something better than just string concats, but it doesn’t so get over it):

function refreshList(json) {
    if (json.max_id != 0)
        $("#lid").val(json.max_id);
 
    $.each(json.results.reverse(), function(i, result) {
        var html = "<tr class=\"status new\"><td class=\"avatar\"><a target=\"_blank\" href=\"http://twitter.com/" + result.UserName + "\"><img height=\"48\" width=\"48\" src=\"" + result.ProfileImageUrl + "\" align=\"left\" /></a></td>";
        html += "<td><p><span class=\"user\"><a target=\"_blank\" href=\"http://twitter.com/" + result.UserName + "\">" + result.UserName + "</a></span> ";
        html += result.Text + "</p><p class=\"meta\">" + result.RelativeTime + " from " + result.ClientLink;
        if (result.IsReply) {
            html += " <a href=\"http://twitter.com/" + result.InReplyToUser + "/statuses/" + result.InReplyToId + "\">in reply to " + result.InReplyToUser + "</a>"
        }
        html += "</p></td></tr>";
 
        $("#statuses").append(html);
    });
 
    if (autoScroll) $("#statusContainer").scrollTo(99999);
}

We store the max ID returned to use for the next call. The tweet list is reversed to put new ones at the bottom (this could be handled server-side too), the HTML is built and appended to the table. At the end of the function we check to see if autoScroll is set and if so, scroll to a big number using the jQuery ScrollTo plugin.

To start things in motion, we call the getUpdates() function in document.ready() and attach the handlers to manage autoScroll state:

var autoScroll = true;
 
$(function() {
    getUpdates();
 
    $("#statusContainer").scroll(function() {
        autoScroll = this.scrollTop + this.clientHeight == this.scrollHeight;
        if (autoScroll) {
            $("#autoScrollOn").show();
            $("#autoScrollOff").hide();
        }
        else {
            $("#autoScrollOff").show();
            $("#autoScrollOn").hide();
        }
    });
});

autoScroll is defined outside of document.ready() so that’s available in all the calls to getUpdates().

There’s a similar method for retrieving the list of users currently being followed for the stream. Since the time I originally wrote this code I’ve started reading JavaScript: The Good Parts by Douglas Crockford so I’m a little ashamed of the JavaScript, but it does work well and that’s all I need for now.

Posted June 3rd, 10:08 PM
Read more posts about .NET, ASP.NET MVC, Managed Assembly, Programming, Tips, jQuery.

View Comments
Link

Building a mostly real-time web-based Twitter client with ASP.NET MVC, jQuery and TweetSharp, Part 1

Back when I launched ManagedAssembly.com around MIX I put together a page to show real-time Twitter search results for ‘MIX09’. On the heels of the dvplrs.com launch this week and the lack of .NET representation, I decided to update my live Twitter page to show live updates of notable people in the .NET community. This is how I built it. You can see the finished product over at ManagedAssembly.com/Twitter.

Since I decided to use the standard Twitter API instead of the Search API, I had to build a server-side cache of updates so that I wasn’t hitting the API on every view of the page which would quickly exhaust the API rate limits. Using a database was overkill so I decided to use ASP.NET’s built-in cache mechanism to store new updates. When the client makes a request for the latest updates, the items will be served out of the cache. The cache is updated every so often with new items.

The workflow when the client makes a request for updates (via jQuery) is like so:

  • Grab the date of the last cache update from the cache. If the value doesn’t exist or it’s older than the cache expiration threshold, it’s time to get more statuses from Twitter.
  • When new updates are pulled from Twitter, store the highest ID in the cache as well so that for future requests we can get only the new tweets.
  • Pull the currently cached items and add the new items from the Twitter API call to it.
  • When updates are sent to the browser, the client-side code stores the latest ID. For future requests this ID is passed back to the server which returns items from the cached list that are greater than the locally-stored ID.

What this gives us is the ability to only call Twitter once a minute no matter how many times the site is hit. It also allows for each visitor to only get back tweets that are new to them (which is different for every visitor depending on when the first hit the page) whenever they make a request. Since the tweets are being pulled from memory on almost every request there’s no delay while the API call is being made. If the application restarts and we lose the cache it won’t really matter because we just grab all the updates it will send and repopulate the cache. It also means that the amount of data being sent to the client via JSON is the bare minimum needed. That makes updating the UI simple (just append the new ones without having to process the full list) and keeps the bandwidth requirements low.

That was a lot of words, so time for some code. We start by determining if we need to get new items from Twitter and if so, grabbing them. This uses the CacheManager from a previous post.

CacheManager cache = new CacheManager(new ShortTermProvider());
 
IEnumerable<TwitterStatus> statuses = new List<TwitterStatus>();
long maxId = 0;
 
DateTime lastFetch = cache.Get<DateTime>("last_fetch");
if (lastFetch < DateTime.Now.AddMinutes(-1)) {
    long lastCacheId = cache.Get<long>("last_id");
    if (lastCacheId == 0)
        lastCacheId = 1; // TweetSharp/twitter returns null if you use Since(0)
 
    var request = FluentTwitter.CreateRequest().AuthenticateAs("UserGoesHere", "passwordgoeshere");
    request.Configuration.UseGzipCompression();
    request.Statuses().OnFriendsTimeline().Since(lastCacheId).AsJson();
 
    statuses = request.Request().AsStatuses();
}

The first time this is hit lastFetch doesn’t exist in cache and the cache manager returns the default for DateTime (DateTime.MinValue) which I’m pretty sure is always more than a minute ago. We’ll update the fetch time in cache later. Once we’ve determined that the cache is empty or expired, we grab the last_id value from cache (which is 0 if it doesn’t exist). We then use TweetSharp to get the latest updates and store them in a list.

We also make sure that statuses isn’t null because if you’ve exceed the Twitter API rate limit, TweetSharp returns a null collection. Once we have the updates we have to process them since the text doesn’t include links for @mentions, #hashtags or links. When deciding how to approach this part I contacted Jon Galloway and he pointed me to the code in Witty which handles creating links, which I borrowed heavily from (thanks Jon!). MA also uses SubSonic 2.2 for the DAL and I used one of the methods provided in that library for determining if a word is a URL.

if (statuses != null) {
    foreach (var status in statuses) {
        string raw = status.Text;
        string[] words = Regex.Split(raw, @"([ \(\)\{\}\[\]])");
        StringBuilder output = new StringBuilder();
        foreach (string word in words) {
            if (word.StartsWith("#")) {
                string hashtag = String.Empty;
                Match foundHashtag = Regex.Match(word, @"#(\w+)(?<suffix>.*)");
                if (foundHashtag.Success) {
                    hashtag = foundHashtag.Groups[1].Captures[0].Value;
                    output.Append(string.Format(@"#<a href=""http://search.twitter.com/search?q=%23{0}"" target=""_blank"">{0}</a>", hashtag));
                }
            }
            else if (word.StartsWith("@")) {
                string userName = String.Empty;
                Match foundUserName = Regex.Match(word, @"@(\w+)(?<suffix>.*)");
                if (foundUserName.Success) {
                    userName = foundUserName.Groups[1].Captures[0].Value;
                    output.Append(string.Format(@"@<a href=""http://twitter.com/{0}"" target=""_blank"">{0}</a>", userName));
                }
            }
            else if (SubSonic.Sugar.Validation.IsURL(word)) {
                output.Append(string.Format(@"<a href=""{0}"" target=""_blank"">{0}</a>", word));
            }
            else {
                output.Append(word);
            }
        }
 
        status.Text = output.ToString();
    }
 
    cache.Store("last_fetch", DateTime.Now);
}

OK now that we’ve got the latest updates from the API and added the links, it’s time to store them in the cache.

var cachedStatuses = cache.Get<List<TwitterStatus>>("cachedStatuses");
if (cachedStatuses == null) {
    cachedStatuses = new List<TwitterStatus>();
}
 
if (statuses != null) {
    cachedStatuses.AddRange(statuses);
}
 
if (cachedStatuses.Count > 0) {
    maxId = cachedStatuses.Max(s => s.Id);
    cache.Store("last_id", maxId);
}
 
cache.Store("cachedStatuses", cachedStatuses);

Last but not least we build a JSON return value for all the tweets in the cache with an ID higher than the one sent by the client (lid is a parameter passed from the client).

long lastClientId = lid ?? 1;
 
var data = from s in cachedStatuses
           where s.Id > lastClientId
           orderby s.Id descending
           select new {
               Id = s.Id,
               ProfileImageUrl = s.User.ProfileImageUrl,
               Text = s.Text,
               Source = s.Source,
               UserName = s.User.ScreenName,
               RelativeTime = s.CreatedDate.ToRelativeTime(false),
               ClientLink = s.Source,
               IsReply = s.InReplyToStatusId != 0,
               InReplyToId = s.InReplyToStatusId,
               InReplyToUser = s.InReplyToScreenName
           };
 
return Json(new { results = data.Take(100), max_id = maxId });

In Part 2 I’ll cover building the UI.

Posted May 25th, 6:51 PM
Read more posts about .NET, ASP.NET MVC, C#, Managed Assembly, Programming, SubSonic, Tips, jQuery.

View Comments
Link

Updated Webforms Custom Selector for jQuery 1.3

In case you hadn’t heard, jQuery 1.3 was released yesterday. I haven’t had a chance to test out the custom selector I wrote for Webforms (I’m working almost exclusively with ASP.NET MVC right now), but Tim Banks left a comment to let me know it wasn’t working with 1.3. Tim took the time to fix the code, so here it is. Thanks Tim!

    jQuery.expr[":"].asp = function(a, i, m) {
        return jQuery(a).attr('id').endsWith(m[3]);
    };

UPDATE: Brian Grinstead has posted an additional update to fix an incompatibility with IE.

Posted January 15th, 10:15 PM
Read more posts about jQuery.

View Comments
Link

A different view of strings

If you use jQuery, I’m betting at some point you’ve needed to extract the database ID associated with an HTML element (e.g. <div id="customer_123">...</div>). How many times have you written code like this?

    $("div").click(function() {
        var attrValue = $(this).attr("id");
	var id = attrValue.substr(attrValue.indexOf("_")+1);
	// do something with the id
    });

This seemed like a perfectly reasonable approach to me for the longest time. While looking through the source of another site though, I saw something I hadn’t ever seen before. Apparently, JavaScript has a regex literal syntax that shortens the code above to the following:

    $("div").click(function() {
	var id = $(this).attr("id").split(/_/)[1];
	// do something with the id
    });

You don’t have to use the regex literal, a character or string will work too. Neither method is exceptionally readable, so I prefer to the shorter version. Either way works, so if you like one more than the other, use that. I’m not sharing this tip though because it’s novel though.

The reason I bring this up is because it demonstrated to me a totally different way of thinking about a really trivial problem. I saw a string of text like “edit_123” as a string to be parsed and the author of the code I learned this technique from viewed that string as a data structure with two parts. There’s a profound difference in thinking there.

Posted January 15th, 10:12 PM
Read more posts about Programming, jQuery.

View Comments
Link

Custom jQuery Selector for ASP.NET WebForms

UPDATE: Commenters have posted bug fixes and a version compatible with jQuery 1.3.

I’ve built a ton of ASP.NET web sites using WebForms. Unfortunately I don’t have the luxury of rewriting them all using ASP.NET MVC so I’m stuck dealing with INamingContainer and the resulting auto-generated HTML element IDs.

Here are all the ways I know of that people use to get around this.

Output the ClientID using <%= %>
I don’t like this because it can make my markup messy, requires server-side interaction and doesn’t work in external script files.

Output a JavaScript Object
I first saw this method on Rick Strahl’s blog. I don’t like this method because it generates even more script to send down the wire and it requires you to specify the IDs in multiple places (codebehind to include them in the object and again in the jQuery selector).

Select by Class Instead of ID
The purist in me doesn’t like using classes as IDs and in some situations, this will conflict with well-structured CSS. Also, selecting by class is slower than by ID (although this is improving in newer browsers).

Select using jQuery’s Attribute Selectors
I came across this method on StackOverflow (don’t recall which question though). It’s the best of the bunch as it is the most concise and doesn’t require any server-side preparation. Here’s an example:

$("*[id$='TextBox1']").show();

This looks for all elements (*) that have an ID that ends with the specified text. Since INamingContainer-generated IDs always end in the ID you specify in your ASPX markup, it works. It’s also a valid CSS3 selector so it’s consistent with the way jQuery works. Still feels like a kludge to me.

I decided to see if I could try to find a more readable selection method. This was my goal:

$("#TextBox1:asp").show();

After looking into jQuery’s custom selectors and stepping through a crap ton of the jQuery core code in Firebug, I don’t believe this is possible. After tweaking and playing, I got this working:

$(":asp('TextBox1')").show();

Here’s how it works. It’s actually pretty simple. Add jQuery to your page. Then add the following script, either inline or in an external file.

String.prototype.endsWith = function(str) {
    return (this.match(str + '$') == str)
}

jQuery.extend(
    jQuery.expr[":"],
    {
        asp: "jQuery(a).attr('id').endsWith(m[3]);"
    }
);

I’m not going to go into the details of how this all works. The first section adds an ‘endsWith’ method to strings for brevity’s sake. The second section extends the jQuery object with the custom selector. For more info on how that works, I would suggest reading the jQuery docs and this tutorial.

Whether or not this is better than an attribute selector is up to your readability preferences, but it’s another option you can use. If anyone knows how to access the part of a selector before the colon, I’d love to hear about it.

Posted September 29th, 10:20 PM
Read more posts about .NET, Tips, jQuery.

View Comments
Link