Subscribe

Monday 22 March 2010

YouTube Video Feed Slide-Show Gadget With JQuery Part 2

This is the second part in my ongoing series of posts which will show you how to get You-Tube video feed and build a simple carousel or slide-show for yourself. If you have not read the first part, please make sure you do so here Part1.
Target For The Post
A Javascript class which uses jquery and the youtube class designed earlier to create a slide show or carousel in any document or website.

Class To Design

  1. tubeslider
  2. SlideTimer
tubeslider methods
  1. initex(tube_data)
  2. start()
  3. stop()
tubeslider class variables

   1. this.timer_obj -----> stores the object of SlideTimer class
   2. this.timer_interval -> stores the user-defined speed in milliseconds
   3. this.divid ----------> stores the original div given by the user where this slider will be created
   4.  this.slider ---------> stores the new div created by the program used to slide the images or videos
   5.  this.master --------> stores the string to identify the main div given by the user, for jQuery it has a #                    infront of it

global variables

  1. var thumb_width -----> stores the thumb width
  2. var master_width -----> stores the mast_div width
  3. var thumb_height------> stores the thumb height

tubeslider constructor

   function(doc, divid, timer_interval) 
parameter doc is for future use and is not used anywhere at present
divid is the id of the div where you want your slider or gadget to reside
timer_interval is the speed in which you want the slider to move

remove any previous instance of the SlideTimer class and attach the CSS stylesheet to the document.

 if (this.timer_obj != null && this.timer_obj != undefined) {
        this.timer_obj.stop();
        delete this.timer_obj;
    }

    this.timer_obj = null;
    var slider_css = document.createElement('link');
    slider_css.rel = 'stylesheet'
    slider_css.type = 'text/css';
    slider_css.href = 'youtubeslider.css';
    document.getElementsByTagName('head')[0].appendChild(slider_css);
This function is the main function for this class, it receives the data in double dimension array called tube_data, then it initializes the thumb_width and height based on the data which is available and not the any random value, by default all the thumb size should be the same yet we search for the first legitimate thumb size and use it in our program.
var j = 0;
        while (j < tube_data.length && tube_data[j]["thumb_url"] == '#') {
            j++;
        }
        if (j >= tube_data.length)
            return -1;
        var k = 0;
        for (var i_loop = 0; i_loop < tube_data.length; i_loop++) {
            if (tube_data[i_loop]['thumb_url'] != '#')
                k++;
        }
        master_width = (parseInt(tube_data[j]["thumb_width"]) + 10) * (k);
        thumb_width = parseInt(tube_data[j]["thumb_width"]);
        thumb_height = parseInt(tube_data[j]["thumb_height"]);
