em-websocket 0.0.5 → 0.0.6

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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.5
1
+ 0.0.6
@@ -0,0 +1,604 @@
1
+ /*
2
+ /*
3
+ Copyright 2006 Adobe Systems Incorporated
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
6
+ to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
+ and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
+
9
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10
+
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
15
+ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
+
17
+ */
18
+
19
+
20
+ /*
21
+ * The Bridge class, responsible for navigating AS instances
22
+ */
23
+ function FABridge(target,bridgeName)
24
+ {
25
+ this.target = target;
26
+ this.remoteTypeCache = {};
27
+ this.remoteInstanceCache = {};
28
+ this.remoteFunctionCache = {};
29
+ this.localFunctionCache = {};
30
+ this.bridgeID = FABridge.nextBridgeID++;
31
+ this.name = bridgeName;
32
+ this.nextLocalFuncID = 0;
33
+ FABridge.instances[this.name] = this;
34
+ FABridge.idMap[this.bridgeID] = this;
35
+
36
+ return this;
37
+ }
38
+
39
+ // type codes for packed values
40
+ FABridge.TYPE_ASINSTANCE = 1;
41
+ FABridge.TYPE_ASFUNCTION = 2;
42
+
43
+ FABridge.TYPE_JSFUNCTION = 3;
44
+ FABridge.TYPE_ANONYMOUS = 4;
45
+
46
+ FABridge.initCallbacks = {};
47
+ FABridge.userTypes = {};
48
+
49
+ FABridge.addToUserTypes = function()
50
+ {
51
+ for (var i = 0; i < arguments.length; i++)
52
+ {
53
+ FABridge.userTypes[arguments[i]] = {
54
+ 'typeName': arguments[i],
55
+ 'enriched': false
56
+ };
57
+ }
58
+ }
59
+
60
+ FABridge.argsToArray = function(args)
61
+ {
62
+ var result = [];
63
+ for (var i = 0; i < args.length; i++)
64
+ {
65
+ result[i] = args[i];
66
+ }
67
+ return result;
68
+ }
69
+
70
+ function instanceFactory(objID)
71
+ {
72
+ this.fb_instance_id = objID;
73
+ return this;
74
+ }
75
+
76
+ function FABridge__invokeJSFunction(args)
77
+ {
78
+ var funcID = args[0];
79
+ var throughArgs = args.concat();//FABridge.argsToArray(arguments);
80
+ throughArgs.shift();
81
+
82
+ var bridge = FABridge.extractBridgeFromID(funcID);
83
+ return bridge.invokeLocalFunction(funcID, throughArgs);
84
+ }
85
+
86
+ FABridge.addInitializationCallback = function(bridgeName, callback)
87
+ {
88
+ var inst = FABridge.instances[bridgeName];
89
+ if (inst != undefined)
90
+ {
91
+ callback.call(inst);
92
+ return;
93
+ }
94
+
95
+ var callbackList = FABridge.initCallbacks[bridgeName];
96
+ if(callbackList == null)
97
+ {
98
+ FABridge.initCallbacks[bridgeName] = callbackList = [];
99
+ }
100
+
101
+ callbackList.push(callback);
102
+ }
103
+
104
+ // updated for changes to SWFObject2
105
+ function FABridge__bridgeInitialized(bridgeName) {
106
+ var objects = document.getElementsByTagName("object");
107
+ var ol = objects.length;
108
+ var activeObjects = [];
109
+ if (ol > 0) {
110
+ for (var i = 0; i < ol; i++) {
111
+ if (typeof objects[i].SetVariable != "undefined") {
112
+ activeObjects[activeObjects.length] = objects[i];
113
+ }
114
+ }
115
+ }
116
+ var embeds = document.getElementsByTagName("embed");
117
+ var el = embeds.length;
118
+ var activeEmbeds = [];
119
+ if (el > 0) {
120
+ for (var j = 0; j < el; j++) {
121
+ if (typeof embeds[j].SetVariable != "undefined") {
122
+ activeEmbeds[activeEmbeds.length] = embeds[j];
123
+ }
124
+ }
125
+ }
126
+ var aol = activeObjects.length;
127
+ var ael = activeEmbeds.length;
128
+ var searchStr = "bridgeName="+ bridgeName;
129
+ if ((aol == 1 && !ael) || (aol == 1 && ael == 1)) {
130
+ FABridge.attachBridge(activeObjects[0], bridgeName);
131
+ }
132
+ else if (ael == 1 && !aol) {
133
+ FABridge.attachBridge(activeEmbeds[0], bridgeName);
134
+ }
135
+ else {
136
+ var flash_found = false;
137
+ if (aol > 1) {
138
+ for (var k = 0; k < aol; k++) {
139
+ var params = activeObjects[k].childNodes;
140
+ for (var l = 0; l < params.length; l++) {
141
+ var param = params[l];
142
+ if (param.nodeType == 1 && param.tagName.toLowerCase() == "param" && param["name"].toLowerCase() == "flashvars" && param["value"].indexOf(searchStr) >= 0) {
143
+ FABridge.attachBridge(activeObjects[k], bridgeName);
144
+ flash_found = true;
145
+ break;
146
+ }
147
+ }
148
+ if (flash_found) {
149
+ break;
150
+ }
151
+ }
152
+ }
153
+ if (!flash_found && ael > 1) {
154
+ for (var m = 0; m < ael; m++) {
155
+ var flashVars = activeEmbeds[m].attributes.getNamedItem("flashVars").nodeValue;
156
+ if (flashVars.indexOf(searchStr) >= 0) {
157
+ FABridge.attachBridge(activeEmbeds[m], bridgeName);
158
+ break;
159
+ }
160
+ }
161
+ }
162
+ }
163
+ return true;
164
+ }
165
+
166
+ // used to track multiple bridge instances, since callbacks from AS are global across the page.
167
+
168
+ FABridge.nextBridgeID = 0;
169
+ FABridge.instances = {};
170
+ FABridge.idMap = {};
171
+ FABridge.refCount = 0;
172
+
173
+ FABridge.extractBridgeFromID = function(id)
174
+ {
175
+ var bridgeID = (id >> 16);
176
+ return FABridge.idMap[bridgeID];
177
+ }
178
+
179
+ FABridge.attachBridge = function(instance, bridgeName)
180
+ {
181
+ var newBridgeInstance = new FABridge(instance, bridgeName);
182
+
183
+ FABridge[bridgeName] = newBridgeInstance;
184
+
185
+ /* FABridge[bridgeName] = function() {
186
+ return newBridgeInstance.root();
187
+ }
188
+ */
189
+ var callbacks = FABridge.initCallbacks[bridgeName];
190
+ if (callbacks == null)
191
+ {
192
+ return;
193
+ }
194
+ for (var i = 0; i < callbacks.length; i++)
195
+ {
196
+ callbacks[i].call(newBridgeInstance);
197
+ }
198
+ delete FABridge.initCallbacks[bridgeName]
199
+ }
200
+
201
+ // some methods can't be proxied. You can use the explicit get,set, and call methods if necessary.
202
+
203
+ FABridge.blockedMethods =
204
+ {
205
+ toString: true,
206
+ get: true,
207
+ set: true,
208
+ call: true
209
+ };
210
+
211
+ FABridge.prototype =
212
+ {
213
+
214
+
215
+ // bootstrapping
216
+
217
+ root: function()
218
+ {
219
+ return this.deserialize(this.target.getRoot());
220
+ },
221
+ //clears all of the AS objects in the cache maps
222
+ releaseASObjects: function()
223
+ {
224
+ return this.target.releaseASObjects();
225
+ },
226
+ //clears a specific object in AS from the type maps
227
+ releaseNamedASObject: function(value)
228
+ {
229
+ if(typeof(value) != "object")
230
+ {
231
+ return false;
232
+ }
233
+ else
234
+ {
235
+ var ret = this.target.releaseNamedASObject(value.fb_instance_id);
236
+ return ret;
237
+ }
238
+ },
239
+ //create a new AS Object
240
+ create: function(className)
241
+ {
242
+ return this.deserialize(this.target.create(className));
243
+ },
244
+
245
+
246
+ // utilities
247
+
248
+ makeID: function(token)
249
+ {
250
+ return (this.bridgeID << 16) + token;
251
+ },
252
+
253
+
254
+ // low level access to the flash object
255
+
256
+ //get a named property from an AS object
257
+ getPropertyFromAS: function(objRef, propName)
258
+ {
259
+ if (FABridge.refCount > 0)
260
+ {
261
+ throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround.");
262
+ }
263
+ else
264
+ {
265
+ FABridge.refCount++;
266
+ retVal = this.target.getPropFromAS(objRef, propName);
267
+ retVal = this.handleError(retVal);
268
+ FABridge.refCount--;
269
+ return retVal;
270
+ }
271
+ },
272
+ //set a named property on an AS object
273
+ setPropertyInAS: function(objRef,propName, value)
274
+ {
275
+ if (FABridge.refCount > 0)
276
+ {
277
+ throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround.");
278
+ }
279
+ else
280
+ {
281
+ FABridge.refCount++;
282
+ retVal = this.target.setPropInAS(objRef,propName, this.serialize(value));
283
+ retVal = this.handleError(retVal);
284
+ FABridge.refCount--;
285
+ return retVal;
286
+ }
287
+ },
288
+
289
+ //call an AS function
290
+ callASFunction: function(funcID, args)
291
+ {
292
+ if (FABridge.refCount > 0)
293
+ {
294
+ throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround.");
295
+ }
296
+ else
297
+ {
298
+ FABridge.refCount++;
299
+ retVal = this.target.invokeASFunction(funcID, this.serialize(args));
300
+ retVal = this.handleError(retVal);
301
+ FABridge.refCount--;
302
+ return retVal;
303
+ }
304
+ },
305
+ //call a method on an AS object
306
+ callASMethod: function(objID, funcName, args)
307
+ {
308
+ if (FABridge.refCount > 0)
309
+ {
310
+ throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround.");
311
+ }
312
+ else
313
+ {
314
+ FABridge.refCount++;
315
+ args = this.serialize(args);
316
+ retVal = this.target.invokeASMethod(objID, funcName, args);
317
+ retVal = this.handleError(retVal);
318
+ FABridge.refCount--;
319
+ return retVal;
320
+ }
321
+ },
322
+
323
+ // responders to remote calls from flash
324
+
325
+ //callback from flash that executes a local JS function
326
+ //used mostly when setting js functions as callbacks on events
327
+ invokeLocalFunction: function(funcID, args)
328
+ {
329
+ var result;
330
+ var func = this.localFunctionCache[funcID];
331
+
332
+ if(func != undefined)
333
+ {
334
+ result = this.serialize(func.apply(null, this.deserialize(args)));
335
+ }
336
+
337
+ return result;
338
+ },
339
+
340
+ // Object Types and Proxies
341
+
342
+ // accepts an object reference, returns a type object matching the obj reference.
343
+ getTypeFromName: function(objTypeName)
344
+ {
345
+ return this.remoteTypeCache[objTypeName];
346
+ },
347
+ //create an AS proxy for the given object ID and type
348
+ createProxy: function(objID, typeName)
349
+ {
350
+ var objType = this.getTypeFromName(typeName);
351
+ instanceFactory.prototype = objType;
352
+ var instance = new instanceFactory(objID);
353
+ this.remoteInstanceCache[objID] = instance;
354
+ return instance;
355
+ },
356
+ //return the proxy associated with the given object ID
357
+ getProxy: function(objID)
358
+ {
359
+ return this.remoteInstanceCache[objID];
360
+ },
361
+
362
+ // accepts a type structure, returns a constructed type
363
+ addTypeDataToCache: function(typeData)
364
+ {
365
+ newType = new ASProxy(this, typeData.name);
366
+ var accessors = typeData.accessors;
367
+ for (var i = 0; i < accessors.length; i++)
368
+ {
369
+ this.addPropertyToType(newType, accessors[i]);
370
+ }
371
+
372
+ var methods = typeData.methods;
373
+ for (var i = 0; i < methods.length; i++)
374
+ {
375
+ if (FABridge.blockedMethods[methods[i]] == undefined)
376
+ {
377
+ this.addMethodToType(newType, methods[i]);
378
+ }
379
+ }
380
+
381
+
382
+ this.remoteTypeCache[newType.typeName] = newType;
383
+ return newType;
384
+ },
385
+
386
+ //add a property to a typename; used to define the properties that can be called on an AS proxied object
387
+ addPropertyToType: function(ty, propName)
388
+ {
389
+ var c = propName.charAt(0);
390
+ var setterName;
391
+ var getterName;
392
+ if(c >= "a" && c <= "z")
393
+ {
394
+ getterName = "get" + c.toUpperCase() + propName.substr(1);
395
+ setterName = "set" + c.toUpperCase() + propName.substr(1);
396
+ }
397
+ else
398
+ {
399
+ getterName = "get" + propName;
400
+ setterName = "set" + propName;
401
+ }
402
+ ty[setterName] = function(val)
403
+ {
404
+ this.bridge.setPropertyInAS(this.fb_instance_id, propName, val);
405
+ }
406
+ ty[getterName] = function()
407
+ {
408
+ return this.bridge.deserialize(this.bridge.getPropertyFromAS(this.fb_instance_id, propName));
409
+ }
410
+ },
411
+
412
+ //add a method to a typename; used to define the methods that can be callefd on an AS proxied object
413
+ addMethodToType: function(ty, methodName)
414
+ {
415
+ ty[methodName] = function()
416
+ {
417
+ return this.bridge.deserialize(this.bridge.callASMethod(this.fb_instance_id, methodName, FABridge.argsToArray(arguments)));
418
+ }
419
+ },
420
+
421
+ // Function Proxies
422
+
423
+ //returns the AS proxy for the specified function ID
424
+ getFunctionProxy: function(funcID)
425
+ {
426
+ var bridge = this;
427
+ if (this.remoteFunctionCache[funcID] == null)
428
+ {
429
+ this.remoteFunctionCache[funcID] = function()
430
+ {
431
+ bridge.callASFunction(funcID, FABridge.argsToArray(arguments));
432
+ }
433
+ }
434
+ return this.remoteFunctionCache[funcID];
435
+ },
436
+
437
+ //reutrns the ID of the given function; if it doesnt exist it is created and added to the local cache
438
+ getFunctionID: function(func)
439
+ {
440
+ if (func.__bridge_id__ == undefined)
441
+ {
442
+ func.__bridge_id__ = this.makeID(this.nextLocalFuncID++);
443
+ this.localFunctionCache[func.__bridge_id__] = func;
444
+ }
445
+ return func.__bridge_id__;
446
+ },
447
+
448
+ // serialization / deserialization
449
+
450
+ serialize: function(value)
451
+ {
452
+ var result = {};
453
+
454
+ var t = typeof(value);
455
+ //primitives are kept as such
456
+ if (t == "number" || t == "string" || t == "boolean" || t == null || t == undefined)
457
+ {
458
+ result = value;
459
+ }
460
+ else if (value instanceof Array)
461
+ {
462
+ //arrays are serializesd recursively
463
+ result = [];
464
+ for (var i = 0; i < value.length; i++)
465
+ {
466
+ result[i] = this.serialize(value[i]);
467
+ }
468
+ }
469
+ else if (t == "function")
470
+ {
471
+ //js functions are assigned an ID and stored in the local cache
472
+ result.type = FABridge.TYPE_JSFUNCTION;
473
+ result.value = this.getFunctionID(value);
474
+ }
475
+ else if (value instanceof ASProxy)
476
+ {
477
+ result.type = FABridge.TYPE_ASINSTANCE;
478
+ result.value = value.fb_instance_id;
479
+ }
480
+ else
481
+ {
482
+ result.type = FABridge.TYPE_ANONYMOUS;
483
+ result.value = value;
484
+ }
485
+
486
+ return result;
487
+ },
488
+
489
+ //on deserialization we always check the return for the specific error code that is used to marshall NPE's into JS errors
490
+ // the unpacking is done by returning the value on each pachet for objects/arrays
491
+ deserialize: function(packedValue)
492
+ {
493
+
494
+ var result;
495
+
496
+ var t = typeof(packedValue);
497
+ if (t == "number" || t == "string" || t == "boolean" || packedValue == null || packedValue == undefined)
498
+ {
499
+ result = this.handleError(packedValue);
500
+ }
501
+ else if (packedValue instanceof Array)
502
+ {
503
+ result = [];
504
+ for (var i = 0; i < packedValue.length; i++)
505
+ {
506
+ result[i] = this.deserialize(packedValue[i]);
507
+ }
508
+ }
509
+ else if (t == "object")
510
+ {
511
+ for(var i = 0; i < packedValue.newTypes.length; i++)
512
+ {
513
+ this.addTypeDataToCache(packedValue.newTypes[i]);
514
+ }
515
+ for (var aRefID in packedValue.newRefs)
516
+ {
517
+ this.createProxy(aRefID, packedValue.newRefs[aRefID]);
518
+ }
519
+ if (packedValue.type == FABridge.TYPE_PRIMITIVE)
520
+ {
521
+ result = packedValue.value;
522
+ }
523
+ else if (packedValue.type == FABridge.TYPE_ASFUNCTION)
524
+ {
525
+ result = this.getFunctionProxy(packedValue.value);
526
+ }
527
+ else if (packedValue.type == FABridge.TYPE_ASINSTANCE)
528
+ {
529
+ result = this.getProxy(packedValue.value);
530
+ }
531
+ else if (packedValue.type == FABridge.TYPE_ANONYMOUS)
532
+ {
533
+ result = packedValue.value;
534
+ }
535
+ }
536
+ return result;
537
+ },
538
+ //increases the reference count for the given object
539
+ addRef: function(obj)
540
+ {
541
+ this.target.incRef(obj.fb_instance_id);
542
+ },
543
+ //decrease the reference count for the given object and release it if needed
544
+ release:function(obj)
545
+ {
546
+ this.target.releaseRef(obj.fb_instance_id);
547
+ },
548
+
549
+ // check the given value for the components of the hard-coded error code : __FLASHERROR
550
+ // used to marshall NPE's into flash
551
+
552
+ handleError: function(value)
553
+ {
554
+ if (typeof(value)=="string" && value.indexOf("__FLASHERROR")==0)
555
+ {
556
+ var myErrorMessage = value.split("||");
557
+ if(FABridge.refCount > 0 )
558
+ {
559
+ FABridge.refCount--;
560
+ }
561
+ throw new Error(myErrorMessage[1]);
562
+ return value;
563
+ }
564
+ else
565
+ {
566
+ return value;
567
+ }
568
+ }
569
+ };
570
+
571
+ // The root ASProxy class that facades a flash object
572
+
573
+ ASProxy = function(bridge, typeName)
574
+ {
575
+ this.bridge = bridge;
576
+ this.typeName = typeName;
577
+ return this;
578
+ };
579
+ //methods available on each ASProxy object
580
+ ASProxy.prototype =
581
+ {
582
+ get: function(propName)
583
+ {
584
+ return this.bridge.deserialize(this.bridge.getPropertyFromAS(this.fb_instance_id, propName));
585
+ },
586
+
587
+ set: function(propName, value)
588
+ {
589
+ this.bridge.setPropertyInAS(this.fb_instance_id, propName, value);
590
+ },
591
+
592
+ call: function(funcName, args)
593
+ {
594
+ this.bridge.callASMethod(this.fb_instance_id, funcName, args);
595
+ },
596
+
597
+ addRef: function() {
598
+ this.bridge.addRef(this);
599
+ },
600
+
601
+ release: function() {
602
+ this.bridge.release(this);
603
+ }
604
+ };
Binary file
@@ -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,312 @@
1
+ // Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
2
+ // Lincense: New BSD Lincense
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
+ function hasFlash() {
14
+ if ('navigator' in window && 'plugins' in navigator && navigator.plugins['Shockwave Flash']) {
15
+ return !!navigator.plugins['Shockwave Flash'].description;
16
+ }
17
+ if ('ActiveXObject' in window) {
18
+ try {
19
+ return !!new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
20
+ } catch (e) {}
21
+ }
22
+ return false;
23
+ }
24
+
25
+ if (!hasFlash()) {
26
+ console.error("Flash Player is not installed.");
27
+ return;
28
+ }
29
+
30
+ WebSocket = function(url, protocol, proxyHost, proxyPort, headers) {
31
+ var self = this;
32
+ self.readyState = WebSocket.CONNECTING;
33
+ self.bufferedAmount = 0;
34
+ WebSocket.__addTask(function() {
35
+ self.__flash =
36
+ WebSocket.__flash.create(url, protocol, proxyHost || null, proxyPort || 0, headers || null);
37
+
38
+ self.__flash.addEventListener("open", function(fe) {
39
+ try {
40
+ if (self.onopen) self.onopen();
41
+ } catch (e) {
42
+ console.error(e.toString());
43
+ }
44
+ });
45
+
46
+ self.__flash.addEventListener("close", function(fe) {
47
+ try {
48
+ if (self.onclose) self.onclose();
49
+ } catch (e) {
50
+ console.error(e.toString());
51
+ }
52
+ });
53
+
54
+ self.__flash.addEventListener("message", function(fe) {
55
+ var data = decodeURIComponent(fe.getData());
56
+ try {
57
+ if (self.onmessage) {
58
+ var e;
59
+ if (window.MessageEvent) {
60
+ e = document.createEvent("MessageEvent");
61
+ e.initMessageEvent("message", false, false, data, null, null, window);
62
+ } else { // IE
63
+ e = {data: data};
64
+ }
65
+ self.onmessage(e);
66
+ }
67
+ } catch (e) {
68
+ console.error(e.toString());
69
+ }
70
+ });
71
+
72
+ self.__flash.addEventListener("stateChange", function(fe) {
73
+ try {
74
+ self.readyState = fe.getReadyState();
75
+ self.bufferedAmount = fe.getBufferedAmount();
76
+ } catch (e) {
77
+ console.error(e.toString());
78
+ }
79
+ });
80
+
81
+ //console.log("[WebSocket] Flash object is ready");
82
+ });
83
+ }
84
+
85
+ WebSocket.prototype.send = function(data) {
86
+ if (!this.__flash || this.readyState == WebSocket.CONNECTING) {
87
+ throw "INVALID_STATE_ERR: Web Socket connection has not been established";
88
+ }
89
+ var result = this.__flash.send(data);
90
+ if (result < 0) { // success
91
+ return true;
92
+ } else {
93
+ this.bufferedAmount = result;
94
+ return false;
95
+ }
96
+ };
97
+
98
+ WebSocket.prototype.close = function() {
99
+ if (!this.__flash) return;
100
+ if (this.readyState != WebSocket.OPEN) return;
101
+ this.__flash.close();
102
+ // Sets/calls them manually here because Flash WebSocketConnection.close cannot fire events
103
+ // which causes weird error:
104
+ // > You are trying to call recursively into the Flash Player which is not allowed.
105
+ this.readyState = WebSocket.CLOSED;
106
+ if (this.onclose) this.onclose();
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.addEventListener = function(type, listener, useCapture) {
118
+ if (!('__events' in this)) {
119
+ this.__events = {};
120
+ }
121
+ if (!(type in this.__events)) {
122
+ this.__events[type] = [];
123
+ if ('function' == typeof this['on' + type]) {
124
+ this.__events[type].defaultHandler = this['on' + type];
125
+ this['on' + type] = WebSocket_FireEvent(this, type);
126
+ }
127
+ }
128
+ this.__events[type].push(listener);
129
+ };
130
+
131
+ /**
132
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
133
+ *
134
+ * @param {string} type
135
+ * @param {function} listener
136
+ * @param {boolean} useCapture NB! Not implemented yet
137
+ * @return void
138
+ */
139
+ WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
140
+ if (!('__events' in this)) {
141
+ this.__events = {};
142
+ }
143
+ if (!(type in this.__events)) return;
144
+ for (var i = this.__events.length; i > -1; --i) {
145
+ if (listener === this.__events[type][i]) {
146
+ this.__events[type].splice(i, 1);
147
+ break;
148
+ }
149
+ }
150
+ };
151
+
152
+ /**
153
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
154
+ *
155
+ * @param {WebSocketEvent} event
156
+ * @return void
157
+ */
158
+ WebSocket.prototype.dispatchEvent = function(event) {
159
+ if (!('__events' in this)) throw 'UNSPECIFIED_EVENT_TYPE_ERR';
160
+ if (!(event.type in this.__events)) throw 'UNSPECIFIED_EVENT_TYPE_ERR';
161
+
162
+ for (var i = 0, l = this.__events[event.type].length; i < l; ++ i) {
163
+ this.__events[event.type][i](event);
164
+ if (event.cancelBubble) break;
165
+ }
166
+
167
+ if (false !== event.returnValue &&
168
+ 'function' == typeof this.__events[event.type].defaultHandler)
169
+ {
170
+ this.__events[event.type].defaultHandler(event);
171
+ }
172
+ };
173
+
174
+ /**
175
+ *
176
+ * @param {object} object
177
+ * @param {string} type
178
+ */
179
+ function WebSocket_FireEvent(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
+ /**
192
+ * Basic implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-interface">DOM 2 EventInterface</a>}
193
+ *
194
+ * @class
195
+ * @constructor
196
+ */
197
+ function WebSocketEvent(){}
198
+
199
+ /**
200
+ *
201
+ * @type boolean
202
+ */
203
+ WebSocketEvent.prototype.cancelable = true;
204
+
205
+ /**
206
+ *
207
+ * @type boolean
208
+ */
209
+ WebSocketEvent.prototype.cancelBubble = false;
210
+
211
+ /**
212
+ *
213
+ * @return void
214
+ */
215
+ WebSocketEvent.prototype.preventDefault = function() {
216
+ if (this.cancelable) {
217
+ this.returnValue = false;
218
+ }
219
+ };
220
+
221
+ /**
222
+ *
223
+ * @return void
224
+ */
225
+ WebSocketEvent.prototype.stopPropagation = function() {
226
+ this.cancelBubble = true;
227
+ };
228
+
229
+ /**
230
+ *
231
+ * @param {string} eventTypeArg
232
+ * @param {boolean} canBubbleArg
233
+ * @param {boolean} cancelableArg
234
+ * @return void
235
+ */
236
+ WebSocketEvent.prototype.initEvent = function(eventTypeArg, canBubbleArg, cancelableArg) {
237
+ this.type = eventTypeArg;
238
+ this.cancelable = cancelableArg;
239
+ this.timeStamp = new Date();
240
+ };
241
+
242
+
243
+ WebSocket.CONNECTING = 0;
244
+ WebSocket.OPEN = 1;
245
+ WebSocket.CLOSED = 2;
246
+
247
+ WebSocket.__tasks = [];
248
+
249
+ WebSocket.__initialize = function() {
250
+ if (!WebSocket.__swfLocation) {
251
+ //console.error("[WebSocket] set WebSocket.__swfLocation to location of WebSocketMain.swf");
252
+ //return;
253
+ WebSocket.__swfLocation = "js/WebSocketMain.swf";
254
+ }
255
+ var container = document.createElement("div");
256
+ container.id = "webSocketContainer";
257
+ // Puts the Flash out of the window. Note that we cannot use display: none or visibility: hidden
258
+ // here because it prevents Flash from loading at least in IE.
259
+ container.style.position = "absolute";
260
+ container.style.left = "-100px";
261
+ container.style.top = "-100px";
262
+ var holder = document.createElement("div");
263
+ holder.id = "webSocketFlash";
264
+ container.appendChild(holder);
265
+ document.body.appendChild(container);
266
+ swfobject.embedSWF(
267
+ WebSocket.__swfLocation, "webSocketFlash", "8", "8", "9.0.0",
268
+ null, {bridgeName: "webSocket"}, null, null,
269
+ function(e) {
270
+ if (!e.success) console.error("[WebSocket] swfobject.embedSWF failed");
271
+ }
272
+ );
273
+ FABridge.addInitializationCallback("webSocket", function() {
274
+ try {
275
+ //console.log("[WebSocket] FABridge initializad");
276
+ WebSocket.__flash = FABridge.webSocket.root();
277
+ WebSocket.__flash.setCallerUrl(location.href);
278
+ for (var i = 0; i < WebSocket.__tasks.length; ++i) {
279
+ WebSocket.__tasks[i]();
280
+ }
281
+ WebSocket.__tasks = [];
282
+ } catch (e) {
283
+ console.error("[WebSocket] " + e.toString());
284
+ }
285
+ });
286
+ };
287
+
288
+ WebSocket.__addTask = function(task) {
289
+ if (WebSocket.__flash) {
290
+ task();
291
+ } else {
292
+ WebSocket.__tasks.push(task);
293
+ }
294
+ }
295
+
296
+ // called from Flash
297
+ function webSocketLog(message) {
298
+ console.log(decodeURIComponent(message));
299
+ }
300
+
301
+ // called from Flash
302
+ function webSocketError(message) {
303
+ console.error(decodeURIComponent(message));
304
+ }
305
+
306
+ if (window.addEventListener) {
307
+ window.addEventListener("load", WebSocket.__initialize, false);
308
+ } else {
309
+ window.attachEvent("onload", WebSocket.__initialize);
310
+ }
311
+
312
+ })();
data/examples/test.html CHANGED
@@ -1,6 +1,9 @@
1
1
  <html>
