diff --git a/doc/contributions.txt b/doc/contributions.txt
index 9df072e937ebdcaa8f7d86199cb7ab46abf6701e..3cb9b4c00fd2f91c45316f20d7c58eddcc34cf1e 100644
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -632,6 +632,8 @@ Tharax Ferraris
 	VWR-605
 Thickbrick Sleaford
 	SNOW-207
+	SNOW-421
+	SNOW-462
 	SNOW-743
 	VWR-7109
 	VWR-9287
@@ -651,6 +653,8 @@ Tue Torok
 	CT-72
 	CT-73
 	CT-74
+Twisted Laws
+	SNOW-352
 Vadim Bigbear
 	VWR-2681
 Vixen Heron
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 07418d1b5e98354beae9e369027c4caf37ea9b65..2707ef3339b166413ed2194e0f53693c6fb0abcd 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -2677,6 +2677,17 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
+    <key>DoubleClickTeleport</key>
+    <map>
+      <key>Comment</key>
+      <string>Enable double-click to teleport where allowed</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
     <key>DragAndDropToolTipDelay</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 37cd289b1ba9522cd3da8895e16c4cc661755379..1819edfd755408c6008c95599fedba4756188123 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -167,6 +167,7 @@ LLAgent::LLAgent() :
 
 	mbAlwaysRun(false),
 	mbRunning(false),
+	mbTeleportKeepsLookAt(false),
 
 	mAgentAccess(gSavedSettings),
 	mTeleportState( TELEPORT_NONE ),
@@ -3244,7 +3245,11 @@ bool LLAgent::teleportCore(bool is_local)
 
 	// local logic
 	LLViewerStats::getInstance()->incStat(LLViewerStats::ST_TELEPORT_COUNT);
-	if (!is_local)
+	if (is_local)
+	{
+		gAgent.setTeleportState( LLAgent::TELEPORT_LOCAL );
+	}
+	else
 	{
 		gTeleportDisplay = TRUE;
 		gAgent.setTeleportState( LLAgent::TELEPORT_START );
@@ -3263,13 +3268,15 @@ bool LLAgent::teleportCore(bool is_local)
 
 void LLAgent::teleportRequest(
 	const U64& region_handle,
-	const LLVector3& pos_local)
+	const LLVector3& pos_local,
+	bool look_at_from_camera)
 {
 	LLViewerRegion* regionp = getRegion();
-	if(regionp && teleportCore())
+	bool is_local = (region_handle == to_region_handle(getPositionGlobal()));
+	if(regionp && teleportCore(is_local))
 	{
-		llinfos << "TeleportRequest: '" << region_handle << "':" << pos_local
-				<< llendl;
+		LL_INFOS("") << "TeleportLocationRequest: '" << region_handle << "':"
+					 << pos_local << LL_ENDL;
 		LLMessageSystem* msg = gMessageSystem;
 		msg->newMessage("TeleportLocationRequest");
 		msg->nextBlockFast(_PREHASH_AgentData);
@@ -3279,6 +3286,10 @@ void LLAgent::teleportRequest(
 		msg->addU64("RegionHandle", region_handle);
 		msg->addVector3("Position", pos_local);
 		LLVector3 look_at(0,1,0);
+		if (look_at_from_camera)
+		{
+			look_at = LLViewerCamera::getInstance()->getAtAxis();
+		}
 		msg->addVector3("LookAt", look_at);
 		sendReliableMessage();
 	}
@@ -3390,6 +3401,16 @@ void LLAgent::teleportViaLocation(const LLVector3d& pos_global)
 	}
 }
 
