diff --git a/doc/contributions.txt b/doc/contributions.txt
index a3c139a7a2bd19ff679454d99004326ec7cdc2fc..68ff7a13d2fe5bf51e08783117832fc314fac4e0 100644
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -49,6 +49,7 @@ Aimee Trescothick
 	VWR-12631
 	VWR-12696
 	VWR-12748
+	VWR-13221
 	VWR-14087
 	VWR-14267
 	VWR-14278
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 9d911777d12f2000448e790b61cdc7a9491b387f..46ae879aeca2f3922c1873903cf022f66f20a4d4 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -4669,6 +4669,17 @@
       <key>Value</key>
       <integer>1</integer>
     </map>
+    <key>MiniMapAutoCenter</key>
+    <map>
+      <key>Comment</key>
+      <string>Center the focal point of the minimap.</string>
+      <key>Persist</key>
+      <integer>0</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>1</integer>
+    </map>
     <key>Marker</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp
index eedadb962f7e54aaa65267a04f8c968ab1ada59f..b9ae976e58f2c59bb42bd585283a060910524490 100644
--- a/indra/newview/llavataractions.cpp
+++ b/indra/newview/llavataractions.cpp
@@ -689,6 +689,7 @@ void LLAvatarActions::toggleBlock(const LLUUID& id)
 		LLMuteList::getInstance()->add(mute);
 	}
 }
+
 // static
 bool LLAvatarActions::canOfferTeleport(const LLUUID& id)
 {
@@ -704,6 +705,21 @@ bool LLAvatarActions::canOfferTeleport(const LLUUID& id)
 	return true;
 }
 
