123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- // Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
- // License: New BSD License
- // Reference: http://dev.w3.org/html5/websockets/
- // Reference: http://tools.ietf.org/html/rfc6455
- (function() {
-
- if (window.WEB_SOCKET_FORCE_FLASH) {
- // Keeps going.
- } else if (window.WebSocket) {
- return;
- } else if (window.MozWebSocket) {
- // Firefox.
- window.WebSocket = MozWebSocket;
- return;
- }
-
- var logger;
- if (window.WEB_SOCKET_LOGGER) {
- logger = WEB_SOCKET_LOGGER;
- } else if (window.console && window.console.log && window.console.error) {
- // In some environment, console is defined but console.log or console.error is missing.
- logger = window.console;
- } else {
- logger = {log: function(){ }, error: function(){ }};
- }
-
- // swfobject.hasFlashPlayerVersion("10.0.0") doesn't work with Gnash.
- if (swfobject.getFlashPlayerVersion().major < 10) {
- logger.error("Flash Player >= 10.0.0 is required.");
- return;
- }
- if (location.protocol == "file:") {
- logger.error(
- "WARNING: web-socket-js doesn't work in file:///... URL " +
- "unless you set Flash Security Settings properly. " +
- "Open the page via Web server i.e. http://...");
- }
- /**
- * Our own implementation of WebSocket class using Flash.
- * @param {string} url
- * @param {array or string} protocols
- * @param {string} proxyHost
- * @param {int} proxyPort
- * @param {string} headers
- */
- window.WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
- var self = this;
- self.__id = WebSocket.__nextId++;
- WebSocket.__instances[self.__id] = self;
- self.readyState = WebSocket.CONNECTING;
- self.bufferedAmount = 0;
- self.__events = {};
- if (!protocols) {
- protocols = [];
- } else if (typeof protocols == "string") {
- protocols = [protocols];
- }
- // 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.
- self.__createTask = setTimeout(function() {
- WebSocket.__addTask(function() {
- self.__createTask = null;
- WebSocket.__flash.create(
- self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
- });
- }, 0);
- };
- /**
- * 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.readyState == WebSocket.CONNECTING) {
- throw "INVALID_STATE_ERR: Web Socket connection has not been established";
- }
- // We use encodeURIComponent() here, because FABridge doesn't work if
- // the argument includes some characters. We don't use escape() here
- // because of this:
- // 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.
- // 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 {
- this.bufferedAmount += result;
- return false;
- }
- };
- /**
- * Close this web socket gracefully.
- */
- WebSocket.prototype.close = function() {
- if (this.__createTask) {
- clearTimeout(this.__createTask);
- this.__createTask = null;
- this.readyState = WebSocket.CLOSED;
- return;
- }
- if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
- return;
- }
- this.readyState = WebSocket.CLOSING;
- WebSocket.__flash.close(this.__id);
- };
- /**
- * 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
- * @return void
- */
- WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
- if (!(type in this.__events)) {
- this.__events[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
- * @return void
- */
- WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
- if (!(type in this.__events)) return;
- var events = this.__events[type];
- for (var i = events.length - 1; i >= 0; --i) {
- if (events[i] === listener) {
- events.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 {Event} event
- * @return void
- */
- WebSocket.prototype.dispatchEvent = function(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.apply(this, [event]);
- };
- /**
- * Handles an event from Flash.
- * @param {Object} flashEvent
- */
- WebSocket.prototype.__handleEvent = function(flashEvent) {
-
- if ("readyState" in flashEvent) {
- this.readyState = flashEvent.readyState;
- }
- if ("protocol" in flashEvent) {
- this.protocol = flashEvent.protocol;
- }
-
- var jsEvent;
- if (flashEvent.type == "open" || flashEvent.type == "error") {
- jsEvent = this.__createSimpleEvent(flashEvent.type);
- } else if (flashEvent.type == "close") {
- jsEvent = this.__createSimpleEvent("close");
- jsEvent.wasClean = flashEvent.wasClean ? true : false;
- jsEvent.code = flashEvent.code;
- jsEvent.reason = flashEvent.reason;
- } 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 (window.MessageEvent && typeof(MessageEvent) == "function" && !window.opera) {
- return new MessageEvent("message", {
- "view": window,
- "bubbles": false,
- "cancelable": false,
- "data": data
- });
- } else 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 {
- // Old IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
- return {type: type, data: data, bubbles: false, cancelable: false};
- }
- };
-
- /**
- * Define the WebSocket readyState enumeration.
- */
- WebSocket.CONNECTING = 0;
- WebSocket.OPEN = 1;
- WebSocket.CLOSING = 2;
- WebSocket.CLOSED = 3;
- // Field to check implementation of WebSocket.
- WebSocket.__isFlashImplementation = true;
- WebSocket.__initialized = false;
- WebSocket.__flash = null;
- WebSocket.__instances = {};
- WebSocket.__tasks = [];
- 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.__initialized) return;
- WebSocket.__initialized = true;
-
- if (WebSocket.__swfLocation) {
- // For backword compatibility.
- window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
- }
- if (!window.WEB_SOCKET_SWF_LOCATION) {
- logger.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
- return;
- }
- if (!window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR &&
- !WEB_SOCKET_SWF_LOCATION.match(/(^|\/)WebSocketMainInsecure\.swf(\?.*)?$/) &&
- WEB_SOCKET_SWF_LOCATION.match(/^\w+:\/\/([^\/]+)/)) {
- var swfHost = RegExp.$1;
- if (location.host != swfHost) {
- logger.error(
- "[WebSocket] You must host HTML and WebSocketMain.swf in the same host " +
- "('" + location.host + "' != '" + swfHost + "'). " +
- "See also 'How to host HTML file and SWF file in different domains' section " +
- "in README.md. If you use WebSocketMainInsecure.swf, you can suppress this message " +
- "by WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;");
- }
- }
- var container = document.createElement("div");
- container.id = "webSocketContainer";
- // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
- // Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
- // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
- // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
- // the best we can do as far as we know now.
- container.style.position = "absolute";
- if (WebSocket.__isFlashLite()) {
- container.style.left = "0px";
- container.style.top = "0px";
- } else {
- container.style.left = "-100px";
- container.style.top = "-100px";
- }
- var holder = document.createElement("div");
- holder.id = "webSocketFlash";
- container.appendChild(holder);
- document.body.appendChild(container);
- // 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 */,
- "10.0.0" /* SWF version */,
- null,
- null,
- {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
- null,
- function(e) {
- if (!e.success) {
- logger.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]();
- }
- 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) {
- logger.error(e);
- }
- }, 0);
- return true;
- };
-
- // Called by Flash.
- WebSocket.__log = function(message) {
- logger.log(decodeURIComponent(message));
- };
-
- // Called by Flash.
- WebSocket.__error = function(message) {
- logger.error(decodeURIComponent(message));
- };
-
- WebSocket.__addTask = function(task) {
- if (WebSocket.__flash) {
- task();
- } else {
- WebSocket.__tasks.push(task);
- }
- };
-
- /**
- * 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;
- }
- var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
- if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
- return false;
- }
- return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
- };
-
- if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
- // NOTE:
- // This fires immediately if web_socket.js is dynamically loaded after
- // the document is loaded.
- swfobject.addDomLoadEvent(function() {
- WebSocket.__initialize();
- });
- }
-
- })();
|