Newer posts are loading.
You are at the newest post.
Click here to check if anything new just came in.

April 25 2013

13:00

Build a Custom WordPress Home Page with Custom Widget Areas

In this video tutorial I will teach you how to build a WordPress website with custom widget areas the easy way. Even if you haven’t touched a single line of code before, this tutorial is simple enough to walk you through every code.

This tutorial goes hand in hand with Christian’s video tutorial on how to create a website from scratch using HTML5 and CSS3. The goal is to help you understand how easy it is to create the same layout (from Christian’s tutorial) in WordPress using custom widget areas. Although you will need to put the pieces together a little, it’s a fun and learning experience nonetheless!

Resources for this tutorial:

  • Canvas - (important) it’s a paid WordPress theme from WooThemes but the video tutorial below can still be applied using other themes.
  • Responsive Theme – a free alternative, really great theme with millions of downloads monthly.

Video Tutorial: Custom WordPress HomePage with Custom Widget Areas

Visit us at 1WD.tv for more learning materials that can get you started with freelancing as a web designer, inside you will get: an eBook, tons of video guides, source codes, child theme, forum support, and most of all the knowledge of how you can start earning $300 in just 3 Days!

presentation-2

January 03 2012

22:30

Create a Scalable Widget Using YUI3: Part 4

Welcome to the last part in the YUI3 widget tutorial; although we’ve actually finished building the widget, we’re going to look at how easy it is to add extra functionality to a widget without having to re-write it.

Let’s get started right away!

If the functionality is required for a particular module it’s an extension. Otherwise, it’s a plugin.

There are two ways of adding functionality – extensions and plugins. The difference between them is subtle but essentially boils down to whether or not the functionality is required or optional. If the functionality is required for a particular module it’s an extension, if the functionality is optional, it’s a plugin.

The plugin that we’ll add will handle the paging functionality for our widget; maybe not all developers will want to add paging, or some may want to add it to some instances of the widget but not others. Adding the functionality makes sense when viewed in this way – if the developer wants to make use of paging, they can use the plugin, but we don’t force developers to run all the extra code that is required if they aren’t going to use it.


Creating a Plugin

The process for creating a plugin is similar to that for creating a widget, so many of the constructs that we’ll use here should be familiar from the previous parts of this tutorial. Just like when creating a widget, we use YUI’s add() method as a wrapper for our code:

