PageRenderTime 25ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/nodechat/lib/vendor/web-socket-js/web_socket.js

https://github.com/sigmonky/LivingRoom
JavaScript | 376 lines | 245 code | 40 blank | 91 comment | 79 complexity | c571dc6223fe3edcb3f4a91ce254bdd7 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("9.0.0")) {
  12. console.error("Flash Player is not installed.");
  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. WebSocket = function(url, protocol, proxyHost, proxyPort, headers) {
  22. var self = this;
  23. self.readyState = WebSocket.CONNECTING;
  24. self.bufferedAmount = 0;
  25. // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
  26. // Otherwise, when onopen fires immediately, onopen is called before it is set.
  27. setTimeout(function() {
  28. WebSocket.__addTask(function() {
  29. self.__createFlash(url, protocol, proxyHost, proxyPort, headers);
  30. });
  31. }, 0);
  32. };
  33. WebSocket.prototype.__createFlash = function(url, protocol, proxyHost, proxyPort, headers) {
  34. var self = this;
  35. self.__flash =
  36. WebSocket.__flash.create(url, protocol, proxyHost || null, proxyPort || 0, headers || null);
  37. self.__flash.addEventListener("event", function(fe) {
  38. // Uses setTimeout() to workaround the error:
  39. // > You are trying to call recursively into the Flash Player which is not allowed.
  40. setTimeout(function() { self.__handleEvents(); }, 0);
  41. });
  42. //console.log("[WebSocket] Flash object is ready");
  43. };
  44. WebSocket.prototype.send = function(data) {
  45. if (!this.__flash || this.readyState == WebSocket.CONNECTING) {
  46. throw "INVALID_STATE_ERR: Web Socket connection has not been established";
  47. }
  48. // We use encodeURIComponent() here, because FABridge doesn't work if
  49. // the argument includes some characters. We don't use escape() here
  50. // because of this:
  51. // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
  52. // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
  53. // preserve all Unicode characters either e.g. "\uffff" in Firefox.
  54. var result = this.__flash.send(encodeURIComponent(data));
  55. if (result < 0) { // success
  56. return true;
  57. } else {
  58. this.bufferedAmount += result;
  59. return false;
  60. }
  61. };
  62. WebSocket.prototype.close = function() {
  63. var self = this;
  64. if (!self.__flash) return;
  65. if (self.readyState == WebSocket.CLOSED || self.readyState == WebSocket.CLOSING) return;
  66. self.__flash.close();
  67. // Sets/calls them manually here because Flash WebSocketConnection.close cannot fire events
  68. // which causes weird error:
  69. // > You are trying to call recursively into the Flash Player which is not allowed.
  70. self.readyState = WebSocket.CLOSED;
  71. if (self.__timer) clearInterval(self.__timer);
  72. if (self.onclose) {
  73. // Make it asynchronous so that it looks more like an actual
  74. // close event
  75. setTimeout(self.onclose, 0);
  76. }
  77. };
  78. /**
  79. * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
  80. *
  81. * @param {string} type
  82. * @param {function} listener
  83. * @param {boolean} useCapture !NB Not implemented yet
  84. * @return void
  85. */
  86. WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
  87. if (!('__events' in this)) {
  88. this.__events = {};
  89. }
  90. if (!(type in this.__events)) {
  91. this.__events[type] = [];
  92. if ('function' == typeof this['on' + type]) {
  93. this.__events[type].defaultHandler = this['on' + type];
  94. this['on' + type] = this.__createEventHandler(this, type);
  95. }
  96. }
  97. this.__events[type].push(listener);
  98. };
  99. /**
  100. * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
  101. *
  102. * @param {string} type
  103. * @param {function} listener
  104. * @param {boolean} useCapture NB! Not implemented yet
  105. * @return void
  106. */
  107. WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
  108. if (!('__events' in this)) {
  109. this.__events = {};
  110. }
  111. if (!(type in this.__events)) return;
  112. for (var i = this.__events.length; i > -1; --i) {
  113. if (listener === this.__events[type][i]) {
  114. this.__events[type].splice(i, 1);
  115. break;
  116. }
  117. }
  118. };
  119. /**
  120. * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
  121. *
  122. * @param {WebSocketEvent} event
  123. * @return void
  124. */
  125. WebSocket.prototype.dispatchEvent = function(event) {
  126. if (!('__events' in this)) throw 'UNSPECIFIED_EVENT_TYPE_ERR';
  127. if (!(event.type in this.__events)) throw 'UNSPECIFIED_EVENT_TYPE_ERR';
  128. for (var i = 0, l = this.__events[event.type].length; i < l; ++ i) {
  129. this.__events[event.type][i](event);
  130. if (event.cancelBubble) break;
  131. }
  132. if (false !== event.returnValue &&
  133. 'function' == typeof this.__events[event.type].defaultHandler)
  134. {
  135. this.__events[event.type].defaultHandler(event);
  136. }
  137. };
  138. WebSocket.prototype.__handleEvents = function() {
  139. // Gets events using receiveEvents() instead of getting it from event object
  140. // of Flash event. This is to make sure to keep message order.
  141. // It seems sometimes Flash events don't arrive in the same order as they are sent.
  142. var events = this.__flash.receiveEvents();
  143. for (var i = 0; i < events.length; i++) {
  144. try {
  145. var event = events[i];
  146. if ("readyState" in event) {
  147. this.readyState = event.readyState;
  148. }
  149. if (event.type == "open") {
  150. if (this.__timer) clearInterval(this.__timer);
  151. if (window.opera) {
  152. // Workaround for weird behavior of Opera which sometimes drops events.
  153. this.__timer = setInterval(function () {
  154. this.__handleEvents();
  155. }, 500);
  156. }
  157. if (this.onopen) this.onopen();
  158. } else if (event.type == "close") {
  159. if (this.__timer) clearInterval(this.__timer);
  160. if (this.onclose) this.onclose();
  161. } else if (event.type == "message") {
  162. if (this.onmessage) {
  163. var data = decodeURIComponent(event.data);
  164. var e;
  165. if (window.MessageEvent && !window.opera) {
  166. e = document.createEvent("MessageEvent");
  167. e.initMessageEvent("message", false, false, data, null, null, window, null);
  168. } else {
  169. // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
  170. e = {data: data};
  171. }
  172. this.onmessage(e);
  173. }
  174. } else if (event.type == "error") {
  175. if (this.__timer) clearInterval(this.__timer);
  176. if (this.onerror) this.onerror();
  177. } else {
  178. throw "unknown event type: " + event.type;
  179. }
  180. } catch (e) {
  181. console.error(e.toString());
  182. }
  183. }
  184. };
  185. /**
  186. * @param {object} object
  187. * @param {string} type
  188. */
  189. WebSocket.prototype.__createEventHandler = function(object, type) {
  190. return function(data) {
  191. var event = new WebSocketEvent();
  192. event.initEvent(type, true, true);
  193. event.target = event.currentTarget = object;
  194. for (var key in data) {
  195. event[key] = data[key];
  196. }
  197. object.dispatchEvent(event, arguments);
  198. };
  199. };
  200. /**
  201. * Basic implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-interface">DOM 2 EventInterface</a>}
  202. *
  203. * @class
  204. * @constructor
  205. */
  206. function WebSocketEvent(){}
  207. /**
  208. *
  209. * @type boolean
  210. */
  211. WebSocketEvent.prototype.cancelable = true;
  212. /**
  213. *
  214. * @type boolean
  215. */
  216. WebSocketEvent.prototype.cancelBubble = false;
  217. /**
  218. *
  219. * @return void
  220. */
  221. WebSocketEvent.prototype.preventDefault = function() {
  222. if (this.cancelable) {
  223. this.returnValue = false;
  224. }
  225. };
  226. /**
  227. *
  228. * @return void
  229. */
  230. WebSocketEvent.prototype.stopPropagation = function() {
  231. this.cancelBubble = true;
  232. };
  233. /**
  234. *
  235. * @param {string} eventTypeArg
  236. * @param {boolean} canBubbleArg
  237. * @param {boolean} cancelableArg
  238. * @return void
  239. */
  240. WebSocketEvent.prototype.initEvent = function(eventTypeArg, canBubbleArg, cancelableArg) {
  241. this.type = eventTypeArg;
  242. this.cancelable = cancelableArg;
  243. this.timeStamp = new Date();
  244. };
  245. WebSocket.CONNECTING = 0;
  246. WebSocket.OPEN = 1;
  247. WebSocket.CLOSING = 2;
  248. WebSocket.CLOSED = 3;
  249. WebSocket.__tasks = [];
  250. WebSocket.loadFlashPolicyFile = function(url) {
  251. WebSocket.__addTask(function() {
  252. WebSocket.__flash.loadManualPolicyFile(url);
  253. });
  254. }
  255. WebSocket.__initialize = function() {
  256. if (WebSocket.__swfLocation) {
  257. // For backword compatibility.
  258. window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
  259. }
  260. if (!window.WEB_SOCKET_SWF_LOCATION) {
  261. console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
  262. return;
  263. }
  264. var container = document.createElement("div");
  265. container.id = "webSocketContainer";
  266. // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
  267. // Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
  268. // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
  269. // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
  270. // the best we can do as far as we know now.
  271. container.style.position = "absolute";
  272. if (WebSocket.__isFlashLite()) {
  273. container.style.left = "0px";
  274. container.style.top = "0px";
  275. } else {
  276. container.style.left = "-100px";
  277. container.style.top = "-100px";
  278. }
  279. var holder = document.createElement("div");
  280. holder.id = "webSocketFlash";
  281. container.appendChild(holder);
  282. document.body.appendChild(container);
  283. // See this article for hasPriority:
  284. // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
  285. swfobject.embedSWF(
  286. WEB_SOCKET_SWF_LOCATION, "webSocketFlash",
  287. "1" /* width */, "1" /* height */, "9.0.0" /* SWF version */,
  288. null, {bridgeName: "webSocket"}, {hasPriority: true, allowScriptAccess: "always"}, null,
  289. function(e) {
  290. if (!e.success) console.error("[WebSocket] swfobject.embedSWF failed");
  291. }
  292. );
  293. FABridge.addInitializationCallback("webSocket", function() {
  294. try {
  295. //console.log("[WebSocket] FABridge initializad");
  296. WebSocket.__flash = FABridge.webSocket.root();
  297. WebSocket.__flash.setCallerUrl(location.href);
  298. WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
  299. for (var i = 0; i < WebSocket.__tasks.length; ++i) {
  300. WebSocket.__tasks[i]();
  301. }
  302. WebSocket.__tasks = [];
  303. } catch (e) {
  304. console.error("[WebSocket] " + e.toString());
  305. }
  306. });
  307. };
  308. WebSocket.__addTask = function(task) {
  309. if (WebSocket.__flash) {
  310. task();
  311. } else {
  312. WebSocket.__tasks.push(task);
  313. }
  314. };
  315. WebSocket.__isFlashLite = function() {
  316. if (!window.navigator || !window.navigator.mimeTypes) return false;
  317. var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
  318. if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) return false;
  319. return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
  320. };
  321. // called from Flash
  322. window.webSocketLog = function(message) {
  323. console.log(decodeURIComponent(message));
  324. };
  325. // called from Flash
  326. window.webSocketError = function(message) {
  327. console.error(decodeURIComponent(message));
  328. };
  329. if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
  330. if (window.addEventListener) {
  331. window.addEventListener("load", WebSocket.__initialize, false);
  332. } else {
  333. window.attachEvent("onload", WebSocket.__initialize);
  334. }
  335. }
  336. })();