From 7a64aad1def1b0612addbf2e66c66db061d7e182 Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxie@lindenlab.com>
Date: Thu, 9 Jul 2009 20:56:23 +0000
Subject: [PATCH] DEV-34822 - merge 1.23 merge -r119443 - basic slurl handling
 ignore dead branch

---
 indra/llcommon/lluri.cpp                      |   3 +-
 indra/llui/llurlentry.cpp                     |  29 +-
 indra/llui/tests/llurlentry_test.cpp          |  24 +
 indra/newview/CMakeLists.txt                  |  11 +-
 indra/newview/Info-SecondLife.plist           |  28 ++
 .../installers/windows/installer_template.nsi |   6 +
 indra/newview/llagent.h                       |   5 +-
 indra/newview/llagentlistener.cpp             |   5 +-
 indra/newview/llagentui.cpp                   |  17 +-
 indra/newview/llagentui.h                     |   3 +-
 indra/newview/llappviewer.cpp                 |  57 +--
 indra/newview/llappviewermacosx.cpp           |   1 -
 indra/newview/llfloaterbuyland.cpp            |   2 +-
 indra/newview/llfloaterchat.cpp               |   2 +-
 indra/newview/llfloaterland.cpp               |   6 +-
 indra/newview/llfloaterpreference.cpp         |   4 +-
 indra/newview/llfloaterregioninfo.cpp         |   3 +-
 indra/newview/llfloaterreporter.cpp           |   6 +-
 indra/newview/llfloaterworldmap.cpp           |  23 +-
 indra/newview/llfloaterworldmap.h             |   3 +-
 indra/newview/llinspectobject.cpp             |   6 +-
 indra/newview/llinspectremoteobject.cpp       |   4 +-
 indra/newview/lllandmarkactions.cpp           |   4 +-
 indra/newview/lllocationinputctrl.cpp         |   5 +-
 indra/newview/llloginhandler.cpp              |  20 +-
 indra/newview/llloginhandler.h                |   2 +-
 indra/newview/lllogininstance.cpp             |  16 +-
 indra/newview/llnavigationbar.cpp             |  18 +-
 indra/newview/llpanellogin.cpp                | 262 +++++++---
 indra/newview/llpanellogin.h                  |  13 +-
 indra/newview/llpanelplacestab.cpp            |   5 +-
 indra/newview/llselectmgr.cpp                 |   8 +-
 indra/newview/llslurl.cpp                     | 470 ++++++++++++++----
 indra/newview/llslurl.h                       | 146 +++---
 indra/newview/llstartup.cpp                   |  87 ++--
 indra/newview/llstartup.h                     |   9 +-
 indra/newview/llstylemap.cpp                  |   2 +-
 indra/newview/llurldispatcher.cpp             | 213 ++++----
 indra/newview/llurldispatcher.h               |  18 +-
 indra/newview/llurldispatcherlistener.cpp     |   1 +
 indra/newview/llurllineeditorctrl.cpp         |   2 +-
 indra/newview/llviewermessage.cpp             |  10 +-
 indra/newview/llviewernetwork.cpp             |  36 +-
 indra/newview/llviewernetwork.h               |   9 +-
 indra/newview/llviewerwindow.cpp              |   2 +-
 .../skins/default/xui/en/notifications.xml    |   9 +
 indra/newview/tests/lllogininstance_test.cpp  |   9 +-
 indra/newview/tests/llslurl_test.cpp          | 258 ++++++++++
 48 files changed, 1306 insertions(+), 576 deletions(-)
 create mode 100644 indra/newview/tests/llslurl_test.cpp

diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp
index f6e8f01f0e..b28657e2f4 100644
--- a/indra/llcommon/lluri.cpp
+++ b/indra/llcommon/lluri.cpp
@@ -220,7 +220,8 @@ static BOOL isDefault(const std::string& scheme, U16 port)
 void LLURI::parseAuthorityAndPathUsingOpaque()
 {
 	if (mScheme == "http" || mScheme == "https" ||
-		mScheme == "ftp" || mScheme == "secondlife" )
+		mScheme == "ftp" || mScheme == "secondlife" || 
+		mScheme == "x-grid-location-info")
 	{
 		if (mEscapedOpaque.substr(0,2) != "//")
 		{
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index 1b6dd1b264..466ac2a7d1 100644
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -39,6 +39,9 @@
 #include "lltrans.h"
 #include "lluicolortable.h"
 
+#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))"
+
+
 LLUrlEntryBase::LLUrlEntryBase()
 : mColor(LLUIColorTable::instance().getColor("HTMLLinkColor"))
 {
@@ -300,10 +303,11 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const
 //
 // LLUrlEntryAgent Describes a Second Life agent Url, e.g.,
 // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about
+// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about
 //
 LLUrlEntryAgent::LLUrlEntryAgent()
 {
-	mPattern = boost::regex("secondlife:///app/agent/[\\da-f-]+/\\w+",
+	mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+",
 							boost::regex::perl|boost::regex::icase);
 	mMenuName = "menu_url_agent.xml";
 	mIcon = "Generic_Person";
@@ -358,10 +362,11 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa
 // LLUrlEntryGroup Describes a Second Life group Url, e.g.,
 // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about
 // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect
+// x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect
 //
 LLUrlEntryGroup::LLUrlEntryGroup()
 {
-	mPattern = boost::regex("secondlife:///app/group/[\\da-f-]+/\\w+",
+	mPattern = boost::regex(APP_HEADER_REGEX "/group/[\\da-f-]+/\\w+",
 							boost::regex::perl|boost::regex::icase);
 	mMenuName = "menu_url_group.xml";
 	mIcon = "Generic_Group";
@@ -422,7 +427,8 @@ LLUrlEntryInventory::LLUrlEntryInventory()
 	//*TODO: add supporting of inventory item names with whitespaces
 	//this pattern cann't parse for example 
 	//secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces&param2=value
-	mPattern = boost::regex("secondlife:///app/inventory/[\\da-f-]+/\\w+\\S*",
+	//x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces&param2=value
+	mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*",
 							boost::regex::perl|boost::regex::icase);
 	mMenuName = "menu_url_inventory.xml";
 }
@@ -436,10 +442,11 @@ std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLab
 ///
 /// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
 /// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
+/// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
 ///
 LLUrlEntryParcel::LLUrlEntryParcel()
 {
-	mPattern = boost::regex("secondlife:///app/parcel/[\\da-f-]+/about",
+	mPattern = boost::regex(APP_HEADER_REGEX "/parcel/[\\da-f-]+/about",
 							boost::regex::perl|boost::regex::icase);
 	mMenuName = "menu_url_parcel.xml";
 	mTooltip = LLTrans::getString("TooltipParcelUrl");
@@ -455,7 +462,7 @@ std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelC
 //
 LLUrlEntryPlace::LLUrlEntryPlace()
 {
-	mPattern = boost::regex("secondlife://\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?",
+	mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?",
 							boost::regex::perl|boost::regex::icase);
 	mMenuName = "menu_url_slurl.xml";
 	mTooltip = LLTrans::getString("TooltipSLURL");
@@ -500,10 +507,11 @@ std::string LLUrlEntryPlace::getLocation(const std::string &url) const
 //
 // LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
 // secondlife:///app/teleport/Ahern/50/50/50/
+// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/
 //
 LLUrlEntryTeleport::LLUrlEntryTeleport()
 {
-	mPattern = boost::regex("secondlife:///app/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*",
+	mPattern = boost::regex(APP_HEADER_REGEX "/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*",
 							boost::regex::perl|boost::regex::icase);
 	mMenuName = "menu_url_teleport.xml";
 	mTooltip = LLTrans::getString("TooltipTeleportUrl");
@@ -521,7 +529,12 @@ std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabe
 	LLURI uri(url);
 	LLSD path_array = uri.pathArray();
 	S32 path_parts = path_array.size();
-	const std::string label = LLTrans::getString("SLurlLabelTeleport");
+	std::string host = uri.hostName();
+	std::string label = LLTrans::getString("SLurlLabelTeleport");
+	if (!host.empty())
+	{
+		label += " " + host;
+	}
 	if (path_parts == 6)
 	{
 		// handle teleport url with (X,Y,Z) coordinates
@@ -606,7 +619,7 @@ std::string LLUrlEntrySLLabel::getUrl(const std::string &string)
 //
 LLUrlEntryWorldMap::LLUrlEntryWorldMap()
 {
-	mPattern = boost::regex("secondlife:///app/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*",
+	mPattern = boost::regex(APP_HEADER_REGEX "/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*",
 							boost::regex::perl|boost::regex::icase);
 	mMenuName = "menu_url_map.xml";
 	mTooltip = LLTrans::getString("TooltipMapUrl");
diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp
index 38cf7124ce..1d66bc268b 100644
--- a/indra/llui/tests/llurlentry_test.cpp
+++ b/indra/llui/tests/llurlentry_test.cpp
@@ -289,6 +289,13 @@ namespace tut
 				  "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar",
 				  "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar");
 
+		testRegex("Nebraska Agent Url ", r,
+				  "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about",
+				  "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");		
+
+		testRegex("Nebraska Agent Url Multicase with Text", r,
+				  "M x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M",
+				  "x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");		
 	}
 
 	template<> template<>
@@ -319,6 +326,15 @@ namespace tut
 		testRegex("Group Url multicase", r,
 				  "XXX secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About XXX",
 				  "secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About");
+		
+		testRegex("Nebraska Group Url ", r,
+				  "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about",
+				  "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");		
+		
+		testRegex("Nebraska Group Url Multicase ith Text", r,
+				  "M x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M",
+				  "x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");		
+		
 	}
 
 	template<> template<>
@@ -367,6 +383,10 @@ namespace tut
 		testRegex("SLURL with quote", r,
 				  "XXX secondlife://A'ksha%20Oasis/41/166/701 XXX",
 				  "secondlife://A'ksha%20Oasis/41/166/701");
+		
+		testRegex("Nebraska All Hands (50,50) [2] with text", r,
+				  "XXX x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50 XXX",
+				  "x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50");		
 	}
 
 	template<> template<>
@@ -468,6 +488,10 @@ namespace tut
 		testRegex("Teleport url with quote", r,
 				  "XXX secondlife:///app/teleport/A'ksha%20Oasis/41/166/701 XXX",
 				  "secondlife:///app/teleport/A'ksha%20Oasis/41/166/701");
+		
+		testRegex("Nebraska All Hands", r,
+				  "XXX x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50 XXX",
+				  "x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50");		
 	}
 
 	template<> template<>
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 35f0a5036d..7891add9b8 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -449,7 +449,6 @@ set(viewer_SOURCE_FILES
     llurldispatcherlistener.cpp
     llurlhistory.cpp
     llurllineeditorctrl.cpp
-    llurlsimstring.cpp
     llurlwhitelist.cpp
     llvectorperfoptions.cpp
     llversioninfo.cpp
@@ -956,7 +955,6 @@ set(viewer_HEADER_FILES
     llurldispatcherlistener.h
     llurlhistory.h
     llurllineeditorctrl.h
-    llurlsimstring.h
     llurlwhitelist.h
     llvectorperfoptions.h
     llversioninfo.h
@@ -1822,12 +1820,21 @@ if (LL_TESTS)
      llsecapi.cpp
     "${test_libs}"
     )
+  set(llslurl_test_sources
+      llslurl.cpp
+      llviewernetwork.cpp
+  )
 
   LL_ADD_INTEGRATION_TEST(llviewernetwork
      llviewernetwork.cpp
     "${test_libs}"
     )
 
+  LL_ADD_INTEGRATION_TEST(llslurl
+     "${llslurl_test_sources}"
+    "${test_libs}"
+    )
+
   #ADD_VIEWER_BUILD_TEST(llmemoryview viewer)
   #ADD_VIEWER_BUILD_TEST(llagentaccess viewer)
   #ADD_VIEWER_BUILD_TEST(llworldmap viewer)
diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist
index 38ebb22b84..a800f4ba79 100644
--- a/indra/newview/Info-SecondLife.plist
+++ b/indra/newview/Info-SecondLife.plist
@@ -18,6 +18,33 @@
 	<string>APPL</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
+        <key>CFBundleDocumentTypes</key>
+        <array>
+                <dict>
+                        <key>CFBundleTypeExtensions</key>
+                        <array>
+                                <string>slurl</string>
+                        </array>
+                        <key>CFBundleTypeIconFile</key>
+                        <string>seconlife</string>
+                        <key>CFBundleTypeMIMETypes</key>
+                        <array>
+                                <string>application/x-grid-location-info</string>
+                        </array>
+                        <key>CFBundleTypeName</key>
+                        <string>Secondlife SLURL</string>
+			<key>CFBundleTypeOSTypes</key>
+			<array>
+			  <string>SLRL</string>
+			</array>
+                        <key>CFBundleTypeRole</key>
+                        <string>Viewer</string>
+                        <key>LSTypeIsPackage</key>
+			<true/>
+                        <key>NSDocumentClass</key>
+                        <string>SecondLifeSLURL</string>
+                </dict>
+        </array>
 	<key>CFBundleURLTypes</key>
 	<array>
 		<dict>
@@ -26,6 +53,7 @@
 			<key>CFBundleURLSchemes</key>
 			<array>
 				<string>secondlife</string>
+				<string>x-grid-location-info</string>
 			</array>
 			<key>LSIsAppleDefaultForScheme</key>
 			<true/>
diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi
index a7322749ca..b7b4c54001 100644
--- a/indra/newview/installers/windows/installer_template.nsi
+++ b/indra/newview/installers/windows/installer_template.nsi
@@ -797,6 +797,12 @@ WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}\DefaultIcon" "" '"$INSTDIR\$INSTEXE"'
 ;; URL param must be last item passed to viewer, it ignores subsequent params
 ;; to avoid parameter injection attacks.
 WriteRegExpandStr HKEY_CLASSES_ROOT "${URLNAME}\shell\open\command" "" '"$INSTDIR\$INSTEXE" $INSTFLAGS -url "%1"'
+WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info"(default)" "URL:Second Life"
+WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "URL Protocol" ""
+WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\DefaultIcon" "" '"$INSTDIR\$INSTEXE"'
+;; URL param must be last item passed to viewer, it ignores subsequent params
+;; to avoid parameter injection attacks.
+WriteRegExpandStr HKEY_CLASSES_ROOT "x-grid-location-info\shell\open\command" "" '"$INSTDIR\$INSTEXE" $INSTFLAGS -url "%1"'
 
 ; write out uninstaller
 WriteUninstaller "$INSTDIR\uninst.exe"
diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
index 2e95dc72be..4ea1185925 100644
--- a/indra/newview/llagent.h
+++ b/indra/newview/llagent.h
@@ -45,6 +45,7 @@
 #include "llpointer.h"
 #include "lluicolor.h"
 #include "llvoavatardefines.h"
+#include "llslurl.h"
 
 extern const BOOL 	ANIMATE;
 extern const U8 	AGENT_STATE_TYPING;  // Typing indication
@@ -594,13 +595,13 @@ public:
 
 public:
 	static void 	parseTeleportMessages(const std::string& xml_filename);
-	const std::string getTeleportSourceSLURL() const { return mTeleportSourceSLURL; }
+	const LLSLURL getTeleportSourceSLURL() const { return mTeleportSourceSLURL; }
 public:
 	// ! TODO ! Define ERROR and PROGRESS enums here instead of exposing the mappings.
 	static std::map<std::string, std::string> sTeleportErrorMessages;
 	static std::map<std::string, std::string> sTeleportProgressMessages;
 private:
-	std::string		mTeleportSourceSLURL; 			// SLURL where last TP began
+	LLSLURL	mTeleportSourceSLURL; 			// SLURL where last TP began
 
 	//--------------------------------------------------------------------
 	// Teleport Actions
diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp
index b3ed7c353e..7a8205acb5 100644
--- a/indra/newview/llagentlistener.cpp
+++ b/indra/newview/llagentlistener.cpp
@@ -53,7 +53,10 @@ void LLAgentListener::requestTeleport(LLSD const & event_data) const
 	}
 	else
 	{
-		std::string url = LLSLURL::buildSLURL(event_data["regionname"], event_data["x"], event_data["y"], event_data["z"]);
+		std::string url = LLSLURL(event_data["regionname"], 
+								  LLVector3(event_data["x"].asReal(), 
+											event_data["y"].asReal(), 
+											event_data["z"].asReal())).getSLURLString();
 		LLURLDispatcher::dispatch(url, NULL, false);
 	}
 }
diff --git a/indra/newview/llagentui.cpp b/indra/newview/llagentui.cpp
index 7404fe5bc4..e26c6470ef 100644
--- a/indra/newview/llagentui.cpp
+++ b/indra/newview/llagentui.cpp
@@ -77,16 +77,15 @@ void LLAgentUI::buildFullname(std::string& name)
 }
 
 //static
-std::string LLAgentUI::buildSLURL(const bool escaped /*= true*/)
+LLSLURL LLAgentUI::buildSLURL(const bool escaped /*= true*/)
 {
-	std::string slurl;
-	LLViewerRegion *regionp = gAgent.getRegion();
-	if (regionp)
-	{
-		LLVector3d agentPos = gAgent.getPositionGlobal();
-		slurl = LLSLURL::buildSLURLfromPosGlobal(regionp->getName(), agentPos, escaped);
-	}
-	return slurl;
+      LLSLURL slurl;
+      LLViewerRegion *regionp = gAgent.getRegion();
+      if (regionp)
+      {
+		  slurl = LLSLURL(regionp->getName(), gAgent.getPositionGlobal());
+      }
+      return slurl;
 }
 
 //static
diff --git a/indra/newview/llagentui.h b/indra/newview/llagentui.h
index 3478793e38..9d10bc7322 100644
--- a/indra/newview/llagentui.h
+++ b/indra/newview/llagentui.h
@@ -32,6 +32,7 @@
 
 #ifndef LLAGENTUI_H
 #define LLAGENTUI_H
+#include "llslurl.h"
 
 class LLAgentUI
 {
@@ -48,7 +49,7 @@ public:
 	static void buildName(std::string& name);
 	static void buildFullname(std::string &name);
 
-	static std::string buildSLURL(const bool escaped = true);
+	static LLSLURL buildSLURL(const bool escaped = true);
 	//build location string using the current position of gAgent.
 	static BOOL buildLocationString(std::string& str, ELocationFormat fmt = LOCATION_FORMAT_LANDMARK);
 	//build location string using a region position of the avatar. 
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index cc32346441..d1e33fa91a 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -150,7 +150,7 @@
 #include "llworld.h"
 #include "llhudeffecttrail.h"
 #include "llvectorperfoptions.h"
-#include "llurlsimstring.h"
+#include "llslurl.h"
 #include "llwatchdog.h"
 
 // Included so that constants/settings might be initialized
@@ -2078,30 +2078,21 @@ bool LLAppViewer::initConfiguration()
     // injection and steal passwords. Phoenix. SL-55321
     if(clp.hasOption("url"))
     {
-        std::string slurl = clp.getOption("url")[0];
-        if (LLSLURL::isSLURLCommand(slurl))
-        {
-	        LLStartUp::sSLURLCommand = slurl;
-        }
-        else
-        {
-	        LLURLSimString::setString(slurl);
-        }
+		LLStartUp::setStartSLURL(LLSLURL(clp.getOption("url")[0]));
+		if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION) 
+		{  
+			LLGridManager::getInstance()->setGridChoice(LLStartUp::getStartSLURL().getGrid());
+			
+		}  
     }
     else if(clp.hasOption("slurl"))
     {
-        std::string slurl = clp.getOption("slurl")[0];
-        if(LLSLURL::isSLURL(slurl))
-        {
-            if (LLSLURL::isSLURLCommand(slurl))
-            {
-	            LLStartUp::sSLURLCommand = slurl;
-            }
-            else
-            {
-	            LLURLSimString::setString(slurl);
-            }
-        }
+      LLStartUp::setStartSLURL(LLSLURL(clp.getOption("surl")[0]));
+      if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION) 
+	  {  
+		  LLGridManager::getInstance()->setGridChoice(LLStartUp::getStartSLURL().getGrid());
+		  
+	  }  
     }
 
     const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent");
@@ -2180,18 +2171,10 @@ bool LLAppViewer::initConfiguration()
 	// don't call anotherInstanceRunning() when doing URL handoff, as
 	// it relies on checking a marker file which will not work when running
 	// out of different directories
-	std::string slurl;
-	if (!LLStartUp::sSLURLCommand.empty())
-	{
-		slurl = LLStartUp::sSLURLCommand;
-	}
-	else if (LLURLSimString::parse())
-	{
-		slurl = LLURLSimString::getURL();
-	}
-	if (!slurl.empty())
+
+	if (LLStartUp::getStartSLURL().isValid())
 	{
-		if (sendURLToOtherInstance(slurl))
+		if (sendURLToOtherInstance(LLStartUp::getStartSLURL().getSLURLString()))
 		{
 			// successfully handed off URL to existing instance, exit
 			return false;
@@ -2247,9 +2230,9 @@ bool LLAppViewer::initConfiguration()
 
    	// need to do this here - need to have initialized global settings first
 	std::string nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" );
-	if ( nextLoginLocation.length() )
+	if ( !nextLoginLocation.empty() )
 	{
-		LLURLSimString::setString( nextLoginLocation );
+		LLStartUp::setStartSLURL(LLSLURL(nextLoginLocation));
 	};
 
 	gLastRunVersion = gSavedSettings.getString("LastRunVersion");
@@ -4246,10 +4229,10 @@ void LLAppViewer::launchUpdater()
 	LLAppViewer::sUpdaterInfo = new LLAppViewer::LLUpdaterInfo() ;
 
 	// if a sim name was passed in via command line parameter (typically through a SLURL)
-	if ( LLURLSimString::sInstance.mSimString.length() )
+	if ( LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION )
 	{
 		// record the location to start at next time
-		gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); 
+		gSavedSettings.setString( "NextLoginLocation", LLStartUp::getStartSLURL().getSLURLString()); 
 	};
 
 #if LL_WINDOWS
diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp
index 1282e437f2..d8c4a7d9d9 100644
--- a/indra/newview/llappviewermacosx.cpp
+++ b/indra/newview/llappviewermacosx.cpp
@@ -44,7 +44,6 @@
 #include "llviewernetwork.h"
 #include "llviewercontrol.h"
 #include "llmd5.h"
-#include "llurlsimstring.h"
 #include "llfloaterworldmap.h"
 #include "llurldispatcher.h"
 #include <Carbon/Carbon.h>
diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp
index 10a4908f3a..5fb129b87b 100644
--- a/indra/newview/llfloaterbuyland.cpp
+++ b/indra/newview/llfloaterbuyland.cpp
@@ -808,7 +808,7 @@ void LLFloaterBuyLandUI::updateNames()
 	else
 	{
 		mParcelSellerName =
-			LLSLURL::buildCommand("agent", parcelp->getOwnerID(), "inspect");
+			LLSLURL("agent", parcelp->getOwnerID(), "inspect").getSLURLString();
 	}
 }
 
diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp
index b9e0f928f1..09b47c9866 100644
--- a/indra/newview/llfloaterchat.cpp
+++ b/indra/newview/llfloaterchat.cpp
@@ -168,7 +168,7 @@ void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, const LLColor4&
 	if (chat.mSourceType == CHAT_SOURCE_AGENT &&
 		chat.mFromID != LLUUID::null)
 	{
-		chat.mURL = LLSLURL::buildCommand("agent", chat.mFromID, "inspect");
+		chat.mURL = LLSLURL("agent", chat.mFromID, "inspect").getSLURLString();
 	}
 
 	// If the chat line has an associated url, link it up to the name.
diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp
index 9b6e24f251..920c7ac58c 100644
--- a/indra/newview/llfloaterland.cpp
+++ b/indra/newview/llfloaterland.cpp
@@ -767,7 +767,7 @@ void LLPanelLandGeneral::refreshNames()
 	else
 	{
 		// Figure out the owner's name
-		owner = LLSLURL::buildCommand("agent", parcel->getOwnerID(), "inspect");
+		owner = LLSLURL("agent", parcel->getOwnerID(), "inspect").getSLURLString();
 	}
 
 	if(LLParcel::OS_LEASE_PENDING == parcel->getOwnershipStatus())
@@ -779,7 +779,7 @@ void LLPanelLandGeneral::refreshNames()
 	std::string group;
 	if (!parcel->getGroupID().isNull())
 	{
-		group = LLSLURL::buildCommand("group", parcel->getGroupID(), "inspect");
+		group = LLSLURL("group", parcel->getGroupID(), "inspect").getSLURLString();
 	}
 	mTextGroup->setText(group);
 
@@ -787,7 +787,7 @@ void LLPanelLandGeneral::refreshNames()
 	if(auth_buyer_id.notNull())
 	{
 		std::string name;
-		name = LLSLURL::buildCommand("agent", auth_buyer_id, "inspect");
+		name = LLSLURL("agent", auth_buyer_id, "inspect").getSLURLString();
 		mSaleInfoForSale2->setTextArg("[BUYER]", name);
 	}
 	else
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index d0716f67b8..f43aa697d1 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -614,7 +614,7 @@ void LLFloaterPreference::onBtnOK()
 		llinfos << "Can't close preferences!" << llendl;
 	}
 
-	LLPanelLogin::refreshLocation( false );
+	LLPanelLogin::updateLocationCombo( false );
 }
 
 // static 
@@ -631,7 +631,7 @@ void LLFloaterPreference::onBtnApply( )
 	apply();
 	saveSettings();
 
-	LLPanelLogin::refreshLocation( false );
+	LLPanelLogin::updateLocationCombo( false );
 }
 
 // static 
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index c4b87c1b2d..1bed47e7e1 100644
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -2926,8 +2926,7 @@ bool LLDispatchEstateUpdateInfo::operator()(
 	LLUUID owner_id(strings[1]);
 	regionp->setOwner(owner_id);
 	// Update estate owner name in UI
-	std::string owner_name =
-		LLSLURL::buildCommand("agent", owner_id, "inspect");
+	std::string owner_name = LLSLURL("agent", owner_id, "inspect").getSLURLString();
 	panel->setOwnerName(owner_name);
 
 	U32 estate_id = strtoul(strings[2].c_str(), NULL, 10);
diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp
index a52fe131cd..3fd37c09dd 100644
--- a/indra/newview/llfloaterreporter.cpp
+++ b/indra/newview/llfloaterreporter.cpp
@@ -124,7 +124,7 @@ void LLFloaterReporter::processRegionInfo(LLMessageSystem* msg)
 // virtual
 BOOL LLFloaterReporter::postBuild()
 {
-	childSetText("abuse_location_edit", LLAgentUI::buildSLURL());
+	childSetText("abuse_location_edit", LLAgentUI::buildSLURL().getSLURLString());
 
 	enableControls(TRUE);
 
@@ -279,7 +279,7 @@ void LLFloaterReporter::getObjectInfo(const LLUUID& object_id)
 				}
 				childSetText("object_name", object_owner);
 				std::string owner_link =
-					LLSLURL::buildCommand("agent", mObjectID, "inspect");
+					LLSLURL("agent", mObjectID, "inspect").getSLURLString();
 				childSetText("owner_name", owner_link);
 				childSetText("abuser_name_edit", object_owner);
 				mAbuserID = object_id;
@@ -483,7 +483,7 @@ void LLFloaterReporter::setPickedObjectProperties(const std::string& object_name
 {
 	childSetText("object_name", object_name);
 	std::string owner_link =
-		LLSLURL::buildCommand("agent", owner_id, "inspect");
+		LLSLURL("agent", owner_id, "inspect").getSLURLString();
 	childSetText("owner_name", owner_link);
 	childSetText("abuser_name_edit", owner_name);
 	mAbuserID = owner_id;
diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp
index 0781d8ed06..49416fff1c 100644
--- a/indra/newview/llfloaterworldmap.cpp
+++ b/indra/newview/llfloaterworldmap.cpp
@@ -457,7 +457,7 @@ void LLFloaterWorldMap::draw()
 	childSetEnabled("Teleport", (BOOL)tracking_status);
 //	childSetEnabled("Clear", (BOOL)tracking_status);
 	childSetEnabled("Show Destination", (BOOL)tracking_status || LLWorldMap::getInstance()->isTracking());
-	childSetEnabled("copy_slurl", (mSLURL.size() > 0) );
+	childSetEnabled("copy_slurl", (mSLURL.isValid()) );
 
 	setMouseOpaque(TRUE);
 	getDragHandle()->setMouseOpaque(TRUE);
@@ -656,14 +656,8 @@ void LLFloaterWorldMap::updateLocation()
 				childSetValue("location", agent_sim_name);
 
 				// Figure out where user is
-				LLVector3d agentPos = gAgent.getPositionGlobal();
-
-				S32 agent_x = llround( (F32)fmod( agentPos.mdV[VX], (F64)REGION_WIDTH_METERS ) );
-				S32 agent_y = llround( (F32)fmod( agentPos.mdV[VY], (F64)REGION_WIDTH_METERS ) );
-				S32 agent_z = llround( (F32)agentPos.mdV[VZ] );
-
 				// Set the current SLURL
-				mSLURL = LLSLURL::buildSLURL(agent_sim_name, agent_x, agent_y, agent_z);
+				mSLURL = LLSLURL(agent_sim_name, gAgent.getPositionGlobal());
 			}
 		}
 
@@ -690,18 +684,15 @@ void LLFloaterWorldMap::updateLocation()
 		}
 
 		childSetValue("location", sim_name);
-		
-		F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS );
-		F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS );
 
 		// simNameFromPosGlobal can fail, so don't give the user an invalid SLURL
 		if ( gotSimName )
 		{
-			mSLURL = LLSLURL::buildSLURL(sim_name, llround(region_x), llround(region_y), llround((F32)pos_global.mdV[VZ]));
+		  mSLURL = LLSLURL(sim_name, pos_global);
 		}
 		else
 		{	// Empty SLURL will disable the "Copy SLURL to clipboard" button
-			mSLURL = "";
+			mSLURL = LLSLURL();
 		}
 	}
 }
