diff --git a/.hgtags b/.hgtags
index 8043bf9f4000860d6367d50b3788d5b7bd8ead1e..b6a9eca2caaba08962e3a2f4166da22a6657b679 100644
--- a/.hgtags
+++ b/.hgtags
@@ -143,3 +143,4 @@ a9abb9633a266c8d2fe62411cfd1c86d32da72bf 2.7.1-release
 19a498fa62570f352d7d246f17e3c81cc1d82d8b 2.7.5-start
 09984bfa6cae17e0f72d02b75c1b7393c65eecfc DRTVWR-69_2.7.5-beta1
 09984bfa6cae17e0f72d02b75c1b7393c65eecfc 2.7.5-beta1
+e1ed60913230dd64269a7f7fc52cbc6004f6d52c 2.8.0-start
diff --git a/doc/contributions.txt b/doc/contributions.txt
index bad78dcd5f2eeeb8be05d5550c45391b7daa874e..a619d11737476c039f064afa807b5f68e0494679 100644
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -20,6 +20,7 @@ Aimee Trescothick
 	SNOW-570
 	SNOW-572
 	SNOW-575
+	STORM-1315
 	VWR-3321
 	VWR-3336
 	VWR-3903
@@ -103,6 +104,7 @@ Ales Beaumont
 Alexandrea Fride
     STORM-255
 	STORM-960
+	STORM-1459
 Alissa Sabre
 	VWR-81
 	VWR-83
@@ -449,6 +451,7 @@ Jonathan Yap
 	STORM-899
 	STORM-1273
 	STORM-1462
+	STORM-1459
 Kage Pixel
 	VWR-11
 Ken March
@@ -690,6 +693,7 @@ Robin Cornelius
 	STORM-1019
 	STORM-1095
 	STORM-1128
+	STORM-1459
 	VWR-2488
 	VWR-9557
 	VWR-10579
@@ -831,6 +835,7 @@ Thickbrick Sleaford
 	VWR-24420
 	STORM-956
 	STORM-1147
+	STORM-1325
 Thraxis Epsilon
 	SVC-371
 	VWR-383
diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
index 3dcaca4f16c3d116fc0ee622df698b269096e3ac..0018b8e844a18b7941a65594f222dbe42f9e8c1b 100644
--- a/indra/llcommon/llversionviewer.h
+++ b/indra/llcommon/llversionviewer.h
@@ -29,7 +29,7 @@
 
 const S32 LL_VERSION_MAJOR = 2;
 const S32 LL_VERSION_MINOR = 8;
-const S32 LL_VERSION_PATCH = 0;
+const S32 LL_VERSION_PATCH = 1;
 const S32 LL_VERSION_BUILD = 0;
 
 const char * const LL_CHANNEL = "Second Life Developer";
diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt
index 9dadad7dd34836f0f107afaed854973ddb85d7cd..cd100cdf9fd38a0147ddb33a91da54de57f9bf28 100644
--- a/indra/llmath/CMakeLists.txt
+++ b/indra/llmath/CMakeLists.txt
@@ -12,6 +12,8 @@ include_directories(
 set(llmath_SOURCE_FILES
     llbbox.cpp
     llbboxlocal.cpp
+    llcalc.cpp
+    llcalcparser.cpp
     llcamera.cpp
     llcoordframe.cpp
     llline.cpp
@@ -46,6 +48,8 @@ set(llmath_HEADER_FILES
     coordframe.h
     llbbox.h
     llbboxlocal.h
+    llcalc.h
+    llcalcparser.h
     llcamera.h
     llcoord.h
     llcoordframe.h
diff --git a/indra/llmath/llcalc.cpp b/indra/llmath/llcalc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..597d0815fb527295369dbd0aa7517af540532bfd
--- /dev/null
+++ b/indra/llmath/llcalc.cpp
@@ -0,0 +1,145 @@
+/*
+ *  LLCalc.cpp
+ *  SecondLife
+ *
+ *  Created by Aimee Walton on 28/09/2008.
+ *  Copyright 2008 Aimee Walton.
+ *
+ */
+
+#include "linden_common.h"
+
+#include "llcalc.h"
+
+#include "llcalcparser.h"
+#include "llmath.h"
+
+
+// Variable names for use in the build floater
+const char* LLCalc::X_POS = "PX";
+const char* LLCalc::Y_POS = "PY";
+const char* LLCalc::Z_POS = "PZ";
+const char* LLCalc::X_SCALE = "SX";
+const char* LLCalc::Y_SCALE = "SY";
+const char* LLCalc::Z_SCALE = "SZ";
+const char* LLCalc::X_ROT = "RX";
+const char* LLCalc::Y_ROT = "RY";
+const char* LLCalc::Z_ROT = "RZ";
+const char* LLCalc::HOLLOW = "HLW";
+const char* LLCalc::CUT_BEGIN = "CB";
+const char* LLCalc::CUT_END = "CE";
+const char* LLCalc::PATH_BEGIN = "PB";
+const char* LLCalc::PATH_END = "PE";
+const char* LLCalc::TWIST_BEGIN = "TB";
+const char* LLCalc::TWIST_END = "TE";
+const char* LLCalc::X_SHEAR = "SHX";
+const char* LLCalc::Y_SHEAR = "SHY";
+const char* LLCalc::X_TAPER = "TPX";
+const char* LLCalc::Y_TAPER = "TPY";
+const char* LLCalc::RADIUS_OFFSET = "ROF";
+const char* LLCalc::REVOLUTIONS = "REV";
+const char* LLCalc::SKEW = "SKW";
+const char* LLCalc::X_HOLE = "HLX";
+const char* LLCalc::Y_HOLE = "HLY";
+const char* LLCalc::TEX_U_SCALE = "TSU";
+const char* LLCalc::TEX_V_SCALE = "TSV";
+const char* LLCalc::TEX_U_OFFSET = "TOU";
+const char* LLCalc::TEX_V_OFFSET = "TOV";
+const char* LLCalc::TEX_ROTATION = "TROT";
+const char* LLCalc::TEX_TRANSPARENCY = "TRNS";
+const char* LLCalc::TEX_GLOW = "GLOW";
+
+
+LLCalc* LLCalc::sInstance = NULL;
+
+LLCalc::LLCalc() : mLastErrorPos(0)
+{
+	// Init table of constants
+	mConstants["PI"] = F_PI;
+	mConstants["TWO_PI"] = F_TWO_PI;
+	mConstants["PI_BY_TWO"] = F_PI_BY_TWO;
+	mConstants["SQRT_TWO_PI"] = F_SQRT_TWO_PI;
+	mConstants["SQRT2"] = F_SQRT2;
+	mConstants["SQRT3"] = F_SQRT3;
+	mConstants["DEG_TO_RAD"] = DEG_TO_RAD;
+	mConstants["RAD_TO_DEG"] = RAD_TO_DEG;
+	mConstants["GRAVITY"] = GRAVITY;
+}
+
+LLCalc::~LLCalc()
+{
+}
+
+//static
+void LLCalc::cleanUp()
+{
+	delete sInstance;
+	sInstance = NULL;
+}
+
+//static
+LLCalc* LLCalc::getInstance()
+{
+    if (!sInstance)	sInstance = new LLCalc();
+	return sInstance;
+}
+
+void LLCalc::setVar(const std::string& name, const F32& value)
+{
+	mVariables[name] = value;
+}
+
+void LLCalc::clearVar(const std::string& name)
+{
+	mVariables.erase(name);
+}
+
+void LLCalc::clearAllVariables()
+{
+	mVariables.clear();
+}
+
+/*
+void LLCalc::updateVariables(LLSD& vars)
+{
+	LLSD::map_iterator cIt = vars.beginMap();
+	for(; cIt != vars.endMap(); cIt++)
+	{
+		setVar(cIt->first, (F32)(LLSD::Real)cIt->second);
+	}
+}
+*/
+
+bool LLCalc::evalString(const std::string& expression, F32& result)
+{
+	std::string expr_upper = expression;
+	LLStringUtil::toUpper(expr_upper);
+	
+	LLCalcParser calc(result, &mConstants, &mVariables);
+
+	mLastErrorPos = 0;
+	std::string::iterator start = expr_upper.begin();
+ 	parse_info<std::string::iterator> info;
+	
+	try
+	{
+		info = parse(start, expr_upper.end(), calc, space_p);
+		lldebugs << "Math expression: " << expression << " = " << result << llendl;
+	}
+	catch(parser_error<std::string, std::string::iterator> &e)
+	{
+		mLastErrorPos = e.where - expr_upper.begin();
+		
+		llinfos << "Calc parser exception: " << e.descriptor << " at " << mLastErrorPos << " in expression: " << expression << llendl;
+		return false;
+	}
+	
+	if (!info.full)
+	{
+		mLastErrorPos = info.stop - expr_upper.begin();
+		llinfos << "Unhandled syntax error at " << mLastErrorPos << " in expression: " << expression << llendl;
+		return false;
+	}
+	
+	return true;
+}
diff --git a/indra/llmath/llcalc.h b/indra/llmath/llcalc.h
new file mode 100644
index 0000000000000000000000000000000000000000..cc31950cb681001ba65b9232249be9161ec173f4
--- /dev/null
+++ b/indra/llmath/llcalc.h
@@ -0,0 +1,83 @@
+/*
+ *  LLCalc.h
+ *  SecondLife
+ *
+ *  Created by Aimee Walton on 28/09/2008.
+ *  Copyright 2008 Aimee Walton.
+ *
+ */
+
+#ifndef LL_CALC_H
+#define LL_CALC_H
+
+#include <map>
+#include <string>
+
+class LLCalc
+{
+public:
+	LLCalc();
+	~LLCalc();
+
+	// Variable name constants
+	static const char* X_POS;
+	static const char* Y_POS;
+	static const char* Z_POS;
+	static const char* X_SCALE;
+	static const char* Y_SCALE;
+	static const char* Z_SCALE;
+	static const char* X_ROT;
+	static const char* Y_ROT;
+	static const char* Z_ROT;
+	static const char* HOLLOW;
+	static const char* CUT_BEGIN;
+	static const char* CUT_END;
+	static const char* PATH_BEGIN;
+	static const char* PATH_END;
+	static const char* TWIST_BEGIN;
+	static const char* TWIST_END;
+	static const char* X_SHEAR;
+	static const char* Y_SHEAR;
+	static const char* X_TAPER;
+	static const char* Y_TAPER;
+	static const char* RADIUS_OFFSET;
+	static const char* REVOLUTIONS;
+	static const char* SKEW;
+	static const char* X_HOLE;
+	static const char* Y_HOLE;
+	static const char* TEX_U_SCALE;
+	static const char* TEX_V_SCALE;
+	static const char* TEX_U_OFFSET;
+	static const char* TEX_V_OFFSET;
+	static const char* TEX_ROTATION;
+	static const char* TEX_TRANSPARENCY;
+	static const char* TEX_GLOW;
+
+	void	setVar(const std::string& name, const F32& value);
+	void	clearVar(const std::string& name);
+	void	clearAllVariables();
+//	void	updateVariables(LLSD& vars);
+
+	bool	evalString(const std::string& expression, F32& result);
+	std::string::size_type	getLastErrorPos()	{ return mLastErrorPos; }
+	
+	static LLCalc* getInstance();
+	static void cleanUp();
+
+	typedef	std::map<std::string, F32> calc_map_t;
+	
+private:
+	std::string::size_type	mLastErrorPos;
+	
+	calc_map_t	mConstants;
+	calc_map_t	mVariables;
+	
+	// *TODO: Add support for storing user defined variables, and stored functions.
+	//	Will need UI work, and a means to save them between sessions.
+//	calc_map_t mUserVariables;
+	
+	// "There shall be only one"
+	static LLCalc*	sInstance;
+};
+
+#endif // LL_CALC_H
diff --git a/indra/llmath/llcalcparser.cpp b/indra/llmath/llcalcparser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fd55376fa9086173be5712dab2ebc415c1e99609
--- /dev/null
+++ b/indra/llmath/llcalcparser.cpp
@@ -0,0 +1,46 @@
+/*
+ *  LLCalcParser.cpp
+ *  SecondLife
+ *
+ *  Created by Aimee Walton on 28/09/2008.
+ *  Copyright 2008 Aimee Walton.
+ *
+ */
+
+#include "linden_common.h"
+
+#include "llcalcparser.h"
+using namespace boost::spirit::classic;
+
+F32 LLCalcParser::lookup(const std::string::iterator& start, const std::string::iterator& end) const
+{
+	LLCalc::calc_map_t::iterator iter;
+
+	std::string name(start, end);
+	
+	if (mConstants)
+	{
+		iter = mConstants->find(name);
+		if (iter != mConstants->end())
+		{
+			return (*iter).second;
+		}
+	}
+	else
+	{
+		// This should never happen!
+		throw_(end, std::string("Missing constants table"));
+	}
+	
+	if (mVariables)
+	{
+		iter = mVariables->find(name);
+		if (iter != mVariables->end())
+		{
+			return (*iter).second;
+		}
+	}
+	
+	throw_(end, std::string("Unknown symbol " + name));
+	return 0.f;
+}
diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h
new file mode 100644
index 0000000000000000000000000000000000000000..600e17366107c9279badc2001341424dc506e07e
--- /dev/null
+++ b/indra/llmath/llcalcparser.h
@@ -0,0 +1,174 @@
+/*
+ *  LLCalcParser.h
+ *  SecondLife
+ *
+ *  Created by Aimee Walton on 28/09/2008.
+ *  Copyright 2008 Aimee Walton.
+ *
+ */
+
+#ifndef LL_CALCPARSER_H
+#define LL_CALCPARSER_H
+
+#include <boost/spirit/include/classic_attribute.hpp>
+#include <boost/spirit/include/classic_core.hpp>
+#include <boost/spirit/include/classic_error_handling.hpp>
+#include <boost/spirit/include/classic_position_iterator.hpp>
+#include <boost/spirit/include/phoenix1_binders.hpp>
+#include <boost/spirit/include/classic_symbols.hpp>
+using namespace boost::spirit::classic;
+
+#include "llcalc.h"
+#include "llmath.h"
+
+struct LLCalcParser : grammar<LLCalcParser>
+{
+	LLCalcParser(F32& result, LLCalc::calc_map_t* constants, LLCalc::calc_map_t* vars) :
+		mResult(result), mConstants(constants), mVariables(vars) {};
+	
+	struct value_closure : closure<value_closure, F32>
+	{
+		member1 value;
+	};
+	
+	template <typename ScannerT>
+	struct definition
+	{
+		// Rule declarations
+		rule<ScannerT> statement, identifier;
+		rule<ScannerT, value_closure::context_t> expression, term,
+			power, 
+			unary_expr, 
+			factor, 
+			unary_func, 
+			binary_func,
+			group;
+
+		// start() should return the starting symbol
+		rule<ScannerT> const& start() const { return statement; }
+		
+		definition(LLCalcParser const& self)
+		{
+			using namespace phoenix;
+			
+			assertion<std::string> assert_domain("Domain error");
+//			assertion<std::string> assert_symbol("Unknown symbol");
+			assertion<std::string> assert_syntax("Syntax error");
+			
+			identifier =
+				lexeme_d[(alpha_p | '_') >> *(alnum_p | '_')]
+			;
+			
+			group =
+				'(' >> expression[group.value = arg1] >> assert_syntax(ch_p(')'))
+			;
+
+			unary_func =
+				((str_p("SIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sin)(self,arg1)]) |
+				 (str_p("COS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_cos)(self,arg1)]) |
+				 (str_p("TAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_tan)(self,arg1)]) |
+				 (str_p("ASIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_asin)(self,arg1)]) |
+				 (str_p("ACOS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_acos)(self,arg1)]) |
+				 (str_p("ATAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_atan)(self,arg1)]) |
+				 (str_p("SQRT") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sqrt)(self,arg1)]) |
+				 (str_p("LOG") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_log)(self,arg1)]) |
+				 (str_p("EXP") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_exp)(self,arg1)]) |
+				 (str_p("ABS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_fabs)(self,arg1)]) |
+				 (str_p("FLR") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_floor)(self,arg1)]) |
+				 (str_p("CEIL") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_ceil)(self,arg1)])
+				) >> assert_syntax(ch_p(')'))
+			;
+			
+			binary_func =
+				((str_p("ATAN2") >> '(' >> expression[binary_func.value = arg1] >> ',' >>
+				  expression[binary_func.value = bind(&LLCalcParser::_atan2)(self, binary_func.value, arg1)]) |
+				 (str_p("MIN") >> '(' >> expression[binary_func.value = arg1] >> ',' >> 
+				  expression[binary_func.value = bind(&LLCalcParser::_min)(self, binary_func.value, arg1)]) |
+				 (str_p("MAX") >> '(' >> expression[binary_func.value = arg1] >> ',' >> 
+				  expression[binary_func.value = bind(&LLCalcParser::_max)(self, binary_func.value, arg1)])
+				) >> assert_syntax(ch_p(')'))
+			;
+			
+			// *TODO: Localisation of the decimal point?
+			// Problem, LLLineEditor::postvalidateFloat accepts a comma when appropriate
+			// for the current locale. However to do that here could clash with using
+			// the comma as a separator when passing arguments to functions.
+			factor =
+				(ureal_p[factor.value = arg1] |
+				 group[factor.value = arg1] |
+				 unary_func[factor.value = arg1] |
+				 binary_func[factor.value = arg1] |
+				 // Lookup throws an Unknown Symbol error if it is unknown, while this works fine,
+				 // would be "neater" to handle symbol lookup from here with an assertive parser.
+//				 constants_p[factor.value = arg1]|
+				 identifier[factor.value = bind(&LLCalcParser::lookup)(self, arg1, arg2)]
+				) >>
+				// Detect and throw math errors.
+				assert_domain(eps_p(bind(&LLCalcParser::checkNaN)(self, factor.value)))
+			;
+
+			unary_expr =
+				!ch_p('+') >> factor[unary_expr.value = arg1] |
+				'-' >> factor[unary_expr.value = -arg1]
+			;
+			
+			power =
+				unary_expr[power.value = arg1] >>
+				*('^' >> assert_syntax(unary_expr[power.value = bind(&powf)(power.value, arg1)]))
+			;
+			
+			term =
+				power[term.value = arg1] >>
+				*(('*' >> assert_syntax(power[term.value *= arg1])) |
+				  ('/' >> assert_syntax(power[term.value /= arg1])) |
+				  ('%' >> assert_syntax(power[term.value = bind(&fmodf)(term.value, arg1)]))
+				)
+			;
+			
+			expression =
+				assert_syntax(term[expression.value = arg1]) >>
+				*(('+' >> assert_syntax(term[expression.value += arg1])) |
+				  ('-' >> assert_syntax(term[expression.value -= arg1]))
+				)
+			;
+
+			statement =
+				!ch_p('=') >> ( expression )[var(self.mResult) = arg1] >> (end_p)
+			;
+		}
+	};
+	
+private:
+	// Member functions for semantic actions
+	F32	lookup(const std::string::iterator&, const std::string::iterator&) const;
+	F32 _min(const F32& a, const F32& b) const { return llmin(a, b); }
+	F32 _max(const F32& a, const F32& b) const { return llmax(a, b); }
+	
+	bool checkNaN(const F32& a) const { return !llisnan(a); }
+	
+	//FIX* non ambigious function fix making SIN() work for calc -Cryogenic Blitz
+	F32 _sin(const F32& a) const { return sin(DEG_TO_RAD * a); }
+	F32 _cos(const F32& a) const { return cos(DEG_TO_RAD * a); }
+	F32 _tan(const F32& a) const { return tan(DEG_TO_RAD * a); }
+	F32 _asin(const F32& a) const { return asin(a * RAD_TO_DEG); }
+	F32 _acos(const F32& a) const { return acos(a * RAD_TO_DEG); }
+	F32 _atan(const F32& a) const { return atan(a * RAD_TO_DEG); }
+	F32 _sqrt(const F32& a) const { return sqrt(a); }
+	F32 _log(const F32& a) const { return log(a); }
+	F32 _exp(const F32& a) const { return exp(a); }
+	F32 _fabs(const F32& a) const { return fabs(a); }
+	F32 _floor(const F32& a) const { return llfloor(a); }
+	F32 _ceil(const F32& a) const { return llceil(a); }
+
+	F32 _atan2(const F32& a,const F32& b) const { return atan2(a,b); }
+
+
+
+	LLCalc::calc_map_t* mConstants;
+	LLCalc::calc_map_t* mVariables;
+//	LLCalc::calc_map_t* mUserVariables;
+	
+	F32&		mResult;
+};
+
+#endif // LL_CALCPARSER_H
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
old mode 100644
new mode 100755
diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py
index 9886d49ccce49936734c50d89d8ab9b326f359fd..22edd9dad84b6c63aeae44866705eca40c23a4aa 100644
--- a/indra/llmessage/tests/test_llsdmessage_peer.py
+++ b/indra/llmessage/tests/test_llsdmessage_peer.py
@@ -124,14 +124,19 @@ def log_error(self, format, *args):
             # Suppress error output as well
             pass
 
+class Server(HTTPServer):
+    # This pernicious flag is on by default in HTTPServer. But proper
+    # operation of freeport() absolutely depends on it being off.
+    allow_reuse_address = False
+
 if __name__ == "__main__":
-    # Instantiate an HTTPServer(TestHTTPRequestHandler) on the first free port
+    # Instantiate a Server(TestHTTPRequestHandler) on the first free port
     # in the specified port range. Doing this inline is better than in a
     # daemon thread: if it blows up here, we'll get a traceback. If it blew up
     # in some other thread, the traceback would get eaten and we'd run the
     # subject test program anyway.
     httpd, port = freeport(xrange(8000, 8020),
-                           lambda port: HTTPServer(('127.0.0.1', port), TestHTTPRequestHandler))
+                           lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler))
     # Pass the selected port number to the subject test program via the
     # environment. We don't want to impose requirements on the test program's
     # command-line parsing -- and anyway, for C++ integration tests, that's
diff --git a/indra/llmessage/tests/testrunner.py b/indra/llmessage/tests/testrunner.py
index f329ec2a0e660363ff2637ed585d34cd9ceb0927..f2c841532a95e9a021aeb7c1173af03ad898b0c1 100644
--- a/indra/llmessage/tests/testrunner.py
+++ b/indra/llmessage/tests/testrunner.py
@@ -27,6 +27,8 @@
 $/LicenseInfo$
 """
 
+from __future__ import with_statement
+
 import os
 import sys
 import re
@@ -79,9 +81,14 @@ def freeport(portlist, expr):
 
     Example:
 
+    class Server(HTTPServer):
+        # If you use BaseHTTPServer.HTTPServer, turning off this flag is
+        # essential for proper operation of freeport()!
+        allow_reuse_address = False
+    # ...
     server, port = freeport(xrange(8000, 8010),
-                            lambda port: HTTPServer(("localhost", port),
-                                                    MyRequestHandler))
+                            lambda port: Server(("localhost", port),
+                                                MyRequestHandler))
     # pass 'port' to client code
     # call server.serve_forever()
     """
@@ -164,3 +171,92 @@ def run(*args, **kwds):
     rc = os.spawnv(os.P_WAIT, args[0], args)
     debug("%s returned %s", args[0], rc)
     return rc
+
+# ****************************************************************************
+#   test code -- manual at this point, see SWAT-564
+# ****************************************************************************
+def test_freeport():
+    # ------------------------------- Helpers --------------------------------
+    from contextlib import contextmanager
+    # helper Context Manager for expecting an exception
+    # with exc(SomeError):
+    #     raise SomeError()
+    # raises AssertionError otherwise.
+    @contextmanager
+    def exc(exception_class, *args):
+        try:
+            yield
+        except exception_class, err:
+            for i, expected_arg in enumerate(args):
+                assert expected_arg == err.args[i], \
+                       "Raised %s, but args[%s] is %r instead of %r" % \
+                       (err.__class__.__name__, i, err.args[i], expected_arg)
+            print "Caught expected exception %s(%s)" % \
+                  (err.__class__.__name__, ', '.join(repr(arg) for arg in err.args))
+        else:
+            assert False, "Failed to raise " + exception_class.__class__.__name__
+
+    # helper to raise specified exception
+    def raiser(exception):
+        raise exception
+
+    # the usual
+    def assert_equals(a, b):
+        assert a == b, "%r != %r" % (a, b)
+
+    # ------------------------ Sanity check the above ------------------------
+    class SomeError(Exception): pass
+    # Without extra args, accept any err.args value
+    with exc(SomeError):
+        raiser(SomeError("abc"))
+    # With extra args, accept only the specified value
+    with exc(SomeError, "abc"):
+        raiser(SomeError("abc"))
+    with exc(AssertionError):
+        with exc(SomeError, "abc"):
+            raiser(SomeError("def"))
+    with exc(AssertionError):
+        with exc(socket.error, errno.EADDRINUSE):
+            raiser(socket.error(errno.ECONNREFUSED, 'Connection refused'))
+
+    # ----------- freeport() without engaging socket functionality -----------
+    # If portlist is empty, freeport() raises StopIteration.
+    with exc(StopIteration):
+        freeport([], None)
+
+    assert_equals(freeport([17], str), ("17", 17))
+
+    # This is the magic exception that should prompt us to retry
+    inuse = socket.error(errno.EADDRINUSE, 'Address already in use')
+    # Get the iterator to our ports list so we can check later if we've used all
+    ports = iter(xrange(5))
+    with exc(socket.error, errno.EADDRINUSE):
+        freeport(ports, lambda port: raiser(inuse))
+    # did we entirely exhaust 'ports'?
+    with exc(StopIteration):
+        ports.next()
+
+    ports = iter(xrange(2))
+    # Any exception but EADDRINUSE should quit immediately
+    with exc(SomeError):
+        freeport(ports, lambda port: raiser(SomeError()))
+    assert_equals(ports.next(), 1)
+
+    # ----------- freeport() with platform-dependent socket stuff ------------
+    # This is what we should've had unit tests to begin with (see CHOP-661).
+    def newbind(port):
+        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        sock.bind(('127.0.0.1', port))
+        return sock
+
+    bound0, port0 = freeport(xrange(7777, 7780), newbind)
+    assert_equals(port0, 7777)
+    bound1, port1 = freeport(xrange(7777, 7780), newbind)
+    assert_equals(port1, 7778)
+    bound2, port2 = freeport(xrange(7777, 7780), newbind)
+    assert_equals(port2, 7779)
+    with exc(socket.error, errno.EADDRINUSE):
+        bound3, port3 = freeport(xrange(7777, 7780), newbind)
+
+if __name__ == "__main__":
+    test_freeport()
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 123997e5e918273e1d8f24e94cd82f821b6c05d4..06fbc0f234850f5c1eb063407bd3d3c94cf4f7fa 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -37,6 +37,7 @@
 #include "llgl.h"
 #include "lltimer.h"
 
+#include "llcalc.h"
 //#include "llclipboard.h"
 #include "llcontrol.h"
 #include "llbutton.h"
@@ -133,6 +134,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
 	mIgnoreTab( p.ignore_tab ),
 	mDrawAsterixes( p.is_password ),
 	mSelectAllonFocusReceived( p.select_on_focus ),
+	mSelectAllonCommit( TRUE ),
 	mPassDelete(FALSE),
 	mReadOnly(FALSE),
 	mBgImage( p.background_image ),
@@ -230,7 +232,10 @@ void LLLineEditor::onCommit()
 
 	setControlValue(getValue());
 	LLUICtrl::onCommit();
-	selectAll();
+
+	// Selection on commit needs to be turned off when evaluating maths
+	// expressions, to allow indication of the error position
+	if (mSelectAllonCommit) selectAll();
 }
 
 // Returns TRUE if user changed value at all
@@ -2072,6 +2077,32 @@ BOOL LLLineEditor::postvalidateFloat(const std::string &str)
 	return success;
 }
 
+BOOL LLLineEditor::evaluateFloat()
+{
+	bool success;
+	F32 result = 0.f;
+	std::string expr = getText();
+	LLStringUtil::toUpper(expr);
+
+	success = LLCalc::getInstance()->evalString(expr, result);
+
+	if (!success)
+	{
+		// Move the cursor to near the error on failure
+		setCursor(LLCalc::getInstance()->getLastErrorPos());
+		// *TODO: Translated error message indicating the type of error? Select error text?
+	}
+	else
+	{
+		// Replace the expression with the result
+		std::string result_str = llformat("%f",result);
+		setText(result_str);
+		selectAll();
+	}
+
+	return success;
+}
+
 void LLLineEditor::onMouseCaptureLost()
 {
 	endSelection();
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index 4b7770859037fdb6b0dffda6ef02456fcabc1b8d..583bde360a3181d15d9a1813a119b1841c7b5d5e 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -221,6 +221,7 @@ class LLLineEditor
 	void			deleteSelection();
 
 	void			setSelectAllonFocusReceived(BOOL b);
+	void			setSelectAllonCommit(BOOL b) { mSelectAllonCommit = b; }
 	
 	typedef boost::function<void (LLLineEditor* caller, void* user_data)> callback_t;
 	void			setKeystrokeCallback(callback_t callback, void* user_data);
@@ -241,6 +242,7 @@ class LLLineEditor
 	static BOOL		postvalidateFloat(const std::string &str);
 
 	bool			prevalidateInput(const LLWString& wstr);
+	BOOL			evaluateFloat();
 
 	// line history support:
 	void			setEnableLineHistory( BOOL enabled ) { mHaveHistory = enabled; } // switches line history on or off 
@@ -340,6 +342,7 @@ class LLLineEditor
 	BOOL		mDrawAsterixes;
 
 	BOOL		mSelectAllonFocusReceived;
+	BOOL		mSelectAllonCommit;
 	BOOL		mPassDelete;
 
 	BOOL		mReadOnly;
diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp
index 15a7438ec956796567cfcbcfe427c6e8e009d70d..934879cdfd1dbd573b2ede3a8275c13be48b4001 100644
--- a/indra/llui/llspinctrl.cpp
+++ b/indra/llui/llspinctrl.cpp
@@ -44,7 +44,7 @@
 #include "llresmgr.h"
 #include "lluictrlfactory.h"
 
-const U32 MAX_STRING_LENGTH = 32;
+const U32 MAX_STRING_LENGTH = 255;
 
 static LLDefaultChildRegistry::Register<LLSpinCtrl> r2("spinner");
 
@@ -124,14 +124,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
 	params.max_length.bytes(MAX_STRING_LENGTH);
 	params.commit_callback.function((boost::bind(&LLSpinCtrl::onEditorCommit, this, _2)));
 	
-	if( mPrecision>0 )//should accept float numbers
-	{
-		params.prevalidate_callback(&LLTextValidate::validateFloat);
-	}
-	else //should accept int numbers
-	{
-		params.prevalidate_callback(&LLTextValidate::validateInt);
-	}
+	//*NOTE: allow entering of any chars for LLCalc, proper input will be evaluated on commit
 	
 	params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
 	mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
@@ -140,6 +133,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
 	// than when it doesn't.  Instead, if you always have to double click to select all the text, 
 	// it's easier to understand
 	//mEditor->setSelectAllonFocusReceived(TRUE);
+	mEditor->setSelectAllonCommit(FALSE);
 	addChild(mEditor);
 
 	updateEditor();
@@ -304,9 +298,10 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data )
 {
 	BOOL success = FALSE;
 	
-	std::string text = mEditor->getText();
-	if( LLLineEditor::postvalidateFloat( text ) )
+	if( mEditor->evaluateFloat() )
 	{
+		std::string text = mEditor->getText();
+
 		LLLocale locale(LLLocale::USER_LOCALE);
 		F32 val = (F32) atof(text.c_str());
 
@@ -327,7 +322,11 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data )
 	}
 	updateEditor();
 
-	if( !success )
+	if( success )
+	{
+		updateEditor();
+	}
+	else
 	{
 		reportInvalidData();		
 	}
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 1d9519d675c71c4ff62c53bb8b249ed6ce39e9a2..80ac385e3b6a9931b7d579d9f71b4bddac5137d6 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -56,6 +56,7 @@
 #include "llallocator.h"
 #include "llares.h" 
 #include "llcurl.h"
+#include "llcalc.h"
 #include "lltexturestats.h"
 #include "lltexturestats.h"
 #include "llviewerwindow.h"
@@ -1543,7 +1544,9 @@ bool LLAppViewer::cleanup()
 	// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be deleted.
 
 	LLWorldMap::getInstance()->reset(); // release any images
-	
+
+	LLCalc::cleanUp();
+
 	llinfos << "Global stuff deleted" << llendflush;
 
 	if (gAudiop)
diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp
index a05cbc64fbf4b3c10ca9a9023e60a37d59ce3dfc..966f5b941e0adfcbbd22b074eb420223dc1b23e8 100755
--- a/indra/newview/llassetuploadresponders.cpp
+++ b/indra/newview/llassetuploadresponders.cpp
@@ -458,7 +458,7 @@ void LLSendTexLayerResponder::uploadComplete(const LLSD& content)
 	std::string result = content["state"];
 	LLUUID new_id = content["new_asset"];
 
-	llinfos << "result: " << result << "new_id:" << new_id << llendl;
+	llinfos << "result: " << result << " new_id: " << new_id << llendl;
 	if (result == "complete"
 		&& mBakedUploadData != NULL)
 	{	// Invoke 
diff --git a/indra/newview/llassetuploadresponders.h b/indra/newview/llassetuploadresponders.h
index 70871b62e2f0fe4b53c455010dbd5a6854cb4ddc..381b919c4a4c15587d0227d22bedfb89a167a43b 100644
--- a/indra/newview/llassetuploadresponders.h
+++ b/indra/newview/llassetuploadresponders.h
@@ -112,6 +112,7 @@ class LLNewAgentInventoryVariablePriceResponder :
 struct LLBakedUploadData;
 class LLSendTexLayerResponder : public LLAssetUploadResponder
 {
+	LOG_CLASS(LLSendTexLayerResponder);
 public:
 	LLSendTexLayerResponder(const LLSD& post_data,
 							const LLUUID& vfile_id,
diff --git a/indra/newview/llfloaterdeleteenvpreset.cpp b/indra/newview/llfloaterdeleteenvpreset.cpp
index 4fefd2242a13d9d33d2e9d2652543f7fbfeb42aa..d08aa81cfeb953c007c42e75a2e14fda6dc81d56 100644
--- a/indra/newview/llfloaterdeleteenvpreset.cpp
+++ b/indra/newview/llfloaterdeleteenvpreset.cpp
@@ -258,7 +258,7 @@ void LLFloaterDeleteEnvPreset::populateDayCyclesList()
 void LLFloaterDeleteEnvPreset::postPopulate()
 {
 	// Handle empty list and empty selection.
-	bool has_selection = mPresetCombo->getItemCount() > 1 && mPresetCombo->getSelectedValue().isDefined();
+	bool has_selection = mPresetCombo->getItemCount() > 0 && mPresetCombo->getSelectedValue().isDefined();
 
 	if (!has_selection)
 	{
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index 07c0878877e5c8acb555c0d9023b7c4be15f0684..bedc7ef7048c5a285c9db56dc1a3134868bab484 100644
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -317,11 +317,8 @@ void LLFloaterRegionInfo::processEstateOwnerRequest(LLMessageSystem* msg,void**)
 // static
 void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg)
 {
-	LL_DEBUGS("Windlight") << "Processing region info" << LL_ENDL;
-
 	LLPanel* panel;
 	LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance<LLFloaterRegionInfo>("region_info");
-	llinfos << "LLFloaterRegionInfo::processRegionInfo" << llendl;
 	if(!floater)
 	{
 		return;
@@ -330,6 +327,7 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg)
 	// We need to re-request environment setting here,
 	// otherwise after we apply (send) updated region settings we won't get them back,
 	// so our environment won't be updated.
+	// This is also the way to know about externally changed region environment.
 	LLEnvManagerNew::instance().requestRegionSettings();
 	
 	LLTabContainer* tab = floater->getChild<LLTabContainer>("region_panels");
@@ -3249,29 +3247,30 @@ void LLPanelEnvironmentInfo::setDirty(bool dirty)
 	getChildView("cancel_btn")->setEnabled(dirty);
 }
 
-void LLPanelEnvironmentInfo::sendRegionSunUpdate(F32 sun_angle)
+void LLPanelEnvironmentInfo::sendRegionSunUpdate()
 {
 	LLRegionInfoModel& region_info = LLRegionInfoModel::instance();
-	bool region_use_fixed_sky = sun_angle >= 0.f;
 
-	// Set sun hour.
+	// If the region is being switched to fixed sky,
+	// change the region's sun hour according to the (fixed) sun position.
+	// This is needed for llGetSunDirection() LSL function to work properly (STORM-1330).
+	const LLSD& sky_map = mNewRegionSettings.getSkyMap();
+	bool region_use_fixed_sky = sky_map.size() == 1;
 	if (region_use_fixed_sky)
 	{
 		LLWLParamSet param_set;
-		LLSD params;
-		std::string unused;
-		if (!getSelectedSkyParams(params, unused))
-		{
-			return;
-		}
-		param_set.setAll(params);
+		llassert(sky_map.isMap());
+		param_set.setAll(sky_map.beginMap()->second);
+		F32 sun_angle = param_set.getSunAngle();
 
+		LL_DEBUGS("Windlight Sync") << "Old sun hour: " << region_info.mSunHour << LL_ENDL;
 		// convert value range from 0..2pi to 6..30
 		region_info.mSunHour = fmodf((sun_angle / F_TWO_PI) * 24.f, 24.f) + 6.f;
 	}
 
 	region_info.setUseFixedSun(region_use_fixed_sky);
 	region_info.mUseEstateSun = !region_use_fixed_sky;
+	LL_DEBUGS("Windlight Sync") << "Sun hour: " << region_info.mSunHour << LL_ENDL;
 
 	region_info.sendRegionTerrain(LLFloaterRegionInfo::getLastInvoice());
 }
@@ -3555,7 +3554,6 @@ void LLPanelEnvironmentInfo::onBtnApply()
 	LLSD day_cycle;
 	LLSD sky_map;
 	LLSD water_params;
-	F32 sun_angle = -1.f; // invalid value meaning no fixed sky
 
 	if (use_defaults)
 	{
@@ -3588,9 +3586,6 @@ void LLPanelEnvironmentInfo::onBtnApply()
 			param_set.setAll(params);
 			refs[LLWLParamKey(preset_name, LLEnvKey::SCOPE_LOCAL)] = param_set; // scope doesn't matter here
 			sky_map = LLWLParamManager::createSkyMap(refs);
-
-			// Remember the sun angle to set fixed region sun hour below.
-			sun_angle = param_set.getSunAngle();
 		}
 		else // use day cycle
 		{
@@ -3611,16 +3606,6 @@ void LLPanelEnvironmentInfo::onBtnApply()
 				LL_DEBUGS("Windlight") << "Fixing negative time" << LL_ENDL;
 				day_cycle[0][0] = 0.0f;
 			}
-
-			// If the day cycle contains exactly one preset (i.e it's effectively a fixed sky),
-			// remember the preset's sun angle to set fixed region sun hour below.
-			if (sky_map.size() == 1)
-			{
-				LLWLParamSet param_set;
-				llassert(sky_map.isMap());
-				param_set.setAll(sky_map.beginMap()->second);
-				sun_angle = param_set.getSunAngle();
-			}
 		}
 
 		// Get water params.
@@ -3640,8 +3625,9 @@ void LLPanelEnvironmentInfo::onBtnApply()
 		return;
 	}
 
-	// Set the region sun phase/flags according to the chosen new preferences.
-	sendRegionSunUpdate(sun_angle);
+	// When the settings get applied, we'll also send the region sun position update.
+	// To determine the sun angle we're going to need the new settings.
+	mNewRegionSettings = new_region_settings;
 
 	// Start spinning the progress indicator.
 	setApplyProgress(true);
@@ -3672,10 +3658,18 @@ void LLPanelEnvironmentInfo::onRegionSettingschange()
 
 void LLPanelEnvironmentInfo::onRegionSettingsApplied(bool ok)
 {
-	LL_DEBUGS("Windlight") << "Applying region settings finished, stopping indicator" << LL_ENDL;
 	// If applying new settings has failed, stop the indicator right away.
 	// Otherwise it will be stopped when we receive the updated settings from server.
-	if (!ok)
+	if (ok)
+	{
+		// Set the region sun phase/flags according to the chosen new preferences.
+		//
+		// If we do this earlier we may get jerky transition from fixed sky to a day cycle (STORM-1481).
+		// That is caused by the simulator re-sending the region info, which in turn makes us
+		// re-request and display old region environment settings while the new ones haven't been applied yet.
+		sendRegionSunUpdate();
+	}
+	else
 	{
 		setApplyProgress(false);
 
diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h
index 48099373242572745692f78e006427cd7083d427..e7917c382c8a87f45f1215f63c59a3c840924dc4 100644
--- a/indra/newview/llfloaterregioninfo.h
+++ b/indra/newview/llfloaterregioninfo.h
@@ -34,6 +34,8 @@
 #include "llhost.h"
 #include "llpanel.h"
 
+#include "llenvmanager.h" // for LLEnvironmentSettings
+
 class LLAvatarName;
 class LLDispatcher;
 class LLLineEditor;
@@ -431,7 +433,7 @@ class LLPanelEnvironmentInfo : public LLPanelRegionInfo
 	void setApplyProgress(bool started);
 	void setDirty(bool dirty);
 
-	void sendRegionSunUpdate(F32 sun_angle);
+	void sendRegionSunUpdate();
 
 	void populateWaterPresetsList();
 	void populateSkyPresetsList();
@@ -454,6 +456,9 @@ class LLPanelEnvironmentInfo : public LLPanelRegionInfo
 	void onRegionSettingschange();
 	void onRegionSettingsApplied(bool ok);
 
+	/// New environment settings that are being applied to the region.
+	LLEnvironmentSettings	mNewRegionSettings;
+
 	bool			mEnableEditing;
 
 	LLRadioGroup*	mRegionSettingsRadioGroup;
diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp
index 07c7f3598965cf24c01619675e81cf0841b536bb..a4f6921f9879ee4c4d73bc1347808c58d05b2540 100644
--- a/indra/newview/llpanelface.cpp
+++ b/indra/newview/llpanelface.cpp
@@ -30,6 +30,7 @@
 #include "llpanelface.h"
  
 // library includes
+#include "llcalc.h"
 #include "llerror.h"
 #include "llfocusmgr.h"
 #include "llrect.h"
@@ -926,6 +927,16 @@ void LLPanelFace::getState()
 				getChildView("button apply")->setEnabled(enabled);
 			}
 		}
+
+		// Set variable values for numeric expressions
+		LLCalc* calcp = LLCalc::getInstance();
+		calcp->setVar(LLCalc::TEX_U_SCALE, childGetValue("TexScaleU").asReal());
+		calcp->setVar(LLCalc::TEX_V_SCALE, childGetValue("TexScaleV").asReal());
+		calcp->setVar(LLCalc::TEX_U_OFFSET, childGetValue("TexOffsetU").asReal());
+		calcp->setVar(LLCalc::TEX_V_OFFSET, childGetValue("TexOffsetV").asReal());
+		calcp->setVar(LLCalc::TEX_ROTATION, childGetValue("TexRot").asReal());
+		calcp->setVar(LLCalc::TEX_TRANSPARENCY, childGetValue("ColorTrans").asReal());
+		calcp->setVar(LLCalc::TEX_GLOW, childGetValue("glow").asReal());
 	}
 	else
 	{
@@ -961,6 +972,16 @@ void LLPanelFace::getState()
 		//getChildView("has media")->setEnabled(FALSE);
 		//getChildView("media info set")->setEnabled(FALSE);
 		
+
+		// Set variable values for numeric expressions
+		LLCalc* calcp = LLCalc::getInstance();
+		calcp->clearVar(LLCalc::TEX_U_SCALE);
+		calcp->clearVar(LLCalc::TEX_V_SCALE);
+		calcp->clearVar(LLCalc::TEX_U_OFFSET);
+		calcp->clearVar(LLCalc::TEX_V_OFFSET);
+		calcp->clearVar(LLCalc::TEX_ROTATION);
+		calcp->clearVar(LLCalc::TEX_TRANSPARENCY);
+		calcp->clearVar(LLCalc::TEX_GLOW);		
 	}
 }
 
diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp
index 52917ff20b3dfbe5e30fca4c53c52a47cf8496be..c222bbb191245ff0bfb83522b0fdac126dbf014b 100644
--- a/indra/newview/llpanelobject.cpp
+++ b/indra/newview/llpanelobject.cpp
@@ -41,6 +41,7 @@
 // project includes
 #include "llagent.h"
 #include "llbutton.h"
+#include "llcalc.h"
 #include "llcheckboxctrl.h"
 #include "llcolorswatch.h"
 #include "llcombobox.h"
@@ -318,6 +319,8 @@ void LLPanelObject::getState( )
 		}
 	}
 
+	LLCalc* calcp = LLCalc::getInstance();
+
 	LLVOVolume *volobjp = NULL;
 	if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME))
 	{
@@ -334,6 +337,7 @@ void LLPanelObject::getState( )
 
 		// Disable all text input fields
 		clearCtrls();
+		calcp->clearAllVariables();
 		return;
 	}
 
