Archive

javascript

Fancybox is a really nice jQuery library for creating lightboxes.  There are a few simple examples and some documentation on their site but there seems to be no demonstrated method for loading a fancybox dynamically with javascript.  All of their examples require you to embed a link to the lightbox content somewhere in the page source.

The following hack, mostly taken from the method described here, allows you to dynamically any web page into a fancybox straight from javascript.

I’ve documented the following example which should be fairly straight forward.  Basically it embeds a hidden link, that’s controlled by the callFancyBox function.  Make sure you have the right things included and you can simply call

callFancyBox( <your iframed url > );

<html>
<head>

<!--- These are the scripts and styles needed for fancybox to work --->
<script type="text/javascript" src="http://qwisk.com/sbmedia/scripts/libraries/jquery-1.3.2-mod.js" charset="utf-8"></script>
<script type="text/javascript" src="http://qwisk.com/sbmedia/scripts/libraries/jquery.fancybox-1.2.6.js" charset="utf-8"></script>
<link href="http://qwisk.com/sbmedia/css/jquery.fancybox-1.2.6.css" rel="stylesheet" type="text/css" media="all" />

</head>
<body>

<h3> Fancybox Test</h3>

A fancybox will load in 3 seconds

<!--- Currently fancybox only works on links in your page.  This div hides a link, who's href we change dynamically --->
<div id="hidden_clicker" style="display:none">
<a id="hiddenclicker" href="http://asdf.com" >Hidden Clicker</a>
</div>

<script type="text/javascript" >

// Register all links with-flash as fancybox's
$(document).ready(function() {
 $("a.overlay-flash").fancybox({
 'padding': 0,
 'zoomOpacity': true,
 'zoomSpeedIn': 500,
 'zoomSpeedOut': 500,
 'overlayOpacity': 0.75,
 'frameWidth': 530,
 'frameHeight': 400,
 'hideOnContentClick': false
 });
});

// This function allows you to call the fancy box from javascript
// origional source: http://outburst.jloop.com/2009/08/06/call-fancybox-from-flash/
function callBoxFancy(my_href) {
var j1 = document.getElementById("hiddenclicker");
j1.href = my_href;
$('#hiddenclicker').trigger('click');
}

// Call the Fancy Box 3 seconds after the page loads
$(document).ready(function() {
 window.setTimeout("callFancyBox('http://google.com');", 3000)
});
</script>

</body>
</html>

Advertisements

Comet implementations require both server and client side components.  In Part 2 of this series we installed and configured the server side component and then used the example STOMP Test client to test it out and get a feel for what was going on.

In this part of the series we’ll be covering the client side to make our custom interface to the comet server.  To demonstrate we’ll go through the steps in creating an app I call EZChat which is basically a bare bones comet chat client.  The interface will allow you to choose a name and submit messages that will be broadcast to everyone viewing the page in real time.

For it to work you will have to have the configuration of Part 2 setup and your Orbited server running.  For reference I learned most of the client side code in this example by playing with the source of the example STOMP Test client. I recommend taking a look at that source if you need more advanced options or another example.

Includes

At the top of the <head> you first need to include the libraries for the Orbited (/static/Orbited.js) and STOMP (/static/protocols/stomp/stomp.js) client-side implementations.


<script>document.domain=document.domain</script>
 <script src="http://localhost:9000/static/Orbited.js"></script>
 <script>
 Orbited.settings.port = 9000;
 TCPSocket = Orbited.TCPSocket;
 </script>
 <script src="http://localhost:9000/static/protocols/stomp/stomp.js"></script>

 <script src="http://www.json.org/json2.js"></script>

In between the scripts we’ve set up a TCPScocket.  It must be don between the two script includes because the STOMP library needs the socket setup for its execution.  We’ve also specified the Orbited port which is necessary especially if you change the port on which the orbited and stomp javascript files are hosted on (they can both be hosted on port 80 along with your other scripts).

Lastly, we’ve also included a popular JSON library.  Typically I use jquery-json but I’ve kept this tutorial free of javascript frameworks so as not to add unneeded complexity.  If you’d like you can of course switch to whatever JSON library you’re used to; just replace the JSON.stringify and JSON.parse functions with your equivalents.

STOMP Setup

Because we’re dealing with the setup we’ll skip to the bottom of the page and add the following script just before the end tag of the body (</body>).


