Browse files

[feature] browser events now broadcast to all clients

  • Loading branch information...
1 parent 8205901 commit 42065f88850fe38f600ac0fc097771e2e46584ab @ianjennings committed Jan 9, 2012
Showing with 687 additions and 631 deletions.
  1. +21 −0 ReadMe.md
  2. +1 −0 bin/browserify
  3. +2 −16 bin/webserver
  4. +5 −2 lib/browser/hook-shim.js
  5. +35 −4 lib/webserver.js
  6. +598 −604 public/hook.js
  7. +25 −5 public/index.html
View
21 ReadMe.md
@@ -2,6 +2,27 @@
a basic [hook.io](http://hook.io) enabled webserver with a [socket.io](http://socket.io) based browser bridge
+# EXPERIMENTAL BROADCAST VERSION
+
+```
+git clone git@github.com:ianjennings/webserver.git
+cd webserver
+./bin/webserver
+```
+Then visit http://localhost:8000 in two browser tabs. The pings seen are coming from the server and should appear in both browser tabs at the same time.
+
+## Client Timeouts
+
+This solution keeps a reference to browser clients in the webserver hook. Since the client won't have the opportunity to emit a disconnection notice, we will disconnect the user if they haven't contacted the webserver within two minutes (by default). Clients that get disconnected get the event:
+```
+webserver::timeout
+```
+Remember, timeout is only relative to the last time a client emited an event. If a client first timesout and then emits an additional event, the client will continue to recieve messages and the tmieout will reset.
+
+## Events
+
+All events that are broadcast by the webserver hook are prepended with ```webserver::```. This makes it easy to distinguish from local events and broadcasted events.
+
## Features
- Provides a cross-browser bridge to [hook.io](http://hook.io) through [socket.io](http://socket.io)
View
1 bin/browserify 100644 → 100755
@@ -1,3 +1,4 @@
+#! /usr/bin/env node
//
// A simple CLI tool for helping to browserify /browser/hook-shim.j
//
View
18 bin/webserver
@@ -9,19 +9,5 @@ var webserver = new Webserver({
webserver.start();
webserver.on('ping', function (data, cb) {
- var place = function () {
-
- var item = places.shift();
- places.push(item);
-
- return item;
- }
-
- cb(null, place());
-})
-
-var places = [
- "world",
- "computer",
- "hook.io"
-];
+ cb(null, null);
+})
View
7 lib/browser/hook-shim.js
@@ -31,14 +31,17 @@ Hook.prototype.connect = function () {
//
// Create a dnode / socketio client which exposes,
- // a `message` function
+ // a `message` function as well as assigns a
+ // hopeful unique id for this client
//
+ date = new Date();
var client = dnode({
message: function(event, data){
self.emit(event, data, function(){}, false);
},
report: function(message) {
- }
+ },
+ ident: String(date.getTime()) + String(Math.floor(Math.random() * 99999))
});
//
View
39 lib/webserver.js
@@ -16,8 +16,9 @@ var Webserver = exports.Webserver = function(options, callback){
Hook.call(self, options);
- self.host = self.host || '127.0.0.1';
- self.webroot = self.webroot || '.'
+ self.host = self.host || '127.0.0.1';
+ self.webroot = self.webroot || '.'
+ self.clientTimeout = self.clientTimeout || 60000;
self.on('hook::started', function(){
self.findPort({ port: self.port }, function(err, port){
@@ -26,9 +27,39 @@ var Webserver = exports.Webserver = function(options, callback){
})
});
- self.on("**", function(data){
+ //
+ // A list of currently connected clients
+ //
+ var clients = {};
+
+ //
+ // Broadcast every message to all clients and
+ // Check how long it's been since we've heard from each client
+ // If it's been more than clientTimeout, remove the client and send it a timeout message
+ //
+ self.on("**", function(data, callback){
+
if(self.client && self.client.message){
- self.client.message(this.event, data, callback);
+
+ if(this.event == "hook::ready"){
+ clients[self.client.ident] = self.client;
+ }
+
+ for(c in clients){
@Marak
Marak added a note Jan 26, 2012

This doesn't seem right. Most of this logic should already be defined in socket.io

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ // if we find the same client that sent the message update their lastHeard time and callback
+ if(self.client.ident == clients[c].ident){
+ clients[c].lastHeard = new Date().getTime();
+ self.client.message("webserver::" + this.event, data, callback);
+ } else {
+ if((new Date().getTime() - clients[c].lastHeard) > self.clientTimeout){
+ clients[c].message("webserver::timeout", {lastHeard: clients[c].lastHeard, timeout: self.clientTimeout});
+ delete clients[c];
+ } else {
+ clients[c].message("webserver::" + this.event, data);
+ }
+ }
+ }
+
}
});
View
1,202 public/hook.js
@@ -2,8 +2,8 @@
hook.io - http://hook.io
(C) 2011 Nodejitsu Inc.
MIT LICENCE
- THIS FILE WAS AUTOGENERATED ON Wed Nov 23 2011 15:58:41 GMT-0800 (PST)
- BUILD ID: 1322092721205
+ THIS FILE WAS AUTOGENERATED ON Tue Nov 08 2011 12:57:20 GMT-0800 (PST)
+ BUILD ID: 1320785840196
WARNING: THIS FILE WAS AUTOGENERATED BY THE HOOKIO BROWSERIFY SCRIPT
MODIFYING THIS FILE IS FINE, BUT YOU REALLY SHOULD BE MODIFYING
@@ -1386,14 +1386,17 @@ Hook.prototype.connect = function () {
//
// Create a dnode / socketio client which exposes,
- // a `message` function
+ // a `message` function as well as assigns a
+ // (hopeful) unique id for this client
//
+ date = new Date();
var client = dnode({
message: function(event, data){
self.emit(event, data, function(){}, false);
},
report: function(message) {
- }
+ },
+ ident: String(date.getTime()) + String(Math.floor(Math.random() * 99999))
});
//
@@ -1626,7 +1629,6 @@ require.define("/node_modules/dnode/node_modules/dnode-protocol/index.js", funct
var Traverse = require('traverse');
var EventEmitter = require('events').EventEmitter;
var stream = process.title === 'browser' ? {} : require('stream');
-var json = typeof JSON === 'object' ? JSON : require('jsonify');
var exports = module.exports = function (wrapper) {
var self = {};
@@ -1695,10 +1697,10 @@ var Session = exports.Session = function (id, wrapper) {
self.parse = function (line) {
var msg = null;
- try { msg = json.parse(line) }
+ try { msg = JSON.parse(line) }
catch (err) {
self.emit('error', new SyntaxError(
- 'Error parsing JSON message: ' + json.stringify(line))
+ 'Error parsing JSON message: ' + JSON.stringify(line))
);
return;
}
@@ -2419,457 +2421,115 @@ require.define("stream", function (require, module, exports, __dirname, __filena
});
-require.define("/node_modules/dnode/node_modules/jsonify/package.json", function (require, module, exports, __dirname, __filename) {
- module.exports = {"main":"index.js"}
+require.define("/node_modules/dnode/node_modules/socket.io-client/package.json", function (require, module, exports, __dirname, __filename) {
+ module.exports = {"main":"./lib/io.js","browserify":"./dist/browserify.js"}
});
-require.define("/node_modules/dnode/node_modules/jsonify/index.js", function (require, module, exports, __dirname, __filename) {
- exports.parse = require('./lib/parse');
-exports.stringify = require('./lib/stringify');
+require.define("/node_modules/dnode/node_modules/socket.io-client/dist/browserify.js", function (require, module, exports, __dirname, __filename) {
+ (function () {var io = module.exports;/*! Socket.IO.js build:0.8.6, development. Copyright(c) 2011 LearnBoost <dev@learnboost.com> MIT Licensed */
-});
+/**
+ * socket.io
+ * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
+ * MIT Licensed
+ */
-require.define("/node_modules/dnode/node_modules/jsonify/lib/parse.js", function (require, module, exports, __dirname, __filename) {
- var at, // The index of the current character
- ch, // The current character
- escapee = {
- '"': '"',
- '\\': '\\',
- '/': '/',
- b: '\b',
- f: '\f',
- n: '\n',
- r: '\r',
- t: '\t'
- },
- text,
+(function (exports, global) {
- error = function (m) {
- // Call error when something is wrong.
- throw {
- name: 'SyntaxError',
- message: m,
- at: at,
- text: text
- };
- },
-
- next = function (c) {
- // If a c parameter is provided, verify that it matches the current character.
- if (c && c !== ch) {
- error("Expected '" + c + "' instead of '" + ch + "'");
- }
-
- // Get the next character. When there are no more characters,
- // return the empty string.
-
- ch = text.charAt(at);
- at += 1;
- return ch;
- },
-
- number = function () {
- // Parse a number value.
- var number,
- string = '';
-
- if (ch === '-') {
- string = '-';
- next('-');
- }
- while (ch >= '0' && ch <= '9') {
- string += ch;
- next();
- }
- if (ch === '.') {
- string += '.';
- while (next() && ch >= '0' && ch <= '9') {
- string += ch;
- }
- }
- if (ch === 'e' || ch === 'E') {
- string += ch;
- next();
- if (ch === '-' || ch === '+') {
- string += ch;
- next();
- }
- while (ch >= '0' && ch <= '9') {
- string += ch;
- next();
- }
- }
- number = +string;
- if (!isFinite(number)) {
- error("Bad number");
- } else {
- return number;
- }
- },
-
- string = function () {
- // Parse a string value.
- var hex,
- i,
- string = '',
- uffff;
-
- // When parsing for string values, we must look for " and \ characters.
- if (ch === '"') {
- while (next()) {
- if (ch === '"') {
- next();
- return string;
- } else if (ch === '\\') {
- next();
- if (ch === 'u') {
- uffff = 0;
- for (i = 0; i < 4; i += 1) {
- hex = parseInt(next(), 16);
- if (!isFinite(hex)) {
- break;
- }
- uffff = uffff * 16 + hex;
- }
- string += String.fromCharCode(uffff);
- } else if (typeof escapee[ch] === 'string') {
- string += escapee[ch];
- } else {
- break;
- }
- } else {
- string += ch;
- }
- }
- }
- error("Bad string");
- },
+ /**
+ * IO namespace.
+ *
+ * @namespace
+ */
- white = function () {
+ var io = exports;
-// Skip whitespace.
+ /**
+ * Socket.IO version
+ *
+ * @api public
+ */
- while (ch && ch <= ' ') {
- next();
- }
- },
+ io.version = '0.8.6';
- word = function () {
+ /**
+ * Protocol implemented.
+ *
+ * @api public
+ */
-// true, false, or null.
+ io.protocol = 1;
- switch (ch) {
- case 't':
- next('t');
- next('r');
- next('u');
- next('e');
- return true;
- case 'f':
- next('f');
- next('a');
- next('l');
- next('s');
- next('e');
- return false;
- case 'n':
- next('n');
- next('u');
- next('l');
- next('l');
- return null;
- }
- error("Unexpected '" + ch + "'");
- },
+ /**
+ * Available transports, these will be populated with the available transports
+ *
+ * @api public
+ */
- value, // Place holder for the value function.
+ io.transports = [];
- array = function () {
+ /**
+ * Keep track of jsonp callbacks.
+ *
+ * @api private
+ */
-// Parse an array value.
+ io.j = [];
- var array = [];
+ /**
+ * Keep track of our io.Sockets
+ *
+ * @api private
+ */
+ io.sockets = {};
- if (ch === '[') {
- next('[');
- white();
- if (ch === ']') {
- next(']');
- return array; // empty array
- }
- while (ch) {
- array.push(value());
- white();
- if (ch === ']') {
- next(']');
- return array;
- }
- next(',');
- white();
- }
- }
- error("Bad array");
- },
- object = function () {
+ /**
+ * Manages connections to hosts.
+ *
+ * @param {String} uri
+ * @Param {Boolean} force creation of new socket (defaults to false)
+ * @api public
+ */
-// Parse an object value.
+ io.connect = function (host, details) {
+ var uri = io.util.parseUri(host)
+ , uuri
+ , socket;
- var key,
- object = {};
+ if (global && global.location) {
+ uri.protocol = uri.protocol || global.location.protocol.slice(0, -1);
+ uri.host = uri.host || (global.document
+ ? global.document.domain : global.location.hostname);
+ uri.port = uri.port || global.location.port;
+ }
- if (ch === '{') {
- next('{');
- white();
- if (ch === '}') {
- next('}');
- return object; // empty object
- }
- while (ch) {
- key = string();
- white();
- next(':');
- if (Object.hasOwnProperty.call(object, key)) {
- error('Duplicate key "' + key + '"');
- }
- object[key] = value();
- white();
- if (ch === '}') {
- next('}');
- return object;
- }
- next(',');
- white();
- }
- }
- error("Bad object");
- };
-
-value = function () {
-
-// Parse a JSON value. It could be an object, an array, a string, a number,
-// or a word.
-
- white();
- switch (ch) {
- case '{':
- return object();
- case '[':
- return array();
- case '"':
- return string();
- case '-':
- return number();
- default:
- return ch >= '0' && ch <= '9' ? number() : word();
- }
-};
-
-// Return the json_parse function. It will have access to all of the above
-// functions and variables.
-
-module.exports = function (source, reviver) {
- var result;
-
- text = source;
- at = 0;
- ch = ' ';
- result = value();
- white();
- if (ch) {
- error("Syntax error");
- }
-
- // If there is a reviver function, we recursively walk the new structure,
- // passing each name/value pair to the reviver function for possible
- // transformation, starting with a temporary root object that holds the result
- // in an empty key. If there is not a reviver function, we simply return the
- // result.
-
- return typeof reviver === 'function' ? (function walk(holder, key) {
- var k, v, value = holder[key];
- if (value && typeof value === 'object') {
- for (k in value) {
- if (Object.prototype.hasOwnProperty.call(value, k)) {
- v = walk(value, k);
- if (v !== undefined) {
- value[k] = v;
- } else {
- delete value[k];
- }
- }
- }
- }
- return reviver.call(holder, key, value);
- }({'': result}, '')) : result;
-};
-
-});
-
-require.define("/node_modules/dnode/node_modules/jsonify/lib/stringify.js", function (require, module, exports, __dirname, __filename) {
- var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
- escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
- gap,
- indent,
- meta = { // table of character substitutions
- '\b': '\\b',
- '\t': '\\t',
- '\n': '\\n',
- '\f': '\\f',
- '\r': '\\r',
- '"' : '\\"',
- '\\': '\\\\'
- },
- rep;
-
-function quote(string) {
- // If the string contains no control characters, no quote characters, and no
- // backslash characters, then we can safely slap some quotes around it.
- // Otherwise we must also replace the offending characters with safe escape
- // sequences.
-
- escapable.lastIndex = 0;
- return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
- var c = meta[a];
- return typeof c === 'string' ? c :
- '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
- }) + '"' : '"' + string + '"';
-}
+ uuri = io.util.uniqueUri(uri);
-function str(key, holder) {
- // Produce a string from holder[key].
- var i, // The loop counter.
- k, // The member key.
- v, // The member value.
- length,
- mind = gap,
- partial,
- value = holder[key];
-
- // If the value has a toJSON method, call it to obtain a replacement value.
- if (value && typeof value === 'object' &&
- typeof value.toJSON === 'function') {
- value = value.toJSON(key);
- }
-
- // If we were called with a replacer function, then call the replacer to
- // obtain a replacement value.
- if (typeof rep === 'function') {
- value = rep.call(holder, key, value);
- }
-
- // What happens next depends on the value's type.
- switch (typeof value) {
- case 'string':
- return quote(value);
-
- case 'number':
- // JSON numbers must be finite. Encode non-finite numbers as null.
- return isFinite(value) ? String(value) : 'null';
-
- case 'boolean':
- case 'null':
- // If the value is a boolean or null, convert it to a string. Note:
- // typeof null does not produce 'null'. The case is included here in
- // the remote chance that this gets fixed someday.
- return String(value);
-
- case 'object':
- if (!value) return 'null';
- gap += indent;
- partial = [];
-
- // Array.isArray
- if (Object.prototype.toString.apply(value) === '[object Array]') {
- length = value.length;
- for (i = 0; i < length; i += 1) {
- partial[i] = str(i, value) || 'null';
- }
-
- // Join all of the elements together, separated with commas, and
- // wrap them in brackets.
- v = partial.length === 0 ? '[]' : gap ?
- '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
- '[' + partial.join(',') + ']';
- gap = mind;
- return v;
- }
-
- // If the replacer is an array, use it to select the members to be
- // stringified.
- if (rep && typeof rep === 'object') {
- length = rep.length;
- for (i = 0; i < length; i += 1) {
- k = rep[i];
- if (typeof k === 'string') {
- v = str(k, value);
- if (v) {
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
- }
- }
- }
- }
- else {
- // Otherwise, iterate through all of the keys in the object.
- for (k in value) {
- if (Object.prototype.hasOwnProperty.call(value, k)) {
- v = str(k, value);
- if (v) {
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
- }
- }
- }
- }
-
- // Join all of the member texts together, separated with commas,
- // and wrap them in braces.
+ var options = {
+ host: uri.host
+ , secure: 'https' == uri.protocol
+ , port: uri.port || ('https' == uri.protocol ? 443 : 80)
+ , query: uri.query || ''
+ };
- v = partial.length === 0 ? '{}' : gap ?
- '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
- '{' + partial.join(',') + '}';
- gap = mind;
- return v;
- }
-}
+ io.util.merge(options, details);
-module.exports = function (value, replacer, space) {
- var i;
- gap = '';
- indent = '';
-
- // If the space parameter is a number, make an indent string containing that
- // many spaces.
- if (typeof space === 'number') {
- for (i = 0; i < space; i += 1) {
- indent += ' ';
- }
- }
- // If the space parameter is a string, it will be used as the indent string.
- else if (typeof space === 'string') {
- indent = space;
+ if (options['force new connection'] || !io.sockets[uuri]) {
+ socket = new io.Socket(options);
}
- // If there is a replacer, it must be a function or an array.
- // Otherwise, throw an error.
- rep = replacer;
- if (replacer && typeof replacer !== 'function'
- && (typeof replacer !== 'object' || typeof replacer.length !== 'number')) {
- throw new Error('JSON.stringify');
+ if (!options['force new connection'] && socket) {
+ io.sockets[uuri] = socket;
}
-
- // Make a fake root object containing our value under the key of ''.
- // Return the result of stringifying the value.
- return str('', {'': value});
-};
-});
+ socket = socket || io.sockets[uuri];
-require.define("/node_modules/dnode/node_modules/socket.io-client/package.json", function (require, module, exports, __dirname, __filename) {
- module.exports = {"main":"./lib/io.js","browserify":"./dist/browserify.js"}
-});
+ // if path is different from '' or /
+ return socket.of(uri.path.length > 1 ? uri.path : '');
+ };
-require.define("/node_modules/dnode/node_modules/socket.io-client/dist/browserify.js", function (require, module, exports, __dirname, __filename) {
- (function () {var io = module.exports;/*! Socket.IO.js build:0.8.6, development. Copyright(c) 2011 LearnBoost <dev@learnboost.com> MIT Licensed */
+})('object' === typeof module ? module.exports : (this.io = {}), this);
/**
* socket.io
@@ -2880,120 +2540,17 @@ require.define("/node_modules/dnode/node_modules/socket.io-client/dist/browserif
(function (exports, global) {
/**
- * IO namespace.
+ * Utilities namespace.
*
* @namespace
*/
- var io = exports;
+ var util = exports.util = {};
/**
- * Socket.IO version
+ * Parses an URI
*
- * @api public
- */
-
- io.version = '0.8.6';
-
- /**
- * Protocol implemented.
- *
- * @api public
- */
-
- io.protocol = 1;
-
- /**
- * Available transports, these will be populated with the available transports
- *
- * @api public
- */
-
- io.transports = [];
-
- /**
- * Keep track of jsonp callbacks.
- *
- * @api private
- */
-
- io.j = [];
-
- /**
- * Keep track of our io.Sockets
- *
- * @api private
- */
- io.sockets = {};
-
-
- /**
- * Manages connections to hosts.
- *
- * @param {String} uri
- * @Param {Boolean} force creation of new socket (defaults to false)
- * @api public
- */
-
- io.connect = function (host, details) {
- var uri = io.util.parseUri(host)
- , uuri
- , socket;
-
- if (global && global.location) {
- uri.protocol = uri.protocol || global.location.protocol.slice(0, -1);
- uri.host = uri.host || (global.document
- ? global.document.domain : global.location.hostname);
- uri.port = uri.port || global.location.port;
- }
-
- uuri = io.util.uniqueUri(uri);
-
- var options = {
- host: uri.host
- , secure: 'https' == uri.protocol
- , port: uri.port || ('https' == uri.protocol ? 443 : 80)
- , query: uri.query || ''
- };
-
- io.util.merge(options, details);
-
- if (options['force new connection'] || !io.sockets[uuri]) {
- socket = new io.Socket(options);
- }
-
- if (!options['force new connection'] && socket) {
- io.sockets[uuri] = socket;
- }
-
- socket = socket || io.sockets[uuri];
-
- // if path is different from '' or /
- return socket.of(uri.path.length > 1 ? uri.path : '');
- };
-
-})('object' === typeof module ? module.exports : (this.io = {}), this);
-
-/**
- * socket.io
- * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
- * MIT Licensed
- */
-
-(function (exports, global) {
-
- /**
- * Utilities namespace.
- *
- * @namespace
- */
-
- var util = exports.util = {};
-
- /**
- * Parses an URI
- *
- * @author Steven Levithan <stevenlevithan.com> (MIT license)
+ * @author Steven Levithan <stevenlevithan.com> (MIT license)
* @api public
*/
@@ -5469,8 +5026,8 @@ require.define("/node_modules/dnode/node_modules/socket.io-client/dist/browserif
'undefined' != typeof io ? io.Transport : module.exports
, 'undefined' != typeof io ? io : module.parent.exports
);
-/* SWFObject v2.2 <http://code.google.com/p/swfobject/>
- is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
+/* SWFObject v2.2 <http://code.google.com/p/swfobject/>
+ is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
if ('undefined' != typeof window) {
var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
@@ -6550,76 +6107,521 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho
document.body.removeChild(iframe);
}, 100);
}
- };
+ };
+
+ /**
+ * Callback function for the incoming message stream from the Socket.IO server.
+ *
+ * @param {String} data The message
+ * @api private
+ */
+
+ JSONPPolling.prototype._ = function (msg) {
+ this.onData(msg);
+ if (this.open) {
+ this.get();
+ }
+ return this;
+ };
+
+ /**
+ * The indicator hack only works after onload
+ *
+ * @param {Socket} socket The socket instance that needs a transport
+ * @param {Function} fn The callback
+ * @api private
+ */
+
+ JSONPPolling.prototype.ready = function (socket, fn) {
+ var self = this;
+ if (!indicator) return fn.call(this);
+
+ io.util.load(function () {
+ fn.call(self);
+ });
+ };
+
+ /**
+ * Checks if browser supports this transport.
+ *
+ * @return {Boolean}
+ * @api public
+ */
+
+ JSONPPolling.check = function () {
+ return 'document' in global;
+ };
+
+ /**
+ * Check if cross domain requests are supported
+ *
+ * @returns {Boolean}
+ * @api public
+ */
+
+ JSONPPolling.xdomainCheck = function () {
+ return true;
+ };
+
+ /**
+ * Add the transport to your public io.transports array.
+ *
+ * @api private
+ */
+
+ io.transports.push('jsonp-polling');
+
+})(
+ 'undefined' != typeof io ? io.Transport : module.exports
+ , 'undefined' != typeof io ? io : module.parent.exports
+ , this
+);
+}).call(window)
+});
+
+require.define("/node_modules/dnode/node_modules/jsonify/package.json", function (require, module, exports, __dirname, __filename) {
+ module.exports = {"main":"index.js"}
+});
+
+require.define("/node_modules/dnode/node_modules/jsonify/index.js", function (require, module, exports, __dirname, __filename) {
+ exports.parse = require('./lib/parse');
+exports.stringify = require('./lib/stringify');
+
+});
+
+require.define("/node_modules/dnode/node_modules/jsonify/lib/parse.js", function (require, module, exports, __dirname, __filename) {
+ var at, // The index of the current character
+ ch, // The current character
+ escapee = {
+ '"': '"',
+ '\\': '\\',
+ '/': '/',
+ b: '\b',
+ f: '\f',
+ n: '\n',
+ r: '\r',
+ t: '\t'
+ },
+ text,
+
+ error = function (m) {
+ // Call error when something is wrong.
+ throw {
+ name: 'SyntaxError',
+ message: m,
+ at: at,
+ text: text
+ };
+ },
+
+ next = function (c) {
+ // If a c parameter is provided, verify that it matches the current character.
+ if (c && c !== ch) {
+ error("Expected '" + c + "' instead of '" + ch + "'");
+ }
+
+ // Get the next character. When there are no more characters,
+ // return the empty string.
+
+ ch = text.charAt(at);
+ at += 1;
+ return ch;
+ },
+
+ number = function () {
+ // Parse a number value.
+ var number,
+ string = '';
+
+ if (ch === '-') {
+ string = '-';
+ next('-');
+ }
+ while (ch >= '0' && ch <= '9') {
+ string += ch;
+ next();
+ }
+ if (ch === '.') {
+ string += '.';
+ while (next() && ch >= '0' && ch <= '9') {
+ string += ch;
+ }
+ }
+ if (ch === 'e' || ch === 'E') {
+ string += ch;
+ next();
+ if (ch === '-' || ch === '+') {
+ string += ch;
+ next();
+ }
+ while (ch >= '0' && ch <= '9') {
+ string += ch;
+ next();
+ }
+ }
+ number = +string;
+ if (!isFinite(number)) {
+ error("Bad number");
+ } else {
+ return number;
+ }
+ },
+
+ string = function () {
+ // Parse a string value.
+ var hex,
+ i,
+ string = '',
+ uffff;
+
+ // When parsing for string values, we must look for " and \ characters.
+ if (ch === '"') {
+ while (next()) {
+ if (ch === '"') {
+ next();
+ return string;
+ } else if (ch === '\\') {
+ next();
+ if (ch === 'u') {
+ uffff = 0;
+ for (i = 0; i < 4; i += 1) {
+ hex = parseInt(next(), 16);
+ if (!isFinite(hex)) {
+ break;
+ }
+ uffff = uffff * 16 + hex;
+ }
+ string += String.fromCharCode(uffff);
+ } else if (typeof escapee[ch] === 'string') {
+ string += escapee[ch];
+ } else {
+ break;
+ }
+ } else {
+ string += ch;
+ }
+ }
+ }
+ error("Bad string");
+ },
+
+ white = function () {
+
+// Skip whitespace.
+
+ while (ch && ch <= ' ') {
+ next();
+ }
+ },
+
+ word = function () {
+
+// true, false, or null.
+
+ switch (ch) {
+ case 't':
+ next('t');
+ next('r');
+ next('u');
+ next('e');
+ return true;
+ case 'f':
+ next('f');
+ next('a');
+ next('l');
+ next('s');
+ next('e');
+ return false;
+ case 'n':
+ next('n');
+ next('u');
+ next('l');
+ next('l');
+ return null;
+ }
+ error("Unexpected '" + ch + "'");
+ },
+
+ value, // Place holder for the value function.
+
+ array = function () {
+
+// Parse an array value.
+
+ var array = [];
+
+ if (ch === '[') {
+ next('[');
+ white();
+ if (ch === ']') {
+ next(']');
+ return array; // empty array
+ }
+ while (ch) {
+ array.push(value());
+ white();
+ if (ch === ']') {
+ next(']');
+ return array;
+ }
+ next(',');
+ white();
+ }
+ }
+ error("Bad array");
+ },
+
+ object = function () {
+
+// Parse an object value.
+
+ var key,
+ object = {};
+
+ if (ch === '{') {
+ next('{');
+ white();
+ if (ch === '}') {
+ next('}');
+ return object; // empty object
+ }
+ while (ch) {
+ key = string();
+ white();
+ next(':');
+ if (Object.hasOwnProperty.call(object, key)) {
+ error('Duplicate key "' + key + '"');
+ }
+ object[key] = value();
+ white();
+ if (ch === '}') {
+ next('}');
+ return object;
+ }
+ next(',');
+ white();
+ }
+ }
+ error("Bad object");
+ };
+
+value = function () {
+
+// Parse a JSON value. It could be an object, an array, a string, a number,
+// or a word.
+
+ white();
+ switch (ch) {
+ case '{':
+ return object();
+ case '[':
+ return array();
+ case '"':
+ return string();
+ case '-':
+ return number();
+ default:
+ return ch >= '0' && ch <= '9' ? number() : word();
+ }
+};
- /**
- * Callback function for the incoming message stream from the Socket.IO server.
- *
- * @param {String} data The message
- * @api private
- */
+// Return the json_parse function. It will have access to all of the above
+// functions and variables.
- JSONPPolling.prototype._ = function (msg) {
- this.onData(msg);
- if (this.open) {
- this.get();
+module.exports = function (source, reviver) {
+ var result;
+
+ text = source;
+ at = 0;
+ ch = ' ';
+ result = value();
+ white();
+ if (ch) {
+ error("Syntax error");
}
- return this;
- };
- /**
- * The indicator hack only works after onload
- *
- * @param {Socket} socket The socket instance that needs a transport
- * @param {Function} fn The callback
- * @api private
- */
+ // If there is a reviver function, we recursively walk the new structure,
+ // passing each name/value pair to the reviver function for possible
+ // transformation, starting with a temporary root object that holds the result
+ // in an empty key. If there is not a reviver function, we simply return the
+ // result.
- JSONPPolling.prototype.ready = function (socket, fn) {
- var self = this;
- if (!indicator) return fn.call(this);
+ return typeof reviver === 'function' ? (function walk(holder, key) {
+ var k, v, value = holder[key];
+ if (value && typeof value === 'object') {
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = walk(value, k);
+ if (v !== undefined) {
+ value[k] = v;
+ } else {
+ delete value[k];
+ }
+ }
+ }
+ }
+ return reviver.call(holder, key, value);
+ }({'': result}, '')) : result;
+};
- io.util.load(function () {
- fn.call(self);
- });
- };
+});
- /**
- * Checks if browser supports this transport.
- *
- * @return {Boolean}
- * @api public
- */
+require.define("/node_modules/dnode/node_modules/jsonify/lib/stringify.js", function (require, module, exports, __dirname, __filename) {
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ gap,
+ indent,
+ meta = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ },
+ rep;
- JSONPPolling.check = function () {
- return 'document' in global;
- };
+function quote(string) {
+ // If the string contains no control characters, no quote characters, and no
+ // backslash characters, then we can safely slap some quotes around it.
+ // Otherwise we must also replace the offending characters with safe escape
+ // sequences.
+
+ escapable.lastIndex = 0;
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
+ var c = meta[a];
+ return typeof c === 'string' ? c :
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ }) + '"' : '"' + string + '"';
+}
- /**
- * Check if cross domain requests are supported
- *
- * @returns {Boolean}
- * @api public
- */
+function str(key, holder) {
+ // Produce a string from holder[key].
+ var i, // The loop counter.
+ k, // The member key.
+ v, // The member value.
+ length,
+ mind = gap,
+ partial,
+ value = holder[key];
+
+ // If the value has a toJSON method, call it to obtain a replacement value.
+ if (value && typeof value === 'object' &&
+ typeof value.toJSON === 'function') {
+ value = value.toJSON(key);
+ }
+
+ // If we were called with a replacer function, then call the replacer to
+ // obtain a replacement value.
+ if (typeof rep === 'function') {
+ value = rep.call(holder, key, value);
+ }
+
+ // What happens next depends on the value's type.
+ switch (typeof value) {
+ case 'string':
+ return quote(value);
+
+ case 'number':
+ // JSON numbers must be finite. Encode non-finite numbers as null.
+ return isFinite(value) ? String(value) : 'null';
+
+ case 'boolean':
+ case 'null':
+ // If the value is a boolean or null, convert it to a string. Note:
+ // typeof null does not produce 'null'. The case is included here in
+ // the remote chance that this gets fixed someday.
+ return String(value);
+
+ case 'object':
+ if (!value) return 'null';
+ gap += indent;
+ partial = [];
+
+ // Array.isArray
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
+ length = value.length;
+ for (i = 0; i < length; i += 1) {
+ partial[i] = str(i, value) || 'null';
+ }
+
+ // Join all of the elements together, separated with commas, and
+ // wrap them in brackets.
+ v = partial.length === 0 ? '[]' : gap ?
+ '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
+ '[' + partial.join(',') + ']';
+ gap = mind;
+ return v;
+ }
+
+ // If the replacer is an array, use it to select the members to be
+ // stringified.
+ if (rep && typeof rep === 'object') {
+ length = rep.length;
+ for (i = 0; i < length; i += 1) {
+ k = rep[i];
+ if (typeof k === 'string') {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ }
+ else {
+ // Otherwise, iterate through all of the keys in the object.
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ }
+
+ // Join all of the member texts together, separated with commas,
+ // and wrap them in braces.
- JSONPPolling.xdomainCheck = function () {
- return true;
- };
+ v = partial.length === 0 ? '{}' : gap ?
+ '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
+ '{' + partial.join(',') + '}';
+ gap = mind;
+ return v;
+ }
+}
- /**
- * Add the transport to your public io.transports array.
- *
- * @api private
- */
+module.exports = function (value, replacer, space) {
+ var i;
+ gap = '';
+ indent = '';
+
+ // If the space parameter is a number, make an indent string containing that
+ // many spaces.
+ if (typeof space === 'number') {
+ for (i = 0; i < space; i += 1) {
+ indent += ' ';
+ }
+ }
+ // If the space parameter is a string, it will be used as the indent string.
+ else if (typeof space === 'string') {
+ indent = space;
+ }
- io.transports.push('jsonp-polling');
+ // If there is a replacer, it must be a function or an array.
+ // Otherwise, throw an error.
+ rep = replacer;
+ if (replacer && typeof replacer !== 'function'
+ && (typeof replacer !== 'object' || typeof replacer.length !== 'number')) {
+ throw new Error('JSON.stringify');
+ }
+
+ // Make a fake root object containing our value under the key of ''.
+ // Return the result of stringifying the value.
+ return str('', {'': value});
+};
-})(
- 'undefined' != typeof io ? io.Transport : module.exports
- , 'undefined' != typeof io ? io : module.parent.exports
- , this
-);
-}).call(window)
});
require.define("/node_modules/eventemitter2/package.json", function (require, module, exports, __dirname, __filename) {
@@ -7082,13 +7084,5 @@ require.define("/node_modules/eventemitter2/lib/eventemitter2.js", function (req
}(typeof exports === 'undefined' ? window : exports);
});
-//
-// Automagically constructs a new Hook for the browser
-//
-//
-// Remark: More advanced users might want to comment this file out and manually create Hooks
-//
-var Hook = require('/hook.js').Hook;
-var hook = new Hook();
-hook.connect();
+var Hook = require('/hook.js').Hook;
View
30 public/index.html
@@ -4,26 +4,46 @@
<script type="text/javascript" src="./hook.js"></script>
<script type="text/javascript">
-
+
+ var Hook = require('/hook.js').Hook;
+ var hook = new Hook();
+ hook.connect();
// This seems to get hit twice.
hook.on('hook::ready', function() {
+ hook.onAny(function(){
+ document.getElementById("place").innerHTML += this.event + '<br />';
+ });
+
setInterval(function ping() {
+ document.getElementById("ping").innerHTML += 'ping<br />';
hook.emit('ping', function (err, place) {
- document.getElementById("place").innerHTML = 'hello ' + place + '!';
+ document.getElementById("ping").innerHTML += 'pong<br />';
});
- }, 2000);
+ }, 1000);
});
+
</script>
</head>
<body>
<h1>Welcome to hook.io-webserver, it worked!</h1>
<h2>You've now got a running websocket server waiting for connections!</h2>
- hook.io-webserver is meant to be low-level and minimal. You probably want to check out <a href="http://github.com/hookio/hook.js/">hook.js</a>
- <h1><span id="place">loading...</span></h1>
+ <p>
+ hook.io-webserver is meant to be low-level and minimal. You probably want to check out <a href="http://github.com/hookio/hook.js/">hook.js</a>.
+ </p>
+ <p>
+ This page pings the sever with a callback (pong). The ping request is remitted to <strong>every</strong> client as webserver::ping. Callbacks will only be made by the client that emitted, while events will be emitted to every browser client.
+ </p>
+ <p>
+ Try opening another browser window. You will still see 1 "ping" and one "pong" on every browser client, but multiple "webserver::pings" because they are being broadcast to all browser clients.
+ </p>
+ <hr />
+ <div id="place" style="float: left; margin-right: 30px;"></div>
+ <div id="ping" style="float: left">
+ </div>
</body>
</html>

0 comments on commit 42065f8

Please sign in to comment.