@@ -1167,7 +1158,7 @@ void LLFloaterWorldMap::onClearBtn()
 	mTrackedStatus = LLTracker::TRACKING_NOTHING;
 	LLTracker::stopTracking((void *)(intptr_t)TRUE);
 	LLWorldMap::getInstance()->cancelTracking();
-	mSLURL = "";					// Clear the SLURL since it's invalid
+	mSLURL = LLSLURL();					// Clear the SLURL since it's invalid
 	mSetToUserPosition = TRUE;	// Revert back to the current user position
 }
 
@@ -1190,10 +1181,10 @@ void LLFloaterWorldMap::onClickTeleportBtn()
 
 void LLFloaterWorldMap::onCopySLURL()
 {
-	getWindow()->copyTextToClipboard(utf8str_to_wstring(mSLURL));
+	getWindow()->copyTextToClipboard(utf8str_to_wstring(mSLURL.getSLURLString()));
 	
 	LLSD args;
-	args["SLURL"] = mSLURL;
+	args["SLURL"] = mSLURL.getSLURLString();
 
 	LLNotificationsUtil::add("CopySLURL", args);
 }
diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h
index 00f5e788fb..52809ff830 100644
--- a/indra/newview/llfloaterworldmap.h
+++ b/indra/newview/llfloaterworldmap.h
@@ -43,6 +43,7 @@
 #include "llhudtext.h"
 #include "llmapimagetype.h"
 #include "lltracker.h"
+#include "llslurl.h"
 
 class LLEventInfo;
 class LLFriendObserver;
@@ -183,7 +184,7 @@ private:
 	LLTracker::ETrackingStatus mTrackedStatus;
 	std::string				mTrackedSimName;
 	std::string				mTrackedAvatarName;
-	std::string				mSLURL;
+	LLSLURL  				mSLURL;
 };
 
 extern LLFloaterWorldMap* gFloaterWorldMap;
diff --git a/indra/newview/llinspectobject.cpp b/indra/newview/llinspectobject.cpp
index dd313c528d..e4ee953eae 100644
--- a/indra/newview/llinspectobject.cpp
+++ b/indra/newview/llinspectobject.cpp
@@ -479,7 +479,7 @@ void LLInspectObject::updateCreator(LLSelectNode* nodep)
 		// Objects cannot be created by a group, so use agent URL format
 		LLUUID creator_id = nodep->mPermissions->getCreator();
 		std::string creator_url =
-			LLSLURL::buildCommand("agent", creator_id, "about");
+			LLSLURL("agent", creator_id, "about").getSLURLString();
 		args["[CREATOR]"] = creator_url;
 				
 		// created by one user but owned by another
@@ -489,12 +489,12 @@ void LLInspectObject::updateCreator(LLSelectNode* nodep)
 		if (group_owned)
 		{
 			owner_id = nodep->mPermissions->getGroup();
-			owner_url =	LLSLURL::buildCommand("group", owner_id, "about");
+			owner_url =	LLSLURL("group", owner_id, "about").getSLURLString();
 		}
 		else
 		{
 			owner_id = nodep->mPermissions->getOwner();
-			owner_url =	LLSLURL::buildCommand("agent", owner_id, "about");
+			owner_url =	LLSLURL("agent", owner_id, "about").getSLURLString();
 		}
 		args["[OWNER]"] = owner_url;
 		
diff --git a/indra/newview/llinspectremoteobject.cpp b/indra/newview/llinspectremoteobject.cpp
index e4d2eec242..7319bd0331 100644
--- a/indra/newview/llinspectremoteobject.cpp
+++ b/indra/newview/llinspectremoteobject.cpp
@@ -176,11 +176,11 @@ void LLInspectRemoteObject::update()
 	{
 		if (mGroupOwned)
 		{
-			owner = LLSLURL::buildCommand("group", mOwnerID, "about");
+			owner = LLSLURL("group", mOwnerID, "about").getSLURLString();
 		}
 		else
 		{
-			owner = LLSLURL::buildCommand("agent", mOwnerID, "about");
+			owner = LLSLURL("agent", mOwnerID, "about").getSLURLString();
 		}
 	}
 	getChild<LLUICtrl>("object_owner")->setValue(owner);
