flotype-bridge 0.1.0.beta.2 → 0.2.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/LICENSE +19 -0
  2. data/README.md +27 -4
  3. data/doc/Bridge/Bridge.html +624 -0
  4. data/doc/Bridge.html +72 -903
  5. data/doc/created.rid +2 -1
  6. data/doc/images/add.png +0 -0
  7. data/doc/images/brick.png +0 -0
  8. data/doc/images/brick_link.png +0 -0
  9. data/doc/images/bug.png +0 -0
  10. data/doc/images/bullet_black.png +0 -0
  11. data/doc/images/bullet_toggle_minus.png +0 -0
  12. data/doc/images/bullet_toggle_plus.png +0 -0
  13. data/doc/images/date.png +0 -0
  14. data/doc/images/delete.png +0 -0
  15. data/doc/images/find.png +0 -0
  16. data/doc/images/loadingAnimation.gif +0 -0
  17. data/doc/images/macFFBgHack.png +0 -0
  18. data/doc/images/package.png +0 -0
  19. data/doc/images/page_green.png +0 -0
  20. data/doc/images/page_white_text.png +0 -0
  21. data/doc/images/page_white_width.png +0 -0
  22. data/doc/images/plugin.png +0 -0
  23. data/doc/images/ruby.png +0 -0
  24. data/doc/images/tag_blue.png +0 -0
  25. data/doc/images/tag_green.png +0 -0
  26. data/doc/images/transparent.png +0 -0
  27. data/doc/images/wrench.png +0 -0
  28. data/doc/images/wrench_orange.png +0 -0
  29. data/doc/images/zoom.png +0 -0
  30. data/doc/index.html +56 -56
  31. data/doc/js/darkfish.js +153 -0
  32. data/doc/js/jquery.js +5 -3
  33. data/doc/js/navigation.js +142 -0
  34. data/doc/js/search.js +94 -0
  35. data/doc/js/search_index.js +1 -0
  36. data/doc/js/searcher.js +228 -0
  37. data/doc/rdoc.css +543 -0
  38. data/doc/table_of_contents.html +70 -0
  39. data/examples/chat/chat_client.rb +15 -35
  40. data/examples/chat/chat_server.rb +21 -21
  41. data/examples/chat/public/css/style.css +4 -0
  42. data/examples/chat/public/js/all.js +5 -0
  43. data/examples/chat/public/js/bridge.min.js +2 -0
  44. data/examples/chat/public/js/index.js +14 -0
  45. data/examples/chat/views/index.erb +5 -0
  46. data/examples/chat/views/layout.erb +23 -0
  47. data/flotype-bridge.gemspec +1 -1
  48. data/lib/bridge.rb +253 -106
  49. data/lib/connection.rb +161 -0
  50. data/lib/reference.rb +49 -0
  51. data/lib/serializer.rb +97 -0
  52. data/lib/tcp.rb +61 -0
  53. data/lib/util.rb +101 -0
  54. data/lib/version.rb +3 -0
  55. data/test/regression/reconnect.rb +48 -0
  56. data/test/regression/rpc.rb +39 -0
  57. data/test/regression/test.rb +58 -0
  58. data/test/unit/bridge_dummy.rb +26 -0
  59. data/test/unit/connection_dummy.rb +21 -0
  60. data/test/unit/reference_dummy.rb +11 -0
  61. data/test/unit/tcp_dummy.rb +12 -0
  62. data/test/unit/test.rb +20 -0
  63. data/test/unit/test_reference.rb +30 -0
  64. data/test/unit/test_serializer.rb +109 -0
  65. data/test/unit/test_tcp.rb +51 -0
  66. data/test/unit/test_util.rb +59 -0
  67. metadata +106 -136
  68. data/doc/Bridge/Callback.html +0 -495
  69. data/doc/Bridge/CallbackRef.html +0 -343
  70. data/doc/Bridge/Conn.html +0 -409
  71. data/doc/Bridge/Core.html +0 -762
  72. data/doc/Bridge/LocalRef.html +0 -509
  73. data/doc/Bridge/Ref.html +0 -647
  74. data/doc/Bridge/Service.html +0 -135
  75. data/doc/Bridge/Sys.html +0 -278
  76. data/doc/Bridge/Util.html +0 -746
  77. data/doc/_index.html +0 -202
  78. data/doc/class_list.html +0 -47
  79. data/doc/classes/Bridge.html +0 -263
  80. data/doc/classes/Bridge.src/M000001.html +0 -24
  81. data/doc/classes/Bridge.src/M000002.html +0 -18
  82. data/doc/classes/Bridge.src/M000003.html +0 -18
  83. data/doc/classes/Bridge.src/M000004.html +0 -20
  84. data/doc/classes/Bridge.src/M000005.html +0 -26
  85. data/doc/classes/Bridge.src/M000006.html +0 -22
  86. data/doc/classes/Bridge.src/M000007.html +0 -18
  87. data/doc/classes/Bridge.src/M000008.html +0 -18
  88. data/doc/css/common.css +0 -1
  89. data/doc/css/full_list.css +0 -55
  90. data/doc/css/style.css +0 -322
  91. data/doc/file.README.html +0 -71
  92. data/doc/file_list.html +0 -49
  93. data/doc/files/lib/bridge_rb.html +0 -108
  94. data/doc/fr_class_index.html +0 -27
  95. data/doc/fr_file_index.html +0 -27
  96. data/doc/fr_method_index.html +0 -34
  97. data/doc/frames.html +0 -13
  98. data/doc/js/app.js +0 -205
  99. data/doc/js/full_list.js +0 -173
  100. data/doc/method_list.html +0 -510
  101. data/doc/rdoc-style.css +0 -208
  102. data/doc/top-level-namespace.html +0 -105
  103. data/lib/bb/callback.rb +0 -33
  104. data/lib/bb/conn.rb +0 -31
  105. data/lib/bb/core.rb +0 -109
  106. data/lib/bb/localref.rb +0 -37
  107. data/lib/bb/ref.rb +0 -49
  108. data/lib/bb/svc.rb +0 -7
  109. data/lib/bb/sys.rb +0 -17
  110. data/lib/bb/util.rb +0 -93
  111. data/lib/bb/version.rb +0 -3
  112. data/tests/bb_test_helper.rb +0 -3
  113. data/tests/test_serialize.rb +0 -4