+// static
+bool LLAvatarActions::canOfferTeleport(const uuid_vec_t& ids)
+{
+	bool result = true;
+	for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it)
+	{
+		if(!canOfferTeleport(*it))
+		{
+			result = false;
+			break;
+		}
+	}
+	return result;
+}
+
 void LLAvatarActions::inviteToGroup(const LLUUID& id)
 {
 	LLFloaterGroupPicker* widget = LLFloaterReg::showTypedInstance<LLFloaterGroupPicker>("group_picker", LLSD(id));
diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h
index cd8ac3b653691a408311f09adc65c001b679885e..6313ae075931c75d8216223ff27521736891d9f5 100644
--- a/indra/newview/llavataractions.h
+++ b/indra/newview/llavataractions.h
@@ -171,12 +171,18 @@ public:
 	static void csr(const LLUUID& id, std::string name);
 
 	/**
-	 * Checks whether can offer teleport to the avatar
-	 * Can't offer only for offline friends
+	 * Checks whether we can offer a teleport to the avatar, only offline friends
+	 * cannot be offered a teleport.
+	 *
+	 * @return false if avatar is a friend and not visibly online
 	 */
 	static bool canOfferTeleport(const LLUUID& id);
 
-	
+	/**
+	 * @return false if any one of the specified avatars a friend and not visibly online
+	 */
+	static bool canOfferTeleport(const uuid_vec_t& ids);
+
 private:
 	static bool callbackAddFriend(const LLSD& notification, const LLSD& response);
 	static bool callbackAddFriendWithMessage(const LLSD& notification, const LLSD& response);
diff --git a/indra/newview/llfloatermap.cpp b/indra/newview/llfloatermap.cpp
index 0f8b709f29dd4657206af1f323de1a5c468ffa08..c9d7eff02b95d124b5628c2b17b2b81b22d8178f 100644
--- a/indra/newview/llfloatermap.cpp
+++ b/indra/newview/llfloatermap.cpp
@@ -47,7 +47,10 @@
 //
 // Constants
 //
-const F32 MAP_MINOR_DIR_THRESHOLD = 0.08f;
+
+// The minor cardinal direction labels are hidden if their height is more
+// than this proportion of the map.
+const F32 MAP_MINOR_DIR_THRESHOLD = 0.07f;
 const S32 MAP_PADDING_LEFT = 0;
 const S32 MAP_PADDING_TOP = 2;
 const S32 MAP_PADDING_RIGHT = 2;
@@ -93,7 +96,7 @@ BOOL LLFloaterMap::postBuild()
 	mTextBoxNorthWest = getChild<LLTextBox> ("floater_map_northwest");
 
 	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
-	
+
 	registrar.add("Minimap.Zoom", boost::bind(&LLFloaterMap::handleZoom, this, _2));
 	registrar.add("Minimap.Tracker", boost::bind(&LLFloaterMap::handleStopTracking, this, _2));
 
@@ -255,7 +258,7 @@ void LLFloaterMap::reshape(S32 width, S32 height, BOOL called_from_parent)
 void LLFloaterMap::handleZoom(const LLSD& userdata)
 {
 	std::string level = userdata.asString();
-	
+
 	F32 scale = 0.0f;
 	if (level == std::string("close"))
 		scale = LLNetMap::MAP_SCALE_MAX;
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
index e4a96cca145bfcd913cd53e02c9edbe43cae8c85..6db8001d57a1d8130d3c414ebbebc8aabeace7e9 100644
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -55,6 +55,7 @@
 #include "llviewermenu.h"
 #include "llviewerobjectlist.h"
 #include "llviewerregion.h"
+#include "llviewerwindow.h"
 #include "llworld.h"
 #include "llworldmapview.h"		// shared draw code
 
@@ -69,6 +70,7 @@ const F32 MAP_SCALE_ZOOM_FACTOR = 1.04f; // Zoom in factor per click of scroll w
 const F32 MIN_DOT_RADIUS = 3.5f;
 const F32 DOT_SCALE = 0.75f;
 const F32 MIN_PICK_SCALE = 2.f;
+const S32 MOUSE_DRAG_SLOP = 2;		// How far the mouse needs to move before we think it's a drag
 
 LLNetMap::LLNetMap (const Params & p)
 :	LLUICtrl (p),
@@ -77,11 +79,12 @@ LLNetMap::LLNetMap (const Params & p)
 	mPixelsPerMeter( MAP_SCALE_MID / REGION_WIDTH_METERS ),
 	mObjectMapTPM(0.f),
 	mObjectMapPixels(0.f),
-	mTargetPanX(0.f),
-	mTargetPanY(0.f),
-	mCurPanX(0.f),
-	mCurPanY(0.f),
-	mUpdateNow(FALSE),
+	mTargetPan(0.f, 0.f),
+	mCurPan(0.f, 0.f),
+	mStartPan(0.f, 0.f),
+	mMouseDown(0, 0),
+	mPanning(false),
+	mUpdateNow(false),
 	mObjectImageCenterGlobal( gAgentCamera.getCameraPositionGlobal() ),
 	mObjectRawImagep(),
 	mObjectImagep(),
@@ -98,7 +101,9 @@ LLNetMap::~LLNetMap()
 
 void LLNetMap::setScale( F32 scale )
 {
-	mScale = llclamp(scale, 0.1f, 16.f*1024.f); // [reasonably small , unreasonably large]
+	scale = llclamp(scale, MAP_SCALE_MIN, MAP_SCALE_MAX);
+	mCurPan *= scale / mScale;
+	mScale = scale;
 	
 	if (mObjectImagep.notNull())
 	{
@@ -115,13 +120,7 @@ void LLNetMap::setScale( F32 scale )
 	mPixelsPerMeter = mScale / REGION_WIDTH_METERS;
 	mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS);
 
-	mUpdateNow = TRUE;
-}
-
-void LLNetMap::translatePan( F32 delta_x, F32 delta_y )
-{
-	mTargetPanX += delta_x;
-	mTargetPanY += delta_y;
+	mUpdateNow = true;
 }
 
 
@@ -141,9 +140,12 @@ void LLNetMap::draw()
 	{
 		createObjectImage();
 	}
-	
-	mCurPanX = lerp(mCurPanX, mTargetPanX, LLCriticalDamp::getInterpolant(0.1f));
-	mCurPanY = lerp(mCurPanY, mTargetPanY, LLCriticalDamp::getInterpolant(0.1f));
+
+	static LLUICachedControl<bool> auto_center("MiniMapAutoCenter", true);
+	if (auto_center)
+	{
+		mCurPan = lerp(mCurPan, mTargetPan, LLCriticalDamp::getInterpolant(0.1f));
+	}
 
 	// Prepare a scissor region
 	F32 rotation = 0;
@@ -174,8 +176,8 @@ void LLNetMap::draw()
 		}
 
 		// region 0,0 is in the middle
-		S32 center_sw_left = getRect().getWidth() / 2 + llfloor(mCurPanX);
-		S32 center_sw_bottom = getRect().getHeight() / 2 + llfloor(mCurPanY);
+		S32 center_sw_left = getRect().getWidth() / 2 + llfloor(mCurPan.mV[VX]);
+		S32 center_sw_bottom = getRect().getHeight() / 2 + llfloor(mCurPan.mV[VY]);
 
 		gGL.pushMatrix();
 
@@ -256,26 +258,24 @@ void LLNetMap::draw()
 			}
 			gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
 		}
