changeset 0:89a05584e39e

initial commit of mood conductor stuff
author gyorgyf
date Sat, 16 Jun 2012 20:31:41 +0100
parents
children 71cea958c8a2
files mcserver/.DS_Store mcserver/mcdevel.cfg mcserver/mcserver.cfg mcserver/mcserver.py mcserver/static/app.js mcserver/static/app.js~ mcserver/static/index.html mcserver/static/index.html~ mcserver/static/moods.csv nodejs-server/app.js nodejs-server/app.js~ nodejs-server/index.html nodejs-server/index.html~ nodejs-server/moods.csv nodejs-server/server.js
diffstat 15 files changed, 783 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
Binary file mcserver/.DS_Store has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcserver/mcdevel.cfg	Sat Jun 16 20:31:41 2012 +0100
@@ -0,0 +1,55 @@
+#global server config
+[global]
+
+# kakapo<->golden
+# server.socket_host = "192.168.122.144"
+server.socket_host = "127.0.0.1" 
+server.socket_port = 3030
+server.thread_pool = 30
+server.show_tracebacks = False
+
+engine.autoreload_on = True #should be off
+engine.autoreload_frequency = 180 #sec
+
+tools.sessions.on = True
+tools.sessions.timeout = 600 #min
+tools.sessions.clean_freq = 120	#min	
+# tools.sessions.storage_type = 'directory'
+# tools.sessions.SESSIONS_PATH = 'sessions/'
+# tools.sessions.SESSIONS_PATH = 'sessions'
+
+# expose everything in static, but note that sessions are enabled in this scope
+tools.staticdir.on = True
+tools.staticdir.dir = 'static'
+# tools.staticdir.content_types = {'n3': 'text/rdf+n3', 'rdf': 'application/rdf+xml'}
+
+# log.error_file = "mcserver.log"
+
+#main app config
+[/]
+response.timeout = 600 #sec
+request.show_tracebacks = False
+log.access_file = 'mcserver-access.log'
+log.error_file = 'mcserver-error.log'
+# tools.staticdir.root = 'mcserver'
+tools.staticdir.on = True
+tools.staticdir.dir = 'static'
+
+# explicit configuration of static content with sessions disabled
+[/script]
+tools.sessions.on = False
+
+[/css]
+tools.sessions.on = False
+
+[/img]
+tools.sessions.on = False
+
+[/images]
+tools.sessions.on = False
+
+[/data]
+tools.sessions.on = False
+
+[/temp]
+tools.sessions.on = False
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcserver/mcserver.cfg	Sat Jun 16 20:31:41 2012 +0100
@@ -0,0 +1,38 @@
+#global server config
+[global]
+
+# kakapo<->golden
+server.socket_host = "192.168.122.144" 
+server.socket_port = 3030
+server.thread_pool = 30
+server.show_tracebacks = False
+
+engine.autoreload_on = False #should be off
+engine.autoreload_frequency = 180 #sec
+
+tools.sessions.on = True
+tools.sessions.timeout = 600 #min
+tools.sessions.clean_freq = 120	#min	
+# tools.sessions.storage_type = 'directory'
+# tools.sessions.SESSIONS_PATH = 'sessions/'
+# tools.sessions.SESSIONS_PATH = 'sessions'
+
+# expose everything in static, but note that sessions are enabled in this scope
+tools.staticdir.on = True
+tools.staticdir.dir = 'static'
+# tools.staticdir.content_types = {'n3': 'text/rdf+n3', 'rdf': 'application/rdf+xml'}
+
+
+#main app config
+[/]
+response.timeout = 600 #sec
+request.show_tracebacks = False
+# log.access_file = 'mcserver-access.log'
+# log.error_file = 'mcserver-error.log'
+# tools.staticdir.root = 'mcserver'
+tools.staticdir.on = True
+tools.staticdir.dir = 'static'
+
+# explicit configuration of static content with sessions disabled
+[/script]
+tools.sessions.on = False
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcserver/mcserver.py	Sat Jun 16 20:31:41 2012 +0100
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+mcserver.py
+
+Created by George Fazekas on 2012-06-16.
+Copyright (c) 2012 . All rights reserved.
+"""
+
+import os,sys,optparse,signal
+import cherrypy as cp
+
+from cherrypy.lib import static
+import subprocess as sp
+from subprocess import Popen as spopen
+
+
+op = optparse.OptionParser()
+op.add_option('-u', '--user', action="store", dest="USER", default="server", type="str")
+options, args = op.parse_args()
+CONFIG_FILE = "mc%(USER)s.cfg" %options.__dict__
+
+print CONFIG_FILE
+
+if not os.path.isfile(CONFIG_FILE) :
+	print >> sys.stderr, "Config file not found."
+	sys.exit(-1)
+
+
+class MoodConductor:
+	
+	# @cp.expose
+	def index(self):
+		return "Hello world!"
+	# index.exposed = True
+	
+	@cp.expose
+	def mood(self,x,y):
+		print x,y
+		return "OK"
+
+
+
+def getProcessPids(port,kill=False):
+	'''Get the pid of the offending Python process given a port after an unsuccessful restart.'''
+	print "Running lsof -i :"+str(port)," ...\n\n"
+	command = "lsof -i :"+str(port)
+	w = spopen(command,stdout=sp.PIPE,stderr=sp.PIPE,shell=True)
+	se = w.stderr.readlines()
+	result = w.stdout.readlines()
+	exitcode = w.wait()
+	if not result : 
+		print "getProcessPid:: Unable to obtain process pid. (lsof returned nothing. exitcode: %s)" %str(exitcode)
+		return False
+	import pprint
+	pprint.pprint(result)
+	
+	# get heading:
+	ix = None
+	head = result[0].upper()
+	if 'PID' in head:
+		head = filter(lambda x: x != str(), head.split(' '))
+		head = map(lambda x: x.strip().replace(' ',''), head)
+		if 'PID' in head : ix = head.index('PID')
+	# get process pid
+	pids = []
+	for line in result :
+		if 'python' in line.lower() :
+			line = filter(lambda x: x != str(), line.split(' '))
+			line = map(lambda x: x.strip().replace(' ',''), line)
+			try :
+				if ix : 
+					pids.append(int(line[ix]))
+				else:
+					numbers = filter(lambda x: x.isdigit(), line)
+					pids.append(int(numbers[0]))
+			except:
+				print 'Error parsing lsof results.' 
+				return False
+	print 'Pids found: ',pids
+	# kill if specified
+	if kill :
+		pids_killed = []
+		import signal
+		for pid in pids:
+			print 'Killing process: ',pid
+			try :
+				os.kill(pid,signal.SIGKILL)
+				pids_killed.append(pid)
+			except :
+				print 'Failed: ',pid
+		if pids_killed :
+			print 'Processes killed:',pids_killed,' Waiting 10 secods...'
+			import time
+			time.sleep(10)
+			return True
+	return False
+
+
+def main(argv=None):
+	
+	# Configure and start
+	port = 3030
+	cp.config.update({'server.socket_host': '127.0.0.1'})
+	cp.config.update({'server.socket_port': port})
+	cp.config.update({'tools.staticdir.root': os.getcwd()})
+	getProcessPids(port,kill=True)
+	cp.tree.mount(MoodConductor(),script_name="/moodconductor",config=CONFIG_FILE)
+	cp.quickstart()
+
+if __name__ == "__main__":
+	main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcserver/static/app.js	Sat Jun 16 20:31:41 2012 +0100
@@ -0,0 +1,89 @@
+var Application = {
+  init: function() {
+    this.canvas = document.getElementById('canvas');
+    this.marker = document.getElementById('marker');
+    this.label = document.getElementById('label');
+
+    this.canvas.addEventListener('mousedown', this.onMouseDown.bind(this));
+    this.canvas.addEventListener('mousemove', this.onMouseMove.bind(this));
+    this.canvas.addEventListener('mouseup', this.onMouseUp.bind(this));
+    
+    this.request("/moodconductor/moods.csv", this.loadCSV.bind(this));
+  },
+
+  loadCSV: function(text) {
+    this.moods = [];
+
+    var lines = text.split("\n");
+
+    for (var i = 1; i < lines.length; i++) {
+      var row = lines[i].split(",");
+      this.moods.push({
+        label: row[1], 
+        x: Number(row[3]), 
+        y: Number(row[5])
+      });
+    }
+  },
+  
+  request: function(url, callback) {
+    var request = new XMLHttpRequest();
+    request.onreadystatechange = function() {
+      callback(request.responseText);
+    }.bind(this);
+    request.open("GET", url);
+    request.send(null);
+  },
+
+  onMouseDown: function(event) {
+    this.mouseDown = true;
+    this.setMarker(event.pageX, event.pageY);
+  },
+
+  onMouseMove: function(event) {
+    if (this.mouseDown) {
+      this.setMarker(event.pageX, event.pageY);
+    }
+  },
+
+  onMouseUp: function(event) {
+    this.mouseDown = false;
+    this.setMarker(event.pageX, event.pageY);
+  },
+
+  setMarker: function(pageX, pageY) {
+    this.marker.style.left = pageX + 'px';
+    this.marker.style.top = pageY + 'px';
+    
+    var x = 1 - pageX / 480;
+    var y = 1 - pageY / 960;
+    
+    var mood = this.findMood(x, y);
+
+    this.marker.innerHTML = mood.label;
+   
+    if (mood != this.mood) {
+      this.mood = mood;
+      this.request("/moodconductor/mood?x=" + x + "&y=" + y);
+    }
+  },
+
+  findMood: function(x, y) {
+    var distance = 1;
+    var index = null;
+    
+    for (var i = 0; i < this.moods.length; i++) {
+      var mood = this.moods[i];
+      var dx = Math.abs(mood.x - x);
+      var dy = Math.abs(mood.y - y);
+      var d = Math.sqrt(dx * dx + dy * dy);
+
+      if (d < distance) {
+        distance = d;
+        index = i;
+      }
+    }
+
+    return this.moods[index];
+  }
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcserver/static/app.js~	Sat Jun 16 20:31:41 2012 +0100
@@ -0,0 +1,80 @@
+var Application = {
+  init: function() {
+    this.canvas = document.getElementById('canvas');
+    this.marker = document.getElementById('marker');
+    this.label = document.getElementById('label');
+
+    this.canvas.addEventListener('mousedown', this.onMouseDown.bind(this));
+    this.canvas.addEventListener('mousemove', this.onMouseMove.bind(this));
+    this.canvas.addEventListener('mouseup', this.onMouseUp.bind(this));
+    
+    this.request("/moods.csv", this.loadCSV.bind(this));
+  },
+
+  loadCSV: function(text) {
+    this.moods = [];
+
+    var lines = text.split("\n");
+
+    for (var i = 1; i < lines.length; i++) {
+      var row = lines[i].split(",");
+      this.moods.push({
+        label: row[1], 
+        x: Number(row[3]), 
+        y: Number(row[5])
+      });
+    }
+  },
+  
+  request: function(url, callback) {
+    var request = new XMLHttpRequest();
+    request.onreadystatechange = function() {
+      callback(request.responseText);
+    }.bind(this);
+    request.open("GET", url);
+    request.send(null);
+  },
+
+  onMouseDown: function(event) {
+    this.mouseDown = true;
+    this.setMarker(event.pageX, event.pageY);
+  },
+
+  onMouseMove: function(event) {
+    if (this.mouseDown) {
+      this.setMarker(event.pageX, event.pageY);
+    }
+  },
+
+  onMouseUp: function(event) {
+    this.mouseDown = false;
+    this.setMarker(event.pageX, event.pageY);
+  },
+
+  setMarker: function(x, y) {
+    this.marker.style.left = x + 'px';
+    this.marker.style.top = y + 'px';
+    
+    var _x = x / 480;
+    var _y = y / 960;
+    var match = {
+      dx: 1,
+      dy: 1,
+      index: null
+    };
+    
+    for (var i = 0; i < this.moods.length; i++) {
+      var mood = this.moods[i];
+      var dx = Math.abs(mood.x - _x);
+      var dy = Math.abs(mood.y - _y);
+
+      if (dx < match.dx && dy < match.dy) {
+        match.dx = dx;
+        match.dy = dy;
+        match.index = i;
+      }
+    }
+
+    this.label.innerHTML = this.moods[match.index].label;
+  }
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcserver/static/index.html	Sat Jun 16 20:31:41 2012 +0100
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+       "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+  <head>
+    <title>Mood Conductor</title>
+    <script src="app.js"></script>
+  </head>
+  <body onload="Application.init()" style="margin:0px;padding:0px">
+    <div style="background:#eee; position:absolute; width: 640px; height: 960px" id="canvas">
+      <span id="marker" style="position:relative; background:#ccc;"></span>
+    </div>
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcserver/static/index.html~	Sat Jun 16 20:31:41 2012 +0100
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+       "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+  <head>
+    <title>Mood Conductor</title>
+    <script src="app.js"></script>
+  </head>
+  <body onload="Application.init()" style="margin:0px;padding:0px">
+    <div id="label">mood</div>
+    <div style="position:absolute; width: 640px; height: 960px" id="canvas">
+      <div id="marker" style="position:relative; background:#ccc;width:10px;height:10px"></div>
+    </div>
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcserver/static/moods.csv	Sat Jun 16 20:31:41 2012 +0100
@@ -0,0 +1,81 @@
+ID,Label,Wdnum,ValMn,ValSD,AroMn,AroSD,DomMn,DomSD,termFreq,tfIdf,Cluster
+694,power,323,0.6925,2.21,0.70875,1.87,0.785,2.35,887,28.5893418465265,1
+540,action,1049,0.70375,1.45,0.69125,1.87,0.57875,2.06,781,28.2483558210672,1
+477,extreme,1526,0.67375,1.93,0.76375,1.97,0.58875,1.61,257,25.1763585519582,1
+6993,future,1605,0.71375,2.12,0.685,2.23,0.48625,2.56,210,24.598760450539,1
+6868,sports,2252,0.68125,1.95,0.65375,2.31,0.59625,2.13,140,23.4195748299111,1
+254,bright,671,0.8125,1.55,0.55,2.33,0.6675,1.82,17914,36.1000023619732,2
+253,lively,849,0.775,1.97,0.56625,2.9,0.63625,1.95,17557,36.0527861238605,2
+50,powerful,324,0.73,1.8,0.60375,2.69,0.77375,2.52,1588,30.1236537090796,2
+453,silly,981,0.80125,1.8,0.61,2.38,0.625,2.09,764,28.1891794407021,2
+150,hopeful,212,0.7625,1.46,0.5975,2.09,0.55125,1.92,699,27.9494397014472,2
+465,brutal,53,0.225,1.9,0.7,2.36,0.44875,2.7,1700,30.3004997894381,3
+161,fear,592,0.22,2.12,0.745,2.17,0.2775,2.2,200,24.4582721352504,3
+467,violent,478,0.16125,1.78,0.73625,2.47,0.52,2.86,168,23.95312270896,3
+671,danger,713,0.24375,2.22,0.79,2.07,0.32375,2.31,116,22.8633926619437,3
+323,angry,18,0.23125,1.7,0.77125,2.07,0.56875,2.74,92,22.1693366147185,3
+219,confused,80,0.27625,1.51,0.62875,1.88,0.405,1.91,486,26.9584966849828,4
+173,uneasy,2402,0.25875,1.91,0.62875,2.29,0.28,1.79,191,24.3253448141016,4
+6817,crime,704,0.23625,2.06,0.55125,2.69,0.39,2.24,172,24.0215836741884,4
+6813,loss,1800,0.11125,1.25,0.5975,2.95,0.1725,2.3,119,22.9392660289403,4
+1562,sadness,2125,0.15125,1.75,0.52625,2.44,0.2275,1.63,90,22.1030289758825,4
+542,rock,965,0.57,1.38,0.44,2.37,0.51875,2.01,406,26.4613688004033,5
+7000,mood,1861,0.575,1.38,0.46625,1.78,0.575,1.65,185,24.2330009076431,5
+7141,robot,2106,0.545,1.31,0.46375,2.14,0.4325,1.97,66,21.1577964718149,5
+6779,autumn,29,0.6625,2.14,0.43875,2.5,0.51875,1.85,39,19.5114355362196,5
+7670,organ,1923,0.53,1.57,0.4425,1.86,0.495,1.75,33,18.9765393849369,5
+213,serious,383,0.51,1.59,0.375,1.87,0.515,1.65,721,28.0331094308249,6
+8,sensitive,2160,0.5625,1.87,0.40875,2.21,0.44375,1.55,603,27.548796315552,6
+591,building,550,0.53625,1.15,0.365,1.94,0.53125,1.57,150,23.6221252039632,6
+789,street,412,0.5275,0.72,0.29875,1.87,0.47625,1.21,97,22.3286386724693,6
+7604,patient,929,0.53625,1.89,0.40125,2.37,0.4875,2.31,71,21.3819774332507,6
+139,relaxed,350,0.75,1.77,0.17375,2.13,0.56875,1.9,14499,35.6020149916429,7
+178,peaceful,1951,0.84625,1.28,0.23375,2.39,0.5875,1.93,9722,34.6493282121594,7
+25,gentle,183,0.78875,1.3,0.27625,2.57,0.5125,2.16,1307,29.6152576102165,7
+719,peace,308,0.84,1.75,0.24375,2.55,0.55625,2.84,8,14.1618492055577,7
+7795,sleep,399,0.775,1.77,0.225,2.66,0.55125,2.41,1,5.72358510195238,7
+19,calm,1239,0.71625,1.68,0.325,2.51,0.67125,2.06,9982,34.7127129441325,8
+15,cool,1344,0.72875,1.34,0.39625,2.27,0.62125,1.38,3106,31.8409617497445,8
+1,carefree,63,0.8175,1.38,0.39625,2.84,0.5975,2.5,2031,30.7595399590721,8
+208,innocent,814,0.68875,1.34,0.40125,1.99,0.535,2.08,1693,30.2898087798446,8
+117,tender,1011,0.74125,1.28,0.485,2.3,0.54125,1.75,910,28.6576769310574,8
+27,dark,714,0.46375,2.36,0.41,2.21,0.48,2.15,2754,31.5368320001884,9
+258,lazy,843,0.4225,2.02,0.20625,2.06,0.38375,1.93,1273,29.5460923812129,9
+6764,slow,982,0.36625,1.6,0.29875,2.22,0.41875,1.61,219,24.7192936903497,9
+6649,science,2143,0.47,2.28,0.46375,2.31,0.4775,2.13,188,24.279561687715,9
+212,solemn,405,0.415,1.51,0.32,1.95,0.45125,1.87,137,23.3558158478845,9
+272,dirty,590,0.26,2.05,0.485,2.29,0.4625,2.12,970,28.8277558155937,10
+74,moody,883,0.275,1.58,0.3975,2.38,0.42375,1.71,611,27.5846578730596,10
+329,heavy,1677,0.33625,1.38,0.4475,1.93,0.3875,1.62,568,27.385799232442,10
+367,raw,2049,0.415,1.68,0.505,2.16,0.5275,1.5,142,23.4612834269641,10
+315,busy,1233,0.39875,1.6,0.4475,1.96,0.46125,1.49,87,22.0005798416296,10
+731,energy,1488,0.77875,1.55,0.7375,1.94,0.74125,1.62,933,28.7242254785195,11
+115,intimate,821,0.82625,1.51,0.7475,2.21,0.6075,2.29,379,26.2699410242964,11
+90,adventure,630,0.825,1.5,0.7475,2.15,0.6825,1.67,359,26.1186543383373,11
+6739,holiday,791,0.81875,2.14,0.69875,2.73,0.6625,2.17,352,26.0635993732902,11
+340,festive,749,0.7875,2.26,0.6975,2.29,0.59625,2.34,303,25.6426324947449,11
+78,fun,759,0.92125,1.11,0.7775,2.01,0.725,1.85,2034,30.7633330898033,12
+155,triumphant,452,0.9775,0.73,0.7225,2.58,0.74375,2.55,1780,30.4195126890535,12
+29,romantic,364,0.915,1,0.82375,2.07,0.635,2.29,1371,29.7404866014185,12
+252,sexy,530,0.8775,1.12,0.795,1.91,0.7275,2.13,1241,29.4792060341353,12
+22,funny,1599,0.945,0.85,0.75,1.86,0.64375,1.92,969,28.8250124776833,12
+226,aggressive,9,0.5125,1.68,0.60375,2.33,0.57375,2.4,6149,33.5380693366296,13
+17,warm,2436,0.6875,1.2,0.5175,1.92,0.545,1.25,3046,31.7917491725818,13
+83,crazy,1365,0.61625,1.95,0.66,2.25,0.51625,2.33,804,28.3263031713168,13
+309,hard,781,0.5275,1.82,0.515,2.19,0.57375,1.63,370,26.2029275113697,13
+6895,news,901,0.5375,1.67,0.52125,2.11,0.45,1.88,205,24.529420068381,13
+243,scary,2139,0.28,1.83,0.71125,1.98,0.3325,2.13,2113,30.8611682388858,14
+52,tense,428,0.32,1.36,0.69125,2.1,0.5275,2.02,1380,29.7576047626339,14
+6652,drama,1446,0.3575,2.59,0.67375,1.93,0.54,2.36,867,28.5283931033577,14
+34,evil,741,0.27875,2.64,0.67375,2.44,0.53125,2.6,683,27.8868350184496,14
+123,anxious,21,0.47625,1.98,0.74,1.81,0.54125,1.82,339,25.9582288134905,14
+5,positive,2005,0.88375,1.1,0.57125,2.18,0.64875,1.83,27157,37.067584198024,15
+136,cheerful,1275,0.8875,1.35,0.67,2.41,0.72375,1.71,11764,35.1056830093899,15
+2,happy,200,0.90125,1.82,0.68625,2.77,0.70375,2.43,11479,35.0471772450881,15
+160,optimistic,1919,0.82375,1.32,0.60375,2.24,0.75,1.51,6997,33.8536040156862,15
+348,proud,334,0.87875,1.56,0.57,3.01,0.7175,2.73,5168,33.1108643649477,15
+106,sad,368,0.07625,0.95,0.39125,2.38,0.30625,2.18,4165,32.5761009776072,16
+225,lonely,261,0.14625,1.76,0.43875,2.68,0.24375,2.12,3272,31.9721116595814,16
+7102,loneliness,260,0.07625,1.02,0.445,2.97,0.18875,2.27,104,22.5376171760696,16
+287,sorrow,2240,0.165,1.68,0.435,2.38,0.33375,2.18,59,20.8116008389468,16
+288,grief,195,0.08625,1.04,0.4725,2.84,0.3125,2.35,5,12.4325371482612,16
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs-server/app.js	Sat Jun 16 20:31:41 2012 +0100
@@ -0,0 +1,89 @@
+var Application = {
+  init: function() {
+    this.canvas = document.getElementById('canvas');
+    this.marker = document.getElementById('marker');
+    this.label = document.getElementById('label');
+
+    this.canvas.addEventListener('mousedown', this.onMouseDown.bind(this));
+    this.canvas.addEventListener('mousemove', this.onMouseMove.bind(this));
+    this.canvas.addEventListener('mouseup', this.onMouseUp.bind(this));
+    
+    this.request("/moods.csv", this.loadCSV.bind(this));
+  },
+
+  loadCSV: function(text) {
+    this.moods = [];
+
+    var lines = text.split("\n");
+
+    for (var i = 1; i < lines.length; i++) {
+      var row = lines[i].split(",");
+      this.moods.push({
+        label: row[1], 
+        x: Number(row[3]), 
+        y: Number(row[5])
+      });
+    }
+  },
+  
+  request: function(url, callback) {
+    var request = new XMLHttpRequest();
+    request.onreadystatechange = function() {
+      callback(request.responseText);
+    }.bind(this);
+    request.open("GET", url);
+    request.send(null);
+  },
+
+  onMouseDown: function(event) {
+    this.mouseDown = true;
+    this.setMarker(event.pageX, event.pageY);
+  },
+
+  onMouseMove: function(event) {
+    if (this.mouseDown) {
+      this.setMarker(event.pageX, event.pageY);
+    }
+  },
+
+  onMouseUp: function(event) {
+    this.mouseDown = false;
+    this.setMarker(event.pageX, event.pageY);
+  },
+
+  setMarker: function(pageX, pageY) {
+    this.marker.style.left = pageX + 'px';
+    this.marker.style.top = pageY + 'px';
+    
+    var x = 1 - pageX / 480;
+    var y = 1 - pageY / 960;
+    
+    var mood = this.findMood(x, y);
+
+    this.marker.innerHTML = mood.label;
+   
+    if (mood != this.mood) {
+      this.mood = mood;
+      this.request("/mood?x=" + x + "&y=" + y);
+    }
+  },
+
+  findMood: function(x, y) {
+    var distance = 1;
+    var index = null;
+    
+    for (var i = 0; i < this.moods.length; i++) {
+      var mood = this.moods[i];
+      var dx = Math.abs(mood.x - x);
+      var dy = Math.abs(mood.y - y);
+      var d = Math.sqrt(dx * dx + dy * dy);
+
+      if (d < distance) {
+        distance = d;
+        index = i;
+      }
+    }
+
+    return this.moods[index];
+  }
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs-server/app.js~	Sat Jun 16 20:31:41 2012 +0100
@@ -0,0 +1,80 @@
+var Application = {
+  init: function() {
+    this.canvas = document.getElementById('canvas');
+    this.marker = document.getElementById('marker');
+    this.label = document.getElementById('label');
+
+    this.canvas.addEventListener('mousedown', this.onMouseDown.bind(this));
+    this.canvas.addEventListener('mousemove', this.onMouseMove.bind(this));
+    this.canvas.addEventListener('mouseup', this.onMouseUp.bind(this));
+    
+    this.request("/moods.csv", this.loadCSV.bind(this));
+  },
+
+  loadCSV: function(text) {
+    this.moods = [];
+
+    var lines = text.split("\n");
+
+    for (var i = 1; i < lines.length; i++) {
+      var row = lines[i].split(",");
+      this.moods.push({
+        label: row[1], 
+        x: Number(row[3]), 
+        y: Number(row[5])
+      });
+    }
+  },
+  
+  request: function(url, callback) {
+    var request = new XMLHttpRequest();
+    request.onreadystatechange = function() {
+      callback(request.responseText);
+    }.bind(this);
+    request.open("GET", url);
+    request.send(null);
+  },
+
+  onMouseDown: function(event) {
+    this.mouseDown = true;
+    this.setMarker(event.pageX, event.pageY);
+  },
+
+  onMouseMove: function(event) {
+    if (this.mouseDown) {
+      this.setMarker(event.pageX, event.pageY);
+    }
+  },
+
+  onMouseUp: function(event) {
+    this.mouseDown = false;
+    this.setMarker(event.pageX, event.pageY);
+  },
+
+  setMarker: function(x, y) {
+    this.marker.style.left = x + 'px';
+    this.marker.style.top = y + 'px';
+    
+    var _x = x / 480;
+    var _y = y / 960;
+    var match = {
+      dx: 1,
+      dy: 1,
+      index: null
+    };
+    
+    for (var i = 0; i < this.moods.length; i++) {
+      var mood = this.moods[i];
+      var dx = Math.abs(mood.x - _x);
+      var dy = Math.abs(mood.y - _y);
+
+      if (dx < match.dx && dy < match.dy) {
+        match.dx = dx;
+        match.dy = dy;
+        match.index = i;
+      }
+    }
+
+    this.label.innerHTML = this.moods[match.index].label;
+  }
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs-server/index.html	Sat Jun 16 20:31:41 2012 +0100
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+       "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+  <head>
+    <title>Mood Conductor</title>
+    <script src="app.js"></script>
+  </head>
+  <body onload="Application.init()" style="margin:0px;padding:0px">
+    <div style="background:#eee; position:absolute; width: 640px; height: 960px" id="canvas">
+      <span id="marker" style="position:relative; background:#ccc;"></span>
+    </div>
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs-server/index.html~	Sat Jun 16 20:31:41 2012 +0100
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+       "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+  <head>
+    <title>Mood Conductor</title>
+    <script src="app.js"></script>
+  </head>
+  <body onload="Application.init()" style="margin:0px;padding:0px">
+    <div id="label">mood</div>
+    <div style="position:absolute; width: 640px; height: 960px" id="canvas">
+      <div id="marker" style="position:relative; background:#ccc;width:10px;height:10px"></div>
+    </div>
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs-server/moods.csv	Sat Jun 16 20:31:41 2012 +0100
@@ -0,0 +1,81 @@
+ID,Label,Wdnum,ValMn,ValSD,AroMn,AroSD,DomMn,DomSD,termFreq,tfIdf,Cluster
+694,power,323,0.6925,2.21,0.70875,1.87,0.785,2.35,887,28.5893418465265,1
+540,action,1049,0.70375,1.45,0.69125,1.87,0.57875,2.06,781,28.2483558210672,1
+477,extreme,1526,0.67375,1.93,0.76375,1.97,0.58875,1.61,257,25.1763585519582,1
+6993,future,1605,0.71375,2.12,0.685,2.23,0.48625,2.56,210,24.598760450539,1
+6868,sports,2252,0.68125,1.95,0.65375,2.31,0.59625,2.13,140,23.4195748299111,1
+254,bright,671,0.8125,1.55,0.55,2.33,0.6675,1.82,17914,36.1000023619732,2
+253,lively,849,0.775,1.97,0.56625,2.9,0.63625,1.95,17557,36.0527861238605,2
+50,powerful,324,0.73,1.8,0.60375,2.69,0.77375,2.52,1588,30.1236537090796,2
+453,silly,981,0.80125,1.8,0.61,2.38,0.625,2.09,764,28.1891794407021,2
+150,hopeful,212,0.7625,1.46,0.5975,2.09,0.55125,1.92,699,27.9494397014472,2
+465,brutal,53,0.225,1.9,0.7,2.36,0.44875,2.7,1700,30.3004997894381,3
+161,fear,592,0.22,2.12,0.745,2.17,0.2775,2.2,200,24.4582721352504,3
+467,violent,478,0.16125,1.78,0.73625,2.47,0.52,2.86,168,23.95312270896,3
+671,danger,713,0.24375,2.22,0.79,2.07,0.32375,2.31,116,22.8633926619437,3
+323,angry,18,0.23125,1.7,0.77125,2.07,0.56875,2.74,92,22.1693366147185,3
+219,confused,80,0.27625,1.51,0.62875,1.88,0.405,1.91,486,26.9584966849828,4
+173,uneasy,2402,0.25875,1.91,0.62875,2.29,0.28,1.79,191,24.3253448141016,4
+6817,crime,704,0.23625,2.06,0.55125,2.69,0.39,2.24,172,24.0215836741884,4
+6813,loss,1800,0.11125,1.25,0.5975,2.95,0.1725,2.3,119,22.9392660289403,4
+1562,sadness,2125,0.15125,1.75,0.52625,2.44,0.2275,1.63,90,22.1030289758825,4
+542,rock,965,0.57,1.38,0.44,2.37,0.51875,2.01,406,26.4613688004033,5
+7000,mood,1861,0.575,1.38,0.46625,1.78,0.575,1.65,185,24.2330009076431,5
+7141,robot,2106,0.545,1.31,0.46375,2.14,0.4325,1.97,66,21.1577964718149,5
+6779,autumn,29,0.6625,2.14,0.43875,2.5,0.51875,1.85,39,19.5114355362196,5
+7670,organ,1923,0.53,1.57,0.4425,1.86,0.495,1.75,33,18.9765393849369,5
+213,serious,383,0.51,1.59,0.375,1.87,0.515,1.65,721,28.0331094308249,6
+8,sensitive,2160,0.5625,1.87,0.40875,2.21,0.44375,1.55,603,27.548796315552,6
+591,building,550,0.53625,1.15,0.365,1.94,0.53125,1.57,150,23.6221252039632,6
+789,street,412,0.5275,0.72,0.29875,1.87,0.47625,1.21,97,22.3286386724693,6
+7604,patient,929,0.53625,1.89,0.40125,2.37,0.4875,2.31,71,21.3819774332507,6
+139,relaxed,350,0.75,1.77,0.17375,2.13,0.56875,1.9,14499,35.6020149916429,7
+178,peaceful,1951,0.84625,1.28,0.23375,2.39,0.5875,1.93,9722,34.6493282121594,7
+25,gentle,183,0.78875,1.3,0.27625,2.57,0.5125,2.16,1307,29.6152576102165,7
+719,peace,308,0.84,1.75,0.24375,2.55,0.55625,2.84,8,14.1618492055577,7
+7795,sleep,399,0.775,1.77,0.225,2.66,0.55125,2.41,1,5.72358510195238,7
+19,calm,1239,0.71625,1.68,0.325,2.51,0.67125,2.06,9982,34.7127129441325,8
+15,cool,1344,0.72875,1.34,0.39625,2.27,0.62125,1.38,3106,31.8409617497445,8
+1,carefree,63,0.8175,1.38,0.39625,2.84,0.5975,2.5,2031,30.7595399590721,8
+208,innocent,814,0.68875,1.34,0.40125,1.99,0.535,2.08,1693,30.2898087798446,8
+117,tender,1011,0.74125,1.28,0.485,2.3,0.54125,1.75,910,28.6576769310574,8
+27,dark,714,0.46375,2.36,0.41,2.21,0.48,2.15,2754,31.5368320001884,9
+258,lazy,843,0.4225,2.02,0.20625,2.06,0.38375,1.93,1273,29.5460923812129,9
+6764,slow,982,0.36625,1.6,0.29875,2.22,0.41875,1.61,219,24.7192936903497,9
+6649,science,2143,0.47,2.28,0.46375,2.31,0.4775,2.13,188,24.279561687715,9
+212,solemn,405,0.415,1.51,0.32,1.95,0.45125,1.87,137,23.3558158478845,9
+272,dirty,590,0.26,2.05,0.485,2.29,0.4625,2.12,970,28.8277558155937,10
+74,moody,883,0.275,1.58,0.3975,2.38,0.42375,1.71,611,27.5846578730596,10
+329,heavy,1677,0.33625,1.38,0.4475,1.93,0.3875,1.62,568,27.385799232442,10
+367,raw,2049,0.415,1.68,0.505,2.16,0.5275,1.5,142,23.4612834269641,10
+315,busy,1233,0.39875,1.6,0.4475,1.96,0.46125,1.49,87,22.0005798416296,10
+731,energy,1488,0.77875,1.55,0.7375,1.94,0.74125,1.62,933,28.7242254785195,11
+115,intimate,821,0.82625,1.51,0.7475,2.21,0.6075,2.29,379,26.2699410242964,11
+90,adventure,630,0.825,1.5,0.7475,2.15,0.6825,1.67,359,26.1186543383373,11
+6739,holiday,791,0.81875,2.14,0.69875,2.73,0.6625,2.17,352,26.0635993732902,11
+340,festive,749,0.7875,2.26,0.6975,2.29,0.59625,2.34,303,25.6426324947449,11
+78,fun,759,0.92125,1.11,0.7775,2.01,0.725,1.85,2034,30.7633330898033,12
+155,triumphant,452,0.9775,0.73,0.7225,2.58,0.74375,2.55,1780,30.4195126890535,12
+29,romantic,364,0.915,1,0.82375,2.07,0.635,2.29,1371,29.7404866014185,12
+252,sexy,530,0.8775,1.12,0.795,1.91,0.7275,2.13,1241,29.4792060341353,12
+22,funny,1599,0.945,0.85,0.75,1.86,0.64375,1.92,969,28.8250124776833,12
+226,aggressive,9,0.5125,1.68,0.60375,2.33,0.57375,2.4,6149,33.5380693366296,13
+17,warm,2436,0.6875,1.2,0.5175,1.92,0.545,1.25,3046,31.7917491725818,13
+83,crazy,1365,0.61625,1.95,0.66,2.25,0.51625,2.33,804,28.3263031713168,13
+309,hard,781,0.5275,1.82,0.515,2.19,0.57375,1.63,370,26.2029275113697,13
+6895,news,901,0.5375,1.67,0.52125,2.11,0.45,1.88,205,24.529420068381,13
+243,scary,2139,0.28,1.83,0.71125,1.98,0.3325,2.13,2113,30.8611682388858,14
+52,tense,428,0.32,1.36,0.69125,2.1,0.5275,2.02,1380,29.7576047626339,14
+6652,drama,1446,0.3575,2.59,0.67375,1.93,0.54,2.36,867,28.5283931033577,14
+34,evil,741,0.27875,2.64,0.67375,2.44,0.53125,2.6,683,27.8868350184496,14
+123,anxious,21,0.47625,1.98,0.74,1.81,0.54125,1.82,339,25.9582288134905,14
+5,positive,2005,0.88375,1.1,0.57125,2.18,0.64875,1.83,27157,37.067584198024,15
+136,cheerful,1275,0.8875,1.35,0.67,2.41,0.72375,1.71,11764,35.1056830093899,15
+2,happy,200,0.90125,1.82,0.68625,2.77,0.70375,2.43,11479,35.0471772450881,15
+160,optimistic,1919,0.82375,1.32,0.60375,2.24,0.75,1.51,6997,33.8536040156862,15
+348,proud,334,0.87875,1.56,0.57,3.01,0.7175,2.73,5168,33.1108643649477,15
+106,sad,368,0.07625,0.95,0.39125,2.38,0.30625,2.18,4165,32.5761009776072,16
+225,lonely,261,0.14625,1.76,0.43875,2.68,0.24375,2.12,3272,31.9721116595814,16
+7102,loneliness,260,0.07625,1.02,0.445,2.97,0.18875,2.27,104,22.5376171760696,16
+287,sorrow,2240,0.165,1.68,0.435,2.38,0.33375,2.18,59,20.8116008389468,16
+288,grief,195,0.08625,1.04,0.4725,2.84,0.3125,2.35,5,12.4325371482612,16
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nodejs-server/server.js	Sat Jun 16 20:31:41 2012 +0100
@@ -0,0 +1,24 @@
+var http = require('http');
+var fs = require('fs');
+
+http.createServer(function (req, res) {
+  console.log(req.url);
+  
+  switch (req.url) {
+  case '/mood':
+    res.end("{status:'ok'}");
+    res.writeHead(200, {'Content-Type': 'application/json'});
+    break;
+  case '/moods.csv':
+    res.end(fs.readFileSync("moods.csv"));
+    res.writeHead(200, {'Content-Type': 'text/plain'});
+    break;
+  case '/app.js':
+    res.end(fs.readFileSync("app.js"));
+    res.writeHead(200, {'Content-Type': 'text/javascript'});
+    break;
+  default:
+    res.end(fs.readFileSync("index.html"));
+    res.writeHead(404, {'Content-Type': 'text/plain'});
+  }
+}).listen(3000, "127.0.0.1");