How to get on the jQuery IRC channel

Tuesday, 8 December 2009

View Comments

This article looks at how to connect to the jQuery IRC channel via your web browser.


To connect is straight forward, you can go here, this will display the following:



Just enter any nickname and #jQuery as the channel and you are in.

The jQuery IRC channel is helpful if you want to get something answered quickly, there's plenty of people hanging around who could help, but... make sure you ask something specific and make sure you ask something relevant and you will be sure to get some help.
more...

Cool transitioniong effect between images using jQuery animate

This tutorial looks at using jQuery animate to transition from one image to another. jQuery's built in animate function provides a method for easily animating styles such as height, color and position. I will show you a method of toggling backwards an forwards between two images by changing on images dimensions to match the others and by fading one image out to reveal the other.


You can view a demo of this functionality here.

You can download the code for this tutorial here.



Each of the steps should work simultaneously to give a smooth transition from one image to the other. The current image will gradually change width/height while fading out to reveal the other image. The functionality will allow transistioning backwards and forwards between the two images.

This functionality is a great example of jQuery's animate function and could be applied in a variety of ways. You could extend it to cycle through several images, use it in an online catalog for different views of an item or to make an interesting photo album.

In our HTML we have a div marked with class .fade_switch, inside that are the two images that will be used to transition between:

<div class='fade_switch' style='display:block;position:relative;'>
 <img src='./images/green_present.png' />
 <img src='./images/zim.jpg'/>
</div>

This is all the markup that is needed, now let's look at our javascript:

