Commits

Fabian Topfstedt  committed c73bc1e

initial import

  • Participants

Comments (0)

Files changed (7)

+application: alonepoem
+version: 1
+runtime: python
+api_version: 1
+
+inbound_services:
+- channel_presence
+
+handlers:
+- url: /static
+  static_dir: static
+      
+- url: .*
+  script: main.py
+indexes:
+
+# AUTOGENERATED
+
+# This index.yaml is automatically updated whenever the dev_appserver
+# detects that a new type of query is run.  If you want to manage the
+# index.yaml file manually, remove the above marker line (the line
+# saying "# AUTOGENERATED").  If you want to manage some indexes
+# manually, move them above the marker line.  The index.yaml file is
+# automatically uploaded to the admin console when you next deploy
+# your application using appcfg.py.
+#!/usr/bin/env python
+import os
+import time
+import random
+
+from django.utils import simplejson as json
+from google.appengine.api import channel
+from google.appengine.api import memcache
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import template
+from google.appengine.ext.webapp import util
+
+
+class MainHandler(webapp.RequestHandler):
+
+    def get(self):
+        """
+        Creates a client_id and a channel token and renders the poem page.
+        """
+        client_id = 'client-%f-%d' % (time.time(), random.random() * 9999)
+        token = channel.create_channel(client_id)
+
+        context = {"token": token, "client_id": client_id}
+        content = template.render("templates/index.html", context)
+
+        self.response.out.write(content)
+
+    def post(self):
+        """
+        Reveives `client_id` and `escaped_message` and sends the escaped
+        message to every other connected clients.
+        """
+        posting_client_id = self.request.get("client_id")
+        escaped_message = self.request.get("escaped_message")
+
+        if posting_client_id is None or escaped_message is None:
+            self.error(400)
+
+        client_ids = memcache.get("client_ids") or []
+
+        json_data = json.dumps({
+            'escaped_message': escaped_message,
+            'connected_clients': len(client_ids),
+        })
+
+        for client_id in client_ids:
+            if client_id != posting_client_id:
+                channel.send_message(client_id, json_data)
+
+        self.response.set_status(204)  # No Content
+
+
+class ChannelConnectedHandler(webapp.RequestHandler):
+
+    def post(self):
+        """
+        Adds clients to the client list
+        """
+        client_id = self.request.get('from')
+
+        client_ids = set(memcache.get("client_ids") or [])
+        client_ids.add(client_id)
+        memcache.set("client_ids", client_ids)
+
+
+class ChannelDisconnectedHandler(webapp.RequestHandler):
+
+    def post(self):
+        """
+        Removes clients from the client list
+        """
+        client_id = self.request.get('from')
+
+        client_ids = memcache.get("client_ids") or []
+        client_ids = set(client_ids).difference([client_id])
+        memcache.set("client_ids", client_ids)
+
+
+def main():
+    DEBUG = os.environ['SERVER_SOFTWARE'].startswith('Dev')
+    application = webapp.WSGIApplication([
+        ('/', MainHandler),
+        ('/_ah/channel/connected/', ChannelConnectedHandler),
+        ('/_ah/channel/disconnected/', ChannelDisconnectedHandler),
+    ], debug=DEBUG)
+    util.run_wsgi_app(application)
+
+
+if __name__ == '__main__':
+    main()

File static/css/styles.css

+body, #poem {
+	font-family: 'Vollkorn', serif;
+}
+
+html {
+	background: url(/static/img/paper_cc_by_flickr_user_bittbox.jpg) no-repeat center center fixed;
+	-webkit-background-size: cover;
+	-moz-background-size: cover;
+	-o-background-size: cover;
+	background-size: cover;
+
+	font-size: 22px;
+	text-align: center;
+}
+
+h1 {
+	font-weight: 400;
+}
+
+#poem {
+	text-align: center;
+	border: 0;
+	color: #222;
+	width: 75%;
+	font-size: 1em;
+	line-height: 1.3em;
+	background:transparent;
+}

File static/img/paper_cc_by_flickr_user_bittbox.jpg

Added
New image

File static/js/bind_with_delay.js

