Extending Shindig with Your Own JavaScript Libraries

In Chapter 3, we discussed the process of installing Shindig to run your own OpenSocial container to host applications. Thus far, this chapter has outlined the “out of the box” JavaScript components that are available to you in a Shindig container, but what if you want to create your own JavaScript libraries and features to extend those native offerings?

As with the default feature libraries, adding new JavaScript libraries and features to Shindig in order to make them available to your gadget is a simple, multistep process. Let’s look at a practical example to showcase how to do this. We’ll add a new JavaScript library feature to create a countdown clock that’s displayed in a gadget.

First, from the root of your Shindig installation directory, go to the JavaScript features directory:

cd features/src/main/javascript/features

This is where the JavaScript features we’ve explored thus far are housed, and where we’ll create a new one. Create a new directory called “countdown” and then move into it:

mkdir countdown
cd countdown

Now we need to create the files that will define our JavaScript library feature. Create a new file called countdown_base.js. This file will house the JavaScript that will run our countdown clock. Within the JavaScript file, add the following code:

gadgets['countdown'] = (function(){
   var time_left = 10;                         //number of seconds for countdown
   var output_element_id = 'countdown';        //node to output time to
   var keep_counting = 1;                      //whether to keep counting
   var no_time_left_message = "Time's Up!!!";  //message to display when time's up

   //decrement time left and check whether time has expired
   function countdown() {
      if(time_left < 2) {
         keep_counting = 0;
      }

      time_left = time_left - 1;
   }

   //add leading 0's on single digit numbers
   function add_leading_zero(n) {
      if(n.toString().length < 2) {
         return '0' + n;
      } else {
         return n;
      }
   }

   //format countdown output string
   function format_output() {
      var hours, minutes, seconds;
      seconds = Math.floor(time_left % 60);
      minutes = Math.floor(time_left / 60) % 60;
      hours = Math.floor(time_left / 3600);

      seconds = add_leading_zero( seconds );
      minutes = add_leading_zero( minutes );
      hours = add_leading_zero( hours );

      return hours + ':' + minutes + ':' + seconds;
   }

   //display time left
   function show_time_left() {
      document.getElementById(output_element_id).innerHTML = format_output();
   }

   //display time expired message
   function no_time_left() {
      document.getElementById(output_element_id).innerHTML = no_time_left_message;
   }

   return {
      //countdown function
      count: function () {
         countdown();
         show_time_left();
      },

      //control timer
      timer: function () {
         this.count();

         if(keep_counting) {
            setTimeout("gadgets.countdown.timer();", 1000);
         } else {
            no_time_left();
         }
      },

      //counter initialization
      init: function (t, element_id) {
         time_left = t;
         output_element_id = element_id;
         this.timer();
      }
   };
})();

The first thing that we need to do to define our core JavaScript is to assign the functionality to a custom gadgets object, gadgets['countdown']. We then include any JavaScript functionality that is necessary to run the feature but is not accessible by calling directly into the JavaScript feature. Next, we issue a series of functions in the return object. These are the functions that we will make requests to in order to initialize the JavaScript feature; for example, we’ll make a request to the init(...) function to create the countdown feature.

Since we assigned the return functionality to a gadgets object, returning these functions allows us to initialize the countdown by calling gadgets.countdown.init(...).

Now we need to create a new file, taming.js, which will allow us to make certain methods available to a gadget if Caja is being employed:

var tamings___ = tamings___ || [];
tamings___.push(function(imports){
   ___.grantRead(gadgets.countdown, 'init');
   ___.grantRead(gadgets.countdown, 'timer');
   ___.grantRead(gadgets.countdown, 'count');
});

We specify that we want to make our three return functions available when Caja is being employed.

Our last file, feature.xml, names the feature and specifies the files that provide the code (namely, our JavaScript) needed to run that feature:

<feature>
   <name>countdown</name>
   <dependency>globals</dependency>
   <gadget>
      <script src="countdown_base.js"/>
      <script src="taming.js"/>
   </gadget>
   <container>
      <script src="countdown_base.js"/>
      <script src="taming.js"/>
   </container>
</feature>

We name the feature “countdown” and state that countdown_base.js and taming.js should be used for the container and gadget.

Now we just need to add our new feature to the list of features to be loaded by the container. Go to the features directory and open the features.txt file:

cd features/src/main/javascript/features
vim features.txt

Add the following line to the file:

features/countdown/feature.xml

If you don’t want to manually edit the file, you can run the following command from the features directory:

ls -R1a **/*.xml > features.txt

At this point, we are ready to start using our new feature in our gadget. To integrate our countdown feature into one of our existing gadgets, we simply need to add the Require node with the appropriate feature name:

<Require feature="countdown"></Require>

When we expand this out into a full-fledged sample gadget, we see how the feature is used:

<?xml version="1.0" encoding="UTF-8"?>
<Module>
   <ModulePrefs title="Countdown Application">
      <Require feature="countdown"/>
   </ModulePrefs>
   <Content type="html">
      <![CDATA[
      <div id="countdown">...</div>

      <script type="text/javascript">
      //calculate time left from current to future time
      var currentTime = new Date();
      var futureTime = new Date("September 26, 2011 17:55:00");
      var timeLeft = (futureTime - currentTime) / 1000;

      //initialize counter
      gadgets.countdown.init(timeLeft, 'countdown');
      </script>
      ]]>
   </Content>
</Module>

In the ModulePrefs node, we have included our Require statement. The countdown feature functionality is loaded within the Content node. We include the div node where we want to render the counter. Next, in the script block, we calculate the time between the current time and some date in the future. We can then initialize the feature by calling the gadgets.countdown.init(...) method, passing in the time remaining and a string representing the ID of the node in which the countdown should be rendered.

Using this method, you can create and use custom JavaScript library features.

Get Programming Social Applications now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.