Browse Source

Update web-socket-js to bb5797cad.

Syncs with same change to websockify (7534574a2f).

Primary change is removal of FABridge interface.

Seems to improve overall latency by perhaps 10%. Also, the slowdown
over time in Opera is about half as bad (but still there).
Joel Martin 14 years ago
parent
commit
bbd21ca7b5

+ 0 - 604
include/web-socket-js/FABridge.js

@@ -1,604 +0,0 @@
-/*
-/*
-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)
-    {
-        var 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
include/web-socket-js/WebSocketMain.swf


+ 174 - 210
include/web-socket-js/web_socket.js

@@ -12,8 +12,8 @@
     console = {log: function(){ }, error: function(){ }};
     console = {log: function(){ }, error: function(){ }};
   }
   }
   
   
-  if (!swfobject.hasFlashPlayerVersion("9.0.0")) {
-    console.error("Flash Player is not installed.");
+  if (!swfobject.hasFlashPlayerVersion("10.0.0")) {
+    console.error("Flash Player >= 10.0.0 is required.");
     return;
     return;
   }
   }
   if (location.protocol == "file:") {
   if (location.protocol == "file:") {
@@ -23,33 +23,38 @@
       "Open the page via Web server i.e. http://...");
       "Open the page via Web server i.e. http://...");
   }
   }
 
 
+  /**
+   * This class represents a faux web socket.
+   * @param {string} url
+   * @param {string} protocol
+   * @param {string} proxyHost
+   * @param {int} proxyPort
+   * @param {string} headers
+   */
   WebSocket = function(url, protocol, proxyHost, proxyPort, headers) {
   WebSocket = function(url, protocol, proxyHost, proxyPort, headers) {
     var self = this;
     var self = this;
+    self.__id = WebSocket.__nextId++;
+    WebSocket.__instances[self.__id] = self;
     self.readyState = WebSocket.CONNECTING;
     self.readyState = WebSocket.CONNECTING;
     self.bufferedAmount = 0;
     self.bufferedAmount = 0;
+    self.__events = {};
     // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
     // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
     // Otherwise, when onopen fires immediately, onopen is called before it is set.
     // Otherwise, when onopen fires immediately, onopen is called before it is set.
     setTimeout(function() {
     setTimeout(function() {
       WebSocket.__addTask(function() {
       WebSocket.__addTask(function() {
-        self.__createFlash(url, protocol, proxyHost, proxyPort, headers);
+        WebSocket.__flash.create(
+            self.__id, url, protocol, proxyHost || null, proxyPort || 0, headers || null);
       });
       });
     }, 0);
     }, 0);
   };
   };
-  
-  WebSocket.prototype.__createFlash = function(url, protocol, proxyHost, proxyPort, headers) {
-    var self = this;
-    self.__flash =
-      WebSocket.__flash.create(url, protocol, proxyHost || null, proxyPort || 0, headers || null);
-    self.__flash.addEventListener("event", function(fe) {
-      // Uses setTimeout() to workaround the error:
-      // > You are trying to call recursively into the Flash Player which is not allowed.
-      setTimeout(function() { self.__handleEvents(); }, 0);
-    });
-    //console.log("[WebSocket] Flash object is ready");
-  };
 
 
+  /**
+   * Send data to the web socket.
+   * @param {string} data  The data to send to the socket.
+   * @return {boolean}  True for success, false for failure.
+   */
   WebSocket.prototype.send = function(data) {
   WebSocket.prototype.send = function(data) {
-    if (!this.__flash || this.readyState == WebSocket.CONNECTING) {
+    if (this.readyState == WebSocket.CONNECTING) {
       throw "INVALID_STATE_ERR: Web Socket connection has not been established";
       throw "INVALID_STATE_ERR: Web Socket connection has not been established";
     }
     }
     // We use encodeURIComponent() here, because FABridge doesn't work if
     // We use encodeURIComponent() here, because FABridge doesn't work if
@@ -58,7 +63,9 @@
     // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
     // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
     // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
     // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
     // preserve all Unicode characters either e.g. "\uffff" in Firefox.
     // preserve all Unicode characters either e.g. "\uffff" in Firefox.
-    var result = this.__flash.send(encodeURIComponent(data));
+    // Note by wtritch: Hopefully this will not be necessary using ExternalInterface.  Will require
+    // additional testing.
+    var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
     if (result < 0) { // success
     if (result < 0) { // success
       return true;
       return true;
     } else {
     } else {
@@ -67,21 +74,15 @@
     }
     }
   };
   };
 
 