+// Teleport to global position, but keep facing in the same direction 
+void LLAgent::teleportViaLocationLookAt(const LLVector3d& pos_global)
+{
+	mbTeleportKeepsLookAt = true;
+	gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE);	// detach camera form avatar, so it keeps direction
+	U64 region_handle = to_region_handle(pos_global);
+	LLVector3 pos_local = (LLVector3)(pos_global - from_region_handle(region_handle));
+	teleportRequest(region_handle, pos_local, getTeleportKeepsLookAt());
+}
+
 void LLAgent::setTeleportState(ETeleportState state)
 {
 	mTeleportState = state;
@@ -3397,18 +3418,28 @@ void LLAgent::setTeleportState(ETeleportState state)
 	{
 		LLFloaterReg::hideInstance("snapshot");
 	}
-	if (mTeleportState == TELEPORT_MOVING)
-	{
-		// We're outa here. Save "back" slurl.
-		LLAgentUI::buildSLURL(mTeleportSourceSLURL);
-	}
-	else if(mTeleportState == TELEPORT_ARRIVING)
+
+	switch (mTeleportState)
 	{
-		// First two position updates after a teleport tend to be weird
-		LLViewerStats::getInstance()->mAgentPositionSnaps.mCountOfNextUpdatesToIgnore = 2;
+		case TELEPORT_NONE:
+			mbTeleportKeepsLookAt = false;
+			break;
+
+		case TELEPORT_MOVING:
+			// We're outa here. Save "back" slurl.
+			LLAgentUI::buildSLURL(mTeleportSourceSLURL);
+			break;
 
-		// Let the interested parties know we've teleported.
-		LLViewerParcelMgr::getInstance()->onTeleportFinished(false, getPositionGlobal());
+		case TELEPORT_ARRIVING:
+			// First two position updates after a teleport tend to be weird
+			LLViewerStats::getInstance()->mAgentPositionSnaps.mCountOfNextUpdatesToIgnore = 2;
+			
+			// Let the interested parties know we've teleported.
+			LLViewerParcelMgr::getInstance()->onTeleportFinished(false, getPositionGlobal());
+			break;
+
+		default:
+			break;
 	}
 }
 
diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
index 0d95683a9837c60a731af0e3a8a1fbd752a5c7db..454d276f825bbede5eef5a43d58fc94daa9d26f4 100644
--- a/indra/newview/llagent.h
+++ b/indra/newview/llagent.h
@@ -339,6 +339,7 @@ class LLAgent : public LLOldEvents::LLObservable
 private:
 	bool 			mbAlwaysRun; 			// Should the avatar run by default rather than walk?
 	bool 			mbRunning;				// Is the avatar trying to run right now?
+	bool			mbTeleportKeepsLookAt;	// Try to keep look-at after teleport is complete
 
 	//--------------------------------------------------------------------
 	// Sit and stand
@@ -504,7 +505,8 @@ class LLAgent : public LLOldEvents::LLObservable
 		TELEPORT_REQUESTED = 2,		// Waiting for source simulator to respond
 		TELEPORT_MOVING = 3,		// Viewer has received destination location from source simulator
 		TELEPORT_START_ARRIVAL = 4,	// Transition to ARRIVING.  Viewer has received avatar update, etc., from destination simulator
-		TELEPORT_ARRIVING = 5		// Make the user wait while content "pre-caches"
+		TELEPORT_ARRIVING = 5,		// Make the user wait while content "pre-caches"
+		TELEPORT_LOCAL = 6			// Teleporting in-sim without showing the progress screen
 	};
 
 public:
@@ -522,12 +524,15 @@ class LLAgent : public LLOldEvents::LLObservable
 	//--------------------------------------------------------------------
 public:
 	void 			teleportRequest(const U64& region_handle,
-									const LLVector3& pos_local);			// Go to a named location home
+									const LLVector3& pos_local,				// Go to a named location home
+									bool look_at_from_camera = false);
 	void 			teleportViaLandmark(const LLUUID& landmark_id);			// Teleport to a landmark
 	void 			teleportHome()	{ teleportViaLandmark(LLUUID::null); }	// Go home
 	void 			teleportViaLure(const LLUUID& lure_id, BOOL godlike);	// To an invited location
 	void 			teleportViaLocation(const LLVector3d& pos_global);		// To a global location - this will probably need to be deprecated
+	void			teleportViaLocationLookAt(const LLVector3d& pos_global);// To a global location, preserving camera rotation
 	void 			teleportCancel();										// May or may not be allowed by server
+	bool			getTeleportKeepsLookAt() { return mbTeleportKeepsLookAt; } // Whether look-at reset after teleport
 protected:
 	bool 			teleportCore(bool is_local = false); 					// Stuff for all teleports; returns true if the teleport can proceed
 
diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp
index d8be70e54698b36ca8b3e7a438609811742aa33d..864de018e0234211bd1f1047e11215525ce97f34 100644
--- a/indra/newview/lltoolpie.cpp
+++ b/indra/newview/lltoolpie.cpp
@@ -619,6 +619,25 @@ BOOL LLToolPie::handleDoubleClick(S32 x, S32 y, MASK mask)
 			return TRUE;
 		}
 	}