@@ -360,12 +364,18 @@ void LLPanelObject::getState( )
 		mCtrlPosX->set( vec.mV[VX] );
 		mCtrlPosY->set( vec.mV[VY] );
 		mCtrlPosZ->set( vec.mV[VZ] );
+		calcp->setVar(LLCalc::X_POS, vec.mV[VX]);
+		calcp->setVar(LLCalc::Y_POS, vec.mV[VY]);
+		calcp->setVar(LLCalc::Z_POS, vec.mV[VZ]);
 	}
 	else
 	{
 		mCtrlPosX->clear();
 		mCtrlPosY->clear();
 		mCtrlPosZ->clear();
+		calcp->clearVar(LLCalc::X_POS);
+		calcp->clearVar(LLCalc::Y_POS);
+		calcp->clearVar(LLCalc::Z_POS);
 	}
 
 
@@ -380,12 +390,18 @@ void LLPanelObject::getState( )
 		mCtrlScaleX->set( vec.mV[VX] );
 		mCtrlScaleY->set( vec.mV[VY] );
 		mCtrlScaleZ->set( vec.mV[VZ] );
+		calcp->setVar(LLCalc::X_SCALE, vec.mV[VX]);
+		calcp->setVar(LLCalc::Y_SCALE, vec.mV[VY]);
+		calcp->setVar(LLCalc::Z_SCALE, vec.mV[VZ]);
 	}
 	else
 	{
 		mCtrlScaleX->clear();
 		mCtrlScaleY->clear();
 		mCtrlScaleZ->clear();
+		calcp->setVar(LLCalc::X_SCALE, 0.f);
+		calcp->setVar(LLCalc::Y_SCALE, 0.f);
+		calcp->setVar(LLCalc::Z_SCALE, 0.f);
 	}
 
 	mLabelSize->setEnabled( enable_scale );