<script type="text/javascript">
(function() { // set up stomp client.

stomp = new STOMPClient();

 stomp.onconnectedframe = function() {  // Run on initial connection to STOMP (comet) server
 stomp.ready = true;
 // subscribe to channel CHANNEL = "/ezchat/"
 var CHANNEL = '/ezchat/'
 stomp.subscribe(CHANNEL);
 };

 stomp.onmessageframe = function(frame) {  // Executed when a messge is received
 my_receive( JSON.parse(frame.body) );
 };

 // Everything is setup. Start the connection!
stomp.connect(document.domain, 61613); //, 'guest', 'guest');
})();
</script>

My apologies as always for the crappiness of the wordpress syntax parser.   Lets walk through what’s happening.

At the top, we initialize a new STOMPClient object.  The StompClient has the following hooks you can override to trigger your own events.

onopen – Called when the Transport is opened
onclose – Called when the Transport has closed
onerror – Called when the Stomp Client has errored
onerrorframe – Called when there is an error in the message received
onconnectedframe – Called when a the client is fully set up for sending/receiving
onmessageframe – Called when a message is received

The STOMP object also has these functions for connecting and resetting the connections.

reset – Resets the STOMP connection
connect – Connects to the STOMP server
send – Sends the object in the first argument to the channel specified by the second argument

In our simple example only the onconnectedframe and onmessage frames need to be overwritten.

The onconnectedframe function is called when the STOMP server has been connected to and everything is setup for sending and receiving messages.  Inside this function we simply need to subscribe/listen to the CHANNEL we’ve setup for our chat.  For the example I’ve chosen the channel ‘/ezchat/’.  Once subscribed our STOMP client will receive any messages sent to that channel in real time.  You can subscribe to multiple channels if you’d like, and you can make clients with different channels if you’d like to have different chat rooms.  But for this example we’ll just stick with the hard coded ‘/ezchat/’ channel.

The onmesageframe function is called when a message has been received.  It is passed a frame object with the following structure

frame

  • body: “{“name”:”Dave”,”message”:”awesome this is working”}”
  • headers: Object
    • content-encoding: “utf-8”
    • content-length: “51”
    • content-type: “text/plain”
    • destination: “/ezchat/”
    • message-id: “/ezchat/_3”
  • type: “MESSAGE”

where the body holds the information that has been sent.  The STOMP server and client add extra “type” and “headers” objects to communicate between each other.  The extra information can be very useful for more complicated applications but for our simple example we’re only interested in the frame “body”.

So you see that the onmessageframe is simply parsing the json object in frame.body of every received message and passing it to my_receive, a function we will soon create.

The Content

For EZChat we need a form where users can specify a name and type messages to send.  We also need an area to put the messages.  Plop this HTML in at the top of the <body> to handle all of that.

<h2>EZChat - Example Comet Client!</h2>
Everyone viewing this page will see the messsages you submit instantly.
<form id="message_form" action="#">
 Name:
 <input type="text" name="chat_name" id="chat_name"></input>
 Message:
 <textarea name="message" id="message" rows="4" cols="40"></textarea>
 <input type="submit" name="Send" onclick="return my_send(); return false"></input>
</form>
<div id="messages"><!--- All received messages will get placed here ---></div>

There are a few things to notice here.  First, the important elements in the form have ids ‘chat_name’ and ‘message’ and the area that will be containing all the received messages is called ‘messages’.  The names don’t matter except that we’ll use them in the functions we create later.

Second, the onclick event of the submit button is overridden with instead calling the my_send function.  We’ll make this function in the next step.

The Functions

Lastly we need to make the custom my_send and my_receive functions that get called to send and receive messages.  Insert these functions into the head after the includes.

my_send

The my_send function will get the values from the ‘chat_name’ and ‘message’ form elements, combine them in an object, convert the object to json, and then sends it to the ‘/ezchat/’ channel.  The sending is handled using the stomp.send command which takes as input the object to send and second, the channel to send it to.

var CHANNEL = '/ezchat/';
function my_send() { 

// Get the values to send from the form
 var name = document.getElementById('chat_name').value;
 var message = document.getElementById('message').value;

 var msg = {'name': name, 'message': message};

 var json_msg = JSON.stringify(msg);
 stomp.send(json_msg, CHANNEL)
 return false;
 }

my_receive

As discussed earlier the my_receive function gets the JSON parsed version of whatever was sent in ‘frame.body’. In the case of our app its always an object of the format

msg = {‘name’: <some name>, ‘message’: <some message>}