-		
-
-		LLVector3d old_center = mObjectImageCenterGlobal;
-		LLVector3d new_center = gAgentCamera.getCameraPositionGlobal();
-
-		new_center.mdV[0] = (5.f/mObjectMapTPM)*floor(0.2f*mObjectMapTPM*new_center.mdV[0]);
-		new_center.mdV[1] = (5.f/mObjectMapTPM)*floor(0.2f*mObjectMapTPM*new_center.mdV[1]);
-		new_center.mdV[2] = 0.f;
 
+		// Redraw object layer periodically
 		if (mUpdateNow || (map_timer.getElapsedTimeF32() > 0.5f))
 		{
-			mUpdateNow = FALSE;
-			mObjectImageCenterGlobal = new_center;
+			mUpdateNow = false;
+
+			// Locate the centre of the object layer, accounting for panning
+			LLVector3 new_center = globalPosToView(gAgentCamera.getCameraPositionGlobal());
+			new_center.mV[VX] -= mCurPan.mV[VX];
+			new_center.mV[VY] -= mCurPan.mV[VY];
+			new_center.mV[VZ] = 0.f;
+			mObjectImageCenterGlobal = viewPosToGlobal(llfloor(new_center.mV[VX]), llfloor(new_center.mV[VY]));
 
-			// Center moved enough.
 			// Create the base texture.
 			U8 *default_texture = mObjectRawImagep->getData();
 			memset( default_texture, 0, mObjectImagep->getWidth() * mObjectImagep->getHeight() * mObjectImagep->getComponents() );
 
-			// Draw buildings
+			// Draw objects
 			gObjectList.renderObjectsForMap(*this);
 
 			mObjectImagep->setSubImage(mObjectRawImagep, 0, 0, mObjectImagep->getWidth(), mObjectImagep->getHeight());
@@ -361,7 +361,8 @@ void LLNetMap::draw()
 					show_as_friend ? map_avatar_friend_color : map_avatar_color, 
 					pos_map.mV[VZ], mDotRadius);
 
-				F32	dist_to_cursor = dist_vec(LLVector2(pos_map.mV[VX], pos_map.mV[VY]), LLVector2(local_mouse_x,local_mouse_y));
+				F32	dist_to_cursor = dist_vec(LLVector2(pos_map.mV[VX], pos_map.mV[VY]),
+											  LLVector2(local_mouse_x,local_mouse_y));
 				if(dist_to_cursor < min_pick_dist && dist_to_cursor < closest_dist)
 				{
 					closest_dist = dist_to_cursor;
@@ -392,12 +393,22 @@ void LLNetMap::draw()
 		// Draw dot for self avatar position
 		pos_global = gAgent.getPositionGlobal();
 		pos_map = globalPosToView(pos_global);
-		LLUIImagePtr you = LLWorldMapView::sAvatarYouLargeImage;
 		S32 dot_width = llround(mDotRadius * 2.f);
-		you->draw(llround(pos_map.mV[VX] - mDotRadius),
-				  llround(pos_map.mV[VY] - mDotRadius),
-				  dot_width,
-				  dot_width);
+		LLUIImagePtr you = LLWorldMapView::sAvatarYouLargeImage;
+		if (you)
+		{
+			you->draw(llround(pos_map.mV[VX] - mDotRadius),
+					  llround(pos_map.mV[VY] - mDotRadius),
+					  dot_width,
+					  dot_width);
+
+			F32	dist_to_cursor = dist_vec(LLVector2(pos_map.mV[VX], pos_map.mV[VY]),
+										  LLVector2(local_mouse_x,local_mouse_y));
+			if(dist_to_cursor < min_pick_dist && dist_to_cursor < closest_dist)
+			{
+				mClosestAgentToCursor = gAgent.getID();
+			}
+		}
 
 		// Draw frustum
 		F32 meters_to_pixels = mScale/ LLWorld::getInstance()->getRegionWidthInMeters();
@@ -472,8 +483,8 @@ LLVector3 LLNetMap::globalPosToView( const LLVector3d& global_pos )
 		pos_local.rotVec( rot );
 	}
 