diff --git a/indra/newview/lllandmarkactions.cpp b/indra/newview/lllandmarkactions.cpp
index f25d2ef574..eaa3a715cb 100644
--- a/indra/newview/lllandmarkactions.cpp
+++ b/indra/newview/lllandmarkactions.cpp
@@ -298,7 +298,7 @@ void LLLandmarkActions::getSLURLfromPosGlobal(const LLVector3d& global_pos, slur
 	bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name);
 	if (gotSimName)
 	{
-		std::string slurl = LLSLURL::buildSLURLfromPosGlobal(sim_name, global_pos, escaped);
+	  std::string slurl = LLSLURL(sim_name, global_pos).getSLURLString();
 		cb(slurl);
 
 		return;
@@ -350,7 +350,7 @@ void LLLandmarkActions::onRegionResponseSLURL(slurl_callback_t cb,
 	bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name);
 	if (gotSimName)
 	{
-		slurl = LLSLURL::buildSLURLfromPosGlobal(sim_name, global_pos, escaped);
+	  slurl = LLSLURL(sim_name, global_pos).getSLURLString();
 	}
 	else
 	{
diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp
index 404e266806..75b7f9313b 100644
--- a/indra/newview/lllocationinputctrl.cpp
+++ b/indra/newview/lllocationinputctrl.cpp
@@ -572,8 +572,7 @@ void LLLocationInputCtrl::onLocationPrearrange(const LLSD& data)
 				value["global_pos"] = result->mGlobalPos.getValue();
 				std::string region_name = result->mTitle.substr(0, result->mTitle.find(','));
 				//TODO*: add Surl to teleportitem or parse region name from title
-				value["tooltip"] = LLSLURL::buildSLURLfromPosGlobal(region_name,
-						result->mGlobalPos,	false);
+				value["tooltip"] = LLSLURL(region_name, result->mGlobalPos).getSLURLString();
 				add(result->getTitle(), value); 
 			}
 			result = std::find_if(result + 1, th_items.end(), boost::bind(
@@ -832,7 +831,7 @@ void LLLocationInputCtrl::changeLocationPresentation()
 	if(mTextEntry && !mTextEntry->hasSelection() && text == mHumanReadableLocation )
 	{
 		//needs unescaped one
-		mTextEntry->setText(LLAgentUI::buildSLURL(false));
+		mTextEntry->setText(LLAgentUI::buildSLURL(false).getSLURLString());
 		mTextEntry->selectAll();
 	}	
 }
diff --git a/indra/newview/llloginhandler.cpp b/indra/newview/llloginhandler.cpp
index 7d43f6a8cc..482660459e 100644
--- a/indra/newview/llloginhandler.cpp
+++ b/indra/newview/llloginhandler.cpp
@@ -38,7 +38,7 @@
 #include "llsecapi.h"
 #include "llpanellogin.h"			// save_password_to_disk()
 #include "llstartup.h"				// getStartupState()
-#include "llurlsimstring.h"
+#include "llslurl.h"
 #include "llviewercontrol.h"		// gSavedSettings
 #include "llviewernetwork.h"		// EGridInfo
 #include "llviewerwindow.h"                    // getWindow()
@@ -69,7 +69,7 @@ void LLLoginHandler::parse(const LLSD& queryMap)
 	
 	if (queryMap.has("grid"))
 	{
-		LLGridManager::getInstance()->setGridChoice(queryMap["grid"].asString());
+	  LLGridManager::getInstance()->setGridChoice(queryMap["grid"].asString());
 	}
 	
 	
@@ -77,11 +77,16 @@ void LLLoginHandler::parse(const LLSD& queryMap)
 	
 	if (startLocation == "specify")
 	{
-	  LLURLSimString::setString(queryMap["region"].asString());
+	  LLStartUp::setStartSLURL(LLSLURL(LLGridManager::getInstance()->getGridID(),
+					   queryMap["region"].asString()));
 	}
-	else if (!startLocation.empty())
+	else if (startLocation == "home")
 	{
-	  LLURLSimString::setString(startLocation);
+	  LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME));
+	}
+	else if (startLocation == "last")
+	{
+	  LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST));
 	}
 }
 
@@ -153,12 +158,9 @@ bool LLLoginHandler::handle(const LLSD& tokens,
 // ones from the protected credential store.                                                                                
 // This always returns with a credential structure set in the                                                               
 // login handler                                                                                                            
-LLPointer<LLCredential> LLLoginHandler::initializeLoginInfo(const std::string& url)                                         
+LLPointer<LLCredential> LLLoginHandler::initializeLoginInfo()                                         
 {                                                                                                                           
-	LLURI uri(url);                                                                                                      
 	LLPointer<LLCredential> result = NULL;                                                                               
-	parse(uri.queryMap());                                                                                               
-	// we weren't able to parse login info from the slurl,                                                               
 	// so try to load it from the UserLoginInfo                                                                          
 	result = loadSavedUserLoginInfo();                                                                                   
 	if (result.isNull())                                                                                                 
diff --git a/indra/newview/llloginhandler.h b/indra/newview/llloginhandler.h
index ec2459c835..c15b998c91 100644
--- a/indra/newview/llloginhandler.h
+++ b/indra/newview/llloginhandler.h
@@ -51,7 +51,7 @@ class LLLoginHandler : public LLCommandHandler
 	//LLUUID getWebLoginKey() const { return mWebLoginKey; }
 
 	LLPointer<LLCredential> loadSavedUserLoginInfo();  
-	LLPointer<LLCredential> initializeLoginInfo(const std::string& url);
+	LLPointer<LLCredential> initializeLoginInfo();
 
 private:
 	void parse(const LLSD& queryMap);
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index e4b8becdd7..04e5cef62e 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -48,7 +48,8 @@
 // newview
 #include "llviewernetwork.h"
 #include "llviewercontrol.h"
-#include "llurlsimstring.h"
+#include "llslurl.h"
+#include "llstartup.h"
 #include "llfloaterreg.h"
 #include "llnotifications.h"
 #include "llwindow.h"
@@ -56,6 +57,7 @@
 #include "lltrans.h"
 #endif
 #include "llsecapi.h"
+#include "llstartup.h"
 
 static const char * const TOS_REPLY_PUMP = "lllogininstance_tos_callback";
 static const char * const TOS_LISTENER_NAME = "lllogininstance_tos";
@@ -462,15 +464,17 @@ bool LLLoginInstance::updateDialogCallback(const LLSD& notification, const LLSD&
 std::string construct_start_string()
 {
 	std::string start;
-	if (LLURLSimString::parse())
+	LLSLURL start_slurl = LLStartUp::getStartSLURL();
+	if (start_slurl.getType() == LLSLURL::LOCATION)
 	{
 		// a startup URL was specified
+		LLVector3 position = start_slurl.getPosition();
 		std::string unescaped_start = 
 			STRINGIZE(  "uri:" 
-						<< LLURLSimString::sInstance.mSimName << "&" 
-						<< LLURLSimString::sInstance.mX << "&" 
-						<< LLURLSimString::sInstance.mY << "&" 
-						<< LLURLSimString::sInstance.mZ);
+					  << start_slurl.getRegion() << "&" 
+						<< position[VX] << "&" 
+						<< position[VY] << "&" 
+						<< position[VZ]);
 		start = xml_escape_string(unescaped_start);
 	}
 	else
diff --git a/indra/newview/llnavigationbar.cpp b/indra/newview/llnavigationbar.cpp
index 71dc0f9011..8c21fb6314 100644
--- a/indra/newview/llnavigationbar.cpp
+++ b/indra/newview/llnavigationbar.cpp
@@ -50,7 +50,6 @@
 #include "llsearchcombobox.h"
 #include "llsidetray.h"
 #include "llslurl.h"
-#include "llurlsimstring.h"
 #include "llurlregistry.h"
 #include "llurldispatcher.h"
 #include "llviewerinventory.h"
@@ -421,16 +420,15 @@ void LLNavigationBar::onLocationSelection()
 	
 	std::string region_name;
 	LLVector3 local_coords(128, 128, 0);
-	S32 x = 0, y = 0, z = 0;
 	// Is the typed location a SLURL?
-	if (LLSLURL::isSLURL(typed_location))
+	LLSLURL slurl = LLSLURL(typed_location);
+	if (slurl.getType() == LLSLURL::LOCATION)
 	{
 		// Yes. Extract region name and local coordinates from it.
-		if (LLURLSimString::parse(LLSLURL::stripProtocol(typed_location), &region_name, &x, &y, &z))
-				local_coords.set(x, y, z);
-		else
-			return;
-	}else
+		region_name = slurl.getRegion();
+		local_coords = slurl.getPosition();
+	}
+	else
 	{
 		// assume that an user has typed the {region name} or possible {region_name, parcel}
 		region_name  = typed_location.substr(0,typed_location.find(','));
@@ -465,7 +463,7 @@ void LLNavigationBar::onTeleportFinished(const LLVector3d& global_agent_pos)
 	 */
 		LLAgentUI::buildLocationString(location, LLAgentUI::LOCATION_FORMAT_NO_MATURITY,
 					gAgent.getPosAgentFromGlobal(global_agent_pos));
-	std::string tooltip (LLSLURL::buildSLURLfromPosGlobal(gAgent.getRegion()->getName(), global_agent_pos, false));
+	std::string tooltip (LLSLURL(gAgent.getRegion()->getName(), global_agent_pos).getSLURLString());
 	
 	LLLocationHistoryItem item (location,
 			global_agent_pos, tooltip,TYPED_REGION_SURL);// we can add into history only TYPED location
@@ -567,7 +565,7 @@ void LLNavigationBar::onRegionNameResponse(
 	LLVector3d region_pos = from_region_handle(region_handle);
 	LLVector3d global_pos = region_pos + (LLVector3d) local_coords;
 
-	llinfos << "Teleporting to: " << LLSLURL::buildSLURLfromPosGlobal(region_name,	global_pos, false)  << llendl;
+	llinfos << "Teleporting to: " << LLSLURL(region_name,	global_pos).getSLURLString()  << llendl;
 	gAgent.teleportViaLocation(global_pos);
 }
 
diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
index 6978d05389..7752750a31 100644
--- a/indra/newview/llpanellogin.cpp
+++ b/indra/newview/llpanellogin.cpp
@@ -56,7 +56,7 @@
 #include "lltextbox.h"
 #include "llui.h"
 #include "lluiconstants.h"
-#include "llurlsimstring.h"
+#include "llslurl.h"
 #include "llversioninfo.h"
 #include "llviewerhelp.h"
 #include "llviewertexturelist.h"
@@ -106,7 +106,6 @@ public:
 LLLoginRefreshHandler gLoginRefreshHandler;
 
 
-
 // helper class that trys to download a URL from a web site and calls a method 
 // on parent class indicating if the web server is working or not
 class LLIamHereLogin : public LLHTTPClient::Responder
@@ -155,10 +154,6 @@ namespace {
 	boost::intrusive_ptr< LLIamHereLogin > gResponsePtr = 0;
 };
 
-void set_start_location(LLUICtrl* ctrl, void* data)
-{
-    LLURLSimString::setString(ctrl->getValue().asString());
-}
 
 //---------------------------------------------------------------------------
 // Public methods
@@ -228,12 +223,11 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,
 
 	LLComboBox* combo = getChild<LLComboBox>("start_location_combo");
 
-	std::string sim_string = LLURLSimString::sInstance.mSimString;
-	if(sim_string.empty())
+	if(!LLStartUp::getStartSLURL().isValid())
 	{
-		LLURLSimString::setString(gSavedSettings.getString("LoginLocation"));
+		LLStartUp::setStartSLURL(LLSLURL(gSavedSettings.getString("LoginLocation")));
 	}
-
+	std::string sim_string = LLStartUp::getStartSLURL().getRegion();
 	if (!sim_string.empty())
 	{
 		// Replace "<Type region name>" with this region name
@@ -242,8 +236,8 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,
 		combo->setTextEntry(sim_string);
 		combo->setCurrentByIndex( 2 );
 	}
-
-	combo->setCommitCallback( &set_start_location, NULL );
+	
+	combo->setCommitCallback(onSelectLocation, NULL);
 
 	LLComboBox* server_choice_combo = sInstance->getChild<LLComboBox>("server_combo");
 	server_choice_combo->setCommitCallback(onSelectServer, NULL);
@@ -306,11 +300,8 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,
 	gResponsePtr = LLIamHereLogin::build( this );
 
 	LLHTTPClient::head( LLGridManager::getInstance()->getLoginPage(), gResponsePtr );
-
-#if !USE_VIEWER_AUTH
-	// Initialize visibility (and don't force visibility - use prefs)
-	refreshLocation( false );
-#endif
+	
+	updateLocationCombo(false);
 
 }
 
@@ -674,46 +665,141 @@ BOOL LLPanelLogin::isGridComboDirty()
 }
 
 // static
-void LLPanelLogin::getLocation(std::string &location)
+BOOL LLPanelLogin::areCredentialFieldsDirty()
 {
 	if (!sInstance)
 	{
-		llwarns << "Attempted getLocation with no login view shown" << llendl;
+		llwarns << "Attempted getServer with no login view shown" << llendl;
+	}
+	else
+	{
+		std::string username = sInstance->childGetText("username_edit");
+		LLStringUtil::trim(username);
+		std::string password = sInstance->childGetText("password_edit");
+		LLLineEditor* ctrl = sInstance->getChild<LLLineEditor>("username_edit");
+		if(ctrl && ctrl->isDirty())
+		{
+			return true;
+		}
+		ctrl = sInstance->getChild<LLLineEditor>("password_edit");
+		if(ctrl && ctrl->isDirty()) 
+		{
+			return true;
+		}
+	}
+	return false;	
+}
+
+
+// static
+void LLPanelLogin::updateLocationCombo( bool force_visible )
+{
+	if (!sInstance) 
+	{
 		return;
+	}	
+	LLComboBox* combo = sInstance->getChild<LLComboBox>("start_location_combo");
+	
+	switch(LLStartUp::getStartSLURL().getType())
+	{
+		case LLSLURL::LOCATION:
+		{
+			
+			combo->setCurrentByIndex( 2 );	
+			combo->setTextEntry(LLStartUp::getStartSLURL().getLocationString());	
+			break;
+		}
+		case LLSLURL::HOME_LOCATION:
+			combo->setCurrentByIndex(0);
+			break;
+		default:
+			combo->setCurrentByIndex(1);
+			break;
 	}
 	
-	LLComboBox* combo = sInstance->getChild<LLComboBox>("start_location_combo");
-	location = combo->getValue().asString();
+	BOOL show_start = TRUE;
+	
+	if ( ! force_visible )
+		show_start = gSavedSettings.getBOOL("ShowStartLocation");
+	
+	sInstance->childSetVisible("start_location_combo", show_start);
+	sInstance->childSetVisible("start_location_text", show_start);
+	
+	sInstance->childSetVisible("server_combo", TRUE);
 }
 
 // static
-void LLPanelLogin::refreshLocation( bool force_visible )
+void LLPanelLogin::onSelectLocation(LLUICtrl*, void*)
 {
 	if (!sInstance) return;
-
-#if USE_VIEWER_AUTH
-	loadLoginPage();
-#else
-	BOOL show_start = TRUE;
-
-	if ( ! force_visible )
+	
+	LLComboBox* combo = sInstance->getChild<LLComboBox>("start_location_combo");
+	S32 index = combo->getCurrentIndex();
+	
+	switch (index)
 	{
-		// Don't show on first run after install
-		// Otherwise ShowStartLocation defaults to true.
-		show_start = gSavedSettings.getBOOL("ShowStartLocation")
-					&& !gSavedSettings.getBOOL("FirstRunThisInstall");
+		case 2:
+		{
+			LLSLURL slurl = LLSLURL(combo->getSelectedValue());
+			if((slurl.getType() == LLSLURL::LOCATION) &&
+			   (slurl.getGrid() != LLStartUp::getStartSLURL().getGrid()))
+			{
+				LLStartUp::setStartSLURL(slurl);
+				// we've changed the grid, so update the grid selection
+				try 
+				{
+					LLGridManager::getInstance()->setGridChoice(slurl.getGrid());
+				}
+				catch (LLInvalidGridName ex)
+				{
+					LLSD args;	
+					args["GRID"] = slurl.getGrid();
+					LLNotificationsUtil::add("InvalidGrid", args);
+					return; 
+				}	
+				loadLoginPage();
+			}
+			break;
+		}
+		case 1:
+		{
+			LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST));
+			break;
+		}
+		default:
+		{
+			LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME));
+			break;
+		}
 	}
+}
 
-	sInstance->childSetVisible("start_location_combo", show_start);
-	sInstance->childSetVisible("start_location_text", show_start);
-
-	// should be true for enterprise viewer
-	BOOL show_server = gSavedSettings.getBOOL("ForceShowGrid");
-	sInstance->childSetVisible("server_combo", show_server);
 