The my_receive function simply takes this object and converts it into a prettier HTML format and appends it to the top of the message list we created in the HTML.

function my_receive( msg ) {
 console.log('received message', msg);
 // append the <msg> to the top of the list of messages.
 var messages_el = document.getElementById('messages');
 var new_message = "
<div><strong>" + msg['name'] + ":</strong> " + msg['message'] + "</div>
";
 messages_el.innerHTML = new_message + messages_el.innerHTML;
 }

That’s it for the code.  Scroll to the bottom of the page for the full version of the source.

Results

Ensure that your Orbited server is running as described in Part 2 of the series.  Then load up the page we’ve made in two or more separate windows.  Choose a different name for each window and start sending each other messages.  You’ll notice that both windows will receive the submitted messages almost instantaneously!  The beauty of Comet!

ezchatIf you open your STOMP Test Client and subscribe to the ‘/ezchat/’ channel you’ll see a more raw input on what’s actually being received by the STOMP clients as you chat.

ezchatstompYou can see how the STOMP Test client is incredibly handy for debugging.

That’s it for this part of the Tutorial!  There are still parts to come including writing a data handler on the server side and hopefully a much requested post on Django integration.

Full Source

For your convenience here’s the full index.html file for this example.


 <html>

<head>
 <script>document.domain=document.domain</script>
 <script src="http://localhost:9000/static/Orbited.js"></script>
 <script>
 Orbited.settings.port = 9000;
 TCPSocket = Orbited.TCPSocket;
 </script>
 <script src="http://localhost:9000/static/protocols/stomp/stomp.js"></script>

 <script src="http://www.json.org/json2.js"></script>

 <script type="text/javascript">
 // These are our custom functions for sending and receiving STOMP messages.
 // They will be sent in the format msg = {'name': somename, 'message': somemessage}

 var CHANNEL = '/ezchat/';

 function my_receive( msg ) {
 console.log('received message', msg);
 // append the <msg> to the top of the list of messages.
 var messages_el = document.getElementById('messages');
 var new_message = "
<div><strong>" + msg['name'] + ":</strong> " + msg['message'] + "</div>
";
 messages_el.innerHTML = new_message + messages_el.innerHTML;
 }
 function my_send() {
 // Get the values to send from the form      

 var name = document.getElementById('chat_name').value;
 var message = document.getElementById('message').value;

 var msg = {'name': name, 'message': message};
 console.log(msg);

 var json_msg = JSON.stringify(msg);
 console.log(json_msg);
 stomp.send(json_msg, CHANNEL)
 return false;
 }       
 </script>

</head>

<body>
<h2>EZChat - Example Comet Client!</h2>
<div>Everyone viewing this page will see the messsages you submit instantly.</div>
<form id="message_form" action="#">
 Name:
 <input type="text" name="chat_name" id="chat_name"></input>
 Message:
 <textarea name="message" id="message" rows="4" cols="40"></textarea>
 <input type="submit" name="Send" onclick="return my_send(); return false"></input>
</form>
<div id="messages"><!--- All received messages will get placed here ---></div>
<script type="text/javascript">
(function() { // set up stomp client.
 stomp = new STOMPClient();
 stomp.onconnectedframe = function() {  // Run on initial connection to STOMP (comet) server
 stomp.ready = true;
 // subscribe to channel CHANNEL = "/ezchat/"
 stomp.subscribe(CHANNEL);          
 };

 stomp.onmessageframe = function(frame) {  // Executed when a messge is received
 console.log('frame is', frame);
 my_receive( JSON.parse(frame.body) );
 };

 // Everything is setup. Start the connection!
 stomp.connect(document.domain, 61613); //, 'guest', 'guest');
})();
</script>

</body>

</html>

As usual, if there are corrections or questions please be sure to leave them in the comments. And remember to subscribe to catch the rest of the series.

This took me a little while to figure out so I thought I’d share. You can use a jQuery selector to find any RSS links on a page very easily.

The following line will return a list of the RSS link elements.

var link_elements = $('link[type="application/rss+xml"]');

The following snippet will create an array of all the urls to the RSS feeds on the page.

var links = [];
$('link[type="application/rss+xml"]').each(function() { links[links.length] = this.href; });

Today I learned that Google has a really awesome AJAX API for RSS and Atom feeds. It allows you to access RSS or Atom feeds using JavaScript in both JSON and XML format without having to setup your own proxy or deal with anything on the server side.