-	pos_local.mV[VX] += getRect().getWidth() / 2 + mCurPanX;
-	pos_local.mV[VY] += getRect().getHeight() / 2 + mCurPanY;
+	pos_local.mV[VX] += getRect().getWidth() / 2 + mCurPan.mV[VX];
+	pos_local.mV[VY] += getRect().getHeight() / 2 + mCurPan.mV[VY];
 
 	return pos_local;
 }
@@ -506,8 +517,8 @@ void LLNetMap::drawTracking(const LLVector3d& pos_global, const LLColor4& color,
 
 LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y )
 {
-	x -= llround(getRect().getWidth() / 2 + mCurPanX);
-	y -= llround(getRect().getHeight() / 2 + mCurPanY);
+	x -= llround(getRect().getWidth() / 2 + mCurPan.mV[VX]);
+	y -= llround(getRect().getHeight() / 2 + mCurPan.mV[VY]);
 
 	LLVector3 pos_local( (F32)x, (F32)y, 0 );
 
@@ -532,10 +543,20 @@ LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y )
 BOOL LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks)
 {
 	// note that clicks are reversed from what you'd think: i.e. > 0  means zoom out, < 0 means zoom in
-	F32 scale = mScale;
-        
-	scale *= pow(MAP_SCALE_ZOOM_FACTOR, -clicks);
-	setScale(llclamp(scale, MAP_SCALE_MIN, MAP_SCALE_MAX));
+	F32 new_scale = mScale * pow(MAP_SCALE_ZOOM_FACTOR, -clicks);
+	F32 old_scale = mScale;
+
+	setScale(new_scale);
+
+	static LLUICachedControl<bool> auto_center("MiniMapAutoCenter", true);
+	if (!auto_center)
+	{
+		// Adjust pan to center the zoom on the mouse pointer
+		LLVector2 zoom_offset;
+		zoom_offset.mV[VX] = x - getRect().getWidth() / 2;
+		zoom_offset.mV[VY] = y - getRect().getHeight() / 2;
+		mCurPan -= zoom_offset * mScale / old_scale - zoom_offset;
+	}
 
 	return TRUE;
 }
@@ -546,20 +567,32 @@ BOOL LLNetMap::handleToolTip( S32 x, S32 y, MASK mask )
 	{
 		return FALSE;
 	}
-	
-	// mToolTipMsg = "[AGENT][REGION](Double-click to open Map)"
-	
-	LLStringUtil::format_map_t args;
-	std::string fullname;
-	if(mClosestAgentToCursor.notNull() && gCacheName->getFullName(mClosestAgentToCursor, fullname))
-	{
-		args["[AGENT]"] = fullname + "\n";
-	}
-	else
+
+	std::string avatar_name;
+	if(mClosestAgentToCursor.notNull() && gCacheName->getFullName(mClosestAgentToCursor, avatar_name))
 	{
-		args["[AGENT]"] = "";
+		// only show tooltip if same inspector not already open
+		LLFloater* existing_inspector = LLFloaterReg::findInstance("inspect_avatar");
+		if (!existing_inspector 
+			|| !existing_inspector->getVisible()
+			|| existing_inspector->getKey()["avatar_id"].asUUID() != mClosestAgentToCursor)
+		{
+			LLInspector::Params p;
+			p.fillFrom(LLUICtrlFactory::instance().getDefaultParams<LLInspector>());
+			p.message(avatar_name);
+			p.image.name("Inspector_I");
+			p.click_callback(boost::bind(showAvatarInspector, mClosestAgentToCursor));
+			p.visible_time_near(6.f);
+			p.visible_time_far(3.f);
+			p.delay_time(0.35f);
+			p.wrap(false);
+
+			LLToolTipMgr::instance().show(p);
+		}
+		return TRUE;
 	}
