diff --git a/include/web-socket-js/FABridge.js b/include/web-socket-js/FABridge.js
deleted file mode 100644
index df7e355c..00000000
--- a/include/web-socket-js/FABridge.js
+++ /dev/null
@@ -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);
- }
-};
diff --git a/include/web-socket-js/WebSocketMain.swf b/include/web-socket-js/WebSocketMain.swf
index 1e4df8d8..244c445b 100644
Binary files a/include/web-socket-js/WebSocketMain.swf and b/include/web-socket-js/WebSocketMain.swf differ
diff --git a/include/web-socket-js/web_socket.js b/include/web-socket-js/web_socket.js
index 48ec0fcb..ec2a8b72 100755
--- a/include/web-socket-js/web_socket.js
+++ b/include/web-socket-js/web_socket.js
@@ -12,8 +12,8 @@
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;
}
if (location.protocol == "file:") {
@@ -23,33 +23,38 @@
"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) {
var self = this;
+ self.__id = WebSocket.__nextId++;
+ WebSocket.__instances[self.__id] = self;
self.readyState = WebSocket.CONNECTING;
self.bufferedAmount = 0;
+ self.__events = {};
// 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.
setTimeout(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);
};
-
- 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) {
- if (!this.__flash || this.readyState == WebSocket.CONNECTING) {
+ if (this.readyState == WebSocket.CONNECTING) {
throw "INVALID_STATE_ERR: Web Socket connection has not been established";
}
// 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
// But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
// 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
return true;
} else {
@@ -67,21 +74,15 @@
}
};
+ /**
+ * Close this web socket gracefully.
+ */
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 {function} listener
- * @param {boolean} useCapture !NB Not implemented yet
+ * @param {boolean} useCapture
* @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] = this.__createEventHandler(this, type);
- }
}
this.__events[type].push(listener);
};
@@ -111,17 +105,15 @@
*
* @param {string} type
* @param {function} listener
- * @param {boolean} useCapture NB! Not implemented yet
+ * @param {boolean} useCapture
* @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);
+ var events = this.__events[type];
+ for (var i = events.length - 1; i >= 0; --i) {
+ if (events[i] === listener) {
+ events.splice(i, 1);
break;
}
}
@@ -130,164 +122,93 @@
/**
* Implementation of {@link DOM 2 EventTarget Interface}
*
- * @param {WebSocketEvent} event
+ * @param {Event} 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);
+ 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());
- }
+ /**
+ * Handles an event from Flash.
+ * @param {Object} flashEvent
+ */
+ 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);
+ };
+
+ 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};
+ }
+ };
+
+ 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 {object} object
- * @param {string} type
+ * Define the WebSocket readyState enumeration.
*/
- 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);
- };
- };
-
- /**
- * Basic implementation of {@link DOM 2 EventInterface}
- *
- * @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.CLOSING = 2;
WebSocket.CLOSED = 3;
+ WebSocket.__flash = null;
+ WebSocket.__instances = {};
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.__flash.loadManualPolicyFile(url);
});
- }
+ };
+ /**
+ * Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
+ */
WebSocket.__initialize = function() {
+ if (WebSocket.__flash) return;
+
if (WebSocket.__swfLocation) {
// For backword compatibility.
window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
@@ -318,29 +239,70 @@
// See this article for hasPriority:
// http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
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) {
- 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);
- WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
- for (var i = 0; i < WebSocket.__tasks.length; ++i) {
- WebSocket.__tasks[i]();
+ if (!e.success) {
+ console.error("[WebSocket] swfobject.embedSWF failed");
}
- WebSocket.__tasks = [];
- } catch (e) {
- console.error("[WebSocket] " + e.toString());
- }
- });
+ });
};
-
+
+ /**
+ * 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]();
+ }
+ WebSocket.__tasks = [];
+ }, 0);
+ };
+
+ /**
+ * Called by Flash to notify WebSockets events are fired.
+ */
+ WebSocket.__onFlashEvent = function() {
+ setTimeout(function() {
+ try {
+ // 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]);
+ }
+ } catch (e) {
+ 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) {
if (WebSocket.__flash) {
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() {
- 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"];
- 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;
};
-
- // 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.addEventListener) {
- window.addEventListener("load", WebSocket.__initialize, false);
+ window.addEventListener("load", function(){
+ WebSocket.__initialize();
+ }, false);
} else {
- window.attachEvent("onload", WebSocket.__initialize);
+ window.attachEvent("onload", function(){
+ WebSocket.__initialize();
+ });
}
}
diff --git a/include/websock.js b/include/websock.js
index 09bdf9ba..dcf6453c 100644
--- a/include/websock.js
+++ b/include/websock.js
@@ -38,7 +38,6 @@ if (window.WebSocket) {
WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
}
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;
document.write(extra);
}());