rev-websocket 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,4 +1,9 @@
1
1
 
2
+ 2010-07-17 version 0.1.2:
3
+
4
+ * sends <cross-domain-policy> on <policy-file-request> to accept Flash-based clients
5
+ * adds examples
6
+
2
7
  2010-07-13 version 0.1.1:
3
8
 
4
9
  * fixes compatibility with Chrome 6 (draft-76)
@@ -13,7 +13,9 @@ This library conforms to WebSocket draft-75 and draft-76.
13
13
  gem install rev-websocket
14
14
 
15
15
 
16
- == Simple example
16
+ == Examples
17
+
18
+ === Simple echo server
17
19
 
18
20
  require 'rubygems'
19
21
  require 'rev/websocket'
@@ -43,6 +45,65 @@ This library conforms to WebSocket draft-75 and draft-76.
43
45
  Rev::Loop.default.run
44
46
 
45
47
 
48
+ === Publisher/Subscriber-style message routing
49
+ require 'rubygems'
50
+ require 'rev/websocket'
51
+
52
+ class PubSub
53
+ def initialize
54
+ @subscriber = {}
55
+ @seqid = 0
56
+ end
57
+
58
+ def subscribe(&block)
59
+ sid = @seqid += 1
60
+ @subscriber[sid] = block
61
+ return sid
62
+ end
63
+
64
+ def unsubscribe(key)
65
+ @subscriber.delete(key)
66
+ end
67
+
68
+ def publish(data)
69
+ @subscriber.each_value {|block|
70
+ block.call(data)
71
+ }
72
+ end
73
+ end
74
+
75
+ $pubsub = PubSub.new
76
+
77
+ class MyConnection < Rev::WebSocket
78
+ def on_open
79
+ puts "WebSocket opened"
80
+ @sid = $pubsub.subscribe {|data|
81
+ send_message data
82
+ }
83
+ end
84
+
85
+ def on_message(data)
86
+ puts "WebSocket data received, broadcasting: '#{data}'"
87
+ $pubsub.publish(data)
88
+ end
89
+
90
+ def on_close
91
+ puts "WebSocket closed"
92
+ $pubsub.unsubscribe(@sid)
93
+ end
94
+ end
95
+
96
+ host = '0.0.0.0'
97
+ port = ARGV[0] || 8080
98
+
99
+ server = Rev::WebSocketServer.new(host, port, MyConnection)
100
+ server.attach(Rev::Loop.default)
101
+
102
+ puts "start on #{host}:#{port}"
103
+
104
+ Rev::Loop.default.run
105
+
106
+
46
107
  == Learn more
47
108
 
48
109
  - [Rev API reference] http://rev.rubyforge.org/rdoc/
data/Rakefile CHANGED
@@ -9,6 +9,8 @@ begin
9
9
  gemspec.email = "frsyuki@users.sourceforge.jp"
10
10
  gemspec.homepage = "http://github.com/frsyuki/rev-websocket"
11
11
  gemspec.authors = ["FURUHASHI Sadayuki"]
12
+ gemspec.files.include "examples/**/*"
13
+ gemspec.files.exclude ".gitignore"
12
14
  gemspec.add_dependency("rev", ">= 0.3.2")
13
15
  gemspec.add_dependency("thin", '>= 1.2.7')