YUI.add("tweet-search-paging", function (Y) {

},
  • The name of the plugin (the name that developers will use to initialize the plugin) is the first argument of the method
  • an anonymous callback function is the second parameter. The function receives a reference to the current YUI instance.
  • the third argument is the version number of the plugin and
  • the fourth is an object listing any dependencies required by the plugin.

The Constructor and Namespace

Just like with our widget, we need to add a constructor for our plugin so that it can initialised and set the namespace for it. Unlike our plugin, setting the namespace is required. Add the following code within the anonymous function we just added:

var Node = Y.Node;

function TweetSearchPaging(config) {
    TweetSearchPaging.superclass.constructor.apply(this, arguments);
}

Y.namespace("Plugin.DW").TweetSearchPaging = TweetSearchPaging;

We start by caching references to any frequently used YUI resources, which in this case is just the Node utility. We add the constructor for the plugin in the same way as before; the TweetSearchPaging plugin method is defined as a function that accepts a configuration object. The class is initialized using the apply() method of the superclass’s constructor.

We set a namespace for our plugin, but this time the namespace is attached to the Plugin namespace as opposed to the YUI object.


Static Properties

As before there are some static properties we should set for our plugin, these are as follows:

TweetSearchPaging.NAME = "tweetsearch-paging";

TweetSearchPaging.NS = "paging";

TweetSearchPaging.ATTRS = {

    origShowUIValue: null,

    strings: {
        value: {
            nextLink: "Next Page",
            prevLink: "Previous Page"
        }
    }
};

TweetSearchPaging.PAGING_CLASS = Y.ClassNameManager.getClassName(TweetSearchPaging.NAME, "link");

TweetSearchPaging.LINK_TEMPLATE = "<a class={linkclass} href={url}>{linktext}</a>";

The name of the plugin is set with the NAME property, and also the NS property, which can be used to refer to the plugin from the host (the host is the widget or module that the plugin is connected to) class.

We can also use the ATTRS property to set any configuration attributes for the plugin. These attributes also use the YUI3 Attributes module, just like the widget attributes, and can be used in the same way. The attributes we define are the origShowUIValue attribute, which the plugin will set to store whether the search UI was initially displayed in the widget when the plugin is initialized. We also store the text strings used by the plugin, again for easy internationalization.

We manually generate a class name for the elements that we’ll create using the classNameManager, and define the template that our paging links will be created with. As there is only a single class name and template we don’t need to worry about using a for loop.


Extending the Plugin Base Class

Just like we did when creating the class for our widget, we use YUI’s extend() method to extend an underlying module. In the case of a plugin, it’s the Plugin.Base class that we’re extending. The extend() method should appear as follows:

Y.extend(TweetSearchPaging, Y.Plugin.Base, {

});

We pass in our plugin as the first argument to the extend() method, the class we’re extending as the second method and an object literal containing the functionality we’re adding.


Life Cycle Methods

Plugins also get access to several life-cycle methods that can be overridden to add custom code that the plugin will execute for us at appropriate times. We can make use of use the initializer and destructor life-cycle methods:

initializer: function () {

    Y.StyleSheet("tweetSearchPagingBase").set(".yui3-tweetsearch-paging-link", { float: "right" });

    if (Y.one(".yui3-skin-sam")) {
        Y.StyleSheet("tweetSearchPagingSkin").set(".yui3-skin-sam .yui3-tweetsearch-paging-link", { marginLeft: "2%" });
    }

    var widget = this.get("host");

    if (!widget.get("showUI")) {
        this.set("_origShowUIValue", false);
        widget.set("showUI", true);
    } else {
        this.set("_origShowUIValue", true);
    }

    this.afterHostEvent("tweetsChange", this._afterHostTweetsChange);
},

destructor: function () {
    Y.StyleSheet("tweetSearchPagingBase").unset(".yui3-tweetsearch-paging-link", "float");

    if (Y.one(".yui3-skin-sam")) {
        Y.StyleSheet("tweetSearchPagingSkin").unset(".yui3-skin-sam .yui3-tweetsearch-paging-link", "marginLeft");
    }

    if (!this.get("_origShowUIValue")) {
        this.get("host").set("showUI", false);
        Y.one(".yui3-tweetsearch-ui").remove();
    }
},

The initializer method will be executed when the plugin is initialized; in this method we first create the base style sheet our plugin needs. We could just include a separate CSS file, but as we only need a single style rule it makes sense to cut down on the number of files that any implementing developer needs to manage.

We use YUI’s StyleSheet() method to create our new style sheet. This method accepts a single argument which is the name of the new style sheet. We then use the set() method to set the styles that we require. The set() method takes two arguments; the first is the selector we wish to target and the second is an object literal containing the styles that should be applied to the selector, which in this case is simply float: right.

We then check whether the .yui3-sam-skin selector exists in the document; if it does, we then go ahead and create a skin style sheet for the plugin. If the sam skin is not in use, it is not worth creating any skin styles as the implementing developer will no doubt have custom styles that they may wish to apply.

Next, we need to check whether the showUI attribute of the widget is enabled. We can get access to the host class that the plugin is attached to using the built-in host attribute, which we get using the get() method just like any other attribute. The showUI attribute of the widget must be enabled if the plugin is used, so if the attribute is not set originally we set it here.

When using plugins we have the ability to detect and react to any of the host’s attributes changing. We add an attribute change-handler for the when the tweets attribute of our widget changes using the afterHostEvent() method. This method accepts two arguments; the first is the attribute to monitor, the second is the method to execute when the attribute changes.

The destructor function is called when the plugin is destroyed; this method is used to tidy up after the plugin. Any changes to the page should be reversed, as well as any changes we make to the widget. The changes we make to the page that we have to undo are the addition of the style sheets, so this is what we do first. The style sheet styles can be removed using the unset() method; this method takes the selector to unset as the first argument and the styles to unset as the second argument.

We then check whether the _origShowUiValue variable is set to true or false; if the variable is set to false we know we have to revert its value, so we set the attribute of the host back to false. If the value was changed and the UI was shown by the plugin, we hide it so that the widget is returned to its original state.


Attribute Change-Handlers

We only use a single attribute change-handling method in this plugin; the one that is called when the tweet attribute of the host changes. This method should appear as follows:

_afterHostTweetsChange: function () {

    var widget = this.get("host");

    if (widget.get("tweets").next_page) {
        var nextPageUrl = widget.get("tweets").next_page,
            nextLink = Node.create(Y.substitute(TweetSearchPaging.LINK_TEMPLATE, {
            linkclass: TweetSearchPaging.PAGING_CLASS, url: ["http://search.twitter.com/search.json", nextPageUrl, "&callback={callback}"].join(""), linktext: this.get("strings").nextLink }));

        if (this._nextLinkNode) {
            this._nextLinkNode.remove();
        }

        this._nextLinkNode = widget._uiNode.appendChild(nextLink);

        Y.on("click", Y.bind(this._getPage, this), this._nextLinkNode);
    }

    if (widget.get("tweets").previous_page) {
        var prevPageUrl = widget.get("tweets").previous_page,
            prevLink = Node.create(Y.substitute(TweetSearchPaging.LINK_TEMPLATE, {
            linkclass: TweetSearchPaging.PAGING_CLASS, url: ["http://search.twitter.com/search.json", prevPageUrl, "&callback={callback}"].join(""), linktext: this.get("strings").prevLink }));

        if (this._prevLinkNode) {
            this._prevLinkNode.remove();
        }
        this._prevLinkNode = widget._uiNode.appendChild(prevLink);
        Y.on("click", Y.bind(this._getPage, this), this._prevLinkNode);
    }
},

We first store a reference to the host class once more as we’ll need to refer to it several times. We now need to determine whether or not there are paged results in the response from Twitter and whether there are previous or next pages of results. The cool thing about the response from twitter is that it will automatically maintain which page of results we are viewing if there are more results than the configured number of results per page.

If there is another page of results after the current page, there will be a property in the JSON response object called next_page. Similarly, if there is a previous page of results, there will be a previous_page property. All we need to do is check for the presence of these properties and create next page and previous page links.

The links are created using the template we stored earlier in the plugin class, and are given the generated CLASS_NAME. The next_page and previous_page response objects are obtained from Twitter using a URL with a special ID in the query string. When we create these new nodes, the URL provided in these properties is added to each link respectively. The links are appended to the searchUI node of the host, and click handlers are added for them. These click handlers point to a utility method called _getPage(). We’ll add this method next.


Custom Prototype Methods

Just like when creating the widget, we can add any number of custom prototype methods that are used to execute any custom code required by our plugin in response to user interaction or state changes. In this plugin, we only need to add a single method, which should appear as follows:

_getPage: function (e) {
    var widget = this.get("host");

    e.preventDefault();

    widget._viewerNode.empty().hide();
    widget._loadingNode.show();

    widget.set("baseURL", e.target.get("href")),

    widget._retrieveTweets();

    Y.all(".yui3-tweetsearch-paging-link").remove();
}

First, we store a reference to the host class, and then prevent the paging link that was clicked being followed. We then remove any existing tweets in the widget’s viewer and show the loading node. Remember, each paging link (or whichever link exists if we are on the first or last page) will have the URL that retrieves the next (or previous) page of results, so we retrieve this URL from the link’s href and set the baseURL attribute of the widget. One this is done, we call the _retrieveTweets() method of our widget to request the next page. Finally, we remove the current paging links as they will be recreated if there are next or previous pages included in the new response object.


Using the Plugin

Now that we’ve created our plugin we can see how easy it is to use with our widget. We need to update our use() method to use our plugin, and call the plug() method before the widget is rendered:

YUI().use("tweet-search", "tweet-search-paging", function (Y) {
    var myTweetSearch = new Y.DW.TweetSearch({
        srcNode: "#ts"
    });
    myTweetSearch.plug(Y.Plugin.DW.TweetSearchPaging);
    myTweetSearch.render();
});

The plug() method connects our plugin, which is accessible via the Plugin namespace and whatever namespace we specified when defining the plugin’s class. Now when we run the page, we should have paging links at the bottom of the widget:

Nettuts+

One of the features of our plugin (just like our widget) is easy internationalization; in order to provide strings for the plugin in another language (or override any attributes if a plugin), we can simply provide the configuration object as the second argument to the plug() method, e.g.:

myTweetSearch.plug(Y.Plugin.DW.TweetSearchPaging, {
    strings: {
        nextLink: "Página Siguiente",
        prevLink: "Página Anterior"
    }
});

The paging link should now appear like so:

Nettuts+

Wrapping Up

In this part of the tutorial, we looked at how easy it is to create a plugin that can be used to enhance existing widgets or other modules. This is a great way of providing extra functionality that is not essential, which developers can choose to include if they wish. We saw that the structure of a plugin is similar to that of a widget on a smaller scale.

In this example, the plugin was very tightly coupled to our widget; it wouldn’t be possible to use the plugin with a different widget for example. This does not have to be the case and plugins, as well as extensions can be much more loosely coupled to add or enhance functionality for a range of different modules.

This now brings us to the end of the series on YUI3 widgets; I hope I’ve given some insight into the powerful mechanisms put in place by the library that enable us to easily create scalable and robust widgets that leverage the strengths of the library.

Let us know what you think in the comments section below and thank you so much for reading!


Sponsored post
feedback2020-admin
20:51

December 28 2011

22:00

Create a Scalable Widget Using YUI3: Part 3

In the last part of this series, we looked at the life-cycle methods, automatic methods and the custom methods that our widget requires or can make use of. In this part, we’re going to finish defining the widget’s class by adding the attribute change-handling methods that we attached in the bindUI() life-cycle method.

Let’s get started right away!


Attribute Change Handlers

The attribute change-handling group of methods are called when some of our attributes change values. We’ll start by adding the method that is called when the showTitle attribute changes; add the following code directly after the _uiSetTitle() method:

_afterShowTitleChange: function () {
    var contentBox = this.get("contentBox"),
        title = contentBox.one(".yui3-tweetsearch-title");

    if (title) {
        title.remove();
        this._titleNode = null;
    } else {
        this._createTitle();
    }
},

We first get a reference to the contentBox, and then use this to select the title node. Remember this is the container in which reside the title and subtitle in the header of the widget.

If the title node already exists, we remove it using YUI’s remove() method. We also set the _titleNode of the widget to null. If the node doesn’t exist, we simple call the _createTitle() method of our widget to generate and display it.

Next we can handle the showUI attribute changing:

_afterShowUIChange: function () {
    var contentBox = this.get("contentBox"),
        ui = contentBox.one(".yui3-tweetsearch-ui");

    if (ui) {
        ui.remove();
        this._uiNode = null;
    } else {
        this._createSearchUI();
    }
},

This method is almost identical to the last one — all that changes is that we are looking for the change of a different attribute, and either removing or creating a different group of elements. Again, we set the _uiNode property of our widget to null, so that the widget is aware of the latest state of its UI.

Our next method is called after the term attribute changes:

_afterTermChange: function () {
    this._viewerNode.empty().hide();
    this._loadingNode.show();

    this._retrieveTweets();
    if (this._titleNode) {
        this._uiSetTitle(this.get("term"));
	}
},

When the term attribute changes, we first remove any previous search results from the viewer by calling YUI’s (specifically the Node module’s) empty() method followed by the hide() method. We also show our loader node for some visual feedback that something is happening.

We then call our _retrieveTweets() method to initiate a new request to Twitter’s search API. This will trigger a cascade of additional methods to be called, that result ultimately in the viewer being updated with a new set of tweets. Finally, we check whether the widget currently has a _titleNode, and if so we call the _uiSetTitle() method in order to update the subtitle with the new search term.

Our last attribute change-handler is by far the largest and deals with the tweets attribute changes, which will occur as a result of the request to Twitter being made:

_afterTweetsChange: function () {
    var x,
        results = this.get("tweets").results,
        not = this.get("numberOfTweets"),
        limit = (not > results.length - 1) ? results.length : not;

    if (results.length) {

        for (x = 0; x < limit; x++) {
            var tweet = results[x],
                text = this._formatTweet(tweet.text),
                tweetNode = Node.create(Y.substitute(TweetSearch.TWEET_TEMPLATE, {
                    userurl: "http://twitter.com/" + tweet.from_user, avatar: tweet.profile_image_url,
                    username: tweet.from_user, text: text
                }));

            if (this.get("showUI") === false && x === limit - 1) {
                tweetNode.addClass("last");
            }
            this._viewerNode.appendChild(tweetNode);
        }

        this._loadingNode.hide();
        this._viewerNode.show();
    } else {
        var errorNode = Node.create(Y.substitute(TweetSearch.ERROR_TEMPLATE, {
	        errorclass: TweetSearch.ERROR_CLASS,
            message: this.get("strings").errorMsg
        }));

        this._viewerNode.appendChild(errorNode);
        this._loadingNode.hide();
        this._viewerNode.show();
    }
},

First up, we set the variables we’ll need within the method including a counter variable for use in the for loop, the results array from the response that is stored in the tweets attribute, the value of the numberOfTweets attribute and the limit, which is either the number of results in the results array, or the configured number of tweets if there are fewer items in the array than the number of tweets.

The remaining code for this method is encased within an if conditional which checks to see if there are actually results, which may not be the case if there were no tweets containing the search term. If there are results in the array, we iterate over each of them using a for loop. On each iteration, we get the current tweet and pass it to a _formatTweet() utility method that will add any links, usernames or hash tags found within the text, and then create a new node for the tweet using the same principles that we looked at in the last part of this tutorial.

When the searchUI is not visible, we should alter the styling of the widget slightly to prevent a double border at the bottom of the widget. We check whether the showUI attribute is set to false, and is the last tweet being processed, and if so add the class name last to the tweet using YUI’s addClass() method. We then add the newly created node to the viewer node to display it in the widget.

After the for loop has completed, we hide the loading node, which will at this point be visible having already been displayed earlier on, and then show the viewer node.

If the results array does not have a length, it means that the search did not return any results. In this case, we create an error node to display to the user and append it to the viewer node, then hide the loading node and show the viewer node as before.


A Final Utility Method

We’ve added all of the methods that support changing attribute values. At this point, we have just one further method to add; the _formatTweet() method that we reference from the within the for loop of the method we just added. This method is as follows:

_formatTweet: function (text) {

    var linkExpr = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig,
        atExpr = /(@[\w]+)/g,
        hashExpr = /[#]+[A-Za-z0-9-_]+/g,
        string = text.replace(linkExpr, function (match) {
            return match.link(match);
        });

    string = string.replace(atExpr, function (match) {
        return match.link("http://twitter.com/" + match.substring(1));
    });
    string = string.replace(hashExpr, function (match) {
        return match.link("http://twitter.com/search?q=" + encodeURI(match));
    });

    return string;
}

This method accepts a single argument, which is the text from the ‘current’ item of the results array that we want to linkify/atify/hashify. We start by defining three regular expressions, the first will match any links within the text that start with http, https or ftp and contain any characters that are allowed within URLs. The second will match any Twitter usernames (any strings that start with the @ symbol), and the last will match any strings that start with the # symbol.

We then set a variable called string which is used to contain the transformed text. First, we add the links. JavaScript’s replace() function accepts the regular expression for matching links as the first argument and a function as the second argument — the function will be executed each time a match is found and is passed the matching text as an argument. The function then returns the match having converted it to a link element using JavaScript’s link() function. This function accepts a URL that is used for the href of the resulting link. The matching text is used for the href.

We then use the replace() function on the string once again, but this time we pass in the @ matching regular expression as the first argument. This function works in the same way as before, but also adds Twitter’s URL to the start of the href that is used to wrap the matching text. The string variable is then operated on in the same way to match and convert any hashed words, but this time Twitter’s search API URL is used to create the link(s). After the text has been operated on, we return the resulting string.

This brings us to the end of our widget’s class; at this point we should have an almost fully functioning widget (we haven’t yet added the paging, this will be the subject of the next and final instalment in this series). We should be able to run the page and get results:

Nettuts+ Image

Styling the Widget

We should provide at least 2 style sheets for our widget; a base style sheet that contains the basic styles that the widget requires in order to display correctly, and a theme style sheet that controls how the widget appears visually. We’ll look at the base style sheet first; add the following code to a new file:

.yui3-tweetsearch-title { padding:1%; }
.yui3-tweetsearch-title h1, .yui3-tweetsearch-title h2 { margin:0; float:left; }
.yui3-tweetsearch-title h1 { padding-left:60px; margin-right:1%; background:url(/img/logo.png) no-repeat 0 50%; }
.yui3-tweetsearch-title h2 { padding-top:5px; float:right; font-size:100%; }
.yui3-tweetsearch-content { margin:1%; }
.yui3-tweetsearch-viewer article, .yui3-tweetsearch-ui { padding:1%; }
.yui3-tweetsearch-viewer img { width:48px; height:48px; margin-right:1%; float:left; }
.yui3-tweetsearch-viewer h1 { margin:0; }
.yui3-tweetsearch-label { margin-right:1%; }
.yui3-tweetsearch-input { padding:0 0 .3%; margin-right:.5%; }
.yui3-tweetsearch-title:after, .yui3-tweetsearch-viewer article:after,
.yui3-tweetsearch-ui:after { content:""; display:block; height:0; visibility:hidden; clear:both; }

Save this style sheet as tweet-search-base.css in the css folder. As you can see, we target all of the elements within the widget using the class names we generated in part one. There may be multiple instances of the widget on a single page and we don’t want our styles to affect any other elements on the page outside of our widget, so using class names in this way is really the only reliable solution.

The styling has been kept as light as possible, using only the barest necessary styles. The widget has no fixed width and uses percentages for things like padding and margins so that it can be put into any sized container by the implementing developer.

Next, we can add the skin file; add the following code in another new file:

.yui3-skin-sam .yui3-tweetsearch-content { border:1px solid #A3A3A3; border-radius:7px; }
.yui3-skin-sam .yui3-tweetsearch-title { border-bottom:1px solid #A3A3A3; border-top:1px solid #fff; background-color:#EDF5FF; }
.yui3-skin-sam .yui3-tweetsearch-title span { color:#EB8C28; }
.yui3-skin-sam .yui3-tweetsearch-loader, .yui3-skin-sam .yui3-tweetsearch-error { padding-top:9%; margin:2% 0; color:#EB8C28; font-weight:bold; text-align:center; background:url(/img/ajax-loader.gif) no-repeat 50% 0; }
.yui3-skin-sam .yui3-tweetsearch-error { background-image:url(/img/error.png); }
.yui3-skin-sam .yui3-tweetsearch article { border-bottom:1px solid #A3A3A3; border-top:2px solid #fff; background:#f9f9f9; background:-moz-linear-gradient(top, #f9f9f9 0%, #f3f3f3 100%, #ffffff 100%); background:-webkit-gradient(linear, left top, left bottom, color-stop(0%,#f9f9f9), color-stop(100%,#f3f3f3), color-stop(100%,#ffffff)); background:-webkit-linear-gradient(top, #f9f9f9 0%,#f3f3f3 100%,#ffffff 100%); background:-o-linear-gradient(top, #f9f9f9 0%,#f3f3f3 100%,#ffffff 100%); background:-ms-linear-gradient(top, #f9f9f9 0%,#f3f3f3 100%,#ffffff 100%); background:linear-gradient(top, #f9f9f9 0%,#f3f3f3 100%,#ffffff 100%); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f9f9f9', endColorstr='#ffffff',GradientType=0); }
.yui3-skin-sam .yui3-tweetsearch article.last { border-bottom:none; }
.yui3-skin-sam .yui3-tweetsearch a { color:#356DE4; }
.yui3-skin-sam .yui3-tweetsearch a:hover { color:#EB8C28; }
.yui3-skin-sam .yui3-tweetsearch-ui { border-top:1px solid #fff; background-color:#EDF5FF; }

Save this file as tweet-search-skin.css in the css folder. Although we also use our generated class names here, each rule is prefixed with the yui3-skin-sam class name so that the rules are only applied when the default Sam theme is in use. This makes it very easy for the overall look of the widget to be changed. This does mean however that the implementing developer will need to add the yui3-skin-sam class name to an element on the page, usually the , but this is likely to be in use already if other modules of the library are being used.

Like before, we’ve added quite light styling, although we do have a little more freedom of expression with a skin file, hence the subtle niceties such as the rounded-corners and css-gradients. We should also recommended that the css-reset, css-fonts and css-base YUI style sheets are also used when implementing our widget, as doing so is part of the reason the custom style sheets used by the widget are nice and small.


Implementing the Widget

Our work as widget builders is complete (for now), but we should spend a little while looking at how the widget is actually used. Create the following HTML page in your text editor:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>YUI3 Twitter Search Client</title>
        <link rel="stylesheet" href="http://yui.yahooapis.com/combo?3.4.1/build/cssreset/cssreset-min.css&3.4.1/build/cssfonts/cssfonts-min.css&3.4.1/build/cssbase/cssbase-min.css">
        <link rel="stylesheet" href="css/tweet-search-base.css" />
        <link rel="stylesheet" href="css/tweet-search-skin.css" />
    </head>
    <body class="yui3-skin-sam">
        <div id="ts"></div>
        <script src="//yui.yahooapis.com/3.4.1/build/yui/yui-min.js"></script>
        <script src="js/tweet-search.js"></script>
        <script>
            YUI().use("tweet-search", function (Y) {
                var myTweetSearch = new Y.DW.TweetSearch({
                    srcNode: "#ts"
                });
                myTweetSearch.render();
            });
        </script>
    </body>
</html>

The only YUI script file we need to link to is the YUI seed file which sets up the YUI global object and loads the required modules.

Save this file in the root project directory. First of all we link to the CDN hosted YUI reset, base and fonts combined style sheet, as well as our two custom style sheets that we just created. We also add the yui3-skin-sam class name to the of the page to pick up the theme styling for our widget. On the page, we add a container for our widget and give it an id attribute for easy selecting.

The only YUI script file we need to link to is the YUI seed file; this file sets up the YUI global object and contains the YUI loader which dynamically loads the modules required by the page. We also link to our plugin’s script file, of course.

Within the final script element we instantiate the YUI global object and call the use() method specifying our widget’s name (not the static NAME used internally by our widget, but the name specified in the add() method of our widget’s class wrapper) as the first argument.

Each YUI instance is a self-contained sandbox in which only the named modules are accessible.

The second argument is an anonymous function in which the initialisation code for our widget is added. This function accepts a single argument which refers to the current YUI instance. We can use any number of YUI objects on the page, each with its own modules. Each YUI instance is a self-contained sandbox in which only the named modules (and their dependencies) are accessible. This means we can have any number of self-contained blocks of code, all independent from each other on the same page.

Within the callback function, we create a new instance of our widget stored in a variable. Our widget’s constructor is available via the namespace we specified in the widget’s class, which is attached to the YUI instance as a property. Our widget’s constructor accepts a configuration object as an argument; we use this to specify the container that we want to render our widget into, in this case the empty

we added to the page. The specified element will become the contentBox of our widget. Finally, we call the render() method on the variable our widget instance is stored in, which renders the HTML for our widget into the specified container.

In the configuration object, we can override any of the default attributes of our widget, so if we wanted to disable the title of the widget and the search UI, we could pass the following configuration object into our widget’s constructor:

{
    srcNode: "#ts",
    showTitle: false,
    showUI: false
}

I mentioned in an earlier part of the widget that by including all of the text strings used by the widget in an attribute, we could easily enable extremely easy internationalization. To render the widget in Spanish, for example, all we need to do is override the strings attribute, like this:

{
    srcNode: "#ts",
    strings: {
        title: "Twitter Search Widget",
        subTitle: "Mostrando resultados de:",
        label: "Término de búsqueda",
        button: "Búsqueda",
        errorMsg: "Lo siento, ese término de búsqueda no ha obtenido ningún resultado. Por favor, intente un término diferente"
    }
}

Now when we run the widget, all of the visible text (aside from the tweets of course) for the widget is in Spanish:

Nettuts+ Image

Summary

In this part of the tutorial, we completed our widget by adding the attribute change-handling methods and a small utility method for formatting the flat text of each tweet into mark-up. We also looked at the styling required by our widget and how the styles should be categorized, i.e. whether they are base styles or skin styles.

We also saw how easy it is to initialise and configure the widget and how it can easily be converted into display in another language. In the next part of this tutorial, we’ll look at a close relative to the widget – the plugin and add a paging feature to our widget.


December 21 2011

04:17

Create a Scalable Widget Using YUI3: Part 2

In part one of this series, we reviewed some of the necessary constructs to use when creating a widget with YUI3. We looked at the static properties we needed to set, the class constructor and namespacing, and briefly looked at the extend() method.

In this part of the tutorial, we’ll review the prototype methods we can override or create in order to make our widget function.

Before we begin, let’s just remind ourselves of the method now, as this method houses all the code below:

TweetSearch = Y.extend(TweetSearch, Y.Widget, {

});

The third argument is what we are interested in, in this part of the tutorial. All of the functionality we add that is specific to our widget will be within functions that are added as values to different properties of the object passed to the extend() method. Some of these methods are added automatically for us –we just need to override them with custom functionality. We’ll look at these methods first.


Lifecycle Methods

Several methods executed at different points in the widget instances life cycle. The first of these is an initializer method (remember to add this code within the extend() method shown above):

initializer: function () {
    this._retrieveTweets();
},

The underscore convention to indicate the method should be treated as private, and not called directly by any implementing developer.

The initializer method is provided to allow us to do any tasks that are required as soon as the widget is initialized. Within any prototype methods we attach to our widget, whether inherited or created ourselves, the value of this is set to the widget instance.

All our widget needs to do at this point is retrieve the search results from Twitter. We package this up as a separate function (which we’ll look at in more detail a little later), instead of just retrieving the results directly within initializer so that we can reuse the functionality and retrieve search results any time we wish. The _retrieveTweets() method uses the underscore convention to indicate the method should be treated as private, and not called directly by any implementing developer. It can be called directly of course, but may result in weirdness.

The next life-cycle method inherited from Widget is renderUI(), which we can use to perform any necessary setup, the creation and insertion of new elements, etc, our widget requires. Add this code directly after that shown above:

renderUI: function () {
    var contentBox = this.get("contentBox"),
        strings = this.get("strings"),
        viewer = Node.create(Y.substitute(TweetSearch.VIEWER_TEMPLATE, { viewerclass: TweetSearch.VIEWER_CLASS })),
        loadingNode = Node.create(Y.substitute(TweetSearch.LOADER_TEMPLATE, { loaderclass: TweetSearch.LOADER_CLASS }));

    if (this.get("showTitle")) {
        this._createTitle();
    }
    this._loadingNode = contentBox.appendChild(loadingNode);
    this._viewerNode = contentBox.appendChild(viewer);

    if (this.get("showUI")) {
        this._createSearchUI();
    }

    contentBox.addClass("yui3-widget-content");
},

When a widget is initialised, YUI will automatically create a wrapper element for the element that was passed to the constructor.

Within the renderUI() method, we first store a reference to the contentBox attribute of the widget. The contentBox represents the inner container of the widget and is one of the attributes automatically inherited from Widget, like the srcNode attribute that we saw briefly in part 1. When a widget is initialised, YUI will automatically create a wrapper element for the element that was passed to the constructor, with the inner element becoming the contentBox. The wrapper is known as the bounding box (available as the boundingBox attribute).

We also get a reference to the strings attribute that contains the localizable strings used by elements created by the widget. We then create two new elements; the viewer which will be used to contain the list of tweets returned by Twitter’s search API, and a loading element that will be displayed while the request is in progress.

We use the create() method of the YUI Node module to create our new elements. This element can accept the string representation of an element, which it will then create. Instead of passing it a string directly however, we use YUI’s substitute() method to replace the tokenised templates that we created in part one of this tutorial.

The substitute() method takes two arguments;

  • the first is the string to perform substitution on.
  • the second is an object whose keys map directly to the tokens within the string.

The values of each property are swapped into the string, so for example, our viewer template will be stored like this:

"<div class={viewerclass}></div>"

The object passed as the second argument to the substitute() method used to create the viewer node contains a key called viewerclass, so the value of this key will be swapped with the matching token in the source string. In this case, we use the stored class name as the substitution, so the viewer will be given the class name yui3-tweetsearch-viewer (the class names were all created and stored on our widget instance in part one).

We then check whether the showTitle attribute of our widget is set to true, which it is by default, but may be disabled by the implementing developer. If the attribute is set to true we call the custom (i.e. not inherited) _createTitle() method. The reason we package this up as a separate unit of code, instead of just creating the widget is because the showTitle attribute may be set at any time by someone implementing our widget, so it can’t just reside within a life-cycle method. We will look at our custom methods in detail after looking at the inherited life-cycle methods.

After we do or do not (depending on configuration) create the title node, we then insert the new elements into the DOM by adding them as child nodes of the contentBox. Note that we also store the new elements on the widget instance so that we can easily refer to them later on.

We then check whether the showUI attribute is enabled (again, it is by default, but it could be changed in the configuration), and if so call the _createSearchUI() method. This is a separate method for the same reason as last time – so that it can be reused throughout the widget instance’s life.

Finally, we add the class name yui3-widget-content to the contentBox. This isn’t strictly necessary, as the implementing developer may not be using any of the YUI’s style sheets (base, fonts, reset, etc), but as the class name isn’t added for us automatically, we should include in case the developer does wish to pick up some of the styling provided by the library.

The final life-cycle method we are going to use is bindUI(), which allows us to hook up any handlers that should be called when an attribute changes value, or an event occurs. Add the following code directly after the renderUI() method:

bindUI: function () {
    if (this.get("showUI")) {

        Y.on("click", Y.bind(this._setTerm, this), this._buttonNode);
        this.after("termChange", this._afterTermChange);
    }

    this.after("showTitleChange", this._afterShowTitleChange);
    this.after("showUIChange", this._afterShowUIChange);
    this.after("tweetsChange", this._afterTweetsChange);
},

The first thing we do is check whether the showUI attribute is enabled; if it has been disabled we don’t need to worry about adding event handlers for it. If it is enabled, we use YUI’s on() method to add a click-handler bound to the custom _setTerm() method. We ensure the widget instance remains bound to the this keyword within the event handler by passing this (which at this point refers to the widget instance) as the second argument to the bind() method.

We also use the after() method that is automatically attached to our widget instance by the library to add a listener that reacts to the term attribute changing. A listener can be bound to any of our custom attributes by simply suffixing After to any attribute name. The term attribute will only change if the search UI is enabled. We then add listeners for each of the other attributes we need to monitor; showTitle, showUI and tweets, hooking these up with the relevant handlers.

Note: There is another life cycle method provided by the Widget class, but in this particular example we don’t need to make use of it. This method is the destructor, which will be called just before the widget is destroyed. It is used to tidy up after the widget, but only needs to be used if elements are added to the DOM outside of the boundingBox (the outer wrapper) of the widget.


Automated Prototype Methods

Remember the validator we specified as part of the ATTRS object in the first part of this tutorial? The method that we set as the value of this property will be called automatically whenever an attempt is made to update the attribute. Let’s take a look at it now; add the following code directly after bindUI():

_validateTerm: function (val) {
    return val !== this.get("term");
},

The method must return true or false and automatically receives the new value (that is, the value that may become the new value if it passes validation) as the first argument; if true is returned, the attribute is updated with the new value, if false is returned the attribute is not updated.

The logic we supply is pretty simple in this example – we simply check that the new value is not the same as the old value. There’s no point after all in making another AJAX call only to receive exactly the same set of results.


Non-Inherited Prototype Methods

Next we can start adding our custom methods that will add more functionality to our widget. The first function we referenced within the initializer method was _retrieveTweets(), so we’ll look at that first:

_retrieveTweets: function () {
    var that = this,
        url = [this.get("baseURL"), "&q=", encodeURI(this.get("term")), "&rpp=", this.get("numberOfTweets")].join(""),
        handler = function (data) {
        that.set("tweets", data);
    },
    request = new Y.JSONPRequest(url, handler);

    request.send();
},

We first set a few variables; the this keyword will no longer point to our widget instance inside the success callback that we’ll specify when we make the request to Twitter, so we store a reference to this in a variable called that, as convention dictates.

We also create the request URL; we retrieve the baseURL, the term and the numberOfTweets attributes, storing each as an item in an array and then using JavaScript’s join() function to concatenate them all into a string. Using an array and the join() method is way faster than concatenating strings with the + operator.

Next we define our success callback; all this simple function needs to do is set the widget’s tweets attribute to the response received from the request. The response will be automatically passed to the callback function.

The last variable we define is for the request itself, which is initialised using YUI’s JSONPRequest() method. This method accepts two arguments; the first is the URL to make the request to and the second is the callback function to invoke on success. Finally, to initiate the request we simply call the send() method.

Our next custom method is _createTitle(), which we call from the renderUI() method:

_createTitle: function () {
    var strings = this.get("strings"),
        titleNode = Node.create(Y.substitute(TweetSearch.TITLE_TEMPLATE, {
	        titleclass: TweetSearch.TITLE_CLASS,
            title: strings.title,
            subtitle: strings.subTitle,
            term: this.get("term")
        }));

    this._titleNode = this.get("contentBox").prepend(titleNode);
},

We also store a reference to the strings attribute for use within the function. A title is created using the same principles as before, although this time we have a few more tokens to replace in our substitute() method. This method is only called if the showTitle attribute is set to true. Note that the get() method is chainable, so we can call the prepend() method to insert the title directly after it.

The code here is very similar to what has been used before, as is the case for our next method, _createSearchUI():

_createSearchUI: function () {

        var contentBox = this.get("contentBox"),
            strings = this.get("strings"),
            ui = Node.create(Y.substitute(TweetSearch.UI_TEMPLATE, { uiclass: TweetSearch.UI_CLASS })),
            label = Node.create(Y.substitute(TweetSearch.LABEL_TEMPLATE, { labelclass: TweetSearch.LABEL_CLASS, labeltext: strings.label })),
            input = Node.create(Y.substitute(TweetSearch.INPUT_TEMPLATE, { inputclass: TweetSearch.INPUT_CLASS })),
            button = Node.create(Y.substitute(TweetSearch.BUTTON_TEMPLATE, { buttonclass: TweetSearch.BUTTON_CLASS, buttontext: strings.button }));

        this._uiNode = ui;

        this._labelNode = this._uiNode.appendChild(label);
        this._inputNode = this._uiNode.appendChild(input);
        this._buttonNode = this._uiNode.appendChild(button);

        this._uiNode.appendTo(contentBox);
    },

Again, very similar to what we have seen before. Remember, the only reason this is in a separate function is so that the UI can be switched on or off at any point during the widget’s life-cycle. This method is only called if the showUI attribute is set to true.

Next up is the _setTerm() method, which is called by the event listener attached to the _buttonNode when the button is clicked:

_setTerm: function () {
        this.set("term", this._inputNode.get("value"));
    },

In this simple method, we just try to set the term attribute to the string entered into the <input>. In trying to set the attribute, our validator will be called and will only update the attribute if the value is different to the attribute’s current value.

The last of our custom methods is another simple method used to update the subtitle in the header of the widget to the new search term; add the following code:

_uiSetTitle: function (val) {
        this._titleNode.one("h2 span").setContent(val);
        },

This method will receive the new value as an argument (we’ll call this method manually from an attribute change-handling method that we’ll look at in the next part of this series). We call YUI’s one() method on our title node to select the <span> within the subtitle, and then use the setContent() method to update its inner-text.


Summary

In this part of the tutorial, we first looked at the life-cycle methods that we get as a result of extending the Widget superclass. These methods are called automatically for us by the library at different points in the widget’s life-cycle.

Although the methods that we’ve added all look similar in structure, there are distinctions between the two; for example, the life-cycle methods receive more ‘protection’ than those methods we add ourselves, hence why these methods are not prefixed with an underscore. These methods, unlike our custom ones, can’t be called directly by the implementing developer.

We also took a look at a validator method; these methods will also be called by the library automatically when appropriate which makes them incredibly useful for ensuring data is in a particular format, or meets a particular requirement before an attribute is updated.

Lastly, we looked at the custom prototype methods that we need in order to make our widget function. We saw that we can easily use the built-in get() and set() methods to get and set attributes, and that within each method the this keyword Is helpfully set to our widget’s instance, so that we can easily obtain and manipulate different aspects of the widget.

In the next part of this tutorial, we’ll look at the attribute change-handling methods that need to be added in order to make our widget respond to user interaction or changes in the page’s state. We can also look at the CSS we need to provide for our widget, and how the widget is initialized and used.

If you have any questions, please let me know in the comments section below. Thank you so much for reading!


December 13 2011

21:30

Create a Scalable Widget Using YUI3: Part 1

In this tutorial, we’re going to look at how easy it is to create scalable, robust and portable widgets using the latest version of the Yahoo User Interface library. YUI3 provides a Widget class for us to extend in order to create widgets in a consistent way that leverage the power of the library.

The widget that we’ll create today is a Twitter search client that will query Twitter’s search API and consume the JSON response in order to display tweets that contain the configured search term. We can also add additional functionality such as allowing the visitor to choose another term and do a new search, and viewing paged results. Join me after the jump!


Getting Started

All required YUI modules will be retrieved dynamically when the page running our widget is loade

We’ll need the usual css, img and js folders created within a project folder for us to store our various resources in. The images our widget will use can be found in the code download. We don’t need to worry about downloading a copy of the YUI library itself as all required YUI modules will be retrieved dynamically when the page running our widget is loaded (we’ll look at this in more detail later).


The Widget Wrapper

Create a new script file and add to it the following code:

YUI.add("tweet-search", function (Y) {

}, "0.0.1", { requires: ["widget", "substitute", "jsonp"] });

This is the outer wrapper for our widget; all of the code we write will reside within the function passed as the second argument to YUI’s add() method. The add() method of the YUI object allows us to add a new module to the library, which could be a simple function or class, a widget, an extension or a plugin.

  • The first argument we provide is the name of our widget. This name is used in the use() method when implementing the widget.
  • The second argument is an anonymous function that is used to define the widget’s class. The argument accepted by this function is the instance of YUI that the widget is attached to.
  • The third argument is used to specify the version number of the widget.
  • The fourth and final argument is an object that we can use to supply additional configuration for the widget.

In this example, we use the requires property to specify an array of other YUI components that are required for our widget to function. There are other properties that can be used here, but they aren’t required for this example.

As you can see, one of the required components is the Widget component. When creating a custom widget the Widget component of the library should be extended to make use of the powerful constructs that Widget sets up. We also use the Substitute component for doing some simple string substitution when building the required HTML elements, and the JSONP component in order to interact with Twitter’s search API.


Top Level Variables, the Constructor and Namespacing

Now we can begin adding some of the variables our widget will require, as well as adding the class constructor and namespace. Add the following code within the anonymous function:

var Node = Y.Node,
    getClassName = Y.ClassNameManager.getClassName,
    i, j,
    baseClasses = ["_CLASS", "title", "loader", "viewer", "tweet", "ui", "label", "input", "button", "error"],
    templates = ["_TEMPLATE", "&lt;hgroup class={titleclass}>&lt;h1>{title}&lt;/h1>&lt;h2>{subtitle}&lt;span>{term}&lt;/span>&lt;/h2>&lt;/hgroup>", "&lt;div class={loaderclass}>loading...&lt;/div>", "&lt;div class={viewerclass}>&lt;/div>", "&lt;article>&lt;a href={userurl} title={username}>&lt;img src={avatar} alt={username} />&lt;h1>{username}&lt;/h1>&lt;/a>&lt;p>{text}&lt;/p>&lt;/article>", "&lt;div class={uiclass}>&lt;/div>", "&lt;label class={labelclass}>{labeltext}&lt;/label>", "&lt;input class={inputclass} />", "&lt;button class={buttonclass}>{buttontext}&lt;/button>", "&lt;p class={errorclass}>{message}&lt;/p>"];

function TweetSearch(config) {
    TweetSearch.superclass.constructor.apply(this, arguments);
}

Y.namespace("DW").TweetSearch = TweetSearch;

The name of our widget has the first letter of its name capitalized, as is the convention for naming constructors.

First up, we cache references to the Y.Node component and the Y.ClassNameManager.getClassName() method as we’ll be using these frequently. We also define a couple of variables for use in the for loop, and create two new arrays; the first containing a series of strings that will form part of the class names added to the HTML elements our widget will create, and the second containing the HTML templates, also in string format, that will be used to create the elements themselves.

Next we add the constructor function for our widget; this is the function that developers implementing our widget will call. The function can accept a single argument which will take the form of an object that sets configuration attributes exposed by our widget. The name of our widget has the first letter of its name capitalized, as is the convention for naming constructors. Within this function our widget’s class is initialised by using the apply() method of the superclass's (Widget) constructor. The value of this is set to our widget instance.

We can also create a namespace for our widget using YUI’s namespace() method; this isn’t mandatory but it is a very good practice to run code within a namespace in order to minimize the possibility of naming collisions when code is used in the wild. The namespace() method accepts a string which represents the namespace, to which is attached the widget name as a property and the widget as the value.

I’ve set the namespace to equal my initials but this can be anything you require; you may already have a namespace that all of your web apps reside in, or it could be the name of your company, the name of your client, or anything else that makes sense. This widget will be accessible via Y.DW.TweetSearch


Static Properties

Next, we can define the static constants required when extending the Widget class. Add the following code directly after the namespace() method:

TweetSearch.NAME = "tweetsearch";

for (i = 1, j = baseClasses.length; i < j; i++) {
    var current = baseClasses[i].toUpperCase(),
        prop1 = current + baseClasses[0],
        prop2 = current + templates[0];

    TweetSearch[prop1] = getClassName(TweetSearch.NAME, baseClasses[i]);
    TweetSearch[prop2] = templates[i];
}

First, we set the NAME property of our widget; the all-caps naming convention here signifies a value that will be constant throughout the life-cycle of our widget instance. The name we set is used by the widget as a prefix when firing events and creating class names for HTML elements.

Next is the for loop we use to add the required class names and mark-up templates to our widget. We initialize the i and j variables that we declare at the top of the function; the i variable that is used as the counter is initially set to 1 instead of 0 as would usually be the case (you’ll see why in just a moment) and the j variable is set to the length of our baseClasses array (the baseClasses and templates arrays are both the same length as every element we create is given a class name. This may not always be the case).

Within the loop we cache a reference to the current item from the baseClasses array and in upper case, and then create two new strings called prop1 and prop2. These strings consist of the variable we just created and the first item in our baseClasses array, so on the first iteration for example, this string will equal TITLE_CLASS for prop1 and TITLE_TEMPLATE for prop2.

We then add these new properties to our widget instance; the first property is set to the result of calling the getClassName() method (remember, we’re using the cached short-cut we created earlier which points to Y.ClassNameManager.getClassName). We pass in the name of our widget as the first argument to this method, and the current item from the baseClasses array. This will result in generated class names such as yui3-tweetsearch-title, available fom the TweetSearch.TITLE_CLASS property for example.

The second property we add is the current item from the templates array. Continuing with the title example this gives us a property name of TweetSearch.TITLE_TEMPLATE with a value of <hgroup class={titleclass}><h1>{title}</h1><h2>{subtitle} <span>{term}</span></h2></hgroup>. The purpose of the for loop is simply so that we don’t have to attach all of the classes and templates to our instance manually.


Configurable Attributes with Sensible Defaults

Now we can define the configurable attributes that our widget will have, which will enable developers implementing the widget to enable or disable different features. Add the following code directly after the for loop:

TweetSearch.ATTRS = {
    term: {
        value: "yui3",
        validator: "_validateTerm"
    },
    numberOfTweets: {
        value: 5
    },
    baseURL: {
        value: "http://search.twitter.com/search.json?&with_twitter_user_id=true&include_entities=true&callback={callback}"
    },
    tweets: {
        value: null
    },
    showTitle: {
        value: true
    },
    showUI: {
        value: true
    },

    strings: {
        value: {
            title: "Twitter Search Widget",
            subTitle: "Showing results for:",
            label: "Search Term",
            button: "Search",
		errorMsg: "I'm sorry, that search term did not return any results. Please try a different term"
        }
    }
};

The YUI library adds a consistent way to add attributes to any class or module.

The ATTRS constant is used to store the configurable attributes that the implementing developer can set when creating an instance of the widget. The YUI library adds a consistent way to add attributes to any class or module, and this mechanism is automatically available when extending Widget.

Instead of setting the value of each attribute to a simple native value like a sting or a Boolean, an object is used. The default for each attribute is set using the value property of this object. In the first attribute, we also make use of the validator property, which allows us to specify a function that will be automatically called whenever the value is updated. This enables us to check that the value is in a particular format, or matches other custom criteria. There are also a range of other properties we can set for each attribute including; custom get and set methods, whether the attribute is read-only, and more.

The attributes used by our widget include the search term, the number of tweets to display, the baseURL of the request sent to Twitter, whether to show a title for the widget and whether to show the search UI. There are a number of other attributes our widget will automatically get, and which we can use. We’ll look at these in more detail later on in the tutorial.

The final attribute we define is the strings attribute, which is available to all modules that subclass Widget. The value of this attribute is also an object and within this we add all of the text strings that our widget will display. Using an attribute to define any words that the widget needs to display in this way makes our widget super easy to internationalize; implementing developers need only to override the strings attribute with their own collection of strings in whichever language they choose.


Built-in Support for Progressive Enhancement

The Widget superclass furnishes us with the HTML_PARSER static property that can retrieve values from any HTML elements that are present within the widget’s container and use these values as attributes, which makes it incredibly easy for us to create widgets that transform underlying mark-up into something more functional and/or pretty.

We don’t really need to worry about this for our widget; if JavaScript is disabled, no AJAX request will be made to Twitter’s search API and there will be no data to display in any case. However, they give implementing developers more ways of instantiating the widget and configuring attributes, we can make the provision that if a text <input> is present within the widget’s container, the value of the field will be used as the search term instead of the default attribute value. In order to retrieve this value we can make use of the HTML_PARSER; add the following code directly after the ATTRS definition:

TweetSearch.HTML_PARSER = {
    term: function (srcNode) {
        var input = srcNode.one("input");

        if (input) {
            var val = input.get("value");
                input.remove();
            }

            return val;
        }
    };

The HTML_PARSER property is an object literal where each property within this object maps directly to an attribute. The only attribute that we wish to add progressive-enhancement support for is the term attribute, the value of which is set to a functional that will automatically be called when our widget is initialized.

This function receives a single argument which is a referece to the srcNode attribute. This is one of the built-in attributes that all widgets automatically get access to and refers explicitly to the element that was passed into the constructor for our widget. This element becomes the content box for the widget.

The first thing we do is try to select an <input> element from the srcNode using YUI’s one() method, which selects a single matching element from the DOM. If an element is retrieved, we store its value in a variable called val, and then remove the element from the page (we’ll create an alternative <input> element for when the search UI is enabled later). We then return val. If val is not set, i.e. if there wasn’t an <input> in the srcNode, underfined will be returned, and the term attribute will stay set to its configured value. If val does contain a value, it will become the value for the term attribute.


Extending the Widget Class

Before we end this part of the tutorial, we’ll take a look at the method we use to extend the Widget class with the functionality specific to our widget. This method will form the bulk of our widget. Directly after the HTML_PARSER add the following:

TweetSearch = Y.extend(TweetSearch, Y.Widget, {

});

The YUI extend() method takes three arguments:

  • The first is the object to extend, which in this example is our widget’s class.
  • The second argument is the object we are extending, in this case the Widget class.
  • The third argument is an object containing prototype methods to add or override to our widget. The object passed as the third argument will be the wrapper for the remainder of our code, which we’ll come to in the next part of this tutorial.

Save this file in the js folder as tweet-search.js.


Summary

In this part of the tutorial we setup some of the required scaffolding for our new widget. Although the widget won’t actually do anything at this stage, it can still be initialised and we can see some of the container that is automatically added by the YUI library, and look in the DOM tab of Firebug to see the attributes it has inherited.

After defining some top-level variables, we first saw how to define the constructor function for our widget so that the widget can be initialized by the library, as well as seeing how easy it is to namespace our widget. We then looked at the static constants that are inherited from the underlying Widget class that we are extending. These included the NAME of the widget, the _CLASS and _TEMPLATE collections and the ATTRS object, the latter of which allowed us to set the attributes that an implementing developer can override if they so wish.

We also looked momentarily at the extend() method which is used to add the prototype methods to our widget’s class in order to implement that custom functionality it provides. This custom functionality will be the subject of the next part of this tutorial.

Stay tuned and thank you so much for reading!


August 25 2011

13:36

How to Create an Advanced Twitter Widget

As of March 2011, the current average number of “Tweets” the world sends per day is 140 million. This tutorial demonstrates how to create a custom Twitter widget from scratch that uses the Twitter API, Web Intents and @Anywhere. Using the Twitter platform to create a fun, interactive tool for your website can be beneficial both in the learning and promotion departments!


A Few Pros and Cons

Pros:

  • Greater Customization than the default Twitter widget.
  • Cache file prevents problems with Twitter API Rate Limiting and over capacity issues

Cons:

  • Requires Twitter oAuth if you want to incorporate the advanced functionality of @anywhere
  • Setting up a Cron Job can be tricky (there are many web hosting server configurations and control panels)

Now, lets get started!


Step 1. Markup and Styling

This tutorial assumes that you have some CSS and HTML knowledge, and starts out with the basic markup and styling for the widget. Note that the content of the widget is located in <div id="tweet"></div> and is created using jQuery, Twitter API and PHP.

The HTML

<!-- Begin Content -->
<div id="content">

<!-- Begin Twitter Feed Area  -->
<div class="twitterfeed">

<img src="images/twitter_bird.png" width="99" height="75" alt="Follow Nettuts+ and Tuts+ on Twitter" />

<h3>Follow <a href="http://twitter.com/envatowebdev">Nettuts+</a> and <a href="http://twitter.com/tutsplus">Tuts+</a> on Twitter</h3>

<div id="tweet"></div>

</div>
<!-- End Twitter Feed Area  -->

</div>
<!-- End Content -->

The CSS

#content .twitterfeed {
  float: left;
  background-color: #fff;
  width: 385px;
  margin: 10px 0px 30px 10px;
  border: 1px solid #d8d8d8;
  padding: 5px 5px 15px 5px;
}

#content img  {
  margin: 5px 0px 0px 10px;
}

#content h3 {
  float: right;
  color: #000;
  background-color: #f2f2f2;
  font-size: 18px;
  font-weight: bold;
  width: 235px;
  padding: 10px 5px 10px 10px;
  margin: 10px 10px 25px 0px;
  text-shadow: 0 1px 0 #fff;
  border: 1px solid #d8d8d8;
}

#content h3 a {
  text-decoration: none;
}

#content hr {
  width: 90%;
  height: 1px;
  background: #dfdfdf;
  display: block;
  border: none;
  margin: 20px 0px 20px 18px;
}

#tweet  {
  float: none;
  clear: both;
}

#tweet p {
  margin: 15px 15px 10px 15px;
}

#web_intent {
  margin: 0px 0px 0px 15px;
}

#web_intent a:link, #web_intent a:visited, #web_intent a:active {
  margin: 0px 10px 0px 0px;
}

#web_intent img {
  margin: 0px 3px 0px 0px;
  position: relative;
  top: 2px;
}

