/js/lib/Socket.IO-node/support/socket.io-client/lib/vendor/web-socket-js/web_socket.js
JavaScript | 413 lines | 250 code | 35 blank | 128 comment | 72 complexity | 5e53feb522d6b7ac8a62712ab6283d70 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
1// Copyright: Hiroshi Ichikawa <http://gimite.net/en/> 2// License: New BSD License 3// Reference: http://dev.w3.org/html5/websockets/ 4// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol 5 6(function() { 7 8 if (window.WebSocket) return; 9 10 var console = window.console; 11 if (!console || !console.log || !console.error) { 12 console = {log: function(){ }, error: function(){ }}; 13 } 14 15 if (!swfobject.hasFlashPlayerVersion("10.0.0")) { 16 console.error("Flash Player >= 10.0.0 is required."); 17 return; 18 } 19 if (location.protocol == "file:") { 20 console.error( 21 "WARNING: web-socket-js doesn't work in file:///... URL " + 22 "unless you set Flash Security Settings properly. " + 23 "Open the page via Web server i.e. http://..."); 24 } 25 26 /** 27 * This class represents a faux web socket. 28 * @param {string} url 29 * @param {string} protocol 30 * @param {string} proxyHost 31 * @param {int} proxyPort 32 * @param {string} headers 33 */ 34 WebSocket = function(url, protocol, proxyHost, proxyPort, headers) { 35 var self = this; 36 self.__id = WebSocket.__nextId++; 37 WebSocket.__instances[self.__id] = self; 38 self.readyState = WebSocket.CONNECTING; 39 self.bufferedAmount = 0; 40 // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc. 41 // Otherwise, when onopen fires immediately, onopen is called before it is set. 42 setTimeout(function() { 43 WebSocket.__addTask(function() { 44 WebSocket.__flash.create( 45 self.__id, url, protocol, proxyHost || null, proxyPort || 0, headers || null); 46 }); 47 }, 0); 48 }; 49 50 /** 51 * Send data to the web socket. 52 * @param {string} data The data to send to the socket. 53 * @return {boolean} True for success, false for failure. 54 */ 55 WebSocket.prototype.send = function(data) { 56 if (this.readyState == WebSocket.CONNECTING) { 57 throw "INVALID_STATE_ERR: Web Socket connection has not been established"; 58 } 59 // We use encodeURIComponent() here, because FABridge doesn't work if 60 // the argument includes some characters. We don't use escape() here 61 // because of this: 62 // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions 63 // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't 64 // preserve all Unicode characters either e.g. "\uffff" in Firefox. 65 // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require 66 // additional testing. 67 var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data)); 68 if (result < 0) { // success 69 return true; 70 } else { 71 this.bufferedAmount += result; 72 return false; 73 } 74 }; 75 76 /** 77 * Close this web socket gracefully. 78 */ 79 WebSocket.prototype.close = function() { 80 if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) { 81 return; 82 } 83 this.readyState = WebSocket.CLOSING; 84 WebSocket.__flash.close(this.__id); 85 }; 86 87 /** 88 * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>} 89 * 90 * @param {string} type 91 * @param {function} listener 92 * @param {boolean} useCapture !NB Not implemented yet 93 * @return void 94 */ 95 WebSocket.prototype.addEventListener = function(type, listener, useCapture) { 96 if (!('__events' in this)) { 97 this.__events = {}; 98 } 99 if (!(type in this.__events)) { 100 this.__events[type] = []; 101 if ('function' == typeof this['on' + type]) { 102 this.__events[type].defaultHandler = this['on' + type]; 103 this['on' + type] = this.__createEventHandler(this, type); 104 } 105 } 106 this.__events[type].push(listener); 107 }; 108 109 /** 110 * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>} 111 * 112 * @param {string} type 113 * @param {function} listener 114 * @param {boolean} useCapture NB! Not implemented yet 115 * @return void 116 */ 117 WebSocket.prototype.removeEventListener = function(type, listener, useCapture) { 118 if (!('__events' in this)) { 119 this.__events = {}; 120 } 121 if (!(type in this.__events)) return; 122 for (var i = this.__events.length; i > -1; --i) { 123 if (listener === this.__events[type][i]) { 124 this.__events[type].splice(i, 1); 125 break; 126 } 127 } 128 }; 129 130 /** 131 * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>} 132 * 133 * @param {WebSocketEvent} event 134 * @return void 135 */ 136 WebSocket.prototype.dispatchEvent = function(event) { 137 if (!('__events' in this)) throw 'UNSPECIFIED_EVENT_TYPE_ERR'; 138 if (!(event.type in this.__events)) throw 'UNSPECIFIED_EVENT_TYPE_ERR'; 139 140 for (var i = 0, l = this.__events[event.type].length; i < l; ++ i) { 141 this.__events[event.type][i](event); 142 if (event.cancelBubble) break; 143 } 144 145 if (false !== event.returnValue && 146 'function' == typeof this.__events[event.type].defaultHandler) 147 { 148 this.__events[event.type].defaultHandler(event); 149 } 150 }; 151 152 /** 153 * Handle an event from flash. Do any websocket-specific 154 * handling before passing the event off to the event handlers. 155 * @param {Object} event 156 */ 157 WebSocket.prototype.__handleEvent = function(event) { 158 if ("readyState" in event) { 159 this.readyState = event.readyState; 160 } 161 162 try { 163 if (event.type == "open") { 164 this.onopen && this.onopen(); 165 } else if (event.type == "close") { 166 this.onclose && this.onclose(); 167 } else if (event.type == "error") { 168 this.onerror && this.onerror(event); 169 } else if (event.type == "message") { 170 if (this.onmessage) { 171 var data = decodeURIComponent(event.message); 172 var e; 173 if (window.MessageEvent && !window.opera) { 174 e = document.createEvent("MessageEvent"); 175 e.initMessageEvent("message", false, false, data, null, null, window, null); 176 } else { 177 // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes. 178 e = {data: data}; 179 } 180 this.onmessage(e); 181 } 182 183 } else { 184 throw "unknown event type: " + event.type; 185 } 186 } catch (e) { 187 console.error(e.toString()); 188 } 189 }; 190 191 /** 192 * @param {object} object 193 * @param {string} type 194 */ 195 WebSocket.prototype.__createEventHandler = function(object, type) { 196 return function(data) { 197 var event = new WebSocketEvent(); 198 event.initEvent(type, true, true); 199 event.target = event.currentTarget = object; 200 for (var key in data) { 201 event[key] = data[key]; 202 } 203 object.dispatchEvent(event, arguments); 204 }; 205 }; 206 207 /** 208 * Define the WebSocket readyState enumeration. 209 */ 210 WebSocket.CONNECTING = 0; 211 WebSocket.OPEN = 1; 212 WebSocket.CLOSING = 2; 213 WebSocket.CLOSED = 3; 214 215 WebSocket.__flash = null; 216 WebSocket.__instances = {}; 217 WebSocket.__tasks = []; 218 WebSocket.__nextId = 0; 219 220 /** 221 * Loads WebSocketMain.swf and creates WebSocketMain object in Flash. 222 */ 223 WebSocket.__initialize = function() { 224 if (WebSocket.__flash) return; 225 226 if (WebSocket.__swfLocation) { 227 // For backword compatibility. 228 window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation; 229 } 230 if (!window.WEB_SOCKET_SWF_LOCATION) { 231 console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf"); 232 return; 233 } 234 var container = document.createElement("div"); 235 container.id = "webSocketContainer"; 236 // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents 237 // Flash from loading at least in IE. So we move it out of the screen at (-100, -100). 238 // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash 239 // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is 240 // the best we can do as far as we know now. 241 container.style.position = "absolute"; 242 if (WebSocket.__isFlashLite()) { 243 container.style.left = "0px"; 244 container.style.top = "0px"; 245 } else { 246 container.style.left = "-100px"; 247 container.style.top = "-100px"; 248 } 249 var holder = document.createElement("div"); 250 holder.id = "webSocketFlash"; 251 container.appendChild(holder); 252 document.body.appendChild(container); 253 // See this article for hasPriority: 254 // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html 255 swfobject.embedSWF( 256 WEB_SOCKET_SWF_LOCATION, 257 "webSocketFlash", 258 "1" /* width */, 259 "1" /* height */, 260 "10.0.0" /* SWF version */, 261 null, 262 null, 263 {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"}, 264 null, 265 function(e) { 266 if (!e.success) { 267 console.error("[WebSocket] swfobject.embedSWF failed"); 268 } 269 }); 270 }; 271 272 /** 273 * Load a new flash security policy file. 274 * @param {string} url 275 */ 276 WebSocket.loadFlashPolicyFile = function(url){ 277 WebSocket.__addTask(function() { 278 WebSocket.__flash.loadManualPolicyFile(url); 279 }); 280 }; 281 282 /** 283 * Called by flash to notify js that it's fully loaded and ready 284 * for communication. 285 */ 286 WebSocket.__onFlashInitialized = function() { 287 // We need to set a timeout here to avoid round-trip calls 288 // to flash during the initialization process. 289 setTimeout(function() { 290 WebSocket.__flash = document.getElementById("webSocketFlash"); 291 WebSocket.__flash.setCallerUrl(location.href); 292 WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG); 293 for (var i = 0; i < WebSocket.__tasks.length; ++i) { 294 WebSocket.__tasks[i](); 295 } 296 WebSocket.__tasks = []; 297 }, 0); 298 }; 299 300 /** 301 * Called by flash to dispatch an event to a web socket. 302 * @param {object} eventObj A web socket event dispatched from flash. 303 */ 304 WebSocket.__onFlashEvent = function() { 305 setTimeout(function() { 306 // Gets events using receiveEvents() instead of getting it from event object 307 // of Flash event. This is to make sure to keep message order. 308 // It seems sometimes Flash events don't arrive in the same order as they are sent. 309 var events = WebSocket.__flash.receiveEvents(); 310 for (var i = 0; i < events.length; ++i) { 311 WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]); 312 } 313 }, 0); 314 return true; 315 }; 316 317 // called from Flash 318 WebSocket.__log = function(message) { 319 console.log(decodeURIComponent(message)); 320 }; 321 322 // called from Flash 323 WebSocket.__error = function(message) { 324 console.error(decodeURIComponent(message)); 325 }; 326 327 WebSocket.__addTask = function(task) { 328 if (WebSocket.__flash) { 329 task(); 330 } else { 331 WebSocket.__tasks.push(task); 332 } 333 }; 334 335 /** 336 * Test if the browser is running flash lite. 337 * @return {boolean} True if flash lite is running, false otherwise. 338 */ 339 WebSocket.__isFlashLite = function() { 340 if (!window.navigator || !window.navigator.mimeTypes) { 341 return false; 342 } 343 var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"]; 344 if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) { 345 return false; 346 } 347 return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false; 348 }; 349 350 /** 351 * Basic implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-interface">DOM 2 EventInterface</a>} 352 * 353 * @class 354 * @constructor 355 */ 356 function WebSocketEvent(){} 357 358 /** 359 * 360 * @type boolean 361 */ 362 WebSocketEvent.prototype.cancelable = true; 363 364 /** 365 * 366 * @type boolean 367 */ 368 WebSocketEvent.prototype.cancelBubble = false; 369 370 /** 371 * 372 * @return void 373 */ 374 WebSocketEvent.prototype.preventDefault = function() { 375 if (this.cancelable) { 376 this.returnValue = false; 377 } 378 }; 379 380 /** 381 * 382 * @return void 383 */ 384 WebSocketEvent.prototype.stopPropagation = function() { 385 this.cancelBubble = true; 386 }; 387 388 /** 389 * 390 * @param {string} eventTypeArg 391 * @param {boolean} canBubbleArg 392 * @param {boolean} cancelableArg 393 * @return void 394 */ 395 WebSocketEvent.prototype.initEvent = function(eventTypeArg, canBubbleArg, cancelableArg) { 396 this.type = eventTypeArg; 397 this.cancelable = cancelableArg; 398 this.timeStamp = new Date(); 399 }; 400 401 if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) { 402 if (window.addEventListener) { 403 window.addEventListener("load", function(){ 404 WebSocket.__initialize(); 405 }, false); 406 } else { 407 window.attachEvent("onload", function(){ 408 WebSocket.__initialize(); 409 }); 410 } 411 } 412 413})();