@@ -0,0 +1,5 @@
1
+ var bridge;
2
+ $(function(){
3
+ bridge = new Bridge({ apiKey: "abcdefgh" }).connect();
4
+ });
5
+
@@ -0,0 +1,2 @@
1
+ /*! bridge.min.js build:0.0.1, production. Copyright(c) 2011 Flotype <team@flotype.com> MIT Licensed */
2
+ function Reference(a,b,c){var d=this;for(var e in c){var f=c[e];f&&(this[f]=function(a,b){return function(){var c=[].slice.apply(arguments);a._call(b,c)}}(this,f))}this._operations=c||[],this._bridge=a,this._address=b}function Connection(a){var b=this;this.bridge=a,this.options=a._options,this.sockBuffer=new SockBuffer,this.sock=this.sockBuffer,this.interval=400}function SockBuffer(){this.buffer=[]}function Bridge(a){var b=this,c={redirector:"http://redirector.flotype.com",reconnect:!0,log:2,tcp:!1},d={hookChannelHandler:function(a,c,d){var e=b._store[c._address[2]];b._store["channel:"+a]=e;if(d){var f=new Reference(b,["channel",a,"channel:"+a],util.findOps(e));d(f,a)}},getService:function(a,c){util.hasProp(b._store,a)?c(b._store[a],a):c(null,a)},remoteError:function(a){util.warn(a),b.emit("remoteError",[a])}};this._options=util.extend(c,a),util.setLogLevel(this._options.log),this._store={system:d},this._ready=!1,this._connection=new Connection(this),this._events={}}var JSON;JSON||(JSON={}),function(){function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c<f;c+=1)h[c]=str(c,i)||"null";return e=h.length===0?"[]":gap?"[\n"+gap+h.join(",\n"+gap)+"\n"+g+"]":"["+h.join(",")+"]",gap=g,e}if(rep&&typeof rep=="object"){f=rep.length;for(c=0;c<f;c+=1)typeof rep[c]=="string"&&(d=rep[c],e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e))}else for(d in i)Object.prototype.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e));return e=h.length===0?"{}":gap?"{\n"+gap+h.join(",\n"+gap)+"\n"+g+"}":"{"+h.join(",")+"}",gap=g,e}}function quote(a){return escapable.lastIndex=0,escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return typeof b=="string"?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function f(a){return a<10?"0"+a:a}"use strict",typeof Date.prototype.toJSON!="function"&&(Date.prototype.toJSON=function(a){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()});var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;typeof JSON.stringify!="function"&&(JSON.stringify=function(a,b,c){var d;gap="",indent="";if(typeof c=="number")for(d=0;d<c;d+=1)indent+=" ";else typeof c=="string"&&(indent=c);rep=b;if(!b||typeof b=="function"||typeof b=="object"&&typeof b.length=="number")return str("",{"":a});throw new Error("JSON.stringify")}),typeof JSON.parse!="function"&&(JSON.parse=function(text,reviver){function walk(a,b){var c,d,e=a[b];if(e&&typeof e=="object")for(c in e)Object.prototype.hasOwnProperty.call(e,c)&&(d=walk(e,c),d!==undefined?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),typeof reviver=="function"?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}(),SockJS=function(){var a=document,b=window,c=function(){};c.prototype.addEventListener=function(a,b){this._listeners||(this._listeners={}),a in this._listeners||(this._listeners[a]=[]);var c=this._listeners[a];e.arrIndexOf(c,b)===-1&&c.push(b);return},c.prototype.removeEventListener=function(a,b){if(!(this._listeners&&a in this._listeners))return;var c=this._listeners[a],d=e.arrIndexOf(c,b);if(d!==-1){c.length>1?this._listeners[a]=c.slice(0,d).concat(c.slice(d+1)):delete this._listeners[a];return}return},c.prototype.dispatchEvent=function(a){var b=a.type,c=Array.prototype.slice.call(arguments,0);this["on"+b]&&this["on"+b].apply(this,c);if(this._listeners&&b in this._listeners)for(var d=0;d<this._listeners[b].length;d++)this._listeners[b][d].apply(this,c)};var d=function(a,b){this.type=a;if(typeof b!="undefined")for(var c in b){if(!b.hasOwnProperty(c))continue;this[c]=b[c]}};d.prototype.toString=function(){var a=[];for(var b in this){if(!this.hasOwnProperty(b))continue;var c=this[b];typeof c=="function"&&(c="[function]"),a.push(b+"="+c)}return"SimpleEvent("+a.join(", ")+")"};var e={},f="abcdefghijklmnopqrstuvwxyz0123456789_";e.random_string=function(a,b){b=b||f.length;var c,d=[];for(c=0;c<a;c++)d.push(f.substr(Math.floor(Math.random()*b),1));return d.join("")},e.random_number=function(a){return Math.floor(Math.random()*a)},e.random_number_string=function(a){var b=(""+(a-1)).length,c=Array(b+1).join("0");return(c+e.random_number(a)).slice(-b)},e.getOrigin=function(a){a+="/";var b=a.split("/").slice(0,3);return b.join("/")},e.objectExtend=function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a};var g="_jp";e.polluteGlobalNamespace=function(){g in b||(b[g]={})},e.closeFrame=function(a,b){return"c"+JSON.stringify([a,b])},e.userSetCode=function(a){return a===1e3||a>=3e3&&a<=4999},e.log=function(){b.console&&console.log&&console.log.apply&&console.log.apply(console,arguments)},e.bind=function(a,b){return a.bind?a.bind(b):function(){return a.apply(b,arguments)}},e.amendUrl=function(b){var c=a.location;if(!b)throw new Error("Wrong url for SockJS");return b.indexOf("//")===0&&(b=c.protocol+b),b.indexOf("/")===0&&(b=c.protocol+"//"+c.host+b),b=b.replace(/[/]+$/,""),b},e.arrIndexOf=function(a,b){for(var c=0;c<a.length;c++)if(a[c]===b)return c;return-1},e.delay=function(a,b){return typeof a=="function"&&(b=a,a=0),setTimeout(b,a)};var h=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,i={"\0":"\\u0000","":"\\u0001","":"\\u0002","":"\\u0003","":"\\u0004","":"\\u0005","":"\\u0006","":"\\u0007","\b":"\\b","\t":"\\t","\n":"\\n"," ":"\\u000b","\f":"\\f","\r":"\\r","":"\\u000e","":"\\u000f","":"\\u0010","":"\\u0011","":"\\u0012","":"\\u0013","":"\\u0014","":"\\u0015","":"\\u0016","":"\\u0017","":"\\u0018","":"\\u0019","":"\\u001a","":"\\u001b","":"\\u001c","":"\\u001d","":"\\u001e","":"\\u001f",'"':'\\"',"\\":"\\\\","":"\\u007f","\u0080":"\\u0080","\u0081":"\\u0081","\u0082":"\\u0082","\u0083":"\\u0083","\u0084":"\\u0084","\u0085":"\\u0085","\u0086":"\\u0086","\u0087":"\\u0087","\u0088":"\\u0088","\u0089":"\\u0089","\u008a":"\\u008a","\u008b":"\\u008b","\u008c":"\\u008c","\u008d":"\\u008d","\u008e":"\\u008e","\u008f":"\\u008f","\u0090":"\\u0090","\u0091":"\\u0091","\u0092":"\\u0092","\u0093":"\\u0093","\u0094":"\\u0094","\u0095":"\\u0095","\u0096":"\\u0096","\u0097":"\\u0097","\u0098":"\\u0098","\u0099":"\\u0099","\u009a":"\\u009a","\u009b":"\\u009b","\u009c":"\\u009c","\u009d":"\\u009d","\u009e":"\\u009e","\u009f":"\\u009f","\u00ad":"\\u00ad","\u0600":"\\u0600","\u0601":"\\u0601","\u0602":"\\u0602","\u0603":"\\u0603","\u0604":"\\u0604","\u070f":"\\u070f","\u17b4":"\\u17b4","\u17b5":"\\u17b5","\u200c":"\\u200c","\u200d":"\\u200d","\u200e":"\\u200e","\u200f":"\\u200f","\u2028":"\\u2028","\u2029":"\\u2029","\u202a":"\\u202a","\u202b":"\\u202b","\u202c":"\\u202c","\u202d":"\\u202d","\u202e":"\\u202e","\u202f":"\\u202f","\u2060":"\\u2060","\u2061":"\\u2061","\u2062":"\\u2062","\u2063":"\\u2063","\u2064":"\\u2064","\u2065":"\\u2065","\u2066":"\\u2066","\u2067":"\\u2067","\u2068":"\\u2068","\u2069":"\\u2069","\u206a":"\\u206a","\u206b":"\\u206b","\u206c":"\\u206c","\u206d":"\\u206d","\u206e":"\\u206e","\u206f":"\\u206f","\ufeff":"\\ufeff","\ufff0":"\\ufff0","\ufff1":"\\ufff1","\ufff2":"\\ufff2","\ufff3":"\\ufff3","\ufff4":"\\ufff4","\ufff5":"\\ufff5","\ufff6":"\\ufff6","\ufff7":"\\ufff7","\ufff8":"\\ufff8","\ufff9":"\\ufff9","\ufffa":"\\ufffa","\ufffb":"\\ufffb","\ufffc":"\\ufffc","\ufffd":"\\ufffd","\ufffe":"\\ufffe","\uffff":"\\uffff"},j=/[\x00-\x1f\ud800-\udfff\ufffe\uffff\u0300-\u0333\u033d-\u0346\u034a-\u034c\u0350-\u0352\u0357-\u0358\u035c-\u0362\u0374\u037e\u0387\u0591-\u05af\u05c4\u0610-\u0617\u0653-\u0654\u0657-\u065b\u065d-\u065e\u06df-\u06e2\u06eb-\u06ec\u0730\u0732-\u0733\u0735-\u0736\u073a\u073d\u073f-\u0741\u0743\u0745\u0747\u07eb-\u07f1\u0951\u0958-\u095f\u09dc-\u09dd\u09df\u0a33\u0a36\u0a59-\u0a5b\u0a5e\u0b5c-\u0b5d\u0e38-\u0e39\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0f69\u0f72-\u0f76\u0f78\u0f80-\u0f83\u0f93\u0f9d\u0fa2\u0fa7\u0fac\u0fb9\u1939-\u193a\u1a17\u1b6b\u1cda-\u1cdb\u1dc0-\u1dcf\u1dfc\u1dfe\u1f71\u1f73\u1f75\u1f77\u1f79\u1f7b\u1f7d\u1fbb\u1fbe\u1fc9\u1fcb\u1fd3\u1fdb\u1fe3\u1feb\u1fee-\u1fef\u1ff9\u1ffb\u1ffd\u2000-\u2001\u20d0-\u20d1\u20d4-\u20d7\u20e7-\u20e9\u2126\u212a-\u212b\u2329-\u232a\u2adc\u302b-\u302c\uaab2-\uaab3\uf900-\ufa0d\ufa10\ufa12\ufa15-\ufa1e\ufa20\ufa22\ufa25-\ufa26\ufa2a-\ufa2d\ufa30-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4e\ufff0-\uffff]/g,k,l=JSON&&JSON.stringify||function(a){return h.lastIndex=0,h.test(a)&&(a=a.replace(h,function(a){return i[a]})),'"'+a+'"'},m=function(a){var b,c={},d=[];for(b=0;b<65536;b++)d.push(String.fromCharCode(b));return a.lastIndex=0,d.join("").replace(a,function(a){return c[a]="\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4),""}),a.lastIndex=0,c};e.quote=function(a){var b=l(a);return j.lastIndex=0,j.test(b)?(k||(k=m(j)),b.replace(j,function(a){return k[a]})):b};var n="_sockjs_global";e.createHook=function(){var a="a"+e.random_string(8);if(!(n in b)){var c={};b[n]=function(a){return a in c||(c[a]={id:a,del:function(){delete c[a]}}),c[a]}}return b[n](a)},e.attachMessage=function(a){e.attachEvent("message",a)},e.attachEvent=function(c,d){typeof b.addEventListener!="undefined"?b.addEventListener(c,d,!1):(a.attachEvent("on"+c,d),b.attachEvent("on"+c,d))},e.detachMessage=function(a){e.detachEvent("message",a)},e.detachEvent=function(c,d){typeof b.addEventListener!="undefined"?b.removeEventListener(c,d,!1):(a.detachEvent("on"+c,d),b.detachEvent("on"+c,d))};var o={"User-Agent":"",Accept:"","Accept-Language":"","Content-Type":"text/plain;charset=UTF-8"};navigator&&(navigator.userAgent.indexOf("Chrome")!=-1||navigator.userAgent.indexOf("Safari")!=-1)&&delete o["User-Agent"],e.createXDR=function(a,b,c,d){var f={status:null,responseText:"",readyState:1},g=new XDomainRequest;b+=(b.indexOf("?")===-1?"?":"&")+"t="+e.random_string(8);var h=function(){if(g){i=g.onerror=g.ontimeout=g.onprogress=g.onload=null;try{g.abort()}catch(a){}g=d=null}},i=g.ontimeout=g.onerror=function(){f.status=500,f.readyState=4,d(f),h()};g.onload=function(){f.status=200,f.readyState=4,f.responseText=g.responseText,d(f),h()},g.onprogress=function(){f.status=200,f.readyState=3,f.responseText=g.responseText,d(f)};try{g.open(a,b),g.send(c)}catch(j){e.delay(i)}return function(a){d&&(d(f,null,a),h())}},e.createXHR=function(a,c,d,f){var g;if(b.ActiveXObject){c+=(c.indexOf("?")===-1?"?":"&")+"t="+ +(new Date);try{g=new ActiveXObject("Microsoft.XMLHTTP")}catch(h){}}g||(g=new XMLHttpRequest),g.open(a,c,!0);for(var i in o)try{g.setRequestHeader(i,o[i])}catch(h){delete o[i]}"withCredentials"in g&&(g.withCredentials="true");var j=function(){if(g){try{g.onreadystatechange=null}catch(a){g.onreadystatechange=function(){}}try{g.abort()}catch(b){}e.detachEvent("unload",j)}f=g=null};return g.onreadystatechange=function(a){g&&f&&(f(g,a),g&&g.readyState===4&&j())},g.send(d),e.attachEvent("unload",j),function(a){f&&(f(g,null,a),j())}},e.createIframe=function(b,c){var d=a.createElement("iframe"),f,g=function(){clearTimeout(f);try{d.onload=null}catch(a){}d.onerror=null},h=function(){d&&(g(),d.src="about:blank",setTimeout(function(){d&&d.parentNode.removeChild(d),d=null},0),e.detachEvent("unload",h))},i=function(a){d&&(h(),c(a))};return d.src=b,d.style.display="none",d.style.position="absolute",d.onerror=function(){i("onerror")},d.onload=function(){clearTimeout(f),f=setTimeout(function(){i("onload timeout")},2e3)},a.body.appendChild(d),f=setTimeout(function(){i("timeout")},5e3),e.attachEvent("unload",h),{iframe:d,cleanup:h,loaded:g}},e.createHtmlfile=function(a,c){var d=new ActiveXObject("htmlfile"),f,h,i=function(){clearTimeout(f)},j=function(){if(d){i(),e.detachEvent("unload",j);try{h.src="about:blank"}catch(a){}h.parentNode.removeChild(h),h=d=null,CollectGarbage()}},k=function(a){d&&(j(),c(a))};d.open(),d.write('<html><script>document.domain="'+document.domain+'";'+"</script></html>"),d.close(),d.parentWindow[g]=b[g];var l=d.createElement("div");return d.body.appendChild(l),h=d.createElement("iframe"),l.appendChild(h),h.src=a,f=setTimeout(function(){k("timeout")},5e3),e.attachEvent("unload",j),{iframe:h,cleanup:j,loaded:i}};var p=function(){var b=this,c=function(a){b.state=p[a];var c={state:b.state,name:a};b.dispatchEvent(new d("change",c)),b.dispatchEvent(new d(a,c))};c("init"),a.body?c("load"):e.attachEvent("load",function(){c("load")}),e.attachEvent("beforeunload",function(){c("beforeunload")}),e.attachEvent("unload",function(){c("unload")})};p.prototype=new c,p.init=0,p.load=1,p.beforeunload=2,p.unload=3;var q=new p,r=function(a,b,c){var d=this;d._options={devel:!1,debug:!1,chunking:undefined},c&&e.objectExtend(d._options,c),d._base_url=e.amendUrl(a),d._server=d._options.server||e.random_number_string(1e3),d._connid=e.random_string(8),d._trans_url=d._base_url+"/"+d._server+"/"+d._connid,d._protocols=["websocket","xhr-streaming","iframe-eventsource","iframe-htmlfile","xhr-polling","iframe-xhr-polling","jsonp-polling"];switch(typeof b){case"undefined":break;case"string":d._protocols=[b];break;default:d._protocols=b}d.protocol=null,d.readyState=r.CONNECTING,d._didClose()};r.prototype=new c,r.version="0.1.2",r.CONNECTING=0,r.OPEN=1,r.CLOSING=2,r.CLOSED=3,r.prototype._debug=function(){this._options.debug&&e.log.apply(e,arguments)},r.prototype._dispatchOpen=function(){var a=this;a.readyState===r.CONNECTING?(a._transport_tref&&(clearTimeout(a._transport_tref),a._transport_tref=null),a.readyState=r.OPEN,a.dispatchEvent(new d("open"))):a._didClose(1006,"Server lost session")},r.prototype._dispatchMessage=function(a){var b=this;if(b.readyState!==r.OPEN)return;b.dispatchEvent(new d("message",{data:a}))},r.prototype._dispatchHeartbeat=function(a){var b=this;if(b.readyState!==r.OPEN)return;b.dispatchEvent(new d("heartbeat",{}))},r.prototype._didClose=function(a,b){var c=this;if(c.readyState!==r.CONNECTING&&c.readyState!==r.OPEN&&c.readyState!==r.CLOSING)throw new Error("INVALID_STATE_ERR");c._transport&&c._transport.doCleanup(),c._transport=null,c._transport_tref&&(clearTimeout(c._transport_tref),c._transport_tref=null);var f=new d("close",{code:a,reason:b,wasClean:e.userSetCode(a)});if(!e.userSetCode(a)&&c.readyState===r.CONNECTING){if(c._try_next_protocol(f)){c._transport_tref=setTimeout(function(){c.readyState===r.CONNECTING&&c._didClose(2007,"Transport timeouted")},5001);return}f=new d("close",{code:2e3,reason:"All transports failed",wasClean:!1,last_event:f})}c.readyState=r.CLOSED,e.delay(function(){c.dispatchEvent(f)})},r.prototype._didMessage=function(a){var b=this,c=a.slice(0,1);switch(c){case"o":b._dispatchOpen();break;case"a":var d=JSON.parse(a.slice(1)||"[]");for(var e=0;e<d.length;e++)b._dispatchMessage(d[e]);break;case"m":var d=JSON.parse(a.slice(1)||"null");b._dispatchMessage(d);break;case"c":var d=JSON.parse(a.slice(1)||"[]");b._didClose(d[0],d[1]);break;case"h":b._dispatchHeartbeat()}},r.prototype._try_next_protocol=function(b){var c=this;c.protocol&&(c._debug("Closed transport:",c.protocol,""+b),c.protocol=null);for(;;){var d=c.protocol=c._protocols.shift();if(!d)return!1;if(r[d]&&r[d].need_chunking===!0&&c._options.chunking===undefined)return c._protocols.unshift(d),c.protocol="chunking-test",c._options.chunking=!1,K(c._base_url,function(a){c._options.chunking=a,c._try_next_protocol()},c._options),!0;if(r[d]&&r[d].need_body===!0&&!a.body)return c._protocols.unshift(d),c.protocol="waiting-for-load",e.attachEvent("load",function(){c._try_next_protocol()}),!0;if(!(!r[d]||r[d].need_chunking===!0&&c._options.chunking!==!0||!r[d].enabled(c._options)))return c._debug("Opening transport:",d),c._transport=new r[d](c,c._trans_url,c._base_url),!0;c._debug("Skipping transport:",d)}},r.prototype.close=function(a,b){var c=this;if(a&&!e.userSetCode(a))throw new Error("INVALID_ACCESS_ERR");return c.readyState!==r.CONNECTING&&c.readyState!==r.OPEN?!1:(c.readyState=r.CLOSING,c._didClose(a||1e3,b||"Normal closure"),!0)},r.prototype.send=function(a){var b=this;if(b.readyState===r.CONNECTING)throw new Error("INVALID_STATE_ERR");return b.readyState===r.OPEN&&b._transport.doSend(e.quote(""+a)),!0};var s=r.websocket=function(a,b){var c=this,d=b+"/websocket";d.slice(0,5)==="https"?d="wss"+d.slice(5):d="ws"+d.slice(4),c.ri=a,c.url=d;var f=window.WebSocket||window.MozWebSocket;if(q.state>=p.beforeunload){e.log("Can't open a WebSocket connection after onbeforeunload!");return}c.ws=new f(c.url),c.ws.onmessage=function(a){c.ri._didMessage(a.data)},c.ws.onclose=function(){c.ri._didMessage(e.closeFrame(1006,"WebSocket connection broken"))}};s.prototype.doSend=function(a){this.ws.send(a)},s.prototype.doCleanup=function(){var a=this,b=a.ws;b&&(b.onmessage=b.onclose=null,b.close(),a.ri=a.ws=null)},s.enabled=function(){return!!window.WebSocket||!!window.MozWebSocket};var t=function(){};t.prototype.send_constructor=function(a){var b=this;b.send_buffer=[],b.sender=a},t.prototype.doSend=function(a){var b=this;b.send_buffer.push(a),b.send_stop||b.send_schedule()},t.prototype.send_schedule_wait=function(){var a=this,b;a.send_stop=function(){a.send_stop=null,clearTimeout(b)},b=e.delay(25,function(){a.send_stop=null,a.send_schedule()})},t.prototype.send_schedule=function(){var a=this;if(a.send_buffer.length>0){var b="["+a.send_buffer.join(",")+"]";a.send_stop=a.sender(a.trans_url,b,function(){a.send_stop=null,a.send_schedule_wait()}),a.send_buffer=[]}},t.prototype.send_destructor=function(){var a=this;a._send_stop&&a._send_stop(),a._send_stop=null};var u=function(b,c,d){var f=this;if(!("_send_form"in f)){var g=f._send_form=a.createElement("form"),h=f._send_area=a.createElement("textarea");h.name="d",g.style.display="none",g.style.position="absolute",g.method="POST",g.enctype="application/x-www-form-urlencoded",g.acceptCharset="UTF-8",g.appendChild(h),a.body.appendChild(g)}var g=f._send_form,h=f._send_area,i="a"+e.random_string(8);g.target=i,g.action=b+"/jsonp_send?i="+i;var j;try{j=a.createElement('<iframe name="'+i+'">')}catch(k){j=a.createElement("iframe"),j.name=i}j.id=i,g.appendChild(j),j.style.display="none",h.value=c,g.submit();var l=function(a){if(!j.onerror)return;j.onreadystatechange=j.onerror=j.onload=null,e.delay(500,function(){j.parentNode.removeChild(j),j=null}),h.value=null,d()};return j.onerror=j.onload=l,j.onreadystatechange=function(a){j.readyState=="complete"&&l()},l},v=function(a,b,c){var d=function(a,b,d){(a.readyState===4||d)&&c(a.status,d)};return e.createXHR("POST",a+"/xhr_send",b,d)},w=function(a,b,c){var d=function(a,b,d){(a.readyState===4||d)&&c(a.status,d)},f=window.XDomainRequest?e.createXDR:e.createXHR;return f("POST",a+"/xhr_send",b,d)},x=function(b,c){var d,f=a.createElement("script"),g,h=function(a){g&&(g.parentNode.removeChild(g),g=null),f&&(clearTimeout(d),f.parentNode.removeChild(f),f.onreadystatechange=f.onerror=f.onload=f.onclick=null,f=null,c(a),c=null)},i=!1,j=null;f.id="a"+e.random_string(8),f.src=b,f.type="text/javascript",f.charset="UTF-8",f.onerror=function(a){j||(j=setTimeout(function(){i||h(e.closeFrame(1006,"JSONP script loaded abnormally (onerror)"))},1e3))},f.onload=function(a){h(e.closeFrame(1006,"JSONP script loaded abnormally (onload)"))},f.onreadystatechange=function(a){if(/loaded|closed/.test(f.readyState)){if(f&&f.htmlFor&&f.onclick){i=!0;try{f.onclick()}catch(b){}}f&&h(e.closeFrame(1006,"JSONP script loaded abnormally (onreadystatechange)"))}};if(typeof f.async=="undefined"&&a.attachEvent)if(!/opera/i.test(navigator.userAgent)){try{f.htmlFor=f.id,f.event="onclick"}catch(k){}f.async=!0}else g=a.createElement("script"),g.text="try{var a = document.getElementById('"+f.id+"'); if (a)a.onerror();}catch(x){};",f.async=g.async=!1;typeof f.async!="undefined"&&(f.async=!0),d=setTimeout(function(){h(e.closeFrame(1006,"JSONP script loaded abnormally (timeout)"))},35e3);var l=a.getElementsByTagName("head")[0];return l.insertBefore(f,l.firstChild),g&&l.insertBefore(g,l.firstChild),h},y=r["jsonp-polling"]=function(a,b){e.polluteGlobalNamespace();var c=this;c.ri=a,c.trans_url=b,c.send_constructor(u),c._schedule_recv()};y.prototype=new t,y.prototype._schedule_recv=function(){var a=this,b=function(b){a._recv_stop=null,b&&(a._is_closing||a.ri._didMessage(b)),a._is_closing||a._schedule_recv()};a._recv_stop=z(a.trans_url+"/jsonp",x,b)},y.enabled=function(){return!0},y.need_body=!0,y.prototype.doCleanup=function(){var a=this;a._is_closing=!0,a._recv_stop&&a._recv_stop(),a.ri=a._recv_stop=null,a.send_destructor()};var z=function(a,c,d){var f="a"+e.random_string(6),h=a+"?c="+escape(g+"."+f),i=function(a){delete b[g][f],d(a)},j=c(h,i);b[g][f]=j;var k=function(){b[g][f]&&b[g][f](e.closeFrame(1e3,"JSONP user aborted read"))};return k},A=r["xhr-streaming"]=function(a,b){var c=this;c.ri=a,c.trans_url=b,c.send_constructor(w),c.poll=new R(a,W,b+"/xhr_streaming",{cors:!0})};A.prototype=new t,A.prototype.doCleanup=function(){var a=this;a.poll&&(a.poll.abort(),a.poll=null)},A.enabled=function(a){return a.cookie!==!0&&window.XDomainRequest?!0:window.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest?!0:!1},A.need_chunking=!0;var B=r["xhr-polling"]=function(a,b){var c=this;c.ri=a,c.trans_url=b,c.send_constructor(w),c.poll=new R(a,W,b+"/xhr",{cors:!0})};B.prototype=new t,B.prototype.doCleanup=function(){var a=this;a.poll&&(a.poll.abort(),a.poll=null)},B.enabled=A.enabled;var C=function(){};C.prototype.i_constructor=function(a,b,c){var d=this;d.ri=a,d.origin=e.getOrigin(c),d.base_url=c,d.trans_url=b;var f=c+"/iframe.html";d.ri._options.devel&&(f+="?t="+ +(new Date)),d.window_id=e.random_string(8),f+="#"+d.window_id,d.iframeObj=e.createIframe(f,function(a){d.ri._didClose(1006,"Unable to load an iframe ("+a+")")}),d.onmessage_cb=e.bind(d.onmessage,d),e.attachMessage(d.onmessage_cb)},C.prototype.doCleanup=function(){var a=this;if(a.iframeObj){e.detachMessage(a.onmessage_cb);try{a.iframeObj.iframe.contentWindow&&a.postMessage("c")}catch(b){}a.iframeObj.cleanup(),a.iframeObj=null,a.onmessage_cb=a.iframeObj=null}},C.prototype.onmessage=function(a){var b=this;if(a.origin!==b.origin)return;var c=a.data.slice(0,8),d=a.data.slice(8,9),e=a.data.slice(9);if(c!==b.window_id)return;switch(d){case"s":b.iframeObj.loaded(),b.postMessage("s",JSON.stringify([r.version,b.protocol,b.trans_url,b.base_url]));break;case"t":b.ri._didMessage(e)}},C.prototype.postMessage=function(a,b){var c=this;c.iframeObj.iframe.contentWindow.postMessage(c.window_id+a+(b||""),c.origin)},C.prototype.doSend=function(a){this.postMessage("m",a)},C.enabled=function(){var a=navigator&&navigator.userAgent&&navigator.userAgent.indexOf("Konqueror")!==-1;return(typeof b.postMessage=="function"||typeof b.postMessage=="object")&&!a};var D,E=function(a,c){parent!==b?parent.postMessage(D+a+(c||""),"*"):e.log("Can't postMessage, no parent window.",a,c)},F=function(){};F.prototype._didClose=function(a,b){E("t",e.closeFrame(a,b))},F.prototype._didMessage=function(a){E("t",a)},F.prototype._doSend=function(a){this._transport.doSend(a)},F.prototype._doCleanup=function(){this._transport.doCleanup()},r.bootstrap_iframe=function(){var b;D=a.location.hash.slice(1);var c=function(a){if(a.source!==parent)return;var c=a.data.slice(0,8),d=a.data.slice(8,9),f=a.data.slice(9);if(c!==D)return;switch(d){case"s":var g=JSON.parse(f),h=g[0],i=g[1],j=g[2],k=g[3];h!==r.version&&e.log('Incompatibile SockJS! Main site uses: "'+h+'", the iframe:'+' "'+r.version+'".'),b=new F,b._transport=new F[i](b,j,k);break;case"m":b._doSend(f);break;case"c":b._doCleanup(),b=null}};e.attachMessage(c),E("s")};var G=function(a,b,c){var d=new W(a+"/chunking_test",{cors:c}),f=0;d.onmessage=function(a){var b=a.responsetext.split("h\n").length;a.readystate===3&&b>0&&b<6&&(f=b,d.abort())},d.onclose=function(a){d=d.onmessage=d.onclose=null,e.log("Chunking test: "+(f?"passed":"failed")+" ("+f+" chunk received)"),b(!!f)}},H=F["w-iframe-chunking-test"]=function(a,b,c){G(c,function(b){a._didMessage("m"+b),a._didClose()},!1)};H.prototype.doCleanup=function(){};var I=r.chunkingTest=function(c,d,f){c=e.amendUrl(c);if(b.XDomainRequest||b.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest){G(c,d,!0);return}a.body?J(c,d,f):e.attachEvent("load",function(){J(c,d,f)})},J=function(a,b,c){if(C.enabled()){var d=new C;d.protocol="w-iframe-chunking-test";var e=function(a){d&&(b(a==="mtrue"),d.doCleanup(),d=null)},f={_options:c||{},_didClose:e,_didMessage:e};d.i_constructor(f,"",a);return}setTimeout(function(){b(!1)},0);return},K=function(){var a,b=0;return function(c,d){var e=(new Date).getTime();e-b>1e4?I(c,function(c){a=c,b=(new Date).getTime(),d(a)}):setTimeout(function(){d(a)},0)}}(),L=r["iframe-eventsource"]=function(){var a=this;a.protocol="w-iframe-eventsource",a.i_constructor.apply(a,arguments)};L.prototype=new C,L.enabled=function(){return"EventSource"in window&&C.enabled()},L.need_chunking=!0,L.need_body=!0;var M=F["w-iframe-eventsource"]=function(a,b){var c=this;c.ri=a,c.trans_url=b,c.send_constructor(v),c.poll=new R(a,S,b+"/eventsource")};M.prototype=new t,M.prototype.doCleanup=function(){var a=this;a.poll&&(a.poll.abort(),a.poll=null)};var N=r["iframe-xhr-polling"]=function(){var a=this;a.protocol="w-iframe-xhr-polling",a.i_constructor.apply(a,arguments)};N.prototype=new C,N.enabled=function(){return window.XMLHttpRequest&&C.enabled()},N.need_body=!0;var O=F["w-iframe-xhr-polling"]=function(a,b){var c=this;c.trans_url=b,c.send_constructor(v),c.poll=new R(a,W,b+"/xhr",{cors:!1})};O.prototype=new t,O.prototype.doCleanup=function(){var a=this;a.poll&&(a.poll.abort(),a.poll=null)};var P=r["iframe-htmlfile"]=function(){var a=this;a.protocol="w-iframe-htmlfile",a.i_constructor.apply(a,arguments)};P.prototype=new C,P.enabled=function(a){var b=U();return a.cookie!==!1&&C.enabled()},P.need_chunking=!0,P.need_body=!0;var Q=F["w-iframe-htmlfile"]=function(a,b){var c=this;c.trans_url=b,c.send_constructor(v),c.poll=new R(a,V,b+"/htmlfile")};Q.prototype=new t,Q.prototype.doCleanup=function(){var a=this;a.poll&&(a.poll.abort(),a.poll=null)};var R=function(a,b,c,d){var e=this;e.ri=a,e.Receiver=b,e.recv_url=c,e.opts=d,e._scheduleRecv()};R.prototype._scheduleRecv=function(){var a=this,b=a.poll=new a.Receiver(a.recv_url,a.opts),c=0;b.onmessage=function(b){c+=1,a.ri._didMessage(b.data)},b.onclose=function(c){a.poll=b=b.onmessage=b.onclose=null,a.poll_is_closing||(c.reason==="permanent"?a.ri._didClose(1006,"Polling error ("+c.reason+")"):a._scheduleRecv())}},R.prototype.abort=function(){var a=this;a.poll_is_closing=!0,a.poll&&a.poll.abort()};var S=function(a){var b=this,c=new EventSource(a);c.onmessage=function(a){b.dispatchEvent(new d("message",{data:unescape(a.data)}))},b.es_close=c.onerror=function(a,f){var g=f?"user":c.readyState!==2?"network":"permanent";b.es_close=c.onmessage=c.onerror=null,c.close(),c=null,e.delay(200,function(){b.dispatchEvent(new d("close",{reason:g}))})}};S.prototype=new c,S.prototype.abort=function(){var a=this;a.es_close&&a.es_close({},!0)};var T,U=function(){if(T===undefined)if("ActiveXObject"in window)try{T=!!(new ActiveXObject("htmlfile"))}catch(a){}else T=!1;return T},V=function(a){var c=this;e.polluteGlobalNamespace(),c.id="a"+e.random_string(6,26),a+=(a.indexOf("?")===-1?"?":"&")+"c="+escape(g+"."+c.id);var f=U()?e.createHtmlfile:e.createIframe,h;b[g][c.id]={start:function(){h.loaded()},message:function(a){c.dispatchEvent(new d("message",{data:a}))},stop:function(){c.iframe_close({},"network")}},c.iframe_close=function(a,e){h.cleanup(),c.iframe_close=h=null,delete b[g][c.id],c.dispatchEvent(new d("close",{reason:e}))},h=f(a,function(a){c.iframe_close({},"permanent")})};V.prototype=new c,V.prototype.abort=function(){var a=this;a.iframe_close&&a.iframe_close({},"user")};var W=function(a,c){var f=this,g=0,h=function(a,b,c){if(a.readyState===3||a.readyState===4){try{var e=a.responseText,h=a.status}catch(i){}if(e&&h===200){var j=[];for(;;){var k=e.slice(g),l=k.indexOf("\n");if(l===-1)break;g+=l+1;var m=k.slice(0,l);f.dispatchEvent(new d("message",{data:m,readystate:a.readyState,responsetext:e}))}}}if(a.readyState===4||c){var n=c?"user":a.status===200?"network":"permanent";f.xhr_close=null,f.dispatchEvent(new d("close",{reason:n}))}},i=c.cors&&b.XDomainRequest?e.createXDR:e.createXHR;f.xhr_close=i("POST",a,null,h)};return W.prototype=new c,W.prototype.abort=function(){var a=this;a.xhr_close&&a.xhr_close(!0)},r.getUtils=function(){return e},r.getIframeTransport=function(){return C},r}(),"_sockjs_onload"in window&&setTimeout(_sockjs_onload,1);var log;window.console&&console.log?log=function(){console.log.apply(console,arguments)}:log=function(){};var util={hasProp:function(a,b){return Object.prototype.hasOwnProperty.call(Object(a),b)},extend:function(a,b){function d(){this.constructor=a}if(a===undefined||b===undefined)return a;for(var c in b)util.hasProp(b,c)&&(a[c]=b[c]);return d.prototype=b.prototype,a.prototype=new d,a.__super__=b.prototype,a},generateGuid:function(){var a="",b="abcdefghijklmnopqrstuvwxyz0123456789";for(var c=0;c<12;c++)a+=b.charAt(Math.floor(Math.random()*b.length));return a},typeOf:function(a){var b=typeof a;return b==="object"&&(a?typeof a.length=="number"&&!a.propertyIsEnumerable("length")&&typeof a.splice=="function"&&(b="array"):b="null"),b},findOps:function(a){var b=[];for(var c in a)typeof a[c]=="function"&&util.isValid(c)&&b.push(c);return b},isValid:function(a){return a.charAt(0)!=="_"},inherit:function(a,b){var c=function(){};c.prototype=b.prototype,a.prototype=new c},stringify:JSON.stringify,parse:JSON.parse,log:log,info:function(){util.log.apply(this,arguments)},warn:function(){util.log.apply(this,arguments)},error:function(){util.log.apply(this,arguments)},setLogLevel:function(a){a<3&&(util.info=function(){}),a<2&&(util.warn=function(){}),a<1&&(util.error=function(){})},refCallback:function(a){var b=function(){var b=[].slice.apply(arguments);a._call("callback",b)};return b._reference=a,b._toDict=function(){return a._toDict()},b.callback=b,b}},Serializer={serialize:function(a,b){var c=util.typeOf(b),d;switch(c){case"object":if(b===null)d=null;else if("_toDict"in b)d=b._toDict();else{var e=util.findOps(b);if(e.length>0)d=a._storeObject(b,e)._toDict();else{d={};for(var f in b){var g=b[f];d[f]=Serializer.serialize(a,g)}}}break;case"array":d=[];for(var h=0,i=b.length;h<i;h++){var g=b[h];d.push(Serializer.serialize(a,g))}break;case"function":util.hasProp("_reference")?d=b._toDict():d=a._storeObject({callback:b},["callback"])._toDict();break;default:d=b}return d},unserialize:function(a,b){var c;for(var d in b){var e=b[d];if(typeof e=="object")if(util.hasProp(e,"ref")){var f=new Reference(a,e.ref,e.operations);e.operations&&e.operations.length===1&&e.operations[0]==="callback"?b[d]=util.refCallback(f):b[d]=f}else Serializer.unserialize(a,e)}}};Reference.prototype._toDict=function(a){var b={},c=this._address;return a&&(c=c.slice(),c.push(a)),b.ref=c,c.length<4&&(b.operations=this._operations),b},Reference.prototype._call=function(a,b){util.info("Calling",this._address+"."+a);var c=this._toDict(a);this._bridge._send(b,c)},Connection.prototype.redirector=function(){var a=this;if(this.options.tcp){var b=url.parse(this.options.redirector);http.get({host:b.hostname,port:b.port,path:"/redirect/"+this.options.apiKey},function(b){var c="";b.on("data",function(a){c+=a}),b.on("end",function(){try{var b=JSON.parse(c);a.options.host=b.data.bridge_host,a.options.port=b.data.bridge_port,!a.options.host||!a.options.port?util.error("Could not find host and port in JSON"):a.establishConnection()}catch(d){util.error("Unable to parse redirector response "+c)}})}).on("error",function(a){util.error("Unable to contact redirector")})}else{window.bridgeHost=function(b,c,d){a.options.host=c,a.options.port=parseInt(d,10),!a.options.host||!a.options.port?util.error("Could not find host and port in JSON"):a.establishConnection(),delete window.bridgeHost};var c=document.createElement("script");c.setAttribute("src",this.options.redirector+"/redirect/"+this.options.apiKey+"/jsonp"),document.getElementsByTagName("head")[0].appendChild(c)}},Connection.prototype.reconnect=function(){util.info("Attempting reconnect");var a=this;this.interval<32768&&setTimeout(function(){a.establishConnection()},this.interval*=2)},Connection.prototype.establishConnection=function(){var a=this,b;this.options.tcp?(util.info("Starting TCP connection",this.options.host,this.options.port),b=(new TCP(this.options)).sock):(util.info("Starting SockJS connection"),b=new SockJS("http://"+this.options.host+":"+this.options.port+"/bridge",this.options.protocols,this.options.sockjs)),b.bridge=this.bridge,b.onmessage=function(c){util.info("clientId and secret received",c.data);var d=c.data.toString().split("|");d.length!==2?a.processMessage(c):(a.clientId=d[0],a.secret=d[1],a.interval=400,a.sock.processQueue(b,a.clientId),a.sock=b,a.sock.onmessage=a.processMessage,util.info("Handshake complete"),a.bridge._ready||(a.bridge._ready=!0,a.bridge.emit("ready")))},b.onopen=function(){util.info("Beginning handshake");var c=util.stringify({command:"CONNECT",data:{session:[a.clientId||null,a.secret||null],api_key:a.options.apiKey}});b.send(c)},b.onclose=function(){util.warn("Connection closed"),a.sock=a.sockBuffer,a.options.reconnect&&a.reconnect()}},Connection.prototype.processMessage=function(a){try{util.info("Received",a.data),a=util.parse(a.data),Serializer.unserialize(this.bridge,a);var b=a.destination;if(!b){util.warn("No destination in message",a);return}this.bridge._execute(a.destination._address,a.args)}catch(c){util.error("Message parsing failed: ",c.message,c.stack)}},Connection.prototype.sendCommand=function(a,b){var c=util.stringify({command:a,data:b});util.info("Sending",c),this.sock.send(c)},Connection.prototype.start=function(){!this.options.host||!this.options.port?this.redirector():this.establishConnection()},SockBuffer.prototype.send=function(a){this.buffer.push(a)},SockBuffer.prototype.processQueue=function(a,b){for(var c=0,d=this.buffer.length;c<d;c++)a.send(this.buffer[c].replace('"client",null','"client","'+b+'"'));this.buffer=[]},Bridge.prototype._execute=function(a,b){var c=this._store[a[2]],d=c[a[3]];d?d.apply(c,b):util.warn("Could not find object to handle",a)},Bridge.prototype._storeObject=function(a,b){var c=util.generateGuid();return this._store[c]=a,new Reference(this,["client",this._connection.clientId,c],b)},Bridge.prototype.on=function(a,b){return util.hasProp(this._events,a)||(this._events[a]=[]),this._events[a].push(b),this},Bridge.prototype.emit=function(a,b){if(util.hasProp(this._events,a)){var c=this._events[a].slice(0);for(var d=0,e=c.length;d<e;d++)c[d].apply(this,b===undefined?[]:b)}return this},Bridge.prototype.removeEvent=function(a,b){if(util.hasProp(this._events,a))for(var c=0,d=this._events[a].length;c<d;c++)this._events[a][c]===b&&this._events[a].splice(c,1);return this},Bridge.prototype._send=function(a,b){this._connection.sendCommand("SEND",{args:Serializer.serialize(this,a),destination:b})},Bridge.prototype.publishService=function(a,b,c){a==="system"?util.error("Invalid service name: "+a):(this._store[a]=b,this._connection.sendCommand("JOINWORKERPOOL",{name:a,callback:Serializer.serialize(this,c)}))},Bridge.prototype.getService=function(a,b){this._connection.sendCommand("GETOPS",{name:a,callback:Serializer.serialize(this,b)})},Bridge.prototype.getChannel=function(a,b){var c=this;this._connection.sendCommand("GETCHANNEL",{name:a,callback:Serializer.serialize(this,function(a,d){d=d.split(":")[1];if(a===null){b(null,d);return}b(new Reference(c,["channel",d,"channel:"+d],a._operations),d)})})},Bridge.prototype.joinChannel=function(a,b,c){this._connection.sendCommand("JOINCHANNEL",{name:a,handler:Serializer.serialize(this,b),callback:Serializer.serialize(this,c)})},Bridge.prototype.leaveChannel=function(a,b,c){this._connection.sendCommand("LEAVECHANNEL",{name:a,handler:Serializer.serialize(this,b),callback:Serializer.serialize(this,c)})},Bridge.prototype.ready=function(a){this._ready?a():this.on("ready",a)},Bridge.prototype.connect=function(a){return a&&this.ready(a),this._connection.start(),this}
@@ -0,0 +1,14 @@
1
+ $(function(){
2
+
3
+ bridge.getService('chatserver', function(chat){
4
+ chat.join('lobby', {msg: function(name, msg){
5
+ $('#messages').append(name + ': ' + msg + '<br>');
6
+ }}, function(lobby) {
7
+ $('#y').click(function(){
8
+ lobby.msg('someone', $('#x').val());
9
+ $('#x').val('');
10
+ });
11
+ });
12
+ });
13
+
14
+ });
@@ -0,0 +1,5 @@
1
+ <div id="messages"></div>
2
+
3
+
4
+ <input type="text" id="x">
5
+ <input type="button" id="y" value="submit">
@@ -0,0 +1,23 @@
1
+ <!DOCTYPE HTML>
2
+ <html lang="en">
3
+ <head>
4
+ <title></title>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6
+ <meta http-equiv="Content-Language" content="en-us">
7
+ <meta name="description" content="">
8
+ <meta name="keywords" content="">
9
+
10
+
11
+ <link href="/css/style.css" rel="stylesheet" type="text/css">
12
+
13
+ <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
14
+ <script type="text/javascript" src="/js/bridge.min.js"></script>
15
+ <script type="text/javascript" src="/js/all.js"></script>
16
+ <script type="text/javascript" src="/js/<%= title %>.js"></script>
17
+
18
+ </head>
19
+
20
+ <body>
21
+ <%= yield %>
22
+ </body>
23
+ </html>
@@ -1,4 +1,4 @@
1
- require File.expand_path('../lib/bb/version', __FILE__)
1
+ require File.expand_path('../lib/version', __FILE__)
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'flotype-bridge'
data/lib/bridge.rb CHANGED
@@ -1,130 +1,277 @@
1
- require 'bb/svc'
2
- require 'bb/conn'
3
- require 'bb/ref'
4
- require 'bb/sys'
5
- require 'bb/core'
6
- require 'bb/localref'
7
- require 'bb/callback'
8
- require 'bb/util'
1
+ require 'util.rb'
2
+ require 'serializer.rb'
3
+ require 'connection.rb'
4
+ require 'reference.rb'
9
5
 
10
6
  require 'eventmachine'
11
7
 
8
+ # == Flotype Bridge
9
+ #
10
+ # Bridge is a cross-language and platform framework for realtime communication and RPC
11
+ #
12
+ # The Bridge ruby client is fully featured and has full compatibility with all other Bridge clients
13
+ #
14
+ # Bridge::Bridge
15
+
12
16
  module Bridge
13
17
 
14
- # Expects to be called inside an EventMachine block in lieu of
15
- # EM::connect.
16
- # @param [Hash] configuration options
17
- @options = {
18
- 'reconnect' => true,
19
- 'redir_host' => 'redirector.flotype.com',
20
- 'redir_port' => 80,
21
- 'log_level' => 3, # 0 for no output.
22
- }
23
- def self.initialize(options = {})
24
- Util::log 'initialize called.'
25
- @options = @options.merge(options)
18
+ class Bridge
19
+
20
+ attr_accessor :options, :connection, :queue, :store, :is_ready #:nodoc:
21
+
22
+ # :call-seq:
23
+ # new(options={})
24
+ #
25
+ # Create an instance of the Bridge object. This object will be used for Bridge interactions
26
+ #
27
+ # Bridge#connect must be called to connect to the Bridge server
28
+ #
29
+ # === Attributes
30
+ #
31
+ # +options+:: Optional hash of arguments specified below
32
+ #
33
+ # === Options
34
+ #
35
+ # Options hash passed to initialize to modify Bridge behavior
36
+ #
37
+ # <tt>:redirector => 'http://redirector.flotype.com'</tt>:: Address to specify Bridge redirector server. The redirector server helps route the client to the appropriate Bridge server
38
+ # <tt>:reconnect => true</tt>:: Enable automatic reconnection to Bridge server
39
+ # <tt>:log => 2</tt>:: An integer specifying log level. 3 => Log all, 2 => Log warnings, 1 => Log errors, 0 => No logging output
40
+ # <tt>:host => nil</tt>:: The hostname of the Bridge server to connect to. Overrides +:redirector+ when both +:host+ and +:port+ are specified
41
+ # <tt>:port => nil</tt>:: An integer specifying the port of the Bridge server to connect to. Overrides +:redirector+ when both +:host+ and +:port+ are specified
42
+ #
43
+ def initialize(options = {})
44
+
45
+ # Set default options
46
+ @options = {
47
+ :redirector => 'http://redirector.flotype.com',
48
+ :reconnect => true,
49
+ :log => 2, # 0 for no output
50
+ }
51
+
52
+ @options = @options.merge(options)
53
+
54
+ Util.set_log_level(@options[:log])
55
+
56
+ @store = {}
57
+ # Initialize system service call
58
+ @store['system'] = SystemService.new(self)
59
+
60
+ # Indicates whether server is connected and handshaken
61
+ @is_ready = false
62
+
63
+ # Create connection object
64
+ @connection = Connection.new(self)
65
+
66
+ # Store ready event handler
67
+ @queue = []
26
68
 
27
- if !(@options.has_key? 'api_key')
28
- raise ArgumentError, 'No API key specified.'
29
69
  end
30
70
 
31
- if Util::has_keys?(@options, 'host', 'port')
32
- EM::connect(@options['host'], @options['port'], Bridge::Conn)
33
- else
34
- # Support for redirector.
35
- conn = EM::Protocols::HttpClient2.connect(@options['redir_host'],
36
- @options['redir_port'])
37
- req = conn.get({:uri => "/redirect/#{@options['api_key']}"})
38
- req.callback do |obj|
39
- obj = JSON::parse obj.content
40
- if obj.has_key?('data')
41
- obj = obj['data']
42
- EM::connect(obj['bridge_host'], obj['bridge_port'], Bridge::Conn)
71
+ def execute address, args #:nodoc:
72
+ # Retrieve stored handler
73
+ obj = @store[address[2]]
74
+ # Retrieve function in handler
75
+ func = obj.method(address[3])
76
+ if func
77
+ last = args.last
78
+ # If last argument is callable and function arity is one less than args length, pass in as block
79
+ if last.is_a? Util::CallbackReference and func.arity == args.length - 1
80
+ args.pop
81
+ func.call *args do |*args, &blk|
82
+ args << blk if blk
83
+ last.call *args
84
+ end
43
85
  else
44
- raise Exception, 'Invalid API key.'
86
+ begin
87
+ func.call *args
88
+ rescue StandardError => err
89
+ Util.error err
90
+ Util.error "Exception while calling #{address[3]}(#{args})"
91
+ end
45
92
  end
93
+ else
94
+ Util.warn "Could not find object to handle, #{address}"
46
95
  end
47
96
  end
48
- end
49
-
50
- # Accessor for @options.
51
- # @return [Object] options
52
- def self.options
53
- @options
54
- end
55
-
56
- # Similar to $(document).ready of jQuery as well as now.ready: takes
57
- # callbacks that it will call when the connection handshake has been
58
- # completed.
59
- # @param [#call] fun Callback to be called when the server connects.
60
- def self.ready fun
61
- Core::enqueue fun
62
- end
63
-
64
- # Calls a remote function specified by `dest` with `args`.
65
- # @param [Ref] dest The identifier of the remote function to call.
66
- # @param [Array] args Arguments to be passed to `dest`.
67
- def self.send dest, args
68
- Core::command(:SEND,
69
- { :args => Util::serialize(args),
70
- :destination => dest })
71
- end
97
+
98
+ def store_object handler, ops #:nodoc:
99
+ # Generate random id for callback being stored
100
+ name = Util.generate_guid
101
+ @store[name] = handler
102
+ # Return reference to stored callback
103
+ Reference.new(self, ['client', @connection.client_id, name], ops)
104
+ end
105
+
106
+ def send args, destination #:nodoc:
107
+ @connection.send_command(:SEND, { :args => Serializer.serialize(self, args), :destination => destination })
108
+ end
72
109
 
73
- # Broadcasts the availability of certain functionality specified by a
74
- # proc `fun` under the name of `svc`.
75
- def self.publish_service svc, handler, fun = nil
76
- if svc == 'system'
77
- Util::err("Invalid service name: #{svc}")
78
- else
79
- obj = { :name => svc}
80
- if fun.respond_to? :call
81
- obj[:callback] = Util::cb(fun)
110
+ # :call-seq:
111
+ # publish_service(name, handler) { |name| block }
112
+ #
113
+ # Publishes a ruby object or module as a Bridge service with the given name.
114
+ #
115
+ # If a block is given, calls the given block with the name of the published service upon publish success
116
+ #
117
+ # === Attributes
118
+ #
119
+ # +name+:: The name of the Bridge service the handler will be published with
120
+ # +handler+:: A ruby object or module to publish
121
+ #
122
+ def publish_service name, handler, &callback
123
+ if name == 'system'
124
+ Util.error("Invalid service name: #{name}")
125
+ else
126
+ @store[name] = handler
127
+ @connection.send_command(:JOINWORKERPOOL, {:name => name, :callback => Serializer.serialize(self, callback)})
82
128
  end
83
- Core::command(:JOINWORKERPOOL, obj)
84
- Core::store(svc, Bridge::LocalRef.new([svc], handler))
85
129
  end
86
- end
87
130
 
88
- # Join the channel specified by `channel`. Messages from this channel
89
- # will be passed in to a handler specified by `handler`. The callback
90
- # `fun` is to be called to confirm successful joining of the channel.
91
- def self.join_channel channel, handler, fun = nil
92
- obj = { :name => channel, :handler => Util::local_ref(handler)}
93
- if fun.respond_to? :call
94
- obj[:callback] = Util::cb(fun)
131
+ # :call-seq:
132
+ # get_service(name) -> service
133
+ # get_service(name) { |service, name| block }
134
+ #
135
+ # Retrives a service published to Bridge with the given name.
136
+ #
137
+ # If multiple Bridge clients have a published a service, the service is retrieved from one of the publishers round-robin.
138
+ #
139
+ # Note that if the requested service does not exist, an object is still returned, however attempts to make method calls on the object will result in a remote error.
140
+ #
141
+ # Returns the requested service.
142
+ #
143
+ # If a block is given, calls the given block with the requested service and service name.
144
+ #
145
+ # === Attributes
146
+ #
147
+ # +name+:: The name of the Bridge service being requested
148
+ #
149
+ def get_service name, &callback
150
+ ref = Reference.new(self, ['named', name, name])
151
+ callback.call(ref, name) if callback
152
+ return ref
95
153
  end
96
- Core::command(:JOINCHANNEL, obj)
97
- end
98
154
 
99
- # Leave a channel.
100
- def self.leave_channel channel, handler, fun = nil
101
- obj = { :name => channel, :handler => Util::local_ref(handler)}
102
- if fun.respond_to? :call
103
- obj[:callback] = Util::cb(fun)
155
+ # :call-seq:
156
+ # get_channel(name) -> channel
157
+ # get_channel(name) { |channel, name| block }
158
+ #
159
+ # Retrives a channel from Bridge with the given name.
160
+ #
161
+ # Calling a method on the channel object will result in the given method being executed on all clients that have been joined to the channel.
162
+ #
163
+ # Note that if the requested channel does not exist or is empty, an object is still returned, however attempts to make method calls on the object will result in a remote error.
164
+ #
165
+ # Returns the requested channel.
166
+ #
167
+ # If a block is given, calls the given block with the requested channel and channel name.
168
+ #
169
+ # === Attributes
170
+ #
171
+ # +name+:: The name of the Bridge channel being requested
172
+ #
173
+ def get_channel name, &callback
174
+ # Send GETCHANNEL command in order to establih link for channel if client is not member
175
+ @connection.send_command(:GETCHANNEL, {:name => name})
176
+ ref = Reference.new(self, ['channel', name, "channel:#{name}"])
177
+ callback.call(ref, name) if callback
178
+ return ref
179
+ end
180
+
181
+ # :call-seq:
182
+ # join_channel(name, handler) { |channel, name| block }
183
+ #
184
+ # Provides a remote object, ruby object or module as a receiver for methods calls on a Bridge channel.
185
+ #
186
+ # The given handler can be a remote object, in which case the Bridge client that created the remote object will be joined to the channel. Method calls to the channel will be not be proxied through this client but go directly to the source of the remote object.
187
+ #
188
+ # If a block is given, calls the given block with the joined channel and channel name.
189
+ #
190
+ # === Attributes
191
+ #
192
+ # +name+:: The name of the Bridge channel the handler will recieve methods calls for
193
+ # +handler+:: A remote object, ruby object or module to handle method calls from the channel
194
+ #
195
+ def join_channel name, handler, &callback
196
+ @connection.send_command(:JOINCHANNEL, {:name => name, :handler => Serializer.serialize(self, handler), :callback => Serializer.serialize(self, callback)})
104
197
  end
105
- Core::command(:LEAVECHANNEL, obj)
106
- end
107
-
108
- # Leave a channel.
109
- def self.leave_channel channel, handler, fun
110
- Core::command(:LEAVECHANNEL,
111
- { :name => channel,
112
- :handler => Util::local_ref(handler),
113
- :callback => Util::cb(fun) })
114
- end
115
198
 
116
- # Returns a reference to the service specified by `svc`.
117
- def self.get_service svc
118
- Core::lookup ['named', svc, svc]
119
- end
199
+ # :call-seq:
200
+ # leave_channel(name, handler)
201
+ # leave_channel(name, handler) { |name| block }
202
+ #
203
+ # Leaves a Bridge channel with the given name and handler object.
204
+ #
205
+ # The given handler can be a remote object, in which case the Bridge client that created the remote object will be removed from the channel.
206
+ #
207
+ # If a block is given, calls the given block with the name of the channel left.
208
+ #
209
+ # === Attributes
210
+ #
211
+ # +name+:: The name of the Bridge channel to leave
212
+ # +handler+:: A remote object, ruby object or module that was used to handle moethod calls from the channel
213
+ #
214
+ def leave_channel channel, handler, &callback
215
+ @connection.send_command(:LEAVECHANNEL, {:name => name, :handler => Serializer.serialize(self, handler), :callback => Serializer.serialize(self, callback)})
216
+ end
217
+
218
+ # :call-seq:
219
+ # ready { block }
220
+ #
221
+ # Calls the given block when Bridge is connected and ready.
222
+ # Calls the given block immediately if Bridge is already ready.
223
+ #
224
+ def ready &callback
225
+ if @is_ready
226
+ callback.call
227
+ else
228
+ @queue << callback
229
+ end
230
+ end
231
+
232
+ # :call-seq:
233
+ # connect { block }
234
+ #
235
+ # Starts the connection to the Bridge server.
236
+ #
237
+ # If a block is given, calls the given block when Bridge is connected and ready.
238
+ #
239
+ def connect &callback
240
+ self.ready &callback if callback
241
+ @connection.start
242
+ return self
243
+ end
120
244
 
121
- # Returns a reference to the channel specified by `channel`.
122
- def self.get_channel channel
123
- Core::lookup ['channel', channel, "channel:#{channel}"]
124
- end
245
+ # These are internal system functions, which should only be called by the
246
+ # Erlang gateway.
247
+ class SystemService #:nodoc:
248
+ def initialize bridge
249
+ @store = bridge.store
250
+ end
251
+
252
+ def hookChannelHandler name, handler, callback = nil
253
+ # Retrieve requested handler
254
+ obj = @store[handler.address[2]]
255
+ # Store under channel name
256
+ @store["channel:#{name}"] = obj
257
+ # Send callback with reference to channel and handler operations
258
+ callback.call(Reference.new(self, ['channel', name, "channel:#{name}"], Util.find_ops(obj)), name) if callback
259
+ end
125
260
 
126
- # The client's ID.
127
- def self.client_id
128
- Core::client_id
261
+ def getService name, callback
262
+ if @store.key? name
263
+ callback.call(@store[name], name)
264
+ else
265
+ callback.call(nil, name)
266
+ end
267
+ end
268
+
269
+ def remoteError msg
270
+ Util.warn msg
271
+ end
272
+ end
273
+
274
+
129
275
  end
276
+
130
277
  end