working websocket
This commit is contained in:
parent
63866a8128
commit
8019fc8d31
6 changed files with 957 additions and 0 deletions
27
app/views/dashboard/socket.erb
Normal file
27
app/views/dashboard/socket.erb
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js'></script>
|
||||
<script src='javascripts/swfobject.js'></script>
|
||||
<script src='javascripts/FABridge.js'></script>
|
||||
<script src='javascripts/web_socket.js'></script>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
function debug(str){ $("#debug").append("<p>" + str); };
|
||||
|
||||
ws = new WebSocket("ws://localhost:8080/");
|
||||
ws.onmessage = function(evt) { $(".msg").prepend("<p>"+evt.data+"</p>"); };
|
||||
ws.onclose = function() { debug("socket closed"); };
|
||||
ws.onopen = function() {
|
||||
debug("connected...");
|
||||
ws.send("hello server");
|
||||
ws.send("hello again");
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
boner
|
||||
<div id="debug"></div>
|
||||
<div class="msg"></div>
|
||||
</body>
|
||||
</html>
|
||||
10
config/initializers/socket.rb
Normal file
10
config/initializers/socket.rb
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
require 'em-websocket'
|
||||
require 'eventmachine'
|
||||
|
||||
EM.next_tick {
|
||||
EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080, :debug => true) do |ws|
|
||||
ws.onopen { ws.send "Hello Client!"}
|
||||
ws.onmessage { |msg| ws.send "Pong: #{msg}" }
|
||||
ws.onclose { puts "WebSocket closed" }
|
||||
end
|
||||
}
|
||||
604
public/javascripts/FABridge.js
Normal file
604
public/javascripts/FABridge.js
Normal file
|
|
@ -0,0 +1,604 @@
|
|||
/*
|
||||
/*
|
||||
Copyright 2006 Adobe Systems Incorporated
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* The Bridge class, responsible for navigating AS instances
|
||||
*/
|
||||
function FABridge(target,bridgeName)
|
||||
{
|
||||
this.target = target;
|
||||
this.remoteTypeCache = {};
|
||||
this.remoteInstanceCache = {};
|
||||
this.remoteFunctionCache = {};
|
||||
this.localFunctionCache = {};
|
||||
this.bridgeID = FABridge.nextBridgeID++;
|
||||
this.name = bridgeName;
|
||||
this.nextLocalFuncID = 0;
|
||||
FABridge.instances[this.name] = this;
|
||||
FABridge.idMap[this.bridgeID] = this;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// type codes for packed values
|
||||
FABridge.TYPE_ASINSTANCE = 1;
|
||||
FABridge.TYPE_ASFUNCTION = 2;
|
||||
|
||||
FABridge.TYPE_JSFUNCTION = 3;
|
||||
FABridge.TYPE_ANONYMOUS = 4;
|
||||
|
||||
FABridge.initCallbacks = {};
|
||||
FABridge.userTypes = {};
|
||||
|
||||
FABridge.addToUserTypes = function()
|
||||
{
|
||||
for (var i = 0; i < arguments.length; i++)
|
||||
{
|
||||
FABridge.userTypes[arguments[i]] = {
|
||||
'typeName': arguments[i],
|
||||
'enriched': false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
FABridge.argsToArray = function(args)
|
||||
{
|
||||
var result = [];
|
||||
for (var i = 0; i < args.length; i++)
|
||||
{
|
||||
result[i] = args[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function instanceFactory(objID)
|
||||
{
|
||||
this.fb_instance_id = objID;
|
||||
return this;
|
||||
}
|
||||
|
||||
function FABridge__invokeJSFunction(args)
|
||||
{
|
||||
var funcID = args[0];
|
||||
var throughArgs = args.concat();//FABridge.argsToArray(arguments);
|
||||
throughArgs.shift();
|
||||
|
||||
var bridge = FABridge.extractBridgeFromID(funcID);
|
||||
return bridge.invokeLocalFunction(funcID, throughArgs);
|
||||
}
|
||||
|
||||
FABridge.addInitializationCallback = function(bridgeName, callback)
|
||||
{
|
||||
var inst = FABridge.instances[bridgeName];
|
||||
if (inst != undefined)
|
||||
{
|
||||
callback.call(inst);
|
||||
return;
|
||||
}
|
||||
|
||||
var callbackList = FABridge.initCallbacks[bridgeName];
|
||||
if(callbackList == null)
|
||||
{
|
||||
FABridge.initCallbacks[bridgeName] = callbackList = [];
|
||||
}
|
||||
|
||||
callbackList.push(callback);
|
||||
}
|
||||
|
||||
// updated for changes to SWFObject2
|
||||
function FABridge__bridgeInitialized(bridgeName) {
|
||||
var objects = document.getElementsByTagName("object");
|
||||
var ol = objects.length;
|
||||
var activeObjects = [];
|
||||
if (ol > 0) {
|
||||
for (var i = 0; i < ol; i++) {
|
||||
if (typeof objects[i].SetVariable != "undefined") {
|
||||
activeObjects[activeObjects.length] = objects[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
var embeds = document.getElementsByTagName("embed");
|
||||
var el = embeds.length;
|
||||
var activeEmbeds = [];
|
||||
if (el > 0) {
|
||||
for (var j = 0; j < el; j++) {
|
||||
if (typeof embeds[j].SetVariable != "undefined") {
|
||||
activeEmbeds[activeEmbeds.length] = embeds[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
var aol = activeObjects.length;
|
||||
var ael = activeEmbeds.length;
|
||||
var searchStr = "bridgeName="+ bridgeName;
|
||||
if ((aol == 1 && !ael) || (aol == 1 && ael == 1)) {
|
||||
FABridge.attachBridge(activeObjects[0], bridgeName);
|
||||
}
|
||||
else if (ael == 1 && !aol) {
|
||||
FABridge.attachBridge(activeEmbeds[0], bridgeName);
|
||||
}
|
||||
else {
|
||||
var flash_found = false;
|
||||
if (aol > 1) {
|
||||
for (var k = 0; k < aol; k++) {
|
||||
var params = activeObjects[k].childNodes;
|
||||
for (var l = 0; l < params.length; l++) {
|
||||
var param = params[l];
|
||||
if (param.nodeType == 1 && param.tagName.toLowerCase() == "param" && param["name"].toLowerCase() == "flashvars" && param["value"].indexOf(searchStr) >= 0) {
|
||||
FABridge.attachBridge(activeObjects[k], bridgeName);
|
||||
flash_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flash_found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!flash_found && ael > 1) {
|
||||
for (var m = 0; m < ael; m++) {
|
||||
var flashVars = activeEmbeds[m].attributes.getNamedItem("flashVars").nodeValue;
|
||||
if (flashVars.indexOf(searchStr) >= 0) {
|
||||
FABridge.attachBridge(activeEmbeds[m], bridgeName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// used to track multiple bridge instances, since callbacks from AS are global across the page.
|
||||
|
||||
FABridge.nextBridgeID = 0;
|
||||
FABridge.instances = {};
|
||||
FABridge.idMap = {};
|
||||
FABridge.refCount = 0;
|
||||
|
||||
FABridge.extractBridgeFromID = function(id)
|
||||
{
|
||||
var bridgeID = (id >> 16);
|
||||
return FABridge.idMap[bridgeID];
|
||||
}
|
||||
|
||||
FABridge.attachBridge = function(instance, bridgeName)
|
||||
{
|
||||
var newBridgeInstance = new FABridge(instance, bridgeName);
|
||||
|
||||
FABridge[bridgeName] = newBridgeInstance;
|
||||
|
||||
/* FABridge[bridgeName] = function() {
|
||||
return newBridgeInstance.root();
|
||||
}
|
||||
*/
|
||||
var callbacks = FABridge.initCallbacks[bridgeName];
|
||||
if (callbacks == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < callbacks.length; i++)
|
||||
{
|
||||
callbacks[i].call(newBridgeInstance);
|
||||
}
|
||||
delete FABridge.initCallbacks[bridgeName]
|
||||
}
|
||||
|
||||
// some methods can't be proxied. You can use the explicit get,set, and call methods if necessary.
|
||||
|
||||
FABridge.blockedMethods =
|
||||
{
|
||||
toString: true,
|
||||
get: true,
|
||||
set: true,
|
||||
call: true
|
||||
};
|
||||
|
||||
FABridge.prototype =
|
||||
{
|
||||
|
||||
|
||||
// bootstrapping
|
||||
|
||||
root: function()
|
||||
{
|
||||
return this.deserialize(this.target.getRoot());
|
||||
},
|
||||
//clears all of the AS objects in the cache maps
|
||||
releaseASObjects: function()
|
||||
{
|
||||
return this.target.releaseASObjects();
|
||||
},
|
||||
//clears a specific object in AS from the type maps
|
||||
releaseNamedASObject: function(value)
|
||||
{
|
||||
if(typeof(value) != "object")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var ret = this.target.releaseNamedASObject(value.fb_instance_id);
|
||||
return ret;
|
||||
}
|
||||
},
|
||||
//create a new AS Object
|
||||
create: function(className)
|
||||
{
|
||||
return this.deserialize(this.target.create(className));
|
||||
},
|
||||
|
||||
|
||||
// utilities
|
||||
|
||||
makeID: function(token)
|
||||
{
|
||||
return (this.bridgeID << 16) + token;
|
||||
},
|
||||
|
||||
|
||||
// low level access to the flash object
|
||||
|
||||
//get a named property from an AS object
|
||||
getPropertyFromAS: function(objRef, propName)
|
||||
{
|
||||
if (FABridge.refCount > 0)
|
||||
{
|
||||
throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround.");
|
||||
}
|
||||
else
|
||||
{
|
||||
FABridge.refCount++;
|
||||
retVal = this.target.getPropFromAS(objRef, propName);
|
||||
retVal = this.handleError(retVal);
|
||||
FABridge.refCount--;
|
||||
return retVal;
|
||||
}
|
||||
},
|
||||
//set a named property on an AS object
|
||||
setPropertyInAS: function(objRef,propName, value)
|
||||
{
|
||||
if (FABridge.refCount > 0)
|
||||
{
|
||||
throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround.");
|
||||
}
|
||||
else
|
||||
{
|
||||
FABridge.refCount++;
|
||||
retVal = this.target.setPropInAS(objRef,propName, this.serialize(value));
|
||||
retVal = this.handleError(retVal);
|
||||
FABridge.refCount--;
|
||||
return retVal;
|
||||
}
|
||||
},
|
||||
|
||||
//call an AS function
|
||||
callASFunction: function(funcID, args)
|
||||
{
|
||||
if (FABridge.refCount > 0)
|
||||
{
|
||||
throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround.");
|
||||
}
|
||||
else
|
||||
{
|
||||
FABridge.refCount++;
|
||||
retVal = this.target.invokeASFunction(funcID, this.serialize(args));
|
||||
retVal = this.handleError(retVal);
|
||||
FABridge.refCount--;
|
||||
return retVal;
|
||||
}
|
||||
},
|
||||
//call a method on an AS object
|
||||
callASMethod: function(objID, funcName, args)
|
||||
{
|
||||
if (FABridge.refCount > 0)
|
||||
{
|
||||
throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround.");
|
||||
}
|
||||
else
|
||||
{
|
||||
FABridge.refCount++;
|
||||
args = this.serialize(args);
|
||||
retVal = this.target.invokeASMethod(objID, funcName, args);
|
||||
retVal = this.handleError(retVal);
|
||||
FABridge.refCount--;
|
||||
return retVal;
|
||||
}
|
||||
},
|
||||
|
||||
// responders to remote calls from flash
|
||||
|
||||
//callback from flash that executes a local JS function
|
||||
//used mostly when setting js functions as callbacks on events
|
||||
invokeLocalFunction: function(funcID, args)
|
||||
{
|
||||
var result;
|
||||
var func = this.localFunctionCache[funcID];
|
||||
|
||||
if(func != undefined)
|
||||
{
|
||||
result = this.serialize(func.apply(null, this.deserialize(args)));
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
// Object Types and Proxies
|
||||
|
||||
// accepts an object reference, returns a type object matching the obj reference.
|
||||
getTypeFromName: function(objTypeName)
|
||||
{
|
||||
return this.remoteTypeCache[objTypeName];
|
||||
},
|
||||
//create an AS proxy for the given object ID and type
|
||||
createProxy: function(objID, typeName)
|
||||
{
|
||||
var objType = this.getTypeFromName(typeName);
|
||||
instanceFactory.prototype = objType;
|
||||
var instance = new instanceFactory(objID);
|
||||
this.remoteInstanceCache[objID] = instance;
|
||||
return instance;
|
||||
},
|
||||
//return the proxy associated with the given object ID
|
||||
getProxy: function(objID)
|
||||
{
|
||||
return this.remoteInstanceCache[objID];
|
||||
},
|
||||
|
||||
// accepts a type structure, returns a constructed type
|
||||
addTypeDataToCache: function(typeData)
|
||||
{
|
||||
newType = new ASProxy(this, typeData.name);
|
||||
var accessors = typeData.accessors;
|
||||
for (var i = 0; i < accessors.length; i++)
|
||||
{
|
||||
this.addPropertyToType(newType, accessors[i]);
|
||||
}
|
||||
|
||||
var methods = typeData.methods;
|
||||
for (var i = 0; i < methods.length; i++)
|
||||
{
|
||||
if (FABridge.blockedMethods[methods[i]] == undefined)
|
||||
{
|
||||
this.addMethodToType(newType, methods[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.remoteTypeCache[newType.typeName] = newType;
|
||||
return newType;
|
||||
},
|
||||
|
||||
//add a property to a typename; used to define the properties that can be called on an AS proxied object
|
||||
addPropertyToType: function(ty, propName)
|
||||
{
|
||||
var c = propName.charAt(0);
|
||||
var setterName;
|
||||
var getterName;
|
||||
if(c >= "a" && c <= "z")
|
||||
{
|
||||
getterName = "get" + c.toUpperCase() + propName.substr(1);
|
||||
setterName = "set" + c.toUpperCase() + propName.substr(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
getterName = "get" + propName;
|
||||
setterName = "set" + propName;
|
||||
}
|
||||
ty[setterName] = function(val)
|
||||
{
|
||||
this.bridge.setPropertyInAS(this.fb_instance_id, propName, val);
|
||||
}
|
||||
ty[getterName] = function()
|
||||
{
|
||||
return this.bridge.deserialize(this.bridge.getPropertyFromAS(this.fb_instance_id, propName));
|
||||
}
|
||||
},
|
||||
|
||||
//add a method to a typename; used to define the methods that can be callefd on an AS proxied object
|
||||
addMethodToType: function(ty, methodName)
|
||||
{
|
||||
ty[methodName] = function()
|
||||
{
|
||||
return this.bridge.deserialize(this.bridge.callASMethod(this.fb_instance_id, methodName, FABridge.argsToArray(arguments)));
|
||||
}
|
||||
},
|
||||
|
||||
// Function Proxies
|
||||
|
||||
//returns the AS proxy for the specified function ID
|
||||
getFunctionProxy: function(funcID)
|
||||
{
|
||||
var bridge = this;
|
||||
if (this.remoteFunctionCache[funcID] == null)
|
||||
{
|
||||
this.remoteFunctionCache[funcID] = function()
|
||||
{
|
||||
bridge.callASFunction(funcID, FABridge.argsToArray(arguments));
|
||||
}
|
||||
}
|
||||
return this.remoteFunctionCache[funcID];
|
||||
},
|
||||
|
||||
//reutrns the ID of the given function; if it doesnt exist it is created and added to the local cache
|
||||
getFunctionID: function(func)
|
||||
{
|
||||
if (func.__bridge_id__ == undefined)
|
||||
{
|
||||
func.__bridge_id__ = this.makeID(this.nextLocalFuncID++);
|
||||
this.localFunctionCache[func.__bridge_id__] = func;
|
||||
}
|
||||
return func.__bridge_id__;
|
||||
},
|
||||
|
||||
// serialization / deserialization
|
||||
|
||||
serialize: function(value)
|
||||
{
|
||||
var result = {};
|
||||
|
||||
var t = typeof(value);
|
||||
//primitives are kept as such
|
||||
if (t == "number" || t == "string" || t == "boolean" || t == null || t == undefined)
|
||||
{
|
||||
result = value;
|
||||
}
|
||||
else if (value instanceof Array)
|
||||
{
|
||||
//arrays are serializesd recursively
|
||||
result = [];
|
||||
for (var i = 0; i < value.length; i++)
|
||||
{
|
||||
result[i] = this.serialize(value[i]);
|
||||
}
|
||||
}
|
||||
else if (t == "function")
|
||||
{
|
||||
//js functions are assigned an ID and stored in the local cache
|
||||
result.type = FABridge.TYPE_JSFUNCTION;
|
||||
result.value = this.getFunctionID(value);
|
||||
}
|
||||
else if (value instanceof ASProxy)
|
||||
{
|
||||
result.type = FABridge.TYPE_ASINSTANCE;
|
||||
result.value = value.fb_instance_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.type = FABridge.TYPE_ANONYMOUS;
|
||||
result.value = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
//on deserialization we always check the return for the specific error code that is used to marshall NPE's into JS errors
|
||||
// the unpacking is done by returning the value on each pachet for objects/arrays
|
||||
deserialize: function(packedValue)
|
||||
{
|
||||
|
||||
var result;
|
||||
|
||||
var t = typeof(packedValue);
|
||||
if (t == "number" || t == "string" || t == "boolean" || packedValue == null || packedValue == undefined)
|
||||
{
|
||||
result = this.handleError(packedValue);
|
||||
}
|
||||
else if (packedValue instanceof Array)
|
||||
{
|
||||
result = [];
|
||||
for (var i = 0; i < packedValue.length; i++)
|
||||
{
|
||||
result[i] = this.deserialize(packedValue[i]);
|
||||
}
|
||||
}
|
||||
else if (t == "object")
|
||||
{
|
||||
for(var i = 0; i < packedValue.newTypes.length; i++)
|
||||
{
|
||||
this.addTypeDataToCache(packedValue.newTypes[i]);
|
||||
}
|
||||
for (var aRefID in packedValue.newRefs)
|
||||
{
|
||||
this.createProxy(aRefID, packedValue.newRefs[aRefID]);
|
||||
}
|
||||
if (packedValue.type == FABridge.TYPE_PRIMITIVE)
|
||||
{
|
||||
result = packedValue.value;
|
||||
}
|
||||
else if (packedValue.type == FABridge.TYPE_ASFUNCTION)
|
||||
{
|
||||
result = this.getFunctionProxy(packedValue.value);
|
||||
}
|
||||
else if (packedValue.type == FABridge.TYPE_ASINSTANCE)
|
||||
{
|
||||
result = this.getProxy(packedValue.value);
|
||||
}
|
||||
else if (packedValue.type == FABridge.TYPE_ANONYMOUS)
|
||||
{
|
||||
result = packedValue.value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
//increases the reference count for the given object
|
||||
addRef: function(obj)
|
||||
{
|
||||
this.target.incRef(obj.fb_instance_id);
|
||||
},
|
||||
//decrease the reference count for the given object and release it if needed
|
||||
release:function(obj)
|
||||
{
|
||||
this.target.releaseRef(obj.fb_instance_id);
|
||||
},
|
||||
|
||||
// check the given value for the components of the hard-coded error code : __FLASHERROR
|
||||
// used to marshall NPE's into flash
|
||||
|
||||
handleError: function(value)
|
||||
{
|
||||
if (typeof(value)=="string" && value.indexOf("__FLASHERROR")==0)
|
||||
{
|
||||
var myErrorMessage = value.split("||");
|
||||
if(FABridge.refCount > 0 )
|
||||
{
|
||||
FABridge.refCount--;
|
||||
}
|
||||
throw new Error(myErrorMessage[1]);
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// The root ASProxy class that facades a flash object
|
||||
|
||||
ASProxy = function(bridge, typeName)
|
||||
{
|
||||
this.bridge = bridge;
|
||||
this.typeName = typeName;
|
||||
return this;
|
||||
};
|
||||
//methods available on each ASProxy object
|
||||
ASProxy.prototype =
|
||||
{
|
||||
get: function(propName)
|
||||
{
|
||||
return this.bridge.deserialize(this.bridge.getPropertyFromAS(this.fb_instance_id, propName));
|
||||
},
|
||||
|
||||
set: function(propName, value)
|
||||
{
|
||||
this.bridge.setPropertyInAS(this.fb_instance_id, propName, value);
|
||||
},
|
||||
|
||||
call: function(funcName, args)
|
||||
{
|
||||
this.bridge.callASMethod(this.fb_instance_id, funcName, args);
|
||||
},
|
||||
|
||||
addRef: function() {
|
||||
this.bridge.addRef(this);
|
||||
},
|
||||
|
||||
release: function() {
|
||||
this.bridge.release(this);
|
||||
}
|
||||
};
|
||||
BIN
public/javascripts/WebSocketMain.swf
Normal file
BIN
public/javascripts/WebSocketMain.swf
Normal file
Binary file not shown.
4
public/javascripts/swfobject.js
Normal file
4
public/javascripts/swfobject.js
Normal file
File diff suppressed because one or more lines are too long
312
public/javascripts/web_socket.js
Executable file
312
public/javascripts/web_socket.js
Executable file
|
|
@ -0,0 +1,312 @@
|
|||
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
|
||||
// Lincense: New BSD Lincense
|
||||
// Reference: http://dev.w3.org/html5/websockets/
|
||||
// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
|
||||
|
||||
(function() {
|
||||
|
||||
if (window.WebSocket) return;
|
||||
|
||||
var console = window.console;
|
||||
if (!console) console = {log: function(){ }, error: function(){ }};
|
||||
|
||||
function hasFlash() {
|
||||
if ('navigator' in window && 'plugins' in navigator && navigator.plugins['Shockwave Flash']) {
|
||||
return !!navigator.plugins['Shockwave Flash'].description;
|
||||
}
|
||||
if ('ActiveXObject' in window) {
|
||||
try {
|
||||
return !!new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
|
||||
} catch (e) {}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasFlash()) {
|
||||
console.error("Flash Player is not installed.");
|
||||
return;
|
||||
}
|
||||
|
||||
WebSocket = function(url, protocol, proxyHost, proxyPort, headers) {
|
||||
var self = this;
|
||||
self.readyState = WebSocket.CONNECTING;
|
||||
self.bufferedAmount = 0;
|
||||
WebSocket.__addTask(function() {
|
||||
self.__flash =
|
||||
WebSocket.__flash.create(url, protocol, proxyHost || null, proxyPort || 0, headers || null);
|
||||
|
||||
self.__flash.addEventListener("open", function(fe) {
|
||||
try {
|
||||
if (self.onopen) self.onopen();
|
||||
} catch (e) {
|
||||
console.error(e.toString());
|
||||
}
|
||||
});
|
||||
|
||||
self.__flash.addEventListener("close", function(fe) {
|
||||
try {
|
||||
if (self.onclose) self.onclose();
|
||||
} catch (e) {
|
||||
console.error(e.toString());
|
||||
}
|
||||
});
|
||||
|
||||
self.__flash.addEventListener("message", function(fe) {
|
||||
var data = decodeURIComponent(fe.getData());
|
||||
try {
|
||||
if (self.onmessage) {
|
||||
var e;
|
||||
if (window.MessageEvent) {
|
||||
e = document.createEvent("MessageEvent");
|
||||
e.initMessageEvent("message", false, false, data, null, null, window);
|
||||
} else { // IE
|
||||
e = {data: data};
|
||||
}
|
||||
self.onmessage(e);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e.toString());
|
||||
}
|
||||
});
|
||||
|
||||
self.__flash.addEventListener("stateChange", function(fe) {
|
||||
try {
|
||||
self.readyState = fe.getReadyState();
|
||||
self.bufferedAmount = fe.getBufferedAmount();
|
||||
} catch (e) {
|
||||
console.error(e.toString());
|
||||
}
|
||||
});
|
||||
|
||||
//console.log("[WebSocket] Flash object is ready");
|
||||
});
|
||||
}
|
||||
|
||||
WebSocket.prototype.send = function(data) {
|
||||
if (!this.__flash || this.readyState == WebSocket.CONNECTING) {
|
||||
throw "INVALID_STATE_ERR: Web Socket connection has not been established";
|
||||
}
|
||||
var result = this.__flash.send(data);
|
||||
if (result < 0) { // success
|
||||
return true;
|
||||
} else {
|
||||
this.bufferedAmount = result;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
WebSocket.prototype.close = function() {
|
||||
if (!this.__flash) return;
|
||||
if (this.readyState != WebSocket.OPEN) return;
|
||||
this.__flash.close();
|
||||
// Sets/calls them manually here because Flash WebSocketConnection.close cannot fire events
|
||||
// which causes weird error:
|
||||
// > You are trying to call recursively into the Flash Player which is not allowed.
|
||||
this.readyState = WebSocket.CLOSED;
|
||||
if (this.onclose) this.onclose();
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
|
||||
*
|
||||
* @param {string} type
|
||||
* @param {function} listener
|
||||
* @param {boolean} useCapture !NB Not implemented yet
|
||||
* @return void
|
||||
*/
|
||||
WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
|
||||
if (!('__events' in this)) {
|
||||
this.__events = {};
|
||||
}
|
||||
if (!(type in this.__events)) {
|
||||
this.__events[type] = [];
|
||||
if ('function' == typeof this['on' + type]) {
|
||||
this.__events[type].defaultHandler = this['on' + type];
|
||||
this['on' + type] = WebSocket_FireEvent(this, type);
|
||||
}
|
||||
}
|
||||
this.__events[type].push(listener);
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
|
||||
*
|
||||
* @param {string} type
|
||||
* @param {function} listener
|
||||
* @param {boolean} useCapture NB! Not implemented yet
|
||||
* @return void
|
||||
*/
|
||||
WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
|
||||
if (!('__events' in this)) {
|
||||
this.__events = {};
|
||||
}
|
||||
if (!(type in this.__events)) return;
|
||||
for (var i = this.__events.length; i > -1; --i) {
|
||||
if (listener === this.__events[type][i]) {
|
||||
this.__events[type].splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
|
||||
*
|
||||
* @param {WebSocketEvent} event
|
||||
* @return void
|
||||
*/
|
||||
WebSocket.prototype.dispatchEvent = function(event) {
|
||||
if (!('__events' in this)) throw 'UNSPECIFIED_EVENT_TYPE_ERR';
|
||||
if (!(event.type in this.__events)) throw 'UNSPECIFIED_EVENT_TYPE_ERR';
|
||||
|
||||
for (var i = 0, l = this.__events[event.type].length; i < l; ++ i) {
|
||||
this.__events[event.type][i](event);
|
||||
if (event.cancelBubble) break;
|
||||
}
|
||||
|
||||
if (false !== event.returnValue &&
|
||||
'function' == typeof this.__events[event.type].defaultHandler)
|
||||
{
|
||||
this.__events[event.type].defaultHandler(event);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} object
|
||||
* @param {string} type
|
||||
*/
|
||||
function WebSocket_FireEvent(object, type) {
|
||||
return function(data) {
|
||||
var event = new WebSocketEvent();
|
||||
event.initEvent(type, true, true);
|
||||
event.target = event.currentTarget = object;
|
||||
for (var key in data) {
|
||||
event[key] = data[key];
|
||||
}
|
||||
object.dispatchEvent(event, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-interface">DOM 2 EventInterface</a>}
|
||||
*
|
||||
* @class
|
||||
* @constructor
|
||||
*/
|
||||
function WebSocketEvent(){}
|
||||
|
||||
/**
|
||||
*
|
||||
* @type boolean
|
||||
*/
|
||||
WebSocketEvent.prototype.cancelable = true;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type boolean
|
||||
*/
|
||||
WebSocketEvent.prototype.cancelBubble = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
WebSocketEvent.prototype.preventDefault = function() {
|
||||
if (this.cancelable) {
|
||||
this.returnValue = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
WebSocketEvent.prototype.stopPropagation = function() {
|
||||
this.cancelBubble = true;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} eventTypeArg
|
||||
* @param {boolean} canBubbleArg
|
||||
* @param {boolean} cancelableArg
|
||||
* @return void
|
||||
*/
|
||||
WebSocketEvent.prototype.initEvent = function(eventTypeArg, canBubbleArg, cancelableArg) {
|
||||
this.type = eventTypeArg;
|
||||
this.cancelable = cancelableArg;
|
||||
this.timeStamp = new Date();
|
||||
};
|
||||
|
||||
|
||||
WebSocket.CONNECTING = 0;
|
||||
WebSocket.OPEN = 1;
|
||||
WebSocket.CLOSED = 2;
|
||||
|
||||
WebSocket.__tasks = [];
|
||||
|
||||
WebSocket.__initialize = function() {
|
||||
if (!WebSocket.__swfLocation) {
|
||||
//console.error("[WebSocket] set WebSocket.__swfLocation to location of WebSocketMain.swf");
|
||||
//return;
|
||||
WebSocket.__swfLocation = "js/WebSocketMain.swf";
|
||||
}
|
||||
var container = document.createElement("div");
|
||||
container.id = "webSocketContainer";
|
||||
// Puts the Flash out of the window. Note that we cannot use display: none or visibility: hidden
|
||||
// here because it prevents Flash from loading at least in IE.
|
||||
container.style.position = "absolute";
|
||||
container.style.left = "-100px";
|
||||
container.style.top = "-100px";
|
||||
var holder = document.createElement("div");
|
||||
holder.id = "webSocketFlash";
|
||||
container.appendChild(holder);
|
||||
document.body.appendChild(container);
|
||||
swfobject.embedSWF(
|
||||
WebSocket.__swfLocation, "webSocketFlash", "8", "8", "9.0.0",
|
||||
null, {bridgeName: "webSocket"}, null, null,
|
||||
function(e) {
|
||||
if (!e.success) console.error("[WebSocket] swfobject.embedSWF failed");
|
||||
}
|
||||
);
|
||||
FABridge.addInitializationCallback("webSocket", function() {
|
||||
try {
|
||||
//console.log("[WebSocket] FABridge initializad");
|
||||
WebSocket.__flash = FABridge.webSocket.root();
|
||||
WebSocket.__flash.setCallerUrl(location.href);
|
||||
for (var i = 0; i < WebSocket.__tasks.length; ++i) {
|
||||
WebSocket.__tasks[i]();
|
||||
}
|
||||
WebSocket.__tasks = [];
|
||||
} catch (e) {
|
||||
console.error("[WebSocket] " + e.toString());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
WebSocket.__addTask = function(task) {
|
||||
if (WebSocket.__flash) {
|
||||
task();
|
||||
} else {
|
||||
WebSocket.__tasks.push(task);
|
||||
}
|
||||
}
|
||||
|
||||
// called from Flash
|
||||
function webSocketLog(message) {
|
||||
console.log(decodeURIComponent(message));
|
||||
}
|
||||
|
||||
// called from Flash
|
||||
function webSocketError(message) {
|
||||
console.error(decodeURIComponent(message));
|
||||
}
|
||||
|
||||
if (window.addEventListener) {
|
||||
window.addEventListener("load", WebSocket.__initialize, false);
|
||||
} else {
|
||||
window.attachEvent("onload", WebSocket.__initialize);
|
||||
}
|
||||
|
||||
})();
|
||||
Loading…
Reference in a new issue