@@ -405,12 +421,18 @@ void LLPanelObject::getState( )
 		mCtrlRotX->set( mCurEulerDegrees.mV[VX] );
 		mCtrlRotY->set( mCurEulerDegrees.mV[VY] );
 		mCtrlRotZ->set( mCurEulerDegrees.mV[VZ] );
+		calcp->setVar(LLCalc::X_ROT, mCurEulerDegrees.mV[VX]);
+		calcp->setVar(LLCalc::Y_ROT, mCurEulerDegrees.mV[VY]);
+		calcp->setVar(LLCalc::Z_ROT, mCurEulerDegrees.mV[VZ]);
 	}
 	else
 	{
 		mCtrlRotX->clear();
 		mCtrlRotY->clear();
 		mCtrlRotZ->clear();
+		calcp->clearVar(LLCalc::X_ROT);
+		calcp->clearVar(LLCalc::Y_ROT);
+		calcp->clearVar(LLCalc::Z_ROT);
 	}
 
 	mLabelRotation->setEnabled( enable_rotate );
@@ -625,9 +647,9 @@ void LLPanelObject::getState( )
 		F32 end_t	= volume_params.getEndT();
 
 		// Hollowness
-		F32 hollow = volume_params.getHollow();
-		mSpinHollow->set( 100.f * hollow );
-
+		F32 hollow = 100.f * volume_params.getHollow();
+		mSpinHollow->set( hollow );
+		calcp->setVar(LLCalc::HOLLOW, hollow);
 		// All hollow objects allow a shape to be selected.
 		if (hollow > 0.f)
 		{
@@ -679,6 +701,10 @@ void LLPanelObject::getState( )
 		mSpinCutEnd		->set( cut_end );
 		mCtrlPathBegin	->set( adv_cut_begin );
 		mCtrlPathEnd	->set( adv_cut_end );
+		calcp->setVar(LLCalc::CUT_BEGIN, cut_begin);
+		calcp->setVar(LLCalc::CUT_END, cut_end);
+		calcp->setVar(LLCalc::PATH_BEGIN, adv_cut_begin);
+		calcp->setVar(LLCalc::PATH_END, adv_cut_end);
 
 		// Twist
 		F32 twist		= volume_params.getTwist();
@@ -697,18 +723,24 @@ void LLPanelObject::getState( )
 
 		mSpinTwist		->set( twist );
 		mSpinTwistBegin	->set( twist_begin );
+		calcp->setVar(LLCalc::TWIST_END, twist);
+		calcp->setVar(LLCalc::TWIST_BEGIN, twist_begin);
 
 		// Shear
 		F32 shear_x = volume_params.getShearX();
 		F32 shear_y = volume_params.getShearY();
 		mSpinShearX->set( shear_x );
 		mSpinShearY->set( shear_y );
+		calcp->setVar(LLCalc::X_SHEAR, shear_x);
+		calcp->setVar(LLCalc::Y_SHEAR, shear_y);
 
 		// Taper
 		F32 taper_x	= volume_params.getTaperX();
 		F32 taper_y = volume_params.getTaperY();
 		mSpinTaperX->set( taper_x );
 		mSpinTaperY->set( taper_y );
+		calcp->setVar(LLCalc::X_TAPER, taper_x);
+		calcp->setVar(LLCalc::Y_TAPER, taper_y);
 
 		// Radius offset.
 		F32 radius_offset = volume_params.getRadiusOffset();
@@ -738,10 +770,12 @@ void LLPanelObject::getState( )
 			}
 		}
 		mSpinRadiusOffset->set( radius_offset);