#web_intent .time {
  margin: 0px 10px 0px 0px;
}

#follow-envatowebdev  {
  margin: 0px 10px 0px 75px;
}

.log-button {
  margin: 10px 10px 0px 115px;
}

The #content ID may need to be changed based on your theme and placement of the widget (Ex. #content, #sidebar, #footer)


Step 2. PHP “Cache File” Script

As of this writing, the Twitter limit for Unauthenticated API calls is 150 requests per hour. OAuth calls are permitted at 350 requests per hour and are measured against the oauth_token used in the request.

To ensure that our custom Twitter widget does not run into problems with Twitter API rate limiting, a PHP “cache file” script needs to be created. This script retreives the Twitter API “GET statuses/user_timeline” information and stores it in a “TXT” file located in the cache directory on your server.

If you do not already have a cache directory, you will need to create one. You will also need to create a directory on your server for the PHP cache file script.

Create a PHP file that contains the following code.

<?php

$cache = dirname(__FILE__) . '/../cache/twitter-json.txt';

$data = file_get_contents('http://api.twitter.com/1/statuses/user_timeline/twitter_screen_name.json?count=3&include_rts=true&include_entities=true'); 

$cachefile = fopen($cache, 'wb');
fwrite($cachefile,utf8_encode($data));
fclose($cachefile);

