diff --git a/indra/newview/llsidetray.cpp b/indra/newview/llsidetray.cpp
index f1bd2f30279439c3ea97f774e619b0e129310ee7..4e3de3650b957d6f8231bc17ee2432917070f075 100644
--- a/indra/newview/llsidetray.cpp
+++ b/indra/newview/llsidetray.cpp
@@ -222,8 +222,13 @@ void LLSideTrayTab::toggleTabDocked()
 		// Remove the tab from Side Tray's tabs list.
 		// We have to do it despite removing the tab from Side Tray's child view tree
 		// by addChild(). Otherwise the tab could be accessed by the pointer in LLSideTray::mTabs.
-		side_tray->removeTab(this);
+		if (!side_tray->removeTab(this))
+		{
+			llwarns << "Failed to remove tab " << getName() << " from side tray" << llendl;
+			return;
+		}
 
+		setVisible(true); // *HACK: restore visibility after being hidden by LLSideTray::selectTabByName().
 		floater_tab->addChild(this);
 		floater_tab->setTitle(mTabTitle);
 
@@ -241,7 +246,11 @@ void LLSideTrayTab::toggleTabDocked()
 	}
 	else
 	{
-		side_tray->addChild(this, 0);
+		if (!side_tray->addTab(this))
+		{
+			llwarns << "Failed to add tab " << getName() << " to side tray" << llendl;
+			return;
+		}
 
 		setRect(side_tray->getLocalRect());
 		reshape(getRect().getWidth(), getRect().getHeight());
@@ -320,6 +329,13 @@ BOOL LLSideTray::postBuild()
 
 	LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLSideTray::handleLoginComplete, this));
 
+	// Remember original tabs order, so that we can restore it if user detaches and then re-attaches a tab.
+	for (child_vector_const_iter_t it = mTabs.begin(); it != mTabs.end(); ++it)
+	{
+		std::string tab_name = (*it)->getName();
+		mOriginalTabOrder.push_back(tab_name);
+	}
+
 	//EXT-8045
 	//connect all already created channels to reflect sidetray collapse/expand
 	std::vector<LLChannelManager::ChannelElem>& channels = LLChannelManager::getInstance()->getChannelList();
@@ -371,22 +387,33 @@ bool LLSideTray::selectTabByIndex(size_t index)
 
 bool LLSideTray::selectTabByName	(const std::string& name)
 {
-	LLSideTrayTab* side_bar = getTab(name);
+	LLSideTrayTab* new_tab = getTab(name);
 
-	if(side_bar == mActiveTab)
+	// Bail out if already selected.
+	if (new_tab == mActiveTab)
 		return false;
+
 	//deselect old tab
-	toggleTabButton(mActiveTab);
-	if(mActiveTab)
-		mActiveTab->setVisible(false);
+	if (mActiveTab)
+	{
+		toggleTabButton(mActiveTab);
+		if(mActiveTab)
+		{
+			mActiveTab->setVisible(false);
+		}
+	}
 
 	//select new tab
-	mActiveTab = side_bar;
-	toggleTabButton(mActiveTab);
-	LLSD key;//empty
-	mActiveTab->onOpen(key);
+	mActiveTab = new_tab;
+
+	if (mActiveTab)
+	{
+		toggleTabButton(mActiveTab);
+		LLSD key;//empty
+		mActiveTab->onOpen(key);
 
-	mActiveTab->setVisible(true);
+		mActiveTab->setVisible(true);
+	}
 
 	//arrange();
 	
@@ -447,18 +474,102 @@ bool LLSideTray::addChild(LLView* view, S32 tab_group)
 	return LLUICtrl::addChild(view, tab_group);
 }
 