+		calcp->setVar(LLCalc::RADIUS_OFFSET, radius_offset);
 
 		// Revolutions
 		F32 revolutions = volume_params.getRevolutions();
 		mSpinRevolutions->set( revolutions );
+		calcp->setVar(LLCalc::REVOLUTIONS, revolutions);
 		
 		// Skew
 		F32 skew	= volume_params.getSkew();
@@ -766,6 +800,7 @@ void LLPanelObject::getState( )
 			}
 		}
 		mSpinSkew->set( skew );
+		calcp->setVar(LLCalc::SKEW, skew);
 	}
 	
 	// Compute control visibility, label names, and twist range.
@@ -869,6 +904,8 @@ void LLPanelObject::getState( )
 	case MI_RING:
 		mSpinScaleX->set( scale_x );
 		mSpinScaleY->set( scale_y );
+		calcp->setVar(LLCalc::X_HOLE, scale_x);
+		calcp->setVar(LLCalc::Y_HOLE, scale_y);
 		mSpinScaleX->setMinValue(OBJECT_MIN_HOLE_SIZE);
 		mSpinScaleX->setMaxValue(OBJECT_MAX_HOLE_SIZE_X);
 		mSpinScaleY->setMinValue(OBJECT_MIN_HOLE_SIZE);
@@ -883,6 +920,14 @@ void LLPanelObject::getState( )
 			mSpinScaleX->setMaxValue(1.f);
 			mSpinScaleY->setMinValue(-1.f);
 			mSpinScaleY->setMaxValue(1.f);