?>

The “count=3″ portion of the code controls how many “tweets” will be displayed in your Twitter widget. If you want to display only one tweet, then change the code to “count=1″.

Note that the "&include_rts=true" needs to be included if you want your Twitter widget to display “retweets”. Visit the Twitter API doumentation “GET statuses/user_timeline” for more information and available options.


Step 3. Set Up Cron Job

Next, a Cron job needs to be created that executes the PHP script at a set number of times per hour. How the Cron job is set up depends on your web hosting control panel. Please refer to “Scheduling Tasks with Cron Jobs” for an in depth tutorial.

Below is an example of Cron tab settings for a Plesk control panel that runs a Cron job (4) times per hour (every fifteen minutes), every day.

  • Minute = */15
  • Hour = *
  • Day of the Month = *
  • Month = *
  • Day of the Week = *
  • Command = /usr/bin/curl -s http://your_domain.com/twitter-widget/twitter-cron.php > /dev/null

Step 4. Add jQuery and Twitter Web Intents Script Tags

We need to make sure that our web page references the jQuery library. For this tutorial, the jQuery library is included through the use of the jQuery CDN.

Between the head tags of your web page add

<script src="http://code.jquery.com/jquery-1.6.2.min.js"></script>

In addition to using the Twitter API, the Twitter widget uses Twitter Web Intents to add “Retweet”, “Reply” and “Favorite” functionality.