-	
+
+	LLStringUtil::format_map_t args;
 	LLViewerRegion*	region = LLWorld::getInstance()->getRegionFromPosGlobal( viewPosToGlobal( x, y ) );
 	if( region )
 	{
@@ -569,10 +602,10 @@ BOOL LLNetMap::handleToolTip( S32 x, S32 y, MASK mask )
 	{
 		args["[REGION]"] = "";
 	}
-	
+
 	std::string msg = mToolTipMsg;
 	LLStringUtil::format(msg, args);
-	
+
 	LLRect sticky_rect;
 	// set sticky_rect
 	if (region)
@@ -592,6 +625,21 @@ BOOL LLNetMap::handleToolTip( S32 x, S32 y, MASK mask )
 	return TRUE;
 }
 
+// static
+void LLNetMap::showAvatarInspector(const LLUUID& avatar_id)
+{
+	LLSD params;
+	params["avatar_id"] = avatar_id;
+
+	if (LLToolTipMgr::instance().toolTipVisible())
+	{
+		LLRect rect = LLToolTipMgr::instance().getToolTipRect();
+		params["pos"]["x"] = rect.mLeft;
+		params["pos"]["y"] = rect.mTop;
+	}
+
+	LLFloaterReg::showInstance("inspect_avatar", params);
+}
 
 void LLNetMap::renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &color, F32 radius_meters )
 {
@@ -715,5 +763,99 @@ void LLNetMap::createObjectImage()
 		mObjectImagep = LLViewerTextureManager::getLocalTexture( mObjectRawImagep.get(), FALSE);
 	}
 	setScale(mScale);
-	mUpdateNow = TRUE;
+	mUpdateNow = true;
+}
+
+BOOL LLNetMap::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+	if (!(mask & MASK_SHIFT)) return FALSE;
+
+	// Start panning
+	gFocusMgr.setMouseCapture(this);
+
+	mStartPan = mCurPan;
+	mMouseDown.mX = x;
+	mMouseDown.mY = y;
+	return TRUE;
+}
+
+BOOL LLNetMap::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+	if (hasMouseCapture())
+	{
+		if (mPanning)
+		{
+			// restore mouse cursor
+			S32 local_x, local_y;
+			local_x = mMouseDown.mX + llfloor(mCurPan.mV[VX] - mStartPan.mV[VX]);
+			local_y = mMouseDown.mY + llfloor(mCurPan.mV[VY] - mStartPan.mV[VY]);
+			LLRect clip_rect = getRect();
+			clip_rect.stretch(-8);
+			clip_rect.clipPointToRect(mMouseDown.mX, mMouseDown.mY, local_x, local_y);
+			LLUI::setMousePositionLocal(this, local_x, local_y);
+
+			// finish the pan
+			mPanning = false;
+
+			mMouseDown.set(0, 0);
+
+			// auto centre
+			mTargetPan.setZero();
+		}
+		gViewerWindow->showCursor();
+		gFocusMgr.setMouseCapture(NULL);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+// static
+bool LLNetMap::outsideSlop( S32 x, S32 y, S32 start_x, S32 start_y, S32 slop )
+{
+	S32 dx = x - start_x;
+	S32 dy = y - start_y;
+
+	return (dx <= -slop || slop <= dx || dy <= -slop || slop <= dy);
+}
+
+BOOL LLNetMap::handleHover( S32 x, S32 y, MASK mask )
+{
+	if (hasMouseCapture())
+	{
+		if (mPanning || outsideSlop(x, y, mMouseDown.mX, mMouseDown.mY, MOUSE_DRAG_SLOP))
+		{
+			if (!mPanning)
+			{
+				// just started panning, so hide cursor
+				mPanning = true;
+				gViewerWindow->hideCursor();
+			}
+
+			LLVector2 delta(static_cast<F32>(gViewerWindow->getCurrentMouseDX()),
+							static_cast<F32>(gViewerWindow->getCurrentMouseDY()));
+
+			// Set pan to value at start of drag + offset
+			mCurPan += delta;
+			mTargetPan = mCurPan;
+
+			gViewerWindow->moveCursorToCenter();
+		}
+
+		// Doesn't really matter, cursor should be hidden
+		gViewerWindow->setCursor( UI_CURSOR_TOOLPAN );
+	}
+	else
+	{
+		if (mask & MASK_SHIFT)
+		{
+			// If shift is held, change the cursor to hint that the map can be dragged
+			gViewerWindow->setCursor( UI_CURSOR_TOOLPAN );
+		}
+		else
+		{
+			gViewerWindow->setCursor( UI_CURSOR_CROSS );
+		}
+	}
+
+	return TRUE;
 }
diff --git a/indra/newview/llnetmap.h b/indra/newview/llnetmap.h
index 6808642505824fd0f2ea82b932007d46499d5d0c..e25ada4c95f559039bc0468e69b9361f1c4fd6d4 100644
--- a/indra/newview/llnetmap.h
+++ b/indra/newview/llnetmap.h
@@ -37,7 +37,6 @@
 class LLColor4U;
 class LLCoordGL;
 class LLImageRaw;
-class LLTextBox;
 class LLViewerTexture;
 
 class LLNetMap : public LLUICtrl
@@ -66,17 +65,17 @@ public:
 
 	/*virtual*/ void	draw();
 	/*virtual*/ BOOL	handleScrollWheel(S32 x, S32 y, S32 clicks);
+	/*virtual*/ BOOL	handleMouseDown(S32 x, S32 y, MASK mask);
+	/*virtual*/ BOOL	handleMouseUp(S32 x, S32 y, MASK mask);
+	/*virtual*/ BOOL	handleHover( S32 x, S32 y, MASK mask );
 	/*virtual*/ BOOL	handleToolTip( S32 x, S32 y, MASK mask);
 	/*virtual*/ void	reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
 	
 	void			setScale( F32 scale );
 	void			setToolTipMsg(const std::string& msg) { mToolTipMsg = msg; }
 	void			renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &color, F32 radius );
