/js/lib/Socket.IO-node/support/socket.io-client/lib/vendor/web-socket-js/web_socket.js

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs · JavaScript · 413 lines · 250 code · 35 blank · 128 comment · 72 complexity · 5e53feb522d6b7ac8a62712ab6283d70 MD5 · raw file

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