14
16
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.1.2
@@ -0,0 +1,25 @@
1
+ Rev-WebSocket Examlpes
2
+ ======================
3
+
4
+ Documents for *rev-websocket* is available at [frsyuki's rev-websocket repository](http://github.com/frsyuki/rev-websocket).
5
+
6
+
7
+ ## Echo server
8
+
9
+ $ gem install rev-websocket
10
+ $ ruby ./echo
11
+
12
+ A HTTP server runs on localhost:8080 and WebSocket server runs on localhost:8081.
13
+
14
+ Then access to [htt://localhost:8080/echo.html](http://localhost:8080/echo.html).
15
+
16
+
17
+ ## ShoutChat
18
+
19
+ ShoutChat is a simple browser-based chat application.
20
+
21
+ $ gem install rev-websocket
22
+ $ ruby ./shoutchat
23
+
24
+ Then access to [htt://localhost:8080/shoutchat.html](http://localhost:8080/shoutchat.html).
25
+
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ fork {
4
+ load 'echo.rb'
5
+ exit 0
6
+ }
7
+
8
+ require 'webrick'
9
+
10
+ server = WEBrick::HTTPServer.new({
11
+ :Port => 8080,
12
+ :BindAddress => '127.0.0.1',
13
+ :DocumentRoot => File.dirname(__FILE__)+'/public'})
14
+
15
+ trap('INT') { server.shutdown }
16
+
17
+ server.start
18
+
@@ -0,0 +1,31 @@
1
+ # WebSocket echo server
2
+
3
+ require 'rubygems'
4
+ require 'rev/websocket'
5
+
6
+ class EchoConnection < Rev::WebSocket
7
+ def on_open
8
+ puts "WebSocket opened"
9
+ send_message("server: Hello, world!")
10
+ end
11
+
12
+ def on_message(data)
13
+ puts "WebSocket data received: '#{data}'"
14
+ send_message(data)
15
+ end
16
+
17
+ def on_close
18
+ puts "WebSocket closed"
19
+ end
20
+ end
21
+
22
+ host = '0.0.0.0'
23
+ port = ARGV[0] || 8081
24
+
25
+ server = Rev::WebSocketServer.new(host, port, EchoConnection)
26
+ server.attach(Rev::Loop.default)
27
+
28
+ puts "start on #{host}:#{port}"
29
+
30
+ Rev::Loop.default.run
31
+
@@ -0,0 +1,11 @@
1
+
2
+ .time {
3
+ color: #007010;
4
+ margin-right: 0.2em;
5
+ padding-right: 0.2em;
6
+ }
7
+
8
+ .time:after {
9
+ content: ":";
10
+ }
11
+
@@ -0,0 +1,60 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta content="text/css" http-equiv="content-style-type" />
6
+ <meta content="text/javascript" http-equiv="content-script-type" />
7
+
8
+ <script type="text/javascript" src='js/jquery.min.js'></script>
9
+ <script type="text/javascript" src='js/swfobject.js'></script>
10
+ <script type="text/javascript" src='js/FABridge.js'></script>
11
+ <script type="text/javascript" src='js/web_socket.js'></script>
12
+ <script type="text/javascript" src='js/json2.js'></script>
13
+ <link rel="stylesheet" type="text/css" href="echo.css" />
14
+
15
+ <title>Rev-WebSocket Demo: Echo server</title>
16
+
17
+ <script>
18
+ WS_URL = "ws://localhost:8081";
19
+ WEB_SOCKET_SWF_LOCATION = "js/WebSocketMain.swf";
20
+
21
+ var global = this;
22
+
23
+ $(document).ready(function(){
24
+
25
+ function debug(message) {
26
+ html = "<p><span class='time'>"+new Date()+"</span>"+message+"</p>"
27
+ $("#debug").append(html);
28
+ }
29
+
30
+ debug("connecting to "+WS_URL+"...");
31
+ ws = new WebSocket(WS_URL);
32
+
33
+ ws.onopen = function() {
34
+ debug("connected.");
35
+
36
+ text = "client: hello";
37
+ ws.send(text);
38
+
39
+ debug("message sent: "+text);
40
+ }
41
+
42
+ ws.onclose = function() {
43
+ debug("disconnected...");
44
+ }
45
+
46
+ ws.onerror = function(msg) {
47
+ debug("failed to connect:"+msg);
48
+ }
49
+
50
+ ws.onmessage = function(event) {
51
+ debug("message received: "+event.data);
52
+ $("#message").append("<p>"+event.data+"</p>");
53
+ }
54
+ });
55
+ </script>
56
+ </head>
57
+ <body>
58
+ <div id="message"></div>
59
+ <div id="debug"></div>
60
+ </body>
@@ -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
+ };