-	
-private:
-	void			translatePan( F32 delta_x, F32 delta_y );
-	void			setPan( F32 x, F32 y )			{ mTargetPanX = x; mTargetPanY = y; }
 
+private:
 	const LLVector3d& getObjectImageCenterGlobal()	{ return mObjectImageCenterGlobal; }
 	void 			renderPoint(const LLVector3 &pos, const LLColor4U &color, 
 								S32 diameter, S32 relative_height = 0);
@@ -87,10 +86,15 @@ private:
 	void			drawTracking( const LLVector3d& pos_global, 
 								  const LLColor4& color,
 								  BOOL draw_arrow = TRUE);
-	
+	static void		showAvatarInspector(const LLUUID& avatar_id);
+
 	void			createObjectImage();
 
 private:
+	static bool		outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y, S32 slop);
+
+	bool			mUpdateNow;
+
 	LLUIColor		mBackgroundColor;
 
 	F32				mScale;					// Size of a region in pixels
@@ -98,11 +102,13 @@ private:
 	F32				mObjectMapTPM;			// texels per meter on map
 	F32				mObjectMapPixels;		// Width of object map in pixels
 	F32				mDotRadius;				// Size of avatar markers
-	F32				mTargetPanX;
-	F32				mTargetPanY;
-	F32				mCurPanX;
-	F32				mCurPanY;
-	BOOL			mUpdateNow;
+
+	bool			mPanning;			// map is being dragged
+	LLVector2		mTargetPan;
+	LLVector2		mCurPan;
+	LLVector2		mStartPan;		// pan offset at start of drag
+	LLCoordGL		mMouseDown;			// pointer position at start of drag
+
 	LLVector3d		mObjectImageCenterGlobal;
 	LLPointer<LLImageRaw> mObjectRawImagep;
 	LLPointer<LLViewerTexture>	mObjectImagep;
diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp
index 8627274e8006691bf695dabbe23bb7448de18130..06ba08b51c9ca4b90a8cca81a50aa767552e2ba7 100644
--- a/indra/newview/llpanelpeople.cpp
+++ b/indra/newview/llpanelpeople.cpp
@@ -776,12 +776,6 @@ void LLPanelPeople::buttonSetAction(const std::string& btn_name, const commit_si
 	button->setClickedCallback(cb);
 }
 
