Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'ping'

* ping:
  meep
  my ping example, poorly named
  • Loading branch information...
commit c577e97951b673eeeb6e2c1e938d3dedf8c6ca00 2 parents 764ad1d + 7384356
@CodeOfficer authored
Showing with 18,785 additions and 0 deletions.
  1. +65 −0 experiments/ping/application.js
  2. +402 −0 experiments/ping/lib/redis.js
  3. +68 −0 experiments/ping/log.js
  4. +8 −0 experiments/ping/modules/_default.js
  5. +156 −0 experiments/ping/modules/chat.js
  6. +8 −0 experiments/ping/modules/echo.js
  7. +58 −0 experiments/ping/modules/ping.js
  8. +21 −0 experiments/ping/modules/processes.js
  9. +14 −0 experiments/ping/modules/time.js
  10. +56 −0 experiments/ping/ping.html
  11. +4 −0 experiments/ping/runserver.js
  12. +41 −0 experiments/ping/tools.js
  13. +236 −0 experiments/ping/websocket.js
  14. +89 −0 vendor/application.css
  15. BIN  vendor/base/images/ui-anim_basic_16x16.gif
  16. BIN  vendor/base/images/ui-bg_flat_0_aaaaaa_40x100.png
  17. BIN  vendor/base/images/ui-bg_flat_75_ffffff_40x100.png
  18. BIN  vendor/base/images/ui-bg_glass_55_fbf9ee_1x400.png
  19. BIN  vendor/base/images/ui-bg_glass_65_ffffff_1x400.png
  20. BIN  vendor/base/images/ui-bg_glass_75_dadada_1x400.png
  21. BIN  vendor/base/images/ui-bg_glass_75_e6e6e6_1x400.png
  22. BIN  vendor/base/images/ui-bg_glass_95_fef1ec_1x400.png
  23. BIN  vendor/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png
  24. BIN  vendor/base/images/ui-icons_222222_256x240.png
  25. BIN  vendor/base/images/ui-icons_2e83ff_256x240.png
  26. BIN  vendor/base/images/ui-icons_454545_256x240.png
  27. BIN  vendor/base/images/ui-icons_888888_256x240.png
  28. BIN  vendor/base/images/ui-icons_cd0a0a_256x240.png
  29. +477 −0 vendor/base/jquery-ui.css
  30. 0  vendor/base/jquery-ui.sass
  31. +6,240 −0 vendor/jquery-1.4.2.js
  32. +10,691 −0 vendor/jquery-ui.js
  33. +151 −0 vendor/jquery.rails.js
