websocket-rack-noodles 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ /* SWFObject v2.2 <http://code.google.com/p/swfobject/>
2
+ is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
3
+ */
4
+ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
@@ -0,0 +1,388 @@
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: function(){ }, error: function(){ }};
12
+
13
+ if (!swfobject.hasFlashPlayerVersion("9.0.0")) {
14
+ console.error("Flash Player is not installed.");
15
+ return;
16
+ }
17
+ if (location.protocol == "file:") {
18
+ console.error(
19
+ "WARNING: web-socket-js doesn't work in file:///... URL " +
20
+ "unless you set Flash Security Settings properly. " +
21
+ "Open the page via Web server i.e. http://...");
22
+ }
23
+
24
+ WebSocket = function(url, protocol, proxyHost, proxyPort, headers) {
25
+ var self = this;
26
+ self.readyState = WebSocket.CONNECTING;
27
+ self.bufferedAmount = 0;
28
+ // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
29
+ // Otherwise, when onopen fires immediately, onopen is called before it is set.
30
+ setTimeout(function() {
31
+ WebSocket.__addTask(function() {
32
+ self.__createFlash(url, protocol, proxyHost, proxyPort, headers);
33
+ });
34
+ }, 1);
35
+ }
36
+
37
+ WebSocket.prototype.__createFlash = function(url, protocol, proxyHost, proxyPort, headers) {
38
+ var self = this;
39
+ self.__flash =
40
+ WebSocket.__flash.create(url, protocol, proxyHost || null, proxyPort || 0, headers || null);
41
+
42
+ self.__flash.addEventListener("open", function(fe) {
43
+ try {
44
+ self.readyState = self.__flash.getReadyState();
45
+ if (self.__timer) clearInterval(self.__timer);
46
+ if (window.opera) {
47
+ // Workaround for weird behavior of Opera which sometimes drops events.
48
+ self.__timer = setInterval(function () {
49
+ self.__handleMessages();
50
+ }, 500);
51
+ }
52
+ if (self.onopen) self.onopen();
53
+ } catch (e) {
54
+ console.error(e.toString());
55
+ }
56
+ });
57
+
58
+ self.__flash.addEventListener("close", function(fe) {
59
+ try {
60
+ self.readyState = self.__flash.getReadyState();
61
+ if (self.__timer) clearInterval(self.__timer);
62
+ if (self.onclose) self.onclose();
63
+ } catch (e) {
64
+ console.error(e.toString());
65
+ }
66
+ });
67
+
68
+ self.__flash.addEventListener("message", function() {
69
+ try {
70
+ self.__handleMessages();
71
+ } catch (e) {
72
+ console.error(e.toString());
73
+ }
74
+ });
75
+
76
+ self.__flash.addEventListener("error", function(fe) {
77
+ try {
78
+ if (self.__timer) clearInterval(self.__timer);
79
+ if (self.onerror) self.onerror();
80
+ } catch (e) {
81
+ console.error(e.toString());
82
+ }
83
+ });
84
+
85
+ self.__flash.addEventListener("stateChange", function(fe) {
86
+ try {
87
+ self.readyState = self.__flash.getReadyState();
88
+ self.bufferedAmount = fe.getBufferedAmount();
89
+ } catch (e) {
90
+ console.error(e.toString());
91
+ }
92
+ });
93
+
94
+ //console.log("[WebSocket] Flash object is ready");
95
+ };
96
+
97
+ WebSocket.prototype.send = function(data) {
98
+ if (this.__flash) {
99
+ this.readyState = this.__flash.getReadyState();
100
+ }
101
+ if (!this.__flash || this.readyState == WebSocket.CONNECTING) {
102
+ throw "INVALID_STATE_ERR: Web Socket connection has not been established";
103
+ }
104
+ // We use encodeURIComponent() here, because FABridge doesn't work if
105
+ // the argument includes some characters. We don't use escape() here
106
+ // because of this:
107
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
108
+ // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
109
+ // preserve all Unicode characters either e.g. "\uffff" in Firefox.
110
+ var result = this.__flash.send(encodeURIComponent(data));
111
+ if (result < 0) { // success
112
+ return true;
113
+ } else {
114
+ this.bufferedAmount = result;
115
+ return false;
116
+ }
117
+ };
118
+
119
+ WebSocket.prototype.close = function() {
120
+ var self = this;
121
+ if (!self.__flash) return;
122
+ self.readyState = self.__flash.getReadyState();
123
+ if (self.readyState == WebSocket.CLOSED || self.readyState == WebSocket.CLOSING) return;
124
+ self.__flash.close();
125
+ // Sets/calls them manually here because Flash WebSocketConnection.close cannot fire events
126
+ // which causes weird error:
127
+ // > You are trying to call recursively into the Flash Player which is not allowed.
128
+ self.readyState = WebSocket.CLOSED;
129
+ if (self.__timer) clearInterval(self.__timer);
130
+ if (self.onclose) {
131
+ // Make it asynchronous so that it looks more like an actual
132
+ // close event
133
+ setTimeout(self.onclose, 1);
134
+ }
135
+ };
136
+
137
+ /**
138
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
139
+ *
140
+ * @param {string} type
141
+ * @param {function} listener
142
+ * @param {boolean} useCapture !NB Not implemented yet
143
+ * @return void
144
+ */
145
+ WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
146
+ if (!('__events' in this)) {
147
+ this.__events = {};
148
+ }
149
+ if (!(type in this.__events)) {
150
+ this.__events[type] = [];
151
+ if ('function' == typeof this['on' + type]) {
152
+ this.__events[type].defaultHandler = this['on' + type];
153
+ this['on' + type] = this.__createEventHandler(this, type);
154
+ }
155
+ }
156
+ this.__events[type].push(listener);
157
+ };
158
+
159
+ /**
160
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
161
+ *
162
+ * @param {string} type
163
+ * @param {function} listener
164
+ * @param {boolean} useCapture NB! Not implemented yet
165
+ * @return void
166
+ */
167
+ WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
168
+ if (!('__events' in this)) {
169
+ this.__events = {};
170
+ }
171
+ if (!(type in this.__events)) return;
172
+ for (var i = this.__events.length; i > -1; --i) {
173
+ if (listener === this.__events[type][i]) {
174
+ this.__events[type].splice(i, 1);
175
+ break;
176
+ }
177
+ }
178
+ };
179
+
180
+ /**
181
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
182
+ *
183
+ * @param {WebSocketEvent} event
184
+ * @return void
185
+ */
186
+ WebSocket.prototype.dispatchEvent = function(event) {
187
+ if (!('__events' in this)) throw 'UNSPECIFIED_EVENT_TYPE_ERR';
188
+ if (!(event.type in this.__events)) throw 'UNSPECIFIED_EVENT_TYPE_ERR';
189
+
190
+ for (var i = 0, l = this.__events[event.type].length; i < l; ++ i) {
191
+ this.__events[event.type][i](event);
192
+ if (event.cancelBubble) break;
193
+ }
194
+
195
+ if (false !== event.returnValue &&
196
+ 'function' == typeof this.__events[event.type].defaultHandler)
197
+ {
198
+ this.__events[event.type].defaultHandler(event);
199
+ }
200
+ };
201
+
202
+ WebSocket.prototype.__handleMessages = function() {
203
+ // Gets data using readSocketData() instead of getting it from event object
204
+ // of Flash event. This is to make sure to keep message order.
205
+ // It seems sometimes Flash events don't arrive in the same order as they are sent.
206
+ var arr = this.__flash.readSocketData();
207
+ for (var i = 0; i < arr.length; i++) {
208
+ var data = decodeURIComponent(arr[i]);
209
+ try {
210
+ if (this.onmessage) {
211
+ var e;
212
+ if (window.MessageEvent && !window.opera) {
213
+ e = document.createEvent("MessageEvent");
214
+ e.initMessageEvent("message", false, false, data, null, null, window, null);
215
+ } else { // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes
216
+ e = {data: data};
217
+ }
218
+ this.onmessage(e);
219
+ }
220
+ } catch (e) {
221
+ console.error(e.toString());
222
+ }
223
+ }
224
+ };
225
+
226
+ /**
227
+ * @param {object} object
228
+ * @param {string} type
229
+ */
230
+ WebSocket.prototype.__createEventHandler = function(object, type) {
231
+ return function(data) {
232
+ var event = new WebSocketEvent();
233
+ event.initEvent(type, true, true);
234
+ event.target = event.currentTarget = object;
235
+ for (var key in data) {
236
+ event[key] = data[key];
237
+ }
238
+ object.dispatchEvent(event, arguments);
239
+ };
240
+ }
241
+
242
+ /**
243
+ * Basic implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-interface">DOM 2 EventInterface</a>}
244
+ *
245
+ * @class
246
+ * @constructor
247
+ */
248
+ function WebSocketEvent(){}
249
+
250
+ /**
251
+ *
252
+ * @type boolean
253
+ */
254
+ WebSocketEvent.prototype.cancelable = true;
255
+
256
+ /**
257
+ *
258
+ * @type boolean
259
+ */
260
+ WebSocketEvent.prototype.cancelBubble = false;
261
+
262
+ /**
263
+ *
264
+ * @return void
265
+ */
266
+ WebSocketEvent.prototype.preventDefault = function() {
267
+ if (this.cancelable) {
268
+ this.returnValue = false;
269
+ }
270
+ };
271
+
272
+ /**
273
+ *
274
+ * @return void
275
+ */
276
+ WebSocketEvent.prototype.stopPropagation = function() {
277
+ this.cancelBubble = true;
278
+ };
279
+
280
+ /**
281
+ *
282
+ * @param {string} eventTypeArg
283
+ * @param {boolean} canBubbleArg
284
+ * @param {boolean} cancelableArg
285
+ * @return void
286
+ */
287
+ WebSocketEvent.prototype.initEvent = function(eventTypeArg, canBubbleArg, cancelableArg) {
288
+ this.type = eventTypeArg;
289
+ this.cancelable = cancelableArg;
290
+ this.timeStamp = new Date();
291
+ };
292
+
293
+
294
+ WebSocket.CONNECTING = 0;
295
+ WebSocket.OPEN = 1;
296
+ WebSocket.CLOSING = 2;
297
+ WebSocket.CLOSED = 3;
298
+
299
+ WebSocket.__tasks = [];
300
+
301
+ WebSocket.__initialize = function() {
302
+ if (WebSocket.__swfLocation) {
303
+ // For backword compatibility.
304
+ window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
305
+ }
306
+ if (!window.WEB_SOCKET_SWF_LOCATION) {
307
+ console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
308
+ return;
309
+ }
310
+ var container = document.createElement("div");
311
+ container.id = "webSocketContainer";
312
+ // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
313
+ // Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
314
+ // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
315
+ // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
316
+ // the best we can do as far as we know now.
317
+ container.style.position = "absolute";
318
+ if (WebSocket.__isFlashLite()) {
319
+ container.style.left = "0px";
320
+ container.style.top = "0px";
321
+ } else {
322
+ container.style.left = "-100px";
323
+ container.style.top = "-100px";
324
+ }
325
+ var holder = document.createElement("div");
326
+ holder.id = "webSocketFlash";
327
+ container.appendChild(holder);
328
+ document.body.appendChild(container);
329
+ // See this article for hasPriority:
330
+ // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
331
+ swfobject.embedSWF(
332
+ WEB_SOCKET_SWF_LOCATION, "webSocketFlash",
333
+ "1" /* width */, "1" /* height */, "9.0.0" /* SWF version */,
334
+ null, {bridgeName: "webSocket"}, {hasPriority: true, allowScriptAccess: "always"}, null,
335
+ function(e) {
336
+ if (!e.success) console.error("[WebSocket] swfobject.embedSWF failed");
337
+ }
338
+ );
339
+ FABridge.addInitializationCallback("webSocket", function() {
340
+ try {
341
+ //console.log("[WebSocket] FABridge initializad");
342
+ WebSocket.__flash = FABridge.webSocket.root();
343
+ WebSocket.__flash.setCallerUrl(location.href);
344
+ WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
345
+ for (var i = 0; i < WebSocket.__tasks.length; ++i) {
346
+ WebSocket.__tasks[i]();
347
+ }
348
+ WebSocket.__tasks = [];
349
+ } catch (e) {
350
+ console.error("[WebSocket] " + e.toString());
351
+ }
352
+ });
353
+ };
354
+
355
+ WebSocket.__addTask = function(task) {
356
+ if (WebSocket.__flash) {
357
+ task();
358
+ } else {
359
+ WebSocket.__tasks.push(task);
360
+ }
361
+ };
362
+
363
+ WebSocket.__isFlashLite = function() {
364
+ if (!window.navigator || !window.navigator.mimeTypes) return false;
365
+ var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
366
+ if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) return false;
367
+ return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
368
+ };
369
+
370
+ // called from Flash
371
+ window.webSocketLog = function(message) {
372
+ console.log(decodeURIComponent(message));
373
+ };
374
+
375
+ // called from Flash
376
+ window.webSocketError = function(message) {
377
+ console.error(decodeURIComponent(message));
378
+ };
379
+
380
+ if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
381
+ if (window.addEventListener) {
382
+ window.addEventListener("load", WebSocket.__initialize, false);
383
+ } else {
384
+ window.attachEvent("onload", WebSocket.__initialize);
385
+ }
386
+ }
387
+
388
+ })();
@@ -0,0 +1,56 @@
1
+ module Rack
2
+ module WebSocket
3
+ class Application
4
+
5
+ DEFAULT_OPTIONS = {}
6
+
7
+ class << self
8
+ attr_accessor :websocket_handler
9
+ end
10
+
11
+ # Standard WebSocket calls
12
+ def on_open(env); end
13
+ def on_message(env, msg); end
14
+ def on_close(env); end
15
+ def on_error(env, error); end
16
+
17
+ # Initializer
18
+ def initialize(options = {})
19
+ @options = DEFAULT_OPTIONS.merge(options)
20
+ end
21
+
22
+ # Detect handler and duplicate it's instance
23
+ def call(env)
24
+ detect_handler(env)
25
+ dup._call(env)
26
+ end
27
+
28
+ # Forward call to duplicated handler
29
+ def _call(env)
30
+ websocket_handler.call(env)
31
+ end
32
+
33
+ # Forward all missing methods to handler
34
+ def method_missing(sym, *args, &block)
35
+ if websocket_handler && websocket_handler.respond_to?(sym)
36
+ websocket_handler.send sym, *args, &block
37
+ else
38
+ super
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ # Detect handler
45
+ def detect_handler(env)
46
+ self.class.websocket_handler ||= Handler.detect(env)
47
+ end
48
+
49
+ # Create and cache handler for current server
50
+ def websocket_handler
51
+ @websocket_handler ||= self.class.websocket_handler.new(self, @options || {})
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,67 @@
1
+ module Rack
2
+ module WebSocket
3
+ module Extensions
4
+ module Common
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+ alias :receive_data_without_websocket :receive_data
9
+ alias :receive_data :receive_data_with_websocket
10
+
11
+ alias :unbind_without_websocket :unbind
12
+ alias :unbind :unbind_with_websocket
13
+
14
+ alias :receive_data_without_flash_policy_file :receive_data
15
+ alias :receive_data :receive_data_with_flash_policy_file
16
+ end
17
+ end
18
+
19
+ attr_accessor :websocket
20
+
21
+ # Is this connection WebSocket?
22
+ def websocket?
23
+ !self.websocket.nil?
24
+ end
25
+
26
+ # Skip default receive_data if this is
27
+ # WebSocket connection
28
+ def receive_data_with_websocket(data)
29
+ if self.websocket?
30
+ self.websocket.receive_data(data)
31
+ else
32
+ receive_data_without_websocket(data)
33
+ end
34
+ end
35
+
36
+ # Skip standard unbind it this is
37
+ # WebSocket connection
38
+ def unbind_with_websocket
39
+ if self.websocket?
40
+ self.websocket.unbind
41
+ else
42
+ unbind_without_websocket
43
+ end
44
+ end
45
+
46
+ # Send flash policy file if requested
47
+ def receive_data_with_flash_policy_file(data)
48
+ # thin require data to be proper http request - in it's not
49
+ # then @request.parse raises exception and data isn't parsed
50
+ # by futher methods. Here we only check if it is flash
51
+ # policy file request ("<policy-file-request/>\000") and
52
+ # if so then flash policy file is returned. if not then
53
+ # rest of request is handled.
54
+ if (data == "<policy-file-request/>\000")
55
+ file = '<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'
56
+ # ignore errors - we will close this anyway
57
+ send_data(file) rescue nil
58
+ close_connection_after_writing
59
+ else
60
+ receive_data_without_flash_policy_file(data)
61
+ end
62
+ end
63
+
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,24 @@
1
+ module Rack
2
+ module WebSocket
3
+ module Extensions
4
+ module Thin
5
+ module Connection
6
+
7
+ def self.included(base)
8
+ base.class_eval do
9
+ alias :pre_process_without_websocket :pre_process
10
+ alias :pre_process :pre_process_with_websocket
11
+ end
12
+ end
13
+
14
+ # Set 'async.connection' Rack env
15
+ def pre_process_with_websocket
16
+ @request.env['async.connection'] = self
17
+ pre_process_without_websocket
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ module Rack
2
+ module WebSocket
3
+ module Extensions
4
+ module Thin
5
+
6
+ autoload :Connection, "#{::File.dirname(__FILE__)}/thin/connection"
7
+
8
+ def self.apply!
9
+ ::Thin::Connection.send(:include, ::Rack::WebSocket::Extensions::Common)
10
+ ::Thin::Connection.send(:include, ::Rack::WebSocket::Extensions::Thin::Connection)
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ module Rack
2
+ module WebSocket
3
+ module Extensions
4
+
5
+ autoload :Common, "#{ROOT_PATH}/websocket/extensions/common"
6
+ autoload :Thin, "#{ROOT_PATH}/websocket/extensions/thin"
7
+
8
+ def self.apply!
9
+ Thin.apply! if defined?(::Thin)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,83 @@
1
+ require 'addressable/uri'
2
+
3
+ module Rack
4
+ module WebSocket
5
+ module Handler
6
+ class Base
7
+ class Connection < ::EventMachine::WebSocket::Connection
8
+
9
+ #########################
10
+ ### EventMachine part ###
11
+ #########################
12
+
13
+ # Overwrite new from EventMachine
14
+ # we need to skip standard procedure called
15
+ # when socket is created - this is just a stub
16
+ def self.new(*args)
17
+ instance = allocate
18
+ instance.__send__(:initialize, *args)
19
+ instance
20
+ end
21
+
22
+ # Overwrite send_data from EventMachine
23
+ # delegate send_data to rack server
24
+ def send_data(*args)
25
+ EM.next_tick do
26
+ @socket.send_data(*args)
27
+ end
28
+ end
29
+
30
+ # Overwrite close_connection from EventMachine
31
+ # delegate close_connection to rack server
32
+ def close_connection(*args)
33
+ EM.next_tick do
34
+ @socket.close_connection(*args)
35
+ end
36
+ end
37
+
38
+ #########################
39
+ ### EM-WebSocket part ###
40
+ #########################
41
+
42
+ # Overwrite triggers from em-websocket
43
+ def trigger_on_message(msg); @app.on_message(msg); end
44
+ def trigger_on_open; @app.on_open; end
45
+ def trigger_on_close; @app.on_close; end
46
+ def trigger_on_error(error); @app.on_error(error); true; end
47
+
48
+ # Overwrite initialize from em-websocket
49
+ # set all standard options and disable
50
+ # EM connection inactivity timeout
51
+ def initialize(app, socket, options = {})
52
+ @app = app
53
+ @socket = socket
54
+ @options = options
55
+ @debug = options[:debug] || false
56
+ @ssl = socket.backend.respond_to?(:ssl?) && socket.backend.ssl?
57
+
58
+ socket.websocket = self
59
+ socket.comm_inactivity_timeout = 0
60
+
61
+ debug [:initialize]
62
+ end
63
+
64
+ # Overwrite dispath from em-websocket
65
+ # we already have request headers parsed so
66
+ # we can skip it and call build_with_request
67
+ def dispatch(data)
68
+ return false if data.nil?
69
+ debug [:inbound_headers, data]
70
+ @handler = EventMachine::WebSocket::HandlerFactory.build_with_request(self, data, data['Body'], @ssl, @debug)
71
+ unless @handler
72
+ # The whole header has not been received yet.
73
+ return false
74
+ end
75
+ @handler.run
76
+ return true
77
+ end
78
+
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end