+  /**
+   * Close this web socket gracefully.
+   */
   WebSocket.prototype.close = function() {
   WebSocket.prototype.close = function() {
-    var self = this;
-    if (!self.__flash) return;
-    if (self.readyState == WebSocket.CLOSED || self.readyState == WebSocket.CLOSING) return;
-    self.__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.
-    self.readyState = WebSocket.CLOSED;
-    if (self.__timer) clearInterval(self.__timer);
-    if (self.onclose) {
-       // Make it asynchronous so that it looks more like an actual
-       // close event
-       setTimeout(self.onclose, 0);
-     }
+    if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
+      return;
+    }
+    this.readyState = WebSocket.CLOSING;
+    WebSocket.__flash.close(this.__id);
   };
   };
 
 
   /**
   /**
@@ -89,19 +90,12 @@
    *
    *
    * @param {string} type
    * @param {string} type
    * @param {function} listener
    * @param {function} listener
-   * @param {boolean} useCapture !NB Not implemented yet
+   * @param {boolean} useCapture
    * @return void
    * @return void
    */
    */
   WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
   WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
-    if (!('__events' in this)) {
-      this.__events = {};
-    }
     if (!(type in this.__events)) {
     if (!(type in this.__events)) {
       this.__events[type] = [];
       this.__events[type] = [];
-      if ('function' == typeof this['on' + type]) {
-        this.__events[type].defaultHandler = this['on' + type];
-        this['on' + type] = this.__createEventHandler(this, type);
-      }
     }
     }
     this.__events[type].push(listener);
     this.__events[type].push(listener);
   };
   };
@@ -111,17 +105,15 @@
    *
    *
    * @param {string} type
    * @param {string} type
    * @param {function} listener
    * @param {function} listener
-   * @param {boolean} useCapture NB! Not implemented yet
+   * @param {boolean} useCapture
    * @return void
    * @return void
    */
    */
   WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
   WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
-    if (!('__events' in this)) {
-      this.__events = {};
-    }
     if (!(type in this.__events)) return;
     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);
+    var events = this.__events[type];
+    for (var i = events.length - 1; i >= 0; --i) {
+      if (events[i] === listener) {
+        events.splice(i, 1);
         break;
         break;
       }
       }
     }
     }
@@ -130,164 +122,93 @@
   /**
   /**
    * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
    * 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
+   * @param {Event} event
    * @return void
    * @return void
    */
    */
   WebSocket.prototype.dispatchEvent = function(event) {
   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);
+    var events = this.__events[event.type] || [];
+    for (var i = 0; i < events.length; ++i) {
+      events[i](event);
     }
     }
+    var handler = this["on" + event.type];
+    if (handler) handler(event);
   };
   };
 
 
-  WebSocket.prototype.__handleEvents = function() {
-    // Gets events using receiveEvents() instead of getting it from event object
-    // of Flash event. This is to make sure to keep message order.
-    // It seems sometimes Flash events don't arrive in the same order as they are sent.
-    var events = this.__flash.receiveEvents();
-    for (var i = 0; i < events.length; i++) {
-      try {
-        var event = events[i];
-        if ("readyState" in event) {
-          this.readyState = event.readyState;
-        }
-        if (event.type == "open") {
-          
-          if (this.__timer) clearInterval(this.__timer);
-          if (window.opera) {
-            // Workaround for weird behavior of Opera which sometimes drops events.
-            var that = this;
-            this.__timer = setInterval(function () {
-              that.__handleEvents();
-            }, 500);
-          }
-          if (this.onopen) this.onopen();
-          
-        } else if (event.type == "close") {
-          
-          if (this.__timer) clearInterval(this.__timer);
-          if (this.onclose) this.onclose();
-          
-        } else if (event.type == "message") {
-          
-          if (this.onmessage) {
-            var data = decodeURIComponent(event.data);
-            var e;
-            if (window.MessageEvent && !window.opera) {
-              e = document.createEvent("MessageEvent");
-              e.initMessageEvent("message", false, false, data, null, null, window, null);
-            } else {
-              // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
-              e = {data: data};
-            }
-            this.onmessage(e);
-          }
-          
-        } else if (event.type == "error") {
-          
-          if (this.__timer) clearInterval(this.__timer);
-          if (this.onerror) this.onerror();
-          
-        } else {
-          throw "unknown event type: " + event.type;
-        }
-      } catch (e) {
-        console.error(e.toString());
-      }
-    }
-  };
-  
   /**
   /**
-   * @param {object} object
-   * @param {string} type
+   * Handles an event from Flash.
+   * @param {Object} flashEvent
    */
    */