Just below the jQuery script tag, add

<script src="http://platform.twitter.com/widgets.js"></script>

Step 5. The jQuery Code

An external JavaScript file, “twitter-widget.js,” needs to be created. This JavaScript file contains the jQuery code that generates the “content” of the Twitter widget. The jQuery code retreives the Twitter API information from the cache file that was created by the PHP script in Step two.

In the JS file, add the following jQuery code.

// Display Latest Tweet

$(document).ready(function(){

$.getJSON('cache/twitter-json.txt', function(data){
        $.each(data, function(index, item){
                $('#tweet').append('<div><p>' + item.text.linkify() + '</p></div>' + '<div id="web_intent">' + '<span class="time">' + relative_time(item.created_at) + '</span>' + '<img src="images/retweet_mini.png" width="16" height="16" alt="Retweet">' + '<a href="http://twitter.com/intent/retweet?tweet_id=' + item.id_str + '">' + 'Retweet</a>' + '<img src="images/reply_mini.png" width="16" height="16" alt="Reply">' + '<a href="http://twitter.com/intent/tweet?in_reply_to=' + item.id_str + '">' + 'Reply</a>' + '<img src="images/favorite_mini.png" width="16" height="16" alt="Favorite">' + '<a href="http://twitter.com/intent/favorite?tweet_id=' + item.id_str + '">' + 'Favorite</a>' + '</div>' + '<hr />');
    });
});

jQuery .getJSON() is used to load the JSON-encoded data from the cache TXT File. Using jQuery .append(), the content is displayed through the <div id=”tweet”></div> of the XHTML markup. Each Twitter API item is prefixed with “item.”. The Twitter API items used in the jQuery code are:

  • text (item.text) – The text of each tweet
  • created_at (item.created_at) – The date each tweet was posted
  • id_str (item.id_str) – The unique ID number of each tweet

Editor’s Note: If you’d prefer to get that muddy HTML out of your JavaScript, consider using the jQuery templating plugin.


Step 6. Converting Twitter Time Stamp to Relative Time (Time Ago)

The data that is retrieved using the Twitter API is “raw” data and needs to be fixed up for the Twitter widget.

In the twitter-widget.js file, below the jQuery code, add the following JavaScript code.

// Convert Twitter API Timestamp to "Time Ago"

function relative_time(time_value) {
  var values = time_value.split(" ");
  time_value = values[1] + " " + values[2] + ", " + values[5] + " " + values[3];
  var parsed_date = Date.parse(time_value);
  var relative_to = (arguments.length > 1) ? arguments[1] : new Date();
  var delta = parseInt((relative_to.getTime() - parsed_date) / 1000);
  delta = delta + (relative_to.getTimezoneOffset() * 60);

  var r = '';
  if (delta < 60) {
        r = 'a minute ago';
  } else if(delta < 120) {
        r = 'couple of minutes ago';
  } else if(delta < (45*60)) {
        r = (parseInt(delta / 60)).toString() + ' minutes ago';
  } else if(delta < (90*60)) {
        r = 'an hour ago';
  } else if(delta < (24*60*60)) {
        r = '' + (parseInt(delta / 3600)).toString() + ' hours ago';
  } else if(delta < (48*60*60)) {
        r = '1 day ago';
  } else {
        r = (parseInt(delta / 86400)).toString() + ' days ago';
  }

      return r;
  }

});