-bool LLPanelPeople::isFriendOnline(const LLUUID& id)
-{
-	uuid_vec_t ids = mOnlineFriendList->getIDs();
-	return std::find(ids.begin(), ids.end(), id) != ids.end();
-}
-
 void LLPanelPeople::updateButtons()
 {
 	std::string cur_tab		= getActiveTabName();
@@ -843,11 +837,11 @@ void LLPanelPeople::updateButtons()
 
 	bool enable_calls = LLVoiceClient::getInstance()->isVoiceWorking() && LLVoiceClient::getInstance()->voiceEnabled();
 
-	buttonSetEnabled("teleport_btn",		friends_tab_active && item_selected && isFriendOnline(selected_uuids.front()));
-	buttonSetEnabled("view_profile_btn",	item_selected);
-	buttonSetEnabled("im_btn",				multiple_selected); // allow starting the friends conference for multiple selection
-	buttonSetEnabled("call_btn",			multiple_selected && enable_calls);
-	buttonSetEnabled("share_btn",			item_selected); // not implemented yet
+	buttonSetEnabled("view_profile_btn",item_selected);
+	buttonSetEnabled("share_btn",		item_selected);
+	buttonSetEnabled("im_btn",			multiple_selected); // allow starting the friends conference for multiple selection
+	buttonSetEnabled("call_btn",		multiple_selected && enable_calls);
+	buttonSetEnabled("teleport_btn",	multiple_selected && LLAvatarActions::canOfferTeleport(selected_uuids));
 
 	bool none_group_selected = item_selected && selected_id.isNull();
 	buttonSetEnabled("group_info_btn", !none_group_selected);
@@ -1328,7 +1322,9 @@ void LLPanelPeople::onGroupCallButtonClicked()
 
 void LLPanelPeople::onTeleportButtonClicked()
 {
-	LLAvatarActions::offerTeleport(getCurrentItemID());
+	uuid_vec_t selected_uuids;
+	getCurrentItemIDs(selected_uuids);
+	LLAvatarActions::offerTeleport(selected_uuids);
 }
 
 void LLPanelPeople::onShareButtonClicked()
diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h
index 3b8b736be16c1c02f7fc65e7f614a324a133ddcb..d0913ee756c7740bfc0b5a8a39951534791462d5 100644
--- a/indra/newview/llpanelpeople.h
+++ b/indra/newview/llpanelpeople.h
@@ -72,7 +72,6 @@ private:
 	void					updateNearbyList();
 	void					updateRecentList();
 
-	bool					isFriendOnline(const LLUUID& id);
 	bool					isItemsFreeOfFriends(const uuid_vec_t& uuids);
 
 	void					updateButtons();
diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp
index efca3ae1c2c9cd84fae733fb08ddafad97afded4..f12c4de2f764894f25b6230df4c6337e55a1ebf7 100644
--- a/indra/newview/llpanelpeoplemenus.cpp
+++ b/indra/newview/llpanelpeoplemenus.cpp
@@ -81,6 +81,7 @@ LLContextMenu* NearbyMenu::createMenu()
 		// registrar.add("Avatar.AddFriend",	boost::bind(&LLAvatarActions::requestFriendshipDialog,	mUUIDs)); // *TODO: unimplemented
 		registrar.add("Avatar.IM",			boost::bind(&LLAvatarActions::startConference,			mUUIDs));
 		registrar.add("Avatar.Call",		boost::bind(&LLAvatarActions::startAdhocCall,			mUUIDs));
+		registrar.add("Avatar.OfferTeleport",	boost::bind(&NearbyMenu::offerTeleport,					this));
 		registrar.add("Avatar.RemoveFriend",boost::bind(&LLAvatarActions::removeFriendsDialog,		mUUIDs));
 		// registrar.add("Avatar.Share",		boost::bind(&LLAvatarActions::startIM,					mUUIDs)); // *TODO: unimplemented
 		// registrar.add("Avatar.Pay",		boost::bind(&LLAvatarActions::pay,						mUUIDs)); // *TODO: unimplemented
@@ -168,8 +169,7 @@ bool NearbyMenu::enableContextMenuItem(const LLSD& userdata)
 	}
 	else if(item == std::string("can_offer_teleport"))
 	{
-		const LLUUID& id = mUUIDs.front();
-		return LLAvatarActions::canOfferTeleport(id);
+		return LLAvatarActions::canOfferTeleport(mUUIDs);
 	}
 	return false;
 }
