diff --git a/indra/newview/llsidetray.cpp b/indra/newview/llsidetray.cpp
index 78890fae93b210cad1c9ca8082a11762c7d91ce7..85b6e0dec45d378a27fc17a53ee745a9097eba12 100644
--- a/indra/newview/llsidetray.cpp
+++ b/indra/newview/llsidetray.cpp
@@ -31,6 +31,7 @@
 #include "llagentcamera.h"
 #include "llappviewer.h"
 #include "llbottomtray.h"
+#include "llfloaterreg.h"
 #include "llsidetray.h"
 #include "llviewerwindow.h"
 #include "llaccordionctrl.h"
@@ -64,6 +65,7 @@ static const std::string TAB_PANEL_CAPTION_TITLE_BOX = "sidetray_tab_title";
 
 LLSideTray* LLSideTray::sInstance = 0;
 
+// static
 LLSideTray* LLSideTray::getInstance()
 {
 	if (!sInstance)
@@ -75,6 +77,7 @@ LLSideTray* LLSideTray::getInstance()
 	return sInstance;
 }
 
+// static
 bool	LLSideTray::instanceCreated	()
 {
 	return sInstance!=0;
@@ -125,7 +128,9 @@ public:
 	const std::string& getTabTitle() const { return mTabTitle;}
 	
 	void			onOpen		(const LLSD& key);
-	
+
+	void			toggleTabDocked();
+
 	LLPanel *getPanel();
 private:
 	std::string mTabTitle;
@@ -171,6 +176,8 @@ BOOL LLSideTrayTab::postBuild()
 	
 	title_panel->getChild<LLTextBox>(TAB_PANEL_CAPTION_TITLE_BOX)->setValue(mTabTitle);
 
+	getChild<LLButton>("dock")->setCommitCallback(boost::bind(&LLSideTrayTab::toggleTabDocked, this));
+
 	return true;
 }
 
@@ -203,6 +210,66 @@ void	LLSideTrayTab::onOpen		(const LLSD& key)
 		panel->onOpen(key);
 }
 
+void LLSideTrayTab::toggleTabDocked()
+{
+	LLFloater* floater_tab = LLFloaterReg::getInstance("side_bar_tab", LLSD().with("name", mTabTitle));
+	if (!floater_tab) return;
+
+	LLFloaterReg::toggleInstance("side_bar_tab", LLSD().with("name", mTabTitle));
+
+	LLSideTray* side_tray = LLSideTray::getInstance();
+
+	if (LLFloater::isShown(floater_tab))
+	{
+		// 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.
+		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);
+
+		LLRect rect = side_tray->getLocalRect();
+		floater_tab->reshape(rect.getWidth(), rect.getHeight());
+
+		rect.mTop -= floater_tab->getHeaderHeight();
+		setRect(rect);
+		reshape(rect.getWidth(), rect.getHeight());
+
+		// Set FOLLOWS_ALL flag for the tab to follow floater dimensions upon resizing.
+		setFollowsAll();
+
+		if (!side_tray->getCollapsed())
+		{
+			side_tray->collapseSideBar();
+		}
+	}
+	else
+	{
+		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());
+
+		// Select the re-docked tab.
+		side_tray->selectTabByName(getName());
+
+		if (side_tray->getCollapsed())
+		{
+			side_tray->expandSideBar();
+		}
+	}
+}
+
 LLPanel*	LLSideTrayTab::getPanel()
 {
 	LLPanel* panel = dynamic_cast<LLPanel*>(mMainPanel);
@@ -218,6 +285,93 @@ LLSideTrayTab*  LLSideTrayTab::createInstance	()
 	return tab;
 }
 