-#endif
+// static
+LLSLURL LLPanelLogin::getLocation()
+{
+	LLSLURL result;
+	if (!sInstance)
+	{
+		llwarns << "Attempted getLocation with no login view shown" << llendl;
+		return result;
+	}
+	
+	LLComboBox* combo = sInstance->getChild<LLComboBox>("start_location_combo");
+	
+	switch(combo->getCurrentIndex())
+	{
+		case 0:
+			return LLSLURL(LLSLURL::SIM_LOCATION_HOME);
+		case 1:
+			return LLSLURL(LLSLURL::SIM_LOCATION_LAST);
+		default:
+			// construct a real slurl
+			return LLSLURL(LLURI::escape(combo->getValue().asString()));
+	}
 }
 
+
 // static
 void LLPanelLogin::closePanel()
 {
@@ -748,6 +834,7 @@ void LLPanelLogin::loadLoginPage()
 	std::ostringstream oStr;
 
 	std::string login_page = LLGridManager::getInstance()->getLoginPage();
+
 	oStr << login_page;
 	
 	// Use the right delimeter depending on how LLURI parses the URL
@@ -859,7 +946,7 @@ void LLPanelLogin::loadLoginPage()
 #endif
 	
 	LLMediaCtrl* web_browser = sInstance->getChild<LLMediaCtrl>("login_html");
-	
+
 	// navigate to the "real" page
 	if (gSavedSettings.getBOOL("RegInClient"))
 	{
@@ -1006,30 +1093,59 @@ void LLPanelLogin::onPassKey(LLLineEditor* caller, void* user_data)
 	}
 }
 
+
+void LLPanelLogin::updateServer(std::string grid)
+{
+	try 
+	{
+		LLGridManager::getInstance()->setGridChoice(grid);
+
+		updateServerCombo();	
+		// if they've selected another grid, we should load the credentials
+		// for that grid and set them to the UI.
+		if(sInstance && !sInstance->areCredentialFieldsDirty())
+		{
+			LLPointer<LLCredential> credential = gSecAPIHandler->loadCredential(grid);	
+			bool remember = sInstance->childGetValue("remember_check");
+			sInstance->setFields(credential, remember);
+		}
+		// grid changed so show new splash screen (possibly)
+		loadLoginPage();
+		updateLocationCombo(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION);
+	}
+	catch (LLInvalidGridName ex)
+	{
+		// do nothing
+	}}
+
 void LLPanelLogin::updateServerCombo()
 {
+	if (!sInstance) 
+	{
+		return;	
+	}
 	// We add all of the possible values, sorted, and then add a bar and the current value at the top
-	LLGridManager* viewer_login = LLGridManager::getInstance();
 	LLComboBox* server_choice_combo = sInstance->getChild<LLComboBox>("server_combo");	
 	server_choice_combo->removeall();
-	std::map<std::string, std::string> known_grids = viewer_login->getKnownGrids();
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+	std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids(TRUE);
+#else
+	std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids(FALSE);	
+#endif
 	for (std::map<std::string, std::string>::iterator grid_choice = known_grids.begin();
 		 grid_choice != known_grids.end();
 		 grid_choice++)
+	{
+		if (!grid_choice->first.empty())
 		{
-			//if (!grid_choice->first.empty())
-			{
-				LL_INFOS("Credentials") << "adding " << grid_choice->second << ":" << grid_choice->first << LL_ENDL;
-				server_choice_combo->add(grid_choice->second, grid_choice->first, ADD_SORTED);
-			}
+			server_choice_combo->add(grid_choice->second, grid_choice->first, ADD_SORTED);
 		}
+	}
 	
 	server_choice_combo->addSeparator(ADD_TOP);
 	
-	LL_INFOS("Credentials") << "adding top grid choice by " << viewer_login->getGridLabel() << LL_ENDL;
-	server_choice_combo->add(viewer_login->getGridLabel(), 
-							 viewer_login->getGridName(), 
-							 ADD_TOP);	
+	server_choice_combo->add(LLGridManager::getInstance()->getGridLabel(), 
+		LLGridManager::getInstance()->getGridName(), ADD_TOP);	
 	
 	server_choice_combo->selectFirstItem();	
 }
@@ -1040,37 +1156,26 @@ void LLPanelLogin::onSelectServer(LLUICtrl*, void*)
 	// *NOTE: The paramters for this method are ignored. 
 	// LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe, void*)
 	// calls this method.
-
+	LL_INFOS("AppInit") << "onSelectServer" << LL_ENDL;
 	// The user twiddled with the grid choice ui.
 	// apply the selection to the grid setting.
 	LLPointer<LLCredential> credential;
-	BOOL remember = FALSE;
-
+	
 	LLComboBox* combo = sInstance->getChild<LLComboBox>("server_combo");
 	LLSD combo_val = combo->getSelectedValue();
 	if (combo_val.isUndefined())
 	{
-	  combo_val = combo->getValue();
+		combo_val = combo->getValue();
 	}
-
-	// This new selection will override preset uris
-	// from the command line.
-
-	LLGridManager::getInstance()->setGridChoice(combo_val.asString());
-	updateServerCombo();
-
-	// grid changed so show new splash screen (possibly)
-	loadLoginPage();
 	
-	// if they've selected another grid, we should load the credentials
-	// for that grid and set them to the UI.
-	credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGridName());
+	combo = sInstance->getChild<LLComboBox>("start_location_combo");	
+	combo->setCurrentByIndex(1);
+	LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST));
 	
-
-	remember = sInstance->childGetValue("remember_check");
-	sInstance->setFields(credential, remember);
-
-	LL_INFOS("Credentials") << "Grid changed to:" << LLGridManager::getInstance()->getGridName() << LL_ENDL;
+	// This new seelction will override preset uris
+	// from the command line.
+	updateServer(combo_val.asString());
+	updateLoginPanelLinks();
 }
 
 void LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe)
@@ -1083,3 +1188,14 @@ void LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe)
 		onSelectServer(combo, NULL);	
 	}
 }
+
+void LLPanelLogin::updateLoginPanelLinks()
+{
+	LLSD grid_data = LLGridManager::getInstance()->getGridInfo();
+	bool system_grid = grid_data.has(GRID_IS_SYSTEM_GRID_VALUE);
+	
+	// need to call through sInstance, as it's called from onSelectServer, which
+	// is static.
+	sInstance->childSetVisible("create_new_account_text", system_grid);
+	sInstance->childSetVisible("forgot_password_text", system_grid);
+}
diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h
index d33aa2d550..4f18b7bfd8 100644
--- a/indra/newview/llpanellogin.h
+++ b/indra/newview/llpanellogin.h
@@ -38,6 +38,7 @@
 #include "llmediactrl.h"	// LLMediaCtrlObserver
 #include <boost/scoped_ptr.hpp>
 #include "llsecapi.h"
+#include "llslurl.h"
 
 class LLLineEditor;
 class LLUIImage;
@@ -68,13 +69,13 @@ public:
 
 	static void setFields(LLPointer<LLCredential> credential, BOOL remember);
 
-	static void refreshLocation( bool force_visible );
-
 	static void getFields(LLPointer<LLCredential>& credential, BOOL& remember);
 
 	static BOOL isGridComboDirty();
-	static void getLocation(std::string &location);
-
+	static BOOL areCredentialFieldsDirty();
+	static LLSLURL getLocation();
+	
+	static void updateLocationCombo(bool force_visible);  // simply update the combo box
 	static void closePanel();
 
 	void setSiteIsAlive( bool alive );
@@ -99,7 +100,11 @@ private:
 	static void onSelectServer(LLUICtrl*, void*);
 	static void onServerComboLostFocus(LLFocusableElement*);
 	static void updateServerCombo();
+	static void onSelectLocation(LLUICtrl*, void*);
 	
+	static void updateServer(std::string grid);  // update the combo box, change the login page to the new server, clear the combo
+	static void updateLoginPanelLinks();
+
 private:
 	LLPointer<LLUIImage> mLogoImage;
 	boost::scoped_ptr<LLPanelLoginListener> mListener;