2
2
  <head>
3
3
  <script src='http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js'></script>
4
+ <script src='js/swfobject.js'></script>
5
+ <script src='js/FABridge.js'></script>
6
+ <script src='js/web_socket.js'></script>
4
7
  <script>
5
8
  $(document).ready(function(){
6
9
  function debug(str){ $("#debug").append("<p>" + str); };
@@ -20,6 +20,7 @@ module EventMachine
20
20
  @state = :handshake
21
21
  @request = {}
22
22
  @data = ''
23
+ @skip_onclose = false
23
24
 
24
25
  debug [:initialize]
25
26
  end
@@ -56,36 +57,48 @@ module EventMachine
56
57
  debug [:inbound_headers, @data]
57
58
  lines = @data.split("\r\n")
58
59
 
59
- # extract request path
60
- @request['Path'] = lines.shift.match(PATH)[1].strip
61
-
62
- # extract query string values
63
- @request['Query'] = Addressable::URI.parse(@request['Path']).query_values ||= {}
64
-
65
- # extract remaining headers
66
- lines.each do |line|
67
- h = HEADER.match(line)
68
- @request[h[1].strip] = h[2].strip
69
- end
70
-
71
- # transform headers
72
- @request['Host'] = Addressable::URI.parse("ws://"+@request['Host'])
73
-
74
- if not websocket_connection?
75
- send_data "HTTP/1.1 400 Bad request\r\n\r\n"
76
- close_connection_after_writing
60
+ begin
61
+ # extract request path
62
+ @request['Path'] = lines.shift.match(PATH)[1].strip
63
+
64
+ # extract query string values
65
+ @request['Query'] = Addressable::URI.parse(@request['Path']).query_values ||= {}
66
+
67
+ # extract remaining headers
68
+ lines.each do |line|
69
+ h = HEADER.match(line)
70
+ @request[h[1].strip] = h[2].strip
71
+ end
72
+
73
+ # transform headers
74
+ @request['Host'] = Addressable::URI.parse("ws://"+@request['Host'])
75
+
76
+ if not websocket_connection?
77
+ process_bad_request
78
+ return false
79
+ else
80
+ @data = ''
81
+ @state = :upgrade
82
+ return true
83
+ end
84
+ rescue => e
85
+ debug [:error, e]
86
+ process_bad_request
77
87
  return false
78
-
79
- else
80
- @data = ''
81
- @state = :upgrade
82
- return true
83
88
  end
89
+ elsif @data.match(/<policy-file-request\s*\/>/)
90
+ send_flash_cross_domain_file
91
+ return false
84
92
  end
85
93
 
86
94
  false
87
95
  end
88
96
 
97
+ def process_bad_request
98
+ send_data "HTTP/1.1 400 Bad request\r\n\r\n"
99
+ close_connection_after_writing
100
+ end
101
+
89
102
  def websocket_connection?
90
103
  @request['Connection'] == 'Upgrade' and @request['Upgrade'] == 'WebSocket'
91
104
  end
@@ -93,7 +106,7 @@ module EventMachine
93
106
  def send_upgrade
94
107
  location = "ws://#{@request['Host'].host}"
95
108
  location << ":#{@request['Host'].port}" if @request['Host'].port
96
- location << @request['Path'].split("?").first
109
+ location << @request['Path']
97
110
 
98
111
  upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
99
112
  upgrade << "Upgrade: WebSocket\r\n"
@@ -113,6 +126,17 @@ module EventMachine
113
126
  false
114
127
  end
115
128
 
129
+ def send_flash_cross_domain_file
130
+ file = '<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'
131
+ debug [:cross_domain, file]
132
+ send_data file
133
+
134
+ # handle the cross-domain request transparently
135
+ # no need to notif the user about this connection
136
+ @onclose = nil
137
+ close_connection_after_writing
138
+ end
139
+
116
140
  def process_message
117
141
  return if not @onmessage
118
142
  debug [:message, @data]
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: em-websocket
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 6
9
+ version: 0.0.6
5
10
  platform: ruby
6
11
  authors:
7
12
  - Ilya Grigorik
@@ -9,39 +14,51 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-01-06 00:00:00 -05:00
17
+ date: 2010-04-18 00:00:00 -04:00
13
18
  default_executable:
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
21
  name: eventmachine
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
20
24
  requirements:
21
25
  - - ">="
22
26
  - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 12
30
+ - 9
23
31
  version: 0.12.9
24
- version:
32
+ type: :runtime
33
+ version_requirements: *id001
25
34
  - !ruby/object:Gem::Dependency
26
35
  name: addressable
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
30
38
  requirements:
31
39
  - - ">="
32
40
  - !ruby/object:Gem::Version
41
+ segments:
42
+ - 2
43
+ - 1
44
+ - 1
33
45
  version: 2.1.1
34
- version:
46
+ type: :runtime
47
+ version_requirements: *id002
35
48
  - !ruby/object:Gem::Dependency
36
49
  name: em-http-request
37
- type: :development
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
40
52
  requirements:
41
53
  - - ">="
42
54
  - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ - 2
58
+ - 6
43
59
  version: 0.2.6
44
- version:
60
+ type: :development
61
+ version_requirements: *id003
45
62
  description: EventMachine based WebSocket server
46
63
  email: ilya@igvita.com
47
64
  executables: []
@@ -56,6 +73,10 @@ files:
56
73
  - Rakefile
57
74
  - VERSION
58
75
  - examples/echo.rb
76
+ - examples/js/FABridge.js
77
+ - examples/js/WebSocketMain.swf
78
+ - examples/js/swfobject.js
79
+ - examples/js/web_socket.js
59
80
  - examples/multicast.rb
60
81
  - examples/test.html
61
82
  - lib/em-websocket.rb
@@ -76,18 +97,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
76
97
  requirements:
77
98
  - - ">="
78
99
  - !ruby/object:Gem::Version
100
+ segments:
101
+ - 0
79
102
  version: "0"
80
- version:
81
103
  required_rubygems_version: !ruby/object:Gem::Requirement
82
104
  requirements:
83
105
  - - ">="
84
106
  - !ruby/object:Gem::Version
107
+ segments:
108
+ - 0
85
109
  version: "0"
86
- version:
87
110
  requirements: []
88
111
 
89
112
  rubyforge_project: em-websocket
90
- rubygems_version: 1.3.5
113
+ rubygems_version: 1.3.6
91
114
  signing_key:
92
115
  specification_version: 3
93
116
  summary: EventMachine based WebSocket server