+//////////////////////////////////////////////////////////////////////////////
+// LLSideTrayButton
+// Side Tray tab button with "tear off" handling.
+//////////////////////////////////////////////////////////////////////////////
+
+class LLSideTrayButton : public LLButton
+{
+public:
+	/*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask)
+	{
+		// Route future Mouse messages here preemptively.  (Release on mouse up.)
+		// No handler needed for focus lost since this class has no state that depends on it.
+		gFocusMgr.setMouseCapture(this);
+
+		localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY);
+
+		// Note: don't pass on to children
+		return TRUE;
+	}
+
+	/*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask)
+	{
+		// We only handle the click if the click both started and ended within us
+		if( !hasMouseCapture() ) return FALSE;
+
+		S32 screen_x;
+		S32 screen_y;
+		localPointToScreen(x, y, &screen_x, &screen_y);
+
+		S32 delta_x = screen_x - mDragLastScreenX;
+		S32 delta_y = screen_y - mDragLastScreenY;
+
+		LLSideTray* side_tray = LLSideTray::getInstance();
+
+		// Check if the tab we are dragging is docked.
+		if (!side_tray->isTabAttached(getName())) return FALSE;
+
+		// Same value is hardcoded in LLDragHandle::handleHover().
+		const S32 undock_threshold = 12;
+
+		// Detach a tab if it has been pulled further than undock_threshold.
+		if (delta_x <= -undock_threshold ||	delta_x >= undock_threshold	||
+			delta_y <= -undock_threshold ||	delta_y >= undock_threshold)
+		{
+			LLSideTrayTab* tab = side_tray->getTab(getName());
+			if (!tab) return FALSE;
+
+			tab->toggleTabDocked();
+
+			LLFloater* floater_tab = LLFloaterReg::getInstance("side_bar_tab", LLSD().with("name", tab->getTabTitle()));
+			if (!floater_tab) return FALSE;
+
+
+			LLRect original_rect = floater_tab->getRect();
+			S32 header_snap_y = floater_tab->getHeaderHeight() / 2;
+			S32 snap_x = screen_x - original_rect.mLeft - original_rect.getWidth() / 2;
+			S32 snap_y = screen_y - original_rect.mTop + header_snap_y;
+
+			// Move the floater to appear "under" the mouse pointer.
+			floater_tab->setRect(original_rect.translate(snap_x, snap_y));
+
+			// Snap the mouse pointer to the center of the floater header
+			// and call 'mouse down' event handler to begin dragging.
+			floater_tab->handleMouseDown(original_rect.getWidth() / 2,
+										 original_rect.getHeight() - header_snap_y,
+										 mask);
+
+			return TRUE;
+		}
+
+		return FALSE;
+	}
+
+protected:
+	LLSideTrayButton(const LLButton::Params& p)
+	: LLButton(p)
+	, mDragLastScreenX(0)
+	, mDragLastScreenY(0)
+	{}
+
+	friend class LLUICtrlFactory;
+
+private:
+	S32		mDragLastScreenX;
+	S32		mDragLastScreenY;
+};
+
 //////////////////////////////////////////////////////////////////////////////
 // LLSideTray
 //////////////////////////////////////////////////////////////////////////////
@@ -273,6 +427,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();
@@ -297,6 +458,17 @@ LLSideTrayTab* LLSideTray::getTab(const std::string& name)
 	return getChild<LLSideTrayTab>(name,false);
 }
 
+bool LLSideTray::isTabAttached(const std::string& name)
+{
+	LLSideTrayTab* tab = getTab(name);
+	return std::find(mTabs.begin(), mTabs.end(), tab) != mTabs.end();
+}
+
+bool LLSideTray::hasTabs()
+{
+	// The open/close tab doesn't count.
+	return mTabs.size() > 1;
+}
 
 void LLSideTray::toggleTabButton(LLSideTrayTab* tab)
 {
@@ -324,22 +496,27 @@ 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);
+	}
 
 	//select new tab
-	mActiveTab = side_bar;
-	toggleTabButton(mActiveTab);
-	LLSD key;//empty
-	mActiveTab->onOpen(key);
+	mActiveTab = new_tab;
 
-	mActiveTab->setVisible(true);
+	if (mActiveTab)
+	{
+		toggleTabButton(mActiveTab);
+		LLSD key;//empty
+		mActiveTab->onOpen(key);
+	}
 
 	//arrange();
 	
@@ -348,7 +525,9 @@ bool LLSideTray::selectTabByName	(const std::string& name)
 	for ( child_it = mTabs.begin(); child_it != mTabs.end(); ++child_it)
 	{
 		LLSideTrayTab* sidebar_tab = *child_it;
-		sidebar_tab->setVisible(sidebar_tab  == mActiveTab);
+		// When the last tab gets detached, for a short moment the "Toggle Sidebar" pseudo-tab
+		// is shown. So, to avoid the flicker we make sure it never gets visible.
+		sidebar_tab->setVisible(sidebar_tab == mActiveTab && (*child_it)->getName() != "sidebar_openclose");
 	}
 	return true;
 }
@@ -372,8 +551,18 @@ LLButton* LLSideTray::createButton	(const std::string& name,const std::string& i
 	bparams.image_disabled(sidetray_params.tab_btn_image_normal);
 	bparams.image_disabled_selected(sidetray_params.tab_btn_image_selected);
 
-	LLButton* button = LLUICtrlFactory::create<LLButton> (bparams);
-	button->setLabel(name);
+	LLButton* button;
+	if (name == "sidebar_openclose")
+	{
+		// "Open/Close" button shouldn't allow "tear off"
+		// hence it is created as LLButton instance.
+		button = LLUICtrlFactory::create<LLButton>(bparams);
+	}
+	else
+	{
+		button = LLUICtrlFactory::create<LLSideTrayButton>(bparams);
+	}
+
 	button->setClickedCallback(callback);
 
 	button->setToolTip(tooltip);
@@ -400,6 +589,103 @@ bool LLSideTray::addChild(LLView* view, S32 tab_group)
 	return LLUICtrl::addChild(view, tab_group);
 }
 
+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)
+{
+	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())
+	{
+		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)
+		{
+			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);
+	}
+
+	// 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	()
 {
@@ -414,12 +700,12 @@ void	LLSideTray::createButtons	()
 		// The "OpenClose" button will open/close the whole panel
 		if (name == "sidebar_openclose")
 		{
-			mCollapseButton = createButton("",sidebar_tab->mImage,sidebar_tab->getTabTitle(),
+			mCollapseButton = createButton(name,sidebar_tab->mImage,sidebar_tab->getTabTitle(),
 				boost::bind(&LLSideTray::onToggleCollapse, this));
 		}
 		else
 		{
-			LLButton* button = createButton("",sidebar_tab->mImage,sidebar_tab->getTabTitle(),
+			LLButton* button = createButton(name,sidebar_tab->mImage,sidebar_tab->getTabTitle(),
 				boost::bind(&LLSideTray::onTabButtonClick, this, name));
 			mTabButtons[name] = button;
 		}
@@ -536,6 +822,9 @@ void LLSideTray::arrange()
 		LLSideTrayTab* sidebar_tab = *child_it;
 		sidebar_tab->setShape(getLocalRect());
 	}
+
+	// The tab buttons should be shown only if there is at least one non-detached tab.
+	mButtonsPanel->setVisible(hasTabs());
 }
 
 void LLSideTray::collapseSideBar()
diff --git a/indra/newview/llsidetray.h b/indra/newview/llsidetray.h
index 4ff560d83b60ae90196294801233a0967b2023cc..f0cc2c11469a25b3f85408b9cdd8865eefa314b7 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,6 +146,8 @@ public:
 	void		onToggleCollapse();
 
 	bool		addChild		(LLView* view, S32 tab_group);
+	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);
 	
@@ -157,10 +161,15 @@ public:
 
 	void		handleLoginComplete();
 
-protected:
 	LLSideTrayTab* getTab		(const std::string& name);
 
+	bool 		isTabAttached	(const std::string& name);
+
+protected:
+	bool		hasTabs			();
+
 	void		createButtons	();
+
 	LLButton*	createButton	(const std::string& name,const std::string& image,const std::string& tooltip,
 									LLUICtrl::commit_callback_t callback);
 	void		arrange			();
@@ -179,10 +188,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;
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index b572a8cff6d4fe1b5f51a67434888328924408af..87282985751572617025722faa36479c19d26ac2 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -234,6 +234,7 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("script_limits", "floater_script_limits.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptLimits>);
 	LLFloaterReg::add("sell_land", "floater_sell_land.xml", &LLFloaterSellLand::buildFloater);
 	LLFloaterReg::add("settings_debug", "floater_settings_debug.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSettingsDebug>);
+	LLFloaterReg::add("side_bar_tab", "floater_side_bar_tab.xml",&LLFloaterReg::build<LLFloater>);
 	LLFloaterReg::add("stats", "floater_stats.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloater>);
 	LLFloaterReg::add("start_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRunQueue>);
 	LLFloaterReg::add("stop_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotRunQueue>);
diff --git a/indra/newview/skins/default/xui/en/floater_side_bar_tab.xml b/indra/newview/skins/default/xui/en/floater_side_bar_tab.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1466c2d2a59e0e3f2da60bcf61801c3915e39dd0
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_side_bar_tab.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<floater
+ can_close="false"
+ can_resize="true">
+</floater>
diff --git a/indra/newview/skins/default/xui/en/panel_side_tray_tab_caption.xml b/indra/newview/skins/default/xui/en/panel_side_tray_tab_caption.xml
index 0a9c70303e1b81d046e45f5f8c98807b4acfb871..60c2e0830c0390dcfb986cf86063d52ec2083195 100644
--- a/indra/newview/skins/default/xui/en/panel_side_tray_tab_caption.xml
+++ b/indra/newview/skins/default/xui/en/panel_side_tray_tab_caption.xml
@@ -20,7 +20,19 @@
      value="Side Panel"
      width="255" />
      <button
-     follows="left|right|top"
+     follows="right|top"
+     height="16"
+     image_selected="Icon_Dock_Press"
+     image_unselected="Icon_Dock_Foreground"
+     image_disabled="Icon_Dock_Press"
+     layout="topleft"
+     name="dock"
+     top="10"
+     right="-28"
+     width="16"
+     tool_tip="Dock/Undock tab" />
+     <button
+     follows="right|top"
      height="16"
      image_selected="Icon_Help_Press"
      image_unselected="Icon_Help_Foreground"