+/* 
+bindWithDelay jQuery plugin
+Author: Brian Grinstead
+MIT license: http://www.opensource.org/licenses/mit-license.php
+
+http://github.com/bgrins/bindWithDelay
+http://briangrinstead.com/files/bindWithDelay
+
+Usage: 
+	See http://api.jquery.com/bind/
+	.bindWithDelay( eventType, [ eventData ], handler(eventObject), timeout, throttle )
+
+Examples:
+	$("#foo").bindWithDelay("click", function(e) { }, 100);
+	$(window).bindWithDelay("resize", { optional: "eventData" }, callback, 1000);
+	$(window).bindWithDelay("resize", callback, 1000, true);
+*/
+
+(function($) {
+$.fn.bindWithDelay = function( type, data, fn, timeout, throttle ) {
+	var wait = null;
+	var that = this;
+
+	if ( $.isFunction( data ) ) {
+		throttle = timeout;
+		timeout = fn;
+		fn = data;
+		data = undefined;
+	}
+
+	function cb() {
+		var e = $.extend(true, { }, arguments[0]);
+		var throttler = function() {
+			wait = null;
+			fn.apply(that, [e]);
+		};
+
+		if (!throttle) { clearTimeout(wait); }
+		if (!throttle || !wait) { wait = setTimeout(throttler, timeout); }
+	}
+
+	return this.bind(type, data, cb);
+}
+})(jQuery);

File templates/index.html

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+	"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>
+	<title>iwannapushyoudown</title>
+	
+	<link href='http://fonts.googleapis.com/css?family=Vollkorn' rel='stylesheet' type='text/css'>
+	<link href='/static/css/styles.css' rel='stylesheet' type='text/css'>
+	
+	<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
+	<script src='/_ah/channel/jsapi'></script>
+	<!-- /_ah/channel/jsapi links to https://talkgadget.google.com/talkgadget/channel.js -->
+    <script type="text/javascript" src="/static/js/bind_with_delay.js"></script>
+
+	<script>
+		var channel,
+		    client_id = '{{ client_id }}',
+			is_connected = false,
+			socket,
+			token = '{{ token }}';
+
+		var connect = function () {
+			channel = new goog.appengine.Channel(token);
+
+	        socket = channel.open();
+
+	        socket.onopen = onopen;
+	        socket.onclose = onclose;
+	        socket.onerror = onerror;
+	        socket.onmessage = onmessage;
+		}
+
+		var onopen = function () {
+			/* Called when the socket is ready to receive messages */
+			is_connected = true;
+		}
+
+		var onclose = function () {
+			/* Called when the socket is closed */
+			is_connected = false;
+		}
+
+		var onerror = function (e) {
+			/* Called when an error occurs on the socket */
+			if(typeof(console) == "object") {
+				console.log("Socket error", e.code, ":", e.description);
+			}
+		}
+
+		var onmessage = function (message) {
+			/* Called when the socket receives a message */
+			var data = JSON.parse(message.data);
+			$('#poem').val(unescape(data['escaped_message']));
+			$('#connected_clients').html(data['connected_clients']);
+		}
+
+		var push = function () {
+			/* Called when the content got changed locally */
+			$.post('/', {
+				"client_id": client_id,
+				"escaped_message": escape($(this).val())
+			});
+		}
+
+		$(document).ready(function (){
+			connect();
+
+			$('#poem').bindWithDelay('keyup', push, 1000, true);
+		});
+   </script>
+</head>
+
+<body>
+	<h1>Alone</h1>
+	<h4>by Edgar Allan Poe</h4>
+	<textarea id='poem' rows='23'>From childhood's hour I have not been 
+As others were; I have not seen 
+As others saw; I could not bring 
+My passions from a common spring. 
+From the same source I have not taken 
+My sorrow; I could not awaken 
+My heart to joy at the same tone; 
+And all I loved, I loved alone. 
+Then - in my childhood, in the dawn 
+Of a most stormy life - was drawn 
+From every depth of good and ill 
+The mystery which binds me still: 
+From the torrent, or the fountain, 
+From the red cliff of the mountain, 
+From the sun that round me rolled 
+In its autumn tint of gold, 
+From the lightning in the sky 
+As it passed me flying by, 
+From the thunder and the storm, 
+And the cloud that took the form 
+(When the rest of Heaven was blue) 
+Of a demon in my view.</textarea>
+<div id='connected_clients'>1</div>
+</body>
+
+</html>