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.
- data/LICENSE +19 -0
- data/README.md +27 -4
- data/doc/Bridge/Bridge.html +624 -0
- data/doc/Bridge.html +72 -903
- data/doc/created.rid +2 -1
- data/doc/images/add.png +0 -0
- data/doc/images/brick.png +0 -0
- data/doc/images/brick_link.png +0 -0
- data/doc/images/bug.png +0 -0
- data/doc/images/bullet_black.png +0 -0
- data/doc/images/bullet_toggle_minus.png +0 -0
- data/doc/images/bullet_toggle_plus.png +0 -0
- data/doc/images/date.png +0 -0
- data/doc/images/delete.png +0 -0
- data/doc/images/find.png +0 -0
- data/doc/images/loadingAnimation.gif +0 -0
- data/doc/images/macFFBgHack.png +0 -0
- data/doc/images/package.png +0 -0
- data/doc/images/page_green.png +0 -0
- data/doc/images/page_white_text.png +0 -0
- data/doc/images/page_white_width.png +0 -0
- data/doc/images/plugin.png +0 -0
- data/doc/images/ruby.png +0 -0
- data/doc/images/tag_blue.png +0 -0
- data/doc/images/tag_green.png +0 -0
- data/doc/images/transparent.png +0 -0
- data/doc/images/wrench.png +0 -0
- data/doc/images/wrench_orange.png +0 -0
- data/doc/images/zoom.png +0 -0
- data/doc/index.html +56 -56
- data/doc/js/darkfish.js +153 -0
- data/doc/js/jquery.js +5 -3
- data/doc/js/navigation.js +142 -0
- data/doc/js/search.js +94 -0
- data/doc/js/search_index.js +1 -0
- data/doc/js/searcher.js +228 -0
- data/doc/rdoc.css +543 -0
- data/doc/table_of_contents.html +70 -0
- data/examples/chat/chat_client.rb +15 -35
- data/examples/chat/chat_server.rb +21 -21
- data/examples/chat/public/css/style.css +4 -0
- data/examples/chat/public/js/all.js +5 -0
- data/examples/chat/public/js/bridge.min.js +2 -0
- data/examples/chat/public/js/index.js +14 -0
- data/examples/chat/views/index.erb +5 -0
- data/examples/chat/views/layout.erb +23 -0
- data/flotype-bridge.gemspec +1 -1
- data/lib/bridge.rb +253 -106
- data/lib/connection.rb +161 -0
- data/lib/reference.rb +49 -0
- data/lib/serializer.rb +97 -0
- data/lib/tcp.rb +61 -0
- data/lib/util.rb +101 -0
- data/lib/version.rb +3 -0
- data/test/regression/reconnect.rb +48 -0
- data/test/regression/rpc.rb +39 -0
- data/test/regression/test.rb +58 -0
- data/test/unit/bridge_dummy.rb +26 -0
- data/test/unit/connection_dummy.rb +21 -0
- data/test/unit/reference_dummy.rb +11 -0
- data/test/unit/tcp_dummy.rb +12 -0
- data/test/unit/test.rb +20 -0
- data/test/unit/test_reference.rb +30 -0
- data/test/unit/test_serializer.rb +109 -0
- data/test/unit/test_tcp.rb +51 -0
- data/test/unit/test_util.rb +59 -0
- metadata +106 -136
- data/doc/Bridge/Callback.html +0 -495
- data/doc/Bridge/CallbackRef.html +0 -343
- data/doc/Bridge/Conn.html +0 -409
- data/doc/Bridge/Core.html +0 -762
- data/doc/Bridge/LocalRef.html +0 -509
- data/doc/Bridge/Ref.html +0 -647
- data/doc/Bridge/Service.html +0 -135
- data/doc/Bridge/Sys.html +0 -278
- data/doc/Bridge/Util.html +0 -746
- data/doc/_index.html +0 -202
- data/doc/class_list.html +0 -47
- data/doc/classes/Bridge.html +0 -263
- data/doc/classes/Bridge.src/M000001.html +0 -24
- data/doc/classes/Bridge.src/M000002.html +0 -18
- data/doc/classes/Bridge.src/M000003.html +0 -18
- data/doc/classes/Bridge.src/M000004.html +0 -20
- data/doc/classes/Bridge.src/M000005.html +0 -26
- data/doc/classes/Bridge.src/M000006.html +0 -22
- data/doc/classes/Bridge.src/M000007.html +0 -18
- data/doc/classes/Bridge.src/M000008.html +0 -18
- data/doc/css/common.css +0 -1
- data/doc/css/full_list.css +0 -55
- data/doc/css/style.css +0 -322
- data/doc/file.README.html +0 -71
- data/doc/file_list.html +0 -49
- data/doc/files/lib/bridge_rb.html +0 -108
- data/doc/fr_class_index.html +0 -27
- data/doc/fr_file_index.html +0 -27
- data/doc/fr_method_index.html +0 -34
- data/doc/frames.html +0 -13
- data/doc/js/app.js +0 -205
- data/doc/js/full_list.js +0 -173
- data/doc/method_list.html +0 -510
- data/doc/rdoc-style.css +0 -208
- data/doc/top-level-namespace.html +0 -105
- data/lib/bb/callback.rb +0 -33
- data/lib/bb/conn.rb +0 -31
- data/lib/bb/core.rb +0 -109
- data/lib/bb/localref.rb +0 -37
- data/lib/bb/ref.rb +0 -49
- data/lib/bb/svc.rb +0 -7
- data/lib/bb/sys.rb +0 -17
- data/lib/bb/util.rb +0 -93
- data/lib/bb/version.rb +0 -3
- data/tests/bb_test_helper.rb +0 -3
- data/tests/test_serialize.rb +0 -4
@@ -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,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>
|
data/flotype-bridge.gemspec
CHANGED
data/lib/bridge.rb
CHANGED
@@ -1,130 +1,277 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
require '
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
#
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
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
|
-
|
127
|
-
|
128
|
-
|
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
|