-  WebSocket.prototype.__createEventHandler = function(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);
-    };
+  WebSocket.prototype.__handleEvent = function(flashEvent) {
+    if ("readyState" in flashEvent) {
+      this.readyState = flashEvent.readyState;
+    }
+    
+    var jsEvent;
+    if (flashEvent.type == "open" || flashEvent.type == "error") {
+      jsEvent = this.__createSimpleEvent(flashEvent.type);
+    } else if (flashEvent.type == "close") {
+      // TODO implement jsEvent.wasClean
+      jsEvent = this.__createSimpleEvent("close");
+    } else if (flashEvent.type == "message") {
+      var data = decodeURIComponent(flashEvent.message);
+      jsEvent = this.__createMessageEvent("message", data);
+    } else {
+      throw "unknown event type: " + flashEvent.type;
+    }
+    
+    this.dispatchEvent(jsEvent);
   };
   };
-
-  /**
-   * 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;
+  
+  WebSocket.prototype.__createSimpleEvent = function(type) {
+    if (document.createEvent && window.Event) {
+      var event = document.createEvent("Event");
+      event.initEvent(type, false, false);
+      return event;
+    } else {
+      return {type: type, bubbles: false, cancelable: false};
     }
     }
   };
   };
-
-  /**
-   *
-   * @return void
-   */
-  WebSocketEvent.prototype.stopPropagation = function() {
-    this.cancelBubble = true;
+  
+  WebSocket.prototype.__createMessageEvent = function(type, data) {
+    if (document.createEvent && window.MessageEvent && !window.opera) {
+      var event = document.createEvent("MessageEvent");
+      event.initMessageEvent("message", false, false, data, null, null, window, null);
+      return event;
+    } else {
+      // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
+      return {type: type, data: data, bubbles: false, cancelable: false};
+    }
   };
   };
-
+  
   /**
   /**
-   *
-   * @param {string} eventTypeArg
-   * @param {boolean} canBubbleArg
-   * @param {boolean} cancelableArg
-   * @return void
+   * Define the WebSocket readyState enumeration.
    */
    */
-  WebSocketEvent.prototype.initEvent = function(eventTypeArg, canBubbleArg, cancelableArg) {
-    this.type = eventTypeArg;
-    this.cancelable = cancelableArg;
-    this.timeStamp = new Date();
-  };
-
-
   WebSocket.CONNECTING = 0;
   WebSocket.CONNECTING = 0;
   WebSocket.OPEN = 1;
   WebSocket.OPEN = 1;
   WebSocket.CLOSING = 2;
   WebSocket.CLOSING = 2;
   WebSocket.CLOSED = 3;
   WebSocket.CLOSED = 3;
 
 
+  WebSocket.__flash = null;
+  WebSocket.__instances = {};
   WebSocket.__tasks = [];
   WebSocket.__tasks = [];