+
+			// Torus' Hole Size is Box/Cyl/Prism's Taper
+			calcp->setVar(LLCalc::X_TAPER, 1.f - scale_x);
+			calcp->setVar(LLCalc::Y_TAPER, 1.f - scale_y);
+
+			// Box/Cyl/Prism have no hole size
+			calcp->setVar(LLCalc::X_HOLE, 0.f);
+			calcp->setVar(LLCalc::Y_HOLE, 0.f);
 		}
 		break;
 	}
diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp
index 0645fd8a54a23b1728a27b111931a45c2fcf07f4..f19b54c1d4d9350e7cd02aca0d5968a6fd328211 100644
--- a/indra/newview/llpanelwearing.cpp
+++ b/indra/newview/llpanelwearing.cpp
@@ -38,6 +38,8 @@
 #include "llsidetray.h"
 #include "llviewermenu.h"
 #include "llwearableitemslist.h"
+#include "llsdserialize.h"
+#include "llclipboard.h"
 
 // Context menu and Gear menu helper.
 static void edit_outfit()
@@ -58,6 +60,7 @@ class LLWearingGearMenu
 
 		registrar.add("Gear.Edit", boost::bind(&edit_outfit));
 		registrar.add("Gear.TakeOff", boost::bind(&LLWearingGearMenu::onTakeOff, this));
+		registrar.add("Gear.Copy", boost::bind(&LLPanelWearing::copyToClipboard, mPanelWearing));
 
 		enable_registrar.add("Gear.OnEnable", boost::bind(&LLPanelWearing::isActionEnabled, mPanelWearing, _2));
 
@@ -280,4 +283,25 @@ void LLPanelWearing::getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const
 	mCOFItemsList->getSelectedUUIDs(selected_uuids);
 }
 
+void LLPanelWearing::copyToClipboard()
+{
+	std::string text;
+	std::vector<LLSD> data;
+	mCOFItemsList->getValues(data);
+
+	for(std::vector<LLSD>::const_iterator iter = data.begin(); iter != data.end();)
+	{
+		LLSD uuid = (*iter);
+		LLViewerInventoryItem* item = gInventory.getItem(uuid);
+
+		iter++;
+		if (item != NULL)
+		{
+			// Append a newline to all but the last line
+			text += iter != data.end() ? item->getName() + "\n" : item->getName();
+		}
+	}
+
+	gClipboard.copyFromString(utf8str_to_wstring(text));
+}
 // EOF
diff --git a/indra/newview/llpanelwearing.h b/indra/newview/llpanelwearing.h
index 157b2c4c5f0ed35c5d063377897cffa1cdfc7ae3..9a212b3cca92c2feafd0e644c1ba1f44c5794e7c 100644
--- a/indra/newview/llpanelwearing.h
+++ b/indra/newview/llpanelwearing.h
@@ -60,6 +60,8 @@ class LLPanelWearing : public LLPanelAppearanceTab
 
 	/*virtual*/ void getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const;
 
+	/*virtual*/ void copyToClipboard();
+
 	boost::signals2::connection setSelectionChangeCallback(commit_callback_t cb);
 
 	bool hasItemSelected();
diff --git a/indra/newview/llregioninfomodel.cpp b/indra/newview/llregioninfomodel.cpp
index 6238f183c163fef9552d415a91bf565d936db625..698c4f9bb93833a084e40903dba5933a715e42a8 100644
--- a/indra/newview/llregioninfomodel.cpp
+++ b/indra/newview/llregioninfomodel.cpp
@@ -157,6 +157,7 @@ void LLRegionInfoModel::update(LLMessageSystem* msg)
 
 	// actually the "last set" sun hour, not the current sun hour. JC
 	msg->getF32(_PREHASH_RegionInfo, _PREHASH_SunHour, mSunHour);
+	LL_DEBUGS("Windlight Sync") << "Got region sun hour: " << mSunHour << LL_ENDL;
 
 	// the only reasonable way to decide if we actually have any data is to
 	// check to see if any of these fields have nonzero sizes
diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp
index 500c2a7b863dcd74d7e29a3b22b48e5c4aa3225d..bd41aa64f0a6df82afab3b367e54d6218a2b7398 100644
--- a/indra/newview/lltexlayer.cpp
+++ b/indra/newview/lltexlayer.cpp
@@ -51,6 +51,9 @@
 
 using namespace LLVOAvatarDefines;
 
+static const S32 BAKE_UPLOAD_ATTEMPTS = 7;
+static const F32 BAKE_UPLOAD_RETRY_DELAY = 2.f; // actual delay grows by power of 2 each attempt
+
 class LLTexLayerInfo
 {
 	friend class LLTexLayer;
@@ -93,11 +96,13 @@ class LLTexLayerInfo
 //-----------------------------------------------------------------------------
 LLBakedUploadData::LLBakedUploadData(const LLVOAvatarSelf* avatar,
 									 LLTexLayerSet* layerset,
-									 const LLUUID& id) : 
+									 const LLUUID& id,
+									 bool highest_res) :
 	mAvatar(avatar),
 	mTexLayerSet(layerset),
 	mID(id),
-	mStartTime(LLFrameTimer::getTotalTime())		// Record starting time
+	mStartTime(LLFrameTimer::getTotalTime()),		// Record starting time
+	mIsHighestRes(highest_res)
 { 
 }
 
@@ -116,6 +121,7 @@ LLTexLayerSetBuffer::LLTexLayerSetBuffer(LLTexLayerSet* const owner,
 	mUploadPending(FALSE), // Not used for any logic here, just to sync sending of updates
 	mNeedsUpload(FALSE),
 	mNumLowresUploads(0),
+	mUploadFailCount(0),
 	mNeedsUpdate(TRUE),
 	mNumLowresUpdates(0),
 	mTexLayerSet(owner)
@@ -204,6 +210,7 @@ void LLTexLayerSetBuffer::cancelUpload()
 	mNeedsUpload = FALSE;
 	mUploadPending = FALSE;
 	mNeedsUploadTimer.pause();
+	mUploadRetryTimer.reset();
 }
 
 void LLTexLayerSetBuffer::pushProjection() const
@@ -356,25 +363,38 @@ BOOL LLTexLayerSetBuffer::isReadyToUpload() const
 	if (!gAgentQueryManager.hasNoPendingQueries()) return FALSE; // Can't upload if there are pending queries.
 	if (isAgentAvatarValid() && !gAgentAvatarp->isUsingBakedTextures()) return FALSE; // Don't upload if avatar is using composites.
 
-	// If we requested an upload and have the final LOD ready, then upload.
-	if (mTexLayerSet->isLocalTextureDataFinal()) return TRUE;
-
-	// Upload if we've hit a timeout.  Upload is a pretty expensive process so we need to make sure
-	// we aren't doing uploads too frequently.
-	const U32 texture_timeout = gSavedSettings.getU32("AvatarBakedTextureUploadTimeout");
-	if (texture_timeout != 0)
+	BOOL ready = FALSE;
+	if (mTexLayerSet->isLocalTextureDataFinal())
+	{
+		// If we requested an upload and have the final LOD ready, upload (or wait a while if this is a retry)
+		if (mUploadFailCount == 0)
+		{
+			ready = TRUE;
+		}
+		else
+		{
+			ready = mUploadRetryTimer.getElapsedTimeF32() >= BAKE_UPLOAD_RETRY_DELAY * (1 << (mUploadFailCount - 1));
+		}
+	}
+	else
 	{
-		// The timeout period increases exponentially between every lowres upload in order to prevent
-		// spamming the server with frequent uploads.
-		const U32 texture_timeout_threshold = texture_timeout*(1 << mNumLowresUploads);
+		// Upload if we've hit a timeout.  Upload is a pretty expensive process so we need to make sure
+		// we aren't doing uploads too frequently.
+		const U32 texture_timeout = gSavedSettings.getU32("AvatarBakedTextureUploadTimeout");
+		if (texture_timeout != 0)
+		{
+			// The timeout period increases exponentially between every lowres upload in order to prevent
+			// spamming the server with frequent uploads.
+			const U32 texture_timeout_threshold = texture_timeout*(1 << mNumLowresUploads);
 
-		// If we hit our timeout and have textures available at even lower resolution, then upload.
-		const BOOL is_upload_textures_timeout = mNeedsUploadTimer.getElapsedTimeF32() >= texture_timeout_threshold;
-		const BOOL has_lower_lod = mTexLayerSet->isLocalTextureDataAvailable();
-		if (has_lower_lod && is_upload_textures_timeout) return TRUE; 
+			// If we hit our timeout and have textures available at even lower resolution, then upload.
+			const BOOL is_upload_textures_timeout = mNeedsUploadTimer.getElapsedTimeF32() >= texture_timeout_threshold;
+			const BOOL has_lower_lod = mTexLayerSet->isLocalTextureDataAvailable();
+			ready = has_lower_lod && is_upload_textures_timeout;
+		}
 	}
 