They have a lot of great examples, but I thought I’d share mine with the usecase of showing the feed of my Socialbrowse shares. In your Socialbrowse settings you can specify a public share name. Once added 3 feeds will be created for you

profile: http://socialbrowse.com/shares/PUBLIC_NAME/
shares rss: http://socialbrowse.com/rss/uname/PUBLIC_NAME/
feed rss: http://socialbrowse.com/rss/socialfeed/PUBLIC_NAME/

where PUBLIC_NAME is the share name you chose.

For this example we’re going to fetch and display the “shares rss” feed using the Google AJAX Feeds API. The example is simulating the Socialbrowse blog widget Zack created a few months ago.

To use the Feeds API you need to get a key.

Now we have everything we need. Create an HTML file and paste in the following code. Make sure to change YOUR_API_KEY, and the feed_title and feed_url variables to your values.

<html>
  <head>
	
	<script>
	  var feed_title = "Dave's Socialbrowse Feed";
	  var feed_url = "http://socialbrowse.com/rss/uname/PUBLIC_NAME/";
	</script>

    <script  type="text/javascript" src="http://www.google.com/jsapi?key=ABQIAAAAmcnSI-mFmfJW8bidL13qfRQHElLAWyCZ_TZ_pzrAvWp3ncTV5hRgCPRM76Ub8GIqowNBQZMVWYastg"></script>
    <script type="text/javascript">

      google.load("feeds", "1");

      function initialize() {
        var feedControl = new google.feeds.FeedControl();
        feedControl.addFeed(feed_url, feed_title);
        feedControl.draw(document.getElementById("feedControl"));
      }
      google.setOnLoadCallback(initialize);

    </script>
  </head>

  <body>
    <div id="feedControl">Loading</div>
  </body>
</html>

Viewing the page in a browser will result in something that looks like this

feed

Its not very pretty but its easy enough to style it however you like using CSS. This code can be added anywhere you’d like to display your Socialbrowse shares.

Finally, note that this code can be used with any feed, not just Socialbrowse. If you want to display the latest Google News for example simply change the feed variables to

	  var feed_title = "Google News";
	  var feed_url = "http://news.google.com/news?pz=1&ned=us&hl=en&topic=h&num=3&output=rss";

As a demo application for a project of mine I wrote TicTacToe in Javascript using the jQuery framework.

I’ve added excessive comments to the code to provide an easy walk-through example on the jQuery/Javascript game. I’m in no way a JavaScript expert, there are a hundred different ways to program TicTacToe, and this code is far from clean but here it is!

There are just two files, the HTML page, and a page holding the javascript.

tictactoe.html


<html>

<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js"></script>
<script src="tictactoe.js"></script>

<style>

table.board {
  border: 1px solid green; 
  height: 600px;
  width: 600px;
}

body {
  text-align: center;
  align: center;
}

td {
  height: 200px;
  width: 200px;
  text-align: center;
  vertical-align: middle;
  font-size: 100px;
  font-weight: bold;
  font-color: green;
  font-family: geniva, verdana, helvetica;
  border: 1px solid green;
}

#menu {
  text-align: center;
  position: absolute;
  width: 400;
  height: 400;
  margin-left: 100px;
  margin-top: 100px;
  border: 5px double red;
  display: none;
  vertical-align: middle;
  background-color: white;
}

#play_again {
  font-size: 20px;
  color: green;
}

</style>


</head>
<body>

<table border="0px" align="center">
<tr><td>
<div id="menu"></div>
<div id="board"></div>
</td></tr>
</table>

</body>


tictactoe.js

