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