├── .hgignore ├── Makefile ├── README.md ├── package.mk ├── priv └── www │ ├── jquery-1.3.2.min.js │ ├── json.js │ ├── jsonrpc.js │ ├── prototype-1.4.0.js │ ├── rabbitmq-jquery.js │ ├── rabbitmq-prototype.js │ └── rabbitmq.js └── src ├── rabbit_jsonrpc_channel.erl ├── rabbit_jsonrpc_channel_app.erl ├── rabbit_jsonrpc_channel_app_sup.erl ├── rabbit_jsonrpc_channel_factory.erl ├── rabbit_jsonrpc_channel_sup.erl └── rabbitmq_jsonrpc_channel.app.src /.hgignore: -------------------------------------------------------------------------------- 1 | ~$ 2 | \.beam$ 3 | ^build/ 4 | ^dist/ 5 | ^cover/ 6 | ^erl_crash.dump$ 7 | ^ebin/rabbitmq_jsonrpc_channel\.app$ 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include ../umbrella.mk 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RabbitMQ JSON RPC Channel Plugin 2 | 3 | This plugin is **deprecated** and **is not under active development**. Team RabbitMQ **recommends 4 | against** its use. 5 | 6 | ## Binary Builds for RabbitMQ 3.6.0 7 | 8 | None are provided by the RabbitMQ team (see above). 9 | 10 | ## Binary Builds for RabbitMQ 3.5.x 11 | 12 | Available from the [Community Plugins archive](http://www.rabbitmq.com/community-plugins/), 13 | as well as its dependencies, [rabbitmq_jsonrpc](http://www.rabbitmq.com/community-plugins/v3.5.x/rabbitmq_jsonrpc-3.5.x-c34cb475.ez) and [erlang-rfc4627](http://www.rabbitmq.com/community-plugins/v3.5.x/rfc4627_jsonrpc-3.5.x-git5e67120.ez). 14 | 15 | ## Building From Source 16 | 17 | You can also compile and install it like any other plugin (see 18 | http://www.rabbitmq.com/plugin-development.html). 19 | 20 | To install and activate the RabbitMQ JSON-RPC channel plugin, copy all 21 | the .ez packages *except rabbit_common* from the plugin's 'dist' 22 | directory into the RabbitMQ server's plugin directory, use the command 23 | at the top to enable rabbitmq_jsonrpc_channel, and restart rabbit. 24 | 25 | Once the server starts successfully, you should be able to point your 26 | web browser at [http://localhost:15670/rpc/rabbitmq](http://localhost:15670/rpc/rabbitmq) 27 | 28 | and get an error along the lines of 29 | 30 | ``` 31 | {"version":"1.1","id":null,"error":{"name":"JSONRPCError","code":404,"message":"Procedure not found","error":["http://localhost:15670/rpc/rabbitmq",""]}} 32 | ``` 33 | 34 | which confirms that the RabbitMQ JSON-RPC channel is ready to accept 35 | requests. 36 | 37 | 38 | The plugin ships with some examples. To try these, ensure no existing 39 | RabbitMQ broker is running and then type 'make run' in the 40 | rabbitmq-jsonrpc-channel directory. This will start a RabbitMQ broker 41 | with the examples configured. You should be able to point your web 42 | browser at [http://localhost:15670/](http://localhost:15670/) 43 | and get a webpage. Clicking on "Simple JSONRPC test" will run a small 44 | test application. Successful output of http://localhost:15670/test/index.html 45 | at the time of writing is: 46 | 47 | ``` 48 | test_main 49 | {"installGenericProxy":{"name":"open","idempotent":false,"params":[{"name":"username","type":"str"},{"name":"password","type":"str"},{"name":"sessionTimeout","type":"num"},{"name":"virtualHost","type":"str"}]}} 50 | open 51 | {"installGenericProxy":{"name":"poll","idempotent":false,"params":[]}} 52 | {"installGenericProxy":{"name":"close","idempotent":false,"params":[]}} 53 | {"installGenericProxy":{"name":"call","idempotent":false,"params":[{"name":"method","type":"str"},{"name":"args","type":"arr"}]}} 54 | {"installGenericProxy":{"name":"cast","idempotent":false,"params":[{"name":"method","type":"str"},{"name":"args","type":"arr"},{"name":"content","type":"str"},{"name":"props","type":"arr"}]}} 55 | handle_channel_ready 56 | {"q1":"test-queue-1a"} 57 | {"q2":"test-queue-1b"} 58 | {"consumeOk":"aa-cons-tag1"} 59 | {"delivery":{"content":"hello, world","delivery_tag":1,"redelivered":false,"exchange":"","routing_key":"test-queue-1a","props":{"content_type":null,"content_encoding":null,"headers":null,"delivery_mode":null,"priority":null,"correlation_id":null,"reply_to":null,"expiration":null,"message_id":null,"timestamp":null,"type":null,"user_id":null,"app_id":null,"cluster_id":null}}} 60 | {"cancelOk":"aa-cons-tag1"} 61 | {"delivery2":{"content":"hello, world, again! pub 2","delivery_tag":2,"redelivered":false,"exchange":"","routing_key":"test-queue-1b","props":{"content_type":null,"content_encoding":null,"headers":null,"delivery_mode":null,"priority":null,"correlation_id":null,"reply_to":"something22","expiration":null,"message_id":null,"timestamp":null,"type":null,"user_id":null,"app_id":null,"cluster_id":null}}} 62 | {"installGenericProxy":{"name":"poll","idempotent":false,"params":[]}} 63 | {"installGenericProxy":{"name":"close","idempotent":false,"params":[]}} 64 | {"installGenericProxy":{"name":"call","idempotent":false,"params":[{"name":"method","type":"str"},{"name":"args","type":"arr"}]}} 65 | {"installGenericProxy":{"name":"cast","idempotent":false,"params":[{"name":"method","type":"str"},{"name":"args","type":"arr"},{"name":"content","type":"str"},{"name":"props","type":"arr"}]}} 66 | test basic.cancel compliance 67 | queue declare OK 68 | {"delivery4":{"content":"One","delivery_tag":1,"redelivered":false,"exchange":"","routing_key":"test-queue-4","props":{"content_type":null,"content_encoding":null,"headers":null,"delivery_mode":null,"priority":null,"correlation_id":null,"reply_to":null,"expiration":null,"message_id":null,"timestamp":null,"type":null,"user_id":null,"app_id":null,"cluster_id":null}}} 69 | {"never existed":"this-never-existed"} 70 | {"cancelled":"my-consumer"} 71 | ``` 72 | 73 | The source to the test program is in 74 | 75 | * priv/www-examples/test/test.js and 76 | * priv/www-examples/test/index.html 77 | -------------------------------------------------------------------------------- /package.mk: -------------------------------------------------------------------------------- 1 | RELEASABLE:=true 2 | DEPS:=rabbitmq-server rabbitmq-erlang-client erlang-rfc4627-wrapper rabbitmq-web-dispatch mochiweb-wrapper rabbitmq-jsonrpc 3 | 4 | define construct_app_commands 5 | cp -r $(PACKAGE_DIR)/priv $(APP_DIR) 6 | endef 7 | -------------------------------------------------------------------------------- /priv/www/jquery-1.3.2.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery JavaScript Library v1.3.2 3 | * http://jquery.com/ 4 | * 5 | * Copyright (c) 2009 John Resig 6 | * Dual licensed under the MIT and GPL licenses. 7 | * http://docs.jquery.com/License 8 | * 9 | * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) 10 | * Revision: 6246 11 | */ 12 | (function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
","
"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); 13 | /* 14 | * Sizzle CSS Selector Engine - v0.9.3 15 | * Copyright 2009, The Dojo Foundation 16 | * Released under the MIT, BSD, and GPL Licenses. 17 | * More information: http://sizzlejs.com/ 18 | */ 19 | (function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); 20 | -------------------------------------------------------------------------------- /priv/www/json.js: -------------------------------------------------------------------------------- 1 | /* 2 | http://www.JSON.org/json2.js 3 | 2010-08-25 4 | 5 | Public Domain. 6 | 7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 8 | 9 | See http://www.JSON.org/js.html 10 | 11 | 12 | This code should be minified before deployment. 13 | See http://javascript.crockford.com/jsmin.html 14 | 15 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 16 | NOT CONTROL. 17 | 18 | 19 | This file creates a global JSON object containing two methods: stringify 20 | and parse. 21 | 22 | JSON.stringify(value, replacer, space) 23 | value any JavaScript value, usually an object or array. 24 | 25 | replacer an optional parameter that determines how object 26 | values are stringified for objects. It can be a 27 | function or an array of strings. 28 | 29 | space an optional parameter that specifies the indentation 30 | of nested structures. If it is omitted, the text will 31 | be packed without extra whitespace. If it is a number, 32 | it will specify the number of spaces to indent at each 33 | level. If it is a string (such as '\t' or ' '), 34 | it contains the characters used to indent at each level. 35 | 36 | This method produces a JSON text from a JavaScript value. 37 | 38 | When an object value is found, if the object contains a toJSON 39 | method, its toJSON method will be called and the result will be 40 | stringified. A toJSON method does not serialize: it returns the 41 | value represented by the name/value pair that should be serialized, 42 | or undefined if nothing should be serialized. The toJSON method 43 | will be passed the key associated with the value, and this will be 44 | bound to the value 45 | 46 | For example, this would serialize Dates as ISO strings. 47 | 48 | Date.prototype.toJSON = function (key) { 49 | function f(n) { 50 | // Format integers to have at least two digits. 51 | return n < 10 ? '0' + n : n; 52 | } 53 | 54 | return this.getUTCFullYear() + '-' + 55 | f(this.getUTCMonth() + 1) + '-' + 56 | f(this.getUTCDate()) + 'T' + 57 | f(this.getUTCHours()) + ':' + 58 | f(this.getUTCMinutes()) + ':' + 59 | f(this.getUTCSeconds()) + 'Z'; 60 | }; 61 | 62 | You can provide an optional replacer method. It will be passed the 63 | key and value of each member, with this bound to the containing 64 | object. The value that is returned from your method will be 65 | serialized. If your method returns undefined, then the member will 66 | be excluded from the serialization. 67 | 68 | If the replacer parameter is an array of strings, then it will be 69 | used to select the members to be serialized. It filters the results 70 | such that only members with keys listed in the replacer array are 71 | stringified. 72 | 73 | Values that do not have JSON representations, such as undefined or 74 | functions, will not be serialized. Such values in objects will be 75 | dropped; in arrays they will be replaced with null. You can use 76 | a replacer function to replace those with JSON values. 77 | JSON.stringify(undefined) returns undefined. 78 | 79 | The optional space parameter produces a stringification of the 80 | value that is filled with line breaks and indentation to make it 81 | easier to read. 82 | 83 | If the space parameter is a non-empty string, then that string will 84 | be used for indentation. If the space parameter is a number, then 85 | the indentation will be that many spaces. 86 | 87 | Example: 88 | 89 | text = JSON.stringify(['e', {pluribus: 'unum'}]); 90 | // text is '["e",{"pluribus":"unum"}]' 91 | 92 | 93 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); 94 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 95 | 96 | text = JSON.stringify([new Date()], function (key, value) { 97 | return this[key] instanceof Date ? 98 | 'Date(' + this[key] + ')' : value; 99 | }); 100 | // text is '["Date(---current time---)"]' 101 | 102 | 103 | JSON.parse(text, reviver) 104 | This method parses a JSON text to produce an object or array. 105 | It can throw a SyntaxError exception. 106 | 107 | The optional reviver parameter is a function that can filter and 108 | transform the results. It receives each of the keys and values, 109 | and its return value is used instead of the original value. 110 | If it returns what it received, then the structure is not modified. 111 | If it returns undefined then the member is deleted. 112 | 113 | Example: 114 | 115 | // Parse the text. Values that look like ISO date strings will 116 | // be converted to Date objects. 117 | 118 | myData = JSON.parse(text, function (key, value) { 119 | var a; 120 | if (typeof value === 'string') { 121 | a = 122 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 123 | if (a) { 124 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 125 | +a[5], +a[6])); 126 | } 127 | } 128 | return value; 129 | }); 130 | 131 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 132 | var d; 133 | if (typeof value === 'string' && 134 | value.slice(0, 5) === 'Date(' && 135 | value.slice(-1) === ')') { 136 | d = new Date(value.slice(5, -1)); 137 | if (d) { 138 | return d; 139 | } 140 | } 141 | return value; 142 | }); 143 | 144 | 145 | This is a reference implementation. You are free to copy, modify, or 146 | redistribute. 147 | */ 148 | 149 | /*jslint evil: true, strict: false */ 150 | 151 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, 152 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 153 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 154 | lastIndex, length, parse, prototype, push, replace, slice, stringify, 155 | test, toJSON, toString, valueOf 156 | */ 157 | 158 | 159 | // Create a JSON object only if one does not already exist. We create the 160 | // methods in a closure to avoid creating global variables. 161 | 162 | if (!this.JSON) { 163 | this.JSON = {}; 164 | } 165 | 166 | (function () { 167 | 168 | function f(n) { 169 | // Format integers to have at least two digits. 170 | return n < 10 ? '0' + n : n; 171 | } 172 | 173 | if (typeof Date.prototype.toJSON !== 'function') { 174 | 175 | Date.prototype.toJSON = function (key) { 176 | 177 | return isFinite(this.valueOf()) ? 178 | this.getUTCFullYear() + '-' + 179 | f(this.getUTCMonth() + 1) + '-' + 180 | f(this.getUTCDate()) + 'T' + 181 | f(this.getUTCHours()) + ':' + 182 | f(this.getUTCMinutes()) + ':' + 183 | f(this.getUTCSeconds()) + 'Z' : null; 184 | }; 185 | 186 | String.prototype.toJSON = 187 | Number.prototype.toJSON = 188 | Boolean.prototype.toJSON = function (key) { 189 | return this.valueOf(); 190 | }; 191 | } 192 | 193 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 194 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 195 | gap, 196 | indent, 197 | meta = { // table of character substitutions 198 | '\b': '\\b', 199 | '\t': '\\t', 200 | '\n': '\\n', 201 | '\f': '\\f', 202 | '\r': '\\r', 203 | '"' : '\\"', 204 | '\\': '\\\\' 205 | }, 206 | rep; 207 | 208 | 209 | function quote(string) { 210 | 211 | // If the string contains no control characters, no quote characters, and no 212 | // backslash characters, then we can safely slap some quotes around it. 213 | // Otherwise we must also replace the offending characters with safe escape 214 | // sequences. 215 | 216 | escapable.lastIndex = 0; 217 | return escapable.test(string) ? 218 | '"' + string.replace(escapable, function (a) { 219 | var c = meta[a]; 220 | return typeof c === 'string' ? c : 221 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 222 | }) + '"' : 223 | '"' + string + '"'; 224 | } 225 | 226 | 227 | function str(key, holder) { 228 | 229 | // Produce a string from holder[key]. 230 | 231 | var i, // The loop counter. 232 | k, // The member key. 233 | v, // The member value. 234 | length, 235 | mind = gap, 236 | partial, 237 | value = holder[key]; 238 | 239 | // If the value has a toJSON method, call it to obtain a replacement value. 240 | 241 | if (value && typeof value === 'object' && 242 | typeof value.toJSON === 'function') { 243 | value = value.toJSON(key); 244 | } 245 | 246 | // If we were called with a replacer function, then call the replacer to 247 | // obtain a replacement value. 248 | 249 | if (typeof rep === 'function') { 250 | value = rep.call(holder, key, value); 251 | } 252 | 253 | // What happens next depends on the value's type. 254 | 255 | switch (typeof value) { 256 | case 'string': 257 | return quote(value); 258 | 259 | case 'number': 260 | 261 | // JSON numbers must be finite. Encode non-finite numbers as null. 262 | 263 | return isFinite(value) ? String(value) : 'null'; 264 | 265 | case 'boolean': 266 | case 'null': 267 | 268 | // If the value is a boolean or null, convert it to a string. Note: 269 | // typeof null does not produce 'null'. The case is included here in 270 | // the remote chance that this gets fixed someday. 271 | 272 | return String(value); 273 | 274 | // If the type is 'object', we might be dealing with an object or an array or 275 | // null. 276 | 277 | case 'object': 278 | 279 | // Due to a specification blunder in ECMAScript, typeof null is 'object', 280 | // so watch out for that case. 281 | 282 | if (!value) { 283 | return 'null'; 284 | } 285 | 286 | // Make an array to hold the partial results of stringifying this object value. 287 | 288 | gap += indent; 289 | partial = []; 290 | 291 | // Is the value an array? 292 | 293 | if (Object.prototype.toString.apply(value) === '[object Array]') { 294 | 295 | // The value is an array. Stringify every element. Use null as a placeholder 296 | // for non-JSON values. 297 | 298 | length = value.length; 299 | for (i = 0; i < length; i += 1) { 300 | partial[i] = str(i, value) || 'null'; 301 | } 302 | 303 | // Join all of the elements together, separated with commas, and wrap them in 304 | // brackets. 305 | 306 | v = partial.length === 0 ? '[]' : 307 | gap ? '[\n' + gap + 308 | partial.join(',\n' + gap) + '\n' + 309 | mind + ']' : 310 | '[' + partial.join(',') + ']'; 311 | gap = mind; 312 | return v; 313 | } 314 | 315 | // If the replacer is an array, use it to select the members to be stringified. 316 | 317 | if (rep && typeof rep === 'object') { 318 | length = rep.length; 319 | for (i = 0; i < length; i += 1) { 320 | k = rep[i]; 321 | if (typeof k === 'string') { 322 | v = str(k, value); 323 | if (v) { 324 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 325 | } 326 | } 327 | } 328 | } else { 329 | 330 | // Otherwise, iterate through all of the keys in the object. 331 | 332 | for (k in value) { 333 | if (Object.hasOwnProperty.call(value, k)) { 334 | v = str(k, value); 335 | if (v) { 336 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 337 | } 338 | } 339 | } 340 | } 341 | 342 | // Join all of the member texts together, separated with commas, 343 | // and wrap them in braces. 344 | 345 | v = partial.length === 0 ? '{}' : 346 | gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + 347 | mind + '}' : '{' + partial.join(',') + '}'; 348 | gap = mind; 349 | return v; 350 | } 351 | } 352 | 353 | // If the JSON object does not yet have a stringify method, give it one. 354 | 355 | if (typeof JSON.stringify !== 'function') { 356 | JSON.stringify = function (value, replacer, space) { 357 | 358 | // The stringify method takes a value and an optional replacer, and an optional 359 | // space parameter, and returns a JSON text. The replacer can be a function 360 | // that can replace values, or an array of strings that will select the keys. 361 | // A default replacer method can be provided. Use of the space parameter can 362 | // produce text that is more easily readable. 363 | 364 | var i; 365 | gap = ''; 366 | indent = ''; 367 | 368 | // If the space parameter is a number, make an indent string containing that 369 | // many spaces. 370 | 371 | if (typeof space === 'number') { 372 | for (i = 0; i < space; i += 1) { 373 | indent += ' '; 374 | } 375 | 376 | // If the space parameter is a string, it will be used as the indent string. 377 | 378 | } else if (typeof space === 'string') { 379 | indent = space; 380 | } 381 | 382 | // If there is a replacer, it must be a function or an array. 383 | // Otherwise, throw an error. 384 | 385 | rep = replacer; 386 | if (replacer && typeof replacer !== 'function' && 387 | (typeof replacer !== 'object' || 388 | typeof replacer.length !== 'number')) { 389 | throw new Error('JSON.stringify'); 390 | } 391 | 392 | // Make a fake root object containing our value under the key of ''. 393 | // Return the result of stringifying the value. 394 | 395 | return str('', {'': value}); 396 | }; 397 | } 398 | 399 | 400 | // If the JSON object does not yet have a parse method, give it one. 401 | 402 | if (typeof JSON.parse !== 'function') { 403 | JSON.parse = function (text, reviver) { 404 | 405 | // The parse method takes a text and an optional reviver function, and returns 406 | // a JavaScript value if the text is a valid JSON text. 407 | 408 | var j; 409 | 410 | function walk(holder, key) { 411 | 412 | // The walk method is used to recursively walk the resulting structure so 413 | // that modifications can be made. 414 | 415 | var k, v, value = holder[key]; 416 | if (value && typeof value === 'object') { 417 | for (k in value) { 418 | if (Object.hasOwnProperty.call(value, k)) { 419 | v = walk(value, k); 420 | if (v !== undefined) { 421 | value[k] = v; 422 | } else { 423 | delete value[k]; 424 | } 425 | } 426 | } 427 | } 428 | return reviver.call(holder, key, value); 429 | } 430 | 431 | 432 | // Parsing happens in four stages. In the first stage, we replace certain 433 | // Unicode characters with escape sequences. JavaScript handles many characters 434 | // incorrectly, either silently deleting them, or treating them as line endings. 435 | 436 | text = String(text); 437 | cx.lastIndex = 0; 438 | if (cx.test(text)) { 439 | text = text.replace(cx, function (a) { 440 | return '\\u' + 441 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 442 | }); 443 | } 444 | 445 | // In the second stage, we run the text against regular expressions that look 446 | // for non-JSON patterns. We are especially concerned with '()' and 'new' 447 | // because they can cause invocation, and '=' because it can cause mutation. 448 | // But just to be safe, we want to reject all unexpected forms. 449 | 450 | // We split the second stage into 4 regexp operations in order to work around 451 | // crippling inefficiencies in IE's and Safari's regexp engines. First we 452 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we 453 | // replace all simple value tokens with ']' characters. Third, we delete all 454 | // open brackets that follow a colon or comma or that begin the text. Finally, 455 | // we look to see that the remaining characters are only whitespace or ']' or 456 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 457 | 458 | if (/^[\],:{}\s]*$/ 459 | .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') 460 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') 461 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 462 | 463 | // In the third stage we use the eval function to compile the text into a 464 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity 465 | // in JavaScript: it can begin a block or an object literal. We wrap the text 466 | // in parens to eliminate the ambiguity. 467 | 468 | j = eval('(' + text + ')'); 469 | 470 | // In the optional fourth stage, we recursively walk the new structure, passing 471 | // each name/value pair to a reviver function for possible transformation. 472 | 473 | return typeof reviver === 'function' ? 474 | walk({'': j}, '') : j; 475 | } 476 | 477 | // If the text is not JSON parseable, then a SyntaxError is thrown. 478 | 479 | throw new SyntaxError('JSON.parse'); 480 | }; 481 | } 482 | }()); 483 | -------------------------------------------------------------------------------- /priv/www/jsonrpc.js: -------------------------------------------------------------------------------- 1 | // The contents of this file are subject to the Mozilla Public License 2 | // Version 1.1 (the "License"); you may not use this file except in 3 | // compliance with the License. You may obtain a copy of the License 4 | // at http://www.mozilla.org/MPL/ 5 | // 6 | // Software distributed under the License is distributed on an "AS IS" 7 | // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | // the License for the specific language governing rights and 9 | // limitations under the License. 10 | // 11 | // The Original Code is RabbitMQ. 12 | // 13 | // The Initial Developer of the Original Code is GoPivotal, Inc. 14 | // Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. 15 | // 16 | 17 | function JsonRpc_ModuleFactory(Support) { 18 | var requestId = 1; 19 | 20 | function Transaction(serviceUrl, methodName, params, options) { 21 | this.options = { 22 | debug: false, 23 | debugLogger: alert, 24 | timeout: 0 /* milliseconds; zero means "do not specify" */ 25 | }; 26 | Support.extend(this.options, options || {}); 27 | this.serviceUrl = serviceUrl; 28 | this.methodName = methodName; 29 | this.params = params; 30 | this.error = null; 31 | this.reply = null; 32 | this.replyReady = 0; 33 | this.callbacks = []; 34 | this.errorCallbacks = []; 35 | this.sendRequest(); 36 | } 37 | 38 | Support.extend(Transaction.prototype, 39 | { 40 | buildRequest: function() { 41 | return { version: "1.1", 42 | id: requestId++, 43 | method: this.methodName, 44 | params: this.params }; 45 | }, 46 | 47 | sendRequest: function() { 48 | var that = this; 49 | this.request = Support.ajaxPost(this.serviceUrl, 50 | this.options.timeout 51 | ? {'X-JSON-RPC-Timeout': this.options.timeout} 52 | : {}, 53 | JSON.stringify(this.buildRequest()), 54 | function (ajaxRequest) { 55 | that.receiveReply(ajaxRequest); 56 | }); 57 | }, 58 | 59 | debugLog: function(x) { 60 | if (this.options.debug) { 61 | this.options.debugLogger(x); 62 | } 63 | }, 64 | 65 | receiveReply: function(ajaxRequest) { 66 | var response; 67 | try { 68 | response = JSON.parse(ajaxRequest.responseText); 69 | } catch (e) { 70 | if (this.options.debug) { 71 | this.debugLog("Error parsing JSON:" + 72 | "\nService: " + JSON.stringify(this.serviceUrl) + 73 | "\nResponse: " + ajaxRequest.responseText); 74 | } 75 | throw e; 76 | } 77 | if (response.error) { 78 | if (this.options.debug) { 79 | this.debugLog("JsonRPC error:" + 80 | "\nService: " + JSON.stringify(this.serviceUrl) + 81 | "\nMethod: " + JSON.stringify(this.methodName) + 82 | "\nParams: " + JSON.stringify(this.params) + 83 | "\nResponse: " + JSON.stringify(response).replace(/\\n/g, "\n")); 84 | } 85 | 86 | this.error = response.error; 87 | Support.each(this.errorCallbacks, 88 | function (cb) { 89 | try { cb(response.error, true); } 90 | catch (err) {} 91 | }); 92 | } else { 93 | var reply = response.result; 94 | this.reply = reply; 95 | this.replyReady = 1; 96 | Support.each(this.callbacks, 97 | function (cb) { 98 | try { cb(reply, false); } 99 | catch (err) {} 100 | }); 101 | } 102 | }, 103 | 104 | addReplyTransformer: function(xformer) { 105 | var that = this; 106 | var oldAddCallback = that.addCallback; 107 | that.addCallback = function(cb) { 108 | return oldAddCallback.apply(that, 109 | [function(reply, is_error) { 110 | cb(is_error ? reply : xformer(reply), is_error); 111 | }]); 112 | }; 113 | return that; 114 | }, 115 | 116 | addCallback: function(cb) { 117 | this.callbacks.push(cb); 118 | if (this.replyReady) { 119 | try { cb(this.reply, false); } 120 | catch (err) {} 121 | } 122 | return this; 123 | }, 124 | 125 | addErrorCallback: function(cb) { 126 | this.errorCallbacks.push(cb); 127 | if (this.error) { 128 | try { cb(this.error, true); } 129 | catch (err) {} 130 | } 131 | return this; 132 | } 133 | }); 134 | 135 | function Service(serviceUrl, onReady, options) { 136 | this.options = { 137 | transactionClass: Transaction, 138 | timeout: 0, /* milliseconds; zero means "do not specify" */ 139 | debug: false, 140 | debugLogger: alert 141 | }; 142 | Support.extend(this.options, options || {}); 143 | this.serviceUrl = serviceUrl; 144 | var svc = this; 145 | var txn = new (this.options.transactionClass)(serviceUrl, 146 | "system.describe", 147 | [], 148 | {debug: this.options.debug, 149 | debugLogger: this.options.debugLogger}); 150 | txn.addCallback(receiveServiceDescription); 151 | function receiveServiceDescription(sd) { 152 | svc.serviceDescription = sd; 153 | Support.each(svc.serviceDescription.procs, 154 | function (desc) { 155 | svc.installGenericProxy(desc); 156 | }); 157 | onReady(); 158 | } 159 | }; 160 | 161 | Support.extend(Service.prototype, 162 | { 163 | installGenericProxy: function(desc) { 164 | if (this.options.debug) { 165 | this.options.debugLogger({installGenericProxy: desc}); 166 | } 167 | this[desc.name] = function () { 168 | var actuals = []; 169 | while (actuals.length < arguments.length) { 170 | actuals.push(arguments[actuals.length]); 171 | } 172 | while (actuals.length < desc.params.length) { 173 | actuals.push(null); 174 | } 175 | return new (this.options.transactionClass)(this.serviceUrl, 176 | desc.name, 177 | actuals, 178 | { 179 | debug: this.options.debug, 180 | debugLogger: 181 | this.options.debugLogger, 182 | timeout: this.options.timeout 183 | }); 184 | }; 185 | } 186 | }); 187 | 188 | return { 189 | Transaction: Transaction, 190 | Service: Service 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /priv/www/prototype-1.4.0.js: -------------------------------------------------------------------------------- 1 | /* Prototype JavaScript framework, version 1.4.0 2 | * (c) 2005 Sam Stephenson 3 | * 4 | * Prototype is freely distributable under the terms of an MIT-style license. 5 | * For details, see the Prototype web site: http://prototype.conio.net/ 6 | * 7 | /*--------------------------------------------------------------------------*/ 8 | 9 | var Prototype = { 10 | Version: '1.4.0', 11 | ScriptFragment: '(?:)((\n|\r|.)*?)(?:<\/script>)', 12 | 13 | emptyFunction: function() {}, 14 | K: function(x) {return x} 15 | } 16 | 17 | var Class = { 18 | create: function() { 19 | return function() { 20 | this.initialize.apply(this, arguments); 21 | } 22 | } 23 | } 24 | 25 | var Abstract = new Object(); 26 | 27 | Object.extend = function(destination, source) { 28 | for (property in source) { 29 | destination[property] = source[property]; 30 | } 31 | return destination; 32 | } 33 | 34 | Object.inspect = function(object) { 35 | try { 36 | if (object == undefined) return 'undefined'; 37 | if (object == null) return 'null'; 38 | return object.inspect ? object.inspect() : object.toString(); 39 | } catch (e) { 40 | if (e instanceof RangeError) return '...'; 41 | throw e; 42 | } 43 | } 44 | 45 | Function.prototype.bind = function() { 46 | var __method = this, args = $A(arguments), object = args.shift(); 47 | return function() { 48 | return __method.apply(object, args.concat($A(arguments))); 49 | } 50 | } 51 | 52 | Function.prototype.bindAsEventListener = function(object) { 53 | var __method = this; 54 | return function(event) { 55 | return __method.call(object, event || window.event); 56 | } 57 | } 58 | 59 | Object.extend(Number.prototype, { 60 | toColorPart: function() { 61 | var digits = this.toString(16); 62 | if (this < 16) return '0' + digits; 63 | return digits; 64 | }, 65 | 66 | succ: function() { 67 | return this + 1; 68 | }, 69 | 70 | times: function(iterator) { 71 | $R(0, this, true).each(iterator); 72 | return this; 73 | } 74 | }); 75 | 76 | var Try = { 77 | these: function() { 78 | var returnValue; 79 | 80 | for (var i = 0; i < arguments.length; i++) { 81 | var lambda = arguments[i]; 82 | try { 83 | returnValue = lambda(); 84 | break; 85 | } catch (e) {} 86 | } 87 | 88 | return returnValue; 89 | } 90 | } 91 | 92 | /*--------------------------------------------------------------------------*/ 93 | 94 | var PeriodicalExecuter = Class.create(); 95 | PeriodicalExecuter.prototype = { 96 | initialize: function(callback, frequency) { 97 | this.callback = callback; 98 | this.frequency = frequency; 99 | this.currentlyExecuting = false; 100 | 101 | this.registerCallback(); 102 | }, 103 | 104 | registerCallback: function() { 105 | setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); 106 | }, 107 | 108 | onTimerEvent: function() { 109 | if (!this.currentlyExecuting) { 110 | try { 111 | this.currentlyExecuting = true; 112 | this.callback(); 113 | } finally { 114 | this.currentlyExecuting = false; 115 | } 116 | } 117 | } 118 | } 119 | 120 | /*--------------------------------------------------------------------------*/ 121 | 122 | function $() { 123 | var elements = new Array(); 124 | 125 | for (var i = 0; i < arguments.length; i++) { 126 | var element = arguments[i]; 127 | if (typeof element == 'string') 128 | element = document.getElementById(element); 129 | 130 | if (arguments.length == 1) 131 | return element; 132 | 133 | elements.push(element); 134 | } 135 | 136 | return elements; 137 | } 138 | Object.extend(String.prototype, { 139 | stripTags: function() { 140 | return this.replace(/<\/?[^>]+>/gi, ''); 141 | }, 142 | 143 | stripScripts: function() { 144 | return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); 145 | }, 146 | 147 | extractScripts: function() { 148 | var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); 149 | var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); 150 | return (this.match(matchAll) || []).map(function(scriptTag) { 151 | return (scriptTag.match(matchOne) || ['', ''])[1]; 152 | }); 153 | }, 154 | 155 | evalScripts: function() { 156 | return this.extractScripts().map(eval); 157 | }, 158 | 159 | escapeHTML: function() { 160 | var div = document.createElement('div'); 161 | var text = document.createTextNode(this); 162 | div.appendChild(text); 163 | return div.innerHTML; 164 | }, 165 | 166 | unescapeHTML: function() { 167 | var div = document.createElement('div'); 168 | div.innerHTML = this.stripTags(); 169 | return div.childNodes[0] ? div.childNodes[0].nodeValue : ''; 170 | }, 171 | 172 | toQueryParams: function() { 173 | var pairs = this.match(/^\??(.*)$/)[1].split('&'); 174 | return pairs.inject({}, function(params, pairString) { 175 | var pair = pairString.split('='); 176 | params[pair[0]] = pair[1]; 177 | return params; 178 | }); 179 | }, 180 | 181 | toArray: function() { 182 | return this.split(''); 183 | }, 184 | 185 | camelize: function() { 186 | var oStringList = this.split('-'); 187 | if (oStringList.length == 1) return oStringList[0]; 188 | 189 | var camelizedString = this.indexOf('-') == 0 190 | ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) 191 | : oStringList[0]; 192 | 193 | for (var i = 1, len = oStringList.length; i < len; i++) { 194 | var s = oStringList[i]; 195 | camelizedString += s.charAt(0).toUpperCase() + s.substring(1); 196 | } 197 | 198 | return camelizedString; 199 | }, 200 | 201 | inspect: function() { 202 | return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'"; 203 | } 204 | }); 205 | 206 | String.prototype.parseQuery = String.prototype.toQueryParams; 207 | 208 | var $break = new Object(); 209 | var $continue = new Object(); 210 | 211 | var Enumerable = { 212 | each: function(iterator) { 213 | var index = 0; 214 | try { 215 | this._each(function(value) { 216 | try { 217 | iterator(value, index++); 218 | } catch (e) { 219 | if (e != $continue) throw e; 220 | } 221 | }); 222 | } catch (e) { 223 | if (e != $break) throw e; 224 | } 225 | }, 226 | 227 | all: function(iterator) { 228 | var result = true; 229 | this.each(function(value, index) { 230 | result = result && !!(iterator || Prototype.K)(value, index); 231 | if (!result) throw $break; 232 | }); 233 | return result; 234 | }, 235 | 236 | any: function(iterator) { 237 | var result = true; 238 | this.each(function(value, index) { 239 | if (result = !!(iterator || Prototype.K)(value, index)) 240 | throw $break; 241 | }); 242 | return result; 243 | }, 244 | 245 | collect: function(iterator) { 246 | var results = []; 247 | this.each(function(value, index) { 248 | results.push(iterator(value, index)); 249 | }); 250 | return results; 251 | }, 252 | 253 | detect: function (iterator) { 254 | var result; 255 | this.each(function(value, index) { 256 | if (iterator(value, index)) { 257 | result = value; 258 | throw $break; 259 | } 260 | }); 261 | return result; 262 | }, 263 | 264 | findAll: function(iterator) { 265 | var results = []; 266 | this.each(function(value, index) { 267 | if (iterator(value, index)) 268 | results.push(value); 269 | }); 270 | return results; 271 | }, 272 | 273 | grep: function(pattern, iterator) { 274 | var results = []; 275 | this.each(function(value, index) { 276 | var stringValue = value.toString(); 277 | if (stringValue.match(pattern)) 278 | results.push((iterator || Prototype.K)(value, index)); 279 | }) 280 | return results; 281 | }, 282 | 283 | include: function(object) { 284 | var found = false; 285 | this.each(function(value) { 286 | if (value == object) { 287 | found = true; 288 | throw $break; 289 | } 290 | }); 291 | return found; 292 | }, 293 | 294 | inject: function(memo, iterator) { 295 | this.each(function(value, index) { 296 | memo = iterator(memo, value, index); 297 | }); 298 | return memo; 299 | }, 300 | 301 | invoke: function(method) { 302 | var args = $A(arguments).slice(1); 303 | return this.collect(function(value) { 304 | return value[method].apply(value, args); 305 | }); 306 | }, 307 | 308 | max: function(iterator) { 309 | var result; 310 | this.each(function(value, index) { 311 | value = (iterator || Prototype.K)(value, index); 312 | if (value >= (result || value)) 313 | result = value; 314 | }); 315 | return result; 316 | }, 317 | 318 | min: function(iterator) { 319 | var result; 320 | this.each(function(value, index) { 321 | value = (iterator || Prototype.K)(value, index); 322 | if (value <= (result || value)) 323 | result = value; 324 | }); 325 | return result; 326 | }, 327 | 328 | partition: function(iterator) { 329 | var trues = [], falses = []; 330 | this.each(function(value, index) { 331 | ((iterator || Prototype.K)(value, index) ? 332 | trues : falses).push(value); 333 | }); 334 | return [trues, falses]; 335 | }, 336 | 337 | pluck: function(property) { 338 | var results = []; 339 | this.each(function(value, index) { 340 | results.push(value[property]); 341 | }); 342 | return results; 343 | }, 344 | 345 | reject: function(iterator) { 346 | var results = []; 347 | this.each(function(value, index) { 348 | if (!iterator(value, index)) 349 | results.push(value); 350 | }); 351 | return results; 352 | }, 353 | 354 | sortBy: function(iterator) { 355 | return this.collect(function(value, index) { 356 | return {value: value, criteria: iterator(value, index)}; 357 | }).sort(function(left, right) { 358 | var a = left.criteria, b = right.criteria; 359 | return a < b ? -1 : a > b ? 1 : 0; 360 | }).pluck('value'); 361 | }, 362 | 363 | toArray: function() { 364 | return this.collect(Prototype.K); 365 | }, 366 | 367 | zip: function() { 368 | var iterator = Prototype.K, args = $A(arguments); 369 | if (typeof args.last() == 'function') 370 | iterator = args.pop(); 371 | 372 | var collections = [this].concat(args).map($A); 373 | return this.map(function(value, index) { 374 | iterator(value = collections.pluck(index)); 375 | return value; 376 | }); 377 | }, 378 | 379 | inspect: function() { 380 | return '#'; 381 | } 382 | } 383 | 384 | Object.extend(Enumerable, { 385 | map: Enumerable.collect, 386 | find: Enumerable.detect, 387 | select: Enumerable.findAll, 388 | member: Enumerable.include, 389 | entries: Enumerable.toArray 390 | }); 391 | var $A = Array.from = function(iterable) { 392 | if (!iterable) return []; 393 | if (iterable.toArray) { 394 | return iterable.toArray(); 395 | } else { 396 | var results = []; 397 | for (var i = 0; i < iterable.length; i++) 398 | results.push(iterable[i]); 399 | return results; 400 | } 401 | } 402 | 403 | Object.extend(Array.prototype, Enumerable); 404 | 405 | Array.prototype._reverse = Array.prototype.reverse; 406 | 407 | Object.extend(Array.prototype, { 408 | _each: function(iterator) { 409 | for (var i = 0; i < this.length; i++) 410 | iterator(this[i]); 411 | }, 412 | 413 | clear: function() { 414 | this.length = 0; 415 | return this; 416 | }, 417 | 418 | first: function() { 419 | return this[0]; 420 | }, 421 | 422 | last: function() { 423 | return this[this.length - 1]; 424 | }, 425 | 426 | compact: function() { 427 | return this.select(function(value) { 428 | return value != undefined || value != null; 429 | }); 430 | }, 431 | 432 | flatten: function() { 433 | return this.inject([], function(array, value) { 434 | return array.concat(value.constructor == Array ? 435 | value.flatten() : [value]); 436 | }); 437 | }, 438 | 439 | without: function() { 440 | var values = $A(arguments); 441 | return this.select(function(value) { 442 | return !values.include(value); 443 | }); 444 | }, 445 | 446 | indexOf: function(object) { 447 | for (var i = 0; i < this.length; i++) 448 | if (this[i] == object) return i; 449 | return -1; 450 | }, 451 | 452 | reverse: function(inline) { 453 | return (inline !== false ? this : this.toArray())._reverse(); 454 | }, 455 | 456 | shift: function() { 457 | var result = this[0]; 458 | for (var i = 0; i < this.length - 1; i++) 459 | this[i] = this[i + 1]; 460 | this.length--; 461 | return result; 462 | }, 463 | 464 | inspect: function() { 465 | return '[' + this.map(Object.inspect).join(', ') + ']'; 466 | } 467 | }); 468 | var Hash = { 469 | _each: function(iterator) { 470 | for (key in this) { 471 | var value = this[key]; 472 | if (typeof value == 'function') continue; 473 | 474 | var pair = [key, value]; 475 | pair.key = key; 476 | pair.value = value; 477 | iterator(pair); 478 | } 479 | }, 480 | 481 | keys: function() { 482 | return this.pluck('key'); 483 | }, 484 | 485 | values: function() { 486 | return this.pluck('value'); 487 | }, 488 | 489 | merge: function(hash) { 490 | return $H(hash).inject($H(this), function(mergedHash, pair) { 491 | mergedHash[pair.key] = pair.value; 492 | return mergedHash; 493 | }); 494 | }, 495 | 496 | toQueryString: function() { 497 | return this.map(function(pair) { 498 | return pair.map(encodeURIComponent).join('='); 499 | }).join('&'); 500 | }, 501 | 502 | inspect: function() { 503 | return '#'; 506 | } 507 | } 508 | 509 | function $H(object) { 510 | var hash = Object.extend({}, object || {}); 511 | Object.extend(hash, Enumerable); 512 | Object.extend(hash, Hash); 513 | return hash; 514 | } 515 | ObjectRange = Class.create(); 516 | Object.extend(ObjectRange.prototype, Enumerable); 517 | Object.extend(ObjectRange.prototype, { 518 | initialize: function(start, end, exclusive) { 519 | this.start = start; 520 | this.end = end; 521 | this.exclusive = exclusive; 522 | }, 523 | 524 | _each: function(iterator) { 525 | var value = this.start; 526 | do { 527 | iterator(value); 528 | value = value.succ(); 529 | } while (this.include(value)); 530 | }, 531 | 532 | include: function(value) { 533 | if (value < this.start) 534 | return false; 535 | if (this.exclusive) 536 | return value < this.end; 537 | return value <= this.end; 538 | } 539 | }); 540 | 541 | var $R = function(start, end, exclusive) { 542 | return new ObjectRange(start, end, exclusive); 543 | } 544 | 545 | var Ajax = { 546 | getTransport: function() { 547 | return Try.these( 548 | function() {return new ActiveXObject('Msxml2.XMLHTTP')}, 549 | function() {return new ActiveXObject('Microsoft.XMLHTTP')}, 550 | function() {return new XMLHttpRequest()} 551 | ) || false; 552 | }, 553 | 554 | activeRequestCount: 0 555 | } 556 | 557 | Ajax.Responders = { 558 | responders: [], 559 | 560 | _each: function(iterator) { 561 | this.responders._each(iterator); 562 | }, 563 | 564 | register: function(responderToAdd) { 565 | if (!this.include(responderToAdd)) 566 | this.responders.push(responderToAdd); 567 | }, 568 | 569 | unregister: function(responderToRemove) { 570 | this.responders = this.responders.without(responderToRemove); 571 | }, 572 | 573 | dispatch: function(callback, request, transport, json) { 574 | this.each(function(responder) { 575 | if (responder[callback] && typeof responder[callback] == 'function') { 576 | try { 577 | responder[callback].apply(responder, [request, transport, json]); 578 | } catch (e) {} 579 | } 580 | }); 581 | } 582 | }; 583 | 584 | Object.extend(Ajax.Responders, Enumerable); 585 | 586 | Ajax.Responders.register({ 587 | onCreate: function() { 588 | Ajax.activeRequestCount++; 589 | }, 590 | 591 | onComplete: function() { 592 | Ajax.activeRequestCount--; 593 | } 594 | }); 595 | 596 | Ajax.Base = function() {}; 597 | Ajax.Base.prototype = { 598 | setOptions: function(options) { 599 | this.options = { 600 | method: 'post', 601 | asynchronous: true, 602 | parameters: '' 603 | } 604 | Object.extend(this.options, options || {}); 605 | }, 606 | 607 | responseIsSuccess: function() { 608 | return this.transport.status == undefined 609 | || this.transport.status == 0 610 | || (this.transport.status >= 200 && this.transport.status < 300); 611 | }, 612 | 613 | responseIsFailure: function() { 614 | return !this.responseIsSuccess(); 615 | } 616 | } 617 | 618 | Ajax.Request = Class.create(); 619 | Ajax.Request.Events = 620 | ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; 621 | 622 | Ajax.Request.prototype = Object.extend(new Ajax.Base(), { 623 | initialize: function(url, options) { 624 | this.transport = Ajax.getTransport(); 625 | this.setOptions(options); 626 | this.request(url); 627 | }, 628 | 629 | request: function(url) { 630 | var parameters = this.options.parameters || ''; 631 | if (parameters.length > 0) parameters += '&_='; 632 | 633 | try { 634 | this.url = url; 635 | if (this.options.method == 'get' && parameters.length > 0) 636 | this.url += (this.url.match(/\?/) ? '&' : '?') + parameters; 637 | 638 | Ajax.Responders.dispatch('onCreate', this, this.transport); 639 | 640 | this.transport.open(this.options.method, this.url, 641 | this.options.asynchronous); 642 | 643 | if (this.options.asynchronous) { 644 | this.transport.onreadystatechange = this.onStateChange.bind(this); 645 | setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); 646 | } 647 | 648 | this.setRequestHeaders(); 649 | 650 | var body = this.options.postBody ? this.options.postBody : parameters; 651 | this.transport.send(this.options.method == 'post' ? body : null); 652 | 653 | } catch (e) { 654 | this.dispatchException(e); 655 | } 656 | }, 657 | 658 | setRequestHeaders: function() { 659 | var requestHeaders = 660 | ['X-Requested-With', 'XMLHttpRequest', 661 | 'X-Prototype-Version', Prototype.Version]; 662 | 663 | if (this.options.method == 'post') { 664 | requestHeaders.push('Content-type', 665 | 'application/x-www-form-urlencoded'); 666 | 667 | /* Force "Connection: close" for Mozilla browsers to work around 668 | * a bug where XMLHttpReqeuest sends an incorrect Content-length 669 | * header. See Mozilla Bugzilla #246651. 670 | */ 671 | if (this.transport.overrideMimeType) 672 | requestHeaders.push('Connection', 'close'); 673 | } 674 | 675 | if (this.options.requestHeaders) 676 | requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); 677 | 678 | for (var i = 0; i < requestHeaders.length; i += 2) 679 | this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); 680 | }, 681 | 682 | onStateChange: function() { 683 | var readyState = this.transport.readyState; 684 | if (readyState != 1) 685 | this.respondToReadyState(this.transport.readyState); 686 | }, 687 | 688 | header: function(name) { 689 | try { 690 | return this.transport.getResponseHeader(name); 691 | } catch (e) {} 692 | }, 693 | 694 | evalJSON: function() { 695 | try { 696 | return eval(this.header('X-JSON')); 697 | } catch (e) {} 698 | }, 699 | 700 | evalResponse: function() { 701 | try { 702 | return eval(this.transport.responseText); 703 | } catch (e) { 704 | this.dispatchException(e); 705 | } 706 | }, 707 | 708 | respondToReadyState: function(readyState) { 709 | var event = Ajax.Request.Events[readyState]; 710 | var transport = this.transport, json = this.evalJSON(); 711 | 712 | if (event == 'Complete') { 713 | try { 714 | (this.options['on' + this.transport.status] 715 | || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] 716 | || Prototype.emptyFunction)(transport, json); 717 | } catch (e) { 718 | this.dispatchException(e); 719 | } 720 | 721 | if ((this.header('Content-type') || '').match(/^text\/javascript/i)) 722 | this.evalResponse(); 723 | } 724 | 725 | try { 726 | (this.options['on' + event] || Prototype.emptyFunction)(transport, json); 727 | Ajax.Responders.dispatch('on' + event, this, transport, json); 728 | } catch (e) { 729 | this.dispatchException(e); 730 | } 731 | 732 | /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ 733 | if (event == 'Complete') 734 | this.transport.onreadystatechange = Prototype.emptyFunction; 735 | }, 736 | 737 | dispatchException: function(exception) { 738 | (this.options.onException || Prototype.emptyFunction)(this, exception); 739 | Ajax.Responders.dispatch('onException', this, exception); 740 | } 741 | }); 742 | 743 | Ajax.Updater = Class.create(); 744 | 745 | Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { 746 | initialize: function(container, url, options) { 747 | this.containers = { 748 | success: container.success ? $(container.success) : $(container), 749 | failure: container.failure ? $(container.failure) : 750 | (container.success ? null : $(container)) 751 | } 752 | 753 | this.transport = Ajax.getTransport(); 754 | this.setOptions(options); 755 | 756 | var onComplete = this.options.onComplete || Prototype.emptyFunction; 757 | this.options.onComplete = (function(transport, object) { 758 | this.updateContent(); 759 | onComplete(transport, object); 760 | }).bind(this); 761 | 762 | this.request(url); 763 | }, 764 | 765 | updateContent: function() { 766 | var receiver = this.responseIsSuccess() ? 767 | this.containers.success : this.containers.failure; 768 | var response = this.transport.responseText; 769 | 770 | if (!this.options.evalScripts) 771 | response = response.stripScripts(); 772 | 773 | if (receiver) { 774 | if (this.options.insertion) { 775 | new this.options.insertion(receiver, response); 776 | } else { 777 | Element.update(receiver, response); 778 | } 779 | } 780 | 781 | if (this.responseIsSuccess()) { 782 | if (this.onComplete) 783 | setTimeout(this.onComplete.bind(this), 10); 784 | } 785 | } 786 | }); 787 | 788 | Ajax.PeriodicalUpdater = Class.create(); 789 | Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { 790 | initialize: function(container, url, options) { 791 | this.setOptions(options); 792 | this.onComplete = this.options.onComplete; 793 | 794 | this.frequency = (this.options.frequency || 2); 795 | this.decay = (this.options.decay || 1); 796 | 797 | this.updater = {}; 798 | this.container = container; 799 | this.url = url; 800 | 801 | this.start(); 802 | }, 803 | 804 | start: function() { 805 | this.options.onComplete = this.updateComplete.bind(this); 806 | this.onTimerEvent(); 807 | }, 808 | 809 | stop: function() { 810 | this.updater.onComplete = undefined; 811 | clearTimeout(this.timer); 812 | (this.onComplete || Prototype.emptyFunction).apply(this, arguments); 813 | }, 814 | 815 | updateComplete: function(request) { 816 | if (this.options.decay) { 817 | this.decay = (request.responseText == this.lastText ? 818 | this.decay * this.options.decay : 1); 819 | 820 | this.lastText = request.responseText; 821 | } 822 | this.timer = setTimeout(this.onTimerEvent.bind(this), 823 | this.decay * this.frequency * 1000); 824 | }, 825 | 826 | onTimerEvent: function() { 827 | this.updater = new Ajax.Updater(this.container, this.url, this.options); 828 | } 829 | }); 830 | document.getElementsByClassName = function(className, parentElement) { 831 | var children = ($(parentElement) || document.body).getElementsByTagName('*'); 832 | return $A(children).inject([], function(elements, child) { 833 | if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) 834 | elements.push(child); 835 | return elements; 836 | }); 837 | } 838 | 839 | /*--------------------------------------------------------------------------*/ 840 | 841 | if (!window.Element) { 842 | var Element = new Object(); 843 | } 844 | 845 | Object.extend(Element, { 846 | visible: function(element) { 847 | return $(element).style.display != 'none'; 848 | }, 849 | 850 | toggle: function() { 851 | for (var i = 0; i < arguments.length; i++) { 852 | var element = $(arguments[i]); 853 | Element[Element.visible(element) ? 'hide' : 'show'](element); 854 | } 855 | }, 856 | 857 | hide: function() { 858 | for (var i = 0; i < arguments.length; i++) { 859 | var element = $(arguments[i]); 860 | element.style.display = 'none'; 861 | } 862 | }, 863 | 864 | show: function() { 865 | for (var i = 0; i < arguments.length; i++) { 866 | var element = $(arguments[i]); 867 | element.style.display = ''; 868 | } 869 | }, 870 | 871 | remove: function(element) { 872 | element = $(element); 873 | element.parentNode.removeChild(element); 874 | }, 875 | 876 | update: function(element, html) { 877 | $(element).innerHTML = html.stripScripts(); 878 | setTimeout(function() {html.evalScripts()}, 10); 879 | }, 880 | 881 | getHeight: function(element) { 882 | element = $(element); 883 | return element.offsetHeight; 884 | }, 885 | 886 | classNames: function(element) { 887 | return new Element.ClassNames(element); 888 | }, 889 | 890 | hasClassName: function(element, className) { 891 | if (!(element = $(element))) return; 892 | return Element.classNames(element).include(className); 893 | }, 894 | 895 | addClassName: function(element, className) { 896 | if (!(element = $(element))) return; 897 | return Element.classNames(element).add(className); 898 | }, 899 | 900 | removeClassName: function(element, className) { 901 | if (!(element = $(element))) return; 902 | return Element.classNames(element).remove(className); 903 | }, 904 | 905 | // removes whitespace-only text node children 906 | cleanWhitespace: function(element) { 907 | element = $(element); 908 | for (var i = 0; i < element.childNodes.length; i++) { 909 | var node = element.childNodes[i]; 910 | if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) 911 | Element.remove(node); 912 | } 913 | }, 914 | 915 | empty: function(element) { 916 | return $(element).innerHTML.match(/^\s*$/); 917 | }, 918 | 919 | scrollTo: function(element) { 920 | element = $(element); 921 | var x = element.x ? element.x : element.offsetLeft, 922 | y = element.y ? element.y : element.offsetTop; 923 | window.scrollTo(x, y); 924 | }, 925 | 926 | getStyle: function(element, style) { 927 | element = $(element); 928 | var value = element.style[style.camelize()]; 929 | if (!value) { 930 | if (document.defaultView && document.defaultView.getComputedStyle) { 931 | var css = document.defaultView.getComputedStyle(element, null); 932 | value = css ? css.getPropertyValue(style) : null; 933 | } else if (element.currentStyle) { 934 | value = element.currentStyle[style.camelize()]; 935 | } 936 | } 937 | 938 | if (window.opera && ['left', 'top', 'right', 'bottom'].include(style)) 939 | if (Element.getStyle(element, 'position') == 'static') value = 'auto'; 940 | 941 | return value == 'auto' ? null : value; 942 | }, 943 | 944 | setStyle: function(element, style) { 945 | element = $(element); 946 | for (name in style) 947 | element.style[name.camelize()] = style[name]; 948 | }, 949 | 950 | getDimensions: function(element) { 951 | element = $(element); 952 | if (Element.getStyle(element, 'display') != 'none') 953 | return {width: element.offsetWidth, height: element.offsetHeight}; 954 | 955 | // All *Width and *Height properties give 0 on elements with display none, 956 | // so enable the element temporarily 957 | var els = element.style; 958 | var originalVisibility = els.visibility; 959 | var originalPosition = els.position; 960 | els.visibility = 'hidden'; 961 | els.position = 'absolute'; 962 | els.display = ''; 963 | var originalWidth = element.clientWidth; 964 | var originalHeight = element.clientHeight; 965 | els.display = 'none'; 966 | els.position = originalPosition; 967 | els.visibility = originalVisibility; 968 | return {width: originalWidth, height: originalHeight}; 969 | }, 970 | 971 | makePositioned: function(element) { 972 | element = $(element); 973 | var pos = Element.getStyle(element, 'position'); 974 | if (pos == 'static' || !pos) { 975 | element._madePositioned = true; 976 | element.style.position = 'relative'; 977 | // Opera returns the offset relative to the positioning context, when an 978 | // element is position relative but top and left have not been defined 979 | if (window.opera) { 980 | element.style.top = 0; 981 | element.style.left = 0; 982 | } 983 | } 984 | }, 985 | 986 | undoPositioned: function(element) { 987 | element = $(element); 988 | if (element._madePositioned) { 989 | element._madePositioned = undefined; 990 | element.style.position = 991 | element.style.top = 992 | element.style.left = 993 | element.style.bottom = 994 | element.style.right = ''; 995 | } 996 | }, 997 | 998 | makeClipping: function(element) { 999 | element = $(element); 1000 | if (element._overflow) return; 1001 | element._overflow = element.style.overflow; 1002 | if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') 1003 | element.style.overflow = 'hidden'; 1004 | }, 1005 | 1006 | undoClipping: function(element) { 1007 | element = $(element); 1008 | if (element._overflow) return; 1009 | element.style.overflow = element._overflow; 1010 | element._overflow = undefined; 1011 | } 1012 | }); 1013 | 1014 | var Toggle = new Object(); 1015 | Toggle.display = Element.toggle; 1016 | 1017 | /*--------------------------------------------------------------------------*/ 1018 | 1019 | Abstract.Insertion = function(adjacency) { 1020 | this.adjacency = adjacency; 1021 | } 1022 | 1023 | Abstract.Insertion.prototype = { 1024 | initialize: function(element, content) { 1025 | this.element = $(element); 1026 | this.content = content.stripScripts(); 1027 | 1028 | if (this.adjacency && this.element.insertAdjacentHTML) { 1029 | try { 1030 | this.element.insertAdjacentHTML(this.adjacency, this.content); 1031 | } catch (e) { 1032 | if (this.element.tagName.toLowerCase() == 'tbody') { 1033 | this.insertContent(this.contentFromAnonymousTable()); 1034 | } else { 1035 | throw e; 1036 | } 1037 | } 1038 | } else { 1039 | this.range = this.element.ownerDocument.createRange(); 1040 | if (this.initializeRange) this.initializeRange(); 1041 | this.insertContent([this.range.createContextualFragment(this.content)]); 1042 | } 1043 | 1044 | setTimeout(function() {content.evalScripts()}, 10); 1045 | }, 1046 | 1047 | contentFromAnonymousTable: function() { 1048 | var div = document.createElement('div'); 1049 | div.innerHTML = '' + this.content + '
'; 1050 | return $A(div.childNodes[0].childNodes[0].childNodes); 1051 | } 1052 | } 1053 | 1054 | var Insertion = new Object(); 1055 | 1056 | Insertion.Before = Class.create(); 1057 | Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { 1058 | initializeRange: function() { 1059 | this.range.setStartBefore(this.element); 1060 | }, 1061 | 1062 | insertContent: function(fragments) { 1063 | fragments.each((function(fragment) { 1064 | this.element.parentNode.insertBefore(fragment, this.element); 1065 | }).bind(this)); 1066 | } 1067 | }); 1068 | 1069 | Insertion.Top = Class.create(); 1070 | Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { 1071 | initializeRange: function() { 1072 | this.range.selectNodeContents(this.element); 1073 | this.range.collapse(true); 1074 | }, 1075 | 1076 | insertContent: function(fragments) { 1077 | fragments.reverse(false).each((function(fragment) { 1078 | this.element.insertBefore(fragment, this.element.firstChild); 1079 | }).bind(this)); 1080 | } 1081 | }); 1082 | 1083 | Insertion.Bottom = Class.create(); 1084 | Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { 1085 | initializeRange: function() { 1086 | this.range.selectNodeContents(this.element); 1087 | this.range.collapse(this.element); 1088 | }, 1089 | 1090 | insertContent: function(fragments) { 1091 | fragments.each((function(fragment) { 1092 | this.element.appendChild(fragment); 1093 | }).bind(this)); 1094 | } 1095 | }); 1096 | 1097 | Insertion.After = Class.create(); 1098 | Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { 1099 | initializeRange: function() { 1100 | this.range.setStartAfter(this.element); 1101 | }, 1102 | 1103 | insertContent: function(fragments) { 1104 | fragments.each((function(fragment) { 1105 | this.element.parentNode.insertBefore(fragment, 1106 | this.element.nextSibling); 1107 | }).bind(this)); 1108 | } 1109 | }); 1110 | 1111 | /*--------------------------------------------------------------------------*/ 1112 | 1113 | Element.ClassNames = Class.create(); 1114 | Element.ClassNames.prototype = { 1115 | initialize: function(element) { 1116 | this.element = $(element); 1117 | }, 1118 | 1119 | _each: function(iterator) { 1120 | this.element.className.split(/\s+/).select(function(name) { 1121 | return name.length > 0; 1122 | })._each(iterator); 1123 | }, 1124 | 1125 | set: function(className) { 1126 | this.element.className = className; 1127 | }, 1128 | 1129 | add: function(classNameToAdd) { 1130 | if (this.include(classNameToAdd)) return; 1131 | this.set(this.toArray().concat(classNameToAdd).join(' ')); 1132 | }, 1133 | 1134 | remove: function(classNameToRemove) { 1135 | if (!this.include(classNameToRemove)) return; 1136 | this.set(this.select(function(className) { 1137 | return className != classNameToRemove; 1138 | }).join(' ')); 1139 | }, 1140 | 1141 | toString: function() { 1142 | return this.toArray().join(' '); 1143 | } 1144 | } 1145 | 1146 | Object.extend(Element.ClassNames.prototype, Enumerable); 1147 | var Field = { 1148 | clear: function() { 1149 | for (var i = 0; i < arguments.length; i++) 1150 | $(arguments[i]).value = ''; 1151 | }, 1152 | 1153 | focus: function(element) { 1154 | $(element).focus(); 1155 | }, 1156 | 1157 | present: function() { 1158 | for (var i = 0; i < arguments.length; i++) 1159 | if ($(arguments[i]).value == '') return false; 1160 | return true; 1161 | }, 1162 | 1163 | select: function(element) { 1164 | $(element).select(); 1165 | }, 1166 | 1167 | activate: function(element) { 1168 | element = $(element); 1169 | element.focus(); 1170 | if (element.select) 1171 | element.select(); 1172 | } 1173 | } 1174 | 1175 | /*--------------------------------------------------------------------------*/ 1176 | 1177 | var Form = { 1178 | serialize: function(form) { 1179 | var elements = Form.getElements($(form)); 1180 | var queryComponents = new Array(); 1181 | 1182 | for (var i = 0; i < elements.length; i++) { 1183 | var queryComponent = Form.Element.serialize(elements[i]); 1184 | if (queryComponent) 1185 | queryComponents.push(queryComponent); 1186 | } 1187 | 1188 | return queryComponents.join('&'); 1189 | }, 1190 | 1191 | getElements: function(form) { 1192 | form = $(form); 1193 | var elements = new Array(); 1194 | 1195 | for (tagName in Form.Element.Serializers) { 1196 | var tagElements = form.getElementsByTagName(tagName); 1197 | for (var j = 0; j < tagElements.length; j++) 1198 | elements.push(tagElements[j]); 1199 | } 1200 | return elements; 1201 | }, 1202 | 1203 | getInputs: function(form, typeName, name) { 1204 | form = $(form); 1205 | var inputs = form.getElementsByTagName('input'); 1206 | 1207 | if (!typeName && !name) 1208 | return inputs; 1209 | 1210 | var matchingInputs = new Array(); 1211 | for (var i = 0; i < inputs.length; i++) { 1212 | var input = inputs[i]; 1213 | if ((typeName && input.type != typeName) || 1214 | (name && input.name != name)) 1215 | continue; 1216 | matchingInputs.push(input); 1217 | } 1218 | 1219 | return matchingInputs; 1220 | }, 1221 | 1222 | disable: function(form) { 1223 | var elements = Form.getElements(form); 1224 | for (var i = 0; i < elements.length; i++) { 1225 | var element = elements[i]; 1226 | element.blur(); 1227 | element.disabled = 'true'; 1228 | } 1229 | }, 1230 | 1231 | enable: function(form) { 1232 | var elements = Form.getElements(form); 1233 | for (var i = 0; i < elements.length; i++) { 1234 | var element = elements[i]; 1235 | element.disabled = ''; 1236 | } 1237 | }, 1238 | 1239 | findFirstElement: function(form) { 1240 | return Form.getElements(form).find(function(element) { 1241 | return element.type != 'hidden' && !element.disabled && 1242 | ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); 1243 | }); 1244 | }, 1245 | 1246 | focusFirstElement: function(form) { 1247 | Field.activate(Form.findFirstElement(form)); 1248 | }, 1249 | 1250 | reset: function(form) { 1251 | $(form).reset(); 1252 | } 1253 | } 1254 | 1255 | Form.Element = { 1256 | serialize: function(element) { 1257 | element = $(element); 1258 | var method = element.tagName.toLowerCase(); 1259 | var parameter = Form.Element.Serializers[method](element); 1260 | 1261 | if (parameter) { 1262 | var key = encodeURIComponent(parameter[0]); 1263 | if (key.length == 0) return; 1264 | 1265 | if (parameter[1].constructor != Array) 1266 | parameter[1] = [parameter[1]]; 1267 | 1268 | return parameter[1].map(function(value) { 1269 | return key + '=' + encodeURIComponent(value); 1270 | }).join('&'); 1271 | } 1272 | }, 1273 | 1274 | getValue: function(element) { 1275 | element = $(element); 1276 | var method = element.tagName.toLowerCase(); 1277 | var parameter = Form.Element.Serializers[method](element); 1278 | 1279 | if (parameter) 1280 | return parameter[1]; 1281 | } 1282 | } 1283 | 1284 | Form.Element.Serializers = { 1285 | input: function(element) { 1286 | switch (element.type.toLowerCase()) { 1287 | case 'submit': 1288 | case 'hidden': 1289 | case 'password': 1290 | case 'text': 1291 | return Form.Element.Serializers.textarea(element); 1292 | case 'checkbox': 1293 | case 'radio': 1294 | return Form.Element.Serializers.inputSelector(element); 1295 | } 1296 | return false; 1297 | }, 1298 | 1299 | inputSelector: function(element) { 1300 | if (element.checked) 1301 | return [element.name, element.value]; 1302 | }, 1303 | 1304 | textarea: function(element) { 1305 | return [element.name, element.value]; 1306 | }, 1307 | 1308 | select: function(element) { 1309 | return Form.Element.Serializers[element.type == 'select-one' ? 1310 | 'selectOne' : 'selectMany'](element); 1311 | }, 1312 | 1313 | selectOne: function(element) { 1314 | var value = '', opt, index = element.selectedIndex; 1315 | if (index >= 0) { 1316 | opt = element.options[index]; 1317 | value = opt.value; 1318 | if (!value && !('value' in opt)) 1319 | value = opt.text; 1320 | } 1321 | return [element.name, value]; 1322 | }, 1323 | 1324 | selectMany: function(element) { 1325 | var value = new Array(); 1326 | for (var i = 0; i < element.length; i++) { 1327 | var opt = element.options[i]; 1328 | if (opt.selected) { 1329 | var optValue = opt.value; 1330 | if (!optValue && !('value' in opt)) 1331 | optValue = opt.text; 1332 | value.push(optValue); 1333 | } 1334 | } 1335 | return [element.name, value]; 1336 | } 1337 | } 1338 | 1339 | /*--------------------------------------------------------------------------*/ 1340 | 1341 | var $F = Form.Element.getValue; 1342 | 1343 | /*--------------------------------------------------------------------------*/ 1344 | 1345 | Abstract.TimedObserver = function() {} 1346 | Abstract.TimedObserver.prototype = { 1347 | initialize: function(element, frequency, callback) { 1348 | this.frequency = frequency; 1349 | this.element = $(element); 1350 | this.callback = callback; 1351 | 1352 | this.lastValue = this.getValue(); 1353 | this.registerCallback(); 1354 | }, 1355 | 1356 | registerCallback: function() { 1357 | setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); 1358 | }, 1359 | 1360 | onTimerEvent: function() { 1361 | var value = this.getValue(); 1362 | if (this.lastValue != value) { 1363 | this.callback(this.element, value); 1364 | this.lastValue = value; 1365 | } 1366 | } 1367 | } 1368 | 1369 | Form.Element.Observer = Class.create(); 1370 | Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { 1371 | getValue: function() { 1372 | return Form.Element.getValue(this.element); 1373 | } 1374 | }); 1375 | 1376 | Form.Observer = Class.create(); 1377 | Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { 1378 | getValue: function() { 1379 | return Form.serialize(this.element); 1380 | } 1381 | }); 1382 | 1383 | /*--------------------------------------------------------------------------*/ 1384 | 1385 | Abstract.EventObserver = function() {} 1386 | Abstract.EventObserver.prototype = { 1387 | initialize: function(element, callback) { 1388 | this.element = $(element); 1389 | this.callback = callback; 1390 | 1391 | this.lastValue = this.getValue(); 1392 | if (this.element.tagName.toLowerCase() == 'form') 1393 | this.registerFormCallbacks(); 1394 | else 1395 | this.registerCallback(this.element); 1396 | }, 1397 | 1398 | onElementEvent: function() { 1399 | var value = this.getValue(); 1400 | if (this.lastValue != value) { 1401 | this.callback(this.element, value); 1402 | this.lastValue = value; 1403 | } 1404 | }, 1405 | 1406 | registerFormCallbacks: function() { 1407 | var elements = Form.getElements(this.element); 1408 | for (var i = 0; i < elements.length; i++) 1409 | this.registerCallback(elements[i]); 1410 | }, 1411 | 1412 | registerCallback: function(element) { 1413 | if (element.type) { 1414 | switch (element.type.toLowerCase()) { 1415 | case 'checkbox': 1416 | case 'radio': 1417 | Event.observe(element, 'click', this.onElementEvent.bind(this)); 1418 | break; 1419 | case 'password': 1420 | case 'text': 1421 | case 'textarea': 1422 | case 'select-one': 1423 | case 'select-multiple': 1424 | Event.observe(element, 'change', this.onElementEvent.bind(this)); 1425 | break; 1426 | } 1427 | } 1428 | } 1429 | } 1430 | 1431 | Form.Element.EventObserver = Class.create(); 1432 | Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { 1433 | getValue: function() { 1434 | return Form.Element.getValue(this.element); 1435 | } 1436 | }); 1437 | 1438 | Form.EventObserver = Class.create(); 1439 | Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { 1440 | getValue: function() { 1441 | return Form.serialize(this.element); 1442 | } 1443 | }); 1444 | if (!window.Event) { 1445 | var Event = new Object(); 1446 | } 1447 | 1448 | Object.extend(Event, { 1449 | KEY_BACKSPACE: 8, 1450 | KEY_TAB: 9, 1451 | KEY_RETURN: 13, 1452 | KEY_ESC: 27, 1453 | KEY_LEFT: 37, 1454 | KEY_UP: 38, 1455 | KEY_RIGHT: 39, 1456 | KEY_DOWN: 40, 1457 | KEY_DELETE: 46, 1458 | 1459 | element: function(event) { 1460 | return event.target || event.srcElement; 1461 | }, 1462 | 1463 | isLeftClick: function(event) { 1464 | return (((event.which) && (event.which == 1)) || 1465 | ((event.button) && (event.button == 1))); 1466 | }, 1467 | 1468 | pointerX: function(event) { 1469 | return event.pageX || (event.clientX + 1470 | (document.documentElement.scrollLeft || document.body.scrollLeft)); 1471 | }, 1472 | 1473 | pointerY: function(event) { 1474 | return event.pageY || (event.clientY + 1475 | (document.documentElement.scrollTop || document.body.scrollTop)); 1476 | }, 1477 | 1478 | stop: function(event) { 1479 | if (event.preventDefault) { 1480 | event.preventDefault(); 1481 | event.stopPropagation(); 1482 | } else { 1483 | event.returnValue = false; 1484 | event.cancelBubble = true; 1485 | } 1486 | }, 1487 | 1488 | // find the first node with the given tagName, starting from the 1489 | // node the event was triggered on; traverses the DOM upwards 1490 | findElement: function(event, tagName) { 1491 | var element = Event.element(event); 1492 | while (element.parentNode && (!element.tagName || 1493 | (element.tagName.toUpperCase() != tagName.toUpperCase()))) 1494 | element = element.parentNode; 1495 | return element; 1496 | }, 1497 | 1498 | observers: false, 1499 | 1500 | _observeAndCache: function(element, name, observer, useCapture) { 1501 | if (!this.observers) this.observers = []; 1502 | if (element.addEventListener) { 1503 | this.observers.push([element, name, observer, useCapture]); 1504 | element.addEventListener(name, observer, useCapture); 1505 | } else if (element.attachEvent) { 1506 | this.observers.push([element, name, observer, useCapture]); 1507 | element.attachEvent('on' + name, observer); 1508 | } 1509 | }, 1510 | 1511 | unloadCache: function() { 1512 | if (!Event.observers) return; 1513 | for (var i = 0; i < Event.observers.length; i++) { 1514 | Event.stopObserving.apply(this, Event.observers[i]); 1515 | Event.observers[i][0] = null; 1516 | } 1517 | Event.observers = false; 1518 | }, 1519 | 1520 | observe: function(element, name, observer, useCapture) { 1521 | var element = $(element); 1522 | useCapture = useCapture || false; 1523 | 1524 | if (name == 'keypress' && 1525 | (navigator.appVersion.match(/Konqueror|Safari|KHTML/) 1526 | || element.attachEvent)) 1527 | name = 'keydown'; 1528 | 1529 | this._observeAndCache(element, name, observer, useCapture); 1530 | }, 1531 | 1532 | stopObserving: function(element, name, observer, useCapture) { 1533 | var element = $(element); 1534 | useCapture = useCapture || false; 1535 | 1536 | if (name == 'keypress' && 1537 | (navigator.appVersion.match(/Konqueror|Safari|KHTML/) 1538 | || element.detachEvent)) 1539 | name = 'keydown'; 1540 | 1541 | if (element.removeEventListener) { 1542 | element.removeEventListener(name, observer, useCapture); 1543 | } else if (element.detachEvent) { 1544 | element.detachEvent('on' + name, observer); 1545 | } 1546 | } 1547 | }); 1548 | 1549 | /* prevent memory leaks in IE */ 1550 | Event.observe(window, 'unload', Event.unloadCache, false); 1551 | var Position = { 1552 | // set to true if needed, warning: firefox performance problems 1553 | // NOT neeeded for page scrolling, only if draggable contained in 1554 | // scrollable elements 1555 | includeScrollOffsets: false, 1556 | 1557 | // must be called before calling withinIncludingScrolloffset, every time the 1558 | // page is scrolled 1559 | prepare: function() { 1560 | this.deltaX = window.pageXOffset 1561 | || document.documentElement.scrollLeft 1562 | || document.body.scrollLeft 1563 | || 0; 1564 | this.deltaY = window.pageYOffset 1565 | || document.documentElement.scrollTop 1566 | || document.body.scrollTop 1567 | || 0; 1568 | }, 1569 | 1570 | realOffset: function(element) { 1571 | var valueT = 0, valueL = 0; 1572 | do { 1573 | valueT += element.scrollTop || 0; 1574 | valueL += element.scrollLeft || 0; 1575 | element = element.parentNode; 1576 | } while (element); 1577 | return [valueL, valueT]; 1578 | }, 1579 | 1580 | cumulativeOffset: function(element) { 1581 | var valueT = 0, valueL = 0; 1582 | do { 1583 | valueT += element.offsetTop || 0; 1584 | valueL += element.offsetLeft || 0; 1585 | element = element.offsetParent; 1586 | } while (element); 1587 | return [valueL, valueT]; 1588 | }, 1589 | 1590 | positionedOffset: function(element) { 1591 | var valueT = 0, valueL = 0; 1592 | do { 1593 | valueT += element.offsetTop || 0; 1594 | valueL += element.offsetLeft || 0; 1595 | element = element.offsetParent; 1596 | if (element) { 1597 | p = Element.getStyle(element, 'position'); 1598 | if (p == 'relative' || p == 'absolute') break; 1599 | } 1600 | } while (element); 1601 | return [valueL, valueT]; 1602 | }, 1603 | 1604 | offsetParent: function(element) { 1605 | if (element.offsetParent) return element.offsetParent; 1606 | if (element == document.body) return element; 1607 | 1608 | while ((element = element.parentNode) && element != document.body) 1609 | if (Element.getStyle(element, 'position') != 'static') 1610 | return element; 1611 | 1612 | return document.body; 1613 | }, 1614 | 1615 | // caches x/y coordinate pair to use with overlap 1616 | within: function(element, x, y) { 1617 | if (this.includeScrollOffsets) 1618 | return this.withinIncludingScrolloffsets(element, x, y); 1619 | this.xcomp = x; 1620 | this.ycomp = y; 1621 | this.offset = this.cumulativeOffset(element); 1622 | 1623 | return (y >= this.offset[1] && 1624 | y < this.offset[1] + element.offsetHeight && 1625 | x >= this.offset[0] && 1626 | x < this.offset[0] + element.offsetWidth); 1627 | }, 1628 | 1629 | withinIncludingScrolloffsets: function(element, x, y) { 1630 | var offsetcache = this.realOffset(element); 1631 | 1632 | this.xcomp = x + offsetcache[0] - this.deltaX; 1633 | this.ycomp = y + offsetcache[1] - this.deltaY; 1634 | this.offset = this.cumulativeOffset(element); 1635 | 1636 | return (this.ycomp >= this.offset[1] && 1637 | this.ycomp < this.offset[1] + element.offsetHeight && 1638 | this.xcomp >= this.offset[0] && 1639 | this.xcomp < this.offset[0] + element.offsetWidth); 1640 | }, 1641 | 1642 | // within must be called directly before 1643 | overlap: function(mode, element) { 1644 | if (!mode) return 0; 1645 | if (mode == 'vertical') 1646 | return ((this.offset[1] + element.offsetHeight) - this.ycomp) / 1647 | element.offsetHeight; 1648 | if (mode == 'horizontal') 1649 | return ((this.offset[0] + element.offsetWidth) - this.xcomp) / 1650 | element.offsetWidth; 1651 | }, 1652 | 1653 | clone: function(source, target) { 1654 | source = $(source); 1655 | target = $(target); 1656 | target.style.position = 'absolute'; 1657 | var offsets = this.cumulativeOffset(source); 1658 | target.style.top = offsets[1] + 'px'; 1659 | target.style.left = offsets[0] + 'px'; 1660 | target.style.width = source.offsetWidth + 'px'; 1661 | target.style.height = source.offsetHeight + 'px'; 1662 | }, 1663 | 1664 | page: function(forElement) { 1665 | var valueT = 0, valueL = 0; 1666 | 1667 | var element = forElement; 1668 | do { 1669 | valueT += element.offsetTop || 0; 1670 | valueL += element.offsetLeft || 0; 1671 | 1672 | // Safari fix 1673 | if (element.offsetParent==document.body) 1674 | if (Element.getStyle(element,'position')=='absolute') break; 1675 | 1676 | } while (element = element.offsetParent); 1677 | 1678 | element = forElement; 1679 | do { 1680 | valueT -= element.scrollTop || 0; 1681 | valueL -= element.scrollLeft || 0; 1682 | } while (element = element.parentNode); 1683 | 1684 | return [valueL, valueT]; 1685 | }, 1686 | 1687 | clone: function(source, target) { 1688 | var options = Object.extend({ 1689 | setLeft: true, 1690 | setTop: true, 1691 | setWidth: true, 1692 | setHeight: true, 1693 | offsetTop: 0, 1694 | offsetLeft: 0 1695 | }, arguments[2] || {}) 1696 | 1697 | // find page position of source 1698 | source = $(source); 1699 | var p = Position.page(source); 1700 | 1701 | // find coordinate system to use 1702 | target = $(target); 1703 | var delta = [0, 0]; 1704 | var parent = null; 1705 | // delta [0,0] will do fine with position: fixed elements, 1706 | // position:absolute needs offsetParent deltas 1707 | if (Element.getStyle(target,'position') == 'absolute') { 1708 | parent = Position.offsetParent(target); 1709 | delta = Position.page(parent); 1710 | } 1711 | 1712 | // correct by body offsets (fixes Safari) 1713 | if (parent == document.body) { 1714 | delta[0] -= document.body.offsetLeft; 1715 | delta[1] -= document.body.offsetTop; 1716 | } 1717 | 1718 | // set position 1719 | if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; 1720 | if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; 1721 | if(options.setWidth) target.style.width = source.offsetWidth + 'px'; 1722 | if(options.setHeight) target.style.height = source.offsetHeight + 'px'; 1723 | }, 1724 | 1725 | absolutize: function(element) { 1726 | element = $(element); 1727 | if (element.style.position == 'absolute') return; 1728 | Position.prepare(); 1729 | 1730 | var offsets = Position.positionedOffset(element); 1731 | var top = offsets[1]; 1732 | var left = offsets[0]; 1733 | var width = element.clientWidth; 1734 | var height = element.clientHeight; 1735 | 1736 | element._originalLeft = left - parseFloat(element.style.left || 0); 1737 | element._originalTop = top - parseFloat(element.style.top || 0); 1738 | element._originalWidth = element.style.width; 1739 | element._originalHeight = element.style.height; 1740 | 1741 | element.style.position = 'absolute'; 1742 | element.style.top = top + 'px';; 1743 | element.style.left = left + 'px';; 1744 | element.style.width = width + 'px';; 1745 | element.style.height = height + 'px';; 1746 | }, 1747 | 1748 | relativize: function(element) { 1749 | element = $(element); 1750 | if (element.style.position == 'relative') return; 1751 | Position.prepare(); 1752 | 1753 | element.style.position = 'relative'; 1754 | var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); 1755 | var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); 1756 | 1757 | element.style.top = top + 'px'; 1758 | element.style.left = left + 'px'; 1759 | element.style.height = element._originalHeight; 1760 | element.style.width = element._originalWidth; 1761 | } 1762 | } 1763 | 1764 | // Safari returns margins on body which is incorrect if the child is absolutely 1765 | // positioned. For performance reasons, redefine Position.cumulativeOffset for 1766 | // KHTML/WebKit only. 1767 | if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { 1768 | Position.cumulativeOffset = function(element) { 1769 | var valueT = 0, valueL = 0; 1770 | do { 1771 | valueT += element.offsetTop || 0; 1772 | valueL += element.offsetLeft || 0; 1773 | if (element.offsetParent == document.body) 1774 | if (Element.getStyle(element, 'position') == 'absolute') break; 1775 | 1776 | element = element.offsetParent; 1777 | } while (element); 1778 | 1779 | return [valueL, valueT]; 1780 | } 1781 | } 1782 | -------------------------------------------------------------------------------- /priv/www/rabbitmq-jquery.js: -------------------------------------------------------------------------------- 1 | // The contents of this file are subject to the Mozilla Public License 2 | // Version 1.1 (the "License"); you may not use this file except in 3 | // compliance with the License. You may obtain a copy of the License 4 | // at http://www.mozilla.org/MPL/ 5 | // 6 | // Software distributed under the License is distributed on an "AS IS" 7 | // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | // the License for the specific language governing rights and 9 | // limitations under the License. 10 | // 11 | // The Original Code is RabbitMQ. 12 | // 13 | // The Initial Developer of the Original Code is GoPivotal, Inc. 14 | // Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. 15 | // 16 | 17 | var JsonRpc; 18 | var RabbitMQ; 19 | (function () { 20 | var Support = { 21 | extend: jQuery.extend, 22 | bindEvent: function (elt, event, cb) { 23 | jQuery(elt).bind(event, cb); 24 | }, 25 | each: function (collection, callback) { 26 | jQuery.each(collection, function (key, value) { callback(value) }); 27 | }, 28 | ajaxPost: function (url, customHeaders, bodyString, callback) { 29 | return new jQuery.ajax({ url: url, 30 | type: 'post', 31 | beforeSend: function (xhr) { 32 | for (var header in customHeaders) { 33 | if (customHeaders.hasOwnProperty(header)) { 34 | xhr.setRequestHeader(header, 35 | customHeaders[header]); 36 | } 37 | } 38 | }, 39 | contentType: 'application/json', 40 | accepts: {json: 'application/json'}, 41 | data: bodyString, 42 | complete: callback }); 43 | } 44 | }; 45 | 46 | JsonRpc = JsonRpc_ModuleFactory(Support); 47 | RabbitMQ = RabbitMQ_ModuleFactory(JsonRpc, Support); 48 | })(); 49 | -------------------------------------------------------------------------------- /priv/www/rabbitmq-prototype.js: -------------------------------------------------------------------------------- 1 | // The contents of this file are subject to the Mozilla Public License 2 | // Version 1.1 (the "License"); you may not use this file except in 3 | // compliance with the License. You may obtain a copy of the License 4 | // at http://www.mozilla.org/MPL/ 5 | // 6 | // Software distributed under the License is distributed on an "AS IS" 7 | // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | // the License for the specific language governing rights and 9 | // limitations under the License. 10 | // 11 | // The Original Code is RabbitMQ. 12 | // 13 | // The Initial Developer of the Original Code is GoPivotal, Inc. 14 | // Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. 15 | // 16 | 17 | var JsonRpc; 18 | var RabbitMQ; 19 | (function () { 20 | var Support = { 21 | extend: Object.extend, 22 | bindEvent: function (elt, event, cb) { 23 | Event.observe(elt, event, cb); 24 | }, 25 | each: function (collection, callback) { 26 | return collection.each(callback); 27 | }, 28 | ajaxPost: function (url, customHeaders, bodyString, callback) { 29 | var headers = ['Content-type', 'application/json', 30 | 'Accept', 'application/json']; 31 | for (var header in customHeaders) { 32 | if (customHeaders.hasOwnProperty(header)) { 33 | headers.push(header, customHeaders[header]); 34 | } 35 | } 36 | return new Ajax.Request(url, { method: 'post', 37 | requestHeaders: headers, 38 | postBody: bodyString, 39 | onComplete: callback }); 40 | } 41 | }; 42 | 43 | JsonRpc = JsonRpc_ModuleFactory(Support); 44 | RabbitMQ = RabbitMQ_ModuleFactory(JsonRpc, Support); 45 | })(); 46 | -------------------------------------------------------------------------------- /priv/www/rabbitmq.js: -------------------------------------------------------------------------------- 1 | // The contents of this file are subject to the Mozilla Public License 2 | // Version 1.1 (the "License"); you may not use this file except in 3 | // compliance with the License. You may obtain a copy of the License 4 | // at http://www.mozilla.org/MPL/ 5 | // 6 | // Software distributed under the License is distributed on an "AS IS" 7 | // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | // the License for the specific language governing rights and 9 | // limitations under the License. 10 | // 11 | // The Original Code is RabbitMQ. 12 | // 13 | // The Initial Developer of the Original Code is GoPivotal, Inc. 14 | // Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. 15 | // 16 | 17 | function RabbitMQ_ModuleFactory(JsonRpc, Support) { 18 | function openChannel(readyFn, options) { 19 | var o = { 20 | factoryServiceUrl: "/rpc/rabbitmq", 21 | timeout: 30000 // timeout for the *factory*, not the channel 22 | }; 23 | Support.extend(o, options || {}); 24 | 25 | var factoryService = new JsonRpc.Service(o.factoryServiceUrl, onServiceReady, o); 26 | function onServiceReady() { 27 | new Channel(factoryService, readyFn, o); 28 | } 29 | } 30 | 31 | function Channel(factory, readyFn, options) { 32 | this.options = { 33 | rpcServiceUrlBase: "/rpc/", 34 | username: "guest", 35 | password: "guest", 36 | virtualHost: null, 37 | realm: "/data", 38 | debug: false, 39 | debugLogger: alert, 40 | channelTimeout: 10 /* seconds; zero means "do not specify" */ 41 | }; 42 | Support.extend(this.options, options || {}); 43 | this.consumers = {}; 44 | this.alive = true; 45 | this.ticket = 0; 46 | 47 | var that = this; 48 | 49 | factory.open(this.options.username, 50 | this.options.password, 51 | this.options.channelTimeout, 52 | this.options.virtualHost) 53 | .addCallback(channel_created); 54 | 55 | function channel_created(reply) { 56 | that.service = new JsonRpc.Service(that.options.rpcServiceUrlBase + reply.service, 57 | ready, 58 | {debug: that.options.debug, 59 | debugLogger: that.options.debugLogger, 60 | timeout: that.options.channelTimeout * 1000}); 61 | } 62 | 63 | function ready(result) { 64 | that.poll_tophalf(); 65 | Support.bindEvent(window, 'unload', function () { that.close() }); 66 | Support.bindEvent(window, 'pagehide', function () { that.close() }); 67 | 68 | readyFn(that); 69 | } 70 | } 71 | 72 | Support.extend(Channel.prototype, 73 | { 74 | _dval: function(v, d) { 75 | return (v == null) ? d : v; 76 | }, 77 | 78 | _call: function(method, args) { 79 | if (this.alive) { 80 | return this.service.call(method, args); 81 | } 82 | }, 83 | 84 | _cast: function(method, args, content, props) { 85 | if (this.alive) { 86 | this.service.cast(method, args, content, props); 87 | } 88 | }, 89 | 90 | _extractArg: function(index) { 91 | return function(reply) { return reply.args[index]; }; 92 | }, 93 | 94 | exchangeDeclare: function(exchange, type, passive, durable, auto_delete, arguments) { 95 | return this._call("exchange.declare", [this.ticket, 96 | exchange, 97 | this._dval(type, "direct"), 98 | this._dval(passive, false), 99 | this._dval(durable, false), 100 | this._dval(auto_delete, false), 101 | false, // internal 102 | false, // nowait 103 | this._dval(arguments, {})]); 104 | }, 105 | 106 | queueDeclare: function(queue, passive, durable, exclusive, auto_delete, arguments) { 107 | return this._call("queue.declare", [this.ticket, 108 | this._dval(queue, ""), 109 | this._dval(passive, false), 110 | this._dval(durable, false), 111 | this._dval(exclusive, false), 112 | this._dval(auto_delete, true), 113 | false, // nowait 114 | this._dval(arguments, {})]) 115 | .addReplyTransformer(this._extractArg(0)); 116 | }, 117 | 118 | queueDelete: function(queue, if_unused, if_empty) { 119 | return this._call("queue.delete", [this.ticket, 120 | this._dval(queue, ""), 121 | this._dval(if_unused, false), 122 | this._dval(if_empty, false), 123 | false // nowait 124 | ]) 125 | .addReplyTransformer(this._extractArg(0)); 126 | }, 127 | 128 | queueBind: function(queue, exchange, routing_key, arguments) { 129 | return this._call("queue.bind", [this.ticket, 130 | queue, 131 | exchange, 132 | this._dval(routing_key, ""), 133 | false, // nowait 134 | this._dval(arguments, {})]); 135 | }, 136 | 137 | basicConsume: function(queue, consumer, options) { 138 | o = { 139 | consumer_tag: "", 140 | no_local: false, 141 | no_ack: false, 142 | exclusive: false 143 | }; 144 | Support.extend(o, options || {}); 145 | var ch = this; 146 | return ch._call("basic.consume", [ch.ticket, 147 | queue, 148 | o.consumer_tag, 149 | o.no_local, 150 | o.no_ack, 151 | o.exclusive, 152 | false, // nowait 153 | [] 154 | ]) 155 | .addReplyTransformer(ch._extractArg(0)) 156 | .addCallback(function (tag) { 157 | ch.consumers[tag] = consumer; 158 | if (consumer.consumeOk) { 159 | consumer.consumeOk(tag); 160 | } 161 | }); 162 | }, 163 | 164 | _js_props: function(props) { 165 | return { content_type: props[0], 166 | content_encoding: props[1], 167 | headers: props[2], 168 | delivery_mode: props[3], 169 | priority: props[4], 170 | correlation_id: props[5], 171 | reply_to: props[6], 172 | expiration: props[7], 173 | message_id: props[8], 174 | timestamp: props[9], 175 | type: props[10], 176 | user_id: props[11], 177 | app_id: props[12], 178 | cluster_id: props[13] }; 179 | }, 180 | 181 | _amqp_props: function(props) { 182 | return [props.content_type, 183 | props.content_encoding, 184 | props.headers, 185 | props.delivery_mode, 186 | props.priority, 187 | props.correlation_id, 188 | props.reply_to, 189 | props.expiration, 190 | props.message_id, 191 | props.timestamp, 192 | props.type, 193 | props.user_id, 194 | props.app_id, 195 | props.cluster_id]; 196 | }, 197 | 198 | basicPublish: function(exchange, routing_key, message, props, mandatory, immediate) { 199 | this._cast("basic.publish", [this.ticket, 200 | exchange, 201 | routing_key, 202 | this._dval(mandatory, false), 203 | this._dval(immediate, false)], 204 | message, this._amqp_props(props || {})); 205 | }, 206 | 207 | basicAck: function(delivery_tag, multiple) { 208 | this._cast("basic.ack", [delivery_tag, 209 | this._dval(multiple, false)]); 210 | }, 211 | 212 | basicCancel: function(consumer_tag) { 213 | var ch = this; 214 | return ch._call("basic.cancel", [consumer_tag, 215 | false // nowait 216 | ]) 217 | .addReplyTransformer(ch._extractArg(0)) 218 | .addCallback(function (tag) { 219 | var consumer = ch.consumers[tag]; 220 | delete ch.consumers[tag]; 221 | if (consumer && consumer.cancelOk) { 222 | consumer.cancelOk(tag); 223 | } 224 | }); 225 | }, 226 | 227 | poll_tophalf: function() { 228 | var ch = this; 229 | if (ch.alive) { 230 | ch.service.poll() 231 | .addCallback(function (result) { ch.handlePollResult(result) }) 232 | .addCallback(function () { ch.poll_tophalf() }); 233 | } 234 | }, 235 | 236 | close: function() { 237 | var ch = this; 238 | if (ch.alive) { 239 | ch.alive = false; 240 | ch.service.close() 241 | .addCallback(function (result) { ch.handlePollResult(result) }); 242 | } 243 | }, 244 | 245 | handlePollResult: function(result) { 246 | var ch = this; 247 | if (result === "stop") { 248 | ch.alive = false; 249 | } else { 250 | Support.each(result, function (e) { ch.handleAsyncMessage(e) }); 251 | } 252 | }, 253 | 254 | handleAsyncMessage: function (message) { 255 | var handler = this["handle_async_" + message.method]; 256 | if (handler) { 257 | handler.apply(this, [message.args, 258 | message.content, 259 | message.props]); 260 | } else { 261 | if (this.options.debug) { 262 | this.options.debugLogger({async: message}); 263 | } 264 | } 265 | }, 266 | 267 | "handle_async_basic.deliver": function(args, content, props) { 268 | var consumer = this.consumers[args[0]]; 269 | if (consumer) { 270 | try { 271 | consumer.deliver({content: content, 272 | delivery_tag: args[1], 273 | redelivered: args[2], 274 | exchange: args[3], 275 | routing_key: args[4], 276 | props: this._js_props(props)}); 277 | } catch (err) {} 278 | } 279 | } 280 | }); 281 | 282 | return { 283 | openChannel: openChannel, 284 | Channel: Channel 285 | }; 286 | } 287 | -------------------------------------------------------------------------------- /src/rabbit_jsonrpc_channel.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_jsonrpc_channel). 18 | -behaviour(gen_server). 19 | 20 | -include_lib("amqp_client/include/amqp_client.hrl"). 21 | 22 | -export([open/1, start_link/2]). 23 | -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2]). 24 | 25 | -define(CHANNELID, ?MODULE). 26 | 27 | open(Args) -> 28 | Oid = list_to_binary(rfc4627_jsonrpc:gen_object_name()), 29 | {ok, Pid} = supervisor:start_child(rabbit_jsonrpc_channel_sup, [Oid, Args]), 30 | Service = rfc4627_jsonrpc:service(Oid, 31 | <<"urn:uuid:b3f82f69-4f63-424b-8dbb-4fa53f63cf06">>, 32 | <<"1.2">>, 33 | [{<<"poll">>, []}, 34 | {<<"close">>, []}, 35 | {<<"call">>, [{"method", str}, 36 | {"args", arr}]}, 37 | {<<"cast">>, [{"method", str}, 38 | {"args", arr}, 39 | {"content", str}, 40 | {"props", arr}]}]), 41 | rfc4627_jsonrpc:register_service(Pid, Service), 42 | {ok, Oid}. 43 | 44 | start_link(Oid, Args) -> 45 | gen_server:start_link(?MODULE, [Oid, Args], []). 46 | 47 | %--------------------------------------------------------------------------- 48 | 49 | -record(state, {channel, 50 | connection, 51 | oid, 52 | vhost, 53 | realm, 54 | ticket, 55 | timeout_millisec, 56 | state, 57 | waiting_rpcs, 58 | waiting_polls, 59 | outbound}). 60 | 61 | compute_timeout(#state{timeout_millisec = T, waiting_polls = []}) -> 62 | T; 63 | compute_timeout(#state{timeout_millisec = T}) -> 64 | T div 2. 65 | 66 | check_reply({result, _}) -> 67 | ok; 68 | check_reply({error, _}) -> 69 | ok. 70 | 71 | noreply(State0) -> 72 | State = check_outbound(State0), 73 | {noreply, State, compute_timeout(State)}. 74 | 75 | reply(Reply, State0) -> 76 | check_reply(Reply), 77 | State = check_outbound(State0), 78 | {reply, Reply, State, compute_timeout(State)}. 79 | 80 | default_param(null, Default) -> 81 | Default; 82 | default_param(X, _Default) -> 83 | X. 84 | 85 | release_many(_Response, []) -> 86 | ok; 87 | release_many(Response, [From | Rest]) -> 88 | gen_server:reply(From, Response), 89 | release_many(Response, Rest). 90 | 91 | release_waiters(Response, State = #state{ waiting_polls = Waiters }) -> 92 | release_many(Response, Waiters), 93 | State#state{ waiting_polls = [] }. 94 | 95 | pop_outbound(State = #state{ outbound = Outbound }) -> 96 | case queue:to_list(Outbound) of 97 | [] -> 98 | {[], State}; 99 | OutboundList -> 100 | {OutboundList, State#state{ outbound = queue:new() }} 101 | end. 102 | 103 | empty_poll_result() -> 104 | {result, []}. 105 | 106 | stop_poll_result() -> 107 | {result, <<"stop">>}. 108 | 109 | check_outbound(State = #state{ waiting_polls = [] }) -> 110 | State; 111 | check_outbound(State0 = #state{ waiting_polls = [LuckyWaiter | Waiting] }) -> 112 | case pop_outbound(State0) of 113 | {[], State} -> 114 | State; 115 | {OutboundList, State} -> 116 | gen_server:reply(LuckyWaiter, {result, OutboundList}), 117 | release_waiters(empty_poll_result(), State#state{ waiting_polls = Waiting }) 118 | end. 119 | 120 | close_connection(State = #state { connection = none }) -> 121 | State; 122 | close_connection(State = #state { connection = Conn, channel = Ch }) -> 123 | ok = amqp_channel:close(Ch), 124 | ok = amqp_connection:close(Conn), 125 | State#state{connection = none, channel = none}. 126 | 127 | final_cleanup(RpcResponse, State0) -> 128 | State = #state{ waiting_rpcs = WaitingRpcs } = 129 | release_waiters(stop_poll_result(), check_outbound(State0)), 130 | release_many(RpcResponse, queue:to_list(WaitingRpcs)), 131 | State#state{ waiting_rpcs = queue:new() }. 132 | 133 | method_result(Command) -> 134 | method_result(Command, []). 135 | 136 | method_result(Command, ExtraFields) -> 137 | [Method | Args] = tuple_to_list(Command), 138 | {obj, [{"method", list_to_binary(atom_to_list(Method))}, 139 | {"args", Args} 140 | | ExtraFields]}. 141 | 142 | do_receive_reply(Command, State = #state{waiting_rpcs = WaitingRpcs}) -> 143 | case queue:out(WaitingRpcs) of 144 | {{value, From}, WaitingRpcsTail} -> 145 | gen_server:reply(From, {result, method_result(Command)}), 146 | State#state{waiting_rpcs = WaitingRpcsTail}; 147 | {empty, _} -> 148 | rabbit_log:error("Unsolicited RPC reply: ~p~n", [Command]), 149 | State 150 | end. 151 | 152 | do_receive_async(Method, #amqp_msg{props = Props, payload = Payload}, 153 | State = #state{outbound = Outbound}) -> 154 | ['P_basic' | PropValues] = tuple_to_list(Props), 155 | Result = method_result(Method, 156 | [{"content", Payload}, 157 | {"props", amqp_to_js(PropValues)}]), 158 | check_outbound(State#state{outbound = queue:in(Result, Outbound)}). 159 | 160 | enqueue_waiter(From, State = #state{ waiting_polls = Waiting }) -> 161 | State#state{ waiting_polls = [From | Waiting] }. 162 | 163 | build_props(P = #'P_basic'{}) -> 164 | P; 165 | build_props(Values) -> 166 | list_to_tuple(['P_basic' | js_to_amqp(Values)]). 167 | 168 | js_to_amqp([]) -> []; 169 | js_to_amqp([null | Rest]) -> [undefined | js_to_amqp(Rest)]; 170 | js_to_amqp([{obj, Fs} | Rest]) -> [js_table_to_amqp(Fs) | js_to_amqp(Rest)]; 171 | js_to_amqp([V | Rest]) -> [V | js_to_amqp(Rest)]. 172 | 173 | js_table_to_amqp([]) -> 174 | []; 175 | js_table_to_amqp([{KeyStr, Value} | Rest]) -> 176 | case guess_js_table_value(Value) of 177 | {Type, AmqpValue} -> 178 | [{list_to_binary(KeyStr), Type, AmqpValue} | js_table_to_amqp(Rest)]; 179 | unknown -> 180 | js_table_to_amqp(Rest) 181 | end. 182 | 183 | guess_js_table_value(X) when is_binary(X) -> {longstr, X}; 184 | guess_js_table_value(X) when is_integer(X) -> {signedint, X}; 185 | guess_js_table_value({obj, Fs}) -> {table, js_table_to_amqp(Fs)}; 186 | guess_js_table_value(X) when is_float(X) -> {double, X}; 187 | guess_js_table_value(X) when is_boolean(X) -> {bool, X}; 188 | guess_js_table_value(_) -> unknown. 189 | 190 | amqp_to_js([]) -> []; 191 | amqp_to_js([undefined | Rest]) -> [null | amqp_to_js(Rest)]; 192 | amqp_to_js([T | Rest]) when is_list(T) -> [{obj, amqp_table_to_js(T)} | amqp_to_js(Rest)]; 193 | amqp_to_js([V | Rest]) -> [V | amqp_to_js(Rest)]. 194 | 195 | amqp_table_to_js([]) -> 196 | []; 197 | amqp_table_to_js([{KeyBin, Type, Value} | Rest]) -> 198 | case guess_amqp_table_value(Type, Value) of 199 | {ok, V} -> 200 | [{binary_to_list(KeyBin), V} | amqp_table_to_js(Rest)]; 201 | unknown -> 202 | amqp_table_to_js(Rest) 203 | end. 204 | 205 | guess_amqp_table_value(longstr, X) -> {ok, X}; 206 | guess_amqp_table_value(signedint, X) -> {ok, X}; 207 | guess_amqp_table_value(table, X) -> {ok, {obj, amqp_table_to_js(X)}}; 208 | guess_amqp_table_value(double, X) -> {ok, X}; 209 | guess_amqp_table_value(bool, X) -> {ok, X}; 210 | guess_amqp_table_value(_, _) -> unknown. 211 | 212 | build_content(none, _) -> 213 | none; 214 | build_content(Bin, Props) -> 215 | #amqp_msg{props = build_props(Props), payload = Bin}. 216 | 217 | cast(Method, Content, State = #state{ channel = Ch }) -> 218 | ok = amqp_channel:cast(Ch, Method, Content), 219 | State. 220 | 221 | call(Method=#'basic.consume'{}, _Content, State = #state{ channel = Ch }) -> 222 | Reply = amqp_channel:subscribe(Ch, Method, self()), 223 | do_receive_reply(Reply, State); 224 | call(Method, Content, State = #state{ channel = Ch }) -> 225 | Reply = amqp_channel:call(Ch, Method, Content), 226 | do_receive_reply(Reply, State). 227 | 228 | check_invoke(MethodName, Args, Content, Props, StateBad, StateOk, Send, K) -> 229 | case catch list_to_existing_atom(binary_to_list(MethodName)) of 230 | {'EXIT', {badarg, _}} -> 231 | reply(rfc4627_jsonrpc:error_response(404, "AMQP method not found", 232 | {obj, [{"method", MethodName}, 233 | {"args", Args}]}), 234 | StateBad); 235 | 'channel.close' -> 236 | %% Forbid client-originated channel.close. We wrap 237 | %% channel.close in our own close method. 238 | reply(rfc4627_jsonrpc:error_response(405, "AMQP method not allowed", 239 | {obj, [{"method", MethodName}]}), 240 | StateBad); 241 | MethodAtom -> 242 | Method = list_to_tuple([MethodAtom | js_to_amqp(Args)]), 243 | Content1 = build_content(default_param(Content, none), 244 | default_param(Props, #'P_basic'{})), 245 | K(Send(Method, Content1, StateOk)) 246 | end. 247 | 248 | %--------------------------------------------------------------------------- 249 | 250 | init([Oid, [Username, Password, SessionTimeout0, VHostPath0]]) -> 251 | SessionTimeout = default_param(SessionTimeout0, 10), 252 | {ok, DefaultVHost} = application:get_env(default_vhost), 253 | VHostPath = default_param(VHostPath0, DefaultVHost), 254 | 255 | rabbit_log:info("HTTP Channel started, timeout ~p~n", [SessionTimeout]), 256 | SessionTimeoutMs = SessionTimeout * 1000, 257 | 258 | AdapterInfo = #amqp_adapter_info{protocol = {'JSON-RPC', "1.0"}}, 259 | Params = #amqp_params_direct{username = Username, 260 | password = Password, 261 | virtual_host = VHostPath, 262 | adapter_info = AdapterInfo}, 263 | {ok, Conn} = amqp_connection:start(Params), 264 | {ok, Ch} = amqp_connection:open_channel(Conn), 265 | %% The test suite basic.cancels a tag that does not exist. That is allowed 266 | %% but we need a default consumer for the cancel_ok. 267 | ok = amqp_selective_consumer:register_default_consumer(Ch, self()), 268 | {ok, 269 | #state{channel = Ch, 270 | connection = Conn, 271 | oid = Oid, 272 | vhost = VHostPath, 273 | timeout_millisec = SessionTimeoutMs, 274 | waiting_rpcs = queue:new(), 275 | waiting_polls = [], 276 | outbound = queue:new()}, 277 | SessionTimeoutMs}. 278 | 279 | handle_call({jsonrpc, <<"poll">>, _RequestInfo, []}, From, State) -> 280 | noreply(enqueue_waiter(From, State)); 281 | handle_call({jsonrpc, <<"close">>, _RequestInfo, []}, _From, State0) -> 282 | %%% FIXME: somehow propagate some of the args into the close reason given to other callers? 283 | rabbit_log:info("HTTP Channel closing by request.~n"), 284 | {OutboundList, State} = pop_outbound(State0), 285 | {stop, normal, {result, OutboundList}, close_connection(State)}; 286 | handle_call({jsonrpc, <<"call">>, _RequestInfo, [Method, Args]}, From, 287 | State = #state{waiting_rpcs = WaitingRpcs}) -> 288 | check_invoke(Method, Args, none, #'P_basic'{}, State, 289 | State#state{waiting_rpcs = queue:in(From, WaitingRpcs)}, 290 | fun call/3, 291 | fun noreply/1); 292 | handle_call({jsonrpc, <<"cast">>, _RequestInfo, [Method, Args, Content, Props]}, _From, State) -> 293 | check_invoke(Method, Args, Content, Props, State, State, 294 | fun cast/3, 295 | fun (State1) -> reply({result, []}, State1) end); 296 | handle_call(Request, _From, State) -> 297 | error_logger:error_msg("Unhandled call in ~p: ~p", [?MODULE, Request]), 298 | reply({result, not_supported}, State). 299 | 300 | handle_cast(Request, State) -> 301 | error_logger:error_msg("Unhandled cast in ~p: ~p", [?MODULE, Request]), 302 | noreply(State). 303 | 304 | handle_info(timeout, State = #state{waiting_polls = []}) -> 305 | rabbit_log:info("HTTP Channel timed out, closing.~n"), 306 | {stop, normal, State}; 307 | handle_info(timeout, State) -> 308 | noreply(release_waiters(empty_poll_result(), State)); 309 | handle_info(shutdown, State) -> 310 | rabbit_log:info("HTTP Channel writer shutdown requested.~n"), 311 | %% We're going to close pretty soon anyway. No special action needed here. 312 | noreply(State); 313 | handle_info(#'channel.close'{reply_code = ErrorCode, 314 | reply_text = ErrorText, 315 | method_id = MethodId}, State) -> 316 | handle_close(channel, ErrorCode, ErrorText, MethodId, State); 317 | handle_info(#'connection.close'{reply_code = ErrorCode, 318 | reply_text = ErrorText, 319 | method_id = MethodId}, State) -> 320 | handle_close(connection, ErrorCode, ErrorText, MethodId, State); 321 | handle_info(#'basic.consume_ok'{}, State) -> 322 | noreply(State); 323 | handle_info(#'basic.cancel_ok'{}, State) -> 324 | noreply(State); 325 | handle_info({Method, Content}, State) -> 326 | noreply(do_receive_async(Method, Content, State)); 327 | handle_info(Info, State) -> 328 | error_logger:error_msg("Unhandled info in ~p: ~p", [?MODULE, Info]), 329 | noreply(State). 330 | 331 | handle_close(Type, ErrorCode, ErrorText, MethodId, State) -> 332 | Detail = {obj, [{type, Type}, 333 | {code, list_to_binary(atom_to_list(ErrorCode))}, 334 | {text, list_to_binary(ErrorText)}, 335 | {method, list_to_binary(atom_to_list(MethodId))}]}, 336 | {stop, normal, final_cleanup( 337 | rfc4627_jsonrpc:error_response(500, "AMQP error", Detail), 338 | State)}. 339 | 340 | terminate(_Reason, State) -> 341 | _State1 = final_cleanup(rfc4627_jsonrpc:error_response(504, "Closed", null), 342 | close_connection(State)), 343 | ok. 344 | 345 | code_change(_OldVsn, State, _Extra) -> 346 | {ok, State}. 347 | -------------------------------------------------------------------------------- /src/rabbit_jsonrpc_channel_app.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_jsonrpc_channel_app). 18 | 19 | -behaviour(application). 20 | -export([start/2,stop/1]). 21 | 22 | start(_Type, _StartArgs) -> 23 | ContextRoot = case application:get_env(rabbitmq_jsonrpc_channel, js_root) of 24 | {ok, Root} -> Root; 25 | undefined -> "rabbitmq_lib" 26 | end, 27 | rabbit_web_dispatch:register_static_context(jsonrpc_lib, 28 | rabbit_jsonrpc:listener(), 29 | ContextRoot, ?MODULE, "priv/www", 30 | "JSON-RPC: JavaScript library"), 31 | rabbit_jsonrpc_channel_app_sup:start_link(). 32 | 33 | stop(_State) -> 34 | rabbit_web_dispatch:unregister_context(jsonrpc_lib), 35 | ok. 36 | -------------------------------------------------------------------------------- /src/rabbit_jsonrpc_channel_app_sup.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_jsonrpc_channel_app_sup). 18 | -behaviour(supervisor). 19 | 20 | -export([start_link/0]). 21 | -export([init/1]). 22 | 23 | -define(SERVER, ?MODULE). 24 | 25 | start_link() -> 26 | {ok, _Pid} = supervisor:start_link({local, ?SERVER}, ?MODULE, []). 27 | 28 | init([]) -> 29 | ChannelFactory = {rabbit_jsonrpc_channel_factory, 30 | {rabbit_jsonrpc_channel_factory, start_link, []}, 31 | permanent, 10000, worker, [rabbit_jsonrpc_channel_factory]}, 32 | ChannelSup = {rabbit_jsonrpc_channel_sup, 33 | {rabbit_jsonrpc_channel_sup, start_link, []}, 34 | permanent, 10000, supervisor, [rabbit_jsonrpc_channel_sup]}, 35 | 36 | {ok, {{one_for_one, 10, 10}, [ChannelSup, ChannelFactory]}}. 37 | -------------------------------------------------------------------------------- /src/rabbit_jsonrpc_channel_factory.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_jsonrpc_channel_factory). 18 | -behaviour(gen_server). 19 | 20 | -export([start_link/0]). 21 | -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2]). 22 | 23 | start_link() -> 24 | gen_server:start_link(?MODULE, [], []). 25 | 26 | %% ----------------------------------------------------------------------------- 27 | %% gen_server callbacks 28 | %% ----------------------------------------------------------------------------- 29 | 30 | init(_Args) -> 31 | Service = rfc4627_jsonrpc:service(<<"rabbitmq">>, 32 | <<"urn:uuid:f98a4235-20a9-4321-a15c-94878a6a14f3">>, 33 | <<"1.2">>, 34 | [{<<"open">>, [{"username", str}, 35 | {"password", str}, 36 | {"sessionTimeout", num}, 37 | {"virtualHost", str}]}]), 38 | rfc4627_jsonrpc:register_service(self(), Service), 39 | {ok, nostate}. 40 | 41 | handle_call({jsonrpc, <<"open">>, _RequestInfo, Args}, _From, State) -> 42 | {ok, Oid} = rabbit_jsonrpc_channel:open(Args), 43 | {reply, 44 | {result, {obj, [{service, Oid}]}}, 45 | State}. 46 | 47 | handle_cast(Request, State) -> 48 | error_logger:error_msg("Unhandled cast in ~p: ~p", [?MODULE, Request]), 49 | {noreply, State}. 50 | 51 | handle_info(Info, State) -> 52 | error_logger:error_msg("Unhandled info in ~p: ~p", [?MODULE, Info]), 53 | {noreply, State}. 54 | 55 | terminate(_Reason, _State) -> 56 | ok. 57 | 58 | code_change(_OldVsn, State, _Extra) -> 59 | {ok, State}. 60 | -------------------------------------------------------------------------------- /src/rabbit_jsonrpc_channel_sup.erl: -------------------------------------------------------------------------------- 1 | %% The contents of this file are subject to the Mozilla Public License 2 | %% Version 1.1 (the "License"); you may not use this file except in 3 | %% compliance with the License. You may obtain a copy of the License 4 | %% at http://www.mozilla.org/MPL/ 5 | %% 6 | %% Software distributed under the License is distributed on an "AS IS" 7 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 8 | %% the License for the specific language governing rights and 9 | %% limitations under the License. 10 | %% 11 | %% The Original Code is RabbitMQ. 12 | %% 13 | %% The Initial Developer of the Original Code is GoPivotal, Inc. 14 | %% Copyright (c) 2007-2013 GoPivotal, Inc. All rights reserved. 15 | %% 16 | 17 | -module(rabbit_jsonrpc_channel_sup). 18 | -behaviour(supervisor). 19 | 20 | -export([start_link/0]). 21 | -export([init/1]). 22 | 23 | -define(SERVER, ?MODULE). 24 | 25 | start_link() -> 26 | {ok, _Pid} = supervisor:start_link({local, ?SERVER}, ?MODULE, []). 27 | 28 | init([]) -> 29 | {ok, {{simple_one_for_one, 10, 10}, 30 | [{rabbit_jsonrpc_channel, {rabbit_jsonrpc_channel, start_link, []}, 31 | temporary, brutal_kill, worker, [rabbit_jsonrpc_channel]}]}}. 32 | -------------------------------------------------------------------------------- /src/rabbitmq_jsonrpc_channel.app.src: -------------------------------------------------------------------------------- 1 | {application, rabbitmq_jsonrpc_channel, 2 | [{description, "RabbitMQ JSON-RPC Channels"}, 3 | {vsn, "%%VSN%%"}, 4 | {modules, []}, 5 | {registered, []}, 6 | {mod, {rabbit_jsonrpc_channel_app, []}}, 7 | {env, [ 8 | {default_vhost, <<"/">>} 9 | ]}, 10 | {applications, [kernel, stdlib, rabbitmq_jsonrpc, rabbit, amqp_client]}]}. 11 | --------------------------------------------------------------------------------