diff --git a/indra/newview/llpanelplacestab.cpp b/indra/newview/llpanelplacestab.cpp
index 9806b8c64d..6b12796e59 100644
--- a/indra/newview/llpanelplacestab.cpp
+++ b/indra/newview/llpanelplacestab.cpp
@@ -70,10 +70,7 @@ void LLPanelPlacesTab::onRegionResponse(const LLVector3d& landmark_global_pos,
 	std::string sl_url;
 	if ( gotSimName )
 	{
-		F32 region_x = (F32)fmod( landmark_global_pos.mdV[VX], (F64)REGION_WIDTH_METERS );
-		F32 region_y = (F32)fmod( landmark_global_pos.mdV[VY], (F64)REGION_WIDTH_METERS );
-
-		sl_url = LLSLURL::buildSLURL(sim_name, llround(region_x), llround(region_y), llround((F32)landmark_global_pos.mdV[VZ]));
+		sl_url = LLSLURL(sim_name, landmark_global_pos).getSLURLString();
 	}
 	else
 	{
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index 60a095506b..36ed1c466b 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -2434,7 +2434,7 @@ BOOL LLSelectMgr::selectGetCreator(LLUUID& result_id, std::string& name)
 	
 	if (identical)
 	{
-		name = LLSLURL::buildCommand("agent", first_id, "inspect");
+		name = LLSLURL("agent", first_id, "inspect").getSLURLString();
 	}
 	else
 	{
@@ -2493,11 +2493,11 @@ BOOL LLSelectMgr::selectGetOwner(LLUUID& result_id, std::string& name)
 		BOOL public_owner = (first_id.isNull() && !first_group_owned);
 		if (first_group_owned)
 		{
-			name = LLSLURL::buildCommand("group", first_id, "inspect");
+			name = LLSLURL("group", first_id, "inspect").getSLURLString();
 		}
 		else if(!public_owner)
 		{
-			name = LLSLURL::buildCommand("agent", first_id, "inspect");
+			name = LLSLURL("agent", first_id, "inspect").getSLURLString();
 		}
 		else
 		{
@@ -2557,7 +2557,7 @@ BOOL LLSelectMgr::selectGetLastOwner(LLUUID& result_id, std::string& name)
 		BOOL public_owner = (first_id.isNull());
 		if(!public_owner)
 		{
-			name = LLSLURL::buildCommand("agent", first_id, "inspect");
+			name = LLSLURL("agent", first_id, "inspect").getSLURLString();
 		}
 		else
 		{
diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp
index 37e268ad34..43c505fae6 100644
--- a/indra/newview/llslurl.cpp
+++ b/indra/newview/llslurl.cpp
@@ -1,10 +1,11 @@
 /** 
- * @file llslurl.cpp
- * @brief SLURL manipulation
+ * @file llurlsimstring.cpp (was llsimurlstring.cpp)
+ * @brief Handles "SLURL fragments" like Ahern/123/45 for
+ * startup processing, login screen, prefs, etc.
  *
- * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * $LicenseInfo:firstyear=2006&license=viewergpl$
  * 
- * Copyright (c) 2009, Linden Research, Inc.
+ * Copyright (c) 2006-2007, Linden Research, Inc.
  * 
  * Second Life Viewer Source Code
  * The source code in this file ("Source Code") is provided by Linden Lab
@@ -12,13 +13,12 @@
  * ("GPL"), unless you have obtained a separate licensing agreement
  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * online at http://secondlife.com/developers/opensource/gplv2
  * 
  * There are special exceptions to the terms and conditions of the GPL as
  * it is applied to this Source Code. View the full text of the exception
  * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * online at http://secondlife.com/developers/opensource/flossexception
  * 
  * By copying, modifying or distributing this software, you acknowledge
  * that you have read and understood your obligations described above,
@@ -34,120 +34,422 @@
 
 #include "llslurl.h"
 
-#include "llweb.h"
+#include "llpanellogin.h"
+#include "llviewercontrol.h"
+#include "llviewernetwork.h"
+#include "llfiltersd2xmlrpc.h"
+#include "curl/curl.h"
+const char* LLSLURL::SLURL_HTTP_SCHEME			= "http";
+const char* LLSLURL::SLURL_HTTPS_SCHEME			= "https";
+const char* LLSLURL::SLURL_SECONDLIFE_SCHEME	= "secondlife";
+const char* LLSLURL::SLURL_SECONDLIFE_PATH	= "secondlife";
+const char* LLSLURL::SLURL_COM		            = "slurl.com";	
+const char* LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME = "x-grid-location-info";
+const char* LLSLURL::SLURL_APP_PATH             = "app";
+const char* LLSLURL::SLURL_REGION_PATH          = "region";
+const char* LLSLURL::SIM_LOCATION_HOME          = "MyHome";
+const char* LLSLURL::SIM_LOCATION_LAST          = "MyLastLocation";
 
-const std::string LLSLURL::PREFIX_SL_HELP		= "secondlife://app.";
-const std::string LLSLURL::PREFIX_SL			= "sl://";
-const std::string LLSLURL::PREFIX_SECONDLIFE	= "secondlife://";
-const std::string LLSLURL::PREFIX_SLURL			= "http://slurl.com/secondlife/";
-
-const std::string LLSLURL::APP_TOKEN = "app/";
-
-// static
-std::string LLSLURL::stripProtocol(const std::string& url)
+// resolve a simstring from a slurl
+LLSLURL::LLSLURL(const std::string& slurl)
 {
-	std::string stripped = url;
-	if (matchPrefix(stripped, PREFIX_SL_HELP))
-	{
-		stripped.erase(0, PREFIX_SL_HELP.length());
-	}
-	else if (matchPrefix(stripped, PREFIX_SL))
+	// by default we go to agni.
+	mType = INVALID;
+	LL_INFOS("AppInit") << "SLURL: " << slurl << LL_ENDL;
+	if(slurl == SIM_LOCATION_HOME)
 	{
-		stripped.erase(0, PREFIX_SL.length());
+		mType = HOME_LOCATION;
 	}
-	else if (matchPrefix(stripped, PREFIX_SECONDLIFE))
+	else if(slurl.empty() || (slurl == SIM_LOCATION_LAST))
 	{
-		stripped.erase(0, PREFIX_SECONDLIFE.length());
+		mType = LAST_LOCATION;
 	}
-	else if (matchPrefix(stripped, PREFIX_SLURL))
+	else
 	{
-		stripped.erase(0, PREFIX_SLURL.length());
+		LLURI slurl_uri;
+		// parse the slurl as a uri
+		if(slurl.find(':') == std::string::npos)
+		{
+			// There may be no scheme ('secondlife:' etc.) passed in.  In that case
+			// we want to normalize the slurl by putting the appropriate scheme
+			// in front of the slurl.  So, we grab the appropriate slurl base
+			// from the grid manager which may be http://slurl.com/secondlife/ for maingrid, or
+			// https://<hostname>/region/ for nebraska grid (the word region, not the region name)
+			// these slurls are typically passed in from the 'starting location' box on the login panel,
+			// where the user can type in <regionname>/<x>/<y>/<z>
+			
+			std::string fixed_slurl = LLGridManager::getInstance()->getSLURLBase();
+			// the slurl that was passed in might have a prepended /, or not.  So,
+			// we strip off the prepended '/' so we don't end up with http://slurl.com/secondlife/<region>/<x>/<y>/<z>
+			// or some such.
+			if(slurl[0] == '/')
+		    {
+				fixed_slurl += slurl.substr(1);
+		    }
+			else
+		    {
+				fixed_slurl += slurl;
+		    }
+			// We then load the slurl into a LLURI form
+			slurl_uri = LLURI(fixed_slurl);
+		}
+		else
+		{
+		    // as we did have a scheme, implying a URI style slurl, we
+		    // simply parse it as a URI
+		    slurl_uri = LLURI(slurl);
+		}
+		
+		LLSD path_array = slurl_uri.pathArray();
+		
+		// determine whether it's a maingrid URI or an nebraska/open style URI
+		// by looking at the scheme.  If it's a 'secondlife:' slurl scheme or
+		// 'sl:' scheme, we know it's maingrid
+		
+		// At the end of this if/else block, we'll have determined the grid,
+		// and the slurl type (APP or LOCATION)
+		if(slurl_uri.scheme() == LLSLURL::SLURL_SECONDLIFE_SCHEME)
+		{
+			// parse a maingrid style slurl.  We know the grid is maingrid
+			// so grab it.
+			// A location slurl for maingrid (with the special schemes) can be in the form
+			// secondlife://<regionname>/<x>/<y>/<z>
+			// or
+			// secondlife://<Grid>/secondlife/<region>/<x>/<y>/<z>
+			// where if grid is empty, it specifies Agni
+			
+			// An app style slurl for maingrid can be
+			// secondlife://<Grid>/app/<app parameters>
+			// where an empty grid implies Agni
+			
+			// we'll start by checking the top of the 'path' which will be 
+			// either 'app', 'secondlife', or <x>.
+			
+			// default to maingrid
+			
+			mGrid = MAINGRID;
+			
+			if ((path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) ||
+				(path_array[0].asString() == LLSLURL::SLURL_APP_PATH))
+		    {
+				// it's in the form secondlife://<grid>/(app|secondlife)
+				// so parse the grid name to derive the grid ID
+				if (!slurl_uri.hostName().empty())
+				{
+					mGrid = LLGridManager::getInstance()->getGridByLabel(slurl_uri.hostName());
+				}
+				else if(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH)
+				{
+					// If the slurl is in the form secondlife:///secondlife/<region> form, 
+					// then we are in fact on maingrid.  
+					mGrid = MAINGRID;
+				}
+				else if(path_array[0].asString() == LLSLURL::SLURL_APP_PATH)
+				{
+					// for app style slurls, where no grid name is specified, assume the currently
+					// selected or logged in grid.
+					mGrid =  LLGridManager::getInstance()->getGridName();
+				}
+
+				if(mGrid.empty())
+				{
+					// we couldn't find the grid in the grid manager, so bail
+					return;
+				}
+				// set the type as appropriate.
+				if(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH)
+				{
+					mType = LOCATION;
+				}
+				else
+				{
+					mType = APP;
+				}
+				path_array.erase(0);
+		    }
+			else
+		    {
+				// it wasn't a /secondlife/<region> or /app/<params>, so it must be secondlife://<region>
+				// therefore the hostname will be the region name, and it's a location type
+				mType = LOCATION;
+				// 'normalize' it so the region name is in fact the head of the path_array
+				path_array.insert(0, slurl_uri.hostName());
+		    }
+		}
+		else if((slurl_uri.scheme() == LLSLURL::SLURL_HTTP_SCHEME) ||
+		   (slurl_uri.scheme() == LLSLURL::SLURL_HTTPS_SCHEME) || 
+		   (slurl_uri.scheme() == LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME))
+		{
+		    // We're dealing with either a nebraska style slurl or slurl.com slurl
+		    if (slurl_uri.hostName() == LLSLURL::SLURL_COM)
+			{
+				// slurl.com implies maingrid
+				mGrid = MAINGRID;
+			}
+		    else
+			{
+				// As it's a nebraska grid/open, we will always have a hostname, as nebraska/open  style
+				// urls are properly formed, unlike the stinky maingrid style
+				mGrid = slurl_uri.hostName();
+			}
+		    if (path_array.size() == 0)
+			{
+				// um, we need a path...
+				return;
+			}
+			
+			// we need to normalize the urls so
+			// the path portion starts with the 'command' that we want to do
+			// it can either be region or app.  
+		    if ((path_array[0].asString() == LLSLURL::SLURL_REGION_PATH) ||
+				(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH))
+			{
+				// strip off 'region' or 'secondlife'
+				path_array.erase(0);
+				// it's a location
+				mType = LOCATION;
+			}
+			else if (path_array[0].asString() == LLSLURL::SLURL_APP_PATH)
+			{
+				mType = APP;
+				path_array.erase(0);
+				// leave app appended.  
+			}
+			else
+			{
+				// not a valid https/http/x-grid-location-info slurl, so it'll likely just be a URL
+				return;
+			}
+		}
+		else
+		{
+		    // invalid scheme, so bail
+		    return;
+		}
+		
+		
+		if(path_array.size() == 0)
+		{
+			// we gotta have some stuff after the specifier as to whether it's a region or command
+			return;
+		}
+		
+		// now that we know whether it's an app slurl or a location slurl,
+		// parse the slurl into the proper data structures.
+		if(mType == APP)
+		{		
+			// grab the app command type and strip it (could be a command to jump somewhere, 
+			// or whatever )
+			mAppCmd = path_array[0].asString();
+			path_array.erase(0);
+			
+			// Grab the parameters
+			mAppPath = path_array;
+			// and the query
+			mAppQuery = slurl_uri.query();
+			mAppQueryMap = slurl_uri.queryMap();
+			return;
+		}
+		else if(mType == LOCATION)
+		{
+			// at this point, head of the path array should be [ <region>, <x>, <y>, <z> ] where x, y and z 
+			// are collectively optional
+			// are optional
+			mRegion = LLURI::unescape(path_array[0].asString());
+			path_array.erase(0);
+			
+			// parse the x, y, z
+			if(path_array.size() >= 3)
+			{	
+				mPosition = LLVector3(path_array);
+			}
+			else
+			{
+				// if x, y and z were not fully passed in, go to the middle of the region.
+				// teleport will adjust the actual location to make sure you're on the ground
+				// and such
+				mPosition = LLVector3(REGION_WIDTH_METERS/2, REGION_WIDTH_METERS/2, 0);
+			}
+		}
 	}
-	
-	return stripped;
 }
 
-// static
-bool LLSLURL::isSLURL(const std::string& url)
+
+// Create a slurl for the middle of the region
+LLSLURL::LLSLURL(const std::string& grid, 
+				 const std::string& region)
 {
-	if (matchPrefix(url, PREFIX_SL_HELP))		return true;
-	if (matchPrefix(url, PREFIX_SL))			return true;
-	if (matchPrefix(url, PREFIX_SECONDLIFE))	return true;
-	if (matchPrefix(url, PREFIX_SLURL))			return true;
-	
-	return false;
+	mGrid = grid;
+	mRegion = region;
+	mType = LOCATION;
+	mPosition = LLVector3((F64)REGION_WIDTH_METERS/2, (F64)REGION_WIDTH_METERS/2, 0);
+}
+
+
+
+// create a slurl given the position.  The position will be modded with the region
+// width handling global positions as well
+LLSLURL::LLSLURL(const std::string& grid, 
+		 const std::string& region, 
+		 const LLVector3& position)
+{
+	mGrid = grid;
+	mRegion = region;
+	S32 x = llround( (F32)fmod( position[VX], (F32)REGION_WIDTH_METERS ) );
+	S32 y = llround( (F32)fmod( position[VY], (F32)REGION_WIDTH_METERS ) );
+	S32 z = llround( (F32)position[VZ] );
+	mType = LOCATION;
+	mPosition = LLVector3(x, y, z);
 }
 
-// static
-bool LLSLURL::isSLURLCommand(const std::string& url)
-{ 
-	if (matchPrefix(url, PREFIX_SL + APP_TOKEN) ||
-		matchPrefix(url, PREFIX_SECONDLIFE + "/" + APP_TOKEN) ||
-		matchPrefix(url, PREFIX_SLURL + APP_TOKEN) )
-	{
-		return true;
-	}
 
-	return false;
+// create a simstring
+LLSLURL::LLSLURL(const std::string& region, 
+		 const LLVector3& position)
+{
+  *this = LLSLURL(LLGridManager::getInstance()->getGridName(),
+		  region, position);
 }
 
-// static
-bool LLSLURL::isSLURLHelp(const std::string& url)
+// create a slurl from a global position
+LLSLURL::LLSLURL(const std::string& grid, 
+		 const std::string& region, 
+		 const LLVector3d& global_position)
 {
-	return matchPrefix(url, PREFIX_SL_HELP);
+  *this = LLSLURL(grid,
+		  region, LLVector3(global_position.mdV[VX],
+				    global_position.mdV[VY],
+				    global_position.mdV[VZ]));
 }
 
-// static
-std::string LLSLURL::buildSLURL(const std::string& regionname, S32 x, S32 y, S32 z)
+// create a slurl from a global position
+LLSLURL::LLSLURL(const std::string& region, 
+		 const LLVector3d& global_position)
 {
-	std::string slurl = PREFIX_SLURL + regionname + llformat("/%d/%d/%d",x,y,z); 
-	slurl = LLWeb::escapeURL( slurl );
-	return slurl;
+  *this = LLSLURL(LLGridManager::getInstance()->getGridName(),
+		  region, global_position);
 }
 
-// static
-std::string LLSLURL::buildCommand(const char* noun, const LLUUID& id, const char* verb)
+LLSLURL::LLSLURL(const std::string& command, const LLUUID&id, const std::string& verb)
 {
-	std::string slurl = llformat("secondlife:///app/%s/%s/%s",
-		noun, id.asString().c_str(), verb);
-	return slurl;
+  mType = APP;
+  mAppCmd = command;
+  mAppPath = LLSD::emptyArray();
+  mAppPath.append(LLSD(id));
+  mAppPath.append(LLSD(verb));
 }
 
-// static
-std::string LLSLURL::buildUnescapedSLURL(const std::string& regionname, S32 x, S32 y, S32 z)
+
+std::string LLSLURL::getSLURLString() const
 {
-	std::string unescapedslurl = PREFIX_SLURL + regionname + llformat("/%d/%d/%d",x,y,z);
-	return unescapedslurl;
+	switch(mType)
+	{
+		case HOME_LOCATION:
+			return SIM_LOCATION_HOME;
+		case LAST_LOCATION:
+			return SIM_LOCATION_LAST;
+		case LOCATION:
+			{
+				// lookup the grid
+				S32 x = llround( (F32)mPosition[VX] );
+				S32 y = llround( (F32)mPosition[VY] );
+				S32 z = llround( (F32)mPosition[VZ] );	
+				return LLGridManager::getInstance()->getSLURLBase(mGrid) + 
+				LLURI::escape(mRegion) + llformat("/%d/%d/%d",x,y,z); 
+			}
+		case APP:
+		{
+			std::ostringstream app_url;
+			app_url << LLGridManager::getInstance()->getAppSLURLBase() << "/" << mAppCmd;
+			for(LLSD::array_const_iterator i = mAppPath.beginArray();
+				i != mAppPath.endArray();
+				i++)
+			{
+				app_url << "/" << i->asString();
+			}
+			if(mAppQuery.length() > 0)
+			{
+				app_url << "?" << mAppQuery;
+			}
+			return app_url.str();
+		}	
+		default:
+			LL_WARNS("AppInit") << "Unexpected SLURL type for SLURL string" << (int)mType << LL_ENDL;			
+			return std::string();
+	}
 }
 
-// static
-std::string LLSLURL::buildSLURLfromPosGlobal(const std::string& regionname,
-											 const LLVector3d& global_pos,
-											 bool escaped /*= true*/)
+std::string LLSLURL::getLoginString() const
 {
-	S32 x, y, z;
-	globalPosToXYZ(global_pos, x, y, z);
-	if(escaped)
+	
+	std::stringstream unescaped_start;
+	switch(mType)
 	{
-		return buildSLURL(regionname, x, y, z);
+		case LOCATION:
+			unescaped_start << "uri:" 
+			<< mRegion << "&" 
+			<< llround(mPosition[0]) << "&" 
+			<< llround(mPosition[1]) << "&" 
+			<< llround(mPosition[2]);
+			break;
+		case HOME_LOCATION:
+			unescaped_start << "home";
+			break;
+		case LAST_LOCATION:
+			unescaped_start << "last";
+			break;
+		default:
+			LL_WARNS("AppInit") << "Unexpected SLURL type for login string" << (int)mType << LL_ENDL;
+			break;
 	}
-	else
+	return  xml_escape_string(unescaped_start.str());
+}
+
+bool LLSLURL::operator==(const LLSLURL& rhs)
+{
+	if(rhs.mType != mType) return false;
+	switch(mType)
 	{
-		return buildUnescapedSLURL(regionname, x, y, z);
+		case LOCATION:
+			return ((mGrid == rhs.mGrid) &&
+					(mRegion == rhs.mRegion) &&
+					(mPosition == rhs.mPosition));
+		case APP:
+			return getSLURLString() == rhs.getSLURLString();
+			
+		case HOME_LOCATION:
+		case LAST_LOCATION:
+			return true;
+		default:
+			return false;
 	}
 }
 
-// static
-bool LLSLURL::matchPrefix(const std::string& url, const std::string& prefix)
+bool LLSLURL::operator !=(const LLSLURL& rhs)
 {
-	std::string test_prefix = url.substr(0, prefix.length());
-	LLStringUtil::toLower(test_prefix);
-	return test_prefix == prefix;
+	return !(*this == rhs);
 }
 
-void LLSLURL::globalPosToXYZ(const LLVector3d& pos, S32& x, S32& y, S32& z)
+std::string LLSLURL::getLocationString() const
 {
-	x = llround((F32)fmod(pos.mdV[VX], (F64)REGION_WIDTH_METERS));
-	y = llround((F32)fmod(pos.mdV[VY], (F64)REGION_WIDTH_METERS));
-	z = llround((F32)pos.mdV[VZ]);
+	return llformat("%s/%d/%d/%d",
+					mRegion.c_str(),
+					(int)llround(mPosition[0]),
+					(int)llround(mPosition[1]),
+					(int)llround(mPosition[2]));						 
 }
+std::string LLSLURL::asString() const
+{
+    std::ostringstream result;
+    result << "   mAppCmd:"  << getAppCmd() <<
+              "   mAppPath:" + getAppPath().asString() <<
+              "   mAppQueryMap:" + getAppQueryMap().asString() <<
+              "   mAppQuery: " + getAppQuery() <<
+              "   mGrid: " + getGrid() <<
+              "   mRegion: " + getRegion() <<
+              "   mPosition: "  <<
+              "   mType: " << mType <<
+              "   mPosition: " << mPosition;
+    return result.str();
+}
+
diff --git a/indra/newview/llslurl.h b/indra/newview/llslurl.h
index 05b0143e72..c12ace17fd 100644
--- a/indra/newview/llslurl.h
+++ b/indra/newview/llslurl.h
@@ -1,6 +1,7 @@
-/** 
+/**
  * @file llslurl.h
- * @brief SLURL manipulation
+ * @brief Handles "SLURL fragments" like Ahern/123/45 for
+ * startup processing, login screen, prefs, etc.
  *
  * $LicenseInfo:firstyear=2009&license=viewergpl$
  * 
@@ -12,13 +13,12 @@
  * ("GPL"), unless you have obtained a separate licensing agreement
  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * online at http://secondlife.com/developers/opensource/gplv2
  * 
  * There are special exceptions to the terms and conditions of the GPL as
  * it is applied to this Source Code. View the full text of the exception
  * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * online at http://secondlife.com/developers/opensource/flossexception
  * 
  * By copying, modifying or distributing this software, you acknowledge
  * that you have read and understood your obligations described above,
@@ -29,78 +29,82 @@
  * COMPLETENESS OR PERFORMANCE.
  * $/LicenseInfo$
  */
+#ifndef LLSLURL_H
+#define LLSLURL_H
 
-#ifndef LL_SLURL_H
-#define LL_SLURL_H
+#include "llstring.h"
 
-#include <string>
 
-// IAN BUG: where should this live?
-// IAN BUG: are static utility functions right?  See LLUUID.
-// question of whether to have a LLSLURL object or a 
-// some of this was moved from LLURLDispatcher
+// represents a location in a grid
 
-/**
- * SLURL manipulation
- */
 class LLSLURL
 {
 public:
-	static const std::string PREFIX_SL_HELP;
-	static const std::string PREFIX_SL;
-	static const std::string PREFIX_SECONDLIFE;
-	static const std::string PREFIX_SLURL;
-
-	static const std::string APP_TOKEN;
-
-	/**
-	 * Is this any sort of secondlife:// or sl:// URL?
-	 */
-	static bool isSLURL(const std::string& url);
-
-	/**
-	 * Is this a special secondlife://app/ URL?
-	 */
-	static bool isSLURLCommand(const std::string& url);
-
-	/**
-	 * Not sure what it is.
-	 */
-	static bool isSLURLHelp(const std::string& url);
-
-	/**
-	 * builds: http://slurl.com/secondlife/Region%20Name/x/y/z/ escaping result url.
-	 */
-	static std::string buildSLURL(const std::string& regionname, S32 x, S32 y, S32 z);
-
-	/// Build a SLURL like secondlife:///app/agent/<uuid>/inspect
-	static std::string buildCommand(const char* noun, const LLUUID& id, const char* verb);
-
-	/**
-	 * builds: http://slurl.com/secondlife/Region Name/x/y/z/ without escaping result url.
-	 */
-	static std::string buildUnescapedSLURL(const std::string& regionname, S32 x, S32 y, S32 z);
-
-	/**
-	 * builds SLURL from global position. Returns escaped or unescaped url.
-	 * Returns escaped url by default.
-	 */
-	static std::string buildSLURLfromPosGlobal(const std::string& regionname,
-											   const LLVector3d& global_pos,
-											   bool escaped = true);
-	/**
-	 * Strip protocol part from the URL.
-	 */
-	static std::string stripProtocol(const std::string& url);
-
-	/**
-	 * Convert global position to X, Y Z
-	 */
-	static void globalPosToXYZ(const LLVector3d& pos, S32& x, S32& y, S32& z);
-
-private:
-	static bool matchPrefix(const std::string& url, const std::string& prefix);
-
+	static const char* SLURL_HTTPS_SCHEME;
+	static const char* SLURL_HTTP_SCHEME;
+	static const char* SLURL_SL_SCHEME;
+	static const char* SLURL_SECONDLIFE_SCHEME;
+	static const char* SLURL_SECONDLIFE_PATH;
+	static const char* SLURL_COM;
+	static const char* SLURL_X_GRID_LOCATION_INFO_SCHEME;
+	static LLSLURL START_LOCATION;
+	static const char* SIM_LOCATION_HOME;
+	static const char* SIM_LOCATION_LAST;
+	static const char* SLURL_APP_PATH;
+	static const char* SLURL_REGION_PATH;	
+	
+	enum SLURL_TYPE { 
+		INVALID, 
+		LOCATION,
+		HOME_LOCATION,
+		LAST_LOCATION,
+		APP,
+		HELP 
+	};
+		
+	
+	LLSLURL(): mType(LAST_LOCATION)  { }
+	LLSLURL(const std::string& slurl);
+	LLSLURL(const std::string& grid, const std::string& region);
+	LLSLURL(const std::string& region, const LLVector3& position);
+	LLSLURL(const std::string& grid, const std::string& region, const LLVector3& position);
+	LLSLURL(const std::string& grid, const std::string& region, const LLVector3d& global_position);
+	LLSLURL(const std::string& region, const LLVector3d& global_position);
+	LLSLURL(const std::string& command, const LLUUID&id, const std::string& verb);
+	
+	SLURL_TYPE getType() const { return mType; }
+	
+	std::string getSLURLString() const;
+	std::string getLoginString() const;
+	std::string getLocationString() const; 
+	std::string getGrid() const { return mGrid; }
+	std::string getRegion() const { return mRegion; }
+	LLVector3   getPosition() const { return mPosition; }
+	std::string getAppCmd() const { return mAppCmd; }
+	std::string getAppQuery() const { return mAppQuery; }
+	LLSD        getAppQueryMap() const { return mAppQueryMap; }
+	LLSD        getAppPath() const { return mAppPath; }
+	
+	bool        isValid() const { return mType != INVALID; }
+	
+	bool operator==(const LLSLURL& rhs);
+	bool operator!=(const LLSLURL&rhs);
+
+    std::string asString() const ;
+
+
+protected:
+	SLURL_TYPE mType;
+	
+	// used for Apps and Help
+	std::string mAppCmd;
+	LLSD        mAppPath;
+	LLSD        mAppQueryMap;
+	std::string mAppQuery;
+	
+	std::string mGrid;  // reference to grid manager grid
+	std::string mRegion;
+	LLVector3  mPosition;
 };
 
-#endif
+#endif // LLSLURL_H
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 6f7a4e2f6a..d1c6fca063 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -144,7 +144,7 @@
 #include "lltoolmgr.h"
 #include "llui.h"
 #include "llurldispatcher.h"
-#include "llurlsimstring.h"
+#include "llslurl.h"
 #include "llurlhistory.h"
 #include "llurlwhitelist.h"
 #include "llvieweraudio.h"
@@ -229,6 +229,7 @@ static std::string sInitialOutfitGender;	// "male" or "female"
 static bool gUseCircuitCallbackCalled = false;
 
 EStartupState LLStartUp::gStartupState = STATE_FIRST;
+LLSLURL LLStartUp::sStartSLURL;
 
 static LLPointer<LLCredential> gUserCredential;
 static std::string gDisplayName;
@@ -419,7 +420,7 @@ bool idle_startup()
 
 	if ( STATE_FIRST == LLStartUp::getStartupState() )
 	{
-		gViewerWindow->showCursor();
+		gViewerWindow->showCursor(); 
 		gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT);
 
 		/////////////////////////////////////////////////
@@ -719,7 +720,7 @@ bool idle_startup()
 		//
 		if (gUserCredential.isNull())
 		{
-			gUserCredential = gLoginHandler.initializeLoginInfo(LLStartUp::sSLURLCommand);
+			gUserCredential = gLoginHandler.initializeLoginInfo();
 		}
 		if (gUserCredential.isNull())
 		{
@@ -769,7 +770,7 @@ bool idle_startup()
 			// show the login view until login_show() is called below.  
 			if (gUserCredential.isNull())                                                                          
 			{                                                                                                      
-				gUserCredential = gLoginHandler.initializeLoginInfo(LLStartUp::sSLURLCommand);                 
+				gUserCredential = gLoginHandler.initializeLoginInfo();                 
 			}     
 			if (gNoRender)
 			{
@@ -936,11 +937,7 @@ bool idle_startup()
 
 		if (show_connect_box)
 		{
-			std::string location;
-			LLPanelLogin::getLocation( location );
-			LLURLSimString::setString( location );
-
-			// END TODO
+			LLStartUp::setStartSLURL(LLPanelLogin::getLocation());
 			LLPanelLogin::closePanel();
 		}
 
@@ -961,26 +958,21 @@ bool idle_startup()
 		// their last location, or some URL "-url //sim/x/y[/z]"
 		// All accounts have both a home and a last location, and we don't support
 		// more locations than that.  Choose the appropriate one.  JC
-		if (LLURLSimString::parse())
-		{
-			// a startup URL was specified
-			agent_location_id = START_LOCATION_ID_URL;
-
-			// doesn't really matter what location_which is, since
-			// gAgentStartLookAt will be overwritten when the
-			// UserLoginLocationReply arrives
-			location_which = START_LOCATION_ID_LAST;
-		}
-		else if (gSavedSettings.getString("LoginLocation") == "last" )
-		{
-			agent_location_id = START_LOCATION_ID_LAST;	// last location
-			location_which = START_LOCATION_ID_LAST;
-		}
-		else
-		{
-			agent_location_id = START_LOCATION_ID_HOME;	// home
-			location_which = START_LOCATION_ID_HOME;
-		}
+		switch (LLStartUp::getStartSLURL().getType())
+		  {
+		  case LLSLURL::LOCATION:
+		    agent_location_id = START_LOCATION_ID_URL;
+		    location_which = START_LOCATION_ID_LAST;
+		    break;
+		  case LLSLURL::LAST_LOCATION:
+		    agent_location_id = START_LOCATION_ID_LAST;
+		    location_which = START_LOCATION_ID_LAST;
+		    break;
+		  default:
+		    agent_location_id = START_LOCATION_ID_HOME;
+		    location_which = START_LOCATION_ID_HOME;
+		    break;
+		  }
 
 		gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT);
 
@@ -1851,7 +1843,8 @@ bool idle_startup()
 		// thus, do not show this alert.
 		if (!gAgent.isFirstLogin())
 		{
-			bool url_ok = LLURLSimString::sInstance.parse();
+			llinfos << "gAgentStartLocation : " << gAgentStartLocation << llendl;
+			bool url_ok = (LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION);
 			if ((url_ok && gAgentStartLocation == "url") ||
 				(!url_ok && ((gAgentStartLocation == gSavedSettings.getString("LoginLocation")))))
 			{
@@ -2197,7 +2190,7 @@ bool login_alert_status(const LLSD& notification, const LLSD& response)
       //      break;
         case 2:     // Teleport
             // Restart the login process, starting at our home locaton
-            LLURLSimString::setString("home");
+	  LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME));
             LLStartUp::setStartupState( STATE_LOGIN_CLEANUP );
             break;
         default:
@@ -2619,7 +2612,6 @@ void reset_login()
 
 //---------------------------------------------------------------------------
 
-std::string LLStartUp::sSLURLCommand;
 
 bool LLStartUp::canGoFullscreen()
 {
@@ -2652,35 +2644,40 @@ void LLStartUp::fontInit()
 bool LLStartUp::dispatchURL()
 {
 	// ok, if we've gotten this far and have a startup URL
-	if (!sSLURLCommand.empty())
+        if (!getStartSLURL().isValid())
 	{
-		LLMediaCtrl* web = NULL;
-		const bool trusted_browser = false;
-		LLURLDispatcher::dispatch(sSLURLCommand, web, trusted_browser);
+	  return false;
 	}
-	else if (LLURLSimString::parse())
-	{
+        if(getStartSLURL().getType() != LLSLURL::APP)
+	  {
+	    
 		// If we started with a location, but we're already
 		// at that location, don't pop dialogs open.
 		LLVector3 pos = gAgent.getPositionAgent();
-		F32 dx = pos.mV[VX] - (F32)LLURLSimString::sInstance.mX;
-		F32 dy = pos.mV[VY] - (F32)LLURLSimString::sInstance.mY;
+		LLVector3 slurlpos = getStartSLURL().getPosition();
+		F32 dx = pos.mV[VX] - slurlpos.mV[VX];
+		F32 dy = pos.mV[VY] - slurlpos.mV[VY];
 		const F32 SLOP = 2.f;	// meters
 
-		if( LLURLSimString::sInstance.mSimName != gAgent.getRegion()->getName()
+		if( getStartSLURL().getRegion() != gAgent.getRegion()->getName()
 			|| (dx*dx > SLOP*SLOP)
 			|| (dy*dy > SLOP*SLOP) )
 		{
-			std::string url = LLURLSimString::getURL();
-			LLMediaCtrl* web = NULL;
-			const bool trusted_browser = false;
-			LLURLDispatcher::dispatch(url, web, trusted_browser);
+			LLURLDispatcher::dispatch(getStartSLURL().getSLURLString(), 
+						  NULL, false);
 		}
 		return true;
 	}
 	return false;
 }
 
+void LLStartUp::setStartSLURL(const LLSLURL& slurl) 
+{
+  sStartSLURL = slurl;
+  gSavedSettings.setBOOL("LoginLastLocation", 
+			 !(slurl.getType() == LLSLURL::HOME_LOCATION)); 
+}
+
 bool login_alert_done(const LLSD& notification, const LLSD& response)
 {
 	LLPanelLogin::giveFocus();
diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h
index 28bc7fcd2a..c01a6fc8e1 100644
--- a/indra/newview/llstartup.h
+++ b/indra/newview/llstartup.h
@@ -34,6 +34,7 @@
 #define LL_LLSTARTUP_H
 
 #include <boost/scoped_ptr.hpp>
+#include "llslurl.h"
 
 class LLViewerTexture ;
 class LLEventPump;
@@ -106,13 +107,13 @@ public:
 		// if we have a SLURL or sim string ("Ahern/123/45") that started
 		// the viewer, dispatch it
 
-	static std::string sSLURLCommand;
-		// *HACK: On startup, if we were passed a secondlife://app/do/foo
-		// command URL, store it for later processing.
-
 	static void postStartupState();
+	static void setStartSLURL(const LLSLURL& slurl); 
+	static LLSLURL& getStartSLURL() { return sStartSLURL; } 
 
 private:
+	static LLSLURL sStartSLURL;
+
 	static std::string startupStateToString(EStartupState state);
 	static EStartupState gStartupState; // Do not set directly, use LLStartup::setStartupState
 	static boost::scoped_ptr<LLEventPump> sStateWatcher;
diff --git a/indra/newview/llstylemap.cpp b/indra/newview/llstylemap.cpp
index 2485563cbc..15e02b38d1 100644
--- a/indra/newview/llstylemap.cpp
+++ b/indra/newview/llstylemap.cpp
@@ -50,7 +50,7 @@ const LLStyle::Params &LLStyleMap::lookupAgent(const LLUUID &source)
 		{
 			style_params.color.control = "HTMLLinkColor";
 			style_params.link_href = 
-					LLSLURL::buildCommand("agent", source, "inspect");
+					LLSLURL("agent", source, "inspect").getSLURLString();
 		}
 		else
 		{
diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp
index 0b6bd4b401..5b11f82a45 100644
--- a/indra/newview/llurldispatcher.cpp
+++ b/indra/newview/llurldispatcher.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2007&license=viewergpl$
  * 
- * Copyright (c) 2007-2009, Linden Research, Inc.
+ * Copyright (c) 2010, Linden Research, Inc.
  * 
  * Second Life Viewer Source Code
  * The source code in this file ("Source Code") is provided by Linden Lab
@@ -12,13 +12,12 @@
  * ("GPL"), unless you have obtained a separate licensing agreement
  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * online at http://secondlife.com/developers/opensource/gplv2
  * 
  * There are special exceptions to the terms and conditions of the GPL as
  * it is applied to this Source Code. View the full text of the exception
  * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * online at http://secondlife.com/developers/opensource/flossexception
  * 
  * By copying, modifying or distributing this software, you acknowledge
  * that you have read and understood your obligations described above,
@@ -45,10 +44,10 @@
 #include "llsidetray.h"
 #include "llslurl.h"
 #include "llstartup.h"			// gStartupState
-#include "llurlsimstring.h"
 #include "llweb.h"
 #include "llworldmapmessage.h"
 #include "llurldispatcherlistener.h"
+#include "llviewernetwork.h"
 
 // library includes
 #include "llnotificationsutil.h"
@@ -59,25 +58,25 @@ static LLURLDispatcherListener sURLDispatcherListener;
 class LLURLDispatcherImpl
 {
 public:
-	static bool dispatch(const std::string& url,
+	static bool dispatch(const LLSLURL& slurl,
 						 LLMediaCtrl* web,
 						 bool trusted_browser);
 		// returns true if handled or explicitly blocked.
 
-	static bool dispatchRightClick(const std::string& url);
+	static bool dispatchRightClick(const LLSLURL& slurl);
 
 private:
-	static bool dispatchCore(const std::string& url, 
+	static bool dispatchCore(const LLSLURL& slurl, 
 							 bool right_mouse,
 							 LLMediaCtrl* web,
 							 bool trusted_browser);
 		// handles both left and right click
 
-	static bool dispatchHelp(const std::string& url, bool right_mouse);
+	static bool dispatchHelp(const LLSLURL& slurl, bool right_mouse);
 		// Handles sl://app.floater.html.help by showing Help floater.
 		// Returns true if handled.
 
-	static bool dispatchApp(const std::string& url,
+	static bool dispatchApp(const LLSLURL& slurl,
 							bool right_mouse,
 							LLMediaCtrl* web,
 							bool trusted_browser);
@@ -85,16 +84,16 @@ private:
 		// by showing panel in Search floater.
 		// Returns true if handled or explicitly blocked.
 
-	static bool dispatchRegion(const std::string& url, bool right_mouse);
+	static bool dispatchRegion(const LLSLURL& slurl, bool right_mouse);
 		// handles secondlife://Ahern/123/45/67/
 		// Returns true if handled.
 
-	static void regionHandleCallback(U64 handle, const std::string& url,
+	static void regionHandleCallback(U64 handle, const LLSLURL& slurl,
 		const LLUUID& snapshot_id, bool teleport);
 		// Called by LLWorldMap when a location has been resolved to a
 	    // region name
 
-	static void regionNameCallback(U64 handle, const std::string& url,
+	static void regionNameCallback(U64 handle, const LLSLURL& slurl,
 		const LLUUID& snapshot_id, bool teleport);
 		// Called by LLWorldMap when a region name has been resolved to a
 		// location in-world, used by places-panel display.
@@ -103,65 +102,57 @@ private:
 };
 
 // static
-bool LLURLDispatcherImpl::dispatchCore(const std::string& url,
+bool LLURLDispatcherImpl::dispatchCore(const LLSLURL& slurl,
 									   bool right_mouse,
 									   LLMediaCtrl* web,
 									   bool trusted_browser)
 {
-	if (url.empty()) return false;
-	//if (dispatchHelp(url, right_mouse)) return true;
-	if (dispatchApp(url, right_mouse, web, trusted_browser)) return true;
-	if (dispatchRegion(url, right_mouse)) return true;
+	//if (dispatchHelp(slurl, right_mouse)) return true;
+	switch(slurl.getType())
+	{
+		case LLSLURL::APP: 
+			return dispatchApp(slurl, right_mouse, web, trusted_browser);
+		case LLSLURL::LOCATION:
+			return dispatchRegion(slurl, right_mouse);
+		default:
+			return false;
+	}
 
 	/*
 	// Inform the user we can't handle this
 	std::map<std::string, std::string> args;
-	args["SLURL"] = url;
+	args["SLURL"] = slurl;
 	r;
 	*/
-	
-	return false;
 }
 
 // static
-bool LLURLDispatcherImpl::dispatch(const std::string& url,
+bool LLURLDispatcherImpl::dispatch(const LLSLURL& slurl,
 								   LLMediaCtrl* web,
 								   bool trusted_browser)
 {
-	llinfos << "url: " << url << llendl;
 	const bool right_click = false;
-	return dispatchCore(url, right_click, web, trusted_browser);
+	return dispatchCore(slurl, right_click, web, trusted_browser);
 }
 
 // static
-bool LLURLDispatcherImpl::dispatchRightClick(const std::string& url)
+bool LLURLDispatcherImpl::dispatchRightClick(const LLSLURL& slurl)
 {
-	llinfos << "url: " << url << llendl;
 	const bool right_click = true;
 	LLMediaCtrl* web = NULL;
 	const bool trusted_browser = false;
-	return dispatchCore(url, right_click, web, trusted_browser);
+	return dispatchCore(slurl, right_click, web, trusted_browser);
 }
 
 // static
-bool LLURLDispatcherImpl::dispatchApp(const std::string& url, 
+bool LLURLDispatcherImpl::dispatchApp(const LLSLURL& slurl, 
 									  bool right_mouse,
 									  LLMediaCtrl* web,
 									  bool trusted_browser)
 {
-	// ensure the URL is in the secondlife:///app/ format
-	if (!LLSLURL::isSLURLCommand(url))
-	{
-		return false;
-	}
-
-	LLURI uri(url);
-	LLSD pathArray = uri.pathArray();
-	pathArray.erase(0); // erase "app"
-	std::string cmd = pathArray.get(0);
-	pathArray.erase(0); // erase "cmd"
+	llinfos << "cmd: " << slurl.getAppCmd() << " path: " << slurl.getAppPath() << " query: " << slurl.getAppQuery() << llendl;
 	bool handled = LLCommandDispatcher::dispatch(
-			cmd, pathArray, uri.queryMap(), web, trusted_browser);
+			slurl.getAppCmd(), slurl.getAppPath(), slurl.getAppQuery(), web, trusted_browser);
 
 	// alert if we didn't handle this secondlife:///app/ SLURL
 	// (but still return true because it is a valid app SLURL)
@@ -173,109 +164,68 @@ bool LLURLDispatcherImpl::dispatchApp(const std::string& url,
 }
 
 // static
-bool LLURLDispatcherImpl::dispatchRegion(const std::string& url, bool right_mouse)
+bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, bool right_mouse)
 {
-	if (!LLSLURL::isSLURL(url))
-	{
-		return false;
-	}
-
 	// Before we're logged in, need to update the startup screen
 	// to tell the user where they are going.
 	if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP)
 	{
 		// Parse it and stash in globals, it will be dispatched in
 		// STATE_CLEANUP.
-		LLURLSimString::setString(url);
+		LLStartUp::setStartSLURL(slurl);
 		// We're at the login screen, so make sure user can see
 		// the login location box to know where they are going.
 		
-		LLPanelLogin::refreshLocation( true );
+		LLPanelLogin::updateLocationCombo( true );
 		return true;
 	}
 
-	std::string sim_string = LLSLURL::stripProtocol(url);
-	std::string region_name;
-	S32 x = 128;
-	S32 y = 128;
-	S32 z = 0;
-	LLURLSimString::parse(sim_string, &region_name, &x, &y, &z);
-
 	// LLFloaterURLDisplay functionality moved to LLPanelPlaces in Side Tray.
-	//LLFloaterURLDisplay* url_displayp = LLFloaterReg::getTypedInstance<LLFloaterURLDisplay>("preview_url",LLSD());
-	//if(url_displayp) url_displayp->setName(region_name);
+	//LLFloaterURLDisplay* slurl_displayp = LLFloaterReg::getTypedInstance<LLFloaterURLDisplay>("preview_url",LLSD());
+	//if(slurl_displayp) slurl_displayp->setName(region_name);
 
 	// Request a region handle by name
-	LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name,
+	LLWorldMapMessage::getInstance()->sendNamedRegionRequest(slurl.getRegion(),
 									  LLURLDispatcherImpl::regionNameCallback,
-									  url,
+									  slurl.getSLURLString(),
 									  false);	// don't teleport
 	return true;
 }
 
 /*static*/
-void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport)
+void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport)
 {
-	std::string sim_string = LLSLURL::stripProtocol(url);
-	std::string region_name;
-	S32 x = 128;
-	S32 y = 128;
-	S32 z = 0;
-	LLURLSimString::parse(sim_string, &region_name, &x, &y, &z);
-
-	LLVector3 local_pos;
-	local_pos.mV[VX] = (F32)x;
-	local_pos.mV[VY] = (F32)y;
-	local_pos.mV[VZ] = (F32)z;
+	regionHandleCallback(region_handle, slurl, snapshot_id, teleport);
+}
 
-	
-	// determine whether the point is in this region
-	if ((x >= 0) && (x < REGION_WIDTH_UNITS) &&
-		(y >= 0) && (y < REGION_WIDTH_UNITS))
-	{
-		// if so, we're done
-		regionHandleCallback(region_handle, url, snapshot_id, teleport);
-	}
+/* static */
+void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport)
+{
 
-	else
+  // we can't teleport cross grid at this point
+	if((!LLGridManager::getInstance()->isSystemGrid(slurl.getGrid()) || !LLGridManager::getInstance()->isSystemGrid()) &&
+	   (slurl.getGrid() != LLGridManager::getInstance()->getGridName()))
 	{
-		// otherwise find the new region from the location
+		LLSD args;
+		args["SLURL"] = slurl.getLocationString();
+		args["CURRENT_GRID"] = LLGridManager::getInstance()->getGridLabel();
+		LLSD grid_info = LLGridManager::getInstance()->getGridInfo(slurl.getGrid());
 		
-		// add the position to get the new region
-		LLVector3d global_pos = from_region_handle(region_handle) + LLVector3d(local_pos);
-
-		U64 new_region_handle = to_region_handle(global_pos);
-		LLWorldMapMessage::getInstance()->sendHandleRegionRequest(new_region_handle,
-										   LLURLDispatcherImpl::regionHandleCallback,
-										   url, teleport);
+		if(grid_info.has(GRID_LABEL_VALUE))
+		{
+			args["GRID"] = grid_info[GRID_LABEL_VALUE].asString();
+		}
+		else 
+		{
+			args["GRID"] = slurl.getGrid();
+		}
+		LLNotificationsUtil::add("CantTeleportToGrid", args);
+		return;
 	}
-}
-
-/* static */
-void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport)
-{
-	std::string sim_string = LLSLURL::stripProtocol(url);
-	std::string region_name;
-	S32 x = 128;
-	S32 y = 128;
-	S32 z = 0;
-	LLURLSimString::parse(sim_string, &region_name, &x, &y, &z);
-
-	// remap x and y to local coordinates
-	S32 local_x = x % REGION_WIDTH_UNITS;
-	S32 local_y = y % REGION_WIDTH_UNITS;
-	if (local_x < 0)
-		local_x += REGION_WIDTH_UNITS;
-	if (local_y < 0)
-		local_y += REGION_WIDTH_UNITS;
 	
-	LLVector3 local_pos;
-	local_pos.mV[VX] = (F32)local_x;
-	local_pos.mV[VY] = (F32)local_y;
-	local_pos.mV[VZ] = (F32)z;
 
 	LLVector3d global_pos = from_region_handle(region_handle);
-	global_pos += LLVector3d(local_pos);
+	global_pos += LLVector3d(slurl.getPosition());
 	
 	if (teleport)
 	{	
@@ -299,8 +249,8 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::str
 		// LLFloaterURLDisplay functionality moved to LLPanelPlaces in Side Tray.
 
 //		// display informational floater, allow user to click teleport btn
-//		LLFloaterURLDisplay* url_displayp = LLFloaterReg::getTypedInstance<LLFloaterURLDisplay>("preview_url",LLSD());
-//		if(url_displayp)
+//		LLFloaterURLDisplay* slurl_displayp = LLFloaterReg::getTypedInstance<LLFloaterURLDisplay>("preview_url",LLSD());
+//		if(slurl_displayp)
 //		{
 //			url_displayp->displayParcelInfo(region_handle, local_pos);
 //			if(snapshot_id.notNull())
@@ -315,7 +265,7 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::str
 
 //---------------------------------------------------------------------------
 // Teleportation links are handled here because they are tightly coupled
-// to URL parsing and sim-fragment parsing
+// to SLURL parsing and sim-fragment parsing
 class LLTeleportHandler : public LLCommandHandler
 {
 public:
@@ -331,18 +281,21 @@ public:
 		// a global position, and teleport to it
 		if (tokens.size() < 1) return false;
 
-		// Region names may be %20 escaped.
-		std::string region_name = LLURLSimString::unescapeRegionName(tokens[0]);
-
-		// build secondlife://De%20Haro/123/45/67 for use in callback
-		std::string url = LLSLURL::PREFIX_SECONDLIFE;
-		for (int i = 0; i < tokens.size(); ++i)
+		LLVector3 coords(128, 128, 0);
+		if (tokens.size() <= 4)
 		{
-			url += tokens[i].asString() + "/";
+			coords = LLVector3(tokens[1].asReal(), 
+							   tokens[2].asReal(), 
+							   tokens[3].asReal());
 		}
+		
+		// Region names may be %20 escaped.
+		
+		std::string region_name = LLURI::unescape(tokens[0]);
+
 		LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name,
 			LLURLDispatcherImpl::regionHandleCallback,
-			url,
+			LLSLURL(region_name, coords).getSLURLString(),
 			true);	// teleport
 		return true;
 	}
@@ -352,21 +305,21 @@ LLTeleportHandler gTeleportHandler;
 //---------------------------------------------------------------------------
 
 // static
-bool LLURLDispatcher::dispatch(const std::string& url,
+bool LLURLDispatcher::dispatch(const std::string& slurl,
 							   LLMediaCtrl* web,
 							   bool trusted_browser)
 {
-	return LLURLDispatcherImpl::dispatch(url, web, trusted_browser);
+	return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), web, trusted_browser);
 }
 
 // static
-bool LLURLDispatcher::dispatchRightClick(const std::string& url)
+bool LLURLDispatcher::dispatchRightClick(const std::string& slurl)
 {
-	return LLURLDispatcherImpl::dispatchRightClick(url);
+	return LLURLDispatcherImpl::dispatchRightClick(LLSLURL(slurl));
 }
 
 // static
-bool LLURLDispatcher::dispatchFromTextEditor(const std::string& url)
+bool LLURLDispatcher::dispatchFromTextEditor(const std::string& slurl)
 {
 	// *NOTE: Text editors are considered sources of trusted URLs
 	// in order to make avatar profile links in chat history work.
@@ -376,5 +329,7 @@ bool LLURLDispatcher::dispatchFromTextEditor(const std::string& url)
 	// *TODO: Make this trust model more refined.  JC
 	const bool trusted_browser = true;
 	LLMediaCtrl* web = NULL;
-	return LLURLDispatcherImpl::dispatch(url, web, trusted_browser);
+	return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), web, trusted_browser);
 }
+
+
diff --git a/indra/newview/llurldispatcher.h b/indra/newview/llurldispatcher.h
index ff8a351253..41c5225e00 100644
--- a/indra/newview/llurldispatcher.h
+++ b/indra/newview/llurldispatcher.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2007&license=viewergpl$
  * 
- * Copyright (c) 2007-2009, Linden Research, Inc.
+ * Copyright (c) 2007, Linden Research, Inc.
  * 
  * Second Life Viewer Source Code
  * The source code in this file ("Source Code") is provided by Linden Lab
@@ -12,13 +12,12 @@
  * ("GPL"), unless you have obtained a separate licensing agreement
  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * online at http://secondlife.com/developers/opensource/gplv2
  * 
  * There are special exceptions to the terms and conditions of the GPL as
  * it is applied to this Source Code. View the full text of the exception
  * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * online at http://secondlife.com/developers/opensource/flossexception
  * 
  * By copying, modifying or distributing this software, you acknowledge
  * that you have read and understood your obligations described above,
@@ -31,16 +30,17 @@
  */
 #ifndef LLURLDISPATCHER_H
 #define LLURLDISPATCHER_H
-
+#include "llslurl.h"
 class LLMediaCtrl;
 
 
 class LLURLDispatcher
 {
 public:
-	static bool dispatch(const std::string& url,
+	
+	static bool dispatch(const std::string& slurl,
 						 LLMediaCtrl* web,
-						 bool trusted_browser);
+						 bool trusted_browser);	
 		// At startup time and on clicks in internal web browsers,
 		// teleport, open map, or run requested command.
 		// @param url
@@ -54,9 +54,9 @@ public:
 		//   that navigates to trusted (Linden Lab) pages.
 		// Returns true if someone handled the URL.
 
-	static bool dispatchRightClick(const std::string& url);
+	static bool dispatchRightClick(const std::string& slurl);
 
-	static bool dispatchFromTextEditor(const std::string& url);
+	static bool dispatchFromTextEditor(const std::string& slurl);
 };
 
 #endif
diff --git a/indra/newview/llurldispatcherlistener.cpp b/indra/newview/llurldispatcherlistener.cpp
index fea6a769c5..8f50f30d6d 100644
--- a/indra/newview/llurldispatcherlistener.cpp
+++ b/indra/newview/llurldispatcherlistener.cpp
@@ -17,6 +17,7 @@
 // std headers
 // external library headers
 // other Linden headers
+#include "llslurl.h"
 #include "llurldispatcher.h"
 
 LLURLDispatcherListener::LLURLDispatcherListener(/* LLURLDispatcher* instance */):
diff --git a/indra/newview/llurllineeditorctrl.cpp b/indra/newview/llurllineeditorctrl.cpp
index 258c3ddd75..2a5c7cbea1 100644
--- a/indra/newview/llurllineeditorctrl.cpp
+++ b/indra/newview/llurllineeditorctrl.cpp
@@ -89,7 +89,7 @@ void LLURLLineEditor::copyEscapedURLToClipboard()
 
 	const std::string unescaped_text = wstring_to_utf8str(mText.getWString().substr(left_pos, length));
 	LLWString text_to_copy;
-	if (LLSLURL::isSLURL(unescaped_text))
+	if (LLSLURL(unescaped_text).isValid())
 		text_to_copy = utf8str_to_wstring(LLWeb::escapeURL(unescaped_text));
 	else
 		text_to_copy = utf8str_to_wstring(unescaped_text);
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 3362142807..6bff78de82 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -1518,9 +1518,9 @@ void inventory_offer_handler(LLOfferInfo* info)
 	payload["give_inventory_notification"] = FALSE;
 	args["OBJECTFROMNAME"] = info->mFromName;
 	args["NAME"] = info->mFromName;
-	args["NAME_SLURL"] = LLSLURL::buildCommand("agent", info->mFromID, "about");
+	args["NAME_SLURL"] = LLSLURL("agent", info->mFromID, "about").getSLURLString();
 	std::string verb = "select?name=" + LLURI::escape(msg);
-	args["ITEM_SLURL"] = LLSLURL::buildCommand("inventory", info->mObjectID, verb.c_str());
+	args["ITEM_SLURL"] = LLSLURL("inventory", info->mObjectID, verb.c_str()).getSLURLString();
 
 	LLNotification::Params p("ObjectGiveItem");
 
@@ -3037,7 +3037,7 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**)
 		if (avatarp)
 		{
 			// Chat the "back" SLURL. (DEV-4907)
-			LLChat chat("Teleport completed from " + gAgent.getTeleportSourceSLURL());
+			LLChat chat("Teleport completed from " + gAgent.getTeleportSourceSLURL().getSLURLString());
 			chat.mSourceType = CHAT_SOURCE_SYSTEM;
  		    LLFloaterChat::addChatHistory(chat);
 
@@ -5353,7 +5353,7 @@ void send_group_notice(const LLUUID& group_id,
 bool handle_lure_callback(const LLSD& notification, const LLSD& response)
 {
 	std::string text = response["message"].asString();
-	text.append("\r\n").append(LLAgentUI::buildSLURL());
+	text.append("\r\n").append(LLAgentUI::buildSLURL().getSLURLString());
 	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
 
 	if(0 == option)
@@ -5774,7 +5774,7 @@ void process_covenant_reply(LLMessageSystem* msg, void**)
 	LLFloaterBuyLand::updateEstateName(estate_name);
 
 	std::string owner_name =
-		LLSLURL::buildCommand("agent", estate_owner_id, "inspect");
+		LLSLURL("agent", estate_owner_id, "inspect").getSLURLString();
 	LLPanelEstateCovenant::updateEstateOwnerName(owner_name);
 	LLPanelLandCovenant::updateEstateOwnerName(owner_name);
 	LLFloaterBuyLand::updateEstateOwnerName(owner_name);
diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp
index 82dc459777..3615c00f37 100644
--- a/indra/newview/llviewernetwork.cpp
+++ b/indra/newview/llviewernetwork.cpp
@@ -5,7 +5,7 @@
  *
  * $LicenseInfo:firstyear=2006&license=viewergpl$
  * 
- * Copyright (c) 2006-2010, Linden Research, Inc.
+ * Copyright (c) 2006-2007, Linden Research, Inc.
  * 
  * Second Life Viewer Source Code
  * The source code in this file ("Source Code") is provided by Linden Lab
@@ -40,7 +40,8 @@
                                                             
 const char* DEFAULT_LOGIN_PAGE = "http://secondlife.com/app/login/";
 
-const char* SYSTEM_GRID_SLURL_BASE = "http://slurl.com/secondlife/";
+const char* SYSTEM_GRID_SLURL_BASE = "secondlife://%s/secondlife/";
+const char* MAIN_GRID_SLURL_BASE = "http://slurl.com/secondlife/";
 const char* SYSTEM_GRID_APP_SLURL_BASE = "secondlife:///app";
 
 const char* DEFAULT_SLURL_BASE = "https://%s/region/";
@@ -60,6 +61,12 @@ LLGridManager::LLGridManager()
 }
 
 
+LLGridManager::LLGridManager(const std::string& grid_file)
+{
+	// initialize with an explicity grid file for testing.
+	initialize(grid_file);
+}
+
 //
 // LLGridManager - class for managing the list of known grids, and the current
 // selection
@@ -391,7 +398,7 @@ void LLGridManager::addSystemGrid(const std::string& label,
 	grid[GRID_LOGIN_PAGE_VALUE] = login_page;
 	grid[GRID_IS_SYSTEM_GRID_VALUE] = TRUE;
 	grid[GRID_LOGIN_CREDENTIAL_PAGE_TYPE_VALUE] = GRID_LOGIN_CREDENTIAL_PAGE_TYPE_AGENT;
-	grid[GRID_SLURL_BASE] = SYSTEM_GRID_SLURL_BASE;
+	
 	grid[GRID_APP_SLURL_BASE] = SYSTEM_GRID_APP_SLURL_BASE;
 	if (login_id.empty())
 	{
@@ -406,8 +413,13 @@ void LLGridManager::addSystemGrid(const std::string& label,
 	// if we're building a debug version.
 	if (name == std::string(MAINGRID))
 	{
+		grid[GRID_SLURL_BASE] = MAIN_GRID_SLURL_BASE;		
 		grid[GRID_IS_FAVORITE_VALUE] = TRUE;		
 	}
+	else
+	{
+		grid[GRID_SLURL_BASE] = llformat(SYSTEM_GRID_SLURL_BASE, label.c_str());		
+	}
 	addGrid(grid);
 }
 
@@ -458,6 +470,20 @@ void LLGridManager::setGridChoice(const std::string& grid_name)
 	gSavedSettings.setString("CurrentGrid", grid_name);
 }
 
+std::string LLGridManager::getGridByLabel( const std::string &grid_label)
+{
+	for(LLSD::map_iterator grid_iter = mGridList.beginMap();
+		grid_iter != mGridList.endMap();
+		grid_iter++) 
+	{
+		if (grid_iter->second.has(GRID_LABEL_VALUE) && (grid_iter->second[GRID_LABEL_VALUE].asString() == grid_label))
+		{
+			return grid_iter->first;
+		}
+	}
+	return std::string();
+}
+
 void LLGridManager::getLoginURIs(std::vector<std::string>& uris)
 {
 	uris.clear();
@@ -528,10 +554,10 @@ std::string LLGridManager::getAppSLURLBase(const std::string& grid_name)
 	std::string grid_base;
 	if(mGridList.has(grid_name) && mGridList[grid_name].has(GRID_APP_SLURL_BASE))
 	{
-		return mGridList[grid_name][GRID_APP_SLURL_BASE].asString();
+	  return mGridList[grid_name][GRID_APP_SLURL_BASE].asString();
 	}
 	else
 	{
-		return  llformat(DEFAULT_APP_SLURL_BASE, grid_name.c_str());
+	  return  llformat(DEFAULT_APP_SLURL_BASE, grid_name.c_str());
 	}
 }
diff --git a/indra/newview/llviewernetwork.h b/indra/newview/llviewernetwork.h
index 0642845d54..bcf0c5a8f2 100644
--- a/indra/newview/llviewernetwork.h
+++ b/indra/newview/llviewernetwork.h
@@ -43,7 +43,6 @@ extern const char* DEFAULT_LOGIN_PAGE;
 #define GRID_LOGIN_PAGE_VALUE "login_page"
 #define GRID_IS_SYSTEM_GRID_VALUE "system_grid"
 #define GRID_IS_FAVORITE_VALUE "favorite"
-#define GRID_IS_VISIBLE_VALUE "visible"
 #define GRID_LOGIN_CREDENTIAL_PAGE_TYPE_VALUE "credential_type"
 #define GRID_LOGIN_CREDENTIAL_PAGE_TYPE_AGENT "agent"
 #define GRID_LOGIN_CREDENTIAL_PAGE_TYPE_ACCOUNT "account"
@@ -79,6 +78,7 @@ public:
 	
 	// when the grid manager is instantiated, the default grids are automatically
 	// loaded, and the grids favorites list is loaded from the xml file.
+	LLGridManager(const std::string& grid_file);
 	LLGridManager();
 	~LLGridManager();
 	
@@ -112,10 +112,7 @@ public:
 	void setGridChoice(const std::string& grid_name);
 	
 	
-	std::string getGridLabel() 
-	{ 
-		return mGridList[mGridName][GRID_LABEL_VALUE]; 
-	} 	
+	std::string getGridLabel() { return mGridList[mGridName][GRID_LABEL_VALUE]; } 	
 	std::string getGridName() const { return mGridName; }
 	void getLoginURIs(std::vector<std::string>& uris);
 	std::string getHelperURI() {return mGridList[mGridName][GRID_HELPER_URI_VALUE];}
@@ -132,6 +129,8 @@ public:
 	
 	LLSD getGridInfo() { return mGridList[mGridName]; }
 	
+	std::string getGridByLabel( const std::string &grid_label);
+	
 	bool isSystemGrid(const std::string& grid) 
 	{ 
 		return mGridList.has(grid) &&
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index f8e08dbf7d..ee6fb8120c 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -2033,7 +2033,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
 			gSavedSettings.setBOOL("ForceShowGrid", visible);
 
 			// Initialize visibility (and don't force visibility - use prefs)
-			LLPanelLogin::refreshLocation( false );
+			LLPanelLogin::updateLocationCombo( false );
 		}
 	}
 
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index c4cbcb1dc8..d177cfce7d 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -2371,6 +2371,15 @@ Please choose the male or female avatar. You can change your mind later.
      notext="Female"
      yestext="Male"/>
   </notification>
+  <notification icon="alertmodal.tga"
+		name="CantTeleportToGrid"
+		type="alertmodal">
+Could not teleport to [SLURL] as it's on a different grid ([GRID]) than the current grid ([CURRENT_GRID]).  Please close your viewer and try again.
+    <usetemplate
+     name="okbutton"
+     yestext="OK"/>
+  </notification>
+
   <notification icon="alertmodal.tga"
 		name="GeneralCertificateError"
 		type="alertmodal">
diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp
index 9222882f5f..f4d9cc99cc 100644
--- a/indra/newview/tests/lllogininstance_test.cpp
+++ b/indra/newview/tests/lllogininstance_test.cpp
@@ -36,7 +36,12 @@ const std::string APPVIEWER_SERIALNUMBER("appviewer_serialno");
 //-----------------------------------------------------------------------------
 static LLEventStream gTestPump("test_pump");
 
+#include "../llslurl.h"
+#include "../llstartup.h"
+LLSLURL LLStartUp::sStartSLURL;
+
 #include "lllogin.h"
+
 static std::string gLoginURI;
 static LLSD gLoginCreds;
 static bool gDisconnectCalled = false;
@@ -141,10 +146,6 @@ BOOL LLControlGroup::declareString(const std::string& name, const std::string &i
 #include "lluicolortable.h"
 void LLUIColorTable::saveUserSettings(void)const {}
 
-//-----------------------------------------------------------------------------
-#include "../llurlsimstring.h"
-LLURLSimString LLURLSimString::sInstance;
-bool LLURLSimString::parse() { return true; }
 
 //-----------------------------------------------------------------------------
 #include "llnotifications.h"
diff --git a/indra/newview/tests/llslurl_test.cpp b/indra/newview/tests/llslurl_test.cpp
new file mode 100644
index 0000000000..90d2526890
--- /dev/null
+++ b/indra/newview/tests/llslurl_test.cpp
@@ -0,0 +1,258 @@
+/** 
+ * @file llsecapi_test.cpp
+ * @author Roxie
+ * @date 2009-02-10
+ * @brief Test the sec api functionality
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * 
+ * Copyright (c) 2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+#include "../llviewerprecompiledheaders.h"
+#include "../llviewernetwork.h"
+#include "../test/lltut.h"
+#include "../llslurl.h"
+#include "../../llxml/llcontrol.h"
+#include "llsdserialize.h"
+//----------------------------------------------------------------------------               
+// Mock objects for the dependencies of the code we're testing                               
+
+LLControlGroup::LLControlGroup(const std::string& name)
+: LLInstanceTracker<LLControlGroup, std::string>(name) {}
+LLControlGroup::~LLControlGroup() {}
+BOOL LLControlGroup::declareString(const std::string& name,
+                                   const std::string& initial_val,
+                                   const std::string& comment,
+                                   BOOL persist) {return TRUE;}
+void LLControlGroup::setString(const std::string& name, const std::string& val){}
+
+std::string gCmdLineLoginURI;
+std::string gCmdLineGridChoice;
+std::string gCmdLineHelperURI;
+std::string gLoginPage;
+std::string gCurrentGrid;
+std::string LLControlGroup::getString(const std::string& name)
+{
+	if (name == "CmdLineGridChoice")
+		return gCmdLineGridChoice;
+	else if (name == "CmdLineHelperURI")
+		return gCmdLineHelperURI;
+	else if (name == "LoginPage")
+		return gLoginPage;
+	else if (name == "CurrentGrid")
+		return gCurrentGrid;
+	return "";
+}
+
+LLSD LLControlGroup::getLLSD(const std::string& name)
+{
+	if (name == "CmdLineLoginURI")
+	{
+		if(!gCmdLineLoginURI.empty())
+		{
+			return LLSD(gCmdLineLoginURI);
+		}
+	}
+	return LLSD();
+}
+
+
+LLControlGroup gSavedSettings("test");
+
+// -------------------------------------------------------------------------------------------
+// TUT
+// -------------------------------------------------------------------------------------------
+namespace tut
+{
+	// Test wrapper declaration : wrapping nothing for the moment
+	struct slurlTest
+	{
+		slurlTest()
+		{	
+			LLGridManager::getInstance()->initialize(std::string(""));
+		}
+		~slurlTest()
+		{
+		}
+	};
+	
+	// Tut templating thingamagic: test group, object and test instance
+	typedef test_group<slurlTest> slurlTestFactory;
+	typedef slurlTestFactory::object slurlTestObject;
+	tut::slurlTestFactory tut_test("llslurl");
+	
+	// ---------------------------------------------------------------------------------------
+	// Test functions 
+	// ---------------------------------------------------------------------------------------
+	// construction from slurl string
+	template<> template<>
+	void slurlTestObject::test<1>()
+	{
+		LLGridManager::getInstance()->setGridChoice("util.agni.lindenlab.com");
+		
+		LLSLURL slurl = LLSLURL("");
+		ensure_equals("null slurl", (int)slurl.getType(), LLSLURL::LAST_LOCATION);
+		
+		slurl = LLSLURL("http://slurl.com/secondlife/myregion");
+		ensure_equals("slurl.com slurl, region only - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals("slurl.com slurl, region only", slurl.getSLURLString(), 
+					  "http://slurl.com/secondlife/myregion/128/128/0");
+		
+		slurl = LLSLURL("http://slurl.com/secondlife/myregion/1/2/3");
+		ensure_equals("slurl.com slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals("slurl.com slurl, region + coords", slurl.getSLURLString(), 
+					  "http://slurl.com/secondlife/myregion/1/2/3");
+
+		slurl = LLSLURL("secondlife://myregion");
+		ensure_equals("secondlife: slurl, region only - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals("secondlife: slurl, region only", slurl.getSLURLString(), 
+					  "http://slurl.com/secondlife/myregion/128/128/0");
+		
+		slurl = LLSLURL("secondlife://myregion/1/2/3");
+		ensure_equals("secondlife: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals("secondlife slurl, region + coords", slurl.getSLURLString(), 
+					  "http://slurl.com/secondlife/myregion/1/2/3");
+		
+		slurl = LLSLURL("/myregion");
+		ensure_equals("/region slurl, region- type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals("/region slurl, region ", slurl.getSLURLString(), 
+					  "http://slurl.com/secondlife/myregion/128/128/0");
+		
+		slurl = LLSLURL("/myregion/1/2/3");
+		ensure_equals("/: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals("/ slurl, region + coords", slurl.getSLURLString(), 
+					  "http://slurl.com/secondlife/myregion/1/2/3");	
+		
+		slurl = LLSLURL("my region/1/2/3");
+		ensure_equals(" slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals(" slurl, region + coords", slurl.getSLURLString(), 
+					  "http://slurl.com/secondlife/my%20region/1/2/3");	
+		
+		slurl = LLSLURL("https://my.grid.com/region/my%20region/1/2/3");
+		ensure_equals("grid slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals("grid slurl, region + coords", slurl.getSLURLString(), 
+					  "https://my.grid.com/region/my%20region/1/2/3");	
+		
+		slurl = LLSLURL("https://my.grid.com/region/my region");
+		ensure_equals("grid slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals("grid slurl, region + coords", slurl.getSLURLString(), 
+					  "https://my.grid.com/region/my%20region/128/128/0");
+		
+		LLGridManager::getInstance()->setGridChoice("foo.bar.com");		
+		slurl = LLSLURL("/myregion/1/2/3");
+		ensure_equals("/: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals("/ slurl, region + coords", slurl.getSLURLString(), 
+					  "https://foo.bar.com/region/myregion/1/2/3");		
+		
+		slurl = LLSLURL("myregion/1/2/3");
+		ensure_equals(": slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals(" slurl, region + coords", slurl.getSLURLString(), 
+					  "https://foo.bar.com/region/myregion/1/2/3");		
+		
+		slurl = LLSLURL(LLSLURL::SIM_LOCATION_HOME);
+		ensure_equals("home", slurl.getType(), LLSLURL::HOME_LOCATION);
+
+		slurl = LLSLURL(LLSLURL::SIM_LOCATION_LAST);
+		ensure_equals("last", slurl.getType(), LLSLURL::LAST_LOCATION);
+		
+		slurl = LLSLURL("secondlife:///app/foo/bar?12345");
+		ensure_equals("app", slurl.getType(), LLSLURL::APP);		
+		ensure_equals("appcmd", slurl.getAppCmd(), "foo");
+		ensure_equals("apppath", slurl.getAppPath().size(), 1);
+		ensure_equals("apppath2", slurl.getAppPath()[0].asString(), "bar");
+		ensure_equals("appquery", slurl.getAppQuery(), "12345");
+		ensure_equals("grid1", "foo.bar.com", slurl.getGrid());
+	
+		slurl = LLSLURL("secondlife://Aditi/app/foo/bar?12345");
+		ensure_equals("app", slurl.getType(), LLSLURL::APP);		
+		ensure_equals("appcmd", slurl.getAppCmd(), "foo");
+		ensure_equals("apppath", slurl.getAppPath().size(), 1);
+		ensure_equals("apppath2", slurl.getAppPath()[0].asString(), "bar");
+		ensure_equals("appquery", slurl.getAppQuery(), "12345");
+		ensure_equals("grid2", "util.aditi.lindenlab.com", slurl.getGrid());		
+
+		LLGridManager::getInstance()->setGridChoice("foo.bar.com");			
+		slurl = LLSLURL("secondlife:///secondlife/myregion/1/2/3");
+		ensure_equals("/: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals("location", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals("region" , "myregion", slurl.getRegion());
+		ensure_equals("grid3", "util.agni.lindenlab.com", slurl.getGrid());
+				
+		slurl = LLSLURL("secondlife://Aditi/secondlife/myregion/1/2/3");
+		ensure_equals("/: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals("location", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals("region" , "myregion", slurl.getRegion());
+		ensure_equals("grid4", "util.aditi.lindenlab.com", slurl.getGrid());		
+		
+		slurl = LLSLURL("https://my.grid.com/app/foo/bar?12345");
+		ensure_equals("app", slurl.getType(), LLSLURL::APP);		
+		ensure_equals("appcmd", slurl.getAppCmd(), "foo");
+		ensure_equals("apppath", slurl.getAppPath().size(), 1);
+		ensure_equals("apppath2", slurl.getAppPath()[0].asString(), "bar");
+		ensure_equals("appquery", slurl.getAppQuery(), "12345");	
+		
+	}
+	
+	// construction from grid/region/vector combos
+	template<> template<>
+	void slurlTestObject::test<2>()
+	{
+		LLSLURL slurl = LLSLURL("mygrid.com", "my region");
+		ensure_equals("grid/region - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals("grid/region", slurl.getSLURLString(), 
+					  "https://mygrid.com/region/my%20region/128/128/0");	
+		
+		slurl = LLSLURL("mygrid.com", "my region", LLVector3(1,2,3));
+		ensure_equals("grid/region/vector - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals(" grid/region/vector", slurl.getSLURLString(), 
+					  "https://mygrid.com/region/my%20region/1/2/3");			
+
+		LLGridManager::getInstance()->setGridChoice("foo.bar.com.bar");			
+		slurl = LLSLURL("my region", LLVector3(1,2,3));
+		ensure_equals("grid/region/vector - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals(" grid/region/vector", slurl.getSLURLString(), 
+					  "https://foo.bar.com.bar/region/my%20region/1/2/3");	
+		
+		LLGridManager::getInstance()->setGridChoice("util.agni.lindenlab.com");	
+		slurl = LLSLURL("my region", LLVector3(1,2,3));
+		ensure_equals("default grid/region/vector - type", slurl.getType(), LLSLURL::LOCATION);
+		ensure_equals(" default grid/region/vector", slurl.getSLURLString(), 
+					  "http://slurl.com/secondlife/my%20region/1/2/3");	
+		
+	}
+	// Accessors
+	template<> template<>
+	void slurlTestObject::test<3>()
+	{
+		LLSLURL slurl = LLSLURL("https://my.grid.com/region/my%20region/1/2/3");
+		ensure_equals("login string", slurl.getLoginString(), "uri:my region&amp;1&amp;2&amp;3");
+		ensure_equals("location string", slurl.getLocationString(), "my region/1/2/3");
+		ensure_equals("grid", slurl.getGrid(), "my.grid.com");
+		ensure_equals("region", slurl.getRegion(), "my region");
+		ensure_equals("position", slurl.getPosition(), LLVector3(1, 2, 3));
+		
+	}
+}
-- 
GitLab