-void LLSideTray::removeTab(LLView* child)
+bool LLSideTray::removeTab(LLSideTrayTab* tab)
+{
+	if (!tab) return false;
+	std::string tab_name = tab->getName();
+
+	// Look up the tab in the list of known tabs.
+	child_vector_iter_t tab_it = std::find(mTabs.begin(), mTabs.end(), tab);
+	if (tab_it == mTabs.end())
+	{
+		llwarns << "Cannot find tab named " << tab_name << llendl;
+		return false;
+	}
+
+	// Find the button corresponding to the tab.
+	button_map_t::iterator btn_it = mTabButtons.find(tab_name);
+	if (btn_it == mTabButtons.end())
+	{
+		llwarns << "Cannot find button for tab named " << tab_name << llendl;
+		return false;
+	}
+	LLButton* btn = btn_it->second;
+
+	// Deselect the tab.
+	if (mActiveTab == tab)
+	{
+		child_vector_iter_t next_tab_it =
+				(tab_it < (mTabs.end() - 1)) ? tab_it + 1 : mTabs.begin();
+		selectTabByName((*next_tab_it)->getName());
+	}
+
+	// Remove the tab.
+	removeChild(tab);
+	mTabs.erase(tab_it);
+
+	// Remove the button from the buttons panel so that it isn't drawn anymore.
+	mButtonsPanel->removeChild(btn);
+
+	// Re-arrange remaining tabs.
+	arrange();
+
+	return true;
+}
+
+bool LLSideTray::addTab(LLSideTrayTab* tab)
 {
-	for (child_vector_iter_t child_it = mTabs.begin(); child_it != mTabs.end(); ++child_it)
+	if (tab == NULL) return false;
+
+	std::string tab_name = tab->getName();
+
+	// Make sure the tab isn't already in the list.
+	if (std::find(mTabs.begin(), mTabs.end(), tab) != mTabs.end())
 	{
-		if (*child_it == child)
+		llwarns << "Attempt to re-add existing tab " << tab_name << llendl;
+		return false;
+	}
+
+	// Look up the corresponding button.
+	button_map_t::const_iterator btn_it = mTabButtons.find(tab_name);
+	if (btn_it == mTabButtons.end())
+	{
+		llwarns << "Tab " << tab_name << " has no associated button" << llendl;
+		return false;
+	}
+	LLButton* btn = btn_it->second;
+
+	// Insert the tab at its original position.
+	LLUICtrl::addChild(tab);
+	{
+		tab_order_vector_const_iter_t new_tab_orig_pos =
+			std::find(mOriginalTabOrder.begin(), mOriginalTabOrder.end(), tab_name);
+		llassert(new_tab_orig_pos != mOriginalTabOrder.end());
+		child_vector_iter_t insert_pos = mTabs.end();
+
+		for (child_vector_iter_t tab_it = mTabs.begin(); tab_it != mTabs.end(); ++tab_it)
 		{
-			mTabs.erase(child_it);
-			break;
+			tab_order_vector_const_iter_t cur_tab_orig_pos =
+				std::find(mOriginalTabOrder.begin(), mOriginalTabOrder.end(), (*tab_it)->getName());
+			llassert(cur_tab_orig_pos != mOriginalTabOrder.end());
+
+			if (new_tab_orig_pos < cur_tab_orig_pos)
+			{
+				insert_pos = tab_it;
+				break;
+			}
 		}
+
+		mTabs.insert(insert_pos, tab);
 	}
 
-	mActiveTab = getTab("sidebar_openclose");
+	// Add the button to the buttons panel so that it's drawn again.
+	mButtonsPanel->addChildInBack(btn);
+
+	// Arrange tabs after inserting a new one.
+	arrange();
+
+	return true;
 }
 
 void	LLSideTray::createButtons	()
diff --git a/indra/newview/llsidetray.h b/indra/newview/llsidetray.h
index 7475d5f7fb8263c8ea08c00a326564f8f7f0dd41..081f382478928a6b7666d64d9c45478b3abd13a3 100644
--- a/indra/newview/llsidetray.h
+++ b/indra/newview/llsidetray.h
@@ -68,6 +68,8 @@ protected:
 	typedef child_vector_t::const_iterator  			child_vector_const_iter_t;
 	typedef child_vector_t::reverse_iterator 			child_vector_reverse_iter_t;
 	typedef child_vector_t::const_reverse_iterator 		child_vector_const_reverse_iter_t;
+	typedef std::vector<std::string>					tab_order_vector_t;
+	typedef tab_order_vector_t::const_iterator			tab_order_vector_const_iter_t;
 
 public:
 
@@ -144,7 +146,8 @@ public:
 	void		onToggleCollapse();
 
 	bool		addChild		(LLView* view, S32 tab_group);
-	void		removeTab		(LLView* view);
+	bool		removeTab		(LLSideTrayTab* tab); // Used to detach tabs temporarily
+	bool		addTab			(LLSideTrayTab* tab); // Used to re-attach tabs
 
 	BOOL		handleMouseDown	(S32 x, S32 y, MASK mask);
 	
@@ -180,10 +183,15 @@ private:
 	
 private:
 
+	typedef std::pair<LLButton*, LLSideTrayTab*> detached_tab_t;
+	typedef std::map<std::string, detached_tab_t> detached_tab_map_t;
+
 	LLPanel*						mButtonsPanel;
 	typedef std::map<std::string,LLButton*> button_map_t;
 	button_map_t					mTabButtons;
 	child_vector_t					mTabs;
+	detached_tab_map_t				mDetachedTabs;
+	tab_order_vector_t				mOriginalTabOrder;
 	LLSideTrayTab*					mActiveTab;	
 	
 	commit_signal_t					mCollapseSignal;