This code converts the Twitter Time stamp to relative time, such as “3 hours ago.”


Step 7. Creating Workable Anchor Links

In the twitter-widget.js file, below the convert timestamp code, add the following JavaScript code.

// Create Usable Links
String.prototype.linkify = function() {
       return this.replace(/[A-Za-z]+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&\?\/.=]+/, function(m) {
              return m.link(m);
      });
};

When using the Twitter API, the links contained in Twitter posts are displayed in text format only, not workable anchor links. The above code converts standard URL text only links into workable anchor links.


Step 8. Adding Advanced Features Using Twitter @anywhere

We have now created a rather neat, custom Twitter widget that includes some basic user interactivity. But what if we want to add really cool, advanced functionality to our Twitter widget? Thanks to the Twitter @anywhere Service, we can accomplish this fairly easily.

Sign up for the Twitter @anywhere Service at https://dev.twitter.com/apps/new.

Create a New “App” and complete the steps of the online registration process. Visit this article for detailed help on completing the registration process.

“The callback URL property (Set in your @anywhere account) of your application must match both the subdomain and domain of the web application utilizing @Anywhere. ”

The next few steps of this tutorial explain how to add Twitter @anywhere advanced functionality.


Step 9. Add @anywhere Script Tag

As close to the top of the web page as possible, add the @anywhere script Tag:

<script src="http://platform.twitter.com/anywhere.js?id=Your_Consumer_Key&v=1"></script>

Note that your Twitter oAuth Consumer Key needs to be added to the script tag.


Step 10. User Login and Authorization

Certain features of the Twitter @anywhere service do not require the user to be logged into Twitter. Hover Cards and Auto-linkification of Twitter usernames as examples. However, most @anywhere features DO require the user to login and authorize your site for access. The “Connect with Twitter” button allows users to authenticate securely with Twitter.

Below <div id=”tweet”></div>, add the following code.

<div class="log-button">
<span id="login-logout"></span>
<script src="js/twitter-login.js"></script>
</div>

Now, create an external JavaScript file “twitter-login.js” containing the following code.

<script>
      jQuery(function () {
        twttr.anywhere(function (T) {
          if (T.isConnected()) {
            $("#login-logout").append('<button id="signout" type="button">Sign out of Twitter</button>');
            $("#signout").bind("click", function () {
              twttr.anywhere.signOut();
          });
        }
          else {
            T("#login-logout").connectButton();
          }
        });
      });
</script>

Step 11. Determining the Connected State

@anywhere provides several ways of determining whether a user is already logged into Twitter and has already granted your application authorization for access. For this tutorial, we are going to use authComplete and signOut events.

Just below the web page <body> tag, add the following:

<script>
    twttr.anywhere(function (T) {
       T.bind("authComplete", function (e, user) {
          // triggered when auth completed successfully
        });

     T.bind("signOut", function (e) {
          // triggered when user logs out
        });
     });
</script>

By adding the above code, the authComplete and signOut events are now triggered by ANY @anywhere functionality.


Step 12. Adding Hovercard Functionality

A Hovercard displays data about a Twitter user in a tooltip and also includes a follow/unfollow button.

To add a hovercard that is “expanded” by default, add the following code below the authComplete and signOut code, just below the web page <body> tag:

<script type="text/javascript">
    twttr.anywhere(function (T) {
     T("#tweet").hovercards({ expanded: true });
     });
</script>

Note that if you add Hovercard functionality, the Auto-linkification of Twitter usernames is also added by default. This makes adding the "T.linkifyUsers()" function to your webpage unnecessary.


Step 13. Adding a Follow Button

To add a Twitter follow button to the widget, below <div id="tweet"></div>, add the following code.

<span id="follow-twitter_screen_name"></span>
<script type="text/javascript">
   twttr.anywhere(function (T) {
       T('#follow-twitter_screen_name').followButton("twitter_screen_name");
     });
</script>

Note that you need to add your Twitter screen name to the above code.


Step 14. Adding a Tweet Box

If you would like to add a Tweet Box instead of a follow button, below <div id=”tweet”></div> add the following code.

<div id="tbox"></div>
<script type="text/javascript">
   twttr.anywhere(function (T) {
      T("#tbox").tweetBox({
          height: 90,
          width: 350,
        });
     });
</script>

All Done!

View the completed demo. Creating a Twitter driven widget from scratch that uses the Twitter API, Web Intents and @anywhere takes a bit of work. However, you can create something interactive and fun for visitors of your website!

Please note that the JavaScript codes for Relative Time and Creating Workable Anchor Links are thanks to this article.

For those of you who would rather create a basic Twitter driven widget without using Twitter oAuth and @anywhere, there are basic demos included in the source files zip. There are also two demos that display Twitter profile images next to each tweet.

If you have any questions about this tutorial, please ask them using the “Add a Comment Box” below.


June 01 2010

03:38

How To Organize Yourself Using Opera With Agenda

I have just finished writing about an Opera widget which Google users will appreciate. Now, on my path to discover great widgets for Opera, I have found yet another tool that will help anybody who likes to keep a virtual diary.

This tool is an Opera widget appropriately titled Agenda.

Agenda is our very own virtual diary. We can use it as an event reminder, alarm clock, and to-do list. After installing the widget, we need to open it from the widgets sidebar.

Agenda

This is what Agenda will look like the first time you open it:

Opened

You can see that the form is that of a physical binder, with a page on each side representing today’s dates. We can see the yellow notepads where it is possible to store whatever notes we want to save.

Notes

There are sticky notes and can be moved around on the page.

Main

The left page is where we can add events. First we use the calendar to highlight a date.

events

Today’s date is highlighted blue. Our selected date is the left one of the two rectangles. This date then becomes our page on the left.

added

Clicking on “Add Event” pops up a window with numerous options.

write

By default, the event we can add is Note. To add something else to the date, we click on the Note button and view our options.

option

Our options appear in a sideways menu on top. Starting from the left, these options are Note, Event, Meeting, Birthday, Anniversary, and a Yearly event.

Choosing an event enables the Alarm box.

Alarm

Speaking of Alarm, we can choose for it ring on our event or an a specific time interval from right now.

time

A maximum of a 255 character length note can be added to each event. Saving an event adds it to the page on the left (the one we had highlighted).

highlight

In front of the time, we can see our note. At the end of the note we see two icons. The first one lets us delete the event and the second one signifies that an alarm is added to the event.

Agenda works in its own window so that you do not need to have Opera open to use Agenda.

Combining all these features of Agenda, we can easily use it as our virtual planner / diary. It will definitely be a useful widget for countless Opera users.

Brought To You By

Premier Survey
Do you want to advertise here? Click to get more info…

April 28 2010

17:00

How to Build a Simple Twitter Widget with ASP.NET

In this tutorial, I’ll be walking you through how to a write a Twitter widget for ASP.NET in the form of a reusable server control complete with nice things such as automatically turning URLs into links, and caching to speed up page load times.


Step 1 Getting Started

To follow this tutorial, all you need is Visual Studio (You can use MonoDevelop if you’re not on Windows, although there’s no guarantees there.) If you don’t want to fork over cash for the full version of Visual Studio, you can grab the free Express Edition.

