verto-1.6.14 - jquery.jsonrpcclient.js Our Modifications - jquery.jsonrpcclient.js
/* /*
* 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);