@@ -191,8 +191,7 @@ void NearbyMenu::offerTeleport()
 {
 	// boost::bind cannot recognize overloaded method LLAvatarActions::offerTeleport(),
 	// so we have to use a wrapper.
-	const LLUUID& id = mUUIDs.front();
-	LLAvatarActions::offerTeleport(id);
+	LLAvatarActions::offerTeleport(mUUIDs);
 }
 
 } // namespace LLPanelPeopleMenus
diff --git a/indra/newview/skins/default/xui/en/floater_map.xml b/indra/newview/skins/default/xui/en/floater_map.xml
index efd96624ab0026d0cbf298824050fd6fadbca19e..6370ff92438cf701c45dc4bcd88d2a704098dbaf 100644
--- a/indra/newview/skins/default/xui/en/floater_map.xml
+++ b/indra/newview/skins/default/xui/en/floater_map.xml
@@ -7,8 +7,8 @@
  follows="top|right"
  height="174"
  layout="topleft"
- min_height="174"
- min_width="174"
+ min_height="128"
+ min_width="128"
  name="Map"
  title=""
  help_topic="map"
@@ -18,41 +18,9 @@
  left="0"
  top="0"
  width="200">
-    <floater.string
-     name="mini_map_north">
-        N
-    </floater.string>
-    <floater.string
-     name="mini_map_east">
-        E
-    </floater.string>
-    <floater.string
-     name="mini_map_west">
-        W
-    </floater.string>
-    <floater.string
-     name="mini_map_south">
-        S
-    </floater.string>
-    <floater.string
-     name="mini_map_southeast">
-        SE
-    </floater.string>
-    <floater.string
-     name="mini_map_northeast">
-        NE
-    </floater.string>
-    <floater.string
-     name="mini_map_southwest">
-        SW
-    </floater.string>
-    <floater.string
-     name="mini_map_northwest">
-        NW
-    </floater.string>
     <floater.string
      name="ToolTipMsg">
-        [AGENT][REGION](Double-click to open Map)
+        [REGION](Double-click to open Map, shift-drag to pan)
     </floater.string>
     <floater.string name="mini_map_caption">
 	MINIMAP
diff --git a/indra/newview/skins/default/xui/en/menu_mini_map.xml b/indra/newview/skins/default/xui/en/menu_mini_map.xml
index f5ea3e735bb86d025340e6c92e0299d2362522f3..8fe89d39343b3b75139bd74ca3ef921f7f4054e4 100644
--- a/indra/newview/skins/default/xui/en/menu_mini_map.xml
+++ b/indra/newview/skins/default/xui/en/menu_mini_map.xml
@@ -29,6 +29,7 @@
          function="Minimap.Zoom"
          parameter="far" />
     </menu_item_call>
+    <menu_item_separator />
     <menu_item_check
        label="Rotate Map"
        name="Rotate Map">
@@ -38,6 +39,15 @@
              function="ToggleControl"
              parameter="MiniMapRotate" />
     </menu_item_check>
+    <menu_item_check
+       label="Auto Center"
+       name="Auto Center">
+          <menu_item_check.on_check
+             control="MiniMapAutoCenter" />
+          <menu_item_check.on_click
+             function="ToggleControl"
+             parameter="MiniMapAutoCenter" />
+    </menu_item_check>
     <menu_item_separator />
     <menu_item_call
      label="Stop Tracking"
diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby_multiselect.xml b/indra/newview/skins/default/xui/en/menu_people_nearby_multiselect.xml
index 588342595eb960d35fbf67b5beab597492efd692..5d58a9d28957813d94ef9cf42c71bbc375b03a65 100644
--- a/indra/newview/skins/default/xui/en/menu_people_nearby_multiselect.xml
+++ b/indra/newview/skins/default/xui/en/menu_people_nearby_multiselect.xml
@@ -57,4 +57,13 @@
         <on_click
          function="Avatar.Pay" />
     </menu_item_call>
+    <menu_item_call
+    label="Offer Teleport"
+    name="teleport">
+      <menu_item_call.on_click
+       function="Avatar.OfferTeleport"/>
+      <menu_item_call.on_enable
+      function="Avatar.EnableItem"
+      parameter="can_offer_teleport"/>
+    </menu_item_call>
 </context_menu>