Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit 28fe4c6c1f5cf65f06e5eed5c856ba95d0e7e931 Stephane Caron committed Jul 22, 2010
Showing with 367 additions and 0 deletions.
  1. +84 −0 admin.html
  2. +74 −0 css/master.css
  3. +45 −0 index.html
  4. +58 −0 js/client.js
  5. +106 −0 js/server.js
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/TR/html5/" lang="fr">
+ <head>
+ <title>Admin | node.js real time server</title>
+
+ <link rel="stylesheet" href="/css/master.css" type="text/css" media="screen" title="Master stylesheet" charset="utf-8" />
+
+ <script src="http://www.google.com/jsapi" type="text/javascript"></script>
+ <script type="text/javascript" charset="utf-8">
+ google.load("jquery", "1.4.2");
+ </script>
+ </head>
+ <body>
+ <div id="page">
+ <div class="ads">
+ <script type="text/javascript">
+ Vertical1237859 = false;
+ ShowAdHereBanner1237859 = true;
+ RepeatAll1237859 = false;
+ NoFollowAll1237859 = false;
+ BannerStyles1237859 = new Array(
+ "a{display:block;font-size:11px;color:#888;font-family:verdana,sans-serif;margin:0 4px 10px 0;text-align:center;text-decoration:none;overflow:hidden;}",
+ "img{border:0;clear:right;}",
+ "a.adhere{color:#666;font-weight:bold;font-size:12px;border:1px solid #ccc;background:#e7e7e7;text-align:center;}",
+ "a.adhere:hover{border:1px solid #999;background:#ddd;color:#333;}"
+ );
+
+ document.write(unescape("%3Cscript src='"+document.location.protocol+"//s3.buysellads.com/1237859/1237859.js?v="+Date.parse(new Date())+"' type='text/javascript'%3E%3C/script%3E"));
+ </script>
+ </div>
+
+ <h1>Deliver real-time information to your users using node.js</h1>
+ <p>Click on one of the following button to send an event to the <a href="/">demo page</a>.</p>
+ <p><a href="#" class="button" id="message" rel="message_sample">Send a message</a></p>
+ <h2>json sample</h2>
+ <p>Fell free to play with the json, just make sure it's valid.</p>
+ <textarea rows="10" cols="50" id="message_sample">
+{
+ type: 'message',
+ content: 'A message has been sent'
+}</textarea>
+
+ <p><a href="#" class="button" id="google_map" rel="google_map_sample">Display a google map</a></p>
+ <h2>json sample</h2>
+ <p>Fell free to play with the json, just make sure it's valid.</p>
+ <textarea rows="10" cols="50" id="google_map_sample">
+{
+ type: 'google_map',
+ content: {
+ lat: '37.331693',
+ long: '-122.0307642'
+ }
+}</textarea>
+
+ <p><a href="#" class="button" id="youtube" rel="youtube_sample">Display a YouTube video</a></p>
+ <h2>json sample</h2>
+ <p>Fell free to play with the json, just make sure it's valid.</p>
+ <textarea rows="10" cols="50" id="youtube_sample">
+{
+ type: 'youtube',
+ content: {
+ url: 'http://www.youtube.com/watch?v=B0ky-VMi9fI'
+ }
+}</textarea>
+
+ <br class="cboth" />
+ </div>
+
+ <script type="text/javascript" charset="utf-8">
+ $('.button').click(function(){
+ json = eval('('+$('#'+$(this).attr('rel')).val()+')');
+ $.post(
+ 'http://nodejs.no-margin-for-errors.com/send_feed_item',
+ json,
+ function(){
+ // Done!
+ }
+ );
+
+ return false;
+ });
+ </script>
+ </body>
+</html>
@@ -0,0 +1,74 @@
+body {
+ font: 62.5%/1.2 Arial, Verdana, Sans-Serif;
+}
+
+#page {
+ background: #e1e1e1;
+ font-size: 1.4em;
+ margin: 0 auto;
+ padding: 20px;
+ width: 900px;
+
+ border-radius: 10px;
+ -webkit-border-radius: 10px;
+ -moz-border-radius: 10px;
+}
+
+h1 {
+ font-size: 1.6em;
+}
+
+h2 {
+ font-size: 1em;
+ margin: 25px 0 10px 0;
+}
+
+p {
+ margin: 0 0 25px 0;
+}
+
+a[href^='#'] {
+ background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#8dbd53), to(#6c9b30));
+ background: -moz-linear-gradient(#8dbd53, #6c9b30);
+ border: 1px #83aa53 solid;
+ color: #fff;
+ padding: 5px;
+ text-decoration: none;
+
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+}
+
+textarea {
+ background: #fff;
+ display: inline-block;
+ margin: 0 0 40px 0;
+ padding: 15px;
+
+ border-radius: 10px;
+ -webkit-border-radius: 10px;
+ -moz-border-radius: 10px;
+}
+
+.ads {
+ float: right;
+ margin: 0 0 0 15px;
+ width: 260px;
+}
+
+.ads a {
+ float: left;
+}
+
+.cboth { clear: both; }
+
+#feed_holder {
+ background: #fff;
+ padding: 20px;
+ width: 575px;
+
+ border-radius: 10px;
+ -webkit-border-radius: 10px;
+ -moz-border-radius: 10px;
+}
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/TR/html5/" lang="fr">
+ <head>
+ <title>node.js real time server</title>
+
+ <link rel="stylesheet" href="/css/master.css" type="text/css" media="screen" title="Master stylsheet" charset="utf-8" />
+
+ <script src="http://www.google.com/jsapi" type="text/javascript"></script>
+ <script type="text/javascript" charset="utf-8">
+ google.load("jquery", "1.4.2");
+ </script>
+ <script src="/js/client.js" type="text/javascript" charset="utf-8"></script>
+ <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div class="ads">
+ <script type="text/javascript">
+ Vertical1237859 = false;
+ ShowAdHereBanner1237859 = true;
+ RepeatAll1237859 = false;
+ NoFollowAll1237859 = false;
+ BannerStyles1237859 = new Array(
+ "a{display:block;font-size:11px;color:#888;font-family:verdana,sans-serif;margin:0 4px 10px 0;text-align:center;text-decoration:none;overflow:hidden;}",
+ "img{border:0;clear:right;}",
+ "a.adhere{color:#666;font-weight:bold;font-size:12px;border:1px solid #ccc;background:#e7e7e7;text-align:center;}",
+ "a.adhere:hover{border:1px solid #999;background:#ddd;color:#333;}"
+ );
+
+ document.write(unescape("%3Cscript src='"+document.location.protocol+"//s3.buysellads.com/1237859/1237859.js?v="+Date.parse(new Date())+"' type='text/javascript'%3E%3C/script%3E"));
+ </script>
+ </div>
+ <h1>Deliver real-time information to your users using node.js</h1>
+ <p>Open <a href="admin.html">the admin page</a> (not protected in any way) in another window and play with it, you should see this webpage react in real-time.</p>
+ <p>If you're lucky, someone else might play with it and you should see the other stuff happen in real-time on this page.</p>
+
+
+ <div id="feed_holder">
+ <p>Real-time called events will be displayed here.</p>
+ </div>
+
+ <br class="cboth" />
+ </div>
+ </body>
+</html>
@@ -0,0 +1,58 @@
+function longPoll_feed () {
+ //make another request
+ $.ajax({
+ cache: false,
+ dataType: 'json',
+ type: "GET",
+ url: "/real_time_feed",
+ error: function () {
+ //don't flood the servers on error, wait 10 seconds before retrying
+ setTimeout(longPoll_feed, 10*1000);
+ },
+ success: function (json) {
+ display_event(json);
+
+ //if everything went well, begin another request immediately
+ //the server will take a long time to respond
+ //how long? well, it will wait until there is another message
+ //and then it will return it to us and close the connection.
+ //since the connection is closed when we get data, we longPoll again
+ longPoll_feed();
+ }
+ });
+}
+
+function display_event(json){
+ $('#feed_holder').prepend('<hr />');
+
+ switch(json[0].type){
+ case 'message':
+ $('#feed_holder').prepend('<p>'+json[0].content+'</p>');
+ break;
+
+ case 'google_map':
+ myLatlng = new google.maps.LatLng(json[0].content.lat, json[0].content.long);
+ myOptions = {
+ zoom: 15,
+ center: myLatlng,
+ mapTypeId: google.maps.MapTypeId.ROADMAP
+ }
+
+ $('#feed_holder').prepend('<div class="map" style="width:300px;height:300px;"></div>');
+
+ map = new google.maps.Map($('.map:first')[0], myOptions);
+ break;
+
+ case 'youtube':
+ video_id = json[0].content.url;
+ video_id = video_id.substring(video_id.indexOf('?v=')+3,video_id.length);
+
+ $('#feed_holder').prepend('<object width="575" height="385"><param name="movie" value="http://www.youtube.com/v/'+video_id+'&amp;hl=en_US&amp;fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/'+video_id+'&amp;hl=en_US&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="575" height="385"></embed></object>');
+ break;
+ }
+}
+
+$(document).ready(function() {
+ //begin listening for updates right away
+ longPoll_feed();
+});
@@ -0,0 +1,106 @@
+var http = require("http"),
+ sys = require("sys"),
+ url = require("url"),
+ qs = require("querystring");
+
+var ITEMS_BACKLOG = 20;
+
+var urlMap = {
+ '/real_time_feed' : function (req, res) {
+ var since = parseInt(qs.parse(url.parse(req.url).query).since, 10);
+ feed.query(since, function (data) {
+ res.simpleJSON(200, data);
+ });
+ },
+ '/send_feed_item' : function (req, res, json) {
+ feed.appendMessage( json );
+ res.simpleJSON(200, {});
+ }
+}
+
+http.createServer(function (req, res) {
+ // Get the url and associate the function to the handler
+ // or
+ // Trigger the 404
+ handler = urlMap[url.parse(req.url).pathname] || notFound;
+
+ var json = "";
+
+ if(req.method === "POST"){
+ // We need to process the post but we need to wait until the request's body is available to get the field/value pairs.
+ req.body = '';
+
+ req.addListener('data', function (chunk) {
+ // Build the body from the chunks sent in the post.
+ req.body = req.body + chunk;
+ })
+ .addListener('end', function () {
+ json = JSON.stringify(qs.parse(req.body));
+ handler(req, res, json);
+ }
+ );
+ }else{
+ handler(req, res);
+ }
+
+ res.simpleJSON = function (code, obj) {
+ var body = JSON.stringify(obj);
+ res.writeHead(code, {
+ "Content-Type": "text/json",
+ "Content-Length": body.length
+ }
+ );
+ res.end(body);
+ };
+}).listen(8001);
+
+
+// This method handles the feed push and querying.
+var feed = new function () {
+ var real_time_items = [],
+ callbacks = [];
+
+ this.appendMessage = function (json) {
+ // Append the new item.
+ real_time_items.push( json );
+
+ // Log it to the console
+ sys.puts(new Date() + ": " + JSON.parse(json).type + " pushed");
+
+ // As soon as something is pushed, call the query callback
+ while (callbacks.length > 0)
+ callbacks.shift().callback([JSON.parse(json)]);
+
+ // Make sur we don't flood the server
+ while (real_time_items.length > ITEMS_BACKLOG)
+ real_time_items.shift();
+ };
+
+ this.query = function (since, callback) {
+ var matching = [];
+
+ for (var i = 0; i < real_time_items.length; i++) {
+ var real_time_item = real_time_items[i];
+ if (real_time_item.timestamp > since)
+ matching.push(real_time_item)
+ }
+
+ if (matching.length != 0) {
+ callback(matching);
+ } else {
+ callbacks.push({ timestamp: new Date(), callback: callback });
+ }
+ };
+};
+
+var NOT_FOUND = "Not Found\n";
+
+function notFound(req, res) {
+ res.sendwriteHeadHeader(404, [ ["Content-Type", "text/plain"]
+ , ["Content-Length", NOT_FOUND.length]
+ ]);
+ res.write(NOT_FOUND);
+ res.end();
+}
+
+console.log('Server running at http://127.0.0.1:8001/');

0 comments on commit 28fe4c6

Please sign in to comment.