View
65 experiments/ping/application.js
@@ -0,0 +1,65 @@
+// https://developer.mozilla.org/en/Canvas_tutorial
+
+var NodeSocket = {
+ ws: null,
+ windowClosing: false,
+ connect: function() {
+ console.log('trying to connect ...');
+ NodeSocket.ws = new WebSocket("ws://localhost:8080/ping");
+ NodeSocket.ws.onmessage = NodeSocket.onmessage;
+ NodeSocket.ws.onclose = NodeSocket.onclose;
+ NodeSocket.ws.onopen = NodeSocket.onopen;
+ },
+ onmessage: function(e) {
+ var jsonData = JSON.parse(e.data);
+ var action = jsonData[0];
+ var data = jsonData[1];
+ if (NodeSocket.handlers[action]) {
+ NodeSocket.handlers[action](data);
+ } else {
+ console.log('handler not found');
+ };
+ },
+ onclose: function() {
+ if (!NodeSocket.windowClosing) {
+ NodeSocket.ws.send('STOP');
+ console.log('connection terminating?', NodeSocket.windowClosing);
+ };
+ },
+ onopen: function() {
+ NodeSocket.ws.send('START');
+ console.log('Connected...');
+ },
+ ping: function(data) {
+ var jsonData = JSON.stringify(data);
+ if (NodeSocket.ws) {
+ NodeSocket.ws.send(jsonData);
+ } else {
+ console.log('trying to send, but not connected');
+ };
+ }
+
+};
+
+NodeSocket.handlers = {
+ pong: function(data) {
+ $("#mark").stop(true, true).animate({"left": data.pageX, "top": data.pageY}, "fast");
+ }
+};
+
+;(function($) {
+ $(function() {
+
+ NodeSocket.connect();
+
+ $('body').click(function(e) {
+ NodeSocket.ping(['pong', { pageX: e.pageX, pageY: e.pageY} ]);
+ });
+
+ $('window').unload(function () {
+ if (NodeSocket.ws) NodeSocket.ws.close();
+ NodeSocket.windowClosing = true;
+ });
+
+ });
+})(jQuery);
View
402 experiments/ping/lib/redis.js
@@ -0,0 +1,402 @@
+// Redis client for Node.js
+// Author: Brian Hammond <brian at fictorial dot com>
+// Copyright (C) 2009 Fictorial LLC
+// License: MIT
+
+var sys = require("sys"),
+ tcp = require("tcp");
+
+var crlf = "\r\n",
+ crlf_len = 2;
+
+
+var inline_commands = {
+ auth:1, bgsave:1, dbsize:1, decr:1, decrby:1, del:1,
+ exists:1, expire:1, flushall:1, flushdb:1, get:1, incr:1, incrby:1, info:1,
+ keys:1, lastsave:1, lindex:1, llen:1, lpop:1, lrange:1, ltrim:1, mget:1,
+ move:1, randomkey:1, rename:1, renamenx:1, rpop:1, save:1, scard:1, sdiff:1,
+ sdiffstore:1, select:1, shutdown:1, sinter:1, sinterstore:1, smembers:1,
+ spop:1, srandmember:1, sunion:1, sunionstore:1, ttl:1, type:1,
+ zrange:1, zrevrange:1, zcard:1, zrangebyscore:1
+};
+
+var bulk_commands = {
+ getset:1, lpush:1, lrem:1, lset:1, rpush:1, sadd:1, set:1,
+ setnx:1, sismember:1, smove:1, srem:1, zadd:1, zrem:1, zscore:1
+};
+
+var multi_bulk_commands = {
+ mset:1, msetnx:1
+};
+
+var Client = exports.Client = function (port, host) {
+ this.host = host || '127.0.0.1';
+ this.port = port || 6379;
+ this.callbacks = [];
+ this.conn = null;
+};
+
+// Callback a function after we've ensured we're connected to Redis.
+
+Client.prototype.connect = function (callback_on_connect, callback_on_error) {
+ var self = this;
+ if (this.conn && this.conn.readyState === "open") {
+ if (typeof(callback_on_connect) === "function")
+ callback_on_connect();
+ } else {
+ this.conn = new process.tcp.Connection();
+ this.conn.addListener("connect", function () {
+ this.setEncoding("binary");
+ this.setTimeout(0); // try to stay connected.
+ this.setNoDelay();
+ if (typeof(callback_on_connect) === "function")
+ callback_on_connect();
+ });
+ this.conn.addListener("data", function (data) {
+ if (!self.buffer)
+ self.buffer = "";
+ self.buffer += data;
+ self.handle_replies();
+ });
+ this.conn.addListener("close", function (encountered_error) {
+ if (encountered_error && callback_on_error) callback_on_error();
+ });
+ this.conn.connect(this.port, this.host);
+ }
+};
+
+Client.prototype.close = function () {
+ if (this.conn && this.conn.readyState === "open") {
+ this.conn.close();
+ this.conn = null;
+ }
+};
+
+// Reply handlers read replies from the current reply buffer. At the time of
+// the call the buffer will start with at least the prefix associated with the
+// relevant reply type which is at this time always of length 1.
+//
+// Note the buffer may not contain a full reply in which case these reply
+// handlers return null. In this case the buffer is left intact for future
+// "receive" events to append onto, and the read-replies process repeats.
+// Repeat ad infinitum.
+//
+// Each handler returns [ value, next_command_index ] on success, null on
+// underflow.
+
+var prefix_len = 1;
+
+// Bulk replies resemble:
+// $6\r\nFOOBAR\r\n
+
+Client.prototype.handle_bulk_reply = function (start_at, buf) {
+ var buffer = buf || this.buffer;
+ start_at = (start_at || 0) + prefix_len;
+ var crlf_at = buffer.indexOf(crlf, start_at);
+ if (crlf_at === -1)
+ return null;
+ var value_len_str = buffer.substring(start_at, crlf_at);
+ var value_len = parseInt(value_len_str, 10);
+ if (value_len === NaN)
+ throw new Error("invalid bulk value len: " + value_len_str);
+ if (value_len === -1) // value doesn't exist
+ return [ null, crlf_at + crlf_len ];
+ var value_at = crlf_at + crlf_len;
+ var next_reply_at = value_at + value_len + crlf_len;
+ if (next_reply_at > buffer.length)
+ return null;
+ var value = buffer.substr(value_at, value_len);
+ return [ value, next_reply_at ];
+}
+
+// Mult-bulk replies resemble:
+// *4\r\n$3\r\nFOO\r\n$3\r\nBAR\r\n$5\r\nHELLO\r\n$5\r\nWORLD\r\n
+// *4 is the number of bulk replies to follow.
+
+Client.prototype.handle_multi_bulk_reply = function (buf) {
+ var buffer = buf || this.buffer;
+ var crlf_at = buffer.indexOf(crlf, prefix_len);
+ if (crlf_at === -1)
+ return null;
+ var count_str = buffer.substring(prefix_len, crlf_at);
+ var count = parseInt(count_str, 10);
+ if (count === NaN)
+ throw new Error("invalid multi-bulk count: " + count_str);
+ var next_reply_at = crlf_at + crlf_len;
+ if (count === -1) // value doesn't exist
+ return [ null, next_reply_at ];
+ if (next_reply_at >= buffer.length)
+ return null;
+ var results = [];
+ for (var i = 0; i < count; ++i) {
+ var bulk_reply = this.handle_bulk_reply(next_reply_at, buffer);
+ if (bulk_reply === null) // no full multi-bulk cmd
+ return null;
+ var bulk_reply_value = bulk_reply[0];
+ results.push(bulk_reply_value);
+ next_reply_at = bulk_reply[1];
+ }
+ return [ results, next_reply_at ];
+};
+
+// Single line replies resemble:
+// +OK\r\n
+
+Client.prototype.handle_single_line_reply = function (buf) {
+ var buffer = buf || this.buffer;
+ var crlf_at = buffer.indexOf(crlf, prefix_len);
+ if (crlf_at === -1)
+ return null;
+ var value = buffer.substring(prefix_len, crlf_at);
+ if (value === 'OK')
+ value = true;
+ var next_reply_at = crlf_at + crlf_len;
+ return [ value, next_reply_at ];
+};
+
+// Integer replies resemble:
+// :1000\r\n
+
+Client.prototype.handle_integer_reply = function (buf) {
+ var buffer = buf || this.buffer;
+ var crlf_at = buffer.indexOf(crlf, prefix_len);
+ if (crlf_at === -1)
+ return null;
+ var value_str = buffer.substring(prefix_len, crlf_at);
+ var value = parseInt(value_str, 10);
+ if (value === NaN)
+ throw new Error("invalid integer reply: " + value_str);
+ var next_reply_at = crlf_at + crlf_len;
+ return [ value, next_reply_at ];
+};
+
+// Error replies resemble:
+// -ERR you suck at tennis\r\n
+
+Client.prototype.handle_error_reply = function (buf) {
+ var buffer = buf || this.buffer;
+ var crlf_at = buffer.indexOf(crlf, prefix_len);
+ if (crlf_at === -1)
+ return null;
+ var value = buffer.substring(prefix_len, crlf_at);
+ var next_reply_at = crlf_at + crlf_len;
+ if (value.indexOf("ERR ") === 0)
+ value = value.substr("ERR ".length);
+ return [ value, next_reply_at ];
+}
+
+// Try to read as many replies from the current buffer as we can. Leave
+// partial replies in the buffer, else eat 'em. Dispatch any promises waiting
+// for these replies. Error replies emit error on the promise, else success is
+// emitted.
+
+Client.prototype.handle_replies = function () {
+ while (this.buffer.length > 0) {
+ if (GLOBAL.DEBUG) {
+ write_debug('---');
+ write_debug('buffer: ' + this.buffer);
+ }
+ var prefix = this.buffer.charAt(0);
+ var result, is_error = false;
+ switch (prefix) {
+ case '$': result = this.handle_bulk_reply(); break;
+ case '*': result = this.handle_multi_bulk_reply(); break;
+ case '+': result = this.handle_single_line_reply(); break;
+ case ':': result = this.handle_integer_reply(); break;
+ case '-': result = this.handle_error_reply(); is_error = true; break;
+ }
+ // The handlers return null when there's not enough data
+ // in the buffer to read a full reply. Leave the buffer alone until
+ // we receive more data.
+ if (result === null)
+ break;
+ if (GLOBAL.DEBUG) {
+ write_debug('prefix: ' + prefix);
+ write_debug('result: ' + JSON.stringify(result));
+ }
+ var next_reply_at = result[1];
+ this.buffer = this.buffer.substring(next_reply_at);
+ var callback = this.callbacks.shift();
+ if (callback.promise) {
+ var result_value = result[0];
+ if (is_error)
+ callback.promise.emitError(result_value);
+ else {
+ result_value = post_process_results(callback.command, result_value);
+ callback.promise.emitSuccess(result_value);
+ }
+ }
+ }
+};
+
+function write_debug(data) {
+ if (!GLOBAL.DEBUG || !data) return;
+ sys.puts(data.replace(/\r\n/g, '<CRLF>'));
+}
+
+function try_convert_to_number(str) {
+ var value = parseInt(str, 10);
+ if (value === NaN)
+ value = parseFloat(str);
+ if (value === NaN)
+ return str;
+ return value;
+}
+
+function format_inline(name, args) {
+ var command = name;
+ for (var arg in args)
+ command += ' ' + args[arg].toString();
+ return command + crlf;
+}
+
+function format_bulk_command(name, args) {
+ var output = name;
+ for (var i = 0; i < args.length - 1; ++i)
+ output += ' ' + args[i].toString();
+ var last_arg = args[args.length - 1].toString();
+ return output + ' ' + last_arg.length + crlf + last_arg + crlf;
+}
+
+function format_multi_bulk_command(name, args) {
+ var output = '*' + (args.length + 1) + crlf + '$' + name.length + crlf + name + crlf;
+ for (var i = 0; i < args.length; ++i)
+ output += '$' + args[i].length + crlf + args[i] + crlf;
+ return output;
+}
+
+function make_command_sender(name) {
+ Client.prototype[name] = function () {
+ if (GLOBAL.DEBUG) {
+ var description = "client." + name + "( ";
+ for (var a in arguments)
+ description += "'" + arguments[a] + "',";
+ description = description.substr(0, description.length - 1) + " )";
+ }
+ var args = arguments;
+ var self = this;
+ var promise = new process.Promise();
+ this.connect(function () {
+ var command;
+ if (inline_commands[name])
+ command = format_inline(name, args);
+ else if (bulk_commands[name])
+ command = format_bulk_command(name, args);
+ else if (multi_bulk_commands[name])
+ command = format_multi_bulk_command(name, args);
+ else
+ throw new Error('unknown command type for "' + name + '"');
+ if (GLOBAL.DEBUG) {
+ write_debug("---");
+ write_debug("call: " + description);
+ write_debug("command:" + command);
+ }
+ self.callbacks.push({ promise:promise, command:name.toLowerCase() });
+ self.conn.write(command);
+ });
+ return promise;
+ };
+}
+
+for (var name in inline_commands)
+ make_command_sender(name);
+
+for (var name in bulk_commands)
+ make_command_sender(name);
+
+for (var name in multi_bulk_commands)
+ make_command_sender(name);
+
+function post_process_results(command, result) {
+ var new_result = result;
+ switch (command) {
+ case 'info':
+ var info = {};
+ result.split(/\r\n/g).forEach(function (line) {
+ var parts = line.split(':');
+ if (parts.length === 2)
+ info[parts[0]] = try_convert_to_number(parts[1]);
+ });
+ new_result = info;
+ break;
+ case 'keys':
+ new_result = result.split(' ');
+ break;
+ case 'lastsave':
+ case 'scard':
+ case 'zcard':
+ case 'zscore':
+ new_result = try_convert_to_number(result);
+ break;
+ default:
+ break;
+ }
+ return new_result;
+}
+
+// Read this: http://code.google.com/p/redis/wiki/SortCommand
+// 'key' is what to sort, 'options' is how to sort.
+// 'options' is an object with optional properties:
+// 'by_pattern': 'pattern'
+// 'limit': [start, end]
+// 'get_patterns': [ 'pattern', 'pattern', ... ]
+// 'ascending': true|false
+// 'lexicographically': true|false
+// 'store_key': 'a_key_name'
+
+Client.prototype.sort = function (key, options) {
+ var promise = new process.Promise();
+ var self = this;
+ this.connect(function () {
+ var opts = [];
+ if (typeof(options) == 'object') {
+ if (options.by_pattern)
+ opts.push('by ' + options.by_pattern);
+ if (options.get_patterns) {
+ options.get_patterns.forEach(function (pat) {
+ opts.push('get ' + pat);
+ });
+ }
+ if (!options.ascending)
+ opts.push('desc');
+ if (options.lexicographically)
+ opts.push('alpha');
+ if (options.store_key)
+ opts.push('store ' + options.store_key);
+ if (options.limit)
+ opts.push('limit ' + options.limit[0] + ' ' + options.limit[1]);
+ }
+ var command = 'sort ' + key + ' ' + opts.join(' ') + crlf;
+ write_debug("call: client.sort(...)\ncommand: " + command);
+ self.callbacks.push({ promise:promise, command:'sort' });
+ self.conn.write(command);
+ });
+ return promise;
+}
+
+Client.prototype.quit = function () {
+ if (this.conn.readyState != "open") {
+ this.conn.close();
+ } else {
+ this.conn.write('quit' + crlf);
+ this.conn.close();
+ }
+};
+
+Client.prototype.make_master = function () {
+ var self = this;
+ this.connect(function () {
+ self.callbacks.push({ promise:null, command:'slaveof' });
+ self.conn.write('slaveof no one');
+ });
+};
+
+Client.prototype.make_slave_of = function (host, port) {
+ var self = this;
+ this.connect(function () {
+ port = port || 6379;
+ var command = 'slaveof ' + host + ' ' + port;
+ self.callbacks.push({ promise:null, command:'slaveof' });
+ self.conn.write(command);
+ });
+};
View
68 experiments/ping/log.js
@@ -0,0 +1,68 @@
+/*
+---
+name: log.js
+
+description: <
+ Very simple logging based on [redis](http://code.google.com/p/redis/) so that:
+ * We don't have to implement a piped logging mechanism / log rotation (no file size concerns)
+ * We can perform common operations like accessing the tail or a RANGE of the logs efficiently
+
+todo:
+ - Support for multiple loggers, and not a single global one. (Logger as a protypical class)
+
+author: [Guillermo Rauch](http://devthought.com)
+...
+*/
+
+var sys = require('sys'),
+ redis = require('./lib/redis'),
+ ready = null,
+ busy = false,
+ client = new redis.Client(),
+ stack = [],
+ key = 'logs',
+
+/*
+ * Called when the redis client is connected. Pushes stack messages
+ *
+ */
+onConnect = function(){
+ sys.puts('[log.js] Connected to redis');
+ ready = true;
+ processStack();
+},
+
+/*
+ * Logging method. If server is not ready or a message hasn't been delivered yet, stack the logs.
+ *
+ */
+log = function(message){
+ sys.puts(message);
+ stack.push(message);
+ if (ready && !busy) processStack();
+},
+
+/*
+ * Does the logging processing in a non-blocking but ordered way.
+ *
+ */
+processStack = function(){
+ if (stack.length){
+ client.rpush(key, stack.shift()).addCallback(processStack);
+ } else {
+ busy = false;
+ }
+};
+
+this.setKey = function(name){
+ key = name;
+};
+
+this.store = function(message, type){
+ log(message);
+};
+
+// connect to redis
+client.connect(onConnect, function(){
+ ready = false;
+});
View
8 experiments/ping/modules/_default.js
@@ -0,0 +1,8 @@
+var Module = this.Module = function(data, connection){
+ // do something
+ // connection is the instance of websocket::Connection
+};
+
+Module.prototype.onData = function(data, connection){
+ // connection.send(data);
+}
View
156 experiments/ping/modules/chat.js
@@ -0,0 +1,156 @@
+var Module = this.Module = function(){
+ this.server = null;
+
+
+ this.addConnection = function(connection) {
+ this.server.chatConnections.push(connection);
+ connection.id = this.server.nextIdToAssign++;
+ };
+ // Broadcast a message to all clients. The advantage of having this
+ // is that we can keep track of all message that have come through and
+ // replay the last N messages when new clients log on.
+ this.broadcast = function(fn) {
+ this.server.chatConnections.forEach(function(connection) {
+ var data = fn(connection);
+ connection.send(JSON.stringify(data));
+ })
+ this.server.chatHistory.push(fn({}));
+ };
+
+ this.lastNMessages = function(N) {
+ if (this.server.chatHistory.length < N) {
+ N = this.server.chatHistory.length;
+ }
+
+ var lastNMessages = []
+ for (var idx = this.server.chatHistory.length - N; idx < this.server.chatHistory.length; ++idx) {
+ lastNMessages.push(this.server.chatHistory[idx]);
+
+ }
+
+ return lastNMessages;
+ }
+
+ this.removeConnection = function(connection) {
+ for (var i = 0; i < this.server.chatConnections.length; i++) {
+ if (this.server.chatConnections[i].id == connection.id) {
+ this.server.chatConnections.splice(i,1)
+ break;
+ }
+ }
+
+ this.broadcast(function(conn) {
+ var timestamp = new Date();
+ return { 'nick': connection.nick,
+ 'type': 'PART',
+ 'timestamp': timestamp.toString() };
+ });
+ };
+
+ this.say = function(connection, message) {
+ this.broadcast(function(conn) {
+ var timestamp = new Date();
+ return {
+ 'type': "MSG",
+ 'isSelf': (conn.id == connection.id),
+ 'text': message,
+ 'timestamp': timestamp.toString(),
+ 'nick': connection.nick
+ };
+ });
+ };
+
+ this.action = function(connection, action) {
+ this.broadcast(function(conn) {
+ var timestamp = new Date();
+ return {
+ 'type': "ACTION",
+ 'isSelf': (conn.id == connection.id),
+ 'text': action,
+ 'timestamp': timestamp.toString(),
+ 'nick': connection.nick
+ };
+ });
+ };
+
+ this.topic = function(connection, topic) {
+ this.broadcast(function(conn) {
+ var timestamp = new Date();
+ return {
+ 'type': "TOPIC",
+ 'text': topic,
+ 'timestamp': timestamp.toString(),
+ 'nick': connection.nick
+ }
+ });
+ }
+};
+
+Module.prototype.onData = function(data, connection){
+ // setup server
+ this.server = connection.server;
+ // is this the first connection?
+ if (typeof(this.server.chatConnections) == "undefined") {
+ this.server.chatConnections = [];
+ this.server.chatHistory = [];
+ this.server.chatTopic = "Chat Room";
+ this.server.chatTopicUser = "System";
+ this.server.nextIdToAssign = 0;
+ }
+
+ // handle the data
+ if (data.match(/^NICK /)) {
+ connection.nick = data.substr(5);
+ this.broadcast(function(conn) {
+ var timestamp = new Date();
+ return { 'nick': connection.nick,
+ 'type': 'JOIN',
+ 'timestamp': timestamp.toString() };
+ });
+
+ // send last 10 messages to client
+ var historySize = 10;
+ var history = this.lastNMessages(historySize);
+ for (var i=0; i< history.length; i++) {
+ connection.send(JSON.stringify(history[i]));
+ }
+ // send topic
+ connection.send(JSON.stringify({
+ 'type': "TOPIC",
+ 'nick': this.server.chatTopicUser,
+ 'topic': this.server.chatTopic
+ }));
+ this.addConnection(connection);
+ connection.send(JSON.stringify({'type': "RPL_WELCOME"}));
+
+ }
+ if (data.match(/^MSG /)) {
+ message = data.substr(4);
+ this.say(connection, message);
+ }
+ if (data.match(/^ACTION /)) {
+ action = data.substr(7);
+ this.action(connection, action);
+ }
+ if (data.match(/^USERS/)) {
+ var users = new Array;
+ for(var i=0; i< this.server.chatConnections.length; i++) {
+ users.push(this.server.chatConnections[i]['nick']);
+ }
+ connection.send(JSON.stringify({'type': "RPL_USERS", 'nicks': users}));
+ }
+ if (data.match(/^TOPIC /)) {
+ topic = data.substr(6);
+ this.server.chatTopic = topic;
+ this.server.chatTopicUser = connection.nick;
+ this.topic(connection, topic)
+ }
+ if (data.match(/^PART/)) {
+ connection.socket.close();
+ }
+
+};
+
+Module.prototype.onDisconnect = function(connection){
+ this.removeConnection(connection);
+};
View
8 experiments/ping/modules/echo.js
@@ -0,0 +1,8 @@
+var Module = this.Module = function(data, connection){
+ // do something
+ // connection is the instance of websocket::Connection
+};
+
+Module.prototype.onData = function(data, connection) {
+ connection.send(data);
+};
View
58 experiments/ping/modules/ping.js
@@ -0,0 +1,58 @@
+var logger = require('../log');
+
+var Module = this.Module = function(data, connection){
+ this.server = null;
+
+ this.addConnection = function(connection) {
+ logger.store('[addConnection]:' + connection);
+ this.server.chatConnections.push(connection);
+ connection.id = this.server.nextIdToAssign++;
+ };
+
+ this.broadcast = function(fn) {
+ var data = fn(connection);
+ var str = 'pageX:' + data[1].pageX + ' pageY:' + data[1].pageY;
+ logger.store('[broadcast] DATA: ' + str);
+ this.server.chatConnections.forEach(function(connection) {
+ connection.send(JSON.stringify(data));
+ });
+ };
+
+ this.removeConnection = function(connection) {
+ logger.store('[removeConnection]');
+ for (var i = 0; i < this.server.chatConnections.length; i++) {
+ if (this.server.chatConnections[i].id == connection.id) {
+ this.server.chatConnections.splice(i,1);
+ break;
+ }
+ }
+ };
+};
+
+Module.prototype.onData = function(data, connection) {
+ // setup server
+ this.server = connection.server;
+ // is this the first connection?
+ if (typeof(this.server.chatConnections) == "undefined") {
+ this.server.chatConnections = [];
+ this.server.nextIdToAssign = 0;
+ };
+
+ switch (data) {
+ case 'START':
+ this.addConnection(connection);
+ break;
+ case 'STOP':
+ this.removeConnection(connection);
+ break;
+ default:
+ this.broadcast(function(conn) {
+ return JSON.parse(data);
+ });
+ };
+};
+
+Module.prototype.onDisconnect = function(connection){
+ logger.store('[onDisconnect]');
+ this.removeConnection(connection);
+};
View
21 experiments/ping/modules/processes.js
@@ -0,0 +1,21 @@
+var Module = this.Module = function(data, connection){
+};
+var ps;
+
+
+Module.prototype.onData = function(data, connection) {
+ if (data == 'start'){
+ connection.send("Gathering ps data");
+ this.interval = setInterval(function(){
+ var sys = require('sys');
+ sys.exec("ps awux").addCallback(function (stdout, stderr) {
+ ps = stdout;
+ }).wait();
+ connection.send(ps);
+ }, 5000);
+ }
+};
+
+Module.prototype.onDisconnect = function(connection){
+ clearInterval(this.interval);
+};
View
14 experiments/ping/modules/time.js
@@ -0,0 +1,14 @@
+var Module = this.Module = function(){
+};
+
+Module.prototype.onData = function(data, connection){
+ if (data == 'start'){
+ this.interval = setInterval(function(){
+ connection.send(JSON.stringify({time: new Date().toString()}));
+ }, 1000);
+ }
+};
+
+Module.prototype.onDisconnect = function(connection){
+ clearInterval(this.interval);
+};
View
56 experiments/ping/ping.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title></title>
+ <meta content='text/html; charset=utf-8' http-equiv='Content-Type'>
+ <link href='../../vendor/base/jquery-ui.css' rel='stylesheet' type='text/css'>
+ <link href='../../vendor//application.css' rel='stylesheet' type='text/css'>
+ <script src='../../vendor/jquery-1.4.2.js' type='text/javascript'></script>
+ <script src='../../vendor/jquery-ui.js' type='text/javascript'></script>
+ <script src='../../vendor/jquery.rails.js' type='text/javascript'></script>
+ <script src='application.js' type='text/javascript'></script>
+ <script type="text/javascript" charset="utf-8">
+ $(function() {
+ // $('#header').effect("explode", {}, 2000);
+ });
+ </script>
+ </head>
+ <body>
+ <div id='header'>
+ <ul>
+ <li>
+ <a href='/'>Home</a>
+ </li>
+ </ul>
+ </div>
+ <div id='container'>
+ <h2>Home</h2>
+ <p>Here is some text.</p>
+ <div id='mark'>
+ X
+ </div>
+ </div>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ <br/>
+ </body>
+</html>
View
4 experiments/ping/runserver.js
@@ -0,0 +1,4 @@
+var tools = require('./tools'),
+ websocket = require('./websocket');
+
+new websocket.Server(tools.argvToObject(process.ARGV));
View
41 experiments/ping/tools.js
@@ -0,0 +1,41 @@
+/*
+---
+name: tools.js
+
+description: Augments prototypes with some useful functions
+
+author: [Guillermo Rauch](http://devthought.com)
+...
+*/
+
+// from jQuery (Dual licensed under the MIT and GPL licenses.)
+// Copyright (c) 2009 John Resig
+this.isArray = function(obj) {
+ return Object.prototype.toString.call(obj) === "[object Array]";
+}
+
+// from MooTools (MIT-style license)
+// Copyright (c) 2009 Valerio Proietti
+this.substitute = function(str, object, regexp){
+ return str.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
+ if (match.charAt(0) == '\\') return match.slice(1);
+ return (object[name] != undefined) ? object[name] : '';
+ });
+};
+
+// dummy merge
+this.merge = function(obj, newobj){
+ if (!newobj) return obj;
+ for (var i in newobj) obj[i] = newobj[i];
+ return obj;
+};
+
+// reads argv, parses --option=value into {option: value}
+this.argvToObject = function(argv){
+ var obj = {}, regex = /\-\-(\w+)(\=(\\'.+\\'))?/;
+ for (var i = 0, l = argv.length, match; i < l; i++){
+ match = argv[i].match(regex);
+ if (match) obj[match[1]] = match[3] !== undefined ? eval(match[3]) : null;
+ }
+ return obj;
+};
View
236 experiments/ping/websocket.js
@@ -0,0 +1,236 @@
+/*
+---
+name: server.js
+
+description: Main server initialization
+
+author: [Guillermo Rauch](http://devthought.com)
+...
+*/
+
+var tcp = require('tcp'),
+ sys = require('sys'),
+ tools = require('./tools'),
+ logger = require('./log'),
+
+ // what the request headers should match
+ requestHeaders = [
+ /^GET (\/[^\s]*) HTTP\/1\.1$/,
+ /^Upgrade: WebSocket$/,
+ /^Connection: Upgrade$/,
+ /^Host: (.+)$/,
+ /^Origin: (.+)$/
+ ],
+
+ // what the response headers should be
+ responseHeaders = [
+ 'HTTP/1.1 101 Web Socket Protocol Handshake',
+ 'Upgrade: WebSocket',
+ 'Connection: Upgrade',
+ 'WebSocket-Origin: {origin}',
+ 'WebSocket-Location: {protocol}://{host}{resource}',
+ '',
+ ''
+ ],
+
+ log = function(message, type){
+ logger.store('['+ new Date() +'] ['+ (type || 'error') +'] ' + message);
+ },
+
+ empty = new Function,
+
+ Connection,
+
+Server = this.Server = function(options){
+ this.options = tools.merge({
+ port: 8080,
+ host: 'localhost',
+ origins: '*',
+ log: true,
+ logKey: null,
+ tls: false
+ }, options || {});
+
+ if (this.options.logKey === null) logger.setKey('node.websocket.' + this.options.host + '.' + this.options.port);
+
+ var self = this;
+ this.clients = 0;
+ this.server = tcp.createServer(function(socket){
+ new Connection(self, socket);
+ });
+ this.server.listen(this.options.port, this.options.host);
+
+ if (this.options.log) setInterval(function(){
+ sys.puts('['+ new Date() +'] [info] ' + self.clients + ' clients connected', 'info');
+ }, 5000);
+};
+
+Server.prototype._verifyOrigin = function(origin){
+ if (this.options.origins === '*' || this.options.origins === origin) return true;
+ if (!tools.isArray(this.options.origins)){
+ log('No valid `origins` array passed to constructor. This server wont accept any connections.', 'info');
+ return false;
+ }
+ for (var i = 0, l = this.options.origins.length; i < l; i++){
+ if (this.options.origins[i] === origin) return true;
+ }
+ return false;
+};
+
+Server.prototype._onConnect = function(){
+ this.clients++;
+};
+
+Server.prototype._onDisconnect = function(){
+ this.clients--;
+};
+
+this.Connection = Connection = function(server, socket){
+ this.server = server;
+ this.socket = socket;
+ this.handshaked = false;
+ this.data = "";
+
+ this.log = server.options.log ? function(message, type){
+ log('[client '+ socket.remoteAddress +'] ' + message, type);
+ } : empty;
+ this.log('Server created', 'info');
+
+ var self = this;
+ if (server.options.tls) socket.setSecure();
+ socket.setTimeout(0);
+ socket.setNoDelay(true); // disabling Nagle's algorithm is encouraged for real time transmissions
+ socket.setEncoding('utf8'); // per spec
+ socket.addListener('connect', function(){ self.onConnect(); });
+ socket.addListener('data', function(data){ self._onData(data); });
+ socket.addListener('end', function(){ self._onDisconnect(); });
+};
+
+Connection.prototype.onConnect = function(data){
+ this.log("Connected", "info")
+
+ this.server._onConnect(this);
+};
+
+Connection.prototype._onData = function(data){
+ if (this.handshaked){
+ this._handle(data);
+ } else {
+ this._handshake(data)
+ }
+};
+
+Connection.prototype._onDisconnect = function(){
+ this.log("Disconnected", "info")
+
+ if (this.module && this.module.onDisconnect) this.module.onDisconnect(this);
+ this.socket.close();
+ this.server._onDisconnect(this);
+};
+
+Connection.prototype.send = function(data){
+ try {
+ this.socket.write('\u0000' + data + '\uffff');
+ } catch(e) {
+ this.socket.close();
+ }
+};
+
+Connection.prototype._handle = function(data){
+ this.data += data;
+
+ chunks = this.data.split('\ufffd');
+ chunk_count = chunks.length - 1; // last chunk is either incomplete or ""
+
+ for (var i = 0; i < chunk_count; i++) {
+ chunk = chunks[i];
+ if (chunk[0] != '\u0000') {
+ this.log('Data incorrectly framed by UA. Dropping connection');
+ this.socket.close();
+ return false;
+ }
+
+ this.module.onData(chunk.slice(1), this);
+ }
+
+ this.data = chunks[chunks.length - 1];
+
+ return true;
+};
+
+Connection.prototype._handshake = function(data){
+ this.log('Performing handshake', 'info');
+
+ var self = this,
+ matches = [],
+ module,
+ headers = data.split('\r\n');
+
+ if (headers.length && headers[0].match(/<policy-file-request.*>/)) {
+ // allows Flash based WebSocket implementation to connect.
+ // e.g. http://github.com/gimite/web-socket-js
+ this.log('Flash policy file request');
+ this._serveFlashPolicy();
+ return false;
+ }
+
+ for (var i = 0, l = headers.length, match; i < l; i++){
+ if (i === requestHeaders.length) break; // handle empty lines that UA send
+ match = headers[i].match(requestHeaders[i]);
+ if (match && match.length > 1){
+ // if there's a capture group, push it into the matches
+ matches.push(match[1]);
+ } else if (!match) {
+ this.log('Handshake aborted. Bad header ' + headers[i]);
+ this.socket.close()
+ return false;
+ }
+ }
+
+ if (!this.server._verifyOrigin(matches[2])){
+ this.log('Handshake aborted. Security policy disallows Origin: ' + matches[2]);
+ this.socket.close();
+ }
+
+ module = './modules' + (matches[0] == '/' ? '/_default' : matches[0]).toLowerCase();
+ try {
+ var module = require(module);
+ this.module = new module.Module();
+ } catch(e){
+ this.log('Handshake aborted. Could not stat module file ' + module + '.js' + ' for resource ' + matches[0]);
+ this.socket.close();
+ return false;
+ }
+
+ if (!this.module.onData){
+ this.log('Module ' + module + '.js doesn\'t implement an onData method.');
+ this.socket.close();
+ return false;
+ }
+
+ this.socket.write(tools.substitute(responseHeaders.join('\r\n'), {
+ resource: matches[0],
+ host: matches[1],
+ origin: matches[2],
+ protocol: this.server.secure ? 'wss' : 'ws'
+ }));
+
+ this.handshaked = true;
+ this.log('Handshake sent', 'info');
+ return true;
+};
+
+Connection.prototype._serveFlashPolicy = function(){
+ var origins = this.server.options.origins;
+ if (!tools.isArray(origins)) {
+ origins = [origins];
+ }
+ this.socket.write('<?xml version="1.0"?>\n');
+ this.socket.write('<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">\n');
+ this.socket.write('<cross-domain-policy>\n');
+ for (var i = 0, l = origins.length; i < l; i++){
+ this.socket.write(' <allow-access-from domain="' + origins[i] + '" to-ports="' + this.server.options.port + '"/>\n');
+ }
+ this.socket.write('</cross-domain-policy>\n');
+ this.socket.close();
+};
View
89 vendor/application.css
@@ -0,0 +1,89 @@
+body {
+ background-color: #4b7399;
+ font-family: Verdana, Helvetica, Arial;
+ font-size: 14px; }
+
+a {
+ color: #0000FF; }
+ a img {
+ border: none; }
+
+textarea {
+ height: 120px; }
+
+.clear {
+ clear: both;
+ height: 0;
+ overflow: hidden; }
+
+#container {
+ width: 75%;
+ margin: 0 auto;
+ background: #fff;
+ padding: 20px 40px;
+ border: solid 1px black;
+ margin-top: 5px; }
+
+#flash_notice,
+#flash_error {
+ padding: 5px 8px;
+ margin: 10px 0; }
+
+#flash_notice {
+ background-color: #CFC;
+ border: solid 1px #6C6; }
+
+#flash_error {
+ background-color: #FCC;
+ border: solid 1px #C66; }
+
+.fieldWithErrors {
+ display: inline; }
+
+#errorExplanation {
+ width: 400px;
+ border: 2px solid #CF0000;
+ padding: 0;
+ padding-bottom: 12px;
+ margin-bottom: 20px;
+ background-color: #f0f0f0; }
+ #errorExplanation h2 {
+ text-align: left;
+ padding: 5px 5px 5px 15px;
+ margin: 0;
+ font-weight: bold;
+ font-size: 12px;
+ background-color: #c00;
+ color: #fff; }
+ #errorExplanation p {
+ color: #333;
+ margin-bottom: 0;
+ padding: 8px; }
+ #errorExplanation ul {
+ margin: 2px 24px; }
+ #errorExplanation ul li {
+ font-size: 12px;
+ list-style: disc; }
+
+#header {
+ width: 75%;
+ margin: 0 auto;
+ background: #fff;
+ padding: 5px 40px;
+ border: solid 1px black;
+ margin-top: 20px; }
+ #header ul {
+ display: inline;
+ padding-left: 0px; }
+ #header ul li {
+ list-style: none;
+ margin-right: 10px;
+ display: inline; }
+
+#mark {
+ position: absolute;
+ width: 30px;
+ height: 30px;
+ border: solid 1px black;
+ top: 50px;
+ left: 50px; }
View
BIN  vendor/base/images/ui-anim_basic_16x16.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  vendor/base/images/ui-bg_flat_0_aaaaaa_40x100.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  vendor/base/images/ui-bg_flat_75_ffffff_40x100.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  vendor/base/images/ui-bg_glass_55_fbf9ee_1x400.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  vendor/base/images/ui-bg_glass_65_ffffff_1x400.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  vendor/base/images/ui-bg_glass_75_dadada_1x400.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  vendor/base/images/ui-bg_glass_75_e6e6e6_1x400.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  vendor/base/images/ui-bg_glass_95_fef1ec_1x400.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  vendor/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  vendor/base/images/ui-icons_222222_256x240.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  vendor/base/images/ui-icons_2e83ff_256x240.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  vendor/base/images/ui-icons_454545_256x240.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  vendor/base/images/ui-icons_888888_256x240.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  vendor/base/images/ui-icons_cd0a0a_256x240.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
477 vendor/base/jquery-ui.css
@@ -0,0 +1,477 @@
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+*/
+
+/* Layout helpers
+----------------------------------*/
+.ui-helper-hidden { display: none; }
+.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
+.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+.ui-helper-clearfix { display: inline-block; }
+/* required comment for clearfix to work in Opera \*/
+* html .ui-helper-clearfix { height:1%; }
+.ui-helper-clearfix { display:block; }
+/* end clearfix */
+.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+/* Accordion
+----------------------------------*/
+.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.ui-accordion .ui-accordion-li-fix { display: inline; }
+.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
+.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
+.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
+.ui-accordion .ui-accordion-content-active { display: block; }/* Autocomplete
+----------------------------------*/
+.ui-autocomplete { position: absolute; cursor: default; }
+.ui-autocomplete-loading { background: white url('images/ui-anim_basic_16x16.gif') right center no-repeat; }
+
+/* workarounds */
+* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
+
+/* Menu
+----------------------------------*/
+.ui-menu {
+ list-style:none;
+ padding: 2px;
+ margin: 0;
+ display:block;
+}
+.ui-menu .ui-menu {
+ margin-top: -3px;
+}
+.ui-menu .ui-menu-item {
+ margin:0;
+ padding: 0;
+ width: 100%;
+}
+.ui-menu .ui-menu-item a {
+ text-decoration:none;
+ display:block;
+ padding:.2em .4em;
+ line-height:1.5;
+}
+.ui-menu .ui-menu-item a.ui-state-hover,
+.ui-menu .ui-menu-item a.ui-state-active {
+ margin: -1px;
+}
+/* Button
+----------------------------------*/
+
+.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
+.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
+button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
+.ui-button-icons-only { width: 3em; }
+button.ui-button-icons-only { width: 3.2em; }
+
+/*button text element */
+.ui-button .ui-button-text { display: block; line-height: 1.4; }
+.ui-button-text-only .ui-button-text { padding: .4em 1em; }
+.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
+.ui-button-text-icon .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 1.8em; }
+.ui-button-text-icons .ui-button-text { padding-right: 1.8em; }
+/* no icon support for input elements, provide padding by default */
+input.ui-button { padding: .4em 1em; }
+
+/*button icon element(s) */
+.ui-button-icon-only .ui-icon, .ui-button-text-icon .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
+.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
+.ui-button-text-icon .ui-icon-primary, .ui-button-text-icons .ui-icon-primary, .ui-button-icons-only .ui-icon-primary { left: .5em; }
+.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+
+/*button sets*/
+.ui-button-set { margin-right: 7px; }
+.ui-button-set .ui-button { margin-left: 0; margin-right: -.3em; }
+
+/* workarounds */
+button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
+
+
+
+
+
+/* Datepicker
+----------------------------------*/
+.ui-datepicker { width: 17em; padding: .2em .2em 0; }
+.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.ui-datepicker .ui-datepicker-prev { left:2px; }
+.ui-datepicker .ui-datepicker-next { right:2px; }
+.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
+.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
+.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.ui-datepicker select.ui-datepicker-month,
+.ui-datepicker select.ui-datepicker-year { width: 49%;}
+.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
+.ui-datepicker td { border: 0; padding: 1px; }
+.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.ui-datepicker.ui-datepicker-multi { width:auto; }
+.ui-datepicker-multi .ui-datepicker-group { float:left; }
+.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.ui-datepicker-row-break { clear:both; width:100%; }
+
+/* RTL support */
+.ui-datepicker-rtl { direction: rtl; }
+.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.ui-datepicker-cover {
+ display: none; /*sorry for IE5*/
+ display/**/: block; /*sorry for IE5*/
+ position: absolute; /*must have*/
+ z-index: -1; /*must have*/
+ filter: mask(); /*must have*/
+ top: -4px; /*must have*/
+ left: -4px; /*must have*/
+ width: 200px; /*must have*/
+ height: 200px; /*must have*/
+}/* Dialog
+----------------------------------*/
+.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
+.ui-dialog .ui-dialog-titlebar { padding: .5em 1em .3em; position: relative; }
+.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; }
+.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
+.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.ui-draggable .ui-dialog-titlebar { cursor: move; }
+/* Progressbar
+----------------------------------*/
+.ui-progressbar { height:2em; text-align: left; }
+.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }/* Resizable
+----------------------------------*/
+.ui-resizable { position: relative;}
+.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
+.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
+.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
+.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
+.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
+.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Slider
+----------------------------------*/
+.ui-slider { position: relative; text-align: left; }
+.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
+
+.ui-slider-horizontal { height: .8em; }
+.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.ui-slider-vertical { width: .8em; height: 100px; }
+.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs
+----------------------------------*/
+.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
+.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
+.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
+.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
+.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.ui-tabs .ui-tabs-panel { display: block; border: 0; padding: 1em 1.4em; background: none; }
+.ui-tabs .ui-tabs-hide { display: none !important; }
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+* To view and modify this theme, visit http://jqueryui.com/themeroller/
+*/
+
+
+/* Component containers
+----------------------------------*/
+.ui-widget { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1.1em/*{fsDefault}*/; }
+.ui-widget .ui-widget { font-size: 1em; }
+.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1em; }
+.ui-widget-content { border: 1px solid #aaaaaa/*{borderColorContent}*/; background: #ffffff/*{bgColorContent}*/ url(images/ui-bg_flat_75_ffffff_40x100.png)/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/; color: #222222/*{fcContent}*/; }
+.ui-widget-content a { color: #222222/*{fcContent}*/; }
+.ui-widget-header { border: 1px solid #aaaaaa/*{borderColorHeader}*/; background: #cccccc/*{bgColorHeader}*/ url(images/ui-bg_highlight-soft_75_cccccc_1x100.png)/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/; color: #222222/*{fcHeader}*/; font-weight: bold; }
+.ui-widget-header a { color: #222222/*{fcHeader}*/; }
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default, .ui-widget-content .ui-state-default { border: 1px solid #d3d3d3/*{borderColorDefault}*/; background: #e6e6e6/*{bgColorDefault}*/ url(images/ui-bg_glass_75_e6e6e6_1x400.png)/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #555555/*{fcDefault}*/; }
+.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555/*{fcDefault}*/; text-decoration: none; }
+.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus { border: 1px solid #999999/*{borderColorHover}*/; background: #dadada/*{bgColorHover}*/ url(images/ui-bg_glass_75_dadada_1x400.png)/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcHover}*/; }
+.ui-state-hover a, .ui-state-hover a:hover { color: #212121/*{fcHover}*/; text-decoration: none; }
+.ui-state-active, .ui-widget-content .ui-state-active { border: 1px solid #aaaaaa/*{borderColorActive}*/; background: #ffffff/*{bgColorActive}*/ url(images/ui-bg_glass_65_ffffff_1x400.png)/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcActive}*/; }
+.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121/*{fcActive}*/; text-decoration: none; }
+.ui-widget :active { outline: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight, .ui-widget-content .ui-state-highlight {border: 1px solid #fcefa1/*{borderColorHighlight}*/; background: #fbf9ee/*{bgColorHighlight}*/ url(images/ui-bg_glass_55_fbf9ee_1x400.png)/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/; color: #363636/*{fcHighlight}*/; }
+.ui-state-highlight a, .ui-widget-content .ui-state-highlight a { color: #363636/*{fcHighlight}*/; }
+.ui-state-error, .ui-widget-content .ui-state-error {border: 1px solid #cd0a0a/*{borderColorError}*/; background: #fef1ec/*{bgColorError}*/ url(images/ui-bg_glass_95_fef1ec_1x400.png)/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/; color: #cd0a0a/*{fcError}*/; }
+.ui-state-error a, .ui-widget-content .ui-state-error a { color: #cd0a0a/*{fcError}*/; }
+.ui-state-error-text, .ui-widget-content .ui-state-error-text { color: #cd0a0a/*{fcError}*/; }
+.ui-priority-primary, .ui-widget-content .ui-priority-primary { font-weight: bold; }
+.ui-priority-secondary, .ui-widget-content .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+.ui-state-disabled, .ui-widget-content .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; }
+.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; }
+.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsHeader}*/; }
+.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png)/*{iconsDefault}*/; }
+.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsHover}*/; }
+.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsActive}*/; }
+.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png)/*{iconsHighlight}*/; }
+.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png)/*{iconsError}*/; }
+
+/* positioning */
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-off { background-position: -96px -144px; }
+.ui-icon-radio-on { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-start { background-position: -80px -160px; }
+/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-tl { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-tr { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-bl { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-br { -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-top { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-bottom { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-right { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-left { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-all { -moz-border-radius: 4px/*{cornerRadius}*/; -webkit-border-radius: 4px/*{cornerRadius}*/; border-radius: 4px/*{cornerRadius}*/; }
+
+/* Overlays */
+.ui-widget-overlay { background: #aaaaaa/*{bgColorOverlay}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityOverlay}*/; }
+.ui-widget-shadow { margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/; padding: 8px/*{thicknessShadow}*/; background: #aaaaaa/*{bgColorShadow}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityShadow}*/; -moz-border-radius: 8px/*{cornerRadiusShadow}*/; -webkit-border-radius: 8px/*{cornerRadiusShadow}*/; border-radius: 8px/*{cornerRadiusShadow}*/; }
View
0  vendor/base/jquery-ui.sass
No changes.
View
6,240 vendor/jquery-1.4.2.js
6,240 additions, 0 deletions not shown
View
10,691 vendor/jquery-ui.js
10,691 additions, 0 deletions not shown
View
151 vendor/jquery.rails.js
@@ -0,0 +1,151 @@
+// http://dev.jquery.com/ticket/5945
+// Namespaced events don't work with live in 1.4.1 (they are never fired).
+// I'm ommiting the namespace for now on ajax events
+// This demonstrates what doesn't work in 1.4.1
+// $('body').live('click.foo', function(){ alert('clicked'); });
+// $('body').trigger('click.foo');
+
+;(function($) {
+ $(function() {
+
+ var authToken = $('meta[name=csrf-token]').attr('content');
+ var authParam = $('meta[name=csrf-param]').attr('content');
+ var formTemplate = '<form method="#{method}" action="#{action}">\
+ #{realmethod}<input name="#{param}" value="#{token}" type="hidden">\
+ </form>';
+ var realmethodTemplate = '<input name="_method" value="#{method}" type="hidden">';
+
+ String.prototype.interpolate = function() {
+ for ( i in arguments[0] ) {
+ eval( i + " = '" + arguments[0][i] + "'" );
+ };
+ return this.replace( /#\{.*?\}/g, function(match) {
+ return eval( match.replace( /^#\{|\}$/g , '') );
+ });
+ };
+
+ function handleRemote(element) {
+ var $element = $(element);
+ var method, url, params, dataType;
+
+ dataType = $element.attr('data-type') || 'script';
+
+ if ($element.is('form')) {
+ method = $element.attr('method') || 'post';
+ url = $element.attr('action');
+ params = $element.serialize();
+ } else {
+ method = $element.attr('data-method') || 'get';
+ // TODO: data-url support is going away, just use href
+ url = $element.attr('data-url') || $element.attr('href');
+ params = {};
+ };
+
+ // Trigger any beforeAjax event handlers on self and ancestors
+ // We're looking for a return of false to cancel this action.
+ // Prototype has a much cleaner way of doing this
+ // var event = element.fire("ajax:before");
+ // if (event.stopped) return false;
+ function triggerHandlers($element, event_name) {
+ var proceed = true;
+ if ($element.triggerHandler(event_name) == false) {
+ proceed = false;
+ } else {
+ var x = $.each( $element.parents(), function(index, dom) {
+ if ($(dom).triggerHandler(event_name) == false) {
+ proceed = false;
+ };
+ });
+ };
+ return proceed;
+ };
+
+ if (!triggerHandlers($element, "beforeAjax")) return false;
+
+ $.ajax({
+ url: url,
+ type: method,
+ dataType: dataType,
+ data: params,
+ beforeSend: function(request){ $element.trigger("beforeSendAjax", [request]); },
+ dataFilter: function(data, type){ $element.trigger("dataFilterAjax", [data, type]); },
+ complete: function(request, textStatus){ $element.trigger("completeAjax", [request, textStatus]); },
+ success: function(data, textStatus, request){ $element.trigger("successAjax", [data, textStatus, request]); },
+ error: function(request, textStatus, errorThrown){ $element.trigger("errorAjax", [request, textStatus, errorThrown]); }
+ });
+
+ $element.trigger("afterAjax");
+ };
+
+ $('body').live('click', function(e){
+ var target = e.target;
+
+ var message = $(target).attr('data-confirm');
+ if (message && !confirm(message)) {
+ return false;
+ };
+
+ var remoteElement = $(target).closest('a[data-remote=true]').get(0);
+ if (remoteElement) {
+ handleRemote(remoteElement);
+ return false;
+ };
+
+ var element = $(target).closest('a[data-method]').get(0);
+ if (element && $(element).attr('data-remote') != 'true') {
+ var method = $(element).attr('data-method');
+ var piggyback = method.toLowerCase() != 'post';
+ var formHTML = formTemplate.interpolate({
+ method: 'POST',
+ realmethod: piggyback ? realmethodTemplate.interpolate({ method: method }) : '',
+ action: $(element).attr('href'),
+ token: authToken,
+ param: authParam
+ });
+ var $form = $('<div>').html(formHTML).children().hide().appendTo('body');
+
+ $form.submit();
+ return false;
+ };
+ });
+
+ $('body').live('submit', function(e){
+ var target = e.target;
+
+ var message = $(target).attr('data-confirm');
+ if (message && !confirm(message)) {
+ return false;
+ };
+
+ var inputs = $(target).find('input[type=submit][data-disable-with]');
+ $.each(inputs, function(i, input) {
+ var $input = $(input);
+ $input.attr('disabled', 'disabled');
+ $input.attr('data-original-value', $input.val());
+ $input.val($input.attr('data-disable-with'));
+ });
+
+ var element = $(target).closest('form[data-remote=true]').get(0);
+ if (element) {
+ handleRemote(element);
+ return false;
+ };
+ });
+
+ $('body').live('completeAjax', function(e){
+ var target = e.target;
+
+ if (target.tagName.toLowerCase() == 'form') {
+ var inputs = $(target).find('input[type=submit][disabled=true][data-disable-with]');
+ $.each(inputs, function(i, input) {
+ var $input = $(input);
+ $input.val($input.attr('data-original-value'));
+ $input.attr('data-original-value', null);
+ $input.attr('disabled', false);
+ });
+ };
+
+ });
+
+ });
+})(jQuery);
Please sign in to comment.
Something went wrong with that request. Please try again.