You’ll also need knowledge of C# 3.0, as this tutorial makes use of some of the newer features of the language, such as lambda expressions and the var keyword.


Step 2 Creating the Control

ASP.NET includes a handy feature known as Server Controls. These are custom tags that aim to help developers structure their code. When a page using a server control is requested, the ASP.NET runtime executes the Render() method and includes the output in the final page.

Once you’ve created a new Web Application in Visual Studio, right click in the Solution Explorer and add a new item to the solution. Select ASP.NET Server Control, and give it a name. Here, I’ve called it Twidget.cs, but you’re welcome to call it whatever you like. Paste the following code in, and don’t worry if it all looks a bit foreign – I’ll explain it all shortly.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.Script.Serialization;
using System.Net;

namespace WebApplication1
{
    public class Twidget : Control
    {
        public string Account { get; set; }
        public int Tweets { get; set; }

        protected override void Render(HtmlTextWriter writer)
        {
            writer.Write("<ul>");

            foreach (var t in GetTweets().Take(Tweets))
                writer.Write("<li>{0}</li>", HttpUtility.HtmlEncode(t));

            writer.Write("</ul>");
        }

        public List<string> GetTweets()
        {
            var ls = new List<string>();

            var jss = new JavaScriptSerializer();
            var d = jss.Deserialize<List<Dictionary<string, object>>>(
                new WebClient()
                .DownloadString("http://api.twitter.com/1/statuses/user_timeline.json?screen_name=" + Account)
                );

            foreach (var x in d)
                ls.Add((string)x["text"]);

            return ls;
        }
    }
}

This is about as basic as you can get for a Twitter widget. Here’s how it works:

When a user requests a page with this control on it, the Render() method gets executed with a HtmlTextWriter passed as a parameter. It writes out the <ul> tag, and then enters a loop which prints out each tweet as a list item. The magic here happens in the GetTweets() method. Notice how we are using the Take() extension method to make sure we only print the amount of tweets that we’re asked to.

Once execution passes to the GetTweets() method, we setup a List>string< to hold our tweets and a JavaScriptSerializer to parse the JSON from the Twitter API servers. The statement on lines 31 – 34 (split up for readability) retrives the user timeline in JSON format, then deserializes into .NET types we can work with. On line 36, we loop through all the tweets and add them one by one to the tweet list. We have to manually cast x["text"] to a string because we deserialized it as an object. We had to do this, because the JSON returned by the Twitter API uses a smorgasboard of different types – which is fine for JavaScript, but a little tricky with C#.


Step 3 Using the Control

Now we have the code for our Twitter widget; let’s put it to use! Open your Default.aspx page (or whichever page you want to use this in) and put the following code immediately after the <%@ Page %> directive:

<%@ Register TagPrefix="widgets" Namespace="WebApplication1" Assembly="WebApplication1" %>

Feel free to change the TagPrefix to whatever you like, but make sure that the Namespace attribute is correctly set to whatever namespace you placed the widget code in, and ensure that the Assembly attribute is set to the name of your web application (in our case, WebApplication1).

Once you’ve registered the proper tag prefix (and you’ll need to do this for every page you want to use the control on), you can start using it. Paste the following code somewhere into your page, and once again, feel free to change the attributes to whatever you want:

<widgets:Twidget runat="server" Account="twitter" Tweets="10" />

If you’ve done everything properly, you should see a page similar to this when you run your web application:


Step 4 Some Fancy Stuff…

You’ve got to admit, the control we’ve got at the moment is pretty rudimentary. It doesn’t have to be though, so let’s spiffy it up a little by turning URLs into nice, clickable links for your visitors.

Find the foreach loop in the Render() method and scrap it completely. Replace it with this:

// you'll need to add this using directive to the top of the file:
using System.Text.RegularExpressions;

foreach (var t in GetTweets().Take(Tweets))
{
    string s = Regex.Replace(
        HttpUtility.HtmlEncode(t),
        @"[a-z]+://[^\s]+",
        x => "<a href='" + x.Value.Replace("'", "&quot;") + "'>" + x.Value + "</a>",
        RegexOptions.Compiled | RegexOptions.IgnoreCase
        );

    writer.Write("<li>{0}</li>", s);
}

It’s all pretty much the same code, except for the humongous call to Regex.Replace() on line 6. I’ll explain what this does.

The first parameter is the input, or the string that the Regex works on. In this case, it’s just the tweet text after being passed through HttpUtility.HtmlEncode() so we don’t fall victim to a vicious XSS attack. The input is then matched against the second parameter which is a regular expression designed to match a URL.

The third parameter is where it gets a little involved. This is a lambda expression, a feature new to C# 3. It’s basically a very short way of writing a method like this:

public static string SomeFunction(Match x)
{
    return "<a href='" + x.Value.Replace("'", "&quot;") + "'>" + x.Value + "</a>";
}

All it does is wrap the URL with an <a> tag, of which all quotation marks in the URL are replace with the HTML entity &quot;, which helps prevent XSS attacks. The fourth and final parameter is just an ORed together pair of flags adjusting the way our regex behaves.

The output of the control after making this adjustment is somewhat similar to the screenshot below.


Step 5 Caching

There’s a big problem with the code I’ve given to you above, and that is that it doesn’t cache the response from the Twitter API. This means that every time someone loads your page, the server has to make a request to the Twitter API and wait for a response. This can slow down your page load time dramatically and can also leave you even more vulnerable to a Denial of Service attack. Thankfully, we can work around all this by implementing a cache.

Although the basic structure of the control’s code remains after implementing caching, there’s too many small changes to list, so I’ll give you the full source and then – as usual – explain how it works.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.Script.Serialization;
using System.Net;
using System.Threading;
using System.Text.RegularExpressions;

namespace WebApplication1
{
    public class Twidget : Control
    {
        public string Account { get; set; }
        public int Tweets { get; set; }
        public int CacheTTL { get; set; }

        static Dictionary<string, CachedData<List<string>>> Cache = new Dictionary<string, CachedData<List<string>>>();

        protected override void Render(HtmlTextWriter writer)
        {
            writer.Write("<ul>");

            foreach (var t in GetTweets().Take(Tweets))
            {
                string s = Regex.Replace(
                    HttpUtility.HtmlEncode(t),
                    @"[a-z]+://[^\s]+",
                    x => "<a href='" + x.Value.Replace("'", """) + "'>" + x.Value + "</a>",
                    RegexOptions.Compiled | RegexOptions.IgnoreCase
                    );

                writer.Write("<li>{0}</li>", s);
            }

            writer.Write("</ul>");
        }

        public List<string> GetTweets()
        {
            if (!Cache.Keys.Contains(Account)
                || (DateTime.Now - Cache[Account].Time).TotalSeconds > CacheTTL
                )
                new Thread(Update).Start(Account);

            if (!Cache.Keys.Contains(Account))
                return new List<string>();

            return Cache[Account].Data;
        }

        public static void Update(object acc)
        {
            try
            {
                string Account = (string)acc;

                var ls = new List<string>();

                var jss = new JavaScriptSerializer();
                var d = jss.Deserialize<List<Dictionary<string, object>>>(
                    new WebClient()
                    .DownloadString("http://api.twitter.com/1/statuses/user_timeline.json?screen_name=" + Account)
                    );

                foreach (var x in d)
                    ls.Add((string)x["text"]);

                if (!Cache.Keys.Contains(Account))
                    Cache.Add(Account, new CachedData<List<string>>());

                Cache[Account].Data = ls;
            }
            catch (Exception) { }
        }
    }

    class CachedData<T>
    {
        public DateTime Time { get; private set; }

        T data;
        public T Data {
            get {
                return data;
            }
            set
            {
                Time = DateTime.Now;
                data = value;
            }
        }
    }
}

As you can see, the Render() method remains unchanged, but there’s some pretty drastic changes everywhere else. We’ve changed the GetTweets() method, added a new property (CacheTTL), added a private static field (Cache), and there’s even a whole new class – CachedData.

The GetTweets() method is no longer responsible for talking to the API. Instead, it just returns the data already sitting in the cache. If it detects that the requested Twitter account hasn’t been cached yet, or is out of date (you can specify how long it takes for the cache to expire in the CacheTTL attribute of the control), it will spawn a seperate thread to asynchronously update the tweet cache. Note that the entire body of the Update() method is enclosed in a try/catch block, as although an exception in the Page thread just displays an error message to the user, if an exception occurs in another thread, it will unwind all the way back up the stack and eventually crash the entire worker process responsible for serving your web application.

The tweet cache is implemented as a Dictionary<string, CachedData<string>>, where the username of the twitter account being cached is the key, and an instance of the CachedData<T> class is the value. The CachedData<T> class is a generic class and can therefore be used with any type (although in our case, we’re only using it to cache a string.) It has two public properties, Data, which uses the data field behind the scenes, and Time, which is updated to the current time whenever the Data property is set.

You can use the following code in your page to use this caching version of the widget. Note that the new CacheTTL attribute sets the expiry (in seconds) of the tweet cache.

<widgets:Twidget runat="server" Account="twitter" Tweets="10" CacheTTL="600" />

Conclusion

I hope this tutorial has not only taught you how to make a Twitter widget, but has given you an insight into how server controls work as well as some best practices when ‘mashing up’ data from external sources. I realise that the browser output of this control isn’t exactly the prettiest, but I felt that styling it and making it look pretty was outside the scope of the article and has therefore been left as an exercise for the reader. Thanks for reading! Feel free to ask any questions that you might have in the comments section below.


Older posts are this way If this message doesn't go away, click anywhere on the page to continue loading posts.
Could not load more posts
Maybe Soup is currently being updated? I'll try again automatically in a few seconds...
Just a second, loading more posts...
You've reached the end.
(PRO)
No Soup for you

Don't be the product, buy the product!

close
YES, I want to SOUP ●UP for ...