-
-  WebSocket.loadFlashPolicyFile = function(url) {
+  WebSocket.__nextId = 0;
+  
+  /**
+   * Load a new flash security policy file.
+   * @param {string} url
+   */
+  WebSocket.loadFlashPolicyFile = function(url){
     WebSocket.__addTask(function() {
     WebSocket.__addTask(function() {
       WebSocket.__flash.loadManualPolicyFile(url);
       WebSocket.__flash.loadManualPolicyFile(url);
     });
     });
-  }
+  };
 
 
+  /**
+   * Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
+   */
   WebSocket.__initialize = function() {
   WebSocket.__initialize = function() {
+    if (WebSocket.__flash) return;
+    
     if (WebSocket.__swfLocation) {
     if (WebSocket.__swfLocation) {
       // For backword compatibility.
       // For backword compatibility.
       window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
       window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
@@ -318,29 +239,70 @@
     // See this article for hasPriority:
     // See this article for hasPriority:
     // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
     // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
     swfobject.embedSWF(
     swfobject.embedSWF(
-      WEB_SOCKET_SWF_LOCATION, "webSocketFlash",
-      "1" /* width */, "1" /* height */, "9.0.0" /* SWF version */,
-      null, {bridgeName: "webSocket"}, {hasPriority: true, allowScriptAccess: "always"}, null,
+      WEB_SOCKET_SWF_LOCATION,
+      "webSocketFlash",
+      "1" /* width */,
+      "1" /* height */,
+      "10.0.0" /* SWF version */,
+      null,
+      null,
+      {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
+      null,
       function(e) {
       function(e) {
-        if (!e.success) console.error("[WebSocket] swfobject.embedSWF failed");
+        if (!e.success) {
+          console.error("[WebSocket] swfobject.embedSWF failed");
+        }
+      });
+  };
+  
+  /**
+   * Called by Flash to notify JS that it's fully loaded and ready
+   * for communication.
+   */
+  WebSocket.__onFlashInitialized = function() {
+    // We need to set a timeout here to avoid round-trip calls
+    // to flash during the initialization process.
+    setTimeout(function() {
+      WebSocket.__flash = document.getElementById("webSocketFlash");
+      WebSocket.__flash.setCallerUrl(location.href);
+      WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
+      for (var i = 0; i < WebSocket.__tasks.length; ++i) {
+        WebSocket.__tasks[i]();
       }
       }
-    );
-    FABridge.addInitializationCallback("webSocket", function() {
+      WebSocket.__tasks = [];
+    }, 0);
+  };
+  
+  /**
+   * Called by Flash to notify WebSockets events are fired.
+   */
+  WebSocket.__onFlashEvent = function() {
+    setTimeout(function() {
       try {
       try {
-        //console.log("[WebSocket] FABridge initializad");
-        WebSocket.__flash = FABridge.webSocket.root();
-        WebSocket.__flash.setCallerUrl(location.href);
-        WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
-        for (var i = 0; i < WebSocket.__tasks.length; ++i) {
-          WebSocket.__tasks[i]();
+        // Gets events using receiveEvents() instead of getting it from event object
+        // of Flash event. This is to make sure to keep message order.
+        // It seems sometimes Flash events don't arrive in the same order as they are sent.
+        var events = WebSocket.__flash.receiveEvents();
+        for (var i = 0; i < events.length; ++i) {
+          WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
         }
         }
-        WebSocket.__tasks = [];
       } catch (e) {
       } catch (e) {
-        console.error("[WebSocket] " + e.toString());
+        console.error(e);
       }
       }
-    });
+    }, 0);
+    return true;
   };
   };
-
+  
+  // Called by Flash.
+  WebSocket.__log = function(message) {
+    console.log(decodeURIComponent(message));
+  };
+  
+  // Called by Flash.
+  WebSocket.__error = function(message) {
+    console.error(decodeURIComponent(message));
+  };
+  
   WebSocket.__addTask = function(task) {
   WebSocket.__addTask = function(task) {
     if (WebSocket.__flash) {
     if (WebSocket.__flash) {
       task();
       task();
@@ -349,28 +311,30 @@
     }
     }
   };
   };
   
   
+  /**
+   * Test if the browser is running flash lite.
+   * @return {boolean} True if flash lite is running, false otherwise.
+   */
   WebSocket.__isFlashLite = function() {
   WebSocket.__isFlashLite = function() {
-    if (!window.navigator || !window.navigator.mimeTypes) return false;
+    if (!window.navigator || !window.navigator.mimeTypes) {
+      return false;
+    }
     var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
     var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
-    if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) return false;
+    if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
+      return false;
+    }
     return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
     return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
   };
   };
-
-  // called from Flash
-  window.webSocketLog = function(message) {
-    console.log(decodeURIComponent(message));
-  };
-
-  // called from Flash
-  window.webSocketError = function(message) {
-    console.error(decodeURIComponent(message));
-  };
-
+  
   if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
   if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
     if (window.addEventListener) {
     if (window.addEventListener) {
-      window.addEventListener("load", WebSocket.__initialize, false);
+      window.addEventListener("load", function(){
+        WebSocket.__initialize();
+      }, false);
     } else {
     } else {
-      window.attachEvent("onload", WebSocket.__initialize);
+      window.attachEvent("onload", function(){
+        WebSocket.__initialize();
+      });
     }
     }
   }
   }
   
   

+ 0 - 1
include/websock.js

@@ -38,7 +38,6 @@ if (window.WebSocket) {
             WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
             WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
         }
         }
         extra += start + "web-socket-js/swfobject.js" + end;
         extra += start + "web-socket-js/swfobject.js" + end;
-        extra += start + "web-socket-js/FABridge.js" + end;
         extra += start + "web-socket-js/web_socket.js" + end;
         extra += start + "web-socket-js/web_socket.js" + end;
         document.write(extra);
         document.write(extra);
     }());
     }());