Next we remove any previous divs inside the main div and create two divs one as the container for the slider and one for the player or the window where the video will be played when it is clicked. The div is also inside the main div thus it cannot be created outside the main div body. Inside the player div there are two divs first one has a link when clicked it will close the window and the other one will house the player. Then we create the slider div to house all the clickable thumbnails
var temp2 = temp + ' > div';
        $(temp2).remove();
        $(temp).css('position', 'absolute');
        $(temp).append("
"); $(temp).append("
" + "
"); var close_win = '#' + this.divid + '_close'; var temp_id2 = this.divid; $(close_win).click(function(event) { event.preventDefault(); if (temp_id2.charAt(0) != '#') temp_id2 = '#' + temp_id2 + '_player'; $(temp_id2).hide(); }); var master_id1 = '#' + this.divid + '_player'; var player_width1 = parseInt($(master_id1).css('width')); var player_height1 = parseInt($(master_id1).css('height')) - 17; //div to show the text temp2 += ":first"; this.master = temp + "master"; this.slider = this.divid + '_slider'; $(this.master).addClass('master'); $(this.master).append("
");
Next step is to create a image as link for the the videos which has been sent through tube_data, when we draw these images we must make sure that the url property has a valid url and not a # symbol, which signifies that the video has been blocked by the content provider.
 this.slider = '#' + this.slider;
        for (var i = 0; i < tube_data.length; i++) {
            if (tube_data[i]["thumb_url"] != '#') {
                var div_tag = "
"; var a_tag = ""; var img_tag = ""; var ending_tags = "" + tube_data[i]['thumb_title'] + "(vid " + tube_data[i]['vid'] + ")" + "
"; $(this.slider).append(div_tag + a_tag + img_tag + ending_tags); } }
After we have the slider ready to rumble the next thing is to create a timer object so that at a predefined interval the slider div is moved by a certain amount. If mouse is hovered over the images it must stop the slide or the timer until the mouse moves out of the div. In this we use jquery $.hover() function which gives us the opportunity to declare two functions one is fired when mouse is moved over and the second is fired when the mouse is moved out. For ease of programming we have used anonymous functions.
        var local_slider = this.slider;
        var local_master = this.master;
        var temp_del = this.slider + " > div"
        this.timer_obj = new SlideTimer(this.slider, this.master, this.timer_interval, this);
        var timer_obj_ref = this.timer_obj;

$(temp_del).hover(function() {
            timer_obj_ref.stop();
            var id_text = $(this).attr('id');
            if (id_text != undefined || id_text == '') {
                var i = id_text.length - 1;

                var chr_txt = id_text.charAt(i);
                while (!isNaN(parseInt(chr_txt))) {
                    i--;
                    var chr_txt = id_text.charAt(i);
                }
                id_text = id_text.substr(0, (i + 1));
                var txt_div = '#' + id_text + '_text';

                $(txt_div).css('display', 'block');
                $(txt_div).html("" + $(this).text() + "");



            }

        },
        function() {
            timer_obj_ref.start();
            var id_text = $(this).attr('id');
            if (id_text != undefined || id_text == '') {
                var i = id_text.length - 1;

                var chr_txt = id_text.charAt(i);
                while (!isNaN(parseInt(chr_txt))) {
                    i--;
                    var chr_txt = id_text.charAt(i);
                }
                id_text = id_text.substr(0, (i + 1));
                var txt_div = '#' + id_text + '_text';
                $(txt_div).css('display', 'none');
            }

        });

Lets say you want to watch some particular video, the logical thing will be to click on the image and it should play. To do so we must trap the click event and show the player div which we created earlier with the embedded player and the correct url.
var master_id = this.divid;
        $(temp_del).click(function(event) {
            timer_obj_ref.stop();
            event.preventDefault();
            var vid_text = $(this).text();

            if (vid_text != undefined || vid_text == '') {
                var i = vid_text.length - 1;
                var vid_mark1 = vid_text.indexOf('(vid');
                var vid_mark2 = -1;
                if (vid_mark1 != -1) {
                    vid_mark1 += 4;
                    vid_text = vid_text.substr(vid_mark1, ((vid_text.length - 1) - vid_mark1));
                }

                var playerid = '#' + master_id + '_player';
                vid_text = vid_text.replace(/^\s+|\s+$/g, "");

                var player_width = parseInt($(playerid).css('width'));
                var player_height = parseInt($(playerid).css('height'));

                var str_player = '<object style="position:relative;display:block;top:15px;left:1px;height' +
                (player_height - 17).toString() + 'px; width:' + (player_width - 2).toString() + 'px">' +
                '<param name="movie" value="http://www.youtube.com/v/' + vid_text +
                '?version=3&iv_load_police=3&autoplay=1&color1=0xb1b1b1&color2=0xcfcfcf&feature=player_embedded">' +
                '<param name="allowFullScreen" value="true">' +
                '<param name="allowScriptAccess" value="always">' +
                '<embed src="http://www.youtube.com/v/' + vid_text +
                '?version=3&iv_load_police=3&autoplay=1&color1=0xb1b1b1&color2=0xcfcfcf&feature=player_embedded" type="application/x-shockwave-flash" allowfullscreen="true" allowScriptAccess="always" width="' +
                 (player_width - 2).toString() + '" height="' + (player_height - 17).toString() + '"></object>'

                $(playerid).css('display', 'block');

                var player_div = '#' + master_id + '_ytapiplayer';
                $(player_div).html(str_player);

            }
        });

The next two functions are trivial, they just start or stops the slide action for the user, actually it starts or stops the timer through the timer object declared earlier.
this.start = function() {
        if (this.timer_obj != null && this.timer_obj != undefined)
            this.timer_obj.start();
    }
    this.stop = function() {
        if (this.timer_obj != null && this.timer_obj != undefined)
            this.timer_obj.stop();
        delete (this.timer_obj);
        this.timer_obj = null;
    }
This is the complete code for our tubeslider class now comes the timer class which controls the timer object.

  SlideTimer Variables


 1. this.slider = slider; 
 2. this.master = master; 
 3. this.obj = slide_obj;
 4. this.timer_interval = timer_interval; 5. this.timerid = null;

SlideTimer Methods


1. start()
2. stop()
3. slideImages()

The first two methods are trivial they just initiate a setInterval function in the start method and clearInterval in the stop method. The only point to be noted is that setInterval creates an anonymous function which in turn calls the slideImages method.This is done in this way because a normal setInterval takes the function name only and does not allow you to prefix it with this to show a class method.
slideImages function uses the jQuery animate function to move the slider on the left by a fixed amount, if the whole slider has moved out of the screen i.e. it is no longer visible the start position is reintialized to the extreme left of the master div which we created in the earlier class as wrapper for the slider and player div.




SlideTimer = function(slider, master, timer_interval, slide_obj) {

    this.slider = slider;

    this.master = master;

    this.obj = slide_obj;

    this.timer_interval = timer_interval;

    this.timerid = null;

    this.start = function() {

       

        thisobject = this;

        this.timerid = setInterval(function() { thisobject.slideImages(); }, this.timer_interval); //"' + slider + '","' + master + '","' + obj + '")', 6000);



    }

    this.stop = function() {

        clearInterval(this.timerid);

    }





    this.slideImages = function() {

       

        var left_attr = parseInt($(this.slider).css('left'));

        var width_attr = parseInt($(this.master).css('width'));

        var slider_width = parseInt($(this.slider).css('width'));

        var animate_str = '-=' + thumb_width.toString();

        if (left_attr > (1 - (slider_width - Math.ceil((thumb_width ))))) {

            left_attr -= thumb_width;



        }

        else {

            left_attr = width_attr - (thumb_width * 2);

           

            $(this.slider).css('left', left_attr.toString() + 'px');

          

        }

     

        $(this.slider).animate({ left: animate_str }, 'slow');

       



    }

}





If you want to download the whole code please go to http://coolnerdblogger.com/mydownloads/YoutubeSlide/YouTubeSlide.php
If you want to test it out first then visit
http://coolnerdblogger.com/mydownloads/YoutubeSlide/jQueryAndYouTube.html
For more updates subscribe to my feed and checkout the videos of these posts, the video for the first part is already online check it out.

Thursday 18 March 2010

YouTube Video Feed Slide-Show Gadget With JQuery Part 1

This is the first part in what is going to be a two or three part tutorial dealing with using the YouTube Feed API to create a slide show of videos which you prefer, not some random images but based on the search criteria or playlist or uploaded videos by the user of your choice.

Target For This Post
Part 1:
A java script class which can request a video feed from YouTube based on your selection criteria and then parse the feed returned to you. This will be done without any server-side script. The class can return an array which will have the following six attributes for each video.

thumb_title The title to be shown for the video
thumb_img The thumbnail which will represent the video
thumb_height The height of the thumbnail
thumb_width The width of the thumbnail
thumb_url The url of the video
vid Video Id used by youtube to identify a video uniquely
This class will have the following major methods
  • getUploadedVideos (username, format)
  • getSearchVideos(tag_txt, boolean_op, format)
  • getPlayListVideos(username, playlist, format)
  • startTubeSlideShow(player_div_id, seconds)
  • stopTubeSlideShow()
Technology Used
  1. Google Data API’s
  2. jQuery
  3. Javascript
  4. HTML
Google Data API has been used to request video RSS feed from the google or you-tube sever. The format which I have used is json-in-script format. For more details about this format please click here. The Google Data API has a very rich and fulfilling  structure. It can be modified in any way you want. If you are using proxies and are interested in getting an XML RSS feed no problem just add alt=rss to your request url and you will get in pure RSS 2.0 format. To learn more about this API you can visit here.
courtesy Google
Name Definition
alt The alt parameter specifies the format of the feed to be returned. Valid values for this parameter are atom, rss, json, json-in-script, and jsonc. The default value is atom and this document only explains the format of Atom responses. For more information about using API responses in JavaScript, please see Using JSON with Google Data APIs.
If you set the parameter value to json-in-script, then you must also set the
callback parameter value to identify the callback function to which the API response will be sent.
author In a search request, the author parameter restricts the search to videos uploaded by a particular YouTube user. Note that if you use this parameter when requesting a standard feed, then YouTube will retrieve the standard feed and then filter the response to only include videos uploaded by the specified author. For example, if you request the "Top Rated" feed for user GoogleDevelopers, the API will retrieve the top-rated feed and return a response containing videos in that feed uploaded by user GoogleDevelopers. The API will not return a list of that user's videos ordered by rating. 
In a request for a user activity feed, the author parameter contains a comma-separated list of up to 20 YouTube usernames. The API response will contain activities performed by any of those users.
callback The callback parameter identifies the callback function to which the API response will be sent. If the value of the alt parameter is jsonc or json-in-script, and you specify a value for the callback parameter, then the API response will be wrapped in a call to the specified function. This parameter is required if the value of the alt parameter is json-in-script.
max-results The max-results parameter specifies the maximum number of results that should be included in the result set. This parameter works in conjunction with the start-index parameter to determine which results to return. For example, to request the second set of 10 results – i.e. results 11-20 – set the max-results parameter to 10 and the start-index parameter to 11. The default value of this parameter is 25, and the maximum value is 50. However, for displaying lists of videos, we recommend that you set the max-results parameter to 10.
prettyprint The prettyprint parameter lets you request an XML response formatted with indentations and line breaks. Set the parameter's value to true to have the response formatted as such. The default parameter value is false.
start-index The start-index parameter specifies the index of the first matching result that should be included in the result set. This parameter uses a one-based index, meaning the first result is 1, the second result is 2 and so forth. This parameter works in conjunction with the max-results parameter to determine which results to return. For example, to request the second set of 10 results – i.e. results 11-20 – set the start-index parameter to11 and the max-results parameter to 10.
strict The strict parameter can be used to instruct YouTube to reject an API request if the request contains invalid request parameters. The default API behavior is to ignore invalid request parameters. If you want YouTube to reject API requests that contain invalid parameters, set the strictparameter value to true. For example, YouTube would reject the following request because "foo" is not a valid request parameter.
http://gdata.youtube.com/feeds/api/videos?foo=nonono&strict=true
v The v parameter specifies the version of the API that YouTube should use to handle the API request. Your request can specify the desired API version using either the v parameter or the GData-Version HTTP request header. All Google-provided client libraries always select a major API version. If your request does not specify an API version, YouTube will handle your request using API version 1. Please see the API versioningsection for more information about specifying an API version.

Say for instance you want to search for videos with a tag India the URL will be something like this
http://gdata.youtube.com/feeds/videos?alt=json-in-script&callback=?&q=India
In our case due to the use of jQuery which provides a call back for us we leave the callback value blank, but remember if you do not provide a question mark infront of the call back the computer will not make any request because of the strange way jQuery works.(I am not complaining).
jQuery has a method called . This method is wonderful and my new favourite because if any feed returns JSON then you can use getJSON to make a cross domain XMLHTTPRequest without those proxies and server side code. Yes it does take advantage of a security hole in the browser. However I believe very soon such requests will become a common place occurrence if it is not already so.
Essentially I ask jquery to brig some result and then I parse it using javascript. Because getJSON can work Asynchronously therefore my whole website is not blocked and I can assign a call back function which is fired when the server returns the data. Again the beauty of javascript is I can use anonymous functions as a call back instead of separate functions.
jQuery.getJSON( url, [ data ], [ callback(data, textStatus) ] )
urlA string containing the URL to which the request is sent.
dataA map or string that is sent to the server with the request.
callback(data, textStatus)A callback function that is executed if the request succeeds.


HTML has been used to create the front end and is inserted in the document as and when required by the various parts.

Short-coming
I have used few global variables to make my life easier, if you find any other method to overcome the problems please feel free to mail me.
The CSS is amateurish if not childish but the beauty of this code is you can change the CSS to suit your own fancies.


simpleTube.js
This file has the code which connects with Google to request anything that you require

SimpleTube this is the class which is has the functions dealing with playlist,search and uploaded videos.

getUploadedVideos Bring all the videos which are publicly available from a particular user


this.getUploadedVideos = function(username, format) {     
        var q = "http://gdata.youtube.com/feeds/api/users/" + username + "/uploads?alt=json-in-script&callback=?" ;
        if (format == '5') { 
            q += "&format=5";
        } 
        $.getJSON(q, function(json) {
            var htmlstr = ''; 
            tube_data = new Array(json.feed.entry.length);
            $.each(json.feed.entry, function(i, pl_item) {
                  tube_data[i] = new Array(6);
                if (pl_item['app$control'] != undefined && pl_item['app$control']['yt$state'] != undefined) {
  
                   var thumb = pl_item['app$control']['yt$state']['$t'];
                   var url = '#';
                   tube_data[i]["thumb_url"] = '#';
                   tube_data[i]["vid"] = pl_item['app$control']['yt$state']['$t'];
  
                } 
                else {
                    tube_data[i]["thumb_title"] = pl_item['title']['$t'];
                    tube_data[i]["thumb_img"] = pl_item['media$group']['media$thumbnail'][0]['url'];
                    tube_data[i]["thumb_width"] = pl_item['media$group']['media$thumbnail'][0]['width'];
                    tube_data[i]["thumb_height"] = pl_item['media$group']['media$thumbnail'][0]['height'];
                   tube_data[i]["thumb_url"] = pl_item['media$group']['media$player'][0]['url']
                  var vid_1 = tube_data[i]["thumb_url"].search('v=');
                  var vid_2 = tube_data[i]["thumb_url"].search('&feature');
                  var vid_len = vid_2 - (vid_1 + 2);
                 tube_data[i]["vid"] = tube_data[i]["thumb_url"].substr(vid_1 + 2, vid_len);
  
                } 

            }); 


        });

  
    }
This function is the easiest one as it accepts a username and the format(I will explain it later). The username is the user whose video you want to watch. As long as there is no restriction placed by the uploader you can easily get all the videos. Once we get the videos we parse the JSON in a double dimension array. for the first part we will not do anything with this array in the next part I will show you how to use this array. Format, if you place 5 as the format parameter then all the videos which cannot be embedded and are blocked or removed, are not included in the search, thus it brings only those videos which can be embedded.
$.getJSON(q, function(json) {
in this line I make a call to youtube for the videos and when the query returns with result the anonymous function gets called which has the result in the variable called json.

$.each(json.feed.entry, function(i, pl_item) {
for each entry of the feed an anonymous function is called which has a counter i, incremented automatically and that particular entry in the variable called pl_item.

if (pl_item['app$control'] != undefined && pl_item['app$control']['yt$state'] != undefined) {


checks to make sure that the video thus available can be used and is not blocked or removed. If the video has a problem then the thumb_url property is given a value of #. this will be used later to filter out the undesired content.

tube_data[i]["vid"] = pl_item['app$control']['yt$state']['$t'];


this stores the reason if any for the video being not available. If the video is usable then the five properties thumbnail width,height,and the src with video url and title are stored then we parse the url string for the VID or the video id used by youtube to uniquely identify a video and needed for loadind a video in a player.


getSearchVideos bring all the videos according to the tags and the boolean operator specified. Three operators are possible and or and random. If you say rand or random then the number of tags and the tags are chosen randomly thus each time the video might be a bit different.


this.getSearchVideos = function(tag_txt, boolean_op, format) {
        var tag_arr = tag_txt.split(',');
        var q = "http://gdata.youtube.com/feeds/videos?max-results=40&alt=json-in-script&callback=?&orderby=relevance&sortorder=descending&q=";

        var j = 0;

        var k = 0;

        var qstring = '';
        if (boolean_op == 'and') {

            qstring = "%22" + tag_arr[0];

            for (k = 1; k < tag_arr.length; k++) {

                qstring += "+" + tag_arr[k];

            }

            qstring += "%22";

        }

        if (boolean_op == 'or') {

            qstring = tag_arr[0];

            for (k = 1; k < tag_arr.length; k++) {

                qstring += "%7c" + tag_arr[k];

            }

        }

        if (boolean_op == 'rand') {
            var randomnumber = Math.floor(Math.random() * (tag_arr.length - 1)) + 1;
            qstring = tag_arr[randomnumber];
            for (k = 0; k <= randomnumber; k++) {
                randomnumber = Math.floor(Math.random() * tag_arr.length);
                qstring += "%7c" + tag_arr[randomnumber];

            }

        }


        q = q + qstring;

        if (format == '5')

            q += "&format=5";
        $.getJSON(q, function(data) {

            var htmlstr = '';

            tube_data = new Array(data.feed.entry.length);

            $.each(data.feed.entry, function(i, feed_item) {

                tube_data[i] = new Array(6);

                if (feed_item['app$control'] != undefined && feed_item['app$control']['yt$state'] != undefined) {

                    var thumb = feed_item['app$control']['yt$state']['$t'];

                    var url = '#';

                    tube_data[i]["thumb_url"] = '#';

                    tube_data[i]["vid"] = feed_item['app$control']['yt$state']['$t'];

                }

                else {

                    tube_data[i]["thumb_title"] = feed_item['title']['$t'];
                    tube_data[i]["thumb_img"] = feed_item['media$group']['media$thumbnail'][0]['url'];
                    tube_data[i]["thumb_width"] = feed_item['media$group']['media$thumbnail'][0]['width'];
                    tube_data[i]["thumb_height"] = feed_item['media$group']['media$thumbnail'][0]['height'];
                    tube_data[i]["thumb_url"] = feed_item['media$group']['media$player'][0]['url']
                    var vid_1 = tube_data[i]["thumb_url"].search('v=');

                    var vid_2 = tube_data[i]["thumb_url"].search('&feature');

                    var vid_len = vid_2 - (vid_1 + 2);

                    tube_data[i]["vid"] = tube_data[i]["thumb_url"].substr(vid_1 + 2, vid_len);

                }

            });
   
        });

    }


This function is a bit more involved because it combines all the tags depending on the user choice. User can ask all the tags to be compulsory(and), any of the tags(or). The user might like a sub-set of new tags to be used from the original tag each time the program is executed.

var randomnumber = Math.floor(Math.random() * (tag_arr.length - 1)) + 1;
            qstring = tag_arr[randomnumber];

            for (k = 0; k <= randomnumber; k++) {
                randomnumber = Math.floor(Math.random() * tag_arr.length);
                qstring += "%7c" + tag_arr[randomnumber];

            }


The Lines above just choose a random number which signifies the number of tags to be considered and then inside a loop each time a new random number is generated to signify the tag chosen.


getPlayListVideos This function gets a playlist depending on the user-name and the playlist name. Unfortunately only those play list are available which have been marked public by the creator, if any play-list is marked as private it will not be available through this function.


this.getPlayListVideos = function(username, playlist, format) {

        var q = "http://gdata.youtube.com/feeds/api/users/" + username + "/playlists?alt=json-in-script";

        q += "&callback=?";

        $.getJSON(q, function(data) {





            $.each(data.feed.entry, function(i, item) {

                var title = item['title']['$t'];

                if (title.toLowerCase() == playlist.toLowerCase())

                    q = item['gd$feedLink'][0]['href'];





            });

            if (format == '5')

                q += "?alt=json-in-script&callback=?&format=5";

            else

                q += "?alt=json-in-script&callback=?&format=5";

            $.getJSON(q, function(json) {

                var htmlstr = '';



                tube_data = new Array(json.feed.entry.length);

                $.each(json.feed.entry, function(i, pl_item) {

                    var title2 = pl_item['media$group']['media$title']['$t'];



                    tube_data[i] = new Array(6)

                    if (pl_item['app$control'] != undefined && pl_item['app$control']['yt$state'] != undefined) {

                        var thumb = pl_item['app$control']['yt$state']['$t'];

                        var url = '#';

                        tube_data[i]["thumb_url"] = '#';

                        tube_data[i]["vid"] = pl_item['app$control']['yt$state']['$t'];

                    }



                    else {

                        tube_data[i]["thumb_title"] = pl_item['title']['$t'];

                        tube_data[i]["thumb_img"] = pl_item['media$group']['media$thumbnail'][0]['url'];

                        tube_data[i]["thumb_width"] = pl_item['media$group']['media$thumbnail'][0]['width'];

                        tube_data[i]["thumb_height"] = pl_item['media$group']['media$thumbnail'][0]['height'];

                        tube_data[i]["thumb_url"] = pl_item['media$group']['media$player'][0]['url']

                        var vid_1 = tube_data[i]["thumb_url"].search('v=');

                        var vid_2 = tube_data[i]["thumb_url"].search('&feature');

                        var vid_len = vid_2 - (vid_1 + 2);

                        tube_data[i]["vid"] = tube_data[i]["thumb_url"].substr(vid_1 + 2, vid_len);

                    }





                });

            });

    });



    }


This function has two getJSON calls why because, the first call brings all the playlist of the user specified. Then the url for the playlist specified is parsed from the first feed result. After this we get the actual request URL for the playlist. this url is used to fire the second request.
The second request brings the videos and their data.
As mentioned earlier, I have not shown you how to use the parsed data because that is another post may be later in the week. I will put up a test page and a you tube video for the full code very soon. Please subscribe to my feed for further updates. If you want to watch a real-life example please click here or visit http://coolnerdblogger.com/mydownloads/YoutubeSlide/jQueryAndYouTube.html


Saturday 13 March 2010

Google Authentication Problems

<script type="text/javascript">
    google.load("gdata", "2.x");
    google.setOnLoadCallback(getMyBlogFeed);
    var feedUrl = "http://feeds.feedburner.com/WorldOfWidgetsAndGadgets"
    var myService;
    function setUpService() {
     var myService = new google.gdata.blogger.BloggerService('exampleC');
     logMeIn();
     return myService; 
    }
    function getMyBlogFeed() {

     myService = setUpService();
     myService.getBlogPostFeed(feedUrl, handleMyFeed, handleError);


 }
 function logMeIn() {
  scope = "http://www.blogger.com/feeds";
  var token = google.accounts.user.login(scope);
 }

 function handleMyFeed(myResultsFeedRoot) {
  alert("This feed's title is: " + myResultsFeedRoot.feed.getTitle().getText());
 }

 function handleError(e) {
  alert("There was an error!");
  alert(e.cause ? e.cause.statusText : e.message);
 }
</script>
This little code which in general is a copy from the Google Sample Code is giving me nightmares. Somehow it refuses to authenticate my application even though I have a google account and a related blogger account. I could not find any reason for this malfunction as it keeps showing the same authentication page again and again. I have tried to run this code from my localhost as well as a web-server. To no avail. I will revisit this problem in a couple of days , until then I have a little mash up code to write dealing with you tube and jquery.

Sunday 7 March 2010

Using Flickr Feed To Create Blogger Gadget

Hi there folks I am back with something different this time around. As this blog deals with widgets and gadgets, one side of the story which I missed out was Blogger gadgets, as you all know who have worked on blogger sometime or the other, these gadgets as they call make your blog either more intresting or adds value to your site. Thus this topic got me intrested. In one of my previous blog, I dealt with getting javascript working on wordpress, the same can be done to some extent with blogger.Unfortunately I dont beleive it to be the best way out.
Gadgets are the proper way of organising your site interface and avoiding unecessary clutter.
Blogger gadgets are nearly same as the google gadgets which you use on your igoogle page.
First thing to understand is these gadgets are nothing more than XML files with HTML/javascript thrown into the mixture. Therefore if you are an experienced HTML developer or Javascript developer, you will fit right into it.
As usual lets talk about some basic theory, google as well as blogger gadgets have this structure you must follow
Basic Theory
<?xml version="1.0" encoding="UTF-8" ?>    
<Module>     
  <ModulePrefs title="hello world example" /> 

<UserPref……/>    
  <Content type="html">     
     <![CDATA[     
       Hello, world!     
     ]]>     
  </Content>     
</Module>

Lets try to figure out each element one by one
Module tag as we know is the root element of the XML file. Everything else is contained inside this element
ModulePrefs tag creates the meta-data for your gadget to be used by any site which is hosting this gadget either it is blogger or orkut or igoogle. These are the few customizations which google API provides for you
Following few lines are exact copy from http://code.google.com/apis/gadgets/docs/reference.html
Attribute Description
title
Optional string that provides the title of the gadget. This title is displayed in the gadget title bar on iGoogle. If this string contains
user preference substitution variables and you are planning to submit your gadget to the iGoogle content directory, you should also provide a directory_title for directory display.
title_url
Optional string that provides a URL that the gadget title links to. For example, you could link the title to a webpage related to the gadget.

description
Optional string that describes the gadget.

author
Optional string that lists the author of the gadget.

author_email
Optional string that provides the gadget author's email address. You can use any email system, but you should not use a personal email address because of spam. One approach is to use an email address of the form helensmith.feedback+coolgadget@gmail.com in your gadget spec.
Gmail drops everything after the plus sign (+), so this email address is interpreted as helensmith.feedback@gmail.com.
You can create a Gmail account
here.
screenshot
Optional string that gives the URL of a gadget screenshot. This must be an image on a public web site that is not blocked by robots.txt. PNG is the preferred format, though GIF and JPG are also acceptable. Gadget screenshots should be 280 pixels wide. The height of the screenshot should be the "natural" height of the gadget when it's in use.

thumbnail
Optional string that gives the URL of a gadget thumbnail. This must be an image on a public web site that is not blocked by robots.txt. PNG is the preferred format, though GIF and JPG are also acceptable. Gadget thumbnails should be 120x60 pixels.


Then comes UserPref tag, this tag is the customization element for your gadget, for example you may require some data for the gadget to work like username or password, this is the element which provides the user with the opportunity to enter data for the gadget. Have a look at the picture below, this is like form which is presented to you when you either install the gadget for the first time or when you edit it later on
blog1
As mentioned all the userpref’s are provided by the author of the gadget, in my case it was pretty simple I provided the Tag to search and Refresh Rate, rest of the customization is provided by the gadget itself you don’t have to worry about it.
UserPref can have the following attributes
name
Required "symbolic" name of the user preference; displayed to the user during editing if no display_name is defined. Must contain only letters, number and underscores, i.e. the regular expression ^[a-zA-Z0-9_]+$. Must be unique.
display_name
Optional string to display alongside the user preferences in the edit window. Must be unique.
urlparam
Optional string to pass as the parameter name for content type="url".
datatype
Optional string that indicates the data type of this attribute. Can be string, bool, enum, hidden (a string that is not visible or user editable), or list(dynamic array generated from user input). The default is string.
required
Optional boolean argument (true or false) indicating whether this user preference is required. The default is false.
default_value
Optional string that indicates a user preference's default value.
Moving forward the next element of interest is Content. It is the heart and soul of your gadget or in simple terms the basic body of this gadget is the content section.Content section may have URL type or HTML type. In this post we will discuss HTML type. Inside this tag you place your HTML or javascript. these must be contained in a CDATA section. You can link to external JS files too.
This is the basic hierarchy of a google/blogger gadget. Now we are ready to define the gadget we need to build.

Gadget Creation

In this post I shall create a slide show gadget of random pictures from Flickr based on any tag that you provide. This gadget may be used from anywhere and can be downloaded from here.
Firstly we must understand that Flickr provides us with an API to interact with itself, therefore we must make a request just like an XMLHTTPRequest shown in my previous post. Unfortunately this is not possible. Why because cross-domain XMLHTTPRequest is not allowed.The reason for this is a topic in itself and we shall not go into it here. Suffice to say that it creates more headaches rather than solutions. But don not despair help is on hand in the form of jquery. JQuery provides us with a handy function to make cross domain request thus we do not need to get stuck with some ugly server code.
$.getJSON is the function which we will call to get the feed in JSON format from Flickr. If you want to know more about jquery please send me a mail and I  will do a tutorial for that. At present my jquery function gets the photos and adds two divs for each photo, one has the pic and the other has the tile. All the divs except the first one are hidden and we will use jquery to show and hide them.
The user can give us the tag to search and the refresh rate that is the time between changing of two images.
function getimages(tags)    
{ 

    var query="http://api.flickr.com/services/feeds/photos_public.gne?tags=" + tags + "&tagmode=any&format=json&jsoncallback=?";     
var x=$.getJSON(query,function(data){     
    var htmlstr="";     
    var htmltxt="";     
    var div_img=$('#PlaceImages');     
    var div_txt=$('#PlaceText');     
    $.each(data.items,function(i,item)     
    {     
        var sourceSquare = (item.media.m).replace("_m.jpg", "_s.jpg");     
        htmlstr="<a target='_blank' href='" + item.link + "'><img src='" + item.media.m + "' width='100%' height='90%'/></a>";     
        htmltxt="<a target='_blank' href='" + item.link + "' >" + item.title + " </a>";     
        if(i>0)     
        {     
            div_img.append("<div id='div" + i.toString() + "' style='left:0px;top:0px;display:none;width:100%;height:99%;z-index:"+i+"'>" + htmlstr +"</div>");     
            div_txt.append("<div id='div_txt" + i.toString() + "' style='left:0px;top:0px;display:none;width:100%;height:99%;z-index:"+i+"'>" + htmltxt +"</div>");     
        }     
        else     
        {     
            div_img.append("<div id='div" + i.toString() + "' style='left:0px;top:0px;height:100%;width:99%"+i+"'>" + htmlstr +"</div>");     
            div_txt.append("<div id='div_txt" + i.toString() + "' style='left:0px;top:0px;width:100%;height:99%;z-index:"+i+"'>" + htmltxt +"</div>");     
        } 

    }    
    );

    });    
}

Looks complicated but it is not, everything is being done by jquery. In our original document we have two divs one is called PlaceImages and the other is called PlaceText. What I am doing is appending a new div for each image object. Therefore htmlstr creates a link with the image object and the script puts this link in the new div, each new div has a unique name which is created by “div” + the sequence number. This will be used later. Same is true for the PlaceText div where a new div with the photo title is added.
Most important part of all this is the divs so created are all hidden except the first one div0 and div_txt0. How do we unhide them that will be shown later. I hope I have made my point clear. After the this function finishes the control is returned to the original function shown below.
function getPics() {    
    var prefs = new _IG_Prefs();     
    var tags = prefs.getString("tags");     
    rate=parseInt(prefs.getString("rate"));     
    var linkColor=document.getElementById("PlaceText");     
    linkColor.style.color=gadgets.skins.getProperty('CONTENT_LINK_COLOR');     
    document.body.style.borderColor = gadgets.skins.getProperty('CONTENT_BG_COLOR');     
    getimages(tags);     
    id=setInterval("changeImage()",32000);     
}

This is the function which is executed when the gadget is loaded. Thus the first line creates a new preference object which will get us all the user preference values.Next two lines get the tag and refresh rate from the user preference. Again I have tried to show you how the blogger widget can use the gadgets.skin class to get the different values for the theme being used and modify your gadget CSS accordingly. Finally we call our function getimages with the user given tag. After the function has been called a timer is set up which will fire every 32000 milli second.Each time the timer is fired the function changeImage will be called.
As mentioned above all the showing and hiding is done in this function, basically this function handles the slide show and disables or enables the timer.
function changeImage()    
    {     
        var y=$('#PlaceImages>div:visible').attr('id');     
        var txt=$('#PlaceText>div:visible').attr('id');     
        //alert(y);     
        if(y!=undefined)     
        {     
            clearInterval(id);     
            var temp1=y.charAt(y.length-1);     
            var temp2=y.charAt(y.length-2);     
            var num=0;     
            if(isNaN(temp2)!=true)     
            {     
                num=parseInt(temp2)*10 + parseInt(temp1);     
            }     
            else     
                num=parseInt(temp1);     
            num+=1;     
            if(num>=20)     
                num=0;     
            hidediv="#" + y;     
            showdiv="#div" + num.toString();     
            showdiv_txt="#div_txt" + num.toString();     
            hidediv_txt="#"+txt;     
            $(showdiv).fadeIn('slow');     
            $(showdiv_txt).fadeIn('fast');     
            $(hidediv).fadeOut('slow');     
            $(hidediv_txt).fadeOut('slow');     
            if(isNaN(rate)==true)     
                rate=9500;     
            id=setInterval("changeImage()",rate);     
        }     
    }

I shall not go into the details of the code because it is quite clear what is going on.  Each time it selects the visible div and using the number at the end of the id shows the next div while the original is hidden. If div0 was visible to start with next time this function gets called div1 is visible and div0 is hidden. Thus creating a slide show. The whole code is shown below .If you want to see this widget working please visit http://widgetsgadgetsworld.blogspot.com/
<?xml version="1.0" encoding="UTF-8"?>   
<Module>    
    <ModulePrefs    
    title="Flickr Photo"    
    author="Someone"    
    title_url="http://best-news-site.com/"    
    author_email="somewhere@lycos.com"    
    description="Flickr image slide show">    
        <Require feature="opensocial-0.7"/>    
        <Require feature="google.blog"/>    
        <Require feature="skins"/>    
        <Require feature="views"/>    
    </ModulePrefs>    
    <UserPref display_name="Tag To Search" name="tags" datatype="string" default_value="india"    
              ></UserPref>    
    <UserPref display_name="Refresh Rate(ms)" name="rate" datatype="string" default_value="9500"    
              ></UserPref>    
    <Content type="html">    
        <![CDATA[    
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>    
<div id="PlaceImages"  style="width:100%;height:90%; overflow:hidden;float:left;"></div><br />    
<div id="PlaceText" style="width:100%;height:8%; text-align:center;overflow:hidden; clear:both;"></div>    
<script type="text/javascript">    
var rate=9500;    
var id;    
function getimages(tags)    
{ 

    var query="http://api.flickr.com/services/feeds/photos_public.gne?tags=" + tags + "&tagmode=any&format=json&jsoncallback=?";    
var x=$.getJSON(query,function(data){    
    var htmlstr="";    
    var htmltxt="";    
    var div_img=$('#PlaceImages');    
    var div_txt=$('#PlaceText');    
    $.each(data.items,function(i,item)    
    {    
        var sourceSquare = (item.media.m).replace("_m.jpg", "_s.jpg");    
        htmlstr="<a target='_blank' href='" + item.link + "'><img src='" + item.media.m + "' width='100%' height='90%'/></a>";    
        htmltxt="<a target='_blank' href='" + item.link + "' >" + item.title + " </a>";    
        if(i>0)    
        {    
            div_img.append("<div id='div" + i.toString() + "' style='left:0px;top:0px;display:none;width:100%;height:99%;z-index:"+i+"'>" + htmlstr +"</div>");    
            div_txt.append("<div id='div_txt" + i.toString() + "' style='left:0px;top:0px;display:none;width:100%;height:99%;z-index:"+i+"'>" + htmltxt +"</div>");    
        }    
        else    
        {    
            div_img.append("<div id='div" + i.toString() + "' style='left:0px;top:0px;height:100%;width:99%"+i+"'>" + htmlstr +"</div>");    
            div_txt.append("<div id='div_txt" + i.toString() + "' style='left:0px;top:0px;width:100%;height:99%;z-index:"+i+"'>" + htmltxt +"</div>");    
        } 

    }   
    );

    });   
}    
 function getPics() {    
    var prefs = new _IG_Prefs();    
    var tags = prefs.getString("tags");    
    rate=parseInt(prefs.getString("rate"));    
    var linkColor=document.getElementById("PlaceText");    
    linkColor.style.color=gadgets.skins.getProperty('CONTENT_LINK_COLOR');    
    document.body.style.borderColor = gadgets.skins.getProperty('CONTENT_BG_COLOR');    
    getimages(tags);    
    id=setInterval("changeImage()",32000);    
} 

function changeImage()   
    {    
        var y=$('#PlaceImages>div:visible').attr('id');    
        var txt=$('#PlaceText>div:visible').attr('id');    
        //alert(y);    
        if(y!=undefined)    
        {    
            clearInterval(id);    
            var temp1=y.charAt(y.length-1);    
            var temp2=y.charAt(y.length-2);    
            var num=0;    
            if(isNaN(temp2)!=true)    
            {    
                num=parseInt(temp2)*10 + parseInt(temp1);    
            }    
            else    
                num=parseInt(temp1);    
            num+=1;    
            if(num>=20)    
                num=0;    
            hidediv="#" + y;    
            showdiv="#div" + num.toString();    
            showdiv_txt="#div_txt" + num.toString();    
            hidediv_txt="#"+txt;    
            $(showdiv).fadeIn('slow');     
            $(showdiv_txt).fadeIn('fast');    
            $(hidediv).fadeOut('slow');    
            $(hidediv_txt).fadeOut('slow');    
            if(isNaN(rate)==true)    
                rate=9500;    
            id=setInterval("changeImage()",rate);    
        }    
    }    
 gadgets.util.registerOnLoadHandler(getPics); 

</script> 

]]> 

    </Content>   
</Module>

Wednesday 3 March 2010

How to create widgets Part4 Custom RSS Feed

Hi there everybody, it has been a few days since my last post, sorry for the delay. We had this big festival thus it was impossible to concentrate, okay in my last post, I had shown you how to develop a little News Reader for your self.
Please refer to my earlier post here.Let us take this concept a bit further as promised. The RSS feed was one of my favourite it may not be yours too thus there must be a way to change it.
As you must have noticed Yahoo provides the widget preference option for you, just right click anywhere on the widget body or click on the setting icon shown above your widget icon in the dock bar. Most widgets provide you with some level of customization.
As mentioned above why dont we start with one simple preference of changing the the news feed according to the user choice.

Some Theory

A Widget can provide a number of preference objects to allow itself to save out settings. These settings
are saved out in per-user preference storage.
• On the Mac, this is in ~/Library/Preferences/Konfabulator .
• On the PC, this is in HKEY_CURRENT_USER\Software\Yahoo\WidgetEngine .
You can define preferences in the XML:
<preference hidden="true" name="windowLevel" value="1">
<preference hidden="true" name="windowOpacity" value="0.7">
And get and set preference objects in JavaScript:
preferences.windowOpacity = "0.9";
print(preferences.windowOpacity);
If your Widget will be saving large amounts of data, consider using the SQLite embedded database
available within the engine.
(reference Yahoo SDK Manual)


Practical
Please open your kon file in an editor of your choice and lets insert few basic entries to customize the feed
<preference name="NewsFeed">

  <type>popup</type>
  <title>Add Feed </title>
  <type>text</type>
  <value>http://www.deccanherald.com/rss/69/news.html</value>
 </preference>
 <preference name="Title">
   <type>popup</type>
   <title>Feed Title </title>
   <type>text</type>
 </preference>

 <preference name="refresh">
  <type>popup</type>
  <title>Refresh Interval </title>
  <type>text</type>
 </preference>

These are choices which user may change. Even though the preferences are quite easy yet..........
NewsFeed preference gives you a chance to get your own favourite feed displayed and not have a static page.
Title preference generated the window title based on the feed, it reads the title of the feed and displays it in your bar refresh preferences gives you anoption to specify the seconds after which the window will fetch the feed again to refresh the contents.
This entry should be kept outside your window element. As this is not the part of the main window.
When these preferences are changed there is an event which is generated called onPreferencesChanged. We will write an event handler for this because user has made some changes which will effect the program
Where we had declared the onLoad trigger, just below it we will write
<action trigger="onPreferencesChanged">
  readRSS();
 </action>
Presently our changes are simple enough to be handled in the same function later on when we have more complicated preferences we might use a separate event handler

Javascript Part

Lets see how we can modify our javascript to read the user preference to load a new feed
The problem is that we have no way of knowing if the user has given any preferences or left the preference field blank. To mitigate that problem please add the next few lines at the top of your readRSS() function
var news=preferences.NewsFeed.value;
if(news.length<8)
  news="http://timesofindia.indiatimes.com/rssfeeds/-2128936835.cms";
Simply if the preference entry is not present we provide a hardcoded one(not the most elegant but lets keep it simple)
Use the same variable in your HTTP send operation
var timer=widget.getElementById("rss1timer");
 if(timer!=null)
 {
  timer.ticking=false;
  var timer_time=preferences.refresh.value;
  if(timer_time=="")
   timer.interval=500;
  else
   timer.interval=timer_time;
  
 }
 preferences.refresh.value=timer.interval;
as mentioned earlier in my previous article when timer event fires it calls the main readRSS() function where we check if there is a preferred value for interval ,if not we provide the timer with one
var win_title=widget.getElementById("winTitle");
This statement will get the title bar text object and initialize with a user given title if available in preferences or get the title from the RSS feed and stores it as the preference called Title(as defined in the Kon file)
win_title.data=preferences.Title.value;
  if(win_title.data=="")
  {
   var temp_titles=xmldoc.evaluate("rss/channel/title");
   win_title.data=temp_titles.item(0).firstChild.data;  
  }
    
  preferences.Title.value=win_title.data;
I have tried to explain the code in piece meal form lets see the whole XML in the kon file
<?xml version="1.0" encoding="UTF-8"?>
<widget minimumversion="4.5">
<script charset="utf-8" src="RssRead.js">
</script>
<window id="mainWin">
 <name> RSS1</name>
 <title> My Rss Widget</title>
 <width>400</width>
 <height>310</height>
 <visible>true</visible>

  <img >

    <src>Resources/widget.png</src>
  </img >
  <img >
    <src>Resources/wintitle.png</src>
    <voffset>-1</voffset>
 </img >

  <text id="winTitle" style="font-size: 24px;">
    <hoffset>15</hoffset>
    <voffset>15</voffset>
    <size>16</size>
    <color>#f0fff9</color>
  </text>

  <web id="rss1">

    <font>Times New Roman</font>
    <size>14</size>
    <voffset>28</voffset>
    <hoffset>18</hoffset>
    <width>380</width>
    <height>270</height>
    <bgcolor>#ffaa55</bgcolor>
</web>
</window>

  <timer id="rss1timer" name="rss1timer1">
    <ticking>false</ticking>
    <ontimerfired>readRSS();</ontimerfired>
    <interval>20</interval>
  </timer>

  <preferencegroup name="RSS" title="RSS">

     <type>popup</type>
  <title>Add Feed </title>
  <type>text</type>
  <value>http://www.deccanherald.com/rss/69/news.html</value>
 </preference>
 <preference name="Title">
   <type>popup</type>
   <title>Feed Title </title>
   <type>text</type>
 </preference>

 <preference name="refresh">
  <type>popup</type>
  <title>Refresh Interval </title>
  <type>text</type>
 </preference>
</preferencegroup>

  <action trigger="onLoad">

     readRSS();

  </action>

  <action trigger="onPreferencesChanged">

       readRSS();

  </action>

</widget>
Now lets have a quick peak in our js file
function GetXmlHttpObject()
{

  return new XMLHttpRequest();
}

function readRSS()
{
 var news=preferences.NewsFeed.value;
 if(news.length<8)
    news="http://timesofindia.indiatimes.com/rssfeeds/-2128936835.cms";
 var timer=widget.getElementById("rss1timer");
 var win_title=widget.getElementById("winTitle");
 if(timer!=null)
{
  timer.ticking=false;
  var timer_time=preferences.refresh.value;
  if(timer_time=="")
    timer.interval=500;
  else
    timer.interval=timer_time;
}
 xmlhttp=GetXmlHttpObject();
 if (xmlhttp==null)
 {
   alert ("Something has gone wrong");
   return;
 }

 xmlhttp.onreadystatechange=function()
 {
   if (xmlhttp.readyState==4)
   {
     xmldoc=xmlhttp.responseXML;
     if (xmlhttp.status==200 && xmldoc!=null)
     {
        win_title.data=preferences.Title.value;
        if(win_title.data=="")
        {
           var temp_titles=xmldoc.evaluate("rss/channel/title");
           win_title.data=temp_titles.item(0).firstChild.data;
        }
        preferences.Title.value=win_title.data;
        var titles=xmldoc.evaluate("rss/channel/item/title");
        var links=xmldoc.evaluate("rss/channel/item/link");
        var strAnswer=""
        for(var i = 0; i < titles.length; i++)
        {
          strAnswer=strAnswer + "<a href='" + links.item(i).firstChild.data + 
          "' target='_self' style='color:white; textDecoration:none'>"+(i+1)+ " : "   +titles.item(i).firstChild.data + "</a><br/>";
        }
       var result=widget.getElementById("rss1");
       result.html=strAnswer;
       timer.ticking=true;
     }
     else
     {
       result.html="An error has occurred";
       timer.ticking=true;
     }
   }
 }

 xmlhttp.open("GET", preferences.NewsFeed.value, true)
 xmlhttp.send(null)
}


Please do not forget to subscribe to my feed for the latest and visit me on youtube for further video tutorials

In the next post we will look into using some kind of animation to let the user know while he/she waits and we will look into sqllite with widgets, before that I may delve into sidebars, I have been thinking of doing so for quite some time .
 
EatonWeb Blog Directory