diff --git a/src/eventhandler.js b/src/eventhandler.js
index 8e608806..56fbf080 100644
--- a/src/eventhandler.js
+++ b/src/eventhandler.js
@@ -36,7 +36,7 @@
 
 /**
  * For use by classes which want to support custom, non-browser events.
- * TODO: This is an aweful name!  This thing represents an "event source",
+ * TODO: This is an awful name!  This thing represents an "event source",
  *       not an "event handler".  PLEASE change the to EventSource. Also please
  *       change 'addHandler', 'removeHandler' and 'raiseEvent' to 'bind',
  *       'unbind', and 'trigger' respectively.  Finally add a method 'one' which
@@ -55,14 +55,15 @@ $.EventHandler.prototype = {
      * @function
      * @param {String} eventName - Name of event to register.
      * @param {Function} handler - Function to call when event is triggered.
+     * @param {Object} optional userData - Arbitrary object to be passed to the handler.
      */
-    addHandler: function( eventName, handler ) {
+    addHandler: function ( eventName, handler, userData ) {
         var events = this.events[ eventName ];
-        if( !events ){
+        if ( !events ) {
             this.events[ eventName ] = events = [];
         }
-        if( handler && $.isFunction( handler ) ){
-            events[ events.length ] = handler;
+        if ( handler && $.isFunction( handler ) ) {
+            events[ events.length ] = { handler: handler, userData: userData || null };
         }
     },
 
@@ -72,16 +73,16 @@ $.EventHandler.prototype = {
      * @param {String} eventName - Name of event for which the handler is to be removed.
      * @param {Function} handler - Function to be removed.
      */
-    removeHandler: function( eventName, handler ) {
+    removeHandler: function ( eventName, handler ) {
         var events = this.events[ eventName ],
             handlers = [],
             i;
-        if ( !events ){
+        if ( !events ) {
             return;
         }
-        if( $.isArray( events ) ){
-            for( i = 0; i < events.length; i++ ){
-                if( events[ i ] !== handler ){
+        if ( $.isArray( events ) ) {
+            for ( i = 0; i < events.length; i++ ) {
+                if ( events[i].handler !== handler ) {
                     handlers.push( events[ i ] );
                 }
             }
@@ -97,11 +98,11 @@ $.EventHandler.prototype = {
      * @param {String} eventName - Name of event for which all handlers are to be removed.
      */
     removeAllHandlers: function( eventName ) {
-        if (eventName){
+        if ( eventName ){
             this.events[ eventName ] = [];
         } else{
-            for (var eventType in this.events) {
-                this.events[eventType] = [];
+            for ( var eventType in this.events ) {
+                this.events[ eventType ] = [];
             }
         }
     },
@@ -111,20 +112,21 @@ $.EventHandler.prototype = {
      * @function
      * @param {String} eventName - Name of event to get handlers for.
      */
-    getHandler: function( eventName ) {
+    getHandler: function ( eventName ) {
         var events = this.events[ eventName ];
-        if ( !events || !events.length ){
+        if ( !events || !events.length ) {
             return null;
         }
         events = events.length === 1 ?
             [ events[ 0 ] ] :
             Array.apply( null, events );
-        return function( source, args ) {
+        return function ( source, args ) {
             var i,
                 length = events.length;
             for ( i = 0; i < length; i++ ) {
-                if( events[ i ] ){
-                    events[ i ]( source, args );
+                if ( events[ i ] ) {
+                    args.userData = events[ i ].userData;
+                    events[ i ].handler( source, args );
                 }
             }
         };
diff --git a/src/legacytilesource.js b/src/legacytilesource.js
index 7c208e44..6bc9ef89 100644
--- a/src/legacytilesource.js
+++ b/src/legacytilesource.js
@@ -71,17 +71,25 @@ $.LegacyTileSource = function( levels ) {
 
     //clean up the levels to make sure we support all formats
     options.levels = filterFiles( options.levels );
-    width = options.levels[ options.levels.length - 1 ].width;
-    height = options.levels[ options.levels.length - 1 ].height;
 
-    $.extend( true,  options, {
-        width:       width,
-        height:      height,
-        tileSize:    Math.max( height, width ),
+    if ( options.levels.length > 0 ) {
+        width = options.levels[ options.levels.length - 1 ].width;
+        height = options.levels[ options.levels.length - 1 ].height;
+    }
+    else {
+        width = 0;
+        height = 0;
+        $.console.error( "No supported image formats found" );
+    }
+
+    $.extend( true, options, {
+        width: width,
+        height: height,
+        tileSize: Math.max( height, width ),
         tileOverlap: 0,
-        minLevel:    0,
-        maxLevel:    options.levels.length - 1
-    });
+        minLevel: 0,
+        maxLevel: options.levels.length > 0 ? options.levels.length - 1 : 0
+    } );
 
     $.TileSource.apply( this, [ options ] );
 
@@ -139,9 +147,9 @@ $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, {
      * @name OpenSeadragon.LegacyTileSource.prototype.getLevelScale
      * @param {Number} level
      */
-    getLevelScale: function( level ) {
+    getLevelScale: function ( level ) {
         var levelScale = NaN;
-        if (  level >= this.minLevel && level <= this.maxLevel ){
+        if ( this.levels.length > 0 && level >= this.minLevel && level <= this.maxLevel ) {
             levelScale =
                 this.levels[ level ].width /
                 this.levels[ this.maxLevel ].width;
@@ -186,14 +194,14 @@ $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, {
      * @param {Number} y
      * @throws {Error}
      */
-    getTileUrl: function( level, x, y ) {
+    getTileUrl: function ( level, x, y ) {
         var url = null;
-        if( level >= this.minLevel && level <= this.maxLevel ){
+        if ( this.levels.length > 0 && level >= this.minLevel && level <= this.maxLevel ) {
             url = this.levels[ level ].url;
         }
         return url;
     }
-});
+} );
 
 /**
  * This method removes any files from the Array which dont conform to our
@@ -223,6 +231,9 @@ function filterFiles( files ){
                 height: Number( file.height )
             });
         }
+        else {
+            $.console.error( 'Unsupported image format: %s', file.url ? file.url : '<no URL>' );
+        }
     }
 
     return filtered.sort(function(a,b){
diff --git a/src/tilesource.js b/src/tilesource.js
index fb22b21c..923806b2 100644
--- a/src/tilesource.js
+++ b/src/tilesource.js
@@ -114,12 +114,19 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
 
     //Any functions that are passed as arguments are bound to the ready callback
     /*jshint loopfunc:true*/
-    for( i = 0; i < arguments.length; i++ ){
-        if( $.isFunction( arguments[i] ) ){
+    for ( i = 0; i < arguments.length; i++ ) {
+        if ( $.isFunction( arguments[ i ] ) ) {
             callback = arguments[ i ];
-            this.addHandler( 'ready', function( placeHolderSource, readySource ){
+            // TODO Send generic object wrapping readySource as a property (breaking change)
+            // TODO Maybe placeHolderSource should be passed to callback as well for consistency
+            //      with event handler signature?
+            //  Should be this (although technically it works as-is):
+            //this.addHandler( 'ready', function ( placeHolderSource, placeHolderArgs ) {
+            //    callback( placeHolderArgs );
+            //} );
+            this.addHandler( 'ready', function ( placeHolderSource, readySource ) {
                 callback( readySource );
-            });
+            } );
             //only one callback per constructor
             break;
         }
@@ -301,6 +308,9 @@ $.TileSource.prototype = {
             options = $TileSource.prototype.configure.apply( _this, [ data, url ]);
             readySource = new $TileSource( options );
             _this.ready = true;
+            // TODO Send generic object wrapping readySource as a property (breaking change)
+            //  Should be this:
+            //_this.raiseEvent( 'ready', { tileSource: readySource } );
             _this.raiseEvent( 'ready', readySource );
         };
 
diff --git a/src/viewer.js b/src/viewer.js
index d9b02708..f1880afc 100644
--- a/src/viewer.js
+++ b/src/viewer.js
@@ -1463,7 +1463,7 @@ function onCanvasDrag( tracker, position, delta, shift ) {
             this.viewport.applyConstraints();
         }
     }
-    this.raiseEvent( 'canvas-click', {
+    this.raiseEvent( 'canvas-drag', {
         tracker: tracker,
         position: position,
         delta: delta,