+	else if (gSavedSettings.getBOOL("DoubleClickTeleport"))
+	{
+		LLViewerObject* objp = mPick.getObject();
+		LLViewerObject* parentp = objp ? objp->getRootEdit() : NULL;
+
+		bool is_in_world = mPick.mObjectID.notNull() && objp && !objp->isHUDAttachment();
+		bool is_land = mPick.mPickType == LLPickInfo::PICK_LAND;
+		bool pos_non_zero = !mPick.mPosGlobal.isExactlyZero();
+		bool has_touch_handler = (objp && objp->flagHandleTouch()) || (parentp && parentp->flagHandleTouch());
+		bool has_click_action = final_click_action(objp);
+
+		if (pos_non_zero && (is_land || (is_in_world && !has_touch_handler && !has_click_action)))
+		{
+			LLVector3d pos = mPick.mPosGlobal;
+			pos.mdV[VZ] += gAgentAvatarp->getPelvisToFoot();
+			gAgent.teleportViaLocationLookAt(pos);
+			return TRUE;
+		}
+	}
 
 	return FALSE;
 }
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index d0ad918c58b05dd954e791a5c21b54e36c0f74a1..916cbe226774b842ebbd803aa33b431263df062f 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -85,6 +85,7 @@ LLPointer<LLViewerTexture> gDisconnectedImagep = NULL;
 // used to toggle renderer back on after teleport
 const F32 TELEPORT_RENDER_DELAY = 20.f; // Max time a teleport is allowed to take before we raise the curtain
 const F32 TELEPORT_ARRIVAL_DELAY = 2.f; // Time to preload the world before raising the curtain after we've actually already arrived.
+const F32 TELEPORT_LOCAL_DELAY = 1.0f;  // Delay to prevent teleports after starting an in-sim teleport.
 BOOL		 gTeleportDisplay = FALSE;
 LLFrameTimer gTeleportDisplayTimer;
 LLFrameTimer gTeleportArrivalTimer;
@@ -406,6 +407,18 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
 			}
 			break;
 
+		case LLAgent::TELEPORT_LOCAL:
+			// Short delay when teleporting in the same sim (progress screen active but not shown - did not
+			// fall-through from TELEPORT_START)
+			{
+				if( gTeleportDisplayTimer.getElapsedTimeF32() > TELEPORT_LOCAL_DELAY )
+				{
+					//LLFirstUse::useTeleport();
+					gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
+				}
+			}
+			break;
+
 		case LLAgent::TELEPORT_NONE:
 			// No teleport in progress
 			gViewerWindow->setShowProgress(FALSE);
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index c2750680282233fb6d3cc0090e95b99ecbb31ff3..409f9685e547e7e6c6a644a89873306b284a12b4 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -6560,6 +6560,16 @@ class LLToggleControl : public view_listener_t
 		std::string control_name = userdata.asString();
 		BOOL checked = gSavedSettings.getBOOL( control_name );
 		gSavedSettings.setBOOL( control_name, !checked );
+
+        // Doubleclick actions - there can be only one
+        if ((control_name == "DoubleClickAutoPilot") && !checked)
+        {
+			gSavedSettings.setBOOL( "DoubleClickTeleport", FALSE );
+        }
+        else if ((control_name == "DoubleClickTeleport") && !checked)
+        {
+			gSavedSettings.setBOOL( "DoubleClickAutoPilot", FALSE );
+        }
 		return true;
 	}
 };
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 92c61ddefebc854dcce52bf12fe26312c1369375..dc66cffd873ee7e5015d72838727a39a506c24d3 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -81,6 +81,7 @@
 #include "lluri.h"
 #include "llviewergenericmessage.h"
 #include "llviewermenu.h"
+#include "llviewerjoystick.h"
 #include "llviewerobjectlist.h"
 #include "llviewerparcelmgr.h"
 #include "llviewerstats.h"
@@ -3126,6 +3127,8 @@ void process_teleport_start(LLMessageSystem *msg, void**)
 	U32 teleport_flags = 0x0;
 	msg->getU32("Info", "TeleportFlags", teleport_flags);
 