-	return FALSE;
+	return ready;
 }
 
 BOOL LLTexLayerSetBuffer::isReadyToUpdate() const
@@ -482,17 +502,20 @@ void LLTexLayerSetBuffer::doUpload()
 			
 			if (valid)
 			{
+				const bool highest_lod = mTexLayerSet->isLocalTextureDataFinal();
 				// Baked_upload_data is owned by the responder and deleted after the request completes.
 				LLBakedUploadData* baked_upload_data = new LLBakedUploadData(gAgentAvatarp, 
 																			 this->mTexLayerSet, 
-																			 asset_id);
+																			 asset_id,
+																			 highest_lod);
 				// upload ID is used to avoid overlaps, e.g. when the user rapidly makes two changes outside of Face Edit.
 				mUploadID = asset_id;
 
 				// Upload the image
 				const std::string url = gAgent.getRegion()->getCapability("UploadBakedTexture");
 				if(!url.empty()
-					&& !LLPipeline::sForceOldBakedUpload) // toggle debug setting UploadBakedTexOld to change between the new caps method and old method
+					&& !LLPipeline::sForceOldBakedUpload // toggle debug setting UploadBakedTexOld to change between the new caps method and old method
+					&& (mUploadFailCount < (BAKE_UPLOAD_ATTEMPTS - 1))) // Try last ditch attempt via asset store if cap upload is failing.
 				{
 					LLSD body = LLSD::emptyMap();
 					// The responder will call LLTexLayerSetBuffer::onTextureUploadComplete()
@@ -511,7 +534,6 @@ void LLTexLayerSetBuffer::doUpload()
 					llinfos << "Baked texture upload via Asset Store." <<  llendl;
 				}
 
