www: speed control
authorErik Andresen <erik@vontaene.de>
Thu, 2 Apr 2020 05:19:40 +0000 (07:19 +0200)
committerErik Andresen <erik@vontaene.de>
Thu, 2 Apr 2020 05:19:40 +0000 (07:19 +0200)
www/assets/javascripts/application.js
www/assets/javascripts/roslib.js
www/assets/stylesheets/images/ui-icons_444444_256x240.png [new file with mode: 0644]
www/assets/stylesheets/images/ui-icons_555555_256x240.png [new file with mode: 0644]
www/assets/stylesheets/images/ui-icons_777620_256x240.png [new file with mode: 0644]
www/assets/stylesheets/images/ui-icons_777777_256x240.png [new file with mode: 0644]
www/assets/stylesheets/images/ui-icons_cc0000_256x240.png [new file with mode: 0644]
www/assets/stylesheets/images/ui-icons_ffffff_256x240.png [new file with mode: 0644]
www/index.html

index a7b4785..2fc4176 100644 (file)
@@ -189,12 +189,12 @@ function init() {
                                Y = e.pageY;
                        }
                        // relative click position
-                       var Xrel = X - this.offsetLeft - $(this).width()/2; 
-                       var Yrel = Y - this.offsetTop - $(this).height()/2; 
+                       var Xrel = X - this.getBoundingClientRect().left - $(this).width()/2;
+                       var Yrel = Y - this.getBoundingClientRect().top - $(this).height()/2;
                        // scale to -1..+1
                        var trans = -Yrel / ($(this).height()/2);
                        var rot = -Xrel / ($(this).width()/2);
-                       setSpeed(trans, rot*3);
+                       setSpeed(trans*$("#scale_trans").val(), rot*$("#scale_rot").val());
                }
        });
 
@@ -238,6 +238,8 @@ function init() {
                // reload
                $("img").attr("src", $("img").attr("src"))
        });