$("document").ready(function() {
 $(".fade_switch img").each(function() {
  var img = new Image;
  img.src = this.src;
  var my_parent = $(this).parent(".fade_switch");
  
  var my_index = $(my_parent).children().index(this);
  
  var myself = $(this);
  
  $(img).load(function() {
   $(myself).data("start_w", img.width);
   $(myself).data("start_h", img.height);
   if (my_index == 0) {
    $(my_parent).css({"width":img.width, "height":img.height});
   } else {
    $(myself).hide();
   }
  });
  
  $(this).css({"position":"absolute", "top":"0px", "left":"0px"});
 });

We begin by looping over each image belonging to a .fade_switch div. We preload the image and after each image has loaded we store its dimensions by using the data function, this will be used later when we animate back to its original size (this is required by IE).

Next we check to see if it is the first image of the two, if so we set the parent .fade_switch element to the size of the image. We then hide the other image.

We finish by setting each image to position absolute, so one image sits in front of the other.

Then we have the click function for each .fade_switch div:

$(".fade_switch").click(function() {
  if (!$(this).is(':animated')) {
   var vis_img = $(this).children(":visible");
   var hid_img = $(this).children(":hidden");
   
   $(vis_img).css("z-Index", "100");
   
   var vis_width = $(vis_img).data("start_w");
   var vis_height = $(vis_img).data("start_h");
   
   var hid_width = $(hid_img).data("start_w");
   var hid_height = $(hid_img).data("start_h");
   
   $(hid_img).css({"z-Index":"1","width":vis_width,"height":vis_height}).show();
   
   
   
   $(vis_img).animate({
    "opacity" : "0",
    "width" : hid_width,
    "height" : hid_height
   }, function() {
    $(this).css("z-Index", "1").hide().css("opacity", "100");
   }); 

   $(hid_img).animate({
    "width" : hid_width,
    "height" : hid_height
   }, function() {
    $(this).css("z-Index", "100");
   });
   
   $(this).animate({
    "width" : hid_width,
    "height" : hid_height
   });
  }
 });

We first check to see that this element is not being currently animated, this is done to stop multiple clicks from screwing things up mid transition. This is done using the :animated selector: if (!$(this).is(':animated')) {

We then retrieve the two elements seperately according to whether they are visible or hidden. We set the visible image to have a zIndex of 100 so it sits on top.

Next we grab the dimensions of each of the images and set the hidden image to have the same dimensions as the visible on, but with zIndex set to 1, then we set it show (it will not appear as it sits behind the other image since it has a zIndex of one and both are absolutely positioned).

Once this is done we begin the animation. We animate the top image to opacity 0, and to shrink or grow to the behind image. Once it has completely faded and changed its dimensions we set it to zIndex 1 and hide it. We animate the back image to its original width and height and set it to zIndex 100 and we also make sure the parent div also has matching width and height.

If you have any great transitioning tricks or want to recommend any good tutorials, share them in the comments below.
more...

Accessing draggable elements from ui.droppable drop function

This post looks at the ui.droppable drop function. We will look at how to access the droppable, the element that has been dropped onto the droppable and how to access it's methods.


In most cases the droppable views the item being dropped as a draggable, whether it is a ui.sortable or a ui.draggable. To get some understanding of how this works, I came across this section of the jQuery UI Development and Planning wiki: "The droppable implementation comes with a global drag & drop manager called $.ui.ddmanager. It tracks the current draggable, has a list of all droppables at all times and its 'drag' function is called during dragging of the draggable (a 'drop' function on mouseup). It then forwards to the actual widget functions of the current intersecting droppable.". This is important to understand as when we access the element after it is dropped it is referenced via ui.draggable.

Let's take a look at the droppable API and how it explains the drop function for the ui.droppable:

This event is triggered when an accepted draggable is dropped 'over' (within the tolerance of) this droppable. In the callback, $(this) represents the droppable the draggable is dropped on. ui.draggable represents the draggable.

Code examples:
Supply a callback function to handle the drop event as an init option.

    $('.selector').droppable({
       drop: function(event, ui) { ... }
    });

So our drop function has the following signature function(event, ui) and we know that $(this) represents the droppable and ui.draggable represents the draggable.

So regardless of whether it is a ui.sortable list item or a ui.draggable being dropped on a droppable we can access the element in the following way:
$("#target").droppable({
 drop: function(event, ui) { 
  var element = $(ui.draggable);
  var DOMelement = $(ui.draggable)[0];
 }
});

You can then manipulate the item in anyway that you like using javascript or jQuery as per usual.

For a standard UI widget when looking at the file you will notice there are two types of functions; those defined with an underscore such as _clear and _trigger in ui.draggable.js and those that don't have an underscore, such as serialize and toArray in ui.sortable.js. Those with an underscore are like privately scoped functions, you cannot call them outside of the UI widget, the others you can and it is done like this. $("#my_sortable_ul").sortable("toArray");. There are also getters and setters available, examples of which are usually available in the API such as ui.droppable addClasses option which can be retrieved as such:
//getter
var addClasses = $('.selector').droppable('option', 'addClasses');
//setter
$('.selector').droppable('option', 'addClasses', false);

So now that we know how to target our element in the drop function and we know which functions are public and how to use setters and getters here is an example of how we can call those functions within the ui.droppable drop function. Note: The sortable and draggable are different in that for sortable list items it is the parent who is the ui.sortable, whereas with the draggable it is the element itself that is the ui.draggable.

// Calling functions on a ui.sortable

drop: function(event, ui) { 
 var array_values = $(ui.draggable).parent().sortable("toArray");
}

// Calling functions on a ui.draggable

drop: function(event, ui) { 
 $(ui.draggable).draggable("destroy");
}

// Accessing getter on a ui.sortable

drop: function(event, ui) { 
 var this_opacity = $(ui.draggable).parent().sortable('option', 'opacity');
}

// Accessing getter on a ui.draggable

drop: function(event, ui) { 
 var this_cursor = $(ui.draggable).draggable('option', 'cursor');
}


With a sortable once it has been dropped it may be also usefull to access the siblings of the element dropped:

// Set the background color on all of the siblings of the dropped element to red

drop: function(event, ui) { 
 $(ui.draggable).siblings().css("background", "red");
}

Using andSelf you can manipulate the siblings as well as the dropped element:

// Set the text color of all of the elements including the dropped element to blue

drop: function(event, ui) { 
 $(ui.draggable).siblings().andSelf().css("color", "blue");
}

jQuery widgets are extremely well designed and have layers of complexity that require a bit of digging. The are very simple to use, but with a bit of research you can get a great amount out of them. Hopefully this tutorial will help you understand UI widgets in a bit more detail.
more...

Using jQuery with forms - part 5 (Traversing)

Monday, 7 December 2009

View Comments

This is the fifth in a series of tutorials on using jQuery to manipulate forms. In this series I will be looking at parts of the jQuery API reference in detail. In this tutorial I will be looking at forms and jQuery Traversing.

Filtering


eq(index)

Matches the element from a group at a particular index

// Set the second radio element with name foods to checked
$("#my_form radio[name='foods']:eq(2)").attr('checked', 'checked');

filter(expr)

Removes elements from a group that do not match the expression

// Set all of the input elements that have the class error and out_of_range to red
$("#my_form :input").filter('.error, .out_of_range').css('color', 'red');

filter(fn)

Removes elements from a group that do not match the supplied function, useful if you want to remove elements based on a complex function

// Set all of the input elements that have the class error and out_of_range to red
$("#my_form :input").filter(function(index) {
 if ((index > 6) || ($(this).attr("type") == "hidden)) {
  return true;
 }
}).css('color', 'red');

is(expr)

Used to check if one of the matched elements matches the expression

// Checks to see if any of the password elements has the error class

if ($("#my_form :password").is('.error')) {
 alert('A password field is still in error');
}

map(callback)

Takes a set of elements and maps each one onto the callback

// This is a very useful function but takes some understanding, rather than looking at the function below I suggest you check out this page for practical uses of this function.

// Map all of the options from #myselect to the $(this).val() function, get the actual DOM elements and join them with a comma
alert($("#myselect option").map(function() {return $(this).val();}).get().join(', '));

not(expr)

Removes elements that do not match the expression from the set

// Get all of the input elements and remove those that are not inputs and then apply a grey background
$("#my_form :input").not(":password").css('background', 'grey');

slice(start, end)

Selects a subset of the elements based on start and end index

// Hide all of the radio elements with name foods except the first and last element
$("radio[name='foods']").slice(1, ($("radio[name='blah']").size() - 1)).hide();

Finding


add(expr)

Adds a set of elements to an existing group

// Set all of the select elements to red border then set all of the select and text elements to color blue
// This could be useful if you wanted to show which elements had been validated as well as those that are in error
$(":select").css('border', '1px solid red').add(":text").css('color', 'blue');

children(expr)

Selects all the children of an element and optionaly filters them according to the expression

// Retrieve all of the select children elements of my form that have the class validated
$("#my_form :select").children(".validated");

closest(expr)

Closest works by first looking at the current element to see if it matches the specified expression, if so it just returns the element itself. If it doesn't match then it will continue to traverse up the document, parent by parent, until an element is found that matches the specified expression. If no matching element is found then none will be returned.

// If a button is clicked it checks to see if it has the disabled class, if not it traverses up the document to find the closest element with the disabled class and then adds the matched class
$("button").click(function(e) {
  $(e.target).closest(".disabled").addClass("matched");
});

find(expr)

Searches for decendant elements that match the given expression

// Apply a yellow background to all p elements that are descendants of the .login_entry fieldset

$(":fieldset .login_entry").find("p").css('background', 'yellow');

next(expr) / nextAll(expr)

Find the next (the siblings after) sibling matching expr or find all the sibling after this element matching expr

// Set the text element directly after a disabled button to read 'this button is disabled'
$("button[disabled]").next().text("this button is disabled");

// Hide all p siblings after a disabled button
$("button[disabled]").nextAll("p").hide();

prev(expr) / prevAll(expr)

Find the previous (the siblings before) sibling matching expr or find all the siblings before this element matching expr


siblings(expr)

Find all the siblings of this element matching expr


parent(expr) / parents(expr)

Find the parent of this element (can be called on a group and filtered by expression) / Find all the parents of this element (the unique ancestors)

// If a parent contains a disabled button then hide it
$("button[disabled]").parent().hide();

Chaining


andSelf()

Find the parent of this element (can be called on a group and filtered by expression) / Find all the parents of this element (the unique ancestors)

// Find all fieldsets that contain radio elements and place a border around the radio elements and around the fieldset
$("fieldset").find(":radio").andSelf().css('border', '1px solid green');

If you found this post useful, here are part 1, part 2, part 3 and part 4 of this series.
more...

Grid based approach to improve your designs

This article looks at using the grid based design approach to improve your design efforts. Using a grid will provide clarity, structure and balance to your designs. There now exists a range of easy to use tools that can be turned on and off at will to guide you in the process of designing with a grid.


The idea of using a grid based system has been around for some while now, Smashing Magazine did one of their usual mega-link articles over two years ago now. But there are still a good reasons to have another look at the grid based approach. If you are like me and you are more on the development side and most of your design ideas suck most of the time it is a process of continual improvement. We spend most of our time concentrating on how things work and not on how they should look. The problem is that design, clarity and presentation all matter to the user experience. So, you should care.

If you are like me and work for a small team, don't have the luxury of a design team or it's just not feasible to have a mock-up for each page you work on, you will be making design decisions and trade-offs all the time. The use of a grid is a simple tool that will improve your design work. Remember it's just a guide, so you can shuffle things around to your liking. But you will find that the use of a guide as a beginning tool-block to lay out your design is a clever and practical way to improve the end result.

There are tools out there available to make designing with a grid very easy. These are simple links that can be dragged on to your toolbar and will apply the grid over the top of your page. They allow you to customise the grid and tweak your designs as you work on them. Firebug + grid = good idea!

960.gs has a jQuery based grid system which sits on a tab at the side of your page and has many options for displaying vertical and horizontal bars. It is my preferred choice as it looks great and the option to hide it without closing is helpful. As well it contains most of the options you need without being too cluttered.



The next is Allan Jardine's Grid javascript bookmarklet. Grid is highly customisable and has a full feature set. It allows you to set alignment, gutters and margins.



The last one is the simple, easy to use grid960 grid overlay bookmarklet. This has a 12 and 16 column option and an option to set the opacity. This is a good starting point if you only want to use the grid as a rough guide or you want to do some quick tweaking and don't want to worry about setting too many options.



If you want to read up more on the topic and how you can use the grid system to improve your designs I suggest you check out the 960 grid system site. The site has a good deal of showcase sites and links to other quality tutorials and resources.

The grid overlay is an easy way to make big gains in your overall site design and using these unobtrusive tools makes it easier for you to apply the grid approach to your next design.

If you have a grid designed site that you would like to share or if you have something to share on the best way to use the grid based approach let us know in the comments section below.
more...

Building a Youtube playlist queue with php and jQuery

Sunday, 6 December 2009

View Comments

This post looks at how you can use php and jQuery to build a Youtube playlist queue, be the envy of your friends at your next party and allow your friends to search and drag items to a Youtube playlist.

As a Youtube playlist refreshes each time the page refreshes there is no need to call a page refresh each you add items, once the current video has finished playing the screen refreshes and updates the playlist.




In order to get a working version of my application you will need to do the following steps:
1. Download Zend Gdata package
2. Download my application code
3. Get a Google developer key
4. Extract the Zend Gdata package
5. Extract my application inside the root folder containing the Zend Gdata package, which should look like this:
/ZendGdata-1.9.6/jyt
6. Open /ZendGdata-1.9.6/jyt/operations.php and enter your developer key on line :56 where it says $_SESSION['developerKey'] = '-set your developer key here -';

To begin with this post should be looked at as an introduction to the world of possibilities available with Gdata and php. The demo application provided is very limited, but presents a glimpse at something that could be a great application given a months development work. It should also give you a taste for how easy it is to use Gdata to build applications based on Google authenticated users.

In order to build good quality applications with Google you really need to do a bit of reading, it's not something that you can understand in a day. Google's APIs are large and you will need to read them if you want to take things further.

My demo application uses AuthSub which is the basic method for giving an application permission for using your Google data. You can read more about AuthSub here.

But the main part of the application is ZendGdata. As Zend so gracefully put it: "These packages (ZendGdata) contain everything you need to access Google's Data APIs from your PHP 5 application.". When thinking about Zend and writing small applications you may be tempted to think why bother; it was something that held me back before from looking at integrating Google application data previously. Why would you want to use Zend framework when I just want to list my damn youtube videos? Well it turns out you don't need an entire framework, you just need the ZendGdata package that can be downloaded from here.

ZendGdata works simply like any other third party API or library. It's not too large or overbearing (the library folder currently comes in at around 2MB), so don't be scared of it. But it does contain a whole bunch of useful functions for authenticating and accessing Google data.

The package also contains the documentation and also some sample applications that you can begin playing with, how cool is that.

My application is based on the Youtube application provided with the package. To begin with it generates a link to ask the user to authenticate, if you are signed in to youtube this will ask you to 'allow' or 'deny' the application access to your data.

Once authentication has taken place the application will then go and retrieve and display your first playlist (it is possible for it to display any available playlist, I have just kept things simple). You are then displayed a search box to enter terms and submit.

Once you have searched and found a video you like, simply drag the video on to the target, it will be removed from the search list. Your playlist will then update and the added video will be at the bottom.

Why did I want to build this application? Many years ago I went to a party, it was at a time when Napster was just starting to become popular, I was amazed at the limitless possibilities of what was essentially an infinite jukebox. People could go over to the computer at the party, select the tunes they wanted and they would be queued and played. Allowing anyone to come and choose the tunes they wanted to hear. CD switching was a pain in those days as you had to wait for one song to finish and for the crowed around the stereo to disperse.

So there you have it. I hope this inspires you to Google application building greatness.

Enjoy!

more...

Display Google analytics with php, jQuery and flot

Friday, 4 December 2009

View Comments

This tutorial looks at how you can display your Google Analytics results using flot, the jQuery graphing library.

Flot will allow us to plot our results on a good looking graph without the need for flash; you can even enable and disable different sites if you are tracking multiple sites.


Unfortunately due to hosting restrictions I don't have a demo for this tutorial, but you can download the code from here



We will also be using the php analytics API class written by Chris Hope available from http://www.electrictoolbox.com. This class will allow us to easily retrieve our analytics data and will allow you to customise your results (including start date etc.)

We begin with a simple placeholder in our HTML, we then call our php script analytics.php using jQuery's getJSON method. The analytics.php script first checks to see if we have a cached version of the results (the retrieval from Google is considerably slow) and then serves the cached results. If there are no cached results it retrieves the data from Google's servers and then builds an array of data for each site. This array is then converted to JSON using php's json_encode and returned to our javascript file. Our javascript file builds an array suitable for Flot and then calls Flot's plot method.

Let's have a look at some of our code, firstly the HTML:

<h1>
My sites</h1>
<div id="placeholder" style="width:600px;height:300px;">
</div>
<p id="choices" style='display:none;'>
Show:</p>

This is simply our title and place holder for our graph and site toggler.

Our javascript file is made up of three functions:

$("document").ready(function() {
   var url = "analytics.php";
   
   $.getJSON(url, {id:  Math.random()}, function(datasets) {
      if (datasets) {
         // hard-code color indices to prevent them from shifting as
         // countries are turned on/off
         var i = 0;
         $.each(datasets, function(key, val) {
            val.color = i;
            ++i;
         });
         
         // insert checkboxes 
         var choiceContainer = $("#choices");
         $.each(datasets, function(key, val) {
            choiceContainer.append('
<input type="checkbox" name="' + key +
                              '" checked="checked" id="id' + key + '">' +
                              '<label for="id' + key + '">'
                              + val.label + '</label>');
         });
         choiceContainer.find("input").click(updateGraph);
         
         plotAccordingToChoices(datasets);
         
         $("#choices").show();
      }
      
   });
});

We begin with our document ready function. This starts by calling our analytics.php file using the getJSON function and retrieves our datasets. We loop over our datasets and assign them a colour. Next we create our checkboxes to turn sites on or off and assign the updateGraph click function to these checkboxes. Finally we call our plotAccordingToChoices function and show our choices checkboxes.

function plotAccordingToChoices(datasets) {
   var data = [];
   
   var choiceContainer = $("#choices");

   choiceContainer.find("input:checked").each(function () {
      var key = $(this).attr("name");
      if (key && datasets[key]) {
         data.push(datasets[key]);
      }
   });

   var options = {
            xaxis: {
                mode: "time"
            }
        };
   
   if (data.length > 0) {
      var plotarea = $("#placeholder");
      plotarea.css("height", "250px");
      plotarea.css("width", "500px");
      $.plot( plotarea , data, options );
   }
}

Our plotAccordingToChoices function takes in our datasets and then compares the data to the checked options to see if any are disabled otherwise they are added to the data array. Our data array is then simply passed through to Flot to plot.

function updateGraph() {
   var url = "analytics.php";

   $.getJSON(url, {id:  Math.random()}, function(datasets) {
      if (datasets) {
         var i = 0;
         $.each(datasets, function(key, val) {
            val.color = i;
            ++i;
         });      
      
         plotAccordingToChoices(datasets);
      }
   });
}

Our updateGraph function simply retrieves our data again and then calls plotAccordingToChoices. You could store this information somewhere in a global or in the DOM, if that's what you're in to.

Now let's look at the php side of things.

require_once('analytics_api.php');

$login = '-enter login here-';
$password = '-enter password here-';

$modified = @filemtime("cache.txt");

$modified = date("d-m-Y", $modified);
$current = date("d-m-Y");

if ($modified && ($current == $modified)) {
   $cache = @file_get_contents("cache.txt");
} else {
   $cache = null;
}

if (!$cache) {
   $api = new analytics_api();
   if($api->login($login, $password)) {
      $api->load_accounts();
      $accounts = $api->accounts;

      $return = array();
      
      foreach ($accounts as $accountname => $account) {
         $data = $api->data($account['tableId'], 'ga:week', 'ga:visits', 'ga:week', '2009-07-05', '', '52');
         $return[$accountname]['label'] = $accountname;
         
         foreach ($data as $week_number => $week) { 
            $return[$accountname]['data'][] = array( (strtotime("2009W".$week_number) * 1000) , $week['ga:visits'] + rand(0, 400) );
         }
      }
      
      $encoded = json_encode($return);
      
      echo $encoded;
      file_put_contents("cache.txt", $encoded);
   }
} else {
   echo $cache;
}

We begin by checking our cached file and then checking the date modified to see if the cache file is from today. If the cache file exists and it is from today we simply return the contents of that file.

Otherwise, we log in to our analytics account using the $api->login($login, $password) method. We loop over each of our accounts and retrieve the data from that account:

$data = $api->data($account['tableId'], 'ga:week', 'ga:visits', 'ga:week', '2009-07-05', '', '52');

You see here when we call the data method you can specify a whole bunch of different options, I will leave these to you to investigate. We are simply going to retrieve all weekly visit data starting from 2009-07-05 up to a maximum of 52 weeks if available.

We loop over this data and store it in the return array per account name. Then once all the data has been looped over we simply JSON encode and return.

Nice and easy.

Enjoy!
more...

jQuery UI sortable drag multiple to target

Thursday, 3 December 2009

View Comments

This tutorial looks at building a jQuery sortable UI widget that allows for sorting and for dragging multiple items on to a droppable target.


This is my first attempt (and possibly last) at building this so I haven't had a chance to check this off with all options available, I only built it for a single purpose.

The demo can be viewed here

And you can download the code from here

A brief rundown on how it works: control-click to select/deselect items. When the first item is dragged the other items are cloned and the original element hidden. If the main item being dragged hits the droppable target then the element and all its siblings are removed:

$("document").ready(function() {
  $("#sortable1").multisortable({});
  $("#sortable1").disableSelection();
  $("#target").droppable({
   accept: "#sortable1 li",
   drop: function(event, ui) { 
    var elements = $(ui.draggable).siblings(".ui-multisort-grouped").andSelf();
    
    $(elements).remove();
   }
  });
});

The element being dragged and its siblings are stored in the elements variable so you can essentially do what you like with them (read attributes etc.) at this point.

Hope this helps some people.

Enjoy!
more...

Using jQuery with forms - part 4 (Attributes)

Wednesday, 2 December 2009

View Comments

This is the fourth in a series of tutorials on using jQuery to manipulate forms. In this series I will be looking at parts of the jQuery API reference in detail. In this tutorial I will be looking at forms and jQuery Attributes.

Attr


attr(name)

Retrieves the value of an attribute


// Retrieve the location that the form is submitting to
$("#my_form").attr("action"); 

attr(key, value)

Sets the value of an attribute


// Set the location that the form is submitting to, this may be 
useful when you want to set the action according to some input
$("#my_form").attr("action", "processing.php"); 

removeAttr(name)

Removes an attribute from an element


// Remove all inline styling from fields with the password class
$(".password").removeAttr("style"); 


HTML


html() / html(val)

Retrieves / sets the innerHTML of the matched element


// Retrieve the html() contents of the fieldset with ID 
#fieldset_login and then set the html to the 
contents of updatedHTML
var updatedHTML = ... some markup ...
var login_html = $("#fieldset_login").html(updatedHTML);


Text


text() / text(val)

Retrieves / sets the text contents of the matched element


// Retrieve the contents of the first label
var label_text = $("label:first").text();


Getting and setting the values of different form elements



// Retrieve and set the contents of text input
$("#my_text_input").val();
$("#my_text_input").val(new_value);

// Retrieve and set the contents of label
$("#my_label").text();
$("#my_label").text(new_value);

// Retrieve and set the contents of textarea
$("#my_textarea").val();
$("#my_textarea").val(new_value);

// Retrieve and set the value of select
$("#my_select").val()
$("#my_select").val(2) // Set select according to value

// Retrieve and set the text of select
$("#my_select :selected").text() // Text value of selected option
// Changes the text of selected option
$("#my_select :selected").text("some text") 

// Retrieve and set the text label of a button
$("#my_button").text();
$("#my_button").text("some text");

// Retrieve the button type attribute
$("#my_button").attr("type"); 

// Retrieve the value of the checked radio element
$("#my_form [name='radio_group']:checked").val();

// Check if a particular checkbox has been checked
$("#my_form [name='checkbox_group[]']").eq(1).is(":checked")

// Retrieve all the checked checkboxes in a serialized string
$("#my_form [name='checkbox_group[]']:checked").serialize() 

// Retrieve and set the contents of password input
$("#my_text_input").val();
$("#my_text_input").val(new_value);


If you found this post useful, here are part 1, part 2, and part 3 of this series.
more...

5 sites and 5 jQuery plug-ins - tools for daily use

This post looks at 5 sites that I use daily and 5 jQuery plug-ins that I use regularly. The aim of this post is to provide you with some useful tools that you can keep in your toolbox and hopefully they will also be of regular use to you. Both the sites and the plug-ins are practical, reliable and most importantly they do what they are supposed to do.

The sites that I have chosen are sites that I personally use frequently. I think it's important to know a bunch of good sites, it saves trying to remember too much: memorise the basics and keep the rest within arms reach. I visit the php API and the jQuery API several times daily, I remember the basics, but I wouldn't want to try memorising the entire php API. I've tried to choose sites that are more on the practical side, these are more tool like and less reference like. I may only use them once or twice a day for a minute or two, but they are good at what they do.

The first site is ColorPicker. No need to install any software, no adds, no clutter, it is a simple color picker and it's handy.



The next site is a Lorem Ipsum generator. I'm surprised at how often I use this. The site is Lorem 2. Lorem 2 has Lorem Ipsum text in many different formats, short/long paragraphs and short/long list items.



I'm sure most of you know about Stack Overflow and w3schools. I use these two for different reasons; I use w3schools whenever I forget the basics, it's presentation is clean and I can reference what I need to know quick and easy, it's for those times when I forget something simple and stupid like how to name radio buttons. I use Stack Overflow when I assume there should exist a practical solution to something I haven't worked out yet. It's a good way to compare your solution to your peers and a good place to get advice and feedback.



The jivebay Handling Checkboxes, Radio Buttons and Select Options in jQuery post was the inspiration for my select element cheat sheet and I still find myself referencing this post every now and then. The questions and answers in the comments section are just as good as the post itself and the answers are well written.



The last site is the dummy data generator Generate Data. This site will generate large amounts of dummy data in different formats (HTML, Excel, XML, CSV, SQL) and has a large variety of data types. You can also specify the number of rows and row names.



The 5 plugins that I have chosen are also simple and practical. Simple in that they are east to use and do not try and overcomplicate.

The main site I work on is an internal CRM, so we have a lot of tabled information. The jQuery tablesorter plugin is awesome; it allows for custom parsing for sort order and with a bit of work it is very easy to have inline editing. You can also sort on multiple rows.



The jQuery cookies plugin works across all browsers and is so easy to set up. You can have a look at this post if you want to read over some of it's features rather than reading the code itself. It has very easy set, and get functions and is lightweight.



There are currently many jQuery autogrow plugins, but my favourite is this one. It is small enough that you could just cut and paste the function out and stick it in your own utility script if needed. It also works well across all browsers without hiccups.



The same could be said for watermark plugins, there are many out there, but the one that I like to use (which has been in beta since 2007) is this one by Josh Bush. It also has built in hideAll, showAll functions which are useful when pulling values for submission via ajax.



The last one and maybe the most useful is the jQuery form plugin. For any ajax heavy application with many forms this plugin has many useful methods for serializing, resetting and submitting forms. It even has a method for submitting files via AJAX.



It is always good to have many sites and tools that you can rely upon, once you find something good make sure to bookmark it for later use and then share it around. What are some of your favourite plug-ins that you use daily? What are some sites that you can't live without?
more...

Collection of December 2009 Wallpapers

Tuesday, 1 December 2009

View Comments

I've scoured the net and tried to find some of the best December 2009 wallpapers to kick of the festive Season. I was surprised that there were so few, being Christmas and all. Here is what I came up with.

Click on any of the images below to go to the artists page and download the full sized image.
















more...

jQuery php voting widget

This tutorial looks at how to build a jQuery UI themeable voting widget using a jQuery sortable and a bit of php to store the current number of votes. The actual widget itself is quite light-weight and the only markup we need is a single unordered list. The jQuery is also minimal and most of the work is done in php to work out the what the current tally is and to store this information.



You can view the working demo this tutorial here.

You can download the code for this tutorial here.

In my implementation I have chosen to store the current number of votes as a single serialized string in a flate file, but this could easily be done in a database.

To begin with I will show you the unordered list, my list is going to contain Charlton Heston movies:
<li class="non-sortable" style="height:2.2em;">Rank your favourite Charlton Heston movies by dragging options</li>
<li class="ui-state-default movie-rank" id="movie-1"><span class='counter'>#1</span>The Ten Commandments</li>
<li class="ui-state-default movie-rank" id="movie-2"><span class='counter'>#2</span>Ben-Hur</li>
<li class="ui-state-default movie-rank" id="movie-3"><span class='counter'>#3</span>Planet of the Apes</li>
<li class="ui-state-default movie-rank" id="movie-4"><span class='counter'>#4</span>The Omega Man</li>
<li class="ui-state-default movie-rank" id="movie-5"><span class='counter'>#5</span>Soylent Green</li>
<li class="ui-state-default movie-rank" id="movie-6"><span class='counter'>#6</span>The Three Musketeers</li>
<li class="ui-state-default movie-rank" id="movie-7"><span class='counter'>#7</span>Antony and Cleopatra</li>
<li class="ui-state-default movie-rank" id="movie-8"><span class='counter'>#8</span>Beneath the Planet of the Apes </li>
<li class="ui-state-default movie-rank" id="movie-9"><span class='counter'>#9</span>El Cid</li>
<li class="ui-state-default movie-rank" id="movie-10"><span class='counter'>#10</span>Julius Caeser</li>
<li class="non-sortable"><button class="ui-button ui-state-default ui-corner-all" type="button" name="submit" style="float:right;">Submit</button></li>

If you look at the li classes above you will see there are two main types, the non-sortable and movie-rank. The jQuery sortable plugin allows us to specify which items should be sortable using the 'items' option and which items will not be part of the sorting process (that is, they stay put). We want our widget title and the submit button to stay put so lets look at the jQuery initialisation:
$("document").ready(function() {
 $("#sortable").sortable({
  placeholder: 'ui-state-highlight',
  items: '.movie-rank',
  cancel: '.non-sortable'
 });
 
 $("#sortable").disableSelection();

The three options from our sortable initialisation are placeholder, items and cancel. Place holder simply provides an indicator for where the item will be dropped, in my example this is the blue bar that appears as the item being dragged hovers over each list item. The items and cancel work as previously mentioned, basically 'items' are sortable and 'cancel' stays put. The final call to disableSelection is an undocumented jQuery UI method that prevents text selection happening on a list element so that grabbing an item to be dragged is easier and the text selection doesn't get the focus first.

Next I decided to add an update function whenever the sort order is changed. This updates the ranking of each item, so if you move #3 to the #7 position all of the numbers update to show your preferred voting order. Let's look at the code:
$('#sortable').bind('sortupdate', function(event, ui) {
 $(".counter").each(function() {
  $(this).empty();
  var index = $(".counter").index(this) + 1;
  $(this).text("#" + index);
 });
});

The sortupdate function is a special function specific to the jQuery UI sortable plugin. You can see all of the options and functions here. This is called when the sort order is updated. I simply remove all of the rankings and then update each of the elements according to their actual position amongst thier peers. This is done using the jQuery index function, which takes in a group of elements and finds the index of the element amongst that group (0 based).

The last piece of jQuery code is our submit function:
$("#sortable [name='submit']").click(function() {
 var rankings = $('#sortable').sortable('serialize');
 
 $.get("vote.php?" + rankings, {}, function(data) {
  $("#sortable").empty().html(data);
 });
});

Our sortable has another great funcion, serialize which looks at the IDs of our li elements and passes them in order as follows: arrayname-id. So I have mine set up as movie-1, movie-2 etc. so these will be returned as movie[]=1,movie[]=4,movie=[]=7... according to how we have sorted them. When this gets rebuilt in php we will have an array that is ordered in the same way as our sorted list with a reference to the index of our movie.

We then make an AJAX call using jQuery's $.get function which sends in our voting submission and waits to get the results back from our php script. Once the results are returned we simply clear out our sortable and insert the results returned from the script.

We start off our php script vote.php with retrieving the values passed in and setting up an array that has a key corresponding to the movie and the voting points allocated to that movie:
$movie_rankings = $_GET['movie'];

if ($movie_rankings && is_array($movie_rankings) && (sizeof($movie_rankings) == 10)) {
 $movie_points = array();
 
 foreach ($movie_rankings as $rank => $index) {
  if (!is_numeric($rank) || !is_numeric($index) || ($rank > 9) || ($index > 10)) exit; // Prevent hacking
  $movie_points[(int)$index] = (10 - (int)$rank);
 } 

You will see I have added a few small security checks in there to avoid someone from screwing this up completely. We begin with getting the value of movie from our $_GET variable. We then check that this wasn't empty, that it is an array and that it has exactly the right number of elements, in our case 10.

Next we create the $movie_points array to hold our votes for each movie. We loop over the rankings passed in and while we are doing this we need to check that someone hasn't just passed in garbage. We check that the $rank and $index are both numbers and that they are both less than 10, another security check. We then set the points for each movie index inside the $movie_points array, casting each value to int in case some tricky person decided to use float values.
$current_rankings = @file_get_contents("rankings.txt");

if ($current_rankings) {
 $current_rankings = unserialize($current_rankings);
 
 foreach ($current_rankings as $index => $score) {
  $movie_points[$index] += $score; 
 }
}

$ser_points = serialize($movie_points);
file_put_contents("rankings.txt", $ser_points); 

Following from that we retrieve the current rankings form our rankings.txt flat file. If the file existed we unserialize those values and loop over each value and add it to the point values just pased in by updating the $movie_points array. We then serialize the $movie_points array and store it in our flat file for later reference.

Now that we have our current standings and all our points tallied we want to build our output. Our output will contain a colourful graphical representation in the form of a bar chart, which will contain the percentages and the names of each of the movies:
arsort($movie_points);
  
$movies = array();
$movies[1] = "The Ten Commandments";
$movies[2] = "Ben-Hur";
$movies[3] = "Planet of the Apes";
$movies[4] = "The Omega Man";
$movies[5] = "Soylent Green";
$movies[6] = "The Three Musketeers";
$movies[7] = "Antony and Cleopatra";
$movies[8] = "Beneath the Planet of the Apes";
$movies[9] = "El Cid";
$movies[10] = "Julius Caeser";

$colours = array();
$colours[1] = "#E52E2E";
$colours[2] = "#E58F2E";
$colours[3] = "#E5DF2E";
$colours[4] = "#8FE52E";
$colours[5] = "#2EE56C";
$colours[6] = "#2EE5D2";
$colours[7] = "#2E49E5";
$colours[8] = "#942EE5";
$colours[9] = "#C02EE5";
$colours[10] = "#DE2EE5";

$total_points = array_sum($movie_points);

$html = '';

$counter = 1;
foreach ($movie_points as $key => $points) {
 $percent = round(($points / $total_points * 100), 0);
 $html .= "<li class='ui-state-default movie-rank non-sortable' id='movie-{$key}'><div style='background:{$colours[$counter]};height:25px;width:{$percent}%;padding:5px;margin-top:-6px;margin-left:-6px;'></div><div style='position:absolute;left:0px;top:0px;'><span class='counter'>{$percent}%</span>{$movies[$key]}</li></div>";
 $counter ++;
}

echo $html;

We begin by sorting our $movie_points from highest to lowest. Then we set up two arrays, one with the index to name mapping of our movies and the next with the colour level for our bar chart. We us php's built in array_sum to count up the total number of points already awarded, this will be used to work out percentages.

Next we loop over (from highest to lowest) each of our movies; we calculate the percentage points and then build the list item displaying our movie name, points and the coloured bar.

This is then echod out and returned to the jQuery scipt which will clear out our voting system and display the colourful results.

That's the lot.

Enjoy!
more...

jQuery UI multiple draggable plugin

Sunday, 29 November 2009

View Comments

The jQuery multiple draggable plugin is an extension to the jQuery UI Draggable plugin. This plugin extends the current functionality to allow for elements to be grouped and dragged as a group. The aim of the plugin is to include all of the current functionality listed in the options.

To group and ungroup elements simply control click, the ui-multidraggable extension is applied for styling purposes and also to alert the plugin as to which elements are grouped.


You can view the following demos:

You can also download the plugin and the demos from here.

An update has now been provided for this plugin thanks to Caili Crane @ Pulse Energy allowing the plugin to be used with jQueryUI 1.8.8 and jQuery 1.4.4 and can be downloaded here.

As this is an extension of the base functionality it is also released under a dual GPL and MIT license. You can view copies of these licenses in the package when you download the source from the link above.

Following is a list of all the options from the draggable element and how they work with the multidraggable plugin:

  • addClasses works and is set on an individual level
  • appendTo works and is set on an individual level
  • axis works and is set on an individual level
  • cancel works and is set on an individual level
  • containment works and is set on an individual level
  • cursor works and is set on an individual level
  • cursorAt works and is set on an individual level
  • delay works and is set on an individual level
  • distance works and is set on an individual level
  • grid works and is set on an individual level
  • handle works and is set on an individual level
  • helper works and is set on an individual level
  • iFrameFix works and is set on an individual level
  • opacity works and is set on an individual level
  • refreshPositions works and is set on an individual level
  • revert works and is set on an individual level
  • revertDuration works and is set on an individual level. Note: this currently throws an error if two elements have different revertDurations and you attempt to drag before both have reverted to their original position.
  • scope works and is set on an individual level
  • scroll works and if any in the group have scroll set to true then enclosing element will scroll
  • scrollSensitivity works and is set on an individual level
  • scrollSpeed works and is set on an individual level, but once all elements are outside the original container the speed of the fastest grouped element is used
  • snap works and is set on an individual level
  • snapMode works and is set on an individual level
  • snapTolerance works and is set on an individual level
  • stack works, whichever element from the group is the one being dragged will have the highest zIndex
  • zIndex works and is set on an individual level

The only functionality I am aware of that currently does not work is the connectToSortable which I am working on resolving for the next release.

As you can see from the working options above, these settings are set on an individual but are likely to work more cohesively if they have the same values and options set. This was purposely done to allow for flexibility and usability (you may want certain items to have different opacity etc.).

I have very pleased to be able to release this plugin and hope it is useful. My first attempt at doing this nearly a year ago was miles away from where it is now and I am grateful to this post for teaching me how to do this the correct way.

Please let me know if you have any constructive feedback and if you are using this plugin anywhere.

Enjoy!
more...

How can Chrome OS win market share?

Thursday, 26 November 2009

View Comments

Who really rules the net? I know with most popular culture that it's somewhere between 8 and 18 year olds with an ever increaing dispensible income; they decide our music stars, our fashion and the growing movie takings. They brought us the Spice Girls, then Brittany, Miley and now New Moon. God bless their socks, they have brought us some real shit, but I'm not going to turn this into a rant about how teenage girls should really be listening to Radiohead.


God knows they've tried to influence the net; Facebook, lolcatz and Perez. But real IT companies are not considered with the steady stream of cultural detrits that is floating around the web. What they are more concerned about is how you are viewing it. You see, the folks at Redmond are sitting on one giant nest-egg that has been around for years that according to statcounter around 90% of us worldwide are continuing to use.

So who decides which OS is king? Is it just a matter of cool? I doubt it, I'm not sure where Windows sits on the cool-o-meter and I don't think that operating systems as a whole are the kind of cool that the kids are interested in. They are kind of the opposite to the big guys, they are more interested in what they are looking and not what they are using. I would argue that the driving influence for any computer technology are those of us who actually care.

Why is my Mother now using Firefox when 6 months ago she was stuck on IE6? Because someone who cared bothered to go and install an alternative. A couple of years back I may not have bothered, but what has made this shift in the environment that now so many people are moving away from Explorer and installing Chrome or Firefox or something else. We have! The people who care. The stuff floating around the net may belong to our cultural heroes, but what you are using belongs to us. The developers, the tech writers, the support team and the salesmen, not the guy on the street.

If it were just a matter choice maybe the Mac guys or the Linux guys would have had a better share, but recently there has been a real movement to change and you can see it in the last twelve months of browser statistics. It has been mobilised through blogging and demonstrated by the number of installs on friends and parents machines. So why is this so important to the OS market?

If you have ever tried, you will know how hard it is to convince someone to switch their browser, but once they have switched it is just as hard to switch them back. So, although at most major stores across the world your shiny new PC will come pre-installed with the latest version of Explorer, these same friends and parents will come back and ask you to reinstall their new favourite browser.

I just finished reading Robert Scoble's latest post titled "Why Google Chrome OS has already won":

"Google is in a war over developers with Microsoft. Google wants developers to build for the open web. Microsoft wants developers to build for Silverlight. Those messages are VERY clear coming out of both camps now."


I would argue that Google is not in a war over developers for the sake of building an open web, but for the reasons I have already mentioned. Google's war is not won at the storefront, but at the same place that Firefox won its battle. They want the tech guys to get off their backsides and install their new operating system on as many Friends / Mum and Dad machines as possible. Yes they are battling over developers, not to write code for a specific purpose, although they are trying to win the developers with their code, but they want the developers to be inspired to change.

In the end the cool kids will still use whatever is put in front of them, they can't tell the difference between Greenday and My Chemical Romance. The only way to change what the average Joe is using is if we can be bothered enough to go out and do it ourselves and now that Google knows that, hopefully they can deliver an OS good enough for us to get out there and do it.
more...

jQuery flickr widget

Wednesday, 25 November 2009

View Comments


This tutorial shows you how to build a jQuery widget for your flickr photos or for your favourite flickr group.

Here is a look at the widget we will be building:

You can download the code for this widget here.

To build the widget we need to grab the jQuery hoverscroll plugin which allows us to do the nice scrolling action.

The rest is just a simple call to flickr's JSON API to retrieve the feed and another function to scale the images to size.

If you want to read about importing your flickr feed with jQuery and JSON I suggest you read this helpful post.

To begin with we simply grab our feed via JSON:

$("document").ready(function() {
     $.getJSON("http://api.flickr.com/services/feeds/groups_pool.gne?id=32142572@N00&lang=en-us&format=json&jsoncallback=?", function(data){
          $.each(data.items, function(i,item){
          $("<img/>").attr("src", item.media.m).appendTo("#images")
               .wrap("
<li><a href='" + item.link + "'></a></li>
");
          });
          
          $('#images').hoverscroll({
               vertical: true,
               width: 166,
               height: 400
          });

The main part of the call is the jQuery getJSON call, this calls the flickr API with the group ID of the group you want to display. You can easily get this URL and ID as it is almost identical to the RSS feed of the group. This feed is displayed at the bottom of the page, so it's a matter of clicking on the feed, grabbing the URL and changing the format over to JSON as above.

To view the different kinds of feed that flickr offers follow this link here.

The jQuery hoverscroll plugin is then called to build our lovely scrolling widget. You can view more detail about how the plugin works here.

The next part of the script just loops over the images and scales them accordingly.

$('#images img').each(function() {
               var maxWidth = 160; // Max width for the image
               var maxHeight = 160;    // Max height for the image
               var ratio = 0;  // Used for aspect ratio
               var width = $(this).width();    // Current image width
               var height = $(this).height();  // Current image height
      
               // Check if the current width is larger than the max
               if(width > maxWidth){
                    ratio = maxWidth / width;   // get ratio for scaling image
                    $(this).css("width", maxWidth); // Set new width
                    $(this).css("height", height * ratio);  // Scale height based on ratio
                    height = height * ratio;    // Reset height to match scaled image
                    width = width * ratio;    // Reset width to match scaled image
               }
      
               // Check if current height is larger than max
               if(height > maxHeight){
                    ratio = maxHeight / height; // get ratio for scaling image
                    $(this).css("height", maxHeight);   // Set new height
                    $(this).css("width", width * ratio);    // Scale width based on ratio
                    width = width * ratio;    // Reset width to match scaled image
               }
          }); 

I won't go into detail as to how the above works, it is just some math to scale the image to the provide constraint. Here I have set maxWidth and maxHeight to 160px.

The rest is just a bit of HTML and css.

Enjoy!
more...

Best jQuery games from across the net

Today I'm going to look at some of the best jQuery games on the net. As javascript speeds get faster and the great things you can now do with jQuery make it possible to produce some good quality games. Although speed and sizes are a bit of a restriction to the type of game that can be produced (no 3D shooters yet) these are great games none the less and some are quite addictive.

I hope to show you a range of what is available. Although there are quite a few jQuery games out there, I chose these for their playability and completeness. You can tell these guys put in a decent effort to make a quality product.


I'd like to start with the jQuery pong clone. Why play against your friends when you can play against your browser! This game has a whole bunch of options that you can set yourself if you decide to download it for yourself including ball speed, score to play to and paddle sizes. Click on the image to follow the link.



The next game is extremely addictive, but based on a simple idea. In jCharacterfall, water drips from the ceiling and you stop them from hitting the ground by pressing the matching letter. As the game goes on the drops fall faster and faster. Hours of fun!



Next up is a sudoku game. This is a nice clean implementation and is a real challenge to get your name up in the highscores (not like all the other hacked high-score lists!). Only thing I think is missing is the ability to pencil in smaller guesses.



Sticking with the retro theme is an old fashioned snake clone. You know the score, snake goes round, eats stuff, crashes into walls.



The next one is really special and a lot of effort has gone in to building it. Jon Raasch has built a clone of the old NES game T&C Surf Designs. It looks and handles like the classic Nintendo game and the sprites look great.



If you have any more suggestions for great jQuery games leave a comment.

Have fun and enjoy!
more...

jQuery weather widget

Tuesday, 24 November 2009

View Comments


In this tutorial we are going to build a jQuery weather widget. This widget is pure javascript and requires no back-end/server so you can place it on your blog or anywhere you like.

Please note: Information in this widget should not be taken as accurate under any circumstance and should not be used to make any decisions in any circumstance. It is purely a toy. We make no claim to its accuracy for any purpose.


You can get the code for this tutorial here.

The widget makes two main calls using JSON to retrieve the users geolocation and then to retrieve the local weather for that location. It also uses the jQuery cookie plugin to remember the details so that it doesn't need to make these calls each time the page reloads.

The two JSON calls are to the geoPlugin site and then to the geonames weather webservice using the retrieved longitude and latitude coordinates. Please check the terms and conditions from their site if you want to use this widget on your own site. It also uses Googles weather images.

Let's look at the first part of the javascript code. We begin with the jQuery document ready function:

$("document").ready(function() {
     if ($.cookie('loc_longitude') && $.cookie('loc_latitude')) {
          getWeather();
     } else {
          $.getJSON("http://www.geoplugin.net/json.gp?callback=?", function(data) {
                eval(data);
          });
     }
});

It begins by checking if the geocordinates for this user have been set for this user. If not it calls the geoplugin site to retrieve geolocation details.

function geoPlugin(data) {
     $.cookie('loc_latitude', data.geoplugin_latitude, {expires: 7}); 
     $.cookie('loc_longitude', data.geoplugin_longitude, {expires: 7});
     $.cookie('loc_country', data.geoplugin_countryName, {expires: 7});
     $.cookie('loc_region', data.geoplugin_region, {expires: 7});
     $.cookie('loc_city', data.geoplugin_city, {expires: 7});
     $.cookie('loc_country_code', data.geoplugin_countryCode, {expires: 7});
     getWeather();
}

The eval function above calls the geoPlugin function and sets the geolocation details in the cookie, this will be used in the getWeather function which you will notice is called above if the location details are already set.

function getWeather() {
     var latitude = $.cookie('loc_latitude');
     var longitude = $.cookie('loc_longitude');

     var loc_conditions = $.cookie('loc_conditions');
     var loc_conditions_img = $.cookie('loc_conditions_img');
     var loc_temp = $.cookie('loc_temp');
     var loc_humidity = $.cookie('loc_humidity');

     if (loc_conditions && loc_conditions_img) {
          setConditions(loc_conditions, loc_conditions_img, loc_temp, loc_humidity);
     } else {
          var url = "http://ws.geonames.org/findNearByWeatherJSON?lat=" + latitude + "&lng=" + longitude + "&callback=?";
          $.getJSON(url, function(data) {
               var clouds = data.weatherObservation.clouds;
               var weather = data.weatherObservation.weatherCondition;
               var temp = data.weatherObservation.temperature;
               var humidity = data.weatherObservation.humidity;
   
               var conditions_img = getConditions(clouds, weather);
   
               var conditions = '';
               if (weather == 'n/a') {
                    if (clouds == 'n/a') {
                         conditions = 'fine';
                    } else {
                         conditions = clouds;
                    }
               } else {
                    conditions = weather;
               }
   
                    $.cookie('loc_conditions', conditions); 
                    $.cookie('loc_conditions_img', conditions_img); 
                    $.cookie('loc_temp', temp); 
                    $.cookie('loc_humidity', humidity); 
                    setConditions(conditions, conditions_img, temp, humidity);
          });
     }
}

The getWeather function starts by retrieving all of the geolocation details from the cookie. It then checks the cookie to see if the temperature details have been set in the cookie, if they have it simply goes ahead and displays all the information in the widget by calling the setConditions function.

If the temperature details haven't been set in the cookie it then calls the JSON weather service using the latitude and longitude details from the first JSON call. It then calls the getConditions function to retrieve the correct image corresponding to the current weather condition.

function getConditions(clouds, weather) {
     if (weather == 'n/a') {
          switch (clouds) {
               case 'n/a':
                    return 'sunny.gif';
               case 'clear sky':
                    return 'sunny.gif';
               case 'few clouds':
                    return 'partly_cloudy.gif';
               case 'scattered clouds':
                    return 'partly_cloudy.gif';
               case 'broken clouds':
                    return 'partly_cloudy.gif';
               default:
                    return 'cloudy.gif';
          }
     } else {
          weather = weather.replace('light ', '').replace('heavy ', '').replace(' in vicinity', '');
          switch(weather) {
               case 'drizzle':
                    return 'rain.gif';
               case 'rain':
                    return 'rain.gif';
               case 'snow':
                    return 'snow.gif';
               case 'snow grains':
                    return 'sleet.gif';
               case 'ice crystals':
                    return 'icy.gif';
               case 'ice pellets':
                    return 'icy.gif';
               case 'hail':
                    return 'sleet.gif';
               case 'small hail':
                    return 'sleet.gif';
               case 'snow pellets':
                    return 'sleet.gif';
               case 'unknown precipitation':
                    return 'rain.gif';
               case 'mist':
                    return 'mist.gif';
               case 'fog':
                    return 'fog.gif';
               case 'smoke':
                    return 'smoke.gif';
               case 'volcanic ash':
                    return 'smoke.gif';
               case 'sand':
                    return 'dust.gif';
               case 'haze':
                    return 'haze.gif';
               case 'spray':
                    return 'rain.gif';
               case 'widespread dust':
                    return 'dust.gif';
               case 'squall':
                    return 'flurries.gif';
               case 'sandstorm':
                    return 'dust.gif';
               case 'duststorm':
                    return 'dust.gif';
               case 'well developed dust':
                    return 'dust.gif';
               case 'sand whirls':
                    return 'dust.gif';
               case 'funnel cloud':
                    return 'flurries.gif';
               case 'tornado':
                    return 'storm.gif';
               case 'waterspout':
                    return 'storm.gif';
               case 'showers':
                    return 'storm.gif';
               case 'thunderstorm':
                    return 'thunderstorm.gif';
               default:
                    if (weather.indexOf("rain")) {
                         return 'rain.gif';
                    } else if (weather.indexOf("snow")) {
                         return 'snow.gif';
                    } else if (weather.indexOf("thunder")) {
                         return 'thunderstorm.gif';
                    } else if (weather.indexOf("dust")) {
                         return 'dust.gif';
                    } else {
                         return 'sunny.gif';
                    }
          }
     }
}

getConditions is simply a couple of large switch statements to retrieve the correct image. Once this is returned the image and the conditions details are set in the cookie. The last step is calling the setConditions function to display the details in the widget.

function setConditions(conditions, conditions_img, temp, humidity) {
     var country = $.cookie('loc_country');
     var region = $.cookie('loc_region');
     var city = $.cookie('loc_city');
     var loc_country_code = $.cookie('loc_country_code');
     if (loc_country_code == 'US') {
          temp = parseInt(temp) + 32;
          temp_type = "F";
     } else {
          temp_type = "C";
     }

     $("#weather_widget").append("");
     $("#weather_widget").append("

" + country + "

" + city + ", " + region + "

Temp: " + temp + "° " + temp_type + "

Humidity: " + humidity + "%

" + conditions.substr(0, 1).toUpperCase() + conditions.substr(1) + "

"); }

The rest is just a little bit of css and html.

Enjoy!
more...

Christmas digital art from deviantART

Monday, 23 November 2009

View Comments

Hope everyone is starting to get into the festive spirit. Today I've had a good look through deviantART and selected some of my favourite designs. I hope you like them and they help to brighten your holidays.


The first one I picked is fractal art, there are a whole bunch of really good fractal art Christmas images so if you like this kind of thing make sure you browse the category.



The second picture I chose looks like a movie poster and subtle Christmas reference is just the icing on the cake to an already fantastic image.




I really like this next picture, you have to look at it a few times to decide how you feel about it.



This next one is a great Christmas card design, it's a shame that there isn't a larger version available. Some lucky people will be getting this card.



Here is another great fractal. It looks like Christmas flowers growing out of mould; kind of strange, but Christmas like.



This next image was actually the first one I chose and it really stood out amongst the others. Something stark and beautiful. It uses great Christmas imagery in a different context.



The last one is some art done over the top of an existing photo. A job well done that really captures the moment.



I hope wherever you end up and whatever you believe this Christmas that you have a fantastic time with friends and family.

Take care and enjoy!
more...

What is most important in a browser? Microsoft discuss IE9

Dean Hachamovitch, General Manager, Internet Explorer recently blogged on the future release of Explorer, IE9. Titled 'An Early Look At IE9 for Developers', the post covers the direction that Microsoft are aiming to take with the next release of Explorer. If you want a good idea of the reaction to this post I suggest you check out the posts comments.


A picture says a thousand words, so here are some pictures (and quotes) taken out of context and hopefully they will say something:

One common test of script performance is from Apple's Webkit team, the SunSpider test




Our goal is to deliver better performance across the board for real-world sites, not just benchmarks.


With IE8, we delivered a highly-interoperable implementation of CSS 2.1


Some standards tests - like Acid3 - have become widely used as shorthand for standards compliance, even with some shortcomings. As we improve support in IE for technologies that site developers use, the score will continue to go up.




A more meaningful (from the point of view of web developers) example of standards support involves rounded corners.






Another example of standards support that matters to web developers is CSS3 selectors.




Ultimately, we want to work with the community and W3C and other members of the working groups to define true validation test suites, like the one that we're all working on together for CSS 2.1, for the standards that matter to developers.



more...

Google AJAX libraries CDN

Thursday, 19 November 2009

View Comments

Are you using the Google AJAX libraries CDN for your applications? Google offers the simplest and fastest way to deliver the latest version of your favourite AJAX libraries over the net.

With a few lines of code you can grab your preferred libraries without having to download or host them yourself. You can even specify a specific version.

Libraries include:

I now regularly use this method to include jQuery and the jQuery UI and the speed is definitely comparable to hosting it yourself.

Here's an example of including jQuery 1.3.2:
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
 google.load("jquery", "1.3.2");
</script>

It also parses as valid HTML.

No need to worry about downloading another copy of jQuery just include the lines above to get the version you need.

John Resig also mentioned in the first jQuery podcast that eventually core plug-ins and add-ons will also be available across the CDN so look forward to seeing that in the near future also.

Enjoy!
more...