| /* |
| /* |
| * Verto HTML5/Javascript Telephony Signaling and Control Protocol Stack for FreeSWITCH |
| * Verto HTML5/Javascript Telephony Signaling and Control Protocol Stack for FreeSWITCH |
| * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org> |
| * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org> |
| * |
| * |
| * Version: MPL 1.1 |
| * Version: MPL 1.1 |
| * |
| * |
| * The contents of this file are subject to the Mozilla Public License Version |
| * The contents of this file are subject to the Mozilla Public License Version |
| * 1.1 (the "License"); you may not use this file except in compliance with |
| * 1.1 (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * the License. You may obtain a copy of the License at |
| * http://www.mozilla.org/MPL/ |
| * http://www.mozilla.org/MPL/ |
| * |
| * |
| * Software distributed under the License is distributed on an "AS IS" basis, |
| * Software distributed under the License is distributed on an "AS IS" basis, |
| * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
| * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
| * for the specific language governing rights and limitations under the |
| * for the specific language governing rights and limitations under the |
| * License. |
| * License. |
| * |
| * |
| * The Original Code is jquery.jsonrpclient.js modified for Verto HTML5/Javascript Telephony Signaling and Control Protocol Stack for FreeSWITCH |
| * The Original Code is jquery.jsonrpclient.js modified for Verto HTML5/Javascript Telephony Signaling and Control Protocol Stack for FreeSWITCH |
| * |
| * |
| * The Initial Developer of the Original Code is |
| * The Initial Developer of the Original Code is |
| * Textalk AB http://textalk.se/ |
| * Textalk AB http://textalk.se/ |
| * Portions created by the Initial Developer are Copyright (C) |
| * Portions created by the Initial Developer are Copyright (C) |
| * the Initial Developer. All Rights Reserved. |
| * the Initial Developer. All Rights Reserved. |
| * |
| * |
| * Contributor(s): |
| * Contributor(s): |
| * |
| * |
| * Anthony Minessale II <anthm@freeswitch.org> |
| * Anthony Minessale II <anthm@freeswitch.org> |
. | |
| * |
| |
| * - Our Modifications: https://meeting.gomeet.com/lib/verto/jquery.jsonrpcclient.js.modifications.html |
| * |
| * |
| * jquery.jsonrpclient.js - JSON RPC client code |
| * jquery.jsonrpclient.js - JSON RPC client code |
| * |
| * |
| */ |
| */ |
| /** |
| /** |
| * This plugin requires jquery.json.js to be available, or at least the methods $.toJSON and |
| * This plugin requires jquery.json.js to be available, or at least the methods $.toJSON and |
| * $.parseJSON. |
| * $.parseJSON. |
| * |
| * |
| * The plan is to make use of websockets if they are available, but work just as well with only |
| * The plan is to make use of websockets if they are available, but work just as well with only |
| * http if not. |
| * http if not. |
| * |
| * |
| * Usage example: |
| * Usage example: |
| * |
| * |
| * var foo = new $.JsonRpcClient({ ajaxUrl: '/backend/jsonrpc' }); |
| * var foo = new $.JsonRpcClient({ ajaxUrl: '/backend/jsonrpc' }); |
| * foo.call( |
| * foo.call( |
| * 'bar', [ 'A parameter', 'B parameter' ], |
| * 'bar', [ 'A parameter', 'B parameter' ], |
| * function(result) { alert('Foo bar answered: ' + result.my_answer); }, |
| * function(result) { alert('Foo bar answered: ' + result.my_answer); }, |
| * function(error) { console.log('There was an error', error); } |
| * function(error) { console.log('There was an error', error); } |
| * ); |
| * ); |
| * |
| * |
| * More examples are available in README.md |
| * More examples are available in README.md |
| */ |
| */ |
| (function($) { |
| (function($) { |
| /** |
| /** |
| * @fn new |
| * @fn new |
| * @memberof $.JsonRpcClient |
| * @memberof $.JsonRpcClient |
| * |
| * |
| * @param options An object stating the backends: |
| * @param options An object stating the backends: |
| * ajaxUrl A url (relative or absolute) to a http(s) backend. |
| * ajaxUrl A url (relative or absolute) to a http(s) backend. |
| * socketUrl A url (relative of absolute) to a ws(s) backend. |
| * socketUrl A url (relative of absolute) to a ws(s) backend. |
| * onmessage A socket message handler for other messages (non-responses). |
| * onmessage A socket message handler for other messages (non-responses). |
| * getSocket A function returning a WebSocket or null. |
| * getSocket A function returning a WebSocket or null. |
| * It must take an onmessage_cb and bind it to the onmessage event |
| * It must take an onmessage_cb and bind it to the onmessage event |
| * (or chain it before/after some other onmessage handler). |
| * (or chain it before/after some other onmessage handler). |
| * Or, it could return null if no socket is available. |
| * Or, it could return null if no socket is available. |
| * The returned instance must have readyState <= 1, and if less than 1, |
| * The returned instance must have readyState <= 1, and if less than 1, |
| * react to onopen binding. |
| * react to onopen binding. |
| */ |
| */ |
| $.JsonRpcClient = function(options) { |
| $.JsonRpcClient = function(options) { |
| var self = this; |
| var self = this; |
| this.options = $.extend({ |
| this.options = $.extend({ |
| ajaxUrl : null, |
| ajaxUrl : null, |
| socketUrl : null, ///< The ws-url for default getSocket. |
| socketUrl : null, ///< The ws-url for default getSocket. |
| onmessage : null, ///< Other onmessage-handler. |
| onmessage : null, ///< Other onmessage-handler. |
| login : null, /// auth login |
| login : null, /// auth login |
| passwd : null, /// auth passwd |
| passwd : null, /// auth passwd |
| sessid : null, |
| sessid : null, |
| loginParams : null, |
| loginParams : null, |
| userVariables : null, |
| userVariables : null, |
| getSocket : function(onmessage_cb) { return self._getSocket(onmessage_cb); } |
| getSocket : function(onmessage_cb) { return self._getSocket(onmessage_cb); } |
| }, options); |
| }, options); |
| |
| |
| self.ws_cnt = 0; |
| self.ws_cnt = 0; |
| |
| |
| // Declare an instance version of the onmessage callback to wrap 'this'. |
| // Declare an instance version of the onmessage callback to wrap 'this'. |
| this.wsOnMessage = function(event) { self._wsOnMessage(event); }; |
| this.wsOnMessage = function(event) { self._wsOnMessage(event); }; |
| }; |
| }; |
| |
| |
| /// Holding the WebSocket on default getsocket. |
| /// Holding the WebSocket on default getsocket. |
| $.JsonRpcClient.prototype._ws_socket = null; |
| $.JsonRpcClient.prototype._ws_socket = null; |
| |
| |
| /// Object <id>: { success_cb: cb, error_cb: cb } |
| /// Object <id>: { success_cb: cb, error_cb: cb } |
| $.JsonRpcClient.prototype._ws_callbacks = {}; |
| $.JsonRpcClient.prototype._ws_callbacks = {}; |
| |
| |
| /// The next JSON-RPC request id. |
| /// The next JSON-RPC request id. |
| $.JsonRpcClient.prototype._current_id = 1; |
| $.JsonRpcClient.prototype._current_id = 1; |
| |
| |
| |
| |
| $.JsonRpcClient.prototype.speedTest = function (bytes, cb) { |
| $.JsonRpcClient.prototype.speedTest = function (bytes, cb) { |
| var socket = this.options.getSocket(this.wsOnMessage); |
| var socket = this.options.getSocket(this.wsOnMessage); |
| if (socket !== null) { |
| if (socket !== null) { |
. | |
| var loops = Math.floor(bytes / 1024); |
| |
| var rem = Math.floor(bytes % 1024); |
| |
| if (rem > 0 && rem < 4) { |
| |
| rem = 4; |
| |
| } |
| |
| |
| this.speedCB = cb; |
| this.speedCB = cb; |
. | this.speedBytes = bytes; |
| this.speedBytes = (loops * 1024) + rem; |
| socket.send("#SPU " + bytes); |
| socket.send("#SPU " + this.speedBytes); |
| |
| |
. | var loops = bytes / 1024; |
| |
| var rem = bytes % 1024; |
| |
| var i; |
| var i; |
. | var data = new Array(1024).join("."); |
| var data = "#SPB " + new Array(1024-5).join("."); |
| for (i = 0; i < loops; i++) { |
| for (i = 0; i < loops; i++) { |
. | socket.send("#SPB " + data); |
| socket.send(data); |
| } |
| } |
. | |
| |
| if (rem) { |
| if (rem) { |
. | socket.send("#SPB " + data); |
| socket.send(data.substring(0, rem)); |
| } |
| } |
| |
| |
| socket.send("#SPE"); |
| socket.send("#SPE"); |
| } |
| } |
| }; |
| }; |
| |
| |
| |
| |
| |
| |
| /** |
| /** |
| * @fn call |
| * @fn call |
| * @memberof $.JsonRpcClient |
| * @memberof $.JsonRpcClient |
| * |
| * |
| * @param method The method to run on JSON-RPC server. |
| * @param method The method to run on JSON-RPC server. |
| * @param params The params; an array or object. |
| * @param params The params; an array or object. |
| * @param success_cb A callback for successful request. |
| * @param success_cb A callback for successful request. |
| * @param error_cb A callback for error. |
| * @param error_cb A callback for error. |
| */ |
| */ |
| $.JsonRpcClient.prototype.call = function(method, params, success_cb, error_cb) { |
| $.JsonRpcClient.prototype.call = function(method, params, success_cb, error_cb) { |
| // Construct the JSON-RPC 2.0 request. |
| // Construct the JSON-RPC 2.0 request. |
| |
| |
| if (!params) { |
| if (!params) { |
| params = {}; |
| params = {}; |
| } |
| } |
| |
| |
| if (this.options.sessid) { |
| if (this.options.sessid) { |
| params.sessid = this.options.sessid; |
| params.sessid = this.options.sessid; |
| } |
| } |
| |
| |
| var request = { |
| var request = { |
| jsonrpc : '2.0', |
| jsonrpc : '2.0', |
| method : method, |
| method : method, |
| params : params, |
| params : params, |
| id : this._current_id++ // Increase the id counter to match request/response |
| id : this._current_id++ // Increase the id counter to match request/response |
| }; |
| }; |
| |
| |
| if (!success_cb) { |
| if (!success_cb) { |
| success_cb = function(e){console.log("Success: ", e);}; |
| success_cb = function(e){console.log("Success: ", e);}; |
| } |
| } |
| |
| |
| if (!error_cb) { |
| if (!error_cb) { |
| error_cb = function(e){console.log("Error: ", e);}; |
| error_cb = function(e){console.log("Error: ", e);}; |
| } |
| } |
| |
| |
| // Try making a WebSocket call. |
| // Try making a WebSocket call. |
| var socket = this.options.getSocket(this.wsOnMessage); |
| var socket = this.options.getSocket(this.wsOnMessage); |
| if (socket !== null) { |
| if (socket !== null) { |
| this._wsCall(socket, request, success_cb, error_cb); |
| this._wsCall(socket, request, success_cb, error_cb); |
| return; |
| return; |
| } |
| } |
| |
| |
| // No WebSocket, and no HTTP backend? This won't work. |
| // No WebSocket, and no HTTP backend? This won't work. |
| if (this.options.ajaxUrl === null) { |
| if (this.options.ajaxUrl === null) { |
| throw "$.JsonRpcClient.call used with no websocket and no http endpoint."; |
| throw "$.JsonRpcClient.call used with no websocket and no http endpoint."; |
| } |
| } |
| |
| |
| $.ajax({ |
| $.ajax({ |
| type : 'POST', |
| type : 'POST', |
| url : this.options.ajaxUrl, |
| url : this.options.ajaxUrl, |
| data : $.toJSON(request), |
| data : $.toJSON(request), |
| dataType : 'json', |
| dataType : 'json', |
| cache : false, |
| cache : false, |
| |
| |
| success : function(data) { |
| success : function(data) { |
| if ('error' in data) error_cb(data.error, this); |
| if ('error' in data) error_cb(data.error, this); |
| success_cb(data.result, this); |
| success_cb(data.result, this); |
| }, |
| }, |
| |
| |
| // JSON-RPC Server could return non-200 on error |
| // JSON-RPC Server could return non-200 on error |
| error : function(jqXHR, textStatus, errorThrown) { |
| error : function(jqXHR, textStatus, errorThrown) { |
| try { |
| try { |
| var response = $.parseJSON(jqXHR.responseText); |
| var response = $.parseJSON(jqXHR.responseText); |
| |
| |
| if ('console' in window) console.log(response); |
| if ('console' in window) console.log(response); |
| |
| |
| error_cb(response.error, this); |
| error_cb(response.error, this); |
| } catch (err) { |
| } catch (err) { |
| // Perhaps the responseText wasn't really a jsonrpc-error. |
| // Perhaps the responseText wasn't really a jsonrpc-error. |
| error_cb({ error: jqXHR.responseText }, this); |
| error_cb({ error: jqXHR.responseText }, this); |
| } |
| } |
| } |
| } |
| }); |
| }); |
| }; |
| }; |
| |
| |
| /** |
| /** |
| * Notify sends a command to the server that won't need a response. In http, there is probably |
| * Notify sends a command to the server that won't need a response. In http, there is probably |
| * an empty response - that will be dropped, but in ws there should be no response at all. |
| * an empty response - that will be dropped, but in ws there should be no response at all. |
| * |
| * |
| * This is very similar to call, but has no id and no handling of callbacks. |
| * This is very similar to call, but has no id and no handling of callbacks. |
| * |
| * |
| * @fn notify |
| * @fn notify |
| * @memberof $.JsonRpcClient |
| * @memberof $.JsonRpcClient |
| * |
| * |
| * @param method The method to run on JSON-RPC server. |
| * @param method The method to run on JSON-RPC server. |
| * @param params The params; an array or object. |
| * @param params The params; an array or object. |
| */ |
| */ |
| $.JsonRpcClient.prototype.notify = function(method, params) { |
| $.JsonRpcClient.prototype.notify = function(method, params) { |
| // Construct the JSON-RPC 2.0 request. |
| // Construct the JSON-RPC 2.0 request. |
| |
| |
| if (this.options.sessid) { |
| if (this.options.sessid) { |
| params.sessid = this.options.sessid; |
| params.sessid = this.options.sessid; |
| } |
| } |
| |
| |
| var request = { |
| var request = { |
| jsonrpc: '2.0', |
| jsonrpc: '2.0', |
| method: method, |
| method: method, |
| params: params |
| params: params |
| }; |
| }; |
| |
| |
| // Try making a WebSocket call. |
| // Try making a WebSocket call. |
| var socket = this.options.getSocket(this.wsOnMessage); |
| var socket = this.options.getSocket(this.wsOnMessage); |
| if (socket !== null) { |
| if (socket !== null) { |
| this._wsCall(socket, request); |
| this._wsCall(socket, request); |
| return; |
| return; |
| } |
| } |
| |
| |
| // No WebSocket, and no HTTP backend? This won't work. |
| // No WebSocket, and no HTTP backend? This won't work. |
| if (this.options.ajaxUrl === null) { |
| if (this.options.ajaxUrl === null) { |
| throw "$.JsonRpcClient.notify used with no websocket and no http endpoint."; |
| throw "$.JsonRpcClient.notify used with no websocket and no http endpoint."; |
| } |
| } |
| |
| |
| $.ajax({ |
| $.ajax({ |
| type : 'POST', |
| type : 'POST', |
| url : this.options.ajaxUrl, |
| url : this.options.ajaxUrl, |
| data : $.toJSON(request), |
| data : $.toJSON(request), |
| dataType : 'json', |
| dataType : 'json', |
| cache : false |
| cache : false |
| }); |
| }); |
| }; |
| }; |
| |
| |
| /** |
| /** |
| * Make a batch-call by using a callback. |
| * Make a batch-call by using a callback. |
| * |
| * |
| * The callback will get an object "batch" as only argument. On batch, you can call the methods |
| * The callback will get an object "batch" as only argument. On batch, you can call the methods |
| * "call" and "notify" just as if it was a normal $.JsonRpcClient object, and all calls will be |
| * "call" and "notify" just as if it was a normal $.JsonRpcClient object, and all calls will be |
| * sent as a batch call then the callback is done. |
| * sent as a batch call then the callback is done. |
| * |
| * |
| * @fn batch |
| * @fn batch |
| * @memberof $.JsonRpcClient |
| * @memberof $.JsonRpcClient |
| * |
| * |
| * @param callback The main function which will get a batch handler to run call and notify on. |
| * @param callback The main function which will get a batch handler to run call and notify on. |
| * @param all_done_cb A callback function to call after all results have been handled. |
| * @param all_done_cb A callback function to call after all results have been handled. |
| * @param error_cb A callback function to call if there is an error from the server. |
| * @param error_cb A callback function to call if there is an error from the server. |
| * Note, that batch calls should always get an overall success, and the |
| * Note, that batch calls should always get an overall success, and the |
| * only error |
| * only error |
| */ |
| */ |
| $.JsonRpcClient.prototype.batch = function(callback, all_done_cb, error_cb) { |
| $.JsonRpcClient.prototype.batch = function(callback, all_done_cb, error_cb) { |
| var batch = new $.JsonRpcClient._batchObject(this, all_done_cb, error_cb); |
| var batch = new $.JsonRpcClient._batchObject(this, all_done_cb, error_cb); |
| callback(batch); |
| callback(batch); |
| batch._execute(); |
| batch._execute(); |
| }; |
| }; |
| |
| |
| /** |
| /** |
| * The default getSocket handler. |
| * The default getSocket handler. |
| * |
| * |
| * @param onmessage_cb The callback to be bound to onmessage events on the socket. |
| * @param onmessage_cb The callback to be bound to onmessage events on the socket. |
| * |
| * |
| * @fn _getSocket |
| * @fn _getSocket |
| * @memberof $.JsonRpcClient |
| * @memberof $.JsonRpcClient |
| */ |
| */ |
| |
| |
| $.JsonRpcClient.prototype.socketReady = function() { |
| $.JsonRpcClient.prototype.socketReady = function() { |
| if (this._ws_socket === null || this._ws_socket.readyState > 1) { |
| if (this._ws_socket === null || this._ws_socket.readyState > 1) { |
| return false; |
| return false; |
| } |
| } |
| |
| |
| return true; |
| return true; |
| }; |
| }; |
| |
| |
| $.JsonRpcClient.prototype.closeSocket = function() { |
| $.JsonRpcClient.prototype.closeSocket = function() { |
| var self = this; |
| var self = this; |
| if (self.socketReady()) { |
| if (self.socketReady()) { |
| self._ws_socket.onclose = function (w) {console.log("Closing Socket");}; |
| self._ws_socket.onclose = function (w) {console.log("Closing Socket");}; |
| self._ws_socket.close(); |
| self._ws_socket.close(); |
| } |
| } |
| }; |
| }; |
| |
| |
| $.JsonRpcClient.prototype.loginData = function(params) { |
| $.JsonRpcClient.prototype.loginData = function(params) { |
| var self = this; |
| var self = this; |
| self.options.login = params.login; |
| self.options.login = params.login; |
| self.options.passwd = params.passwd; |
| self.options.passwd = params.passwd; |
| self.options.loginParams = params.loginParams; |
| self.options.loginParams = params.loginParams; |
| self.options.userVariables = params.userVariables; |
| self.options.userVariables = params.userVariables; |
| }; |
| }; |
| |
| |
| $.JsonRpcClient.prototype.connectSocket = function(onmessage_cb) { |
| $.JsonRpcClient.prototype.connectSocket = function(onmessage_cb) { |
| var self = this; |
| var self = this; |
| |
| |
| if (self.to) { |
| if (self.to) { |
| clearTimeout(self.to); |
| clearTimeout(self.to); |
| } |
| } |
| |
| |
| if (!self.socketReady()) { |
| if (!self.socketReady()) { |
| self.authing = false; |
| self.authing = false; |
| |
| |
| if (self._ws_socket) { |
| if (self._ws_socket) { |
| delete self._ws_socket; |
| delete self._ws_socket; |
| } |
| } |
| |
| |
| // No socket, or dying socket, let's get a new one. |
| // No socket, or dying socket, let's get a new one. |
| self._ws_socket = new WebSocket(self.options.socketUrl); |
| self._ws_socket = new WebSocket(self.options.socketUrl); |
| |
| |
| if (self._ws_socket) { |
| if (self._ws_socket) { |
| // Set up onmessage handler. |
| // Set up onmessage handler. |
| self._ws_socket.onmessage = onmessage_cb; |
| self._ws_socket.onmessage = onmessage_cb; |
| self._ws_socket.onclose = function (w) { |
| self._ws_socket.onclose = function (w) { |
| if (!self.ws_sleep) { |
| if (!self.ws_sleep) { |
| self.ws_sleep = 1000; |
| self.ws_sleep = 1000; |
| } |
| } |
| |
| |
| if (self.options.onWSClose) { |
| if (self.options.onWSClose) { |
| self.options.onWSClose(self); |
| self.options.onWSClose(self); |
| } |
| } |
| |
| |
. | |
| if (self.ws_cnt > 10 && self.options.wsFallbackURL) { |
| |
| self.options.socketUrl = self.options.wsFallbackURL; |
| |
| } |
| |
| |
| console.error("Websocket Lost " + self.ws_cnt + " sleep: " + self.ws_sleep + "msec"); |
| console.error("Websocket Lost " + self.ws_cnt + " sleep: " + self.ws_sleep + "msec"); |
| |
| |
| self.to = setTimeout(function() { |
| self.to = setTimeout(function() { |
| console.log("Attempting Reconnection...."); |
| console.log("Attempting Reconnection...."); |
| self.connectSocket(onmessage_cb); |
| self.connectSocket(onmessage_cb); |
| }, self.ws_sleep); |
| }, self.ws_sleep); |
| |
| |
| self.ws_cnt++; |
| self.ws_cnt++; |
| |
| |
| if (self.ws_sleep < 3000 && (self.ws_cnt % 10) === 0) { |
| if (self.ws_sleep < 3000 && (self.ws_cnt % 10) === 0) { |
| self.ws_sleep += 1000; |
| self.ws_sleep += 1000; |
| } |
| } |
| }; |
| }; |
| |
| |
| // Set up sending of message for when the socket is open. |
| // Set up sending of message for when the socket is open. |
| self._ws_socket.onopen = function() { |
| self._ws_socket.onopen = function() { |
| if (self.to) { |
| if (self.to) { |
| clearTimeout(self.to); |
| clearTimeout(self.to); |
| } |
| } |
| self.ws_sleep = 1000; |
| self.ws_sleep = 1000; |
| self.ws_cnt = 0; |
| self.ws_cnt = 0; |
| if (self.options.onWSConnect) { |
| if (self.options.onWSConnect) { |
| self.options.onWSConnect(self); |
| self.options.onWSConnect(self); |
| } |
| } |
| |
| |
| var req; |
| var req; |
| // Send the requests. |
| // Send the requests. |
| while ((req = $.JsonRpcClient.q.pop())) { |
| while ((req = $.JsonRpcClient.q.pop())) { |
| self._ws_socket.send(req); |
| self._ws_socket.send(req); |
| } |
| } |
| }; |
| }; |
| } |
| } |
| } |
| } |
| |
| |
| return self._ws_socket ? true : false; |
| return self._ws_socket ? true : false; |
| }; |
| }; |
| |
| |
| $.JsonRpcClient.prototype.stopRetrying = function() { |
| $.JsonRpcClient.prototype.stopRetrying = function() { |
| if (self.to) |
| if (self.to) |
| clearTimeout(self.to); |
| clearTimeout(self.to); |
| } |
| } |
| |
| |
| $.JsonRpcClient.prototype._getSocket = function(onmessage_cb) { |
| $.JsonRpcClient.prototype._getSocket = function(onmessage_cb) { |
| // If there is no ws url set, we don't have a socket. |
| // If there is no ws url set, we don't have a socket. |
| // Likewise, if there is no window.WebSocket. |
| // Likewise, if there is no window.WebSocket. |
| if (this.options.socketUrl === null || !("WebSocket" in window)) return null; |
| if (this.options.socketUrl === null || !("WebSocket" in window)) return null; |
| |
| |
| this.connectSocket(onmessage_cb); |
| this.connectSocket(onmessage_cb); |
| |
| |
| return this._ws_socket; |
| return this._ws_socket; |
| }; |
| }; |
| |
| |
| /** |
| /** |
| * Queue to save messages delivered when websocket is not ready |
| * Queue to save messages delivered when websocket is not ready |
| */ |
| */ |
| $.JsonRpcClient.q = []; |
| $.JsonRpcClient.q = []; |
| |
| |
| /** |
| /** |
| * Internal handler to dispatch a JRON-RPC request through a websocket. |
| * Internal handler to dispatch a JRON-RPC request through a websocket. |
| * |
| * |
| * @fn _wsCall |
| * @fn _wsCall |
| * @memberof $.JsonRpcClient |
| * @memberof $.JsonRpcClient |
| */ |
| */ |
| $.JsonRpcClient.prototype._wsCall = function(socket, request, success_cb, error_cb) { |
| $.JsonRpcClient.prototype._wsCall = function(socket, request, success_cb, error_cb) { |
| var request_json = $.toJSON(request); |
| var request_json = $.toJSON(request); |
| |
| |
| if (socket.readyState < 1) { |
| if (socket.readyState < 1) { |
| // The websocket is not open yet; we have to set sending of the message in onopen. |
| // The websocket is not open yet; we have to set sending of the message in onopen. |
| self = this; // In closure below, this is set to the WebSocket. Use self instead. |
| self = this; // In closure below, this is set to the WebSocket. Use self instead. |
| $.JsonRpcClient.q.push(request_json); |
| $.JsonRpcClient.q.push(request_json); |
| } else { |
| } else { |
| // We have a socket and it should be ready to send on. |
| // We have a socket and it should be ready to send on. |
| socket.send(request_json); |
| socket.send(request_json); |
| } |
| } |
| |
| |
| // Setup callbacks. If there is an id, this is a call and not a notify. |
| // Setup callbacks. If there is an id, this is a call and not a notify. |
| if ('id' in request && typeof success_cb !== 'undefined') { |
| if ('id' in request && typeof success_cb !== 'undefined') { |
| this._ws_callbacks[request.id] = { request: request_json, request_obj: request, success_cb: success_cb, error_cb: error_cb }; |
| this._ws_callbacks[request.id] = { request: request_json, request_obj: request, success_cb: success_cb, error_cb: error_cb }; |
| } |
| } |
| }; |
| }; |
| |
| |
| /** |
| /** |
| * Internal handler for the websocket messages. It determines if the message is a JSON-RPC |
| * Internal handler for the websocket messages. It determines if the message is a JSON-RPC |
| * response, and if so, tries to couple it with a given callback. Otherwise, it falls back to |
| * response, and if so, tries to couple it with a given callback. Otherwise, it falls back to |
| * given external onmessage-handler, if any. |
| * given external onmessage-handler, if any. |
| * |
| * |
| * @param event The websocket onmessage-event. |
| * @param event The websocket onmessage-event. |
| */ |
| */ |
| $.JsonRpcClient.prototype._wsOnMessage = function(event) { |
| $.JsonRpcClient.prototype._wsOnMessage = function(event) { |
. | |
| var self = this; //Added this line for ANYAM-6816 |
| |
| |
| // Check if this could be a JSON RPC message. |
| // Check if this could be a JSON RPC message. |
| var response; |
| var response; |
| |
| |
| // Special sub proto |
| // Special sub proto |
. | if (event.data[0] == "#" && event.data[1] == "S" && event.data[2] == "P") { |
| if (event.data[0] == "#") { |
| |
| // ANYAM-10038 Fix downlink calculation |
| |
| if (event.data.length >= 4 && event.data[1] == "S" && event.data[2] == "P") { |
| if (event.data[3] == "U") { |
| if (event.data[3] == "U") { |
. | |
| var current_time = new Date().getTime(); |
| this.up_dur = parseInt(event.data.substring(4)); |
| this.up_dur = parseInt(event.data.substring(4)); |
. | } else if (this.speedCB && event.data[3] == "D") { |
| this.down_dur = 0; |
| this.down_dur = parseInt(event.data.substring(4)); |
| this.speed_down_start_time = current_time; |
| |
| this.speed_down_bytes = 0; |
| |
| |
| |
| } else if (event.data[3] == "B" && this.speed_down_start_time > 0) { |
| |
| this.speed_down_bytes += event.data.length; |
| |
| |
| |
| } else if (event.data[3] == "D" && this.speed_down_start_time > 0) { |
| |
| var current_time = new Date().getTime(); |
| |
| this.down_dur = (current_time - this.speed_down_start_time); |
| var up_kps = (((this.speedBytes * 8) / (this.up_dur / 1000)) / 1024).toFixed(0); |
| var up_kps = (((this.speedBytes * 8) / (this.up_dur / 1000)) / 1024).toFixed(0); |
. | var down_kps = (((this.speedBytes * 8) / (this.down_dur / 1000)) / 1024).toFixed(0); |
| var down_kps = (((this.speed_down_bytes * 8) / (this.down_dur / 1000)) / 1024).toFixed(0); |
| |
| |
| console.info("Speed Test: Up: " + up_kps + " Down: " + down_kps); |
| console.info("Speed Test: Up: " + up_kps + " Down: " + down_kps); |
. | this.speedCB(event, { upDur: this.up_dur, downDur: this.down_dur, upKPS: up_kps, downKPS: down_kps }); |
| var cb = this.speedCB; |
| this.speedCB = null; |
| this.speedCB = null; |
. | |
| cb(event, { |
| |
| upDur: this.up_dur, |
| |
| downDur: this.down_dur, |
| |
| upKPS: up_kps, |
| |
| downKPS: down_kps |
| |
| }); |
| |
| } |
| |
| |
| |
| } else if (event.data.length >= 5 && event.data[1] == "P" && event.data[2] == "I" && event.data[3] == "N" && event.data[4] == "G") { |
| |
| var socket = this.options.getSocket(this.wsOnMessage); |
| |
| if (socket !== null) { |
| |
| socket.send("#PONG " + event.data.substring(6, 30)); |
| |
| } |
| } |
| } |
| |
| |
| return; |
| return; |
| } |
| } |
| |
| |
| |
| |
| try { |
| try { |
| response = $.parseJSON(event.data); |
| response = $.parseJSON(event.data); |
| |
| |
| /// @todo Make using the jsonrcp 2.0 check optional, to use this on JSON-RPC 1 backends. |
| /// @todo Make using the jsonrcp 2.0 check optional, to use this on JSON-RPC 1 backends. |
| |
| |
| if (typeof response === 'object' && |
| if (typeof response === 'object' && |
| 'jsonrpc' in response && |
| 'jsonrpc' in response && |
| response.jsonrpc === '2.0') { |
| response.jsonrpc === '2.0') { |
| |
| |
| /// @todo Handle bad response (without id). |
| /// @todo Handle bad response (without id). |
| |
| |
| // If this is an object with result, it is a response. |
| // If this is an object with result, it is a response. |
| if ('result' in response && this._ws_callbacks[response.id]) { |
| if ('result' in response && this._ws_callbacks[response.id]) { |
| // Get the success callback. |
| // Get the success callback. |
| var success_cb = this._ws_callbacks[response.id].success_cb; |
| var success_cb = this._ws_callbacks[response.id].success_cb; |
| |
| |
| /* |
| /* |
| // set the sessid if present |
| // set the sessid if present |
| if ('sessid' in response.result && !this.options.sessid || (this.options.sessid != response.result.sessid)) { |
| if ('sessid' in response.result && !this.options.sessid || (this.options.sessid != response.result.sessid)) { |
| this.options.sessid = response.result.sessid; |
| this.options.sessid = response.result.sessid; |
| if (this.options.sessid) { |
| if (this.options.sessid) { |
| console.log("setting session UUID to: " + this.options.sessid); |
| console.log("setting session UUID to: " + this.options.sessid); |
| } |
| } |
| } |
| } |
| */ |
| */ |
| // Delete the callback from the storage. |
| // Delete the callback from the storage. |
| delete this._ws_callbacks[response.id]; |
| delete this._ws_callbacks[response.id]; |
| |
| |
| // Run callback with result as parameter. |
| // Run callback with result as parameter. |
| success_cb(response.result, this); |
| success_cb(response.result, this); |
| return; |
| return; |
| } else if ('error' in response && this._ws_callbacks[response.id]) { |
| } else if ('error' in response && this._ws_callbacks[response.id]) { |
| // If this is an object with error, it is an error response. |
| // If this is an object with error, it is an error response. |
| |
| |
| // Get the error callback. |
| // Get the error callback. |
| var error_cb = this._ws_callbacks[response.id].error_cb; |
| var error_cb = this._ws_callbacks[response.id].error_cb; |
| var orig_req = this._ws_callbacks[response.id].request; |
| var orig_req = this._ws_callbacks[response.id].request; |
| |
| |
| // if this is an auth request, send the credentials and resend the failed request |
| // if this is an auth request, send the credentials and resend the failed request |
| if (!self.authing && response.error.code == -32000 && self.options.login && self.options.passwd) { |
| if (!self.authing && response.error.code == -32000 && self.options.login && self.options.passwd) { |
| self.authing = true; |
| self.authing = true; |
| |
| |
| this.call("login", { login: self.options.login, passwd: self.options.passwd, loginParams: self.options.loginParams, |
| this.call("login", { login: self.options.login, passwd: self.options.passwd, loginParams: self.options.loginParams, |
| userVariables: self.options.userVariables}, |
| userVariables: self.options.userVariables}, |
| this._ws_callbacks[response.id].request_obj.method == "login" ? |
| this._ws_callbacks[response.id].request_obj.method == "login" ? |
| function(e) { |
| function(e) { |
| self.authing = false; |
| self.authing = false; |
| console.log("logged in"); |
| console.log("logged in"); |
| delete self._ws_callbacks[response.id]; |
| delete self._ws_callbacks[response.id]; |
| |
| |
| if (self.options.onWSLogin) { |
| if (self.options.onWSLogin) { |
| self.options.onWSLogin(true, self); |
| self.options.onWSLogin(true, self); |
| } |
| } |
| } |
| } |
| |
| |
| : |
| : |
| |
| |
| function(e) { |
| function(e) { |
| self.authing = false; |
| self.authing = false; |
| console.log("logged in, resending request id: " + response.id); |
| console.log("logged in, resending request id: " + response.id); |
| var socket = self.options.getSocket(self.wsOnMessage); |
| var socket = self.options.getSocket(self.wsOnMessage); |
| if (socket !== null) { |
| if (socket !== null) { |
| socket.send(orig_req); |
| socket.send(orig_req); |
| } |
| } |
| if (self.options.onWSLogin) { |
| if (self.options.onWSLogin) { |
| self.options.onWSLogin(true, self); |
| self.options.onWSLogin(true, self); |
| } |
| } |
| }, |
| }, |
| |
| |
| function(e) { |
| function(e) { |
| console.log("error logging in, request id:", response.id); |
| console.log("error logging in, request id:", response.id); |
| delete self._ws_callbacks[response.id]; |
| delete self._ws_callbacks[response.id]; |
| error_cb(response.error, this); |
| error_cb(response.error, this); |
| if (self.options.onWSLogin) { |
| if (self.options.onWSLogin) { |
| self.options.onWSLogin(false, self); |
| self.options.onWSLogin(false, self); |
| } |
| } |
| }); |
| }); |
| return; |
| return; |
| } |
| } |
| |
| |
| // Delete the callback from the storage. |
| // Delete the callback from the storage. |
| delete this._ws_callbacks[response.id]; |
| delete this._ws_callbacks[response.id]; |
| |
| |
| // Run callback with the error object as parameter. |
| // Run callback with the error object as parameter. |
| error_cb(response.error, this); |
| error_cb(response.error, this); |
| return; |
| return; |
| } |
| } |
| } |
| } |
| } catch (err) { |
| } catch (err) { |
| // Probably an error while parsing a non json-string as json. All real JSON-RPC cases are |
| // Probably an error while parsing a non json-string as json. All real JSON-RPC cases are |
| // handled above, and the fallback method is called below. |
| // handled above, and the fallback method is called below. |
| console.log("ERROR: "+ err); |
| console.log("ERROR: "+ err); |
| return; |
| return; |
| } |
| } |
| |
| |
| // This is not a JSON-RPC response. Call the fallback message handler, if given. |
| // This is not a JSON-RPC response. Call the fallback message handler, if given. |
| if (typeof this.options.onmessage === 'function') { |
| if (typeof this.options.onmessage === 'function') { |
| event.eventData = response; |
| event.eventData = response; |
| if (!event.eventData) { |
| if (!event.eventData) { |
| event.eventData = {}; |
| event.eventData = {}; |
| } |
| } |
| |
| |
| var reply = this.options.onmessage(event); |
| var reply = this.options.onmessage(event); |
| |
| |
| if (reply && typeof reply === "object" && event.eventData.id) { |
| if (reply && typeof reply === "object" && event.eventData.id) { |
| var msg = { |
| var msg = { |
| jsonrpc: "2.0", |
| jsonrpc: "2.0", |
| id: event.eventData.id, |
| id: event.eventData.id, |
| result: reply |
| result: reply |
| }; |
| }; |
| |
| |
| var socket = self.options.getSocket(self.wsOnMessage); |
| var socket = self.options.getSocket(self.wsOnMessage); |
| if (socket !== null) { |
| if (socket !== null) { |
| socket.send($.toJSON(msg)); |
| socket.send($.toJSON(msg)); |
| } |
| } |
| } |
| } |
| } |
| } |
| }; |
| }; |
| |
| |
| |
| |
| /************************************************************************************************ |
| /************************************************************************************************ |
| * Batch object with methods |
| * Batch object with methods |
| ************************************************************************************************/ |
| ************************************************************************************************/ |
| |
| |
| /** |
| /** |
| * Handling object for batch calls. |
| * Handling object for batch calls. |
| */ |
| */ |
| $.JsonRpcClient._batchObject = function(jsonrpcclient, all_done_cb, error_cb) { |
| $.JsonRpcClient._batchObject = function(jsonrpcclient, all_done_cb, error_cb) { |
| // Array of objects to hold the call and notify requests. Each objects will have the request |
| // Array of objects to hold the call and notify requests. Each objects will have the request |
| // object, and unless it is a notify, success_cb and error_cb. |
| // object, and unless it is a notify, success_cb and error_cb. |
| this._requests = []; |
| this._requests = []; |
| |
| |
| this.jsonrpcclient = jsonrpcclient; |
| this.jsonrpcclient = jsonrpcclient; |
| this.all_done_cb = all_done_cb; |
| this.all_done_cb = all_done_cb; |
| this.error_cb = typeof error_cb === 'function' ? error_cb : function() {}; |
| this.error_cb = typeof error_cb === 'function' ? error_cb : function() {}; |
| |
| |
| }; |
| }; |
| |
| |
| /** |
| /** |
| * @sa $.JsonRpcClient.prototype.call |
| * @sa $.JsonRpcClient.prototype.call |
| */ |
| */ |
| $.JsonRpcClient._batchObject.prototype.call = function(method, params, success_cb, error_cb) { |
| $.JsonRpcClient._batchObject.prototype.call = function(method, params, success_cb, error_cb) { |
| |
| |
| if (!params) { |
| if (!params) { |
| params = {}; |
| params = {}; |
| } |
| } |
| |
| |
| if (this.options.sessid) { |
| if (this.options.sessid) { |
| params.sessid = this.options.sessid; |
| params.sessid = this.options.sessid; |
| } |
| } |
| |
| |
| if (!success_cb) { |
| if (!success_cb) { |
| success_cb = function(e){console.log("Success: ", e);}; |
| success_cb = function(e){console.log("Success: ", e);}; |
| } |
| } |
| |
| |
| if (!error_cb) { |
| if (!error_cb) { |
| error_cb = function(e){console.log("Error: ", e);}; |
| error_cb = function(e){console.log("Error: ", e);}; |
| } |
| } |
| |
| |
| this._requests.push({ |
| this._requests.push({ |
| request : { |
| request : { |
| jsonrpc : '2.0', |
| jsonrpc : '2.0', |
| method : method, |
| method : method, |
| params : params, |
| params : params, |
| id : this.jsonrpcclient._current_id++ // Use the client's id series. |
| id : this.jsonrpcclient._current_id++ // Use the client's id series. |
| }, |
| }, |
| success_cb : success_cb, |
| success_cb : success_cb, |
| error_cb : error_cb |
| error_cb : error_cb |
| }); |
| }); |
| }; |
| }; |
| |
| |
| /** |
| /** |
| * @sa $.JsonRpcClient.prototype.notify |
| * @sa $.JsonRpcClient.prototype.notify |
| */ |
| */ |
| $.JsonRpcClient._batchObject.prototype.notify = function(method, params) { |
| $.JsonRpcClient._batchObject.prototype.notify = function(method, params) { |
| if (this.options.sessid) { |
| if (this.options.sessid) { |
| params.sessid = this.options.sessid; |
| params.sessid = this.options.sessid; |
| } |
| } |
| |
| |
| this._requests.push({ |
| this._requests.push({ |
| request : { |
| request : { |
| jsonrpc : '2.0', |
| jsonrpc : '2.0', |
| method : method, |
| method : method, |
| params : params |
| params : params |
| } |
| } |
| }); |
| }); |
| }; |
| }; |
| |
| |
| /** |
| /** |
| * Executes the batched up calls. |
| * Executes the batched up calls. |
| */ |
| */ |
| $.JsonRpcClient._batchObject.prototype._execute = function() { |
| $.JsonRpcClient._batchObject.prototype._execute = function() { |
| var self = this; |
| var self = this; |
| |
| |
| if (this._requests.length === 0) return; // All done :P |
| if (this._requests.length === 0) return; // All done :P |
| |
| |
| // Collect all request data and sort handlers by request id. |
| // Collect all request data and sort handlers by request id. |
| var batch_request = []; |
| var batch_request = []; |
| var handlers = {}; |
| var handlers = {}; |
| var i = 0; |
| var i = 0; |
| var call; |
| var call; |
| var success_cb; |
| var success_cb; |
| var error_cb; |
| var error_cb; |
| |
| |
| // If we have a WebSocket, just send the requests individually like normal calls. |
| // If we have a WebSocket, just send the requests individually like normal calls. |
| var socket = self.jsonrpcclient.options.getSocket(self.jsonrpcclient.wsOnMessage); |
| var socket = self.jsonrpcclient.options.getSocket(self.jsonrpcclient.wsOnMessage); |
| if (socket !== null) { |
| if (socket !== null) { |
| for (i = 0; i < this._requests.length; i++) { |
| for (i = 0; i < this._requests.length; i++) { |
| call = this._requests[i]; |
| call = this._requests[i]; |
| success_cb = ('success_cb' in call) ? call.success_cb : undefined; |
| success_cb = ('success_cb' in call) ? call.success_cb : undefined; |
| error_cb = ('error_cb' in call) ? call.error_cb : undefined; |
| error_cb = ('error_cb' in call) ? call.error_cb : undefined; |
| self.jsonrpcclient._wsCall(socket, call.request, success_cb, error_cb); |
| self.jsonrpcclient._wsCall(socket, call.request, success_cb, error_cb); |
| } |
| } |
| |
| |
| if (typeof all_done_cb === 'function') all_done_cb(result); |
| if (typeof all_done_cb === 'function') all_done_cb(result); |
| return; |
| return; |
| } |
| } |
| |
| |
| for (i = 0; i < this._requests.length; i++) { |
| for (i = 0; i < this._requests.length; i++) { |
| call = this._requests[i]; |
| call = this._requests[i]; |
| batch_request.push(call.request); |
| batch_request.push(call.request); |
| |
| |
| // If the request has an id, it should handle returns (otherwise it's a notify). |
| // If the request has an id, it should handle returns (otherwise it's a notify). |
| if ('id' in call.request) { |
| if ('id' in call.request) { |
| handlers[call.request.id] = { |
| handlers[call.request.id] = { |
| success_cb : call.success_cb, |
| success_cb : call.success_cb, |
| error_cb : call.error_cb |
| error_cb : call.error_cb |
| }; |
| }; |
| } |
| } |
| } |
| } |
| |
| |
| success_cb = function(data) { self._batchCb(data, handlers, self.all_done_cb); }; |
| success_cb = function(data) { self._batchCb(data, handlers, self.all_done_cb); }; |
| |
| |
| // No WebSocket, and no HTTP backend? This won't work. |
| // No WebSocket, and no HTTP backend? This won't work. |
| if (self.jsonrpcclient.options.ajaxUrl === null) { |
| if (self.jsonrpcclient.options.ajaxUrl === null) { |
| throw "$.JsonRpcClient.batch used with no websocket and no http endpoint."; |
| throw "$.JsonRpcClient.batch used with no websocket and no http endpoint."; |
| } |
| } |
| |
| |
| // Send request |
| // Send request |
| $.ajax({ |
| $.ajax({ |
| url : self.jsonrpcclient.options.ajaxUrl, |
| url : self.jsonrpcclient.options.ajaxUrl, |
| data : $.toJSON(batch_request), |
| data : $.toJSON(batch_request), |
| dataType : 'json', |
| dataType : 'json', |
| cache : false, |
| cache : false, |
| type : 'POST', |
| type : 'POST', |
| |
| |
| // Batch-requests should always return 200 |
| // Batch-requests should always return 200 |
| error : function(jqXHR, textStatus, errorThrown) { |
| error : function(jqXHR, textStatus, errorThrown) { |
| self.error_cb(jqXHR, textStatus, errorThrown); |
| self.error_cb(jqXHR, textStatus, errorThrown); |
| }, |
| }, |
| success : success_cb |
| success : success_cb |
| }); |
| }); |
| }; |
| }; |
| |
| |
| /** |
| /** |
| * Internal helper to match the result array from a batch call to their respective callbacks. |
| * Internal helper to match the result array from a batch call to their respective callbacks. |
| * |
| * |
| * @fn _batchCb |
| * @fn _batchCb |
| * @memberof $.JsonRpcClient |
| * @memberof $.JsonRpcClient |
| */ |
| */ |
| $.JsonRpcClient._batchObject.prototype._batchCb = function(result, handlers, all_done_cb) { |
| $.JsonRpcClient._batchObject.prototype._batchCb = function(result, handlers, all_done_cb) { |
| for (var i = 0; i < result.length; i++) { |
| for (var i = 0; i < result.length; i++) { |
| var response = result[i]; |
| var response = result[i]; |
| |
| |
| // Handle error |
| // Handle error |
| if ('error' in response) { |
| if ('error' in response) { |
| if (response.id === null || !(response.id in handlers)) { |
| if (response.id === null || !(response.id in handlers)) { |
| // An error on a notify? Just log it to the console. |
| // An error on a notify? Just log it to the console. |
| if ('console' in window) console.log(response); |
| if ('console' in window) console.log(response); |
| } else { |
| } else { |
| handlers[response.id].error_cb(response.error, this); |
| handlers[response.id].error_cb(response.error, this); |
| } |
| } |
| } else { |
| } else { |
| // Here we should always have a correct id and no error. |
| // Here we should always have a correct id and no error. |
| if (!(response.id in handlers) && 'console' in window) { |
| if (!(response.id in handlers) && 'console' in window) { |
| console.log(response); |
| console.log(response); |
| } else { |
| } else { |
| handlers[response.id].success_cb(response.result, this); |
| handlers[response.id].success_cb(response.result, this); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| |
| if (typeof all_done_cb === 'function') all_done_cb(result); |
| if (typeof all_done_cb === 'function') all_done_cb(result); |
| }; |
| }; |
| |
| |
| })(jQuery); |
| })(jQuery); |
| |
| |