-				const BOOL highest_lod = mTexLayerSet->isLocalTextureDataFinal();	
 				if (highest_lod)
 				{
 					// Sending the final LOD for the baked texture.  All done, pause 
@@ -603,14 +625,15 @@ void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid,
 {
 	LLBakedUploadData* baked_upload_data = (LLBakedUploadData*)userdata;
 
-	if ((result == 0) &&
-		isAgentAvatarValid() &&
+	if (isAgentAvatarValid() &&
 		!gAgentAvatarp->isDead() &&
 		(baked_upload_data->mAvatar == gAgentAvatarp) && // Sanity check: only the user's avatar should be uploading textures.
 		(baked_upload_data->mTexLayerSet->hasComposite()))
 	{
 		LLTexLayerSetBuffer* layerset_buffer = baked_upload_data->mTexLayerSet->getComposite();
-			
+		S32 failures = layerset_buffer->mUploadFailCount;
+		layerset_buffer->mUploadFailCount = 0;
+
 		if (layerset_buffer->mUploadID.isNull())
 		{
 			// The upload got canceled, we should be in the
@@ -626,20 +649,28 @@ void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid,
 		{
 			// This is the upload we're currently waiting for.
 			layerset_buffer->mUploadID.setNull();
+			const std::string name(baked_upload_data->mTexLayerSet->getBodyRegionName());
+			const std::string resolution = baked_upload_data->mIsHighestRes ? " full res " : " low res ";
 			if (result >= 0)
 			{
-				layerset_buffer->mUploadPending = FALSE;
+				layerset_buffer->mUploadPending = FALSE; // Allows sending of AgentSetAppearance later
 				LLVOAvatarDefines::ETextureIndex baked_te = gAgentAvatarp->getBakedTE(layerset_buffer->mTexLayerSet);
 				// Update baked texture info with the new UUID
 				U64 now = LLFrameTimer::getTotalTime();		// Record starting time
-				llinfos << "Baked texture upload took " << (S32)((now - baked_upload_data->mStartTime) / 1000) << " ms" << llendl;
+				llinfos << "Baked" << resolution << "texture upload for " << name << " took " << (S32)((now - baked_upload_data->mStartTime) / 1000) << " ms" << llendl;
 				gAgentAvatarp->setNewBakedTexture(baked_te, uuid);
 			}
 			else
 			{	
-				// Avatar appearance is changing, ignore the upload results
-				llinfos << "Baked upload failed. Reason: " << result << llendl;
-				// *FIX: retry upload after n seconds, asset server could be busy
+				++failures;
+				S32 max_attempts = baked_upload_data->mIsHighestRes ? BAKE_UPLOAD_ATTEMPTS : 1; // only retry final bakes
+				llwarns << "Baked" << resolution << "texture upload for " << name << " failed (attempt " << failures << "/" << max_attempts << ")" << llendl;
+				if (failures < max_attempts)
+				{
+					layerset_buffer->mUploadFailCount = failures;
+					layerset_buffer->mUploadRetryTimer.start();
+					layerset_buffer->requestUpload();
+				}
 			}
 		}
 		else
diff --git a/indra/newview/lltexlayer.h b/indra/newview/lltexlayer.h
index 2d710d2dce862294abb6f877c4593e7597fb75e1..85dadb213c55d777bdb2e329d677a43830d34f8d 100644
--- a/indra/newview/lltexlayer.h
+++ b/indra/newview/lltexlayer.h
@@ -312,6 +312,8 @@ class LLTexLayerSetBuffer : public LLViewerDynamicTexture
 	BOOL					mUploadPending; 				// Whether we have received back the new baked textures
 	LLUUID					mUploadID; 						// The current upload process (null if none).
 	LLFrameTimer    		mNeedsUploadTimer; 				// Tracks time since upload was requested and performed.
+	S32						mUploadFailCount;				// Number of consecutive upload failures
+	LLFrameTimer    		mUploadRetryTimer; 				// Tracks time since last upload failure.
 
 	//--------------------------------------------------------------------
 	// Updates
@@ -363,12 +365,14 @@ struct LLBakedUploadData
 {
 	LLBakedUploadData(const LLVOAvatarSelf* avatar, 
 					  LLTexLayerSet* layerset, 
-					  const LLUUID& id);
+					  const LLUUID& id,
+					  bool highest_res);
 	~LLBakedUploadData() {}
 	const LLUUID				mID;
 	const LLVOAvatarSelf*		mAvatar; // note: backlink only; don't LLPointer 
 	LLTexLayerSet*				mTexLayerSet;
    	const U64					mStartTime;	// for measuring baked texture upload time
+   	const bool					mIsHighestRes; // whether this is a "final" bake, or intermediate low res
 };
 
 #endif  // LL_LLTEXLAYER_H
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 44b3a85f253ea1752043230d6c7a04b705aa3401..e934c38c2276a2105782139ce12324dc58cc85e1 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -4346,8 +4346,7 @@ void process_time_synch(LLMessageSystem *mesgsys, void **user_data)
 
 	LLWorld::getInstance()->setSpaceTimeUSec(space_time_usec);
 
-	LL_DEBUGS("Windlight Sync") << "time_synch() - " << sun_direction << ", " << sun_ang_velocity
-		<< ", " << phase << LL_ENDL;
+	LL_DEBUGS("Windlight Sync") << "Sun phase: " << phase << " rad = " << fmodf(phase / F_TWO_PI + 0.25, 1.f) * 24.f << " h" << LL_ENDL;
 
 	gSky.setSunPhase(phase);
 	gSky.setSunTargetDirection(sun_direction, sun_ang_velocity);
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 8bb38e3e46864c297152ab3bc2a2e33dbbfb7e25..1d0796093704647f873c7ca335274ab990955a37 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -645,6 +645,7 @@ void LLViewerRegion::processRegionInfo(LLMessageSystem* msg, void**)
 {
 	// send it to 'observers'
 	// *TODO: switch the floaters to using LLRegionInfoModel
+	llinfos << "Processing region info" << llendl;
 	LLRegionInfoModel::instance().update(msg);
 	LLFloaterGodTools::processRegionInfo(msg);
 	LLFloaterRegionInfo::processRegionInfo(msg);
diff --git a/indra/newview/skins/default/xui/da/strings.xml b/indra/newview/skins/default/xui/da/strings.xml
index 68b861fe924a640126a205a9bdfc9d7287c53238..b5d8ac44bc463314d331d1484e74e776679dc720 100644
--- a/indra/newview/skins/default/xui/da/strings.xml
+++ b/indra/newview/skins/default/xui/da/strings.xml
@@ -1209,9 +1209,6 @@ Prøv venligst om lidt igen.
 	<string name="InvFolder My Inventory">
 		Min beholdning
 	</string>
-	<string name="InvFolder My Favorites">
-		Mine favoritter
-	</string>
 	<string name="InvFolder Library">
 		Bibliotek
 	</string>
@@ -1270,10 +1267,10 @@ Prøv venligst om lidt igen.
 		Bevægelser
 	</string>
 	<string name="InvFolder Favorite">
-		Favoritter
+		Mine favoritter
 	</string>
 	<string name="InvFolder favorite">
-		Favoritter
+		Mine favoritter
 	</string>
 	<string name="InvFolder Current Outfit">
 		Nuværende sæt
diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml
index d77b4a1e444f78441d85647078314499ebdce5ee..ed382674661f9cc2fd411a2ea05f75193c93f8c8 100644
--- a/indra/newview/skins/default/xui/de/strings.xml
+++ b/indra/newview/skins/default/xui/de/strings.xml
@@ -1238,9 +1238,6 @@ Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden.
 	<string name="InvFolder My Inventory">
 		Mein Inventar
 	</string>
-	<string name="InvFolder My Favorites">
-		Meine Favoriten
-	</string>
 	<string name="InvFolder Library">
 		Bibliothek
 	</string>
@@ -1299,10 +1296,10 @@ Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden.
 		Gesten
 	</string>
 	<string name="InvFolder Favorite">
-		Favoriten
+		Meine Favoriten
 	</string>
 	<string name="InvFolder favorite">
-		Favoriten
+		Meine Favoriten
 	</string>
 	<string name="InvFolder Current Outfit">
 		Aktuelles Outfit
diff --git a/indra/newview/skins/default/xui/en/menu_wearing_gear.xml b/indra/newview/skins/default/xui/en/menu_wearing_gear.xml
index 0ac2c14253500db711a98e9ccf4186ebe2285a3e..0e858ccf107f34dda72b12467fe1b86587d7cd84 100644
--- a/indra/newview/skins/default/xui/en/menu_wearing_gear.xml
+++ b/indra/newview/skins/default/xui/en/menu_wearing_gear.xml
@@ -20,4 +20,11 @@
          function="Gear.OnEnable"
          parameter="take_off" />
     </menu_item_call>
+    <menu_item_call
+     label="Copy outfit list to clipboard"
+     layout="topleft"
+     name="copy">
+        <on_click
+         function="Gear.Copy" />
+    </menu_item_call>
 </toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 2e2ef6ee193b79359f34c0ea164fc6f42d95e278..022c97f34192c86140dce27a07543a5a3444ec73 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -2069,7 +2069,6 @@ Returns a string with the requested data about the region
 
 	<!-- inventory folder -->
 	<string name="InvFolder My Inventory">My Inventory</string>
-	<string name="InvFolder My Favorites">My Favorites</string>
 	<string name="InvFolder Library">Library</string>
 	<string name="InvFolder Textures">Textures</string>
 	<string name="InvFolder Sounds">Sounds</string>
@@ -2089,10 +2088,10 @@ Returns a string with the requested data about the region
 	<string name="InvFolder Uncompressed Sounds">Uncompressed Sounds</string>
 	<string name="InvFolder Animations">Animations</string>
 	<string name="InvFolder Gestures">Gestures</string>
-	<string name="InvFolder Favorite">Favorites</string>
+	<string name="InvFolder Favorite">My Favorites</string>
   <!-- historically default name of the Favorites folder can start from either "f" or "F" letter.
   We should localize both of them with the same value -->
-	<string name="InvFolder favorite">Favorites</string>
+	<string name="InvFolder favorite">My Favorites</string>
 	<string name="InvFolder Current Outfit">Current Outfit</string>
 	<string name="InvFolder Initial Outfits">Initial Outfits</string>
 	<string name="InvFolder My Outfits">My Outfits</string>
diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml
index b759eed738e47fa215ff9fc8c01289a9c4bc9728..72d7493a0282efd296e41d8d1c70ea023e45056b 100644
--- a/indra/newview/skins/default/xui/es/strings.xml
+++ b/indra/newview/skins/default/xui/es/strings.xml
@@ -1211,9 +1211,6 @@ Intenta iniciar sesión de nuevo en unos instantes.
 	<string name="InvFolder My Inventory">
 		Mi Inventario
 	</string>
-	<string name="InvFolder My Favorites">
-		Mis Favoritos
-	</string>
 	<string name="InvFolder Library">
 		Biblioteca
 	</string>
@@ -1272,10 +1269,10 @@ Intenta iniciar sesión de nuevo en unos instantes.
 		Gestos
 	</string>
 	<string name="InvFolder Favorite">
-		Favoritos
+		Mis Favoritos
 	</string>
 	<string name="InvFolder favorite">
-		Favoritos
+		Mis Favoritos
 	</string>
 	<string name="InvFolder Current Outfit">
 		Vestuario actual
diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml
index 3ec85551da755cbecb32024873d2dd866c247e4f..077e545851ff43f152fb08fbaedc11d226001555 100644
--- a/indra/newview/skins/default/xui/fr/strings.xml
+++ b/indra/newview/skins/default/xui/fr/strings.xml
@@ -1238,9 +1238,6 @@ Veuillez réessayer de vous connecter dans une minute.
 	<string name="InvFolder My Inventory">
 		Mon inventaire
 	</string>
-	<string name="InvFolder My Favorites">
-		Mes Favoris
-	</string>
 	<string name="InvFolder Library">
 		Bibliothèque
 	</string>
@@ -1299,10 +1296,10 @@ Veuillez réessayer de vous connecter dans une minute.
 		Gestes
 	</string>
 	<string name="InvFolder Favorite">
-		Favoris
+		Mes Favoris
 	</string>
 	<string name="InvFolder favorite">
-		Favoris
+		Mes Favoris
 	</string>
 	<string name="InvFolder Current Outfit">
 		Tenue actuelle
diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml
index cbe8ef24c44e6c112e511a9135f898e6dd9f1c5e..6af515d82d0b10fe5184ea27ad30a4c5a2a4e721 100644
--- a/indra/newview/skins/default/xui/it/strings.xml
+++ b/indra/newview/skins/default/xui/it/strings.xml
@@ -1217,9 +1217,6 @@ Prova ad accedere nuovamente tra un minuto.
 	<string name="InvFolder My Inventory">
 		Il mio inventario
 	</string>
-	<string name="InvFolder My Favorites">
-		I miei preferiti
-	</string>
 	<string name="InvFolder Library">
 		Libreria
 	</string>
@@ -1278,10 +1275,10 @@ Prova ad accedere nuovamente tra un minuto.
 		Gesture
 	</string>
 	<string name="InvFolder Favorite">
-		Preferiti
+		I miei preferiti
 	</string>
 	<string name="InvFolder favorite">
-		Preferiti
+		I miei preferiti
 	</string>
 	<string name="InvFolder Current Outfit">
 		Abbigliamento attuale
diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml
index ff22221aabaa8903c0fc15de485b4db7a56a36d0..fa6d25d238500d97ff37ec3629860fded98dad1d 100644
--- a/indra/newview/skins/default/xui/ja/strings.xml
+++ b/indra/newview/skins/default/xui/ja/strings.xml
@@ -1238,9 +1238,6 @@ support@secondlife.com にお問い合わせください。
 	<string name="InvFolder My Inventory">
 		持ち物
 	</string>
-	<string name="InvFolder My Favorites">
-		お気に入り
-	</string>
 	<string name="InvFolder Library">
 		ライブラリ
 	</string>
diff --git a/indra/newview/skins/default/xui/nl/strings.xml b/indra/newview/skins/default/xui/nl/strings.xml
index a53c0769dc873cb55ced11143741b7a76a8be91c..e9db237e8229eff787c9e74185ea585c6a30da51 100644
--- a/indra/newview/skins/default/xui/nl/strings.xml
+++ b/indra/newview/skins/default/xui/nl/strings.xml
@@ -849,9 +849,6 @@
 	<string name="InvFolder My Inventory">
 		Mijn Inventaris
 	</string>
-	<string name="InvFolder My Favorites">
-		Mijn Favorieten
-	</string>
 	<string name="InvFolder Library">
 		Bibliotheek
 	</string>
@@ -910,10 +907,10 @@
 		Gebaren
 	</string>
 	<string name="InvFolder Favorite">
-		Favoriten
+		Mijn Favorieten
 	</string>
 	<string name="InvFolder favorite">
-		Favoriten
+		Mijn Favorieten
 	</string>
 	<string name="InvFolder Current Outfit">
 		Huidige Uitrusting
diff --git a/indra/newview/skins/default/xui/pl/strings.xml b/indra/newview/skins/default/xui/pl/strings.xml
index 6eceed46d3acf981d678bb5b04bb507914b6a061..e93da48dc0f394f7ce3924208deb5e42e9bb54f6 100644
--- a/indra/newview/skins/default/xui/pl/strings.xml
+++ b/indra/newview/skins/default/xui/pl/strings.xml
@@ -1072,9 +1072,6 @@
 	<string name="InvFolder My Inventory">
 		Moja Szafa
 	</string>
-	<string name="InvFolder My Favorites">
-		Moje ulubione
-	</string>
 	<string name="InvFolder Library">
 		Biblioteka
 	</string>
@@ -1133,10 +1130,10 @@
 		Gesturki
 	</string>
 	<string name="InvFolder Favorite">
-		Ulubione
+		Moje ulubione
 	</string>
 	<string name="InvFolder favorite">
-		Ulubione
+		Moje ulubione
 	</string>
 	<string name="InvFolder Current Outfit">
 		Obecny strój
diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml
index c5268966c1f1eefd6092bca27abd825c67051959..ed93217d597d4c2dbdb6638260fcbc1098d23d44 100644
--- a/indra/newview/skins/default/xui/pt/strings.xml
+++ b/indra/newview/skins/default/xui/pt/strings.xml
@@ -1172,9 +1172,6 @@ Titulares de contas gratuitas não poderão acessar o Second Life para acomodar
 	<string name="InvFolder My Inventory">
 		Meu inventário
 	</string>
-	<string name="InvFolder My Favorites">
-		Meus favoritos
-	</string>
 	<string name="InvFolder Library">
 		Biblioteca
 	</string>
@@ -1233,10 +1230,10 @@ Titulares de contas gratuitas não poderão acessar o Second Life para acomodar
 		Gestos
 	</string>
 	<string name="InvFolder Favorite">
-		Favoritos
+		Meus favoritos
 	</string>
 	<string name="InvFolder favorite">
-		Favoritos
+		Meus favoritos
 	</string>
 	<string name="InvFolder Current Outfit">
 		Look atual
diff --git a/indra/newview/skins/default/xui/zh/strings.xml b/indra/newview/skins/default/xui/zh/strings.xml
index 986ab825238df0d2307bb6a692ef564c8d650d53..28b8cce5b228dec7dbb7a8e384a4f00e20351056 100644
--- a/indra/newview/skins/default/xui/zh/strings.xml
+++ b/indra/newview/skins/default/xui/zh/strings.xml
@@ -1099,9 +1099,6 @@
 	<string name="InvFolder My Inventory">
 		我的收納區
 	</string>
-	<string name="InvFolder My Favorites">
-		My Favorites
-	</string>
 	<string name="InvFolder Library">
 		Library
 	</string>
@@ -1160,10 +1157,10 @@
 		å§¿å‹¢
 	</string>
 	<string name="InvFolder Favorite">
-		Favorites
+		My Favorites
 	</string>
 	<string name="InvFolder favorite">
-		Favorites
+		My Favorites
 	</string>
 	<string name="InvFolder Current Outfit">
 		目前裝扮