+
+       $("input[type='number']").spinner();
 }
 
 Vue.component('input-value', {
index 2193da8..fccc080 100644 (file)
@@ -1,4 +1,4 @@
-(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){\r
 /*!\r
  * EventEmitter2\r
  * https://github.com/hij1nx/EventEmitter2\r
     window.EventEmitter2 = EventEmitter;\r
   }\r
 }();\r
-
-},{}],2:[function(require,module,exports){
-/*
-object-assign
-(c) Sindre Sorhus
-@license MIT
-*/
-
-'use strict';
-/* eslint-disable no-unused-vars */
-var getOwnPropertySymbols = Object.getOwnPropertySymbols;
-var hasOwnProperty = Object.prototype.hasOwnProperty;
-var propIsEnumerable = Object.prototype.propertyIsEnumerable;
-
-function toObject(val) {
-       if (val === null || val === undefined) {
-               throw new TypeError('Object.assign cannot be called with null or undefined');
-       }
-
-       return Object(val);
-}
-
-function shouldUseNative() {
-       try {
-               if (!Object.assign) {
-                       return false;
-               }
-
-               // Detect buggy property enumeration order in older V8 versions.
-
-               // https://bugs.chromium.org/p/v8/issues/detail?id=4118
-               var test1 = new String('abc');  // eslint-disable-line no-new-wrappers
-               test1[5] = 'de';
-               if (Object.getOwnPropertyNames(test1)[0] === '5') {
-                       return false;
-               }
-
-               // https://bugs.chromium.org/p/v8/issues/detail?id=3056
-               var test2 = {};
-               for (var i = 0; i < 10; i++) {
-                       test2['_' + String.fromCharCode(i)] = i;
-               }
-               var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
-                       return test2[n];
-               });
-               if (order2.join('') !== '0123456789') {
-                       return false;
-               }
-
-               // https://bugs.chromium.org/p/v8/issues/detail?id=3056
-               var test3 = {};
-               'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
-                       test3[letter] = letter;
-               });
-               if (Object.keys(Object.assign({}, test3)).join('') !==
-                               'abcdefghijklmnopqrst') {
-                       return false;
-               }
-
-               return true;
-       } catch (err) {
-               // We don't expect any of the above to throw, but better to be safe.
-               return false;
-       }
-}
-
-module.exports = shouldUseNative() ? Object.assign : function (target, source) {
-       var from;
-       var to = toObject(target);
-       var symbols;
-
-       for (var s = 1; s < arguments.length; s++) {
-               from = Object(arguments[s]);
-
-               for (var key in from) {
-                       if (hasOwnProperty.call(from, key)) {
-                               to[key] = from[key];
-                       }
-               }
-
-               if (getOwnPropertySymbols) {
-                       symbols = getOwnPropertySymbols(from);
-                       for (var i = 0; i < symbols.length; i++) {
-                               if (propIsEnumerable.call(from, symbols[i])) {
-                                       to[symbols[i]] = from[symbols[i]];
-                               }
-                       }
-               }
-       }
-
-       return to;
-};
-
-},{}],3:[function(require,module,exports){
-/**
- * @fileOverview
- * @author Russell Toris - rctoris@wpi.edu
- */
-
-/**
- * If you use roslib in a browser, all the classes will be exported to a global variable called ROSLIB.
- *
- * If you use nodejs, this is the variable you get when you require('roslib')
- */
-var ROSLIB = this.ROSLIB || {
-  REVISION : '0.20.0'
-};
-
-var assign = require('object-assign');
-
-// Add core components
-assign(ROSLIB, require('./core'));
-
-assign(ROSLIB, require('./actionlib'));
-
-assign(ROSLIB, require('./math'));
-
-assign(ROSLIB, require('./tf'));
-
-assign(ROSLIB, require('./urdf'));
-
-module.exports = ROSLIB;
-
-},{"./actionlib":9,"./core":18,"./math":23,"./tf":26,"./urdf":38,"object-assign":2}],4:[function(require,module,exports){
-(function (global){
-global.ROSLIB = require('./RosLib');
-}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
-},{"./RosLib":3}],5:[function(require,module,exports){
-/**
- * @fileOverview
- * @author Russell Toris - rctoris@wpi.edu
- */
-
-var Topic = require('../core/Topic');
-var Message = require('../core/Message');
-var EventEmitter2 = require('eventemitter2').EventEmitter2;
-
-/**
- * An actionlib action client.
- *
- * Emits the following events:
- *  * 'timeout' - if a timeout occurred while sending a goal
- *  * 'status' - the status messages received from the action server
- *  * 'feedback' -  the feedback messages received from the action server
- *  * 'result' - the result returned from the action server
- *
- *  @constructor
- *  @param options - object with following keys:
- *   * ros - the ROSLIB.Ros connection handle
- *   * serverName - the action server name, like /fibonacci
- *   * actionName - the action message name, like 'actionlib_tutorials/FibonacciAction'
- *   * timeout - the timeout length when connecting to the action server
- */
-function ActionClient(options) {
-  var that = this;
-  options = options || {};
-  this.ros = options.ros;
-  this.serverName = options.serverName;
-  this.actionName = options.actionName;
-  this.timeout = options.timeout;
-  this.omitFeedback = options.omitFeedback;
-  this.omitStatus = options.omitStatus;
-  this.omitResult = options.omitResult;
-  this.goals = {};
-
-  // flag to check if a status has been received
-  var receivedStatus = false;
-
-  // create the topics associated with actionlib
-  this.feedbackListener = new Topic({
-    ros : this.ros,
-    name : this.serverName + '/feedback',
-    messageType : this.actionName + 'Feedback'
-  });
-
-  this.statusListener = new Topic({
-    ros : this.ros,
-    name : this.serverName + '/status',
-    messageType : 'actionlib_msgs/GoalStatusArray'
-  });
-
-  this.resultListener = new Topic({
-    ros : this.ros,
-    name : this.serverName + '/result',
-    messageType : this.actionName + 'Result'
-  });
-
-  this.goalTopic = new Topic({
-    ros : this.ros,
-    name : this.serverName + '/goal',
-    messageType : this.actionName + 'Goal'
-  });
-
-  this.cancelTopic = new Topic({
-    ros : this.ros,
-    name : this.serverName + '/cancel',
-    messageType : 'actionlib_msgs/GoalID'
-  });
-
-  // advertise the goal and cancel topics
-  this.goalTopic.advertise();
-  this.cancelTopic.advertise();
-
-  // subscribe to the status topic
-  if (!this.omitStatus) {
-    this.statusListener.subscribe(function(statusMessage) {
-      receivedStatus = true;
-      statusMessage.status_list.forEach(function(status) {
-        var goal = that.goals[status.goal_id.id];
-        if (goal) {
-          goal.emit('status', status);
-        }
-      });
-    });
-  }
-
-  // subscribe the the feedback topic
-  if (!this.omitFeedback) {
-    this.feedbackListener.subscribe(function(feedbackMessage) {
-      var goal = that.goals[feedbackMessage.status.goal_id.id];
-      if (goal) {
-        goal.emit('status', feedbackMessage.status);
-        goal.emit('feedback', feedbackMessage.feedback);
-      }
-    });
-  }
-
-  // subscribe to the result topic
-  if (!this.omitResult) {
-    this.resultListener.subscribe(function(resultMessage) {
-      var goal = that.goals[resultMessage.status.goal_id.id];
-
-      if (goal) {
-        goal.emit('status', resultMessage.status);
-        goal.emit('result', resultMessage.result);
-      }
-    });
-  }
-
-  // If timeout specified, emit a 'timeout' event if the action server does not respond
-  if (this.timeout) {
-    setTimeout(function() {
-      if (!receivedStatus) {
-        that.emit('timeout');
-      }
-    }, this.timeout);
-  }
-}
-
-ActionClient.prototype.__proto__ = EventEmitter2.prototype;
-
-/**
- * Cancel all goals associated with this ActionClient.
- */
-ActionClient.prototype.cancel = function() {
-  var cancelMessage = new Message();
-  this.cancelTopic.publish(cancelMessage);
-};
-
-/**
- * Unsubscribe and unadvertise all topics associated with this ActionClient.
- */
-ActionClient.prototype.dispose = function() {
-  this.goalTopic.unadvertise();
-  this.cancelTopic.unadvertise();
-  if (!this.omitStatus) {this.statusListener.unsubscribe();}
-  if (!this.omitFeedback) {this.feedbackListener.unsubscribe();}
-  if (!this.omitResult) {this.resultListener.unsubscribe();}
-};
-
-module.exports = ActionClient;
-
-},{"../core/Message":10,"../core/Topic":17,"eventemitter2":1}],6:[function(require,module,exports){
-/**
- * @fileOverview
- * @author Justin Young - justin@oodar.com.au
- * @author Russell Toris - rctoris@wpi.edu
- */
-
-var Topic = require('../core/Topic');
-var Message = require('../core/Message');
-var EventEmitter2 = require('eventemitter2').EventEmitter2;
-
-/**
- * An actionlib action listener
- *
- * Emits the following events:
- *  * 'status' - the status messages received from the action server
- *  * 'feedback' -  the feedback messages received from the action server
- *  * 'result' - the result returned from the action server
- *
- *  @constructor
- *  @param options - object with following keys:
- *   * ros - the ROSLIB.Ros connection handle
- *   * serverName - the action server name, like /fibonacci
- *   * actionName - the action message name, like 'actionlib_tutorials/FibonacciAction'
- */
-function ActionListener(options) {
-  var that = this;
-  options = options || {};
-  this.ros = options.ros;
-  this.serverName = options.serverName;
-  this.actionName = options.actionName;
-  this.timeout = options.timeout;
-  this.omitFeedback = options.omitFeedback;
-  this.omitStatus = options.omitStatus;
-  this.omitResult = options.omitResult;
-
-
-  // create the topics associated with actionlib
-  var goalListener = new Topic({
-    ros : this.ros,
-    name : this.serverName + '/goal',
-    messageType : this.actionName + 'Goal'
-  });
-
-  var feedbackListener = new Topic({
-    ros : this.ros,
-    name : this.serverName + '/feedback',
-    messageType : this.actionName + 'Feedback'
-  });
-
-  var statusListener = new Topic({
-    ros : this.ros,
-    name : this.serverName + '/status',
-    messageType : 'actionlib_msgs/GoalStatusArray'
-  });
-
-  var resultListener = new Topic({
-    ros : this.ros,
-    name : this.serverName + '/result',
-    messageType : this.actionName + 'Result'
-  });
-
-  goalListener.subscribe(function(goalMessage) {
-      that.emit('goal', goalMessage);
-  });
-
-  statusListener.subscribe(function(statusMessage) {
-      statusMessage.status_list.forEach(function(status) {
-          that.emit('status', status);
-      });
-  });
-
-  feedbackListener.subscribe(function(feedbackMessage) {
-      that.emit('status', feedbackMessage.status);
-      that.emit('feedback', feedbackMessage.feedback);
-  });
-
-  // subscribe to the result topic
-  resultListener.subscribe(function(resultMessage) {
-      that.emit('status', resultMessage.status);
-      that.emit('result', resultMessage.result);
-  });
-
-}
-
-ActionListener.prototype.__proto__ = EventEmitter2.prototype;
-
-module.exports = ActionListener;
-
-},{"../core/Message":10,"../core/Topic":17,"eventemitter2":1}],7:[function(require,module,exports){
-/**
- * @fileOverview
- * @author Russell Toris - rctoris@wpi.edu
- */
-
-var Message = require('../core/Message');
-var EventEmitter2 = require('eventemitter2').EventEmitter2;
-
-/**
- * An actionlib goal goal is associated with an action server.
- *
- * Emits the following events:
- *  * 'timeout' - if a timeout occurred while sending a goal
- *
- *  @constructor
- *  @param object with following keys:
- *   * actionClient - the ROSLIB.ActionClient to use with this goal
- *   * goalMessage - The JSON object containing the goal for the action server
- */
-function Goal(options) {
-  var that = this;
-  this.actionClient = options.actionClient;
-  this.goalMessage = options.goalMessage;
-  this.isFinished = false;
-
-  // Used to create random IDs
-  var date = new Date();
-
-  // Create a random ID
-  this.goalID = 'goal_' + Math.random() + '_' + date.getTime();
-  // Fill in the goal message
-  this.goalMessage = new Message({
-    goal_id : {
-      stamp : {
-        secs : 0,
-        nsecs : 0
-      },
-      id : this.goalID
-    },
-    goal : this.goalMessage
-  });
-
-  this.on('status', function(status) {
-    that.status = status;
-  });
-
-  this.on('result', function(result) {
-    that.isFinished = true;
-    that.result = result;
-  });
-
-  this.on('feedback', function(feedback) {
-    that.feedback = feedback;
-  });
-
-  // Add the goal
-  this.actionClient.goals[this.goalID] = this;
-}
-
-Goal.prototype.__proto__ = EventEmitter2.prototype;
-
-/**
- * Send the goal to the action server.
- *
- * @param timeout (optional) - a timeout length for the goal's result
- */
-Goal.prototype.send = function(timeout) {
-  var that = this;
-  that.actionClient.goalTopic.publish(that.goalMessage);
-  if (timeout) {
-    setTimeout(function() {
-      if (!that.isFinished) {
-        that.emit('timeout');
-      }
-    }, timeout);
-  }
-};
-
-/**
- * Cancel the current goal.
- */
-Goal.prototype.cancel = function() {
-  var cancelMessage = new Message({
-    id : this.goalID
-  });
-  this.actionClient.cancelTopic.publish(cancelMessage);
-};
-
-module.exports = Goal;
-},{"../core/Message":10,"eventemitter2":1}],8:[function(require,module,exports){
-/**
- * @fileOverview
- * @author Laura Lindzey - lindzey@gmail.com
- */
-
-var Topic = require('../core/Topic');
-var Message = require('../core/Message');
-var EventEmitter2 = require('eventemitter2').EventEmitter2;
-
-/**
- * An actionlib action server client.
- *
- * Emits the following events:
- *  * 'goal' - goal sent by action client
- *  * 'cancel' - action client has canceled the request
- *
- *  @constructor
- *  @param options - object with following keys:
- *   * ros - the ROSLIB.Ros connection handle
- *   * serverName - the action server name, like /fibonacci
- *   * actionName - the action message name, like 'actionlib_tutorials/FibonacciAction'
- */
-
-function SimpleActionServer(options) {
-    var that = this;
-    options = options || {};
-    this.ros = options.ros;
-    this.serverName = options.serverName;
-    this.actionName = options.actionName;
-
-    // create and advertise publishers
-    this.feedbackPublisher = new Topic({
-        ros : this.ros,
-        name : this.serverName + '/feedback',
-        messageType : this.actionName + 'Feedback'
-    });
-    this.feedbackPublisher.advertise();
-
-    var statusPublisher = new Topic({
-        ros : this.ros,
-        name : this.serverName + '/status',
-        messageType : 'actionlib_msgs/GoalStatusArray'
-    });
-    statusPublisher.advertise();
-
-    this.resultPublisher = new Topic({
-        ros : this.ros,
-        name : this.serverName + '/result',
-        messageType : this.actionName + 'Result'
-    });
-    this.resultPublisher.advertise();
-
-    // create and subscribe to listeners
-    var goalListener = new Topic({
-        ros : this.ros,
-        name : this.serverName + '/goal',
-        messageType : this.actionName + 'Goal'
-    });
-
-    var cancelListener = new Topic({
-        ros : this.ros,
-        name : this.serverName + '/cancel',
-        messageType : 'actionlib_msgs/GoalID'
-    });
-
-    // Track the goals and their status in order to publish status...
-    this.statusMessage = new Message({
-        header : {
-            stamp : {secs : 0, nsecs : 100},
-            frame_id : ''
-        },
-        status_list : []
-    });
-
-    // needed for handling preemption prompted by a new goal being received
-    this.currentGoal = null; // currently tracked goal
-    this.nextGoal = null; // the one that'll be preempting
-
-    goalListener.subscribe(function(goalMessage) {
-        
-    if(that.currentGoal) {
-            that.nextGoal = goalMessage;
-            // needs to happen AFTER rest is set up
-            that.emit('cancel');
-    } else {
-            that.statusMessage.status_list = [{goal_id : goalMessage.goal_id, status : 1}];
-            that.currentGoal = goalMessage;
-            that.emit('goal', goalMessage.goal);
-    }
-    });
-
-    // helper function for determing ordering of timestamps
-    // returns t1 < t2
-    var isEarlier = function(t1, t2) {
-        if(t1.secs > t2.secs) {
-            return false;
-        } else if(t1.secs < t2.secs) {
-            return true;
-        } else if(t1.nsecs < t2.nsecs) {
-            return true;
-        } else {
-            return false;
-        }
-    };
-
-    // TODO: this may be more complicated than necessary, since I'm
-    // not sure if the callbacks can ever wind up with a scenario
-    // where we've been preempted by a next goal, it hasn't finished
-    // processing, and then we get a cancel message
-    cancelListener.subscribe(function(cancelMessage) {
-
-        // cancel ALL goals if both empty
-        if(cancelMessage.stamp.secs === 0 && cancelMessage.stamp.secs === 0 && cancelMessage.id === '') {
-            that.nextGoal = null;
-            if(that.currentGoal) {
-                that.emit('cancel');
-            }
-        } else { // treat id and stamp independently
-            if(that.currentGoal && cancelMessage.id === that.currentGoal.goal_id.id) {
-                that.emit('cancel');
-            } else if(that.nextGoal && cancelMessage.id === that.nextGoal.goal_id.id) {
-                that.nextGoal = null;
-            }
-
-            if(that.nextGoal && isEarlier(that.nextGoal.goal_id.stamp,
-                                          cancelMessage.stamp)) {
-                that.nextGoal = null;
-            }
-            if(that.currentGoal && isEarlier(that.currentGoal.goal_id.stamp,
-                                             cancelMessage.stamp)) {
-                
-                that.emit('cancel');
-            }
-        }
-    });
-
-    // publish status at pseudo-fixed rate; required for clients to know they've connected
-    var statusInterval = setInterval( function() {
-        var currentTime = new Date();
-        var secs = Math.floor(currentTime.getTime()/1000);
-        var nsecs = Math.round(1000000000*(currentTime.getTime()/1000-secs));
-        that.statusMessage.header.stamp.secs = secs;
-        that.statusMessage.header.stamp.nsecs = nsecs;
-        statusPublisher.publish(that.statusMessage);
-    }, 500); // publish every 500ms
-
-}
-
-SimpleActionServer.prototype.__proto__ = EventEmitter2.prototype;
-
-/**
-*  Set action state to succeeded and return to client
-*/
-
-SimpleActionServer.prototype.setSucceeded = function(result2) {
-    
-
-    var resultMessage = new Message({
-        status : {goal_id : this.currentGoal.goal_id, status : 3},
-        result : result2
-    });
-    this.resultPublisher.publish(resultMessage);
-
-    this.statusMessage.status_list = [];
-    if(this.nextGoal) {
-        this.currentGoal = this.nextGoal;
-        this.nextGoal = null;
-        this.emit('goal', this.currentGoal.goal);
-    } else {
-        this.currentGoal = null;
-    }
-};
-
-/**
-*  Function to send feedback
-*/
-
-SimpleActionServer.prototype.sendFeedback = function(feedback2) {
-
-    var feedbackMessage = new Message({
-        status : {goal_id : this.currentGoal.goal_id, status : 1},
-        feedback : feedback2
-    });
-    this.feedbackPublisher.publish(feedbackMessage);
-};
-
-/**
-*  Handle case where client requests preemption
-*/
-
-SimpleActionServer.prototype.setPreempted = function() {
-
-    this.statusMessage.status_list = [];
-    var resultMessage = new Message({
-        status : {goal_id : this.currentGoal.goal_id, status : 2},
-    });
-    this.resultPublisher.publish(resultMessage);
-
-    if(this.nextGoal) {
-        this.currentGoal = this.nextGoal;
-        this.nextGoal = null;
-        this.emit('goal', this.currentGoal.goal);
-    } else {
-        this.currentGoal = null;
-    }
-};
-
-module.exports = SimpleActionServer;
-},{"../core/Message":10,"../core/Topic":17,"eventemitter2":1}],9:[function(require,module,exports){
-var Ros = require('../core/Ros');
-var mixin = require('../mixin');
-
-var action = module.exports = {
-    ActionClient: require('./ActionClient'),
-    ActionListener: require('./ActionListener'),
-    Goal: require('./Goal'),
-    SimpleActionServer: require('./SimpleActionServer')
-};
-
-mixin(Ros, ['ActionClient', 'SimpleActionServer'], action);
-
-},{"../core/Ros":12,"../mixin":24,"./ActionClient":5,"./ActionListener":6,"./Goal":7,"./SimpleActionServer":8}],10:[function(require,module,exports){
-/**
- * @fileoverview
- * @author Brandon Alexander - baalexander@gmail.com
- */
-
-var assign = require('object-assign');
-
-/**
- * Message objects are used for publishing and subscribing to and from topics.
- *
- * @constructor
- * @param values - object matching the fields defined in the .msg definition file
- */
-function Message(values) {
-  assign(this, values);
-}
-
-module.exports = Message;
-},{"object-assign":2}],11:[function(require,module,exports){
-/**
- * @fileoverview
- * @author Brandon Alexander - baalexander@gmail.com
- */
-
-var Service = require('./Service');
-var ServiceRequest = require('./ServiceRequest');
-
-/**
- * A ROS parameter.
- *
- * @constructor
- * @param options - possible keys include:
- *   * ros - the ROSLIB.Ros connection handle
- *   * name - the param name, like max_vel_x
- */
-function Param(options) {
-  options = options || {};
-  this.ros = options.ros;
-  this.name = options.name;
-}
-
-/**
- * Fetches the value of the param.
- *
- * @param callback - function with the following params:
- *  * value - the value of the param from ROS.
- */
-Param.prototype.get = function(callback) {
-  var paramClient = new Service({
-    ros : this.ros,
-    name : '/rosapi/get_param',
-    serviceType : 'rosapi/GetParam'
-  });
-
-  var request = new ServiceRequest({
-    name : this.name
-  });
-
-  paramClient.callService(request, function(result) {
-    var value = JSON.parse(result.value);
-    callback(value);
-  });
-};
-
-/**
- * Sets the value of the param in ROS.
- *
- * @param value - value to set param to.
- */
-Param.prototype.set = function(value, callback) {
-  var paramClient = new Service({
-    ros : this.ros,
-    name : '/rosapi/set_param',
-    serviceType : 'rosapi/SetParam'
-  });
-
-  var request = new ServiceRequest({
-    name : this.name,
-    value : JSON.stringify(value)
-  });
-
-  paramClient.callService(request, callback);
-};
-
-/**
- * Delete this parameter on the ROS server.
- */
-Param.prototype.delete = function(callback) {
-  var paramClient = new Service({
-    ros : this.ros,
-    name : '/rosapi/delete_param',
-    serviceType : 'rosapi/DeleteParam'
-  });
-
-  var request = new ServiceRequest({
-    name : this.name
-  });
-
-  paramClient.callService(request, callback);
-};
-
-module.exports = Param;
-},{"./Service":13,"./ServiceRequest":14}],12:[function(require,module,exports){
-/**
- * @fileoverview
- * @author Brandon Alexander - baalexander@gmail.com
- */
-
-var WebSocket = require('ws');
-var socketAdapter = require('./SocketAdapter.js');
-
-var Service = require('./Service');
-var ServiceRequest = require('./ServiceRequest');
-
-var assign = require('object-assign');
-var EventEmitter2 = require('eventemitter2').EventEmitter2;
-
-/**
- * Manages connection to the server and all interactions with ROS.
- *
- * Emits the following events:
- *  * 'error' - there was an error with ROS
- *  * 'connection' - connected to the WebSocket server
- *  * 'close' - disconnected to the WebSocket server
- *  * <topicName> - a message came from rosbridge with the given topic name
- *  * <serviceID> - a service response came from rosbridge with the given ID
- *
- * @constructor
- * @param options - possible keys include: <br>
- *   * url (optional) - (can be specified later with `connect`) the WebSocket URL for rosbridge or the node server url to connect using socket.io (if socket.io exists in the page) <br>
- *   * groovyCompatibility - don't use interfaces that changed after the last groovy release or rosbridge_suite and related tools (defaults to true)
- *   * transportLibrary (optional) - one of 'websocket' (default), 'socket.io' or RTCPeerConnection instance controlling how the connection is created in `connect`.
- *   * transportOptions (optional) - the options to use use when creating a connection. Currently only used if `transportLibrary` is RTCPeerConnection.
- */
-function Ros(options) {
-  options = options || {};
-  this.socket = null;
-  this.idCounter = 0;
-  this.isConnected = false;
-  this.transportLibrary = options.transportLibrary || 'websocket';
-  this.transportOptions = options.transportOptions || {};
-
-  if (typeof options.groovyCompatibility === 'undefined') {
-    this.groovyCompatibility = true;
-  }
-  else {
-    this.groovyCompatibility = options.groovyCompatibility;
-  }
-
-  // Sets unlimited event listeners.
-  this.setMaxListeners(0);
-
-  // begin by checking if a URL was given
-  if (options.url) {
-    this.connect(options.url);
-  }
-}
-
-Ros.prototype.__proto__ = EventEmitter2.prototype;
-
-/**
- * Connect to the specified WebSocket.
- *
- * @param url - WebSocket URL or RTCDataChannel label for Rosbridge
- */
-Ros.prototype.connect = function(url) {
-  if (this.transportLibrary === 'socket.io') {
-    this.socket = assign(io(url, {'force new connection': true}), socketAdapter(this));
-    this.socket.on('connect', this.socket.onopen);
-    this.socket.on('data', this.socket.onmessage);
-    this.socket.on('close', this.socket.onclose);
-    this.socket.on('error', this.socket.onerror);
-  } else if (this.transportLibrary.constructor.name === 'RTCPeerConnection') {
-    this.socket = assign(this.transportLibrary.createDataChannel(url, this.transportOptions), socketAdapter(this));
-  }else {
-    this.socket = assign(new WebSocket(url), socketAdapter(this));
-  }
-
-};
-
-/**
- * Disconnect from the WebSocket server.
- */
-Ros.prototype.close = function() {
-  if (this.socket) {
-    this.socket.close();
-  }
-};
-
-/**
- * Sends an authorization request to the server.
- *
- * @param mac - MAC (hash) string given by the trusted source.
- * @param client - IP of the client.
- * @param dest - IP of the destination.
- * @param rand - Random string given by the trusted source.
- * @param t - Time of the authorization request.
- * @param level - User level as a string given by the client.
- * @param end - End time of the client's session.
- */
-Ros.prototype.authenticate = function(mac, client, dest, rand, t, level, end) {
-  // create the request
-  var auth = {
-    op : 'auth',
-    mac : mac,
-    client : client,
-    dest : dest,
-    rand : rand,
-    t : t,
-    level : level,
-    end : end
-  };
-  // send the request
-  this.callOnConnection(auth);
-};
-
-/**
- * Sends the message over the WebSocket, but queues the message up if not yet
- * connected.
- */
-Ros.prototype.callOnConnection = function(message) {
-  var that = this;
-  var messageJson = JSON.stringify(message);
-  var emitter = null;
-  if (this.transportLibrary === 'socket.io') {
-    emitter = function(msg){that.socket.emit('operation', msg);};
-  } else {
-    emitter = function(msg){that.socket.send(msg);};
-  }
-
-  if (!this.isConnected) {
-    that.once('connection', function() {
-      emitter(messageJson);
-    });
-  } else {
-    emitter(messageJson);
-  }
-};
-
-/**
- * Sends a set_level request to the server
- *
- * @param level - Status level (none, error, warning, info)
- * @param id - Optional: Operation ID to change status level on
- */
-Ros.prototype.setStatusLevel = function(level, id){
-  var levelMsg = {
-    op: 'set_level',
-    level: level,
-    id: id
-  };
-
-  this.callOnConnection(levelMsg);
-};
-
-/**
- * Retrieves Action Servers in ROS as an array of string
- *
- *   * actionservers - Array of action server names
- */
-Ros.prototype.getActionServers = function(callback, failedCallback) {
-  var getActionServers = new Service({
-    ros : this,
-    name : '/rosapi/action_servers',
-    serviceType : 'rosapi/GetActionServers'
-  });
-
-  var request = new ServiceRequest({});
-  if (typeof failedCallback === 'function'){
-    getActionServers.callService(request,
-      function(result) {
-        callback(result.action_servers);
-      },
-      function(message){
-        failedCallback(message);
-      }
-    );
-  }else{
-    getActionServers.callService(request, function(result) {
-      callback(result.action_servers);
-    });
-  }
-};
-
-/**
- * Retrieves list of topics in ROS as an array.
- *
- * @param callback function with params:
- *   * topics - Array of topic names
- */
-Ros.prototype.getTopics = function(callback, failedCallback) {
-  var topicsClient = new Service({
-    ros : this,
-    name : '/rosapi/topics',
-    serviceType : 'rosapi/Topics'
-  });
-
-  var request = new ServiceRequest();
-  if (typeof failedCallback === 'function'){
-    topicsClient.callService(request,
-      function(result) {
-        callback(result);
-      },
-      function(message){
-        failedCallback(message);
-      }
-    );
-  }else{
-    topicsClient.callService(request, function(result) {
-      callback(result);
-    });
-  }
-};
-
-/**
- * Retrieves Topics in ROS as an array as specific type
- *
- * @param topicType topic type to find:
- * @param callback function with params:
- *   * topics - Array of topic names
- */
-Ros.prototype.getTopicsForType = function(topicType, callback, failedCallback) {
-  var topicsForTypeClient = new Service({
-    ros : this,
-    name : '/rosapi/topics_for_type',
-    serviceType : 'rosapi/TopicsForType'
-  });
-
-  var request = new ServiceRequest({
-    type: topicType
-  });
-  if (typeof failedCallback === 'function'){
-    topicsForTypeClient.callService(request,
-      function(result) {
-        callback(result.topics);
-      },
-      function(message){
-        failedCallback(message);
-      }
-    );
-  }else{
-    topicsForTypeClient.callService(request, function(result) {
-      callback(result.topics);
-    });
-  }
-};
-
-/**
- * Retrieves list of active service names in ROS.
- *
- * @param callback - function with the following params:
- *   * services - array of service names
- */
-Ros.prototype.getServices = function(callback, failedCallback) {
-  var servicesClient = new Service({
-    ros : this,
-    name : '/rosapi/services',
-    serviceType : 'rosapi/Services'
-  });
-
-  var request = new ServiceRequest();
-  if (typeof failedCallback === 'function'){
-    servicesClient.callService(request,
-      function(result) {
-        callback(result.services);
-      },
-      function(message) {
-        failedCallback(message);
-      }
-    );
-  }else{
-    servicesClient.callService(request, function(result) {
-      callback(result.services);
-    });
-  }
-};
-
-/**
- * Retrieves list of services in ROS as an array as specific type
- *
- * @param serviceType service type to find:
- * @param callback function with params:
- *   * topics - Array of service names
- */
-Ros.prototype.getServicesForType = function(serviceType, callback, failedCallback) {
-  var servicesForTypeClient = new Service({
-    ros : this,
-    name : '/rosapi/services_for_type',
-    serviceType : 'rosapi/ServicesForType'
-  });
-
-  var request = new ServiceRequest({
-    type: serviceType
-  });
-  if (typeof failedCallback === 'function'){
-    servicesForTypeClient.callService(request,
-      function(result) {
-        callback(result.services);
-      },
-      function(message) {
-        failedCallback(message);
-      }
-    );
-  }else{
-    servicesForTypeClient.callService(request, function(result) {
-      callback(result.services);
-    });
-  }
-};
-
-/**
- * Retrieves a detail of ROS service request.
- *
- * @param service name of service:
- * @param callback - function with params:
- *   * type - String of the service type
- */
-Ros.prototype.getServiceRequestDetails = function(type, callback, failedCallback) {
-  var serviceTypeClient = new Service({
-    ros : this,
-    name : '/rosapi/service_request_details',
-    serviceType : 'rosapi/ServiceRequestDetails'
-  });
-  var request = new ServiceRequest({
-    type: type
-  });
-
-  if (typeof failedCallback === 'function'){
-    serviceTypeClient.callService(request,
-      function(result) {
-        callback(result);
-      },
-      function(message){
-        failedCallback(message);
-      }
-    );
-  }else{
-    serviceTypeClient.callService(request, function(result) {
-      callback(result);
-    });
-  }
-};
-
-/**
- * Retrieves a detail of ROS service request.
- *
- * @param service name of service:
- * @param callback - function with params:
- *   * type - String of the service type
- */
-Ros.prototype.getServiceResponseDetails = function(type, callback, failedCallback) {
-  var serviceTypeClient = new Service({
-    ros : this,
-    name : '/rosapi/service_response_details',
-    serviceType : 'rosapi/ServiceResponseDetails'
-  });
-  var request = new ServiceRequest({
-    type: type
-  });
-
-  if (typeof failedCallback === 'function'){
-    serviceTypeClient.callService(request,
-      function(result) {
-        callback(result);
-      },
-      function(message){
-        failedCallback(message);
-      }
-    );
-  }else{
-    serviceTypeClient.callService(request, function(result) {
-      callback(result);
-    });
-  }
-};
-
-/**
- * Retrieves list of active node names in ROS.
- *
- * @param callback - function with the following params:
- *   * nodes - array of node names
- */
-Ros.prototype.getNodes = function(callback, failedCallback) {
-  var nodesClient = new Service({
-    ros : this,
-    name : '/rosapi/nodes',
-    serviceType : 'rosapi/Nodes'
-  });
-
-  var request = new ServiceRequest();
-  if (typeof failedCallback === 'function'){
-    nodesClient.callService(request,
-      function(result) {
-        callback(result.nodes);
-      },
-      function(message) {
-        failedCallback(message);
-      }
-    );
-  }else{
-    nodesClient.callService(request, function(result) {
-      callback(result.nodes);
-    });
-  }
-};
-
-/**
-  * Retrieves list subscribed topics, publishing topics and services of a specific node
-  *
-  * @param node name of the node:
-  * @param callback - function with params:
-  *   * publications - array of published topic names
-  *   * subscriptions - array of subscribed topic names
-  *   * services - array of service names hosted
-  */
-Ros.prototype.getNodeDetails = function(node, callback, failedCallback) {
-  var nodesClient = new Service({
-    ros : this,
-    name : '/rosapi/node_details',
-    serviceType : 'rosapi/NodeDetails'
-  });
-
-  var request = new ServiceRequest({
-    node: node
-  });
-  if (typeof failedCallback === 'function'){
-    nodesClient.callService(request,
-      function(result) {
-        callback(result.subscribing, result.publishing, result.services);
-      },
-      function(message) {
-        failedCallback(message);
-      }
-    );
-  } else {
-    nodesClient.callService(request, function(result) {
-      callback(result);
-    });
-  }
-};
-
-/**
- * Retrieves list of param names from the ROS Parameter Server.
- *
- * @param callback function with params:
- *  * params - array of param names.
- */
-Ros.prototype.getParams = function(callback, failedCallback) {
-  var paramsClient = new Service({
-    ros : this,
-    name : '/rosapi/get_param_names',
-    serviceType : 'rosapi/GetParamNames'
-  });
-  var request = new ServiceRequest();
-  if (typeof failedCallback === 'function'){
-    paramsClient.callService(request,
-      function(result) {
-        callback(result.names);
-      },
-      function(message){
-        failedCallback(message);
-      }
-    );
-  }else{
-    paramsClient.callService(request, function(result) {
-      callback(result.names);
-    });
-  }
-};
-
-/**
- * Retrieves a type of ROS topic.
- *
- * @param topic name of the topic:
- * @param callback - function with params:
- *   * type - String of the topic type
- */
-Ros.prototype.getTopicType = function(topic, callback, failedCallback) {
-  var topicTypeClient = new Service({
-    ros : this,
-    name : '/rosapi/topic_type',
-    serviceType : 'rosapi/TopicType'
-  });
-  var request = new ServiceRequest({
-    topic: topic
-  });
-
-  if (typeof failedCallback === 'function'){
-    topicTypeClient.callService(request,
-      function(result) {
-        callback(result.type);
-      },
-      function(message){
-        failedCallback(message);
-      }
-    );
-  }else{
-    topicTypeClient.callService(request, function(result) {
-      callback(result.type);
-    });
-  }
-};
-
-/**
- * Retrieves a type of ROS service.
- *
- * @param service name of service:
- * @param callback - function with params:
- *   * type - String of the service type
- */
-Ros.prototype.getServiceType = function(service, callback, failedCallback) {
-  var serviceTypeClient = new Service({
-    ros : this,
-    name : '/rosapi/service_type',
-    serviceType : 'rosapi/ServiceType'
-  });
-  var request = new ServiceRequest({
-    service: service
-  });
-
-  if (typeof failedCallback === 'function'){
-    serviceTypeClient.callService(request,
-      function(result) {
-        callback(result.type);
-      },
-      function(message){
-        failedCallback(message);
-      }
-    );
-  }else{
-    serviceTypeClient.callService(request, function(result) {
-      callback(result.type);
-    });
-  }
-};
-
-/**
- * Retrieves a detail of ROS message.
- *
- * @param callback - function with params:
- *   * details - Array of the message detail
- * @param message - String of a topic type
- */
-Ros.prototype.getMessageDetails = function(message, callback, failedCallback) {
-  var messageDetailClient = new Service({
-    ros : this,
-    name : '/rosapi/message_details',
-    serviceType : 'rosapi/MessageDetails'
-  });
-  var request = new ServiceRequest({
-    type: message
-  });
-
-  if (typeof failedCallback === 'function'){
-    messageDetailClient.callService(request,
-      function(result) {
-        callback(result.typedefs);
-      },
-      function(message){
-        failedCallback(message);
-      }
-    );
-  }else{
-    messageDetailClient.callService(request, function(result) {
-      callback(result.typedefs);
-    });
-  }
-};
-
-/**
- * Decode a typedefs into a dictionary like `rosmsg show foo/bar`
- *
- * @param defs - array of type_def dictionary
- */
-Ros.prototype.decodeTypeDefs = function(defs) {
-  var that = this;
-
-  // calls itself recursively to resolve type definition using hints.
-  var decodeTypeDefsRec = function(theType, hints) {
-    var typeDefDict = {};
-    for (var i = 0; i < theType.fieldnames.length; i++) {
-      var arrayLen = theType.fieldarraylen[i];
-      var fieldName = theType.fieldnames[i];
-      var fieldType = theType.fieldtypes[i];
-      if (fieldType.indexOf('/') === -1) { // check the fieldType includes '/' or not
-        if (arrayLen === -1) {
-          typeDefDict[fieldName] = fieldType;
-        }
-        else {
-          typeDefDict[fieldName] = [fieldType];
-        }
-      }
-      else {
-        // lookup the name
-        var sub = false;
-        for (var j = 0; j < hints.length; j++) {
-          if (hints[j].type.toString() === fieldType.toString()) {
-            sub = hints[j];
-            break;
-          }
-        }
-        if (sub) {
-          var subResult = decodeTypeDefsRec(sub, hints);
-          if (arrayLen === -1) {
-          }
-          else {
-            typeDefDict[fieldName] = [subResult];
-          }
-        }
-        else {
-          that.emit('error', 'Cannot find ' + fieldType + ' in decodeTypeDefs');
-        }
-      }
-    }
-    return typeDefDict;
-  };
-
-  return decodeTypeDefsRec(defs[0], defs);
-};
-
-
-module.exports = Ros;
-
-},{"./Service":13,"./ServiceRequest":14,"./SocketAdapter.js":16,"eventemitter2":1,"object-assign":2,"ws":39}],13:[function(require,module,exports){
-/**
- * @fileoverview
- * @author Brandon Alexander - baalexander@gmail.com
- */
-
-var ServiceResponse = require('./ServiceResponse');
-var ServiceRequest = require('./ServiceRequest');
-var EventEmitter2 = require('eventemitter2').EventEmitter2;
-
-/**
- * A ROS service client.
- *
- * @constructor
- * @params options - possible keys include:
- *   * ros - the ROSLIB.Ros connection handle
- *   * name - the service name, like /add_two_ints
- *   * serviceType - the service type, like 'rospy_tutorials/AddTwoInts'
- */
-function Service(options) {
-  options = options || {};
-  this.ros = options.ros;
-  this.name = options.name;
-  this.serviceType = options.serviceType;
-  this.isAdvertised = false;
-
-  this._serviceCallback = null;
-}
-Service.prototype.__proto__ = EventEmitter2.prototype;
-/**
- * Calls the service. Returns the service response in the callback.
- *
- * @param request - the ROSLIB.ServiceRequest to send
- * @param callback - function with params:
- *   * response - the response from the service request
- * @param failedCallback - the callback function when the service call failed (optional). Params:
- *   * error - the error message reported by ROS
- */
-Service.prototype.callService = function(request, callback, failedCallback) {
-  if (this.isAdvertised) {
-    return;
-  }
-
-  var serviceCallId = 'call_service:' + this.name + ':' + (++this.ros.idCounter);
-
-  if (callback || failedCallback) {
-    this.ros.once(serviceCallId, function(message) {
-      if (message.result !== undefined && message.result === false) {
-        if (typeof failedCallback === 'function') {
-          failedCallback(message.values);
-        }
-      } else if (typeof callback === 'function') {
-        callback(new ServiceResponse(message.values));
-      }
-    });
-  }
-
-  var call = {
-    op : 'call_service',
-    id : serviceCallId,
-    service : this.name,
-    args : request
-  };
-  this.ros.callOnConnection(call);
-};
-
-/**
- * Every time a message is published for the given topic, the callback
- * will be called with the message object.
- *
- * @param callback - function with the following params:
- *   * message - the published message
- */
-Service.prototype.advertise = function(callback) {
-  if (this.isAdvertised || typeof callback !== 'function') {
-    return;
-  }
-
-  this._serviceCallback = callback;
-  this.ros.on(this.name, this._serviceResponse.bind(this));
-  this.ros.callOnConnection({
-    op: 'advertise_service',
-    type: this.serviceType,
-    service: this.name
-  });
-  this.isAdvertised = true;
-};
-
-Service.prototype.unadvertise = function() {
-  if (!this.isAdvertised) {
-    return;
-  }
-  this.ros.callOnConnection({
-    op: 'unadvertise_service',
-    service: this.name
-  });
-  this.isAdvertised = false;
-};
-
-Service.prototype._serviceResponse = function(rosbridgeRequest) {
-  var response = {};
-  var success = this._serviceCallback(rosbridgeRequest.args, response);
-
-  var call = {
-    op: 'service_response',
-    service: this.name,
-    values: new ServiceResponse(response),
-    result: success
-  };
-
-  if (rosbridgeRequest.id) {
-    call.id = rosbridgeRequest.id;
-  }
-
-  this.ros.callOnConnection(call);
-};
-
-module.exports = Service;
-},{"./ServiceRequest":14,"./ServiceResponse":15,"eventemitter2":1}],14:[function(require,module,exports){
-/**
- * @fileoverview
- * @author Brandon Alexander - balexander@willowgarage.com
- */
-
-var assign = require('object-assign');
-
-/**
- * A ServiceRequest is passed into the service call.
- *
- * @constructor
- * @param values - object matching the fields defined in the .srv definition file
- */
-function ServiceRequest(values) {
-  assign(this, values);
-}
-
-module.exports = ServiceRequest;
-},{"object-assign":2}],15:[function(require,module,exports){
-/**
- * @fileoverview
- * @author Brandon Alexander - balexander@willowgarage.com
- */
-
-var assign = require('object-assign');
-
-/**
- * A ServiceResponse is returned from the service call.
- *
- * @constructor
- * @param values - object matching the fields defined in the .srv definition file
- */
-function ServiceResponse(values) {
-  assign(this, values);
-}
-
-module.exports = ServiceResponse;
-},{"object-assign":2}],16:[function(require,module,exports){
-/**
- * Socket event handling utilities for handling events on either
- * WebSocket and TCP sockets
- *
- * Note to anyone reviewing this code: these functions are called
- * in the context of their parent object, unless bound
- * @fileOverview
- */
-'use strict';
-
-var decompressPng = require('../util/decompressPng');
-var WebSocket = require('ws');
-var BSON = null;
-if(typeof bson !== 'undefined'){
-    BSON = bson().BSON;
-}
-
-/**
- * Events listeners for a WebSocket or TCP socket to a JavaScript
- * ROS Client. Sets up Messages for a given topic to trigger an
- * event on the ROS client.
- *
- * @namespace SocketAdapter
- * @private
- */
-function SocketAdapter(client) {
-  function handleMessage(message) {
-    if (message.op === 'publish') {
-      client.emit(message.topic, message.msg);
-    } else if (message.op === 'service_response') {
-      client.emit(message.id, message);
-    } else if (message.op === 'call_service') {
-      client.emit(message.service, message);
-    } else if(message.op === 'status'){
-      if(message.id){
-        client.emit('status:'+message.id, message);
-      } else {
-        client.emit('status', message);
-      }
-    }
-  }
-
-  function handlePng(message, callback) {
-    if (message.op === 'png') {
-      decompressPng(message.data, callback);
-    } else {
-      callback(message);
-    }
-  }
-
-  function decodeBSON(data, callback) {
-    if (!BSON) {
-      throw 'Cannot process BSON encoded message without BSON header.';
-    }
-    var reader = new FileReader();
-    reader.onload  = function() {
-      var uint8Array = new Uint8Array(this.result);
-      var msg = BSON.deserialize(uint8Array);
-      callback(msg);
-    };
-    reader.readAsArrayBuffer(data);
-  }
-
-  return {
-    /**
-     * Emits a 'connection' event on WebSocket connection.
-     *
-     * @param event - the argument to emit with the event.
-     * @memberof SocketAdapter
-     */
-    onopen: function onOpen(event) {
-      client.isConnected = true;
-      client.emit('connection', event);
-    },
-
-    /**
-     * Emits a 'close' event on WebSocket disconnection.
-     *
-     * @param event - the argument to emit with the event.
-     * @memberof SocketAdapter
-     */
-    onclose: function onClose(event) {
-      client.isConnected = false;
-      client.emit('close', event);
-    },
-
-    /**
-     * Emits an 'error' event whenever there was an error.
-     *
-     * @param event - the argument to emit with the event.
-     * @memberof SocketAdapter
-     */
-    onerror: function onError(event) {
-      client.emit('error', event);
-    },
-
-    /**
-     * Parses message responses from rosbridge and sends to the appropriate
-     * topic, service, or param.
-     *
-     * @param message - the raw JSON message from rosbridge.
-     * @memberof SocketAdapter
-     */
-    onmessage: function onMessage(data) {
-      if (typeof Blob !== 'undefined' && data.data instanceof Blob) {
-        decodeBSON(data.data, function (message) {
-          handlePng(message, handleMessage);
-        });
-      } else {
-        var message = JSON.parse(typeof data === 'string' ? data : data.data);
-        handlePng(message, handleMessage);
-      }
-    }
-  };
-}
-
-module.exports = SocketAdapter;
-
-},{"../util/decompressPng":41,"ws":39}],17:[function(require,module,exports){
-/**
- * @fileoverview
- * @author Brandon Alexander - baalexander@gmail.com
- */
-
-var EventEmitter2 = require('eventemitter2').EventEmitter2;
-var Message = require('./Message');
-
-/**
- * Publish and/or subscribe to a topic in ROS.
- *
- * Emits the following events:
- *  * 'warning' - if there are any warning during the Topic creation
- *  * 'message' - the message data from rosbridge
- *
- * @constructor
- * @param options - object with following keys:
- *   * ros - the ROSLIB.Ros connection handle
- *   * name - the topic name, like /cmd_vel
- *   * messageType - the message type, like 'std_msgs/String'
- *   * compression - the type of compression to use, like 'png'
- *   * throttle_rate - the rate (in ms in between messages) at which to throttle the topics
- *   * queue_size - the queue created at bridge side for re-publishing webtopics (defaults to 100)
- *   * latch - latch the topic when publishing
- *   * queue_length - the queue length at bridge side used when subscribing (defaults to 0, no queueing).
- *   * reconnect_on_close - the flag to enable resubscription and readvertisement on close event(defaults to true).
- */
-function Topic(options) {
-  options = options || {};
-  this.ros = options.ros;
-  this.name = options.name;
-  this.messageType = options.messageType;
-  this.isAdvertised = false;
-  this.compression = options.compression || 'none';
-  this.throttle_rate = options.throttle_rate || 0;
-  this.latch = options.latch || false;
-  this.queue_size = options.queue_size || 100;
-  this.queue_length = options.queue_length || 0;
-  this.reconnect_on_close = options.reconnect_on_close || true;
-
-  // Check for valid compression types
-  if (this.compression && this.compression !== 'png' &&
-    this.compression !== 'none') {
-    this.emit('warning', this.compression +
-      ' compression is not supported. No compression will be used.');
-  }
-
-  // Check if throttle rate is negative
-  if (this.throttle_rate < 0) {
-    this.emit('warning', this.throttle_rate + ' is not allowed. Set to 0');
-    this.throttle_rate = 0;
-  }
-
-  var that = this;
-  if (this.reconnect_on_close) {
-    this.callForSubscribeAndAdvertise = function(message) {
-      that.ros.callOnConnection(message);
-
-      that.waitForReconnect = false;
-      that.reconnectFunc = function() {
-        if(!that.waitForReconnect) {
-          that.waitForReconnect = true;
-          that.ros.callOnConnection(message);
-          that.ros.once('connection', function() {
-            that.waitForReconnect = false;
-          });
-        }
-      };
-      that.ros.on('close', that.reconnectFunc);
-    };
-  }
-  else {
-    this.callForSubscribeAndAdvertise = this.ros.callOnConnection;
-  }
-
-  this._messageCallback = function(data) {
-    that.emit('message', new Message(data));
-  };
-}
-Topic.prototype.__proto__ = EventEmitter2.prototype;
-
-/**
- * Every time a message is published for the given topic, the callback
- * will be called with the message object.
- *
- * @param callback - function with the following params:
- *   * message - the published message
- */
-Topic.prototype.subscribe = function(callback) {
-  if (typeof callback === 'function') {
-    this.on('message', callback);
-  }
-
-  if (this.subscribeId) { return; }
-  this.ros.on(this.name, this._messageCallback);
-  this.subscribeId = 'subscribe:' + this.name + ':' + (++this.ros.idCounter);
-
-  this.callForSubscribeAndAdvertise({
-    op: 'subscribe',
-    id: this.subscribeId,
-    type: this.messageType,
-    topic: this.name,
-    compression: this.compression,
-    throttle_rate: this.throttle_rate,
-    queue_length: this.queue_length
-  });
-};
-
-/**
- * Unregisters as a subscriber for the topic. Unsubscribing stop remove
- * all subscribe callbacks. To remove a call back, you must explicitly
- * pass the callback function in.
- *
- * @param callback - the optional callback to unregister, if
- *     * provided and other listeners are registered the topic won't
- *     * unsubscribe, just stop emitting to the passed listener
- */
-Topic.prototype.unsubscribe = function(callback) {
-  if (callback) {
-    this.off('message', callback);
-    // If there is any other callbacks still subscribed don't unsubscribe
-    if (this.listeners('message').length) { return; }
-  }
-  if (!this.subscribeId) { return; }
-  // Note: Don't call this.removeAllListeners, allow client to handle that themselves
-  this.ros.off(this.name, this._messageCallback);
-  if(this.reconnect_on_close) {
-    this.ros.off('close', this.reconnectFunc);
-  }
-  this.emit('unsubscribe');
-  this.ros.callOnConnection({
-    op: 'unsubscribe',
-    id: this.subscribeId,
-    topic: this.name
-  });
-  this.subscribeId = null;
-};
-
-
-/**
- * Registers as a publisher for the topic.
- */
-Topic.prototype.advertise = function() {
-  if (this.isAdvertised) {
-    return;
-  }
-  this.advertiseId = 'advertise:' + this.name + ':' + (++this.ros.idCounter);
-  this.callForSubscribeAndAdvertise({
-    op: 'advertise',
-    id: this.advertiseId,
-    type: this.messageType,
-    topic: this.name,
-    latch: this.latch,
-    queue_size: this.queue_size
-  });
-  this.isAdvertised = true;
-
-  if(!this.reconnect_on_close) {
-    var that = this;
-    this.ros.on('close', function() {
-      that.isAdvertised = false;
-    });
-  }
-};
-
-/**
- * Unregisters as a publisher for the topic.
- */
-Topic.prototype.unadvertise = function() {
-  if (!this.isAdvertised) {
-    return;
-  }
-  if(this.reconnect_on_close) {
-    this.ros.off('close', this.reconnectFunc);
-  }
-  this.emit('unadvertise');
-  this.ros.callOnConnection({
-    op: 'unadvertise',
-    id: this.advertiseId,
-    topic: this.name
-  });
-  this.isAdvertised = false;
-};
-
-/**
- * Publish the message.
- *
- * @param message - A ROSLIB.Message object.
- */
-Topic.prototype.publish = function(message) {
-  if (!this.isAdvertised) {
-    this.advertise();
-  }
-
-  this.ros.idCounter++;
-  var call = {
-    op: 'publish',
-    id: 'publish:' + this.name + ':' + this.ros.idCounter,
-    topic: this.name,
-    msg: message,
-    latch: this.latch
-  };
-  this.ros.callOnConnection(call);
-};
-
-module.exports = Topic;
-
-},{"./Message":10,"eventemitter2":1}],18:[function(require,module,exports){
-var mixin = require('../mixin');
-
-var core = module.exports = {
-    Ros: require('./Ros'),
-    Topic: require('./Topic'),
-    Message: require('./Message'),
-    Param: require('./Param'),
-    Service: require('./Service'),
-    ServiceRequest: require('./ServiceRequest'),
-    ServiceResponse: require('./ServiceResponse')
-};
-
-mixin(core.Ros, ['Param', 'Service', 'Topic'], core);
-
-},{"../mixin":24,"./Message":10,"./Param":11,"./Ros":12,"./Service":13,"./ServiceRequest":14,"./ServiceResponse":15,"./Topic":17}],19:[function(require,module,exports){
-/**
- * @fileoverview
- * @author David Gossow - dgossow@willowgarage.com
- */
-
-var Vector3 = require('./Vector3');
-var Quaternion = require('./Quaternion');
-
-/**
- * A Pose in 3D space. Values are copied into this object.
- *
- *  @constructor
- *  @param options - object with following keys:
- *   * position - the Vector3 describing the position
- *   * orientation - the ROSLIB.Quaternion describing the orientation
- */
-function Pose(options) {
-  options = options || {};
-  // copy the values into this object if they exist
-  this.position = new Vector3(options.position);
-  this.orientation = new Quaternion(options.orientation);
-}
-
-/**
- * Apply a transform against this pose.
- *
- * @param tf the transform
- */
-Pose.prototype.applyTransform = function(tf) {
-  this.position.multiplyQuaternion(tf.rotation);
-  this.position.add(tf.translation);
-  var tmp = tf.rotation.clone();
-  tmp.multiply(this.orientation);
-  this.orientation = tmp;
-};
-
-/**
- * Clone a copy of this pose.
- *
- * @returns the cloned pose
- */
-Pose.prototype.clone = function() {
-  return new Pose(this);
-};
-
-module.exports = Pose;
-},{"./Quaternion":20,"./Vector3":22}],20:[function(require,module,exports){
-/**
- * @fileoverview
- * @author David Gossow - dgossow@willowgarage.com
- */
-
-/**
- * A Quaternion.
- *
- *  @constructor
- *  @param options - object with following keys:
- *   * x - the x value
- *   * y - the y value
- *   * z - the z value
- *   * w - the w value
- */
-function Quaternion(options) {
-  options = options || {};
-  this.x = options.x || 0;
-  this.y = options.y || 0;
-  this.z = options.z || 0;
-  this.w = (typeof options.w === 'number') ? options.w : 1;
-}
-
-/**
- * Perform a conjugation on this quaternion.
- */
-Quaternion.prototype.conjugate = function() {
-  this.x *= -1;
-  this.y *= -1;
-  this.z *= -1;
-};
-
-/**
- * Return the norm of this quaternion.
- */
-Quaternion.prototype.norm = function() {
-  return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
-};
-
-/**
- * Perform a normalization on this quaternion.
- */
-Quaternion.prototype.normalize = function() {
-  var l = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
-  if (l === 0) {
-    this.x = 0;
-    this.y = 0;
-    this.z = 0;
-    this.w = 1;
-  } else {
-    l = 1 / l;
-    this.x = this.x * l;
-    this.y = this.y * l;
-    this.z = this.z * l;
-    this.w = this.w * l;
-  }
-};
-
-/**
- * Convert this quaternion into its inverse.
- */
-Quaternion.prototype.invert = function() {
-  this.conjugate();
-  this.normalize();
-};
-
-/**
- * Set the values of this quaternion to the product of itself and the given quaternion.
- *
- * @param q the quaternion to multiply with
- */
-Quaternion.prototype.multiply = function(q) {
-  var newX = this.x * q.w + this.y * q.z - this.z * q.y + this.w * q.x;
-  var newY = -this.x * q.z + this.y * q.w + this.z * q.x + this.w * q.y;
-  var newZ = this.x * q.y - this.y * q.x + this.z * q.w + this.w * q.z;
-  var newW = -this.x * q.x - this.y * q.y - this.z * q.z + this.w * q.w;
-  this.x = newX;
-  this.y = newY;
-  this.z = newZ;
-  this.w = newW;
-};
-
-/**
- * Clone a copy of this quaternion.
- *
- * @returns the cloned quaternion
- */
-Quaternion.prototype.clone = function() {
-  return new Quaternion(this);
-};
-
-module.exports = Quaternion;
-
-},{}],21:[function(require,module,exports){
-/**
- * @fileoverview
- * @author David Gossow - dgossow@willowgarage.com
- */
-
-var Vector3 = require('./Vector3');
-var Quaternion = require('./Quaternion');
-
-/**
- * A Transform in 3-space. Values are copied into this object.
- *
- *  @constructor
- *  @param options - object with following keys:
- *   * translation - the Vector3 describing the translation
- *   * rotation - the ROSLIB.Quaternion describing the rotation
- */
-function Transform(options) {
-  options = options || {};
-  // Copy the values into this object if they exist
-  this.translation = new Vector3(options.translation);
-  this.rotation = new Quaternion(options.rotation);
-}
-
-/**
- * Clone a copy of this transform.
- *
- * @returns the cloned transform
- */
-Transform.prototype.clone = function() {
-  return new Transform(this);
-};
-
-module.exports = Transform;
-},{"./Quaternion":20,"./Vector3":22}],22:[function(require,module,exports){
-/**
- * @fileoverview
- * @author David Gossow - dgossow@willowgarage.com
- */
-
-/**
- * A 3D vector.
- *
- *  @constructor
- *  @param options - object with following keys:
- *   * x - the x value
- *   * y - the y value
- *   * z - the z value
- */
-function Vector3(options) {
-  options = options || {};
-  this.x = options.x || 0;
-  this.y = options.y || 0;
-  this.z = options.z || 0;
-}
-
-/**
- * Set the values of this vector to the sum of itself and the given vector.
- *
- * @param v the vector to add with
- */
-Vector3.prototype.add = function(v) {
-  this.x += v.x;
-  this.y += v.y;
-  this.z += v.z;
-};
-
-/**
- * Set the values of this vector to the difference of itself and the given vector.
- *
- * @param v the vector to subtract with
- */
-Vector3.prototype.subtract = function(v) {
-  this.x -= v.x;
-  this.y -= v.y;
-  this.z -= v.z;
-};
-
-/**
- * Multiply the given Quaternion with this vector.
- *
- * @param q - the quaternion to multiply with
- */
-Vector3.prototype.multiplyQuaternion = function(q) {
-  var ix = q.w * this.x + q.y * this.z - q.z * this.y;
-  var iy = q.w * this.y + q.z * this.x - q.x * this.z;
-  var iz = q.w * this.z + q.x * this.y - q.y * this.x;
-  var iw = -q.x * this.x - q.y * this.y - q.z * this.z;
-  this.x = ix * q.w + iw * -q.x + iy * -q.z - iz * -q.y;
-  this.y = iy * q.w + iw * -q.y + iz * -q.x - ix * -q.z;
-  this.z = iz * q.w + iw * -q.z + ix * -q.y - iy * -q.x;
-};
-
-/**
- * Clone a copy of this vector.
- *
- * @returns the cloned vector
- */
-Vector3.prototype.clone = function() {
-  return new Vector3(this);
-};
-
-module.exports = Vector3;
-},{}],23:[function(require,module,exports){
-module.exports = {
-    Pose: require('./Pose'),
-    Quaternion: require('./Quaternion'),
-    Transform: require('./Transform'),
-    Vector3: require('./Vector3')
-};
-
-},{"./Pose":19,"./Quaternion":20,"./Transform":21,"./Vector3":22}],24:[function(require,module,exports){
-/**
- * Mixin a feature to the core/Ros prototype.
- * For example, mixin(Ros, ['Topic'], {Topic: <Topic>})
- * will add a topic bound to any Ros instances so a user
- * can call `var topic = ros.Topic({name: '/foo'});`
- *
- * @author Graeme Yeates - github.com/megawac
- */
-module.exports = function(Ros, classes, features) {
-    classes.forEach(function(className) {
-        var Class = features[className];
-        Ros.prototype[className] = function(options) {
-            options.ros = this;
-            return new Class(options);
-        };
-    });
-};
-
-},{}],25:[function(require,module,exports){
-/**
- * @fileoverview
- * @author David Gossow - dgossow@willowgarage.com
- */
-
-var ActionClient = require('../actionlib/ActionClient');
-var Goal = require('../actionlib/Goal');
-
-var Service = require('../core/Service.js');
-var ServiceRequest = require('../core/ServiceRequest.js');
-
-var Transform = require('../math/Transform');
-
-/**
- * A TF Client that listens to TFs from tf2_web_republisher.
- *
- *  @constructor
- *  @param options - object with following keys:
- *   * ros - the ROSLIB.Ros connection handle
- *   * fixedFrame - the fixed frame, like /base_link
- *   * angularThres - the angular threshold for the TF republisher
- *   * transThres - the translation threshold for the TF republisher
- *   * rate - the rate for the TF republisher
- *   * updateDelay - the time (in ms) to wait after a new subscription
- *                   to update the TF republisher's list of TFs
- *   * topicTimeout - the timeout parameter for the TF republisher
- *   * serverName (optional) - the name of the tf2_web_republisher server
- *   * repubServiceName (optional) - the name of the republish_tfs service (non groovy compatibility mode only)
- *                                                                                                                              default: '/republish_tfs'
- */
-function TFClient(options) {
-  options = options || {};
-  this.ros = options.ros;
-  this.fixedFrame = options.fixedFrame || '/base_link';
-  this.angularThres = options.angularThres || 2.0;
-  this.transThres = options.transThres || 0.01;
-  this.rate = options.rate || 10.0;
-  this.updateDelay = options.updateDelay || 50;
-  var seconds = options.topicTimeout || 2.0;
-  var secs = Math.floor(seconds);
-  var nsecs = Math.floor((seconds - secs) * 1000000000);
-  this.topicTimeout = {
-    secs: secs,
-    nsecs: nsecs
-  };
-  this.serverName = options.serverName || '/tf2_web_republisher';
-  this.repubServiceName = options.repubServiceName || '/republish_tfs';
-
-  this.currentGoal = false;
-  this.currentTopic = false;
-  this.frameInfos = {};
-  this.republisherUpdateRequested = false;
-
-  // Create an Action client
-  this.actionClient = this.ros.ActionClient({
-    serverName : this.serverName,
-    actionName : 'tf2_web_republisher/TFSubscriptionAction',
-    omitStatus : true,
-    omitResult : true
-  });
-
-  // Create a Service client
-  this.serviceClient = this.ros.Service({
-    name: this.repubServiceName,
-    serviceType: 'tf2_web_republisher/RepublishTFs'
-  });
-}
-
-/**
- * Process the incoming TF message and send them out using the callback
- * functions.
- *
- * @param tf - the TF message from the server
- */
-TFClient.prototype.processTFArray = function(tf) {
-  var that = this;
-  tf.transforms.forEach(function(transform) {
-    var frameID = transform.child_frame_id;
-    if (frameID[0] === '/')
-    {
-      frameID = frameID.substring(1);
-    }
-    var info = this.frameInfos[frameID];
-    if (info) {
-      info.transform = new Transform({
-        translation : transform.transform.translation,
-        rotation : transform.transform.rotation
-      });
-      info.cbs.forEach(function(cb) {
-        cb(info.transform);
-      });
-    }
-  }, this);
-};
-
-/**
- * Create and send a new goal (or service request) to the tf2_web_republisher
- * based on the current list of TFs.
- */
-TFClient.prototype.updateGoal = function() {
-  var goalMessage = {
-    source_frames : Object.keys(this.frameInfos),
-    target_frame : this.fixedFrame,
-    angular_thres : this.angularThres,
-    trans_thres : this.transThres,
-    rate : this.rate
-  };
-
-  // if we're running in groovy compatibility mode (the default)
-  // then use the action interface to tf2_web_republisher
-  if(this.ros.groovyCompatibility) {
-    if (this.currentGoal) {
-      this.currentGoal.cancel();
-    }
-    this.currentGoal = new Goal({
-      actionClient : this.actionClient,
-      goalMessage : goalMessage
-    });
-
-    this.currentGoal.on('feedback', this.processTFArray.bind(this));
-    this.currentGoal.send();
-  }
-  else {
-    // otherwise, use the service interface
-    // The service interface has the same parameters as the action,
-    // plus the timeout
-    goalMessage.timeout = this.topicTimeout;
-    var request = new ServiceRequest(goalMessage);
-
-    this.serviceClient.callService(request, this.processResponse.bind(this));
-  }
-
-  this.republisherUpdateRequested = false;
-};
-
-/**
- * Process the service response and subscribe to the tf republisher
- * topic
- *
- * @param response the service response containing the topic name
- */
-TFClient.prototype.processResponse = function(response) {
-  // if we subscribed to a topic before, unsubscribe so
-  // the republisher stops publishing it
-  if (this.currentTopic) {
-    this.currentTopic.unsubscribe();
-  }
-
-  this.currentTopic = this.ros.Topic({
-    name: response.topic_name,
-    messageType: 'tf2_web_republisher/TFArray'
-  });
-  this.currentTopic.subscribe(this.processTFArray.bind(this));
-};
-
-/**
- * Subscribe to the given TF frame.
- *
- * @param frameID - the TF frame to subscribe to
- * @param callback - function with params:
- *   * transform - the transform data
- */
-TFClient.prototype.subscribe = function(frameID, callback) {
-  // remove leading slash, if it's there
-  if (frameID[0] === '/')
-  {
-    frameID = frameID.substring(1);
-  }
-  // if there is no callback registered for the given frame, create emtpy callback list
-  if (!this.frameInfos[frameID]) {
-    this.frameInfos[frameID] = {
-      cbs: []
-    };
-    if (!this.republisherUpdateRequested) {
-      setTimeout(this.updateGoal.bind(this), this.updateDelay);
-      this.republisherUpdateRequested = true;
-    }
-  }
-  // if we already have a transform, call back immediately
-  else if (this.frameInfos[frameID].transform) {
-    callback(this.frameInfos[frameID].transform);
-  }
-  this.frameInfos[frameID].cbs.push(callback);
-};
-
-/**
- * Unsubscribe from the given TF frame.
- *
- * @param frameID - the TF frame to unsubscribe from
- * @param callback - the callback function to remove
- */
-TFClient.prototype.unsubscribe = function(frameID, callback) {
-  // remove leading slash, if it's there
-  if (frameID[0] === '/')
-  {
-    frameID = frameID.substring(1);
-  }
-  var info = this.frameInfos[frameID];
-  for (var cbs = info && info.cbs || [], idx = cbs.length; idx--;) {
-    if (cbs[idx] === callback) {
-      cbs.splice(idx, 1);
-    }
-  }
-  if (!callback || cbs.length === 0) {
-    delete this.frameInfos[frameID];
-  }
-};
-
-/**
- * Unsubscribe and unadvertise all topics associated with this TFClient.
- */
-TFClient.prototype.dispose = function() {
-  this.actionClient.dispose();
-  if (this.currentTopic) {
-    this.currentTopic.unsubscribe();
-  }
-};
-
-module.exports = TFClient;
-
-},{"../actionlib/ActionClient":5,"../actionlib/Goal":7,"../core/Service.js":13,"../core/ServiceRequest.js":14,"../math/Transform":21}],26:[function(require,module,exports){
-var Ros = require('../core/Ros');
-var mixin = require('../mixin');
-
-var tf = module.exports = {
-    TFClient: require('./TFClient')
-};
-
-mixin(Ros, ['TFClient'], tf);
-},{"../core/Ros":12,"../mixin":24,"./TFClient":25}],27:[function(require,module,exports){
-/**
- * @fileOverview 
- * @author Benjamin Pitzer - ben.pitzer@gmail.com
- * @author Russell Toris - rctoris@wpi.edu
- */
-
-var Vector3 = require('../math/Vector3');
-var UrdfTypes = require('./UrdfTypes');
-
-/**
- * A Box element in a URDF.
- *
- * @constructor
- * @param options - object with following keys:
- *  * xml - the XML element to parse
- */
-function UrdfBox(options) {
-  this.dimension = null;
-  this.type = UrdfTypes.URDF_BOX;
-
-  // Parse the xml string
-  var xyz = options.xml.getAttribute('size').split(' ');
-  this.dimension = new Vector3({
-    x : parseFloat(xyz[0]),
-    y : parseFloat(xyz[1]),
-    z : parseFloat(xyz[2])
-  });
-}
-
-module.exports = UrdfBox;
-},{"../math/Vector3":22,"./UrdfTypes":36}],28:[function(require,module,exports){
-/**
- * @fileOverview 
- * @author Benjamin Pitzer - ben.pitzer@gmail.com
- * @author Russell Toris - rctoris@wpi.edu
- */
-
-/**
- * A Color element in a URDF.
- *
- * @constructor
- * @param options - object with following keys:
- *  * xml - the XML element to parse
- */
-function UrdfColor(options) {
-  // Parse the xml string
-  var rgba = options.xml.getAttribute('rgba').split(' ');
-  this.r = parseFloat(rgba[0]);
-  this.g = parseFloat(rgba[1]);
-  this.b = parseFloat(rgba[2]);
-  this.a = parseFloat(rgba[3]);
-}
-
-module.exports = UrdfColor;
-},{}],29:[function(require,module,exports){
-/**
- * @fileOverview 
- * @author Benjamin Pitzer - ben.pitzer@gmail.com
- * @author Russell Toris - rctoris@wpi.edu
- */
-
-var UrdfTypes = require('./UrdfTypes');
-
-/**
- * A Cylinder element in a URDF.
- *
- * @constructor
- * @param options - object with following keys:
- *  * xml - the XML element to parse
- */
-function UrdfCylinder(options) {
-  this.type = UrdfTypes.URDF_CYLINDER;
-  this.length = parseFloat(options.xml.getAttribute('length'));
-  this.radius = parseFloat(options.xml.getAttribute('radius'));
-}
-
-module.exports = UrdfCylinder;
-},{"./UrdfTypes":36}],30:[function(require,module,exports){
-/**
- * @fileOverview
- * @author David V. Lu!!  davidvlu@gmail.com
- */
-
-/**
- * A Joint element in a URDF.
- *
- * @constructor
- * @param options - object with following keys:
- *  * xml - the XML element to parse
- */
-function UrdfJoint(options) {
-  this.name = options.xml.getAttribute('name');
-  this.type = options.xml.getAttribute('type');
-
-  var parents = options.xml.getElementsByTagName('parent');
-  if(parents.length > 0) {
-    this.parent = parents[0].getAttribute('link');
-  }
-
-  var children = options.xml.getElementsByTagName('child');
-  if(children.length > 0) {
-    this.child = children[0].getAttribute('link');
-  }
-
-  var limits = options.xml.getElementsByTagName('limit');
-  if (limits.length > 0) {
-    this.minval = parseFloat( limits[0].getAttribute('lower') );
-    this.maxval = parseFloat( limits[0].getAttribute('upper') );
-  }
-}
-
-module.exports = UrdfJoint;
-
-},{}],31:[function(require,module,exports){
-/**
- * @fileOverview 
- * @author Benjamin Pitzer - ben.pitzer@gmail.com
- * @author Russell Toris - rctoris@wpi.edu
- */
-
-var UrdfVisual = require('./UrdfVisual');
-
-/**
- * A Link element in a URDF.
- *
- * @constructor
- * @param options - object with following keys:
- *  * xml - the XML element to parse
- */
-function UrdfLink(options) {
-  this.name = options.xml.getAttribute('name');
-  this.visuals = [];
-  var visuals = options.xml.getElementsByTagName('visual');
-
-  for( var i=0; i<visuals.length; i++ ) {
-    this.visuals.push( new UrdfVisual({
-      xml : visuals[i]
-    }) );
-  }
-}
-
-module.exports = UrdfLink;
-},{"./UrdfVisual":37}],32:[function(require,module,exports){
-/**
- * @fileOverview 
- * @author Benjamin Pitzer - ben.pitzer@gmail.com
- * @author Russell Toris - rctoris@wpi.edu
- */
-
-var UrdfColor = require('./UrdfColor');
-
-/**
- * A Material element in a URDF.
- *
- * @constructor
- * @param options - object with following keys:
- *  * xml - the XML element to parse
- */
-function UrdfMaterial(options) {
-  this.textureFilename = null;
-  this.color = null;
-
-  this.name = options.xml.getAttribute('name');
-
-  // Texture
-  var textures = options.xml.getElementsByTagName('texture');
-  if (textures.length > 0) {
-    this.textureFilename = textures[0].getAttribute('filename');
-  }
-
-  // Color
-  var colors = options.xml.getElementsByTagName('color');
-  if (colors.length > 0) {
-    // Parse the RBGA string
-    this.color = new UrdfColor({
-      xml : colors[0]
-    });
-  }
-}
-
-UrdfMaterial.prototype.isLink = function() {
-  return this.color === null && this.textureFilename === null;
-};
-
-var assign = require('object-assign');
-
-UrdfMaterial.prototype.assign = function(obj) {
-    return assign(this, obj);
-};
-
-module.exports = UrdfMaterial;
-
-},{"./UrdfColor":28,"object-assign":2}],33:[function(require,module,exports){
-/**
- * @fileOverview 
- * @author Benjamin Pitzer - ben.pitzer@gmail.com
- * @author Russell Toris - rctoris@wpi.edu
- */
-
-var Vector3 = require('../math/Vector3');
-var UrdfTypes = require('./UrdfTypes');
-
-/**
- * A Mesh element in a URDF.
- *
- * @constructor
- * @param options - object with following keys:
- *  * xml - the XML element to parse
- */
-function UrdfMesh(options) {
-  this.scale = null;
-
-  this.type = UrdfTypes.URDF_MESH;
-  this.filename = options.xml.getAttribute('filename');
-
-  // Check for a scale
-  var scale = options.xml.getAttribute('scale');
-  if (scale) {
-    // Get the XYZ
-    var xyz = scale.split(' ');
-    this.scale = new Vector3({
-      x : parseFloat(xyz[0]),
-      y : parseFloat(xyz[1]),
-      z : parseFloat(xyz[2])
-    });
-  }
-}
-
-module.exports = UrdfMesh;
-},{"../math/Vector3":22,"./UrdfTypes":36}],34:[function(require,module,exports){
-/**
- * @fileOverview 
- * @author Benjamin Pitzer - ben.pitzer@gmail.com
- * @author Russell Toris - rctoris@wpi.edu
- */
-
-var UrdfMaterial = require('./UrdfMaterial');
-var UrdfLink = require('./UrdfLink');
-var UrdfJoint = require('./UrdfJoint');
-var DOMParser = require('xmldom').DOMParser;
-
-// See https://developer.mozilla.org/docs/XPathResult#Constants
-var XPATH_FIRST_ORDERED_NODE_TYPE = 9;
-
-/**
- * A URDF Model can be used to parse a given URDF into the appropriate elements.
- *
- * @constructor
- * @param options - object with following keys:
- *  * xml - the XML element to parse
- *  * string - the XML element to parse as a string
- */
-function UrdfModel(options) {
-  options = options || {};
-  var xmlDoc = options.xml;
-  var string = options.string;
-  this.materials = {};
-  this.links = {};
-  this.joints = {};
-
-  // Check if we are using a string or an XML element
-  if (string) {
-    // Parse the string
-    var parser = new DOMParser();
-    xmlDoc = parser.parseFromString(string, 'text/xml');
-  }
-
-  // Initialize the model with the given XML node.
-  // Get the robot tag
-  var robotXml = xmlDoc.documentElement;
-
-  // Get the robot name
-  this.name = robotXml.getAttribute('name');
-
-  // Parse all the visual elements we need
-  for (var nodes = robotXml.childNodes, i = 0; i < nodes.length; i++) {
-    var node = nodes[i];
-    if (node.tagName === 'material') {
-      var material = new UrdfMaterial({
-        xml : node
-      });
-      // Make sure this is unique
-      if (this.materials[material.name] !== void 0) {
-        if( this.materials[material.name].isLink() ) {
-          this.materials[material.name].assign( material );
-        } else {
-          console.warn('Material ' + material.name + 'is not unique.');
-        }
-      } else {
-        this.materials[material.name] = material;
-      }
-    } else if (node.tagName === 'link') {
-      var link = new UrdfLink({
-        xml : node
-      });
-      // Make sure this is unique
-      if (this.links[link.name] !== void 0) {
-        console.warn('Link ' + link.name + ' is not unique.');
-      } else {
-        // Check for a material
-        for( var j=0; j<link.visuals.length; j++ )
-        {
-          var mat = link.visuals[j].material; 
-          if ( mat !== null ) {
-            if (this.materials[mat.name] !== void 0) {
-              link.visuals[j].material = this.materials[mat.name];
-            } else {
-              this.materials[mat.name] = mat;
-            }
-          }
-        }
-
-        // Add the link
-        this.links[link.name] = link;
-      }
-    } else if (node.tagName === 'joint') {
-      var joint = new UrdfJoint({
-        xml : node
-      });
-      this.joints[joint.name] = joint;
-    }
-  }
-}
-
-module.exports = UrdfModel;
-
-},{"./UrdfJoint":30,"./UrdfLink":31,"./UrdfMaterial":32,"xmldom":42}],35:[function(require,module,exports){
-/**
- * @fileOverview 
- * @author Benjamin Pitzer - ben.pitzer@gmail.com
- * @author Russell Toris - rctoris@wpi.edu
- */
-
-var UrdfTypes = require('./UrdfTypes');
-
-/**
- * A Sphere element in a URDF.
- *
- * @constructor
- * @param options - object with following keys:
- *  * xml - the XML element to parse
- */
-function UrdfSphere(options) {
-  this.type = UrdfTypes.URDF_SPHERE;
-  this.radius = parseFloat(options.xml.getAttribute('radius'));
-}
-
-module.exports = UrdfSphere;
-},{"./UrdfTypes":36}],36:[function(require,module,exports){
-module.exports = {
-       URDF_SPHERE : 0,
-       URDF_BOX : 1,
-       URDF_CYLINDER : 2,
-       URDF_MESH : 3
-};
-
-},{}],37:[function(require,module,exports){
-/**
- * @fileOverview 
- * @author Benjamin Pitzer - ben.pitzer@gmail.com
- * @author Russell Toris - rctoris@wpi.edu
- */
-
-var Pose = require('../math/Pose');
-var Vector3 = require('../math/Vector3');
-var Quaternion = require('../math/Quaternion');
-
-var UrdfCylinder = require('./UrdfCylinder');
-var UrdfBox = require('./UrdfBox');
-var UrdfMaterial = require('./UrdfMaterial');
-var UrdfMesh = require('./UrdfMesh');
-var UrdfSphere = require('./UrdfSphere');
-
-/**
- * A Visual element in a URDF.
- *
- * @constructor
- * @param options - object with following keys:
- *  * xml - the XML element to parse
- */
-function UrdfVisual(options) {
-  var xml = options.xml;
-  this.origin = null;
-  this.geometry = null;
-  this.material = null;
-
-  // Origin
-  var origins = xml.getElementsByTagName('origin');
-  if (origins.length === 0) {
-    // use the identity as the default
-    this.origin = new Pose();
-  } else {
-    // Check the XYZ
-    var xyz = origins[0].getAttribute('xyz');
-    var position = new Vector3();
-    if (xyz) {
-      xyz = xyz.split(' ');
-      position = new Vector3({
-        x : parseFloat(xyz[0]),
-        y : parseFloat(xyz[1]),
-        z : parseFloat(xyz[2])
-      });
-    }
-
-    // Check the RPY
-    var rpy = origins[0].getAttribute('rpy');
-    var orientation = new Quaternion();
-    if (rpy) {
-      rpy = rpy.split(' ');
-      // Convert from RPY
-      var roll = parseFloat(rpy[0]);
-      var pitch = parseFloat(rpy[1]);
-      var yaw = parseFloat(rpy[2]);
-      var phi = roll / 2.0;
-      var the = pitch / 2.0;
-      var psi = yaw / 2.0;
-      var x = Math.sin(phi) * Math.cos(the) * Math.cos(psi) - Math.cos(phi) * Math.sin(the)
-          * Math.sin(psi);
-      var y = Math.cos(phi) * Math.sin(the) * Math.cos(psi) + Math.sin(phi) * Math.cos(the)
-          * Math.sin(psi);
-      var z = Math.cos(phi) * Math.cos(the) * Math.sin(psi) - Math.sin(phi) * Math.sin(the)
-          * Math.cos(psi);
-      var w = Math.cos(phi) * Math.cos(the) * Math.cos(psi) + Math.sin(phi) * Math.sin(the)
-          * Math.sin(psi);
-
-      orientation = new Quaternion({
-        x : x,
-        y : y,
-        z : z,
-        w : w
-      });
-      orientation.normalize();
-    }
-    this.origin = new Pose({
-      position : position,
-      orientation : orientation
-    });
-  }
-
-  // Geometry
-  var geoms = xml.getElementsByTagName('geometry');
-  if (geoms.length > 0) {
-    var geom = geoms[0];
-    var shape = null;
-    // Check for the shape
-    for (var i = 0; i < geom.childNodes.length; i++) {
-      var node = geom.childNodes[i];
-      if (node.nodeType === 1) {
-        shape = node;
-        break;
-      }
-    }
-    // Check the type
-    var type = shape.nodeName;
-    if (type === 'sphere') {
-      this.geometry = new UrdfSphere({
-        xml : shape
-      });
-    } else if (type === 'box') {
-      this.geometry = new UrdfBox({
-        xml : shape
-      });
-    } else if (type === 'cylinder') {
-      this.geometry = new UrdfCylinder({
-        xml : shape
-      });
-    } else if (type === 'mesh') {
-      this.geometry = new UrdfMesh({
-        xml : shape
-      });
-    } else {
-      console.warn('Unknown geometry type ' + type);
-    }
-  }
-
-  // Material
-  var materials = xml.getElementsByTagName('material');
-  if (materials.length > 0) {
-    this.material = new UrdfMaterial({
-      xml : materials[0]
-    });
-  }
-}
-
-module.exports = UrdfVisual;
-},{"../math/Pose":19,"../math/Quaternion":20,"../math/Vector3":22,"./UrdfBox":27,"./UrdfCylinder":29,"./UrdfMaterial":32,"./UrdfMesh":33,"./UrdfSphere":35}],38:[function(require,module,exports){
-module.exports = require('object-assign')({
-    UrdfBox: require('./UrdfBox'),
-    UrdfColor: require('./UrdfColor'),
-    UrdfCylinder: require('./UrdfCylinder'),
-    UrdfLink: require('./UrdfLink'),
-    UrdfMaterial: require('./UrdfMaterial'),
-    UrdfMesh: require('./UrdfMesh'),
-    UrdfModel: require('./UrdfModel'),
-    UrdfSphere: require('./UrdfSphere'),
-    UrdfVisual: require('./UrdfVisual')
-}, require('./UrdfTypes'));
-
-},{"./UrdfBox":27,"./UrdfColor":28,"./UrdfCylinder":29,"./UrdfLink":31,"./UrdfMaterial":32,"./UrdfMesh":33,"./UrdfModel":34,"./UrdfSphere":35,"./UrdfTypes":36,"./UrdfVisual":37,"object-assign":2}],39:[function(require,module,exports){
-(function (global){
-module.exports = global.WebSocket;
-}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
-},{}],40:[function(require,module,exports){
-/* global document */
-module.exports = function Canvas() {
-       return document.createElement('canvas');
-};
-},{}],41:[function(require,module,exports){
-(function (global){
-/**
- * @fileOverview
- * @author Graeme Yeates - github.com/megawac
- */
-
-'use strict';
-
-var Canvas = require('canvas');
-var Image = Canvas.Image || global.Image;
-
-/**
- * If a message was compressed as a PNG image (a compression hack since
- * gzipping over WebSockets * is not supported yet), this function places the
- * "image" in a canvas element then decodes the * "image" as a Base64 string.
- *
- * @private
- * @param data - object containing the PNG data.
- * @param callback - function with params:
- *   * data - the uncompressed data
- */
-function decompressPng(data, callback) {
-  // Uncompresses the data before sending it through (use image/canvas to do so).
-  var image = new Image();
-  // When the image loads, extracts the raw data (JSON message).
-  image.onload = function() {
-    // Creates a local canvas to draw on.
-    var canvas = new Canvas();
-    var context = canvas.getContext('2d');
-
-    // Sets width and height.
-    canvas.width = image.width;
-    canvas.height = image.height;
-
-    // Prevents anti-aliasing and loosing data
-    context.imageSmoothingEnabled = false;
-    context.webkitImageSmoothingEnabled = false;
-    context.mozImageSmoothingEnabled = false;
-
-    // Puts the data into the image.
-    context.drawImage(image, 0, 0);
-    // Grabs the raw, uncompressed data.
-    var imageData = context.getImageData(0, 0, image.width, image.height).data;
-
-    // Constructs the JSON.
-    var jsonData = '';
-    for (var i = 0; i < imageData.length; i += 4) {
-      // RGB
-      jsonData += String.fromCharCode(imageData[i], imageData[i + 1], imageData[i + 2]);
-    }
-    callback(JSON.parse(jsonData));
-  };
-  // Sends the image data to load.
-  image.src = 'data:image/png;base64,' + data;
-}
-
-module.exports = decompressPng;
-}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
-},{"canvas":40}],42:[function(require,module,exports){
-(function (global){
-exports.DOMImplementation = global.DOMImplementation;
-exports.XMLSerializer = global.XMLSerializer;
-exports.DOMParser = global.DOMParser;
-}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
-},{}]},{},[4]);
+\r
+},{}],2:[function(require,module,exports){\r
+/*\r
+object-assign\r
+(c) Sindre Sorhus\r
+@license MIT\r
+*/\r
+\r
+'use strict';\r
+/* eslint-disable no-unused-vars */\r
+var getOwnPropertySymbols = Object.getOwnPropertySymbols;\r
+var hasOwnProperty = Object.prototype.hasOwnProperty;\r
+var propIsEnumerable = Object.prototype.propertyIsEnumerable;\r
+\r
+function toObject(val) {\r
+       if (val === null || val === undefined) {\r
+               throw new TypeError('Object.assign cannot be called with null or undefined');\r
+       }\r
+\r
+       return Object(val);\r
+}\r
+\r
+function shouldUseNative() {\r
+       try {\r
+               if (!Object.assign) {\r
+                       return false;\r
+               }\r
+\r
+               // Detect buggy property enumeration order in older V8 versions.\r
+\r
+               // https://bugs.chromium.org/p/v8/issues/detail?id=4118\r
+               var test1 = new String('abc');  // eslint-disable-line no-new-wrappers\r
+               test1[5] = 'de';\r
+               if (Object.getOwnPropertyNames(test1)[0] === '5') {\r
+                       return false;\r
+               }\r
+\r
+               // https://bugs.chromium.org/p/v8/issues/detail?id=3056\r
+               var test2 = {};\r
+               for (var i = 0; i < 10; i++) {\r
+                       test2['_' + String.fromCharCode(i)] = i;\r
+               }\r
+               var order2 = Object.getOwnPropertyNames(test2).map(function (n) {\r
+                       return test2[n];\r
+               });\r
+               if (order2.join('') !== '0123456789') {\r
+                       return false;\r
+               }\r
+\r
+               // https://bugs.chromium.org/p/v8/issues/detail?id=3056\r
+               var test3 = {};\r
+               'abcdefghijklmnopqrst'.split('').forEach(function (letter) {\r
+                       test3[letter] = letter;\r
+               });\r
+               if (Object.keys(Object.assign({}, test3)).join('') !==\r
+                               'abcdefghijklmnopqrst') {\r
+                       return false;\r
+               }\r
+\r
+               return true;\r
+       } catch (err) {\r
+               // We don't expect any of the above to throw, but better to be safe.\r
+               return false;\r
+       }\r
+}\r
+\r
+module.exports = shouldUseNative() ? Object.assign : function (target, source) {\r
+       var from;\r
+       var to = toObject(target);\r
+       var symbols;\r
+\r
+       for (var s = 1; s < arguments.length; s++) {\r
+               from = Object(arguments[s]);\r
+\r
+               for (var key in from) {\r
+                       if (hasOwnProperty.call(from, key)) {\r
+                               to[key] = from[key];\r
+                       }\r
+               }\r
+\r
+               if (getOwnPropertySymbols) {\r
+                       symbols = getOwnPropertySymbols(from);\r
+                       for (var i = 0; i < symbols.length; i++) {\r
+                               if (propIsEnumerable.call(from, symbols[i])) {\r
+                                       to[symbols[i]] = from[symbols[i]];\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       return to;\r
+};\r
+\r
+},{}],3:[function(require,module,exports){\r
+/**\r
+ * @fileOverview\r
+ * @author Russell Toris - rctoris@wpi.edu\r
+ */\r
+\r
+/**\r
+ * If you use roslib in a browser, all the classes will be exported to a global variable called ROSLIB.\r
+ *\r
+ * If you use nodejs, this is the variable you get when you require('roslib')\r
+ */\r
+var ROSLIB = this.ROSLIB || {\r
+  REVISION : '0.20.0'\r
+};\r
+\r
+var assign = require('object-assign');\r
+\r
+// Add core components\r
+assign(ROSLIB, require('./core'));\r
+\r
+assign(ROSLIB, require('./actionlib'));\r
+\r
+assign(ROSLIB, require('./math'));\r
+\r
+assign(ROSLIB, require('./tf'));\r
+\r
+assign(ROSLIB, require('./urdf'));\r
+\r
+module.exports = ROSLIB;\r
+\r
+},{"./actionlib":9,"./core":18,"./math":23,"./tf":26,"./urdf":38,"object-assign":2}],4:[function(require,module,exports){\r
+(function (global){\r
+global.ROSLIB = require('./RosLib');\r
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})\r
+},{"./RosLib":3}],5:[function(require,module,exports){\r
+/**\r
+ * @fileOverview\r
+ * @author Russell Toris - rctoris@wpi.edu\r
+ */\r
+\r
+var Topic = require('../core/Topic');\r
+var Message = require('../core/Message');\r
+var EventEmitter2 = require('eventemitter2').EventEmitter2;\r
+\r
+/**\r
+ * An actionlib action client.\r
+ *\r
+ * Emits the following events:\r
+ *  * 'timeout' - if a timeout occurred while sending a goal\r
+ *  * 'status' - the status messages received from the action server\r
+ *  * 'feedback' -  the feedback messages received from the action server\r
+ *  * 'result' - the result returned from the action server\r
+ *\r
+ *  @constructor\r
+ *  @param options - object with following keys:\r
+ *   * ros - the ROSLIB.Ros connection handle\r
+ *   * serverName - the action server name, like /fibonacci\r
+ *   * actionName - the action message name, like 'actionlib_tutorials/FibonacciAction'\r
+ *   * timeout - the timeout length when connecting to the action server\r
+ */\r
+function ActionClient(options) {\r
+  var that = this;\r
+  options = options || {};\r
+  this.ros = options.ros;\r
+  this.serverName = options.serverName;\r
+  this.actionName = options.actionName;\r
+  this.timeout = options.timeout;\r
+  this.omitFeedback = options.omitFeedback;\r
+  this.omitStatus = options.omitStatus;\r
+  this.omitResult = options.omitResult;\r
+  this.goals = {};\r
+\r
+  // flag to check if a status has been received\r
+  var receivedStatus = false;\r
+\r
+  // create the topics associated with actionlib\r
+  this.feedbackListener = new Topic({\r
+    ros : this.ros,\r
+    name : this.serverName + '/feedback',\r
+    messageType : this.actionName + 'Feedback'\r
+  });\r
+\r
+  this.statusListener = new Topic({\r
+    ros : this.ros,\r
+    name : this.serverName + '/status',\r
+    messageType : 'actionlib_msgs/GoalStatusArray'\r
+  });\r
+\r
+  this.resultListener = new Topic({\r
+    ros : this.ros,\r
+    name : this.serverName + '/result',\r
+    messageType : this.actionName + 'Result'\r
+  });\r
+\r
+  this.goalTopic = new Topic({\r
+    ros : this.ros,\r
+    name : this.serverName + '/goal',\r
+    messageType : this.actionName + 'Goal'\r
+  });\r
+\r
+  this.cancelTopic = new Topic({\r
+    ros : this.ros,\r
+    name : this.serverName + '/cancel',\r
+    messageType : 'actionlib_msgs/GoalID'\r
+  });\r
+\r
+  // advertise the goal and cancel topics\r
+  this.goalTopic.advertise();\r
+  this.cancelTopic.advertise();\r
+\r
+  // subscribe to the status topic\r
+  if (!this.omitStatus) {\r
+    this.statusListener.subscribe(function(statusMessage) {\r
+      receivedStatus = true;\r
+      statusMessage.status_list.forEach(function(status) {\r
+        var goal = that.goals[status.goal_id.id];\r
+        if (goal) {\r
+          goal.emit('status', status);\r
+        }\r
+      });\r
+    });\r
+  }\r
+\r
+  // subscribe the the feedback topic\r
+  if (!this.omitFeedback) {\r
+    this.feedbackListener.subscribe(function(feedbackMessage) {\r
+      var goal = that.goals[feedbackMessage.status.goal_id.id];\r
+      if (goal) {\r
+        goal.emit('status', feedbackMessage.status);\r
+        goal.emit('feedback', feedbackMessage.feedback);\r
+      }\r
+    });\r
+  }\r
+\r
+  // subscribe to the result topic\r
+  if (!this.omitResult) {\r
+    this.resultListener.subscribe(function(resultMessage) {\r
+      var goal = that.goals[resultMessage.status.goal_id.id];\r
+\r
+      if (goal) {\r
+        goal.emit('status', resultMessage.status);\r
+        goal.emit('result', resultMessage.result);\r
+      }\r
+    });\r
+  }\r
+\r
+  // If timeout specified, emit a 'timeout' event if the action server does not respond\r
+  if (this.timeout) {\r
+    setTimeout(function() {\r
+      if (!receivedStatus) {\r
+        that.emit('timeout');\r
+      }\r
+    }, this.timeout);\r
+  }\r
+}\r
+\r
+ActionClient.prototype.__proto__ = EventEmitter2.prototype;\r
+\r
+/**\r
+ * Cancel all goals associated with this ActionClient.\r
+ */\r
+ActionClient.prototype.cancel = function() {\r
+  var cancelMessage = new Message();\r
+  this.cancelTopic.publish(cancelMessage);\r
+};\r
+\r
+/**\r
+ * Unsubscribe and unadvertise all topics associated with this ActionClient.\r
+ */\r
+ActionClient.prototype.dispose = function() {\r
+  this.goalTopic.unadvertise();\r
+  this.cancelTopic.unadvertise();\r
+  if (!this.omitStatus) {this.statusListener.unsubscribe();}\r
+  if (!this.omitFeedback) {this.feedbackListener.unsubscribe();}\r
+  if (!this.omitResult) {this.resultListener.unsubscribe();}\r
+};\r
+\r
+module.exports = ActionClient;\r
+\r
+},{"../core/Message":10,"../core/Topic":17,"eventemitter2":1}],6:[function(require,module,exports){\r
+/**\r
+ * @fileOverview\r
+ * @author Justin Young - justin@oodar.com.au\r
+ * @author Russell Toris - rctoris@wpi.edu\r
+ */\r
+\r
+var Topic = require('../core/Topic');\r
+var Message = require('../core/Message');\r
+var EventEmitter2 = require('eventemitter2').EventEmitter2;\r
+\r
+/**\r
+ * An actionlib action listener\r
+ *\r
+ * Emits the following events:\r
+ *  * 'status' - the status messages received from the action server\r
+ *  * 'feedback' -  the feedback messages received from the action server\r
+ *  * 'result' - the result returned from the action server\r
+ *\r
+ *  @constructor\r
+ *  @param options - object with following keys:\r
+ *   * ros - the ROSLIB.Ros connection handle\r
+ *   * serverName - the action server name, like /fibonacci\r
+ *   * actionName - the action message name, like 'actionlib_tutorials/FibonacciAction'\r
+ */\r
+function ActionListener(options) {\r
+  var that = this;\r
+  options = options || {};\r
+  this.ros = options.ros;\r
+  this.serverName = options.serverName;\r
+  this.actionName = options.actionName;\r
+  this.timeout = options.timeout;\r
+  this.omitFeedback = options.omitFeedback;\r
+  this.omitStatus = options.omitStatus;\r
+  this.omitResult = options.omitResult;\r
+\r
+\r
+  // create the topics associated with actionlib\r
+  var goalListener = new Topic({\r
+    ros : this.ros,\r
+    name : this.serverName + '/goal',\r
+    messageType : this.actionName + 'Goal'\r
+  });\r
+\r
+  var feedbackListener = new Topic({\r
+    ros : this.ros,\r
+    name : this.serverName + '/feedback',\r
+    messageType : this.actionName + 'Feedback'\r
+  });\r
+\r
+  var statusListener = new Topic({\r
+    ros : this.ros,\r
+    name : this.serverName + '/status',\r
+    messageType : 'actionlib_msgs/GoalStatusArray'\r
+  });\r
+\r
+  var resultListener = new Topic({\r
+    ros : this.ros,\r
+    name : this.serverName + '/result',\r
+    messageType : this.actionName + 'Result'\r
+  });\r
+\r
+  goalListener.subscribe(function(goalMessage) {\r
+      that.emit('goal', goalMessage);\r
+  });\r
+\r
+  statusListener.subscribe(function(statusMessage) {\r
+      statusMessage.status_list.forEach(function(status) {\r
+          that.emit('status', status);\r
+      });\r
+  });\r
+\r
+  feedbackListener.subscribe(function(feedbackMessage) {\r
+      that.emit('status', feedbackMessage.status);\r
+      that.emit('feedback', feedbackMessage.feedback);\r
+  });\r
+\r
+  // subscribe to the result topic\r
+  resultListener.subscribe(function(resultMessage) {\r
+      that.emit('status', resultMessage.status);\r
+      that.emit('result', resultMessage.result);\r
+  });\r
+\r
+}\r
+\r
+ActionListener.prototype.__proto__ = EventEmitter2.prototype;\r
+\r
+module.exports = ActionListener;\r
+\r
+},{"../core/Message":10,"../core/Topic":17,"eventemitter2":1}],7:[function(require,module,exports){\r
+/**\r
+ * @fileOverview\r
+ * @author Russell Toris - rctoris@wpi.edu\r
+ */\r
+\r
+var Message = require('../core/Message');\r
+var EventEmitter2 = require('eventemitter2').EventEmitter2;\r
+\r
+/**\r
+ * An actionlib goal goal is associated with an action server.\r
+ *\r
+ * Emits the following events:\r
+ *  * 'timeout' - if a timeout occurred while sending a goal\r
+ *\r
+ *  @constructor\r
+ *  @param object with following keys:\r
+ *   * actionClient - the ROSLIB.ActionClient to use with this goal\r
+ *   * goalMessage - The JSON object containing the goal for the action server\r
+ */\r
+function Goal(options) {\r
+  var that = this;\r
+  this.actionClient = options.actionClient;\r
+  this.goalMessage = options.goalMessage;\r
+  this.isFinished = false;\r
+\r
+  // Used to create random IDs\r
+  var date = new Date();\r
+\r
+  // Create a random ID\r
+  this.goalID = 'goal_' + Math.random() + '_' + date.getTime();\r
+  // Fill in the goal message\r
+  this.goalMessage = new Message({\r
+    goal_id : {\r
+      stamp : {\r
+        secs : 0,\r
+        nsecs : 0\r
+      },\r
+      id : this.goalID\r
+    },\r
+    goal : this.goalMessage\r
+  });\r
+\r
+  this.on('status', function(status) {\r
+    that.status = status;\r
+  });\r
+\r
+  this.on('result', function(result) {\r
+    that.isFinished = true;\r
+    that.result = result;\r
+  });\r
+\r
+  this.on('feedback', function(feedback) {\r
+    that.feedback = feedback;\r
+  });\r
+\r
+  // Add the goal\r
+  this.actionClient.goals[this.goalID] = this;\r
+}\r
+\r
+Goal.prototype.__proto__ = EventEmitter2.prototype;\r
+\r
+/**\r
+ * Send the goal to the action server.\r
+ *\r
+ * @param timeout (optional) - a timeout length for the goal's result\r
+ */\r
+Goal.prototype.send = function(timeout) {\r
+  var that = this;\r
+  that.actionClient.goalTopic.publish(that.goalMessage);\r
+  if (timeout) {\r
+    setTimeout(function() {\r
+      if (!that.isFinished) {\r
+        that.emit('timeout');\r
+      }\r
+    }, timeout);\r
+  }\r
+};\r
+\r
+/**\r
+ * Cancel the current goal.\r
+ */\r
+Goal.prototype.cancel = function() {\r
+  var cancelMessage = new Message({\r
+    id : this.goalID\r
+  });\r
+  this.actionClient.cancelTopic.publish(cancelMessage);\r
+};\r
+\r
+module.exports = Goal;\r
+},{"../core/Message":10,"eventemitter2":1}],8:[function(require,module,exports){\r
+/**\r
+ * @fileOverview\r
+ * @author Laura Lindzey - lindzey@gmail.com\r
+ */\r
+\r
+var Topic = require('../core/Topic');\r
+var Message = require('../core/Message');\r
+var EventEmitter2 = require('eventemitter2').EventEmitter2;\r
+\r
+/**\r
+ * An actionlib action server client.\r
+ *\r
+ * Emits the following events:\r
+ *  * 'goal' - goal sent by action client\r
+ *  * 'cancel' - action client has canceled the request\r
+ *\r
+ *  @constructor\r
+ *  @param options - object with following keys:\r
+ *   * ros - the ROSLIB.Ros connection handle\r
+ *   * serverName - the action server name, like /fibonacci\r
+ *   * actionName - the action message name, like 'actionlib_tutorials/FibonacciAction'\r
+ */\r
+\r
+function SimpleActionServer(options) {\r
+    var that = this;\r
+    options = options || {};\r
+    this.ros = options.ros;\r
+    this.serverName = options.serverName;\r
+    this.actionName = options.actionName;\r
+\r
+    // create and advertise publishers\r
+    this.feedbackPublisher = new Topic({\r
+        ros : this.ros,\r
+        name : this.serverName + '/feedback',\r
+        messageType : this.actionName + 'Feedback'\r
+    });\r
+    this.feedbackPublisher.advertise();\r
+\r
+    var statusPublisher = new Topic({\r
+        ros : this.ros,\r
+        name : this.serverName + '/status',\r
+        messageType : 'actionlib_msgs/GoalStatusArray'\r
+    });\r
+    statusPublisher.advertise();\r
+\r
+    this.resultPublisher = new Topic({\r
+        ros : this.ros,\r
+        name : this.serverName + '/result',\r
+        messageType : this.actionName + 'Result'\r
+    });\r
+    this.resultPublisher.advertise();\r
+\r
+    // create and subscribe to listeners\r
+    var goalListener = new Topic({\r
+        ros : this.ros,\r
+        name : this.serverName + '/goal',\r
+        messageType : this.actionName + 'Goal'\r
+    });\r
+\r
+    var cancelListener = new Topic({\r
+        ros : this.ros,\r
+        name : this.serverName + '/cancel',\r
+        messageType : 'actionlib_msgs/GoalID'\r
+    });\r
+\r
+    // Track the goals and their status in order to publish status...\r
+    this.statusMessage = new Message({\r
+        header : {\r
+            stamp : {secs : 0, nsecs : 100},\r
+            frame_id : ''\r
+        },\r
+        status_list : []\r
+    });\r
+\r
+    // needed for handling preemption prompted by a new goal being received\r
+    this.currentGoal = null; // currently tracked goal\r
+    this.nextGoal = null; // the one that'll be preempting\r
+\r
+    goalListener.subscribe(function(goalMessage) {\r
+        \r
+    if(that.currentGoal) {\r
+            that.nextGoal = goalMessage;\r
+            // needs to happen AFTER rest is set up\r
+            that.emit('cancel');\r
+    } else {\r
+            that.statusMessage.status_list = [{goal_id : goalMessage.goal_id, status : 1}];\r
+            that.currentGoal = goalMessage;\r
+            that.emit('goal', goalMessage.goal);\r
+    }\r
+    });\r
+\r
+    // helper function for determing ordering of timestamps\r
+    // returns t1 < t2\r
+    var isEarlier = function(t1, t2) {\r
+        if(t1.secs > t2.secs) {\r
+            return false;\r
+        } else if(t1.secs < t2.secs) {\r
+            return true;\r
+        } else if(t1.nsecs < t2.nsecs) {\r
+            return true;\r
+        } else {\r
+            return false;\r
+        }\r
+    };\r
+\r
+    // TODO: this may be more complicated than necessary, since I'm\r
+    // not sure if the callbacks can ever wind up with a scenario\r
+    // where we've been preempted by a next goal, it hasn't finished\r
+    // processing, and then we get a cancel message\r
+    cancelListener.subscribe(function(cancelMessage) {\r
+\r
+        // cancel ALL goals if both empty\r
+        if(cancelMessage.stamp.secs === 0 && cancelMessage.stamp.secs === 0 && cancelMessage.id === '') {\r
+            that.nextGoal = null;\r
+            if(that.currentGoal) {\r
+                that.emit('cancel');\r
+            }\r
+        } else { // treat id and stamp independently\r
+            if(that.currentGoal && cancelMessage.id === that.currentGoal.goal_id.id) {\r
+                that.emit('cancel');\r
+            } else if(that.nextGoal && cancelMessage.id === that.nextGoal.goal_id.id) {\r
+                that.nextGoal = null;\r
+            }\r
+\r
+            if(that.nextGoal && isEarlier(that.nextGoal.goal_id.stamp,\r
+                                          cancelMessage.stamp)) {\r
+                that.nextGoal = null;\r
+            }\r
+            if(that.currentGoal && isEarlier(that.currentGoal.goal_id.stamp,\r
+                                             cancelMessage.stamp)) {\r
+                \r
+                that.emit('cancel');\r
+            }\r
+        }\r
+    });\r
+\r
+    // publish status at pseudo-fixed rate; required for clients to know they've connected\r
+    var statusInterval = setInterval( function() {\r
+        var currentTime = new Date();\r
+        var secs = Math.floor(currentTime.getTime()/1000);\r
+        var nsecs = Math.round(1000000000*(currentTime.getTime()/1000-secs));\r
+        that.statusMessage.header.stamp.secs = secs;\r
+        that.statusMessage.header.stamp.nsecs = nsecs;\r
+        statusPublisher.publish(that.statusMessage);\r
+    }, 500); // publish every 500ms\r
+\r
+}\r
+\r
+SimpleActionServer.prototype.__proto__ = EventEmitter2.prototype;\r
+\r
+/**\r
+*  Set action state to succeeded and return to client\r
+*/\r
+\r
+SimpleActionServer.prototype.setSucceeded = function(result2) {\r
+    \r
+\r
+    var resultMessage = new Message({\r
+        status : {goal_id : this.currentGoal.goal_id, status : 3},\r
+        result : result2\r
+    });\r
+    this.resultPublisher.publish(resultMessage);\r
+\r
+    this.statusMessage.status_list = [];\r
+    if(this.nextGoal) {\r
+        this.currentGoal = this.nextGoal;\r
+        this.nextGoal = null;\r
+        this.emit('goal', this.currentGoal.goal);\r
+    } else {\r
+        this.currentGoal = null;\r
+    }\r
+};\r
+\r
+/**\r
+*  Function to send feedback\r
+*/\r
+\r
+SimpleActionServer.prototype.sendFeedback = function(feedback2) {\r
+\r
+    var feedbackMessage = new Message({\r
+        status : {goal_id : this.currentGoal.goal_id, status : 1},\r
+        feedback : feedback2\r
+    });\r
+    this.feedbackPublisher.publish(feedbackMessage);\r
+};\r
+\r
+/**\r
+*  Handle case where client requests preemption\r
+*/\r
+\r
+SimpleActionServer.prototype.setPreempted = function() {\r
+\r
+    this.statusMessage.status_list = [];\r
+    var resultMessage = new Message({\r
+        status : {goal_id : this.currentGoal.goal_id, status : 2},\r
+    });\r
+    this.resultPublisher.publish(resultMessage);\r
+\r
+    if(this.nextGoal) {\r
+        this.currentGoal = this.nextGoal;\r
+        this.nextGoal = null;\r
+        this.emit('goal', this.currentGoal.goal);\r
+    } else {\r
+        this.currentGoal = null;\r
+    }\r
+};\r
+\r
+module.exports = SimpleActionServer;\r
+},{"../core/Message":10,"../core/Topic":17,"eventemitter2":1}],9:[function(require,module,exports){\r
+var Ros = require('../core/Ros');\r
+var mixin = require('../mixin');\r
+\r
+var action = module.exports = {\r
+    ActionClient: require('./ActionClient'),\r
+    ActionListener: require('./ActionListener'),\r
+    Goal: require('./Goal'),\r
+    SimpleActionServer: require('./SimpleActionServer')\r
+};\r
+\r
+mixin(Ros, ['ActionClient', 'SimpleActionServer'], action);\r
+\r
+},{"../core/Ros":12,"../mixin":24,"./ActionClient":5,"./ActionListener":6,"./Goal":7,"./SimpleActionServer":8}],10:[function(require,module,exports){\r
+/**\r
+ * @fileoverview\r
+ * @author Brandon Alexander - baalexander@gmail.com\r
+ */\r
+\r
+var assign = require('object-assign');\r
+\r
+/**\r
+ * Message objects are used for publishing and subscribing to and from topics.\r
+ *\r
+ * @constructor\r
+ * @param values - object matching the fields defined in the .msg definition file\r
+ */\r
+function Message(values) {\r
+  assign(this, values);\r
+}\r
+\r
+module.exports = Message;\r
+},{"object-assign":2}],11:[function(require,module,exports){\r
+/**\r
+ * @fileoverview\r
+ * @author Brandon Alexander - baalexander@gmail.com\r
+ */\r
+\r
+var Service = require('./Service');\r
+var ServiceRequest = require('./ServiceRequest');\r
+\r
+/**\r
+ * A ROS parameter.\r
+ *\r
+ * @constructor\r
+ * @param options - possible keys include:\r
+ *   * ros - the ROSLIB.Ros connection handle\r
+ *   * name - the param name, like max_vel_x\r
+ */\r
+function Param(options) {\r
+  options = options || {};\r
+  this.ros = options.ros;\r
+  this.name = options.name;\r
+}\r
+\r
+/**\r
+ * Fetches the value of the param.\r
+ *\r
+ * @param callback - function with the following params:\r
+ *  * value - the value of the param from ROS.\r
+ */\r
+Param.prototype.get = function(callback) {\r
+  var paramClient = new Service({\r
+    ros : this.ros,\r
+    name : '/rosapi/get_param',\r
+    serviceType : 'rosapi/GetParam'\r
+  });\r
+\r
+  var request = new ServiceRequest({\r
+    name : this.name\r
+  });\r
+\r
+  paramClient.callService(request, function(result) {\r
+    var value = JSON.parse(result.value);\r
+    callback(value);\r
+  });\r
+};\r
+\r
+/**\r
+ * Sets the value of the param in ROS.\r
+ *\r
+ * @param value - value to set param to.\r
+ */\r
+Param.prototype.set = function(value, callback) {\r
+  var paramClient = new Service({\r
+    ros : this.ros,\r
+    name : '/rosapi/set_param',\r
+    serviceType : 'rosapi/SetParam'\r
+  });\r
+\r
+  var request = new ServiceRequest({\r
+    name : this.name,\r
+    value : JSON.stringify(value)\r
+  });\r
+\r
+  paramClient.callService(request, callback);\r
+};\r
+\r
+/**\r
+ * Delete this parameter on the ROS server.\r
+ */\r
+Param.prototype.delete = function(callback) {\r
+  var paramClient = new Service({\r
+    ros : this.ros,\r
+    name : '/rosapi/delete_param',\r
+    serviceType : 'rosapi/DeleteParam'\r
+  });\r
+\r
+  var request = new ServiceRequest({\r
+    name : this.name\r
+  });\r
+\r
+  paramClient.callService(request, callback);\r
+};\r
+\r
+module.exports = Param;\r
+},{"./Service":13,"./ServiceRequest":14}],12:[function(require,module,exports){\r
+/**\r
+ * @fileoverview\r
+ * @author Brandon Alexander - baalexander@gmail.com\r
+ */\r
+\r
+var WebSocket = require('ws');\r
+var socketAdapter = require('./SocketAdapter.js');\r
+\r
+var Service = require('./Service');\r
+var ServiceRequest = require('./ServiceRequest');\r
+\r
+var assign = require('object-assign');\r
+var EventEmitter2 = require('eventemitter2').EventEmitter2;\r
+\r
+/**\r
+ * Manages connection to the server and all interactions with ROS.\r
+ *\r
+ * Emits the following events:\r
+ *  * 'error' - there was an error with ROS\r
+ *  * 'connection' - connected to the WebSocket server\r
+ *  * 'close' - disconnected to the WebSocket server\r
+ *  * <topicName> - a message came from rosbridge with the given topic name\r
+ *  * <serviceID> - a service response came from rosbridge with the given ID\r
+ *\r
+ * @constructor\r
+ * @param options - possible keys include: <br>\r
+ *   * url (optional) - (can be specified later with `connect`) the WebSocket URL for rosbridge or the node server url to connect using socket.io (if socket.io exists in the page) <br>\r
+ *   * groovyCompatibility - don't use interfaces that changed after the last groovy release or rosbridge_suite and related tools (defaults to true)\r
+ *   * transportLibrary (optional) - one of 'websocket' (default), 'socket.io' or RTCPeerConnection instance controlling how the connection is created in `connect`.\r
+ *   * transportOptions (optional) - the options to use use when creating a connection. Currently only used if `transportLibrary` is RTCPeerConnection.\r
+ */\r
+function Ros(options) {\r
+  options = options || {};\r
+  this.socket = null;\r
+  this.idCounter = 0;\r
+  this.isConnected = false;\r
+  this.transportLibrary = options.transportLibrary || 'websocket';\r
+  this.transportOptions = options.transportOptions || {};\r
+\r
+  if (typeof options.groovyCompatibility === 'undefined') {\r
+    this.groovyCompatibility = true;\r
+  }\r
+  else {\r
+    this.groovyCompatibility = options.groovyCompatibility;\r
+  }\r
+\r
+  // Sets unlimited event listeners.\r
+  this.setMaxListeners(0);\r
+\r
+  // begin by checking if a URL was given\r
+  if (options.url) {\r
+    this.connect(options.url);\r
+  }\r
+}\r
+\r
+Ros.prototype.__proto__ = EventEmitter2.prototype;\r
+\r
+/**\r
+ * Connect to the specified WebSocket.\r
+ *\r
+ * @param url - WebSocket URL or RTCDataChannel label for Rosbridge\r
+ */\r
+Ros.prototype.connect = function(url) {\r
+  if (this.transportLibrary === 'socket.io') {\r
+    this.socket = assign(io(url, {'force new connection': true}), socketAdapter(this));\r
+    this.socket.on('connect', this.socket.onopen);\r
+    this.socket.on('data', this.socket.onmessage);\r
+    this.socket.on('close', this.socket.onclose);\r
+    this.socket.on('error', this.socket.onerror);\r
+  } else if (this.transportLibrary.constructor.name === 'RTCPeerConnection') {\r
+    this.socket = assign(this.transportLibrary.createDataChannel(url, this.transportOptions), socketAdapter(this));\r
+  }else {\r
+    this.socket = assign(new WebSocket(url), socketAdapter(this));\r
+  }\r
+\r
+};\r
+\r
+/**\r
+ * Disconnect from the WebSocket server.\r
+ */\r
+Ros.prototype.close = function() {\r
+  if (this.socket) {\r
+    this.socket.close();\r
+  }\r
+};\r
+\r
+/**\r
+ * Sends an authorization request to the server.\r
+ *\r
+ * @param mac - MAC (hash) string given by the trusted source.\r
+ * @param client - IP of the client.\r
+ * @param dest - IP of the destination.\r
+ * @param rand - Random string given by the trusted source.\r
+ * @param t - Time of the authorization request.\r
+ * @param level - User level as a string given by the client.\r
+ * @param end - End time of the client's session.\r
+ */\r
+Ros.prototype.authenticate = function(mac, client, dest, rand, t, level, end) {\r
+  // create the request\r
+  var auth = {\r
+    op : 'auth',\r
+    mac : mac,\r
+    client : client,\r
+    dest : dest,\r
+    rand : rand,\r
+    t : t,\r
+    level : level,\r
+    end : end\r
+  };\r
+  // send the request\r
+  this.callOnConnection(auth);\r
+};\r
+\r
+/**\r
+ * Sends the message over the WebSocket, but queues the message up if not yet\r
+ * connected.\r
+ */\r
+Ros.prototype.callOnConnection = function(message) {\r
+  var that = this;\r
+  var messageJson = JSON.stringify(message);\r
+  var emitter = null;\r
+  if (this.transportLibrary === 'socket.io') {\r
+    emitter = function(msg){that.socket.emit('operation', msg);};\r
+  } else {\r
+    emitter = function(msg){that.socket.send(msg);};\r
+  }\r
+\r
+  if (!this.isConnected) {\r
+    that.once('connection', function() {\r
+      emitter(messageJson);\r
+    });\r
+  } else {\r
+    emitter(messageJson);\r
+  }\r
+};\r
+\r
+/**\r
+ * Sends a set_level request to the server\r
+ *\r
+ * @param level - Status level (none, error, warning, info)\r
+ * @param id - Optional: Operation ID to change status level on\r
+ */\r
+Ros.prototype.setStatusLevel = function(level, id){\r
+  var levelMsg = {\r
+    op: 'set_level',\r
+    level: level,\r
+    id: id\r
+  };\r
+\r
+  this.callOnConnection(levelMsg);\r
+};\r
+\r
+/**\r
+ * Retrieves Action Servers in ROS as an array of string\r
+ *\r
+ *   * actionservers - Array of action server names\r
+ */\r
+Ros.prototype.getActionServers = function(callback, failedCallback) {\r
+  var getActionServers = new Service({\r
+    ros : this,\r
+    name : '/rosapi/action_servers',\r
+    serviceType : 'rosapi/GetActionServers'\r
+  });\r
+\r
+  var request = new ServiceRequest({});\r
+  if (typeof failedCallback === 'function'){\r
+    getActionServers.callService(request,\r
+      function(result) {\r
+        callback(result.action_servers);\r
+      },\r
+      function(message){\r
+        failedCallback(message);\r
+      }\r
+    );\r
+  }else{\r
+    getActionServers.callService(request, function(result) {\r
+      callback(result.action_servers);\r
+    });\r
+  }\r
+};\r
+\r
+/**\r
+ * Retrieves list of topics in ROS as an array.\r
+ *\r
+ * @param callback function with params:\r
+ *   * topics - Array of topic names\r
+ */\r
+Ros.prototype.getTopics = function(callback, failedCallback) {\r
+  var topicsClient = new Service({\r
+    ros : this,\r
+    name : '/rosapi/topics',\r
+    serviceType : 'rosapi/Topics'\r
+  });\r
+\r
+  var request = new ServiceRequest();\r
+  if (typeof failedCallback === 'function'){\r
+    topicsClient.callService(request,\r
+      function(result) {\r
+        callback(result);\r
+      },\r
+      function(message){\r
+        failedCallback(message);\r
+      }\r
+    );\r
+  }else{\r
+    topicsClient.callService(request, function(result) {\r
+      callback(result);\r
+    });\r
+  }\r
+};\r
+\r
+/**\r
+ * Retrieves Topics in ROS as an array as specific type\r
+ *\r
+ * @param topicType topic type to find:\r
+ * @param callback function with params:\r
+ *   * topics - Array of topic names\r
+ */\r
+Ros.prototype.getTopicsForType = function(topicType, callback, failedCallback) {\r
+  var topicsForTypeClient = new Service({\r
+    ros : this,\r
+    name : '/rosapi/topics_for_type',\r
+    serviceType : 'rosapi/TopicsForType'\r
+  });\r
+\r
+  var request = new ServiceRequest({\r
+    type: topicType\r
+  });\r
+  if (typeof failedCallback === 'function'){\r
+    topicsForTypeClient.callService(request,\r
+      function(result) {\r
+        callback(result.topics);\r
+      },\r
+      function(message){\r
+        failedCallback(message);\r
+      }\r
+    );\r
+  }else{\r
+    topicsForTypeClient.callService(request, function(result) {\r
+      callback(result.topics);\r
+    });\r
+  }\r
+};\r
+\r
+/**\r
+ * Retrieves list of active service names in ROS.\r
+ *\r
+ * @param callback - function with the following params:\r
+ *   * services - array of service names\r
+ */\r
+Ros.prototype.getServices = function(callback, failedCallback) {\r
+  var servicesClient = new Service({\r
+    ros : this,\r
+    name : '/rosapi/services',\r
+    serviceType : 'rosapi/Services'\r
+  });\r
+\r
+  var request = new ServiceRequest();\r
+  if (typeof failedCallback === 'function'){\r
+    servicesClient.callService(request,\r
+      function(result) {\r
+        callback(result.services);\r
+      },\r
+      function(message) {\r
+        failedCallback(message);\r
+      }\r
+    );\r
+  }else{\r
+    servicesClient.callService(request, function(result) {\r
+      callback(result.services);\r
+    });\r
+  }\r
+};\r
+\r
+/**\r
+ * Retrieves list of services in ROS as an array as specific type\r
+ *\r
+ * @param serviceType service type to find:\r
+ * @param callback function with params:\r
+ *   * topics - Array of service names\r
+ */\r
+Ros.prototype.getServicesForType = function(serviceType, callback, failedCallback) {\r
+  var servicesForTypeClient = new Service({\r
+    ros : this,\r
+    name : '/rosapi/services_for_type',\r
+    serviceType : 'rosapi/ServicesForType'\r
+  });\r
+\r
+  var request = new ServiceRequest({\r
+    type: serviceType\r
+  });\r
+  if (typeof failedCallback === 'function'){\r
+    servicesForTypeClient.callService(request,\r
+      function(result) {\r
+        callback(result.services);\r
+      },\r
+      function(message) {\r
+        failedCallback(message);\r
+      }\r
+    );\r
+  }else{\r
+    servicesForTypeClient.callService(request, function(result) {\r
+      callback(result.services);\r
+    });\r
+  }\r
+};\r
+\r
+/**\r
+ * Retrieves a detail of ROS service request.\r
+ *\r
+ * @param service name of service:\r
+ * @param callback - function with params:\r
+ *   * type - String of the service type\r
+ */\r
+Ros.prototype.getServiceRequestDetails = function(type, callback, failedCallback) {\r
+  var serviceTypeClient = new Service({\r
+    ros : this,\r
+    name : '/rosapi/service_request_details',\r
+    serviceType : 'rosapi/ServiceRequestDetails'\r
+  });\r
+  var request = new ServiceRequest({\r
+    type: type\r
+  });\r
+\r
+  if (typeof failedCallback === 'function'){\r
+    serviceTypeClient.callService(request,\r
+      function(result) {\r
+        callback(result);\r
+      },\r
+      function(message){\r
+        failedCallback(message);\r
+      }\r
+    );\r
+  }else{\r
+    serviceTypeClient.callService(request, function(result) {\r
+      callback(result);\r
+    });\r
+  }\r
+};\r
+\r
+/**\r
+ * Retrieves a detail of ROS service request.\r
+ *\r
+ * @param service name of service:\r
+ * @param callback - function with params:\r
+ *   * type - String of the service type\r
+ */\r
+Ros.prototype.getServiceResponseDetails = function(type, callback, failedCallback) {\r
+  var serviceTypeClient = new Service({\r
+    ros : this,\r
+    name : '/rosapi/service_response_details',\r
+    serviceType : 'rosapi/ServiceResponseDetails'\r
+  });\r
+  var request = new ServiceRequest({\r
+    type: type\r
+  });\r
+\r
+  if (typeof failedCallback === 'function'){\r
+    serviceTypeClient.callService(request,\r
+      function(result) {\r
+        callback(result);\r
+      },\r
+      function(message){\r
+        failedCallback(message);\r
+      }\r
+    );\r
+  }else{\r
+    serviceTypeClient.callService(request, function(result) {\r
+      callback(result);\r
+    });\r
+  }\r
+};\r
+\r
+/**\r
+ * Retrieves list of active node names in ROS.\r
+ *\r
+ * @param callback - function with the following params:\r
+ *   * nodes - array of node names\r
+ */\r
+Ros.prototype.getNodes = function(callback, failedCallback) {\r
+  var nodesClient = new Service({\r
+    ros : this,\r
+    name : '/rosapi/nodes',\r
+    serviceType : 'rosapi/Nodes'\r
+  });\r
+\r
+  var request = new ServiceRequest();\r
+  if (typeof failedCallback === 'function'){\r
+    nodesClient.callService(request,\r
+      function(result) {\r
+        callback(result.nodes);\r
+      },\r
+      function(message) {\r
+        failedCallback(message);\r
+      }\r
+    );\r
+  }else{\r
+    nodesClient.callService(request, function(result) {\r
+      callback(result.nodes);\r
+    });\r
+  }\r
+};\r
+\r
+/**\r
+  * Retrieves list subscribed topics, publishing topics and services of a specific node\r
+  *\r
+  * @param node name of the node:\r
+  * @param callback - function with params:\r
+  *   * publications - array of published topic names\r
+  *   * subscriptions - array of subscribed topic names\r
+  *   * services - array of service names hosted\r
+  */\r
+Ros.prototype.getNodeDetails = function(node, callback, failedCallback) {\r
+  var nodesClient = new Service({\r
+    ros : this,\r
+    name : '/rosapi/node_details',\r
+    serviceType : 'rosapi/NodeDetails'\r
+  });\r
+\r
+  var request = new ServiceRequest({\r
+    node: node\r
+  });\r
+  if (typeof failedCallback === 'function'){\r
+    nodesClient.callService(request,\r
+      function(result) {\r
+        callback(result.subscribing, result.publishing, result.services);\r
+      },\r
+      function(message) {\r
+        failedCallback(message);\r
+      }\r
+    );\r
+  } else {\r
+    nodesClient.callService(request, function(result) {\r
+      callback(result);\r
+    });\r
+  }\r
+};\r
+\r
+/**\r
+ * Retrieves list of param names from the ROS Parameter Server.\r
+ *\r
+ * @param callback function with params:\r
+ *  * params - array of param names.\r
+ */\r
+Ros.prototype.getParams = function(callback, failedCallback) {\r
+  var paramsClient = new Service({\r
+    ros : this,\r
+    name : '/rosapi/get_param_names',\r
+    serviceType : 'rosapi/GetParamNames'\r
+  });\r
+  var request = new ServiceRequest();\r
+  if (typeof failedCallback === 'function'){\r
+    paramsClient.callService(request,\r
+      function(result) {\r
+        callback(result.names);\r
+      },\r
+      function(message){\r
+        failedCallback(message);\r
+      }\r
+    );\r
+  }else{\r
+    paramsClient.callService(request, function(result) {\r
+      callback(result.names);\r
+    });\r
+  }\r
+};\r
+\r
+/**\r
+ * Retrieves a type of ROS topic.\r
+ *\r
+ * @param topic name of the topic:\r
+ * @param callback - function with params:\r
+ *   * type - String of the topic type\r
+ */\r
+Ros.prototype.getTopicType = function(topic, callback, failedCallback) {\r
+  var topicTypeClient = new Service({\r
+    ros : this,\r
+    name : '/rosapi/topic_type',\r
+    serviceType : 'rosapi/TopicType'\r
+  });\r
+  var request = new ServiceRequest({\r
+    topic: topic\r
+  });\r
+\r
+  if (typeof failedCallback === 'function'){\r
+    topicTypeClient.callService(request,\r
+      function(result) {\r
+        callback(result.type);\r
+      },\r
+      function(message){\r
+        failedCallback(message);\r
+      }\r
+    );\r
+  }else{\r
+    topicTypeClient.callService(request, function(result) {\r
+      callback(result.type);\r
+    });\r
+  }\r
+};\r
+\r
+/**\r
+ * Retrieves a type of ROS service.\r
+ *\r
+ * @param service name of service:\r
+ * @param callback - function with params:\r
+ *   * type - String of the service type\r
+ */\r
+Ros.prototype.getServiceType = function(service, callback, failedCallback) {\r
+  var serviceTypeClient = new Service({\r
+    ros : this,\r
+    name : '/rosapi/service_type',\r
+    serviceType : 'rosapi/ServiceType'\r
+  });\r
+  var request = new ServiceRequest({\r
+    service: service\r
+  });\r
+\r
+  if (typeof failedCallback === 'function'){\r
+    serviceTypeClient.callService(request,\r
+      function(result) {\r
+        callback(result.type);\r
+      },\r
+      function(message){\r
+        failedCallback(message);\r
+      }\r
+    );\r
+  }else{\r
+    serviceTypeClient.callService(request, function(result) {\r
+      callback(result.type);\r
+    });\r
+  }\r
+};\r
+\r
+/**\r
+ * Retrieves a detail of ROS message.\r
+ *\r
+ * @param callback - function with params:\r
+ *   * details - Array of the message detail\r
+ * @param message - String of a topic type\r
+ */\r
+Ros.prototype.getMessageDetails = function(message, callback, failedCallback) {\r
+  var messageDetailClient = new Service({\r
+    ros : this,\r
+    name : '/rosapi/message_details',\r
+    serviceType : 'rosapi/MessageDetails'\r
+  });\r
+  var request = new ServiceRequest({\r
+    type: message\r
+  });\r
+\r
+  if (typeof failedCallback === 'function'){\r
+    messageDetailClient.callService(request,\r
+      function(result) {\r
+        callback(result.typedefs);\r
+      },\r
+      function(message){\r
+        failedCallback(message);\r
+      }\r
+    );\r
+  }else{\r
+    messageDetailClient.callService(request, function(result) {\r
+      callback(result.typedefs);\r
+    });\r
+  }\r
+};\r
+\r
+/**\r
+ * Decode a typedefs into a dictionary like `rosmsg show foo/bar`\r
+ *\r
+ * @param defs - array of type_def dictionary\r
+ */\r
+Ros.prototype.decodeTypeDefs = function(defs) {\r
+  var that = this;\r
+\r
+  // calls itself recursively to resolve type definition using hints.\r
+  var decodeTypeDefsRec = function(theType, hints) {\r
+    var typeDefDict = {};\r
+    for (var i = 0; i < theType.fieldnames.length; i++) {\r
+      var arrayLen = theType.fieldarraylen[i];\r
+      var fieldName = theType.fieldnames[i];\r
+      var fieldType = theType.fieldtypes[i];\r
+      if (fieldType.indexOf('/') === -1) { // check the fieldType includes '/' or not\r
+        if (arrayLen === -1) {\r
+          typeDefDict[fieldName] = fieldType;\r
+        }\r
+        else {\r
+          typeDefDict[fieldName] = [fieldType];\r
+        }\r
+      }\r
+      else {\r
+        // lookup the name\r
+        var sub = false;\r
+        for (var j = 0; j < hints.length; j++) {\r
+          if (hints[j].type.toString() === fieldType.toString()) {\r
+            sub = hints[j];\r
+            break;\r
+          }\r
+        }\r
+        if (sub) {\r
+          var subResult = decodeTypeDefsRec(sub, hints);\r
+          if (arrayLen === -1) {\r
+          }\r
+          else {\r
+            typeDefDict[fieldName] = [subResult];\r
+          }\r
+        }\r
+        else {\r
+          that.emit('error', 'Cannot find ' + fieldType + ' in decodeTypeDefs');\r
+        }\r
+      }\r
+    }\r
+    return typeDefDict;\r
+  };\r
+\r
+  return decodeTypeDefsRec(defs[0], defs);\r
+};\r
+\r
+\r
+module.exports = Ros;\r
+\r
+},{"./Service":13,"./ServiceRequest":14,"./SocketAdapter.js":16,"eventemitter2":1,"object-assign":2,"ws":39}],13:[function(require,module,exports){\r
+/**\r
+ * @fileoverview\r
+ * @author Brandon Alexander - baalexander@gmail.com\r
+ */\r
+\r
+var ServiceResponse = require('./ServiceResponse');\r
+var ServiceRequest = require('./ServiceRequest');\r
+var EventEmitter2 = require('eventemitter2').EventEmitter2;\r
+\r
+/**\r
+ * A ROS service client.\r
+ *\r
+ * @constructor\r
+ * @params options - possible keys include:\r
+ *   * ros - the ROSLIB.Ros connection handle\r
+ *   * name - the service name, like /add_two_ints\r
+ *   * serviceType - the service type, like 'rospy_tutorials/AddTwoInts'\r
+ */\r
+function Service(options) {\r
+  options = options || {};\r
+  this.ros = options.ros;\r
+  this.name = options.name;\r
+  this.serviceType = options.serviceType;\r
+  this.isAdvertised = false;\r
+\r
+  this._serviceCallback = null;\r
+}\r
+Service.prototype.__proto__ = EventEmitter2.prototype;\r
+/**\r
+ * Calls the service. Returns the service response in the callback.\r
+ *\r
+ * @param request - the ROSLIB.ServiceRequest to send\r
+ * @param callback - function with params:\r
+ *   * response - the response from the service request\r
+ * @param failedCallback - the callback function when the service call failed (optional). Params:\r
+ *   * error - the error message reported by ROS\r
+ */\r
+Service.prototype.callService = function(request, callback, failedCallback) {\r
+  if (this.isAdvertised) {\r
+    return;\r
+  }\r
+\r
+  var serviceCallId = 'call_service:' + this.name + ':' + (++this.ros.idCounter);\r
+\r
+  if (callback || failedCallback) {\r
+    this.ros.once(serviceCallId, function(message) {\r
+      if (message.result !== undefined && message.result === false) {\r
+        if (typeof failedCallback === 'function') {\r
+          failedCallback(message.values);\r
+        }\r
+      } else if (typeof callback === 'function') {\r
+        callback(new ServiceResponse(message.values));\r
+      }\r
+    });\r
+  }\r
+\r
+  var call = {\r
+    op : 'call_service',\r
+    id : serviceCallId,\r
+    service : this.name,\r
+    args : request\r
+  };\r
+  this.ros.callOnConnection(call);\r
+};\r
+\r
+/**\r
+ * Every time a message is published for the given topic, the callback\r
+ * will be called with the message object.\r
+ *\r
+ * @param callback - function with the following params:\r
+ *   * message - the published message\r
+ */\r
+Service.prototype.advertise = function(callback) {\r
+  if (this.isAdvertised || typeof callback !== 'function') {\r
+    return;\r
+  }\r
+\r
+  this._serviceCallback = callback;\r
+  this.ros.on(this.name, this._serviceResponse.bind(this));\r
+  this.ros.callOnConnection({\r
+    op: 'advertise_service',\r
+    type: this.serviceType,\r
+    service: this.name\r
+  });\r
+  this.isAdvertised = true;\r
+};\r
+\r
+Service.prototype.unadvertise = function() {\r
+  if (!this.isAdvertised) {\r
+    return;\r
+  }\r
+  this.ros.callOnConnection({\r
+    op: 'unadvertise_service',\r
+    service: this.name\r
+  });\r
+  this.isAdvertised = false;\r
+};\r
+\r
+Service.prototype._serviceResponse = function(rosbridgeRequest) {\r
+  var response = {};\r
+  var success = this._serviceCallback(rosbridgeRequest.args, response);\r
+\r
+  var call = {\r
+    op: 'service_response',\r
+    service: this.name,\r
+    values: new ServiceResponse(response),\r
+    result: success\r
+  };\r
+\r
+  if (rosbridgeRequest.id) {\r
+    call.id = rosbridgeRequest.id;\r
+  }\r
+\r
+  this.ros.callOnConnection(call);\r
+};\r
+\r
+module.exports = Service;\r
+},{"./ServiceRequest":14,"./ServiceResponse":15,"eventemitter2":1}],14:[function(require,module,exports){\r
+/**\r
+ * @fileoverview\r
+ * @author Brandon Alexander - balexander@willowgarage.com\r
+ */\r
+\r
+var assign = require('object-assign');\r
+\r
+/**\r
+ * A ServiceRequest is passed into the service call.\r
+ *\r
+ * @constructor\r
+ * @param values - object matching the fields defined in the .srv definition file\r
+ */\r
+function ServiceRequest(values) {\r
+  assign(this, values);\r
+}\r
+\r
+module.exports = ServiceRequest;\r
+},{"object-assign":2}],15:[function(require,module,exports){\r
+/**\r
+ * @fileoverview\r
+ * @author Brandon Alexander - balexander@willowgarage.com\r
+ */\r
+\r
+var assign = require('object-assign');\r
+\r
+/**\r
+ * A ServiceResponse is returned from the service call.\r
+ *\r
+ * @constructor\r
+ * @param values - object matching the fields defined in the .srv definition file\r
+ */\r
+function ServiceResponse(values) {\r
+  assign(this, values);\r
+}\r
+\r
+module.exports = ServiceResponse;\r
+},{"object-assign":2}],16:[function(require,module,exports){\r
+/**\r
+ * Socket event handling utilities for handling events on either\r
+ * WebSocket and TCP sockets\r
+ *\r
+ * Note to anyone reviewing this code: these functions are called\r
+ * in the context of their parent object, unless bound\r
+ * @fileOverview\r
+ */\r
+'use strict';\r
+\r
+var decompressPng = require('../util/decompressPng');\r
+var WebSocket = require('ws');\r
+var BSON = null;\r
+if(typeof bson !== 'undefined'){\r
+    BSON = bson().BSON;\r
+}\r
+\r
+/**\r
+ * Events listeners for a WebSocket or TCP socket to a JavaScript\r
+ * ROS Client. Sets up Messages for a given topic to trigger an\r
+ * event on the ROS client.\r
+ *\r
+ * @namespace SocketAdapter\r
+ * @private\r
+ */\r
+function SocketAdapter(client) {\r
+  function handleMessage(message) {\r
+    if (message.op === 'publish') {\r
+      client.emit(message.topic, message.msg);\r
+    } else if (message.op === 'service_response') {\r
+      client.emit(message.id, message);\r
+    } else if (message.op === 'call_service') {\r
+      client.emit(message.service, message);\r
+    } else if(message.op === 'status'){\r
+      if(message.id){\r
+        client.emit('status:'+message.id, message);\r
+      } else {\r
+        client.emit('status', message);\r
+      }\r
+    }\r
+  }\r
+\r
+  function handlePng(message, callback) {\r
+    if (message.op === 'png') {\r
+      decompressPng(message.data, callback);\r
+    } else {\r
+      callback(message);\r
+    }\r
+  }\r
+\r
+  function decodeBSON(data, callback) {\r
+    if (!BSON) {\r
+      throw 'Cannot process BSON encoded message without BSON header.';\r
+    }\r
+    var reader = new FileReader();\r
+    reader.onload  = function() {\r
+      var uint8Array = new Uint8Array(this.result);\r
+      var msg = BSON.deserialize(uint8Array);\r
+      callback(msg);\r
+    };\r
+    reader.readAsArrayBuffer(data);\r
+  }\r
+\r
+  return {\r
+    /**\r
+     * Emits a 'connection' event on WebSocket connection.\r
+     *\r
+     * @param event - the argument to emit with the event.\r
+     * @memberof SocketAdapter\r
+     */\r
+    onopen: function onOpen(event) {\r
+      client.isConnected = true;\r
+      client.emit('connection', event);\r
+    },\r
+\r
+    /**\r
+     * Emits a 'close' event on WebSocket disconnection.\r
+     *\r
+     * @param event - the argument to emit with the event.\r
+     * @memberof SocketAdapter\r
+     */\r
+    onclose: function onClose(event) {\r
+      client.isConnected = false;\r
+      client.emit('close', event);\r
+    },\r
+\r
+    /**\r
+     * Emits an 'error' event whenever there was an error.\r
+     *\r
+     * @param event - the argument to emit with the event.\r
+     * @memberof SocketAdapter\r
+     */\r
+    onerror: function onError(event) {\r
+      client.emit('error', event);\r
+    },\r
+\r
+    /**\r
+     * Parses message responses from rosbridge and sends to the appropriate\r
+     * topic, service, or param.\r
+     *\r
+     * @param message - the raw JSON message from rosbridge.\r
+     * @memberof SocketAdapter\r
+     */\r
+    onmessage: function onMessage(data) {\r
+      if (typeof Blob !== 'undefined' && data.data instanceof Blob) {\r
+        decodeBSON(data.data, function (message) {\r
+          handlePng(message, handleMessage);\r
+        });\r
+      } else {\r
+        var message = JSON.parse(typeof data === 'string' ? data : data.data);\r
+        handlePng(message, handleMessage);\r
+      }\r
+    }\r
+  };\r
+}\r
+\r
+module.exports = SocketAdapter;\r
+\r
+},{"../util/decompressPng":41,"ws":39}],17:[function(require,module,exports){\r
+/**\r
+ * @fileoverview\r
+ * @author Brandon Alexander - baalexander@gmail.com\r
+ */\r
+\r
+var EventEmitter2 = require('eventemitter2').EventEmitter2;\r
+var Message = require('./Message');\r
+\r
+/**\r
+ * Publish and/or subscribe to a topic in ROS.\r
+ *\r
+ * Emits the following events:\r
+ *  * 'warning' - if there are any warning during the Topic creation\r
+ *  * 'message' - the message data from rosbridge\r
+ *\r
+ * @constructor\r
+ * @param options - object with following keys:\r
+ *   * ros - the ROSLIB.Ros connection handle\r
+ *   * name - the topic name, like /cmd_vel\r
+ *   * messageType - the message type, like 'std_msgs/String'\r
+ *   * compression - the type of compression to use, like 'png'\r
+ *   * throttle_rate - the rate (in ms in between messages) at which to throttle the topics\r
+ *   * queue_size - the queue created at bridge side for re-publishing webtopics (defaults to 100)\r
+ *   * latch - latch the topic when publishing\r
+ *   * queue_length - the queue length at bridge side used when subscribing (defaults to 0, no queueing).\r
+ *   * reconnect_on_close - the flag to enable resubscription and readvertisement on close event(defaults to true).\r
+ */\r
+function Topic(options) {\r
+  options = options || {};\r
+  this.ros = options.ros;\r
+  this.name = options.name;\r
+  this.messageType = options.messageType;\r
+  this.isAdvertised = false;\r
+  this.compression = options.compression || 'none';\r
+  this.throttle_rate = options.throttle_rate || 0;\r
+  this.latch = options.latch || false;\r
+  this.queue_size = options.queue_size || 100;\r
+  this.queue_length = options.queue_length || 0;\r
+  this.reconnect_on_close = options.reconnect_on_close || true;\r
+\r
+  // Check for valid compression types\r
+  if (this.compression && this.compression !== 'png' &&\r
+    this.compression !== 'none') {\r
+    this.emit('warning', this.compression +\r
+      ' compression is not supported. No compression will be used.');\r
+  }\r
+\r
+  // Check if throttle rate is negative\r
+  if (this.throttle_rate < 0) {\r
+    this.emit('warning', this.throttle_rate + ' is not allowed. Set to 0');\r
+    this.throttle_rate = 0;\r
+  }\r
+\r
+  var that = this;\r
+  if (this.reconnect_on_close) {\r
+    this.callForSubscribeAndAdvertise = function(message) {\r
+      that.ros.callOnConnection(message);\r
+\r
+      that.waitForReconnect = false;\r
+      that.reconnectFunc = function() {\r
+        if(!that.waitForReconnect) {\r
+          that.waitForReconnect = true;\r
+          that.ros.callOnConnection(message);\r
+          that.ros.once('connection', function() {\r
+            that.waitForReconnect = false;\r
+          });\r
+        }\r
+      };\r
+      that.ros.on('close', that.reconnectFunc);\r
+    };\r
+  }\r
+  else {\r
+    this.callForSubscribeAndAdvertise = this.ros.callOnConnection;\r
+  }\r
+\r
+  this._messageCallback = function(data) {\r
+    that.emit('message', new Message(data));\r
+  };\r
+}\r
+Topic.prototype.__proto__ = EventEmitter2.prototype;\r
+\r
+/**\r
+ * Every time a message is published for the given topic, the callback\r
+ * will be called with the message object.\r
+ *\r
+ * @param callback - function with the following params:\r
+ *   * message - the published message\r
+ */\r
+Topic.prototype.subscribe = function(callback) {\r
+  if (typeof callback === 'function') {\r
+    this.on('message', callback);\r
+  }\r
+\r
+  if (this.subscribeId) { return; }\r
+  this.ros.on(this.name, this._messageCallback);\r
+  this.subscribeId = 'subscribe:' + this.name + ':' + (++this.ros.idCounter);\r
+\r
+  this.callForSubscribeAndAdvertise({\r
+    op: 'subscribe',\r
+    id: this.subscribeId,\r
+    type: this.messageType,\r
+    topic: this.name,\r
+    compression: this.compression,\r
+    throttle_rate: this.throttle_rate,\r
+    queue_length: this.queue_length\r
+  });\r
+};\r
+\r
+/**\r
+ * Unregisters as a subscriber for the topic. Unsubscribing stop remove\r
+ * all subscribe callbacks. To remove a call back, you must explicitly\r
+ * pass the callback function in.\r
+ *\r
+ * @param callback - the optional callback to unregister, if\r
+ *     * provided and other listeners are registered the topic won't\r
+ *     * unsubscribe, just stop emitting to the passed listener\r
+ */\r
+Topic.prototype.unsubscribe = function(callback) {\r
+  if (callback) {\r
+    this.off('message', callback);\r
+    // If there is any other callbacks still subscribed don't unsubscribe\r
+    if (this.listeners('message').length) { return; }\r
+  }\r
+  if (!this.subscribeId) { return; }\r
+  // Note: Don't call this.removeAllListeners, allow client to handle that themselves\r
+  this.ros.off(this.name, this._messageCallback);\r
+  if(this.reconnect_on_close) {\r
+    this.ros.off('close', this.reconnectFunc);\r
+  }\r
+  this.emit('unsubscribe');\r
+  this.ros.callOnConnection({\r
+    op: 'unsubscribe',\r
+    id: this.subscribeId,\r
+    topic: this.name\r
+  });\r
+  this.subscribeId = null;\r
+};\r
+\r
+\r
+/**\r
+ * Registers as a publisher for the topic.\r
+ */\r
+Topic.prototype.advertise = function() {\r
+  if (this.isAdvertised) {\r
+    return;\r
+  }\r
+  this.advertiseId = 'advertise:' + this.name + ':' + (++this.ros.idCounter);\r
+  this.callForSubscribeAndAdvertise({\r
+    op: 'advertise',\r
+    id: this.advertiseId,\r
+    type: this.messageType,\r
+    topic: this.name,\r
+    latch: this.latch,\r
+    queue_size: this.queue_size\r
+  });\r
+  this.isAdvertised = true;\r
+\r
+  if(!this.reconnect_on_close) {\r
+    var that = this;\r
+    this.ros.on('close', function() {\r
+      that.isAdvertised = false;\r
+    });\r
+  }\r
+};\r
+\r
+/**\r
+ * Unregisters as a publisher for the topic.\r
+ */\r
+Topic.prototype.unadvertise = function() {\r
+  if (!this.isAdvertised) {\r
+    return;\r
+  }\r
+  if(this.reconnect_on_close) {\r
+    this.ros.off('close', this.reconnectFunc);\r
+  }\r
+  this.emit('unadvertise');\r
+  this.ros.callOnConnection({\r
+    op: 'unadvertise',\r
+    id: this.advertiseId,\r
+    topic: this.name\r
+  });\r
+  this.isAdvertised = false;\r
+};\r
+\r
+/**\r
+ * Publish the message.\r
+ *\r
+ * @param message - A ROSLIB.Message object.\r
+ */\r
+Topic.prototype.publish = function(message) {\r
+  if (!this.isAdvertised) {\r
+    this.advertise();\r
+  }\r
+\r
+  this.ros.idCounter++;\r
+  var call = {\r
+    op: 'publish',\r
+    id: 'publish:' + this.name + ':' + this.ros.idCounter,\r
+    topic: this.name,\r
+    msg: message,\r
+    latch: this.latch\r
+  };\r
+  this.ros.callOnConnection(call);\r
+};\r
+\r
+module.exports = Topic;\r
+\r
+},{"./Message":10,"eventemitter2":1}],18:[function(require,module,exports){\r
+var mixin = require('../mixin');\r
+\r
+var core = module.exports = {\r
+    Ros: require('./Ros'),\r
+    Topic: require('./Topic'),\r
+    Message: require('./Message'),\r
+    Param: require('./Param'),\r
+    Service: require('./Service'),\r
+    ServiceRequest: require('./ServiceRequest'),\r
+    ServiceResponse: require('./ServiceResponse')\r
+};\r
+\r
+mixin(core.Ros, ['Param', 'Service', 'Topic'], core);\r
+\r
+},{"../mixin":24,"./Message":10,"./Param":11,"./Ros":12,"./Service":13,"./ServiceRequest":14,"./ServiceResponse":15,"./Topic":17}],19:[function(require,module,exports){\r
+/**\r
+ * @fileoverview\r
+ * @author David Gossow - dgossow@willowgarage.com\r
+ */\r
+\r
+var Vector3 = require('./Vector3');\r
+var Quaternion = require('./Quaternion');\r
+\r
+/**\r
+ * A Pose in 3D space. Values are copied into this object.\r
+ *\r
+ *  @constructor\r
+ *  @param options - object with following keys:\r
+ *   * position - the Vector3 describing the position\r
+ *   * orientation - the ROSLIB.Quaternion describing the orientation\r
+ */\r
+function Pose(options) {\r
+  options = options || {};\r
+  // copy the values into this object if they exist\r
+  this.position = new Vector3(options.position);\r
+  this.orientation = new Quaternion(options.orientation);\r
+}\r
+\r
+/**\r
+ * Apply a transform against this pose.\r
+ *\r
+ * @param tf the transform\r
+ */\r
+Pose.prototype.applyTransform = function(tf) {\r
+  this.position.multiplyQuaternion(tf.rotation);\r
+  this.position.add(tf.translation);\r
+  var tmp = tf.rotation.clone();\r
+  tmp.multiply(this.orientation);\r
+  this.orientation = tmp;\r
+};\r
+\r
+/**\r
+ * Clone a copy of this pose.\r
+ *\r
+ * @returns the cloned pose\r
+ */\r
+Pose.prototype.clone = function() {\r
+  return new Pose(this);\r
+};\r
+\r
+module.exports = Pose;\r
+},{"./Quaternion":20,"./Vector3":22}],20:[function(require,module,exports){\r
+/**\r
+ * @fileoverview\r
+ * @author David Gossow - dgossow@willowgarage.com\r
+ */\r
+\r
+/**\r
+ * A Quaternion.\r
+ *\r
+ *  @constructor\r
+ *  @param options - object with following keys:\r
+ *   * x - the x value\r
+ *   * y - the y value\r
+ *   * z - the z value\r
+ *   * w - the w value\r
+ */\r
+function Quaternion(options) {\r
+  options = options || {};\r
+  this.x = options.x || 0;\r
+  this.y = options.y || 0;\r
+  this.z = options.z || 0;\r
+  this.w = (typeof options.w === 'number') ? options.w : 1;\r
+}\r
+\r
+/**\r
+ * Perform a conjugation on this quaternion.\r
+ */\r
+Quaternion.prototype.conjugate = function() {\r
+  this.x *= -1;\r
+  this.y *= -1;\r
+  this.z *= -1;\r
+};\r
+\r
+/**\r
+ * Return the norm of this quaternion.\r
+ */\r
+Quaternion.prototype.norm = function() {\r
+  return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);\r
+};\r
+\r
+/**\r
+ * Perform a normalization on this quaternion.\r
+ */\r
+Quaternion.prototype.normalize = function() {\r
+  var l = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);\r
+  if (l === 0) {\r
+    this.x = 0;\r
+    this.y = 0;\r
+    this.z = 0;\r
+    this.w = 1;\r
+  } else {\r
+    l = 1 / l;\r
+    this.x = this.x * l;\r
+    this.y = this.y * l;\r
+    this.z = this.z * l;\r
+    this.w = this.w * l;\r
+  }\r
+};\r
+\r
+/**\r
+ * Convert this quaternion into its inverse.\r
+ */\r
+Quaternion.prototype.invert = function() {\r
+  this.conjugate();\r
+  this.normalize();\r
+};\r
+\r
+/**\r
+ * Set the values of this quaternion to the product of itself and the given quaternion.\r
+ *\r
+ * @param q the quaternion to multiply with\r
+ */\r
+Quaternion.prototype.multiply = function(q) {\r
+  var newX = this.x * q.w + this.y * q.z - this.z * q.y + this.w * q.x;\r
+  var newY = -this.x * q.z + this.y * q.w + this.z * q.x + this.w * q.y;\r
+  var newZ = this.x * q.y - this.y * q.x + this.z * q.w + this.w * q.z;\r
+  var newW = -this.x * q.x - this.y * q.y - this.z * q.z + this.w * q.w;\r
+  this.x = newX;\r
+  this.y = newY;\r
+  this.z = newZ;\r
+  this.w = newW;\r
+};\r
+\r
+/**\r
+ * Clone a copy of this quaternion.\r
+ *\r
+ * @returns the cloned quaternion\r
+ */\r
+Quaternion.prototype.clone = function() {\r
+  return new Quaternion(this);\r
+};\r
+\r
+module.exports = Quaternion;\r
+\r
+},{}],21:[function(require,module,exports){\r
+/**\r
+ * @fileoverview\r
+ * @author David Gossow - dgossow@willowgarage.com\r
+ */\r
+\r
+var Vector3 = require('./Vector3');\r
+var Quaternion = require('./Quaternion');\r
+\r
+/**\r
+ * A Transform in 3-space. Values are copied into this object.\r
+ *\r
+ *  @constructor\r
+ *  @param options - object with following keys:\r
+ *   * translation - the Vector3 describing the translation\r
+ *   * rotation - the ROSLIB.Quaternion describing the rotation\r
+ */\r
+function Transform(options) {\r
+  options = options || {};\r
+  // Copy the values into this object if they exist\r
+  this.translation = new Vector3(options.translation);\r
+  this.rotation = new Quaternion(options.rotation);\r
+}\r
+\r
+/**\r
+ * Clone a copy of this transform.\r
+ *\r
+ * @returns the cloned transform\r
+ */\r
+Transform.prototype.clone = function() {\r
+  return new Transform(this);\r
+};\r
+\r
+module.exports = Transform;\r
+},{"./Quaternion":20,"./Vector3":22}],22:[function(require,module,exports){\r
+/**\r
+ * @fileoverview\r
+ * @author David Gossow - dgossow@willowgarage.com\r
+ */\r
+\r
+/**\r
+ * A 3D vector.\r
+ *\r
+ *  @constructor\r
+ *  @param options - object with following keys:\r
+ *   * x - the x value\r
+ *   * y - the y value\r
+ *   * z - the z value\r
+ */\r
+function Vector3(options) {\r
+  options = options || {};\r
+  this.x = options.x || 0;\r
+  this.y = options.y || 0;\r
+  this.z = options.z || 0;\r
+}\r
+\r
+/**\r
+ * Set the values of this vector to the sum of itself and the given vector.\r
+ *\r
+ * @param v the vector to add with\r
+ */\r
+Vector3.prototype.add = function(v) {\r
+  this.x += v.x;\r
+  this.y += v.y;\r
+  this.z += v.z;\r
+};\r
+\r
+/**\r
+ * Set the values of this vector to the difference of itself and the given vector.\r
+ *\r
+ * @param v the vector to subtract with\r
+ */\r
+Vector3.prototype.subtract = function(v) {\r
+  this.x -= v.x;\r
+  this.y -= v.y;\r
+  this.z -= v.z;\r
+};\r
+\r
+/**\r
+ * Multiply the given Quaternion with this vector.\r
+ *\r
+ * @param q - the quaternion to multiply with\r
+ */\r
+Vector3.prototype.multiplyQuaternion = function(q) {\r
+  var ix = q.w * this.x + q.y * this.z - q.z * this.y;\r
+  var iy = q.w * this.y + q.z * this.x - q.x * this.z;\r
+  var iz = q.w * this.z + q.x * this.y - q.y * this.x;\r
+  var iw = -q.x * this.x - q.y * this.y - q.z * this.z;\r
+  this.x = ix * q.w + iw * -q.x + iy * -q.z - iz * -q.y;\r
+  this.y = iy * q.w + iw * -q.y + iz * -q.x - ix * -q.z;\r
+  this.z = iz * q.w + iw * -q.z + ix * -q.y - iy * -q.x;\r
+};\r
+\r
+/**\r
+ * Clone a copy of this vector.\r
+ *\r
+ * @returns the cloned vector\r
+ */\r
+Vector3.prototype.clone = function() {\r
+  return new Vector3(this);\r
+};\r
+\r
+module.exports = Vector3;\r
+},{}],23:[function(require,module,exports){\r
+module.exports = {\r
+    Pose: require('./Pose'),\r
+    Quaternion: require('./Quaternion'),\r
+    Transform: require('./Transform'),\r
+    Vector3: require('./Vector3')\r
+};\r
+\r
+},{"./Pose":19,"./Quaternion":20,"./Transform":21,"./Vector3":22}],24:[function(require,module,exports){\r
+/**\r
+ * Mixin a feature to the core/Ros prototype.\r
+ * For example, mixin(Ros, ['Topic'], {Topic: <Topic>})\r
+ * will add a topic bound to any Ros instances so a user\r
+ * can call `var topic = ros.Topic({name: '/foo'});`\r
+ *\r
+ * @author Graeme Yeates - github.com/megawac\r
+ */\r
+module.exports = function(Ros, classes, features) {\r
+    classes.forEach(function(className) {\r
+        var Class = features[className];\r
+        Ros.prototype[className] = function(options) {\r
+            options.ros = this;\r
+            return new Class(options);\r
+        };\r
+    });\r
+};\r
+\r
+},{}],25:[function(require,module,exports){\r
+/**\r
+ * @fileoverview\r
+ * @author David Gossow - dgossow@willowgarage.com\r
+ */\r
+\r
+var ActionClient = require('../actionlib/ActionClient');\r
+var Goal = require('../actionlib/Goal');\r
+\r
+var Service = require('../core/Service.js');\r
+var ServiceRequest = require('../core/ServiceRequest.js');\r
+\r
+var Transform = require('../math/Transform');\r
+\r
+/**\r
+ * A TF Client that listens to TFs from tf2_web_republisher.\r
+ *\r
+ *  @constructor\r
+ *  @param options - object with following keys:\r
+ *   * ros - the ROSLIB.Ros connection handle\r
+ *   * fixedFrame - the fixed frame, like /base_link\r
+ *   * angularThres - the angular threshold for the TF republisher\r
+ *   * transThres - the translation threshold for the TF republisher\r
+ *   * rate - the rate for the TF republisher\r
+ *   * updateDelay - the time (in ms) to wait after a new subscription\r
+ *                   to update the TF republisher's list of TFs\r
+ *   * topicTimeout - the timeout parameter for the TF republisher\r
+ *   * serverName (optional) - the name of the tf2_web_republisher server\r
+ *   * repubServiceName (optional) - the name of the republish_tfs service (non groovy compatibility mode only)\r
+ *                                                                                                                              default: '/republish_tfs'\r
+ */\r
+function TFClient(options) {\r
+  options = options || {};\r
+  this.ros = options.ros;\r
+  this.fixedFrame = options.fixedFrame || '/base_link';\r
+  this.angularThres = options.angularThres || 2.0;\r
+  this.transThres = options.transThres || 0.01;\r
+  this.rate = options.rate || 10.0;\r
+  this.updateDelay = options.updateDelay || 50;\r
+  var seconds = options.topicTimeout || 2.0;\r
+  var secs = Math.floor(seconds);\r
+  var nsecs = Math.floor((seconds - secs) * 1000000000);\r
+  this.topicTimeout = {\r
+    secs: secs,\r
+    nsecs: nsecs\r
+  };\r
+  this.serverName = options.serverName || '/tf2_web_republisher';\r
+  this.repubServiceName = options.repubServiceName || '/republish_tfs';\r
+\r
+  this.currentGoal = false;\r
+  this.currentTopic = false;\r
+  this.frameInfos = {};\r
+  this.republisherUpdateRequested = false;\r
+\r
+  // Create an Action client\r
+  this.actionClient = this.ros.ActionClient({\r
+    serverName : this.serverName,\r
+    actionName : 'tf2_web_republisher/TFSubscriptionAction',\r
+    omitStatus : true,\r
+    omitResult : true\r
+  });\r
+\r
+  // Create a Service client\r
+  this.serviceClient = this.ros.Service({\r
+    name: this.repubServiceName,\r
+    serviceType: 'tf2_web_republisher/RepublishTFs'\r
+  });\r
+}\r
+\r
+/**\r
+ * Process the incoming TF message and send them out using the callback\r
+ * functions.\r
+ *\r
+ * @param tf - the TF message from the server\r
+ */\r
+TFClient.prototype.processTFArray = function(tf) {\r
+  var that = this;\r
+  tf.transforms.forEach(function(transform) {\r
+    var frameID = transform.child_frame_id;\r
+    if (frameID[0] === '/')\r
+    {\r
+      frameID = frameID.substring(1);\r
+    }\r
+    var info = this.frameInfos[frameID];\r
+    if (info) {\r
+      info.transform = new Transform({\r
+        translation : transform.transform.translation,\r
+        rotation : transform.transform.rotation\r
+      });\r
+      info.cbs.forEach(function(cb) {\r
+        cb(info.transform);\r
+      });\r
+    }\r
+  }, this);\r
+};\r
+\r
+/**\r
+ * Create and send a new goal (or service request) to the tf2_web_republisher\r
+ * based on the current list of TFs.\r
+ */\r
+TFClient.prototype.updateGoal = function() {\r
+  var goalMessage = {\r
+    source_frames : Object.keys(this.frameInfos),\r
+    target_frame : this.fixedFrame,\r
+    angular_thres : this.angularThres,\r
+    trans_thres : this.transThres,\r
+    rate : this.rate\r
+  };\r
+\r
+  // if we're running in groovy compatibility mode (the default)\r
+  // then use the action interface to tf2_web_republisher\r
+  if(this.ros.groovyCompatibility) {\r
+    if (this.currentGoal) {\r
+      this.currentGoal.cancel();\r
+    }\r
+    this.currentGoal = new Goal({\r
+      actionClient : this.actionClient,\r
+      goalMessage : goalMessage\r
+    });\r
+\r
+    this.currentGoal.on('feedback', this.processTFArray.bind(this));\r
+    this.currentGoal.send();\r
+  }\r
+  else {\r
+    // otherwise, use the service interface\r
+    // The service interface has the same parameters as the action,\r
+    // plus the timeout\r
+    goalMessage.timeout = this.topicTimeout;\r
+    var request = new ServiceRequest(goalMessage);\r
+\r
+    this.serviceClient.callService(request, this.processResponse.bind(this));\r
+  }\r
+\r
+  this.republisherUpdateRequested = false;\r
+};\r
+\r
+/**\r
+ * Process the service response and subscribe to the tf republisher\r
+ * topic\r
+ *\r
+ * @param response the service response containing the topic name\r
+ */\r
+TFClient.prototype.processResponse = function(response) {\r
+  // if we subscribed to a topic before, unsubscribe so\r
+  // the republisher stops publishing it\r
+  if (this.currentTopic) {\r
+    this.currentTopic.unsubscribe();\r
+  }\r
+\r
+  this.currentTopic = this.ros.Topic({\r
+    name: response.topic_name,\r
+    messageType: 'tf2_web_republisher/TFArray'\r
+  });\r
+  this.currentTopic.subscribe(this.processTFArray.bind(this));\r
+};\r
+\r
+/**\r
+ * Subscribe to the given TF frame.\r
+ *\r
+ * @param frameID - the TF frame to subscribe to\r
+ * @param callback - function with params:\r
+ *   * transform - the transform data\r
+ */\r
+TFClient.prototype.subscribe = function(frameID, callback) {\r
+  // remove leading slash, if it's there\r
+  if (frameID[0] === '/')\r
+  {\r
+    frameID = frameID.substring(1);\r
+  }\r
+  // if there is no callback registered for the given frame, create emtpy callback list\r
+  if (!this.frameInfos[frameID]) {\r
+    this.frameInfos[frameID] = {\r
+      cbs: []\r
+    };\r
+    if (!this.republisherUpdateRequested) {\r
+      setTimeout(this.updateGoal.bind(this), this.updateDelay);\r
+      this.republisherUpdateRequested = true;\r
+    }\r
+  }\r
+  // if we already have a transform, call back immediately\r
+  else if (this.frameInfos[frameID].transform) {\r
+    callback(this.frameInfos[frameID].transform);\r
+  }\r
+  this.frameInfos[frameID].cbs.push(callback);\r
+};\r
+\r
+/**\r
+ * Unsubscribe from the given TF frame.\r
+ *\r
+ * @param frameID - the TF frame to unsubscribe from\r
+ * @param callback - the callback function to remove\r
+ */\r
+TFClient.prototype.unsubscribe = function(frameID, callback) {\r
+  // remove leading slash, if it's there\r
+  if (frameID[0] === '/')\r
+  {\r
+    frameID = frameID.substring(1);\r
+  }\r
+  var info = this.frameInfos[frameID];\r
+  for (var cbs = info && info.cbs || [], idx = cbs.length; idx--;) {\r
+    if (cbs[idx] === callback) {\r
+      cbs.splice(idx, 1);\r
+    }\r
+  }\r
+  if (!callback || cbs.length === 0) {\r
+    delete this.frameInfos[frameID];\r
+  }\r
+};\r
+\r
+/**\r
+ * Unsubscribe and unadvertise all topics associated with this TFClient.\r
+ */\r
+TFClient.prototype.dispose = function() {\r
+  this.actionClient.dispose();\r
+  if (this.currentTopic) {\r
+    this.currentTopic.unsubscribe();\r
+  }\r
+};\r
+\r
+module.exports = TFClient;\r
+\r
+},{"../actionlib/ActionClient":5,"../actionlib/Goal":7,"../core/Service.js":13,"../core/ServiceRequest.js":14,"../math/Transform":21}],26:[function(require,module,exports){\r
+var Ros = require('../core/Ros');\r
+var mixin = require('../mixin');\r
+\r
+var tf = module.exports = {\r
+    TFClient: require('./TFClient')\r
+};\r
+\r
+mixin(Ros, ['TFClient'], tf);\r
+},{"../core/Ros":12,"../mixin":24,"./TFClient":25}],27:[function(require,module,exports){\r
+/**\r
+ * @fileOverview \r
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com\r
+ * @author Russell Toris - rctoris@wpi.edu\r
+ */\r
+\r
+var Vector3 = require('../math/Vector3');\r
+var UrdfTypes = require('./UrdfTypes');\r
+\r
+/**\r
+ * A Box element in a URDF.\r
+ *\r
+ * @constructor\r
+ * @param options - object with following keys:\r
+ *  * xml - the XML element to parse\r
+ */\r
+function UrdfBox(options) {\r
+  this.dimension = null;\r
+  this.type = UrdfTypes.URDF_BOX;\r
+\r
+  // Parse the xml string\r
+  var xyz = options.xml.getAttribute('size').split(' ');\r
+  this.dimension = new Vector3({\r
+    x : parseFloat(xyz[0]),\r
+    y : parseFloat(xyz[1]),\r
+    z : parseFloat(xyz[2])\r
+  });\r
+}\r
+\r
+module.exports = UrdfBox;\r
+},{"../math/Vector3":22,"./UrdfTypes":36}],28:[function(require,module,exports){\r
+/**\r
+ * @fileOverview \r
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com\r
+ * @author Russell Toris - rctoris@wpi.edu\r
+ */\r
+\r
+/**\r
+ * A Color element in a URDF.\r
+ *\r
+ * @constructor\r
+ * @param options - object with following keys:\r
+ *  * xml - the XML element to parse\r
+ */\r
+function UrdfColor(options) {\r
+  // Parse the xml string\r
+  var rgba = options.xml.getAttribute('rgba').split(' ');\r
+  this.r = parseFloat(rgba[0]);\r
+  this.g = parseFloat(rgba[1]);\r
+  this.b = parseFloat(rgba[2]);\r
+  this.a = parseFloat(rgba[3]);\r
+}\r
+\r
+module.exports = UrdfColor;\r
+},{}],29:[function(require,module,exports){\r
+/**\r
+ * @fileOverview \r
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com\r
+ * @author Russell Toris - rctoris@wpi.edu\r
+ */\r
+\r
+var UrdfTypes = require('./UrdfTypes');\r
+\r
+/**\r
+ * A Cylinder element in a URDF.\r
+ *\r
+ * @constructor\r
+ * @param options - object with following keys:\r
+ *  * xml - the XML element to parse\r
+ */\r
+function UrdfCylinder(options) {\r
+  this.type = UrdfTypes.URDF_CYLINDER;\r
+  this.length = parseFloat(options.xml.getAttribute('length'));\r
+  this.radius = parseFloat(options.xml.getAttribute('radius'));\r
+}\r
+\r
+module.exports = UrdfCylinder;\r
+},{"./UrdfTypes":36}],30:[function(require,module,exports){\r
+/**\r
+ * @fileOverview\r
+ * @author David V. Lu!!  davidvlu@gmail.com\r
+ */\r
+\r
+/**\r
+ * A Joint element in a URDF.\r
+ *\r
+ * @constructor\r
+ * @param options - object with following keys:\r
+ *  * xml - the XML element to parse\r
+ */\r
+function UrdfJoint(options) {\r
+  this.name = options.xml.getAttribute('name');\r
+  this.type = options.xml.getAttribute('type');\r
+\r
+  var parents = options.xml.getElementsByTagName('parent');\r
+  if(parents.length > 0) {\r
+    this.parent = parents[0].getAttribute('link');\r
+  }\r
+\r
+  var children = options.xml.getElementsByTagName('child');\r
+  if(children.length > 0) {\r
+    this.child = children[0].getAttribute('link');\r
+  }\r
+\r
+  var limits = options.xml.getElementsByTagName('limit');\r
+  if (limits.length > 0) {\r
+    this.minval = parseFloat( limits[0].getAttribute('lower') );\r
+    this.maxval = parseFloat( limits[0].getAttribute('upper') );\r
+  }\r
+}\r
+\r
+module.exports = UrdfJoint;\r
+\r
+},{}],31:[function(require,module,exports){\r
+/**\r
+ * @fileOverview \r
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com\r
+ * @author Russell Toris - rctoris@wpi.edu\r
+ */\r
+\r
+var UrdfVisual = require('./UrdfVisual');\r
+\r
+/**\r
+ * A Link element in a URDF.\r
+ *\r
+ * @constructor\r
+ * @param options - object with following keys:\r
+ *  * xml - the XML element to parse\r
+ */\r
+function UrdfLink(options) {\r
+  this.name = options.xml.getAttribute('name');\r
+  this.visuals = [];\r
+  var visuals = options.xml.getElementsByTagName('visual');\r
+\r
+  for( var i=0; i<visuals.length; i++ ) {\r
+    this.visuals.push( new UrdfVisual({\r
+      xml : visuals[i]\r
+    }) );\r
+  }\r
+}\r
+\r
+module.exports = UrdfLink;\r
+},{"./UrdfVisual":37}],32:[function(require,module,exports){\r
+/**\r
+ * @fileOverview \r
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com\r
+ * @author Russell Toris - rctoris@wpi.edu\r
+ */\r
+\r
+var UrdfColor = require('./UrdfColor');\r
+\r
+/**\r
+ * A Material element in a URDF.\r
+ *\r
+ * @constructor\r
+ * @param options - object with following keys:\r
+ *  * xml - the XML element to parse\r
+ */\r
+function UrdfMaterial(options) {\r
+  this.textureFilename = null;\r
+  this.color = null;\r
+\r
+  this.name = options.xml.getAttribute('name');\r
+\r
+  // Texture\r
+  var textures = options.xml.getElementsByTagName('texture');\r
+  if (textures.length > 0) {\r
+    this.textureFilename = textures[0].getAttribute('filename');\r
+  }\r
+\r
+  // Color\r
+  var colors = options.xml.getElementsByTagName('color');\r
+  if (colors.length > 0) {\r
+    // Parse the RBGA string\r
+    this.color = new UrdfColor({\r
+      xml : colors[0]\r
+    });\r
+  }\r
+}\r
+\r
+UrdfMaterial.prototype.isLink = function() {\r
+  return this.color === null && this.textureFilename === null;\r
+};\r
+\r
+var assign = require('object-assign');\r
+\r
+UrdfMaterial.prototype.assign = function(obj) {\r
+    return assign(this, obj);\r
+};\r
+\r
+module.exports = UrdfMaterial;\r
+\r
+},{"./UrdfColor":28,"object-assign":2}],33:[function(require,module,exports){\r
+/**\r
+ * @fileOverview \r
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com\r
+ * @author Russell Toris - rctoris@wpi.edu\r
+ */\r
+\r
+var Vector3 = require('../math/Vector3');\r
+var UrdfTypes = require('./UrdfTypes');\r
+\r
+/**\r
+ * A Mesh element in a URDF.\r
+ *\r
+ * @constructor\r
+ * @param options - object with following keys:\r
+ *  * xml - the XML element to parse\r
+ */\r
+function UrdfMesh(options) {\r
+  this.scale = null;\r
+\r
+  this.type = UrdfTypes.URDF_MESH;\r
+  this.filename = options.xml.getAttribute('filename');\r
+\r
+  // Check for a scale\r
+  var scale = options.xml.getAttribute('scale');\r
+  if (scale) {\r
+    // Get the XYZ\r
+    var xyz = scale.split(' ');\r
+    this.scale = new Vector3({\r
+      x : parseFloat(xyz[0]),\r
+      y : parseFloat(xyz[1]),\r
+      z : parseFloat(xyz[2])\r
+    });\r
+  }\r
+}\r
+\r
+module.exports = UrdfMesh;\r
+},{"../math/Vector3":22,"./UrdfTypes":36}],34:[function(require,module,exports){\r
+/**\r
+ * @fileOverview \r
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com\r
+ * @author Russell Toris - rctoris@wpi.edu\r
+ */\r
+\r
+var UrdfMaterial = require('./UrdfMaterial');\r
+var UrdfLink = require('./UrdfLink');\r
+var UrdfJoint = require('./UrdfJoint');\r
+var DOMParser = require('xmldom').DOMParser;\r
+\r
+// See https://developer.mozilla.org/docs/XPathResult#Constants\r
+var XPATH_FIRST_ORDERED_NODE_TYPE = 9;\r
+\r
+/**\r
+ * A URDF Model can be used to parse a given URDF into the appropriate elements.\r
+ *\r
+ * @constructor\r
+ * @param options - object with following keys:\r
+ *  * xml - the XML element to parse\r
+ *  * string - the XML element to parse as a string\r
+ */\r
+function UrdfModel(options) {\r
+  options = options || {};\r
+  var xmlDoc = options.xml;\r
+  var string = options.string;\r
+  this.materials = {};\r
+  this.links = {};\r
+  this.joints = {};\r
+\r
+  // Check if we are using a string or an XML element\r
+  if (string) {\r
+    // Parse the string\r
+    var parser = new DOMParser();\r
+    xmlDoc = parser.parseFromString(string, 'text/xml');\r
+  }\r
+\r
+  // Initialize the model with the given XML node.\r
+  // Get the robot tag\r
+  var robotXml = xmlDoc.documentElement;\r
+\r
+  // Get the robot name\r
+  this.name = robotXml.getAttribute('name');\r
+\r
+  // Parse all the visual elements we need\r
+  for (var nodes = robotXml.childNodes, i = 0; i < nodes.length; i++) {\r
+    var node = nodes[i];\r
+    if (node.tagName === 'material') {\r
+      var material = new UrdfMaterial({\r
+        xml : node\r
+      });\r
+      // Make sure this is unique\r
+      if (this.materials[material.name] !== void 0) {\r
+        if( this.materials[material.name].isLink() ) {\r
+          this.materials[material.name].assign( material );\r
+        } else {\r
+          console.warn('Material ' + material.name + 'is not unique.');\r
+        }\r
+      } else {\r
+        this.materials[material.name] = material;\r
+      }\r
+    } else if (node.tagName === 'link') {\r
+      var link = new UrdfLink({\r
+        xml : node\r
+      });\r
+      // Make sure this is unique\r
+      if (this.links[link.name] !== void 0) {\r
+        console.warn('Link ' + link.name + ' is not unique.');\r
+      } else {\r
+        // Check for a material\r
+        for( var j=0; j<link.visuals.length; j++ )\r
+        {\r
+          var mat = link.visuals[j].material; \r
+          if ( mat !== null ) {\r
+            if (this.materials[mat.name] !== void 0) {\r
+              link.visuals[j].material = this.materials[mat.name];\r
+            } else {\r
+              this.materials[mat.name] = mat;\r
+            }\r
+          }\r
+        }\r
+\r
+        // Add the link\r
+        this.links[link.name] = link;\r
+      }\r
+    } else if (node.tagName === 'joint') {\r
+      var joint = new UrdfJoint({\r
+        xml : node\r
+      });\r
+      this.joints[joint.name] = joint;\r
+    }\r
+  }\r
+}\r
+\r
+module.exports = UrdfModel;\r
+\r
+},{"./UrdfJoint":30,"./UrdfLink":31,"./UrdfMaterial":32,"xmldom":42}],35:[function(require,module,exports){\r
+/**\r
+ * @fileOverview \r
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com\r
+ * @author Russell Toris - rctoris@wpi.edu\r
+ */\r
+\r
+var UrdfTypes = require('./UrdfTypes');\r
+\r
+/**\r
+ * A Sphere element in a URDF.\r
+ *\r
+ * @constructor\r
+ * @param options - object with following keys:\r
+ *  * xml - the XML element to parse\r
+ */\r
+function UrdfSphere(options) {\r
+  this.type = UrdfTypes.URDF_SPHERE;\r
+  this.radius = parseFloat(options.xml.getAttribute('radius'));\r
+}\r
+\r
+module.exports = UrdfSphere;\r
+},{"./UrdfTypes":36}],36:[function(require,module,exports){\r
+module.exports = {\r
+       URDF_SPHERE : 0,\r
+       URDF_BOX : 1,\r
+       URDF_CYLINDER : 2,\r
+       URDF_MESH : 3\r
+};\r
+\r
+},{}],37:[function(require,module,exports){\r
+/**\r
+ * @fileOverview \r
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com\r
+ * @author Russell Toris - rctoris@wpi.edu\r
+ */\r
+\r
+var Pose = require('../math/Pose');\r
+var Vector3 = require('../math/Vector3');\r
+var Quaternion = require('../math/Quaternion');\r
+\r
+var UrdfCylinder = require('./UrdfCylinder');\r
+var UrdfBox = require('./UrdfBox');\r
+var UrdfMaterial = require('./UrdfMaterial');\r
+var UrdfMesh = require('./UrdfMesh');\r
+var UrdfSphere = require('./UrdfSphere');\r
+\r
+/**\r
+ * A Visual element in a URDF.\r
+ *\r
+ * @constructor\r
+ * @param options - object with following keys:\r
+ *  * xml - the XML element to parse\r
+ */\r
+function UrdfVisual(options) {\r
+  var xml = options.xml;\r
+  this.origin = null;\r
+  this.geometry = null;\r
+  this.material = null;\r
+\r
+  // Origin\r
+  var origins = xml.getElementsByTagName('origin');\r
+  if (origins.length === 0) {\r
+    // use the identity as the default\r
+    this.origin = new Pose();\r
+  } else {\r
+    // Check the XYZ\r
+    var xyz = origins[0].getAttribute('xyz');\r
+    var position = new Vector3();\r
+    if (xyz) {\r
+      xyz = xyz.split(' ');\r
+      position = new Vector3({\r
+        x : parseFloat(xyz[0]),\r
+        y : parseFloat(xyz[1]),\r
+        z : parseFloat(xyz[2])\r
+      });\r
+    }\r
+\r
+    // Check the RPY\r
+    var rpy = origins[0].getAttribute('rpy');\r
+    var orientation = new Quaternion();\r
+    if (rpy) {\r
+      rpy = rpy.split(' ');\r
+      // Convert from RPY\r
+      var roll = parseFloat(rpy[0]);\r
+      var pitch = parseFloat(rpy[1]);\r
+      var yaw = parseFloat(rpy[2]);\r
+      var phi = roll / 2.0;\r
+      var the = pitch / 2.0;\r
+      var psi = yaw / 2.0;\r
+      var x = Math.sin(phi) * Math.cos(the) * Math.cos(psi) - Math.cos(phi) * Math.sin(the)\r
+          * Math.sin(psi);\r
+      var y = Math.cos(phi) * Math.sin(the) * Math.cos(psi) + Math.sin(phi) * Math.cos(the)\r
+          * Math.sin(psi);\r
+      var z = Math.cos(phi) * Math.cos(the) * Math.sin(psi) - Math.sin(phi) * Math.sin(the)\r
+          * Math.cos(psi);\r
+      var w = Math.cos(phi) * Math.cos(the) * Math.cos(psi) + Math.sin(phi) * Math.sin(the)\r
+          * Math.sin(psi);\r
+\r
+      orientation = new Quaternion({\r
+        x : x,\r
+        y : y,\r
+        z : z,\r
+        w : w\r
+      });\r
+      orientation.normalize();\r
+    }\r
+    this.origin = new Pose({\r
+      position : position,\r
+      orientation : orientation\r
+    });\r
+  }\r
+\r
+  // Geometry\r
+  var geoms = xml.getElementsByTagName('geometry');\r
+  if (geoms.length > 0) {\r
+    var geom = geoms[0];\r
+    var shape = null;\r
+    // Check for the shape\r
+    for (var i = 0; i < geom.childNodes.length; i++) {\r
+      var node = geom.childNodes[i];\r
+      if (node.nodeType === 1) {\r
+        shape = node;\r
+        break;\r
+      }\r
+    }\r
+    // Check the type\r
+    var type = shape.nodeName;\r
+    if (type === 'sphere') {\r
+      this.geometry = new UrdfSphere({\r
+        xml : shape\r
+      });\r
+    } else if (type === 'box') {\r
+      this.geometry = new UrdfBox({\r
+        xml : shape\r
+      });\r
+    } else if (type === 'cylinder') {\r
+      this.geometry = new UrdfCylinder({\r
+        xml : shape\r
+      });\r
+    } else if (type === 'mesh') {\r
+      this.geometry = new UrdfMesh({\r
+        xml : shape\r
+      });\r
+    } else {\r
+      console.warn('Unknown geometry type ' + type);\r
+    }\r
+  }\r
+\r
+  // Material\r
+  var materials = xml.getElementsByTagName('material');\r
+  if (materials.length > 0) {\r
+    this.material = new UrdfMaterial({\r
+      xml : materials[0]\r
+    });\r
+  }\r
+}\r
+\r
+module.exports = UrdfVisual;\r
+},{"../math/Pose":19,"../math/Quaternion":20,"../math/Vector3":22,"./UrdfBox":27,"./UrdfCylinder":29,"./UrdfMaterial":32,"./UrdfMesh":33,"./UrdfSphere":35}],38:[function(require,module,exports){\r
+module.exports = require('object-assign')({\r
+    UrdfBox: require('./UrdfBox'),\r
+    UrdfColor: require('./UrdfColor'),\r
+    UrdfCylinder: require('./UrdfCylinder'),\r
+    UrdfLink: require('./UrdfLink'),\r
+    UrdfMaterial: require('./UrdfMaterial'),\r
+    UrdfMesh: require('./UrdfMesh'),\r
+    UrdfModel: require('./UrdfModel'),\r
+    UrdfSphere: require('./UrdfSphere'),\r
+    UrdfVisual: require('./UrdfVisual')\r
+}, require('./UrdfTypes'));\r
+\r
+},{"./UrdfBox":27,"./UrdfColor":28,"./UrdfCylinder":29,"./UrdfLink":31,"./UrdfMaterial":32,"./UrdfMesh":33,"./UrdfModel":34,"./UrdfSphere":35,"./UrdfTypes":36,"./UrdfVisual":37,"object-assign":2}],39:[function(require,module,exports){\r
+(function (global){\r
+module.exports = global.WebSocket;\r
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})\r
+},{}],40:[function(require,module,exports){\r
+/* global document */\r
+module.exports = function Canvas() {\r
+       return document.createElement('canvas');\r
+};\r
+},{}],41:[function(require,module,exports){\r
+(function (global){\r
+/**\r
+ * @fileOverview\r
+ * @author Graeme Yeates - github.com/megawac\r
+ */\r
+\r
+'use strict';\r
+\r
+var Canvas = require('canvas');\r
+var Image = Canvas.Image || global.Image;\r
+\r
+/**\r
+ * If a message was compressed as a PNG image (a compression hack since\r
+ * gzipping over WebSockets * is not supported yet), this function places the\r
+ * "image" in a canvas element then decodes the * "image" as a Base64 string.\r
+ *\r
+ * @private\r
+ * @param data - object containing the PNG data.\r
+ * @param callback - function with params:\r
+ *   * data - the uncompressed data\r
+ */\r
+function decompressPng(data, callback) {\r
+  // Uncompresses the data before sending it through (use image/canvas to do so).\r
+  var image = new Image();\r
+  image.crossOrigin = "Anonymous";\r
+  // When the image loads, extracts the raw data (JSON message).\r
+  image.onload = function() {\r
+    // Creates a local canvas to draw on.\r
+    var canvas = new Canvas();\r
+    var context = canvas.getContext('2d');\r
+\r
+    // Sets width and height.\r
+    canvas.width = image.width;\r
+    canvas.height = image.height;\r
+\r
+    // Prevents anti-aliasing and loosing data\r
+    context.imageSmoothingEnabled = false;\r
+    context.webkitImageSmoothingEnabled = false;\r
+    context.mozImageSmoothingEnabled = false;\r
+\r
+    // Puts the data into the image.\r
+    context.drawImage(image, 0, 0);\r
+    // Grabs the raw, uncompressed data.\r
+    var imageData = context.getImageData(0, 0, image.width, image.height).data;\r
+\r
+    // Constructs the JSON.\r
+    var jsonData = '';\r
+    for (var i = 0; i < imageData.length; i += 4) {\r
+      // RGB\r
+      jsonData += String.fromCharCode(imageData[i], imageData[i + 1], imageData[i + 2]);\r
+    }\r
+    callback(JSON.parse(jsonData));\r
+  };\r
+  // Sends the image data to load.\r
+  image.src = 'data:image/png;base64,' + data;\r
+}\r
+\r
+module.exports = decompressPng;\r
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})\r
+},{"canvas":40}],42:[function(require,module,exports){\r
+(function (global){\r
+exports.DOMImplementation = global.DOMImplementation;\r
+exports.XMLSerializer = global.XMLSerializer;\r
+exports.DOMParser = global.DOMParser;\r
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})\r
+},{}]},{},[4]);\r
diff --git a/www/assets/stylesheets/images/ui-icons_444444_256x240.png b/www/assets/stylesheets/images/ui-icons_444444_256x240.png
new file mode 100644 (file)
index 0000000..c2daae1
Binary files /dev/null and b/www/assets/stylesheets/images/ui-icons_444444_256x240.png differ
diff --git a/www/assets/stylesheets/images/ui-icons_555555_256x240.png b/www/assets/stylesheets/images/ui-icons_555555_256x240.png
new file mode 100644 (file)
index 0000000..4784928
Binary files /dev/null and b/www/assets/stylesheets/images/ui-icons_555555_256x240.png differ
diff --git a/www/assets/stylesheets/images/ui-icons_777620_256x240.png b/www/assets/stylesheets/images/ui-icons_777620_256x240.png
new file mode 100644 (file)
index 0000000..d2f58d2
Binary files /dev/null and b/www/assets/stylesheets/images/ui-icons_777620_256x240.png differ
diff --git a/www/assets/stylesheets/images/ui-icons_777777_256x240.png b/www/assets/stylesheets/images/ui-icons_777777_256x240.png
new file mode 100644 (file)
index 0000000..1d53258
Binary files /dev/null and b/www/assets/stylesheets/images/ui-icons_777777_256x240.png differ
diff --git a/www/assets/stylesheets/images/ui-icons_cc0000_256x240.png b/www/assets/stylesheets/images/ui-icons_cc0000_256x240.png
new file mode 100644 (file)
index 0000000..2825f20
Binary files /dev/null and b/www/assets/stylesheets/images/ui-icons_cc0000_256x240.png differ
diff --git a/www/assets/stylesheets/images/ui-icons_ffffff_256x240.png b/www/assets/stylesheets/images/ui-icons_ffffff_256x240.png
new file mode 100644 (file)
index 0000000..136a4f9
Binary files /dev/null and b/www/assets/stylesheets/images/ui-icons_ffffff_256x240.png differ
index 7a86765..253c35d 100644 (file)
                                        </div>
                                </div>
                                <div id="drive" class="tab-pane" role="tabpanel">
-                                       <div class="cmd_vel_circle"></div>
+                                       <div class="row">
+                                               <div class="col-sd-10">
+                                                       <div class="cmd_vel_circle"></div>
+                                               </div>
+                                               <div class="col-sd-1">
+                                                       <input type="number" value="1.0" min="0.1" max="5.0" step="0.1" class="form-control" id="scale_trans">
+                                                       <input type="number" value="3.0" min="1.0" max="5.0" step="0.1" class="form-control" id="scale_rot">
+                                               </div>
+                                       </div>
                                </div>
                                <div id="image" class="tab-pane" role="tabpanel">
                                        <img id="usb_cam" src="http://wildthumper:8080/stream?topic=/usb_cam/image_raw" style="transform:rotate(-90deg); transform-origin: 60% 60%;"></img>