/* Main Game Handling class */
var TicTacToe = {
    turn: "O",  // Keeps a record of who's turn it is
    board: ["", "", "", "", "", "", "", "", "", ""],  // Keeps a record of the TicTacToe Board
    win: false, // records who won if the game is over
    
    /* Clears and starts a new game with a new board */
    restartGame: function() {
      // Draw the board
      var board_table = '<table class="board" border="0px" cellpadding="0px" cellspacing="0px" align="center"><tr><td id="ttt0">&nbsp;</td><td id="ttt1">&nbsp;</td><td id="ttt2">&nbsp;</td></tr><tr><td id="ttt3">&nbsp;</td><td id="ttt4">&nbsp;</td><td id="ttt5">&nbsp;</td></tr><tr><td id="ttt6">&nbsp;</td><td id="ttt7">&nbsp;</td><td id="ttt8">&nbsp;</td></tr></table>';
      $("#board").html(board_table);
      $("#menu").hide();
      
      // clear the board
      this.board = ["", "", "", "", "", "", "", "", "", ""];
      
      // Add on-click events to each of the boxes of the board
      $("#board td").click(function(e) {
          TicTacToe.move( e.target.id );
         });

    },

    /* Handles clicks spaces on the board */
    move: function(id) {
      var space = $("#" + id);  // Board space table element
      var num = id.replace("ttt", ""); // # representing the space on the board
    
      // If no one's gone there, and the game isn't over, go there!
      if (!this.board[num] && !this.win) {
        space.html( this.turn );
        this.board[num] = this.turn;
        this.nextTurn();  // End turn
      } 
    },

    /* Iterate turn and check if anyone one yet */
    nextTurn: function() {
      this.turn = (this.turn == "O") ? "X" : "O";
      this.win = this.check4Win();
      if (this.win) {
          this.endGame();
      }
    },

    /* Display who won and options for new games */
    endGame: function() {
    
      if (this.win == "Cat") {
          $("#menu").html("Cats Game.");
      } else {
          $("#menu").html(this.win + " wins!");
      }
      $("#menu").append("<div id='play_again'>Play Again</div>");
      
      // Button for playing again.
      $("#play_again").click(function () { TicTacToe.restartGame();  });
      $("#menu").show();
      this.win = false;
    
    },

    // If any of these patters of board spaces have all X's or all O's somebody won!
    wins: [[0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [6,4,2]],
    
    /* Check for whether someone won the game of it was a Cat's game. */
    check4Win: function() {
        
      // Loop through all possible winning combinations 
      for (k in this.wins){
        var pattern = this.wins[k];
        var p = this.board[pattern[0]] + this.board[pattern[1]] + this.board[pattern[2]];
        if (p == "XXX") {
          return "X";  // X Won!
        } else if (p == "OOO") {
          return "O";  // O Won!
        }
      }
      
      // Check if all spaces in the board are filled, then its a Cat's game
      var cnt = 0;
      for (s in this.board) {
        if (this.board[s]) { cnt+=1; }
      }
      if (cnt == 9) { 
        return "Cat";  // Cat's game!
      }
  }
};

$(document).ready(function() {
    
    // Start a game!
    TicTacToe.restartGame();
});

I’d originally setup the game on AppJet but unfortunately today they announced that they are closing down their framework and free hosting for a while to focus on one of their successful apps EtherPad. For at least the next month however you can play the game here, and play with the source code here. Feel free to use my code in any way.

If you do use the source for something, or have suggestions on improvements make sure to leave a comment.

I recently discovered a small oddity in javascript link elements.  Simply stated, if you access the ‘href’ attribute using getAttribute(‘href’) the result will be different than accessing it with simply ‘href’.  

The getAttribute will return the relative link, where the direct call will return the absolute. 

Here’s the test:

<html>
  <body >
    <a href="/relative/link" id='rellink'>
      Relative Link</a>
    <div id='answer1'></div>
    <div id='answer2'></div>
    <script type='text/javascript'>
      var link, a1,a2;
      link = document.getElementById('rellink');
      a1 = document.getElementById('answer1');
      a2 = document.getElementById('answer2');
      a1.innerHTML =  "getAttribute('href'): "
      + link.getAttribute('href');
      a2.innerHTML = "href: " + link.href;
    </script>
  </body>
</html>

and the resulting page show:

Relative Link
getAttribute('href'): /relative/link

href: http://localhost/relative/link
 

I was researching some javascript capabilities earlier and from a wikipedia article on Comet learned this

“browsers render HTML pages incrementally, each script tag is executed as it is received.

Awesome!  This means not just that you can execute javascript before and as the page is loading, but you choose WHEN/WHERE to run it.  To test it out I made the following example.

<html>
<body>
  <div>Original</div>
  <div>Original</div>
  <script>
    var link, links;
    links = document.getElementsByTagName( 'div' );
    for (l in links) { links[l].innerHTML = 'Edited'; }
  </script>
  <div>Original</div>
</body>
</html>

The inserted script collects all ‘div’ elements in the document and changes them to instead say “Edited”.  The code outputs the following result.

Edited
Edited
Original

Because the script is executed in the order that the page is loaded, the first two divs get edited, but the last one which didn’t exist at the time the script was run, was not edited.  Some clever things can be done with this.  I’ll blog more about it later.