+	LL_DEBUGS("Messaging") << "Got TeleportStart with TeleportFlags=" << teleport_flags << ". gTeleportDisplay: " << gTeleportDisplay << ", gAgent.mTeleportState: " << gAgent.getTeleportState() << LL_ENDL;
+
 	if (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL)
 	{
 		gViewerWindow->setProgressCancelButtonVisible(FALSE);
@@ -3144,6 +3147,7 @@ void process_teleport_start(LLMessageSystem *msg, void**)
 		gAgent.setTeleportState( LLAgent::TELEPORT_START );
 		make_ui_sound("UISndTeleportOut");
 		
+		LL_INFOS("Messaging") << "Teleport initiated by remote TeleportStart message with TeleportFlags: " <<  teleport_flags << LL_ENDL;
 		// Don't call LLFirstUse::useTeleport here because this could be
 		// due to being killed, which would send you home, not to a Telehub
 	}
@@ -3485,6 +3489,12 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**)
 
 	if( is_teleport )
 	{
+		if (gAgent.getTeleportKeepsLookAt())
+		{
+			// *NOTE: the LookAt data we get from the sim here doesn't
+			// seem to be useful, so get it from the camera instead
+			look_at = LLViewerCamera::getInstance()->getAtAxis();
+		}
 		// Force the camera back onto the agent, don't animate.
 		gAgentCamera.setFocusOnAvatar(TRUE, FALSE);
 		gAgentCamera.slamLookAt(look_at);
@@ -3531,7 +3541,7 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**)
 		{
 			LLTracker::stopTracking(NULL);
 		}
-		else if ( is_teleport )
+		else if ( is_teleport && !gAgent.getTeleportKeepsLookAt() )
 		{
 			//look at the beacon
 			LLVector3 global_agent_pos = agent_pos;
@@ -5803,7 +5813,18 @@ void process_teleport_local(LLMessageSystem *msg,void**)
 
 	if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE )
 	{
-		gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
+		if( gAgent.getTeleportState() == LLAgent::TELEPORT_LOCAL )
+		{
+			// To prevent TeleportStart messages re-activating the progress screen right
+			// after tp, keep the teleport state and let progress screen clear it after a short delay
+			// (progress screen is active but not visible)  *TODO: remove when SVC-5290 is fixed
+			gTeleportDisplayTimer.reset();
+			gTeleportDisplay = TRUE;
+		}
+		else
+		{
+			gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
+		}
 	}
 
 	// Sim tells us whether the new position is off the ground
@@ -5819,8 +5840,10 @@ void process_teleport_local(LLMessageSystem *msg,void**)
 	gAgent.setPositionAgent(pos);
 	gAgentCamera.slamLookAt(look_at);
 
-	// likewise make sure the camera is behind the avatar
-	gAgentCamera.resetView(TRUE, TRUE);
+	if ( !(gAgent.getTeleportKeepsLookAt() && LLViewerJoystick::getInstance()->getOverrideCamera()) )
+	{
+		gAgentCamera.resetView(TRUE, TRUE);
+	}
 
 	// send camera update to new region
 	gAgentCamera.updateCamera();
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 0b85074eb6ce4249c9a75a31b6f09bf7a313ccec..8f973aec4414cd7e45054382fe806d4157d5565b 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -1662,8 +1662,8 @@
             </menu_item_call>
             <menu_item_separator
              visible="false"/>
-            <!-- Made invisible to avoid a dissonance: menu item toggle menu where it is located. EXT-8069.
-              Can't be removed to keep sortcut workable.
+            <!-- Made invisible to avoid a dissonance: menu item toggles the menu where it is located. EXT-8069.
+              Can't be removed, to keep shortcut workable.
             -->
             <menu_item_check
              label="Show Advanced Menu"
@@ -2631,6 +2631,17 @@
                  function="ToggleControl"
                  parameter="DoubleClickAutoPilot" />
             </menu_item_check>
+            <menu_item_check
+             label="Double-ClickTeleport"
+             layout="topleft"
+             name="DoubleClickTeleport">
+                <menu_item_check.on_check
+                 function="CheckControl"
+                 parameter="DoubleClickTeleport" />
+                <menu_item_check.on_click
+                 function="ToggleControl"
+                 parameter="DoubleClickTeleport" />
+            </menu_item_check>
 
             <menu_item_separator />