Click here to Skip to main content
Click here to Skip to main content
Articles » Web Development » Node.js » General » Downloads
 
Add your own
alternative version
Go to top

Node.Js And Stuff

, 11 Feb 2013
Small demo app using Node.Js/Socket.IO/MongoDB/D3.Js and jQuery.
WebSocketDemo.zip
WebSocketDemo
node_modules
.bin
express
jade
static
stylus
express
.npmignore
.travis.yml
bin
express
History.md
lib
router
LICENSE
Makefile
node_modules
commander
.npmignore
.travis.yml
History.md
lib
Makefile
package.json
Readme.md
connect
.npmignore
lib
middleware
session
public
favicon.ico
icons
page.png
page_add.png
page_attach.png
page_code.png
page_copy.png
page_delete.png
page_edit.png
page_error.png
page_excel.png
page_find.png
page_gear.png
page_go.png
page_green.png
page_key.png
page_lightning.png
page_link.png
page_paintbrush.png
page_paste.png
page_red.png
page_refresh.png
page_save.png
page_white.png
page_white_acrobat.png
page_white_actionscript.png
page_white_add.png
page_white_c.png
page_white_camera.png
page_white_cd.png
page_white_code.png
page_white_code_red.png
page_white_coldfusion.png
page_white_compressed.png
page_white_copy.png
page_white_cplusplus.png
page_white_csharp.png
page_white_cup.png
page_white_database.png
page_white_delete.png
page_white_dvd.png
page_white_edit.png
page_white_error.png
page_white_excel.png
page_white_find.png
page_white_flash.png
page_white_freehand.png
page_white_gear.png
page_white_get.png
page_white_go.png
page_white_h.png
page_white_horizontal.png
page_white_key.png
page_white_lightning.png
page_white_link.png
page_white_magnify.png
page_white_medal.png
page_white_office.png
page_white_paint.png
page_white_paintbrush.png
page_white_paste.png
page_white_php.png
page_white_picture.png
page_white_powerpoint.png
page_white_put.png
page_white_ruby.png
page_white_stack.png
page_white_star.png
page_white_swoosh.png
page_white_text.png
page_white_text_width.png
page_white_tux.png
page_white_vector.png
page_white_visualstudio.png
page_white_width.png
page_white_word.png
page_white_world.png
page_white_wrench.png
page_white_zip.png
page_word.png
page_world.png
LICENSE
node_modules
bytes
.npmignore
component.json
History.md
Makefile
package.json
Readme.md
formidable
.npmignore
.travis.yml
benchmark
example
lib
Makefile
node-gently
example
lib
gently
Makefile
package.json
Readme.md
test
simple
package.json
Readme.md
test
fixture
file
http
special-chars-in-filename
info.md
js
integration
legacy
integration
simple
system
unit
TODO
tool
pause
.npmignore
History.md
Makefile
package.json
Readme.md
qs
.gitmodules
.npmignore
.travis.yml
History.md
lib
Makefile
package.json
Readme.md
test
mocha.opts
package.json
Readme.md
cookie
.npmignore
.travis.yml
package.json
README.md
test
mocha.opts
crc
.gitmodules
.npmignore
lib
Makefile
package.json
README.md
test
debug
.npmignore
example
History.md
lib
Makefile
package.json
Readme.md
fresh
.npmignore
Makefile
package.json
Readme.md
methods
package.json
mkdirp
.gitignore.orig
.gitignore.rej
.npmignore
.travis.yml
examples
pow.js.orig
pow.js.rej
LICENSE
package.json
README.markdown
test
range-parser
.npmignore
History.md
Makefile
package.json
Readme.md
send
.npmignore
History.md
lib
Makefile
node_modules
mime
LICENSE
package.json
README.md
types
mime.types
node.types
package.json
Readme.md
package.json
Readme.md
jade
.npmignore
bin
jade
jade.md
lib
nodes
LICENSE
node_modules
commander
.npmignore
.travis.yml
History.md
lib
Makefile
package.json
Readme.md
mkdirp
.npmignore
.travis.yml
examples
LICENSE
package.json
README.markdown
test
package.json
Readme.md
test.jade
testing
head.jade
index.jade
layout.jade
user.jade
mongodb
.travis.yml
CONTRIBUTING.md
lib
mongodb
commands
connection
strategies
gridfs
responses
Makefile
node_modules
bson
.travis.yml
benchmarks
binding.gyp
build
binding.Makefile
bson.target.mk
config.gypi
gyp-mac-tool
Makefile
Release
.deps
Release
bson.node.d
obj.target
bson
ext
bson.o.d
bson.node
linker.lock
obj.target
bson
ext
bson.o
ext
bson.cc
Makefile
win32
ia32
bson.node
x64
bson.node
wscript
lib
bson
Makefile
package.json
README.md
test
browser
node
data
test_gs_weird_bug.png
tools
tools
jasmine-1.1.0
jasmine_favicon.png
MIT.LICENSE
package.json
Readme.md
node-static
.npmignore
benchmark
bin
etc
trainwreck.jpg
examples
lib
node-static
LICENSE
node_modules
colors
package.json
ReadMe.md
optimist
.travis.yml
example
LICENSE
node_modules
wordwrap
.npmignore
example
package.json
README.markdown
test
package.json
README.markdown
test
_
package.json
README.md
test
fixtures
integration
socket.io
.npmignore
.travis.yml
benchmarks
History.md
lib
stores
transports
websocket
LICENSE
Makefile
node_modules
policyfile
.npmignore
doc
examples
lib
LICENSE
Makefile
package.json
README.md
tests
ssl
ssl.crt
ssl.private.key
redis
.npmignore
benches
stress
pubsub
run
rpushblpop
run
speed
00
plot
size-rate.png
changelog.md
examples
lib
parser
package.json
README.md
socket.io-client
.npmignore
bin
dist
WebSocketMain.swf
WebSocketMainInsecure.swf
History.md
lib
transports
vendor
web-socket-js
.npmignore
flash-src
build.sh
com
adobe
net
proxies
RFC2817Socket.as
gsolo
encryption
MD5.as
hurlant
crypto
cert
MozillaRootCertificates.as
X509Certificate.as
X509CertificateCollection.as
Crypto.as
hash
HMAC.as
IHash.as
IHMAC.as
MAC.as
MD2.as
MD5.as
SHA1.as
SHA224.as
SHA256.as
SHABase.as
prng
ARC4.as
IPRNG.as
Random.as
TLSPRF.as
rsa
RSAKey.as
symmetric
AESKey.as
aeskey.pl
BlowFishKey.as
CBCMode.as
CFB8Mode.as
CFBMode.as
CTRMode.as
DESKey.as
ECBMode.as
ICipher.as
IMode.as
IPad.as
IStreamCipher.as
ISymmetricKey.as
IVMode.as
NullPad.as
OFBMode.as
PKCS5.as
SimpleIVMode.as
SSLPad.as
TLSPad.as
TripleDESKey.as
XTeaKey.as
tests
AESKeyTest.as
ARC4Test.as
BigIntegerTest.as
BlowFishKeyTest.as
CBCModeTest.as
CFB8ModeTest.as
CFBModeTest.as
CTRModeTest.as
DESKeyTest.as
ECBModeTest.as
HMACTest.as
ITestHarness.as
MD2Test.as
MD5Test.as
OFBModeTest.as
RSAKeyTest.as
SHA1Test.as
SHA224Test.as
SHA256Test.as
TestCase.as
TLSPRFTest.as
TripleDESKeyTest.as
XTeaKeyTest.as
tls
BulkCiphers.as
CipherSuites.as
IConnectionState.as
ISecurityParameters.as
KeyExchanges.as
MACs.as
SSLConnectionState.as
SSLEvent.as
SSLSecurityParameters.as
TLSConfig.as
TLSConnectionState.as
TLSEngine.as
TLSError.as
TLSEvent.as
TLSSecurityParameters.as
TLSSocket.as
TLSSocketEvent.as
TLSTest.as
math
BarrettReduction.as
bi_internal.as
BigInteger.as
ClassicReduction.as
IReduction.as
MontgomeryReduction.as
NullReduction.as
util
ArrayUtil.as
Base64.as
der
ByteString.as
DER.as
IAsn1Type.as
Integer.as
ObjectIdentifier.as
OID.as
PEM.as
PrintableString.as
Sequence.as
Set.as
Type.as
UTCTime.as
Hex.as
Memory.as
IWebSocketLogger.as
WebSocket.as
WebSocketEvent.as
WebSocketMain.as
WebSocketMainInsecure.as
README.md
WebSocketMain.swf
WebSocketMainInsecure.zip
Makefile
node_modules
.bin
uglifyjs
wscat
active-x-obfuscator
.npmignore
node_modules
zeparser
.npmignore
LICENSE
package.json
README
package.json
Readme.md
uglify-js
.npmignore
bin
uglifyjs
lib
package.json
package.json~
README.org
test
unit
compress
expected
test
tmp
ws
.npmignore
.travis.yml
bench
bin
wscat
binding.gyp
doc
ws.md
examples
fileapi
.npmignore
package.json
public
serverstats
package.json
public
serverstats-express_3
package.json
public
History.md
lib
Makefile
node_modules
commander
.npmignore
.travis.yml
History.md
lib
Makefile
package.json
Readme.md
options
.npmignore
lib
Makefile
package.json
README.md
test
fixtures
test.conf
tinycolor
.npmignore
package.json
README.md
package.json
README.md
src
bufferutil.cc
validation.cc
test
fixtures
certificate.pem
key.pem
request.pem
textfile
xmlhttprequest
autotest.watchr
example
lib
package.json
README.md
tests
package.json
README.md
test
node
package.json
Readme.md
stylus
bin
stylus
lib
convert
functions
index.styl
nodes
stack
visitor
LICENSE
node_modules
cssom
.gitmodules
.npmignore
lib
package.json
README.mdown
debug
.npmignore
example
History.md
lib
Makefile
package.json
Readme.md
mkdirp
.npmignore
.travis.yml
examples
LICENSE
package.json
README.markdown
test
package.json
Readme.md
testing
small.styl
test.styl
package.json
public
images
Friend.png
Header.pdn
Header.png
javascripts
jquery-ui-1.9.1.custom
css
ui-lightness
images
ui-bg_diagonals-thick_18_b81900_40x40.png
ui-bg_diagonals-thick_20_666666_40x40.png
ui-bg_flat_10_000000_40x100.png
ui-bg_glass_100_f6f6f6_1x400.png
ui-bg_glass_100_fdf5ce_1x400.png
ui-bg_glass_65_ffffff_1x400.png
ui-bg_gloss-wave_35_f6a828_500x100.png
ui-bg_highlight-soft_100_eeeeee_1x100.png
ui-bg_highlight-soft_75_ffe45c_1x100.png
ui-icons_222222_256x240.png
ui-icons_228ef1_256x240.png
ui-icons_ef8c08_256x240.png
ui-icons_ffd27a_256x240.png
ui-icons_ffffff_256x240.png
js
stylesheets
home.styl
style.styl
routes
views
d3demo.jade
home.jade
layout.jade
/** 
 * @fileoverview 
 *
 * JsWorld
 *
 * <p>Javascript library for localised formatting and parsing of:
 *    <ul>
 *        <li>Numbers
 *        <li>Dates and times
 *        <li>Currency
 *    </ul>
 *
 * <p>The library classes are configured with standard POSIX locale definitions
 * derived from Unicode's Common Locale Data Repository (CLDR).
 *
 * <p>Website: <a href="http://software.dzhuvinov.com/jsworld.html">JsWorld</a>
 *
 * @author Vladimir Dzhuvinov
 * @version 2.5 (2011-12-23)
 */



/** 
 * @namespace Namespace container for the JsWorld library objects.
 */
jsworld = {};


/** 
 * @function
 * 
 * @description Formats a JavaScript Date object as an ISO-8601 date/time 
 * string.
 *
 * @param {Date} [d] A valid JavaScript Date object. If undefined the 
 *        current date/time will be used.
 * @param {Boolean} [withTZ] Include timezone offset, default false.
 *
 * @returns {String} The date/time formatted as YYYY-MM-DD HH:MM:SS.
 */
jsworld.formatIsoDateTime = function(d, withTZ) {

	if (typeof d === "undefined")
		d = new Date(); // now
	
	if (typeof withTZ === "undefined")
		withTZ = false;
	
	var s = jsworld.formatIsoDate(d) + " " + jsworld.formatIsoTime(d);
	
	if (withTZ) {
	
		var diff = d.getHours() - d.getUTCHours();
		var hourDiff = Math.abs(diff);
		
		var minuteUTC = d.getUTCMinutes();
		var minute = d.getMinutes();
		
		if (minute != minuteUTC && minuteUTC < 30 && diff < 0)
			hourDiff--;
			
		if (minute != minuteUTC && minuteUTC > 30 && diff > 0)
			hourDiff--;
		
		var minuteDiff;
		if (minute != minuteUTC)
			minuteDiff = ":30";
		else
			minuteDiff = ":00";
		
		var timezone;
		if (hourDiff < 10)
			timezone = "0" + hourDiff + minuteDiff;
		
		else
			timezone = "" + hourDiff + minuteDiff;

		if (diff < 0)
			timezone = "-" + timezone;
		
		else
			timezone = "+" + timezone;
		
		s = s + timezone;
	}
	
	return s;
};


/** 
 * @function
 * 
 * @description Formats a JavaScript Date object as an ISO-8601 date string.
 *
 * @param {Date} [d] A valid JavaScript Date object. If undefined the current 
 *        date will be used.
 *
 * @returns {String} The date formatted as YYYY-MM-DD.
 */
jsworld.formatIsoDate = function(d) {

	if (typeof d === "undefined")
		d = new Date(); // now
	
	var year = d.getFullYear();
	var month = d.getMonth() + 1;
	var day = d.getDate();
	
	return year + "-" + jsworld._zeroPad(month, 2) + "-" + jsworld._zeroPad(day, 2);
};


/** 
 * @function
 * 
 * @description Formats a JavaScript Date object as an ISO-8601 time string.
 *
 * @param {Date} [d] A valid JavaScript Date object. If undefined the current 
 *        time will be used.
 *
 * @returns {String} The time formatted as HH:MM:SS.
 */
jsworld.formatIsoTime = function(d) {

	if (typeof d === "undefined")
		d = new Date(); // now
	
	var hour = d.getHours();
	var minute = d.getMinutes();
	var second = d.getSeconds();
	
	return jsworld._zeroPad(hour, 2) + ":" + jsworld._zeroPad(minute, 2) + ":" + jsworld._zeroPad(second, 2);
};


/** 
 * @function
 * 
 * @description Parses an ISO-8601 formatted date/time string to a JavaScript 
 * Date object.
 *
 * @param {String} isoDateTimeVal An ISO-8601 formatted date/time string.
 *
 * <p>Accepted formats:
 *
 * <ul>
 *     <li>YYYY-MM-DD HH:MM:SS
 *     <li>YYYYMMDD HHMMSS
 *     <li>YYYY-MM-DD HHMMSS
 *     <li>YYYYMMDD HH:MM:SS
 * </ul>
 *
 * @returns {Date} The corresponding Date object.
 *
 * @throws Error on a badly formatted date/time string or on a invalid date.
 */
jsworld.parseIsoDateTime = function(isoDateTimeVal) {

	if (typeof isoDateTimeVal != "string")
		throw "Error: The parameter must be a string";

	// First, try to match "YYYY-MM-DD HH:MM:SS" format
	var matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/);
	
	// If unsuccessful, try to match "YYYYMMDD HHMMSS" format
	if (matches === null)
		matches = isoDateTimeVal.match(/^(\d\d\d\d)(\d\d)(\d\d)[T ](\d\d)(\d\d)(\d\d)/);
		
	// ... try to match "YYYY-MM-DD HHMMSS" format
	if (matches === null)
		matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d)(\d\d)(\d\d)/);
	
	// ... try to match "YYYYMMDD HH:MM:SS" format
	if (matches === null)
		matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/);

	// Report bad date/time string
	if (matches === null)
		throw "Error: Invalid ISO-8601 date/time string";

	// Force base 10 parse int as some values may have leading zeros!
	// (to avoid implicit octal base conversion)
	var year = parseInt(matches[1], 10);
	var month = parseInt(matches[2], 10);
	var day = parseInt(matches[3], 10);
	
	var hour = parseInt(matches[4], 10);
	var mins = parseInt(matches[5], 10);
	var secs = parseInt(matches[6], 10);
	
	// Simple value range check, leap years not checked
	// Note: the originial ISO time spec for leap hours (24:00:00) and seconds (00:00:60) is not supported
	if (month < 1 || month > 12 ||
	    day   < 1 || day   > 31 ||
	    hour  < 0 || hour  > 23 ||
	    mins  < 0 || mins  > 59 ||
	    secs  < 0 || secs  > 59    )
	    
		throw "Error: Invalid ISO-8601 date/time value";

	var d = new Date(year, month - 1, day, hour, mins, secs);
	
	// Check if the input date was valid 
	// (JS Date does automatic forward correction)
	if (d.getDate() != day || d.getMonth() +1 != month)
		throw "Error: Invalid date";
	
	return d;
};


/** 
 * @function
 * 
 * @description Parses an ISO-8601 formatted date string to a JavaScript 
 * Date object.
 *
 * @param {String} isoDateVal An ISO-8601 formatted date string.
 *
 * <p>Accepted formats:
 *
 * <ul>
 *     <li>YYYY-MM-DD
 *     <li>YYYYMMDD
 * </ul>
 *
 * @returns {Date} The corresponding Date object.
 *
 * @throws Error on a badly formatted date string or on a invalid date.
 */
jsworld.parseIsoDate = function(isoDateVal) {

	if (typeof isoDateVal != "string")
		throw "Error: The parameter must be a string";

	// First, try to match "YYYY-MM-DD" format
	var matches = isoDateVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)/);
	
	// If unsuccessful, try to match "YYYYMMDD" format
	if (matches === null)
		matches = isoDateVal.match(/^(\d\d\d\d)(\d\d)(\d\d)/);

	// Report bad date/time string
	if (matches === null)
		throw "Error: Invalid ISO-8601 date string";

	// Force base 10 parse int as some values may have leading zeros!
	// (to avoid implicit octal base conversion)
	var year = parseInt(matches[1], 10);
	var month = parseInt(matches[2], 10);
	var day = parseInt(matches[3], 10);
	
	// Simple value range check, leap years not checked
	if (month < 1 || month > 12 ||
	    day   < 1 || day   > 31    )
	    
		throw "Error: Invalid ISO-8601 date value";

	var d = new Date(year, month - 1, day);
	
	// Check if the input date was valid 
	// (JS Date does automatic forward correction)
	if (d.getDate() != day || d.getMonth() +1 != month)
		throw "Error: Invalid date";
	
	return d;
};


/** 
 * @function
 * 
 * @description Parses an ISO-8601 formatted time string to a JavaScript 
 * Date object.
 *
 * @param {String} isoTimeVal An ISO-8601 formatted time string.
 *
 * <p>Accepted formats:
 *
 * <ul>
 *     <li>HH:MM:SS
 *     <li>HHMMSS
 * </ul>
 *
 * @returns {Date} The corresponding Date object, with year, month and day set
 *          to zero.
 *
 * @throws Error on a badly formatted time string.
 */
jsworld.parseIsoTime = function(isoTimeVal) {

	if (typeof isoTimeVal != "string")
		throw "Error: The parameter must be a string";

	// First, try to match "HH:MM:SS" format
	var matches = isoTimeVal.match(/^(\d\d):(\d\d):(\d\d)/);
	
	// If unsuccessful, try to match "HHMMSS" format
	if (matches === null)
		matches = isoTimeVal.match(/^(\d\d)(\d\d)(\d\d)/);
	
	// Report bad date/time string
	if (matches === null)
		throw "Error: Invalid ISO-8601 date/time string";

	// Force base 10 parse int as some values may have leading zeros!
	// (to avoid implicit octal base conversion)
	var hour = parseInt(matches[1], 10);
	var mins = parseInt(matches[2], 10);
	var secs = parseInt(matches[3], 10);
	
	// Simple value range check, leap years not checked
	if (hour < 0 || hour > 23 ||
	    mins < 0 || mins > 59 ||
	    secs < 0 || secs > 59    )
	    
		throw "Error: Invalid ISO-8601 time value";

	return new Date(0, 0, 0, hour, mins, secs);
};


/**
 * @private
 *
 * @description Trims leading and trailing whitespace from a string.
 *
 * <p>Used non-regexp the method from http://blog.stevenlevithan.com/archives/faster-trim-javascript
 *
 * @param {String} str The string to trim.
 *
 * @returns {String} The trimmed string.
 */
jsworld._trim = function(str) {

	var whitespace = ' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000';
	
	for (var i = 0; i < str.length; i++) {
	
		if (whitespace.indexOf(str.charAt(i)) === -1) {
			str = str.substring(i);
			break;
		}
	}
	
	for (i = str.length - 1; i >= 0; i--) {
		if (whitespace.indexOf(str.charAt(i)) === -1) {
			str = str.substring(0, i + 1);
			break;
		}
	}
	
	return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';
};



/**
 * @private
 *
 * @description Returns true if the argument represents a decimal number.
 *
 * @param {Number|String} arg The argument to test.
 *
 * @returns {Boolean} true if the argument represents a decimal number, 
 *          otherwise false.
 */
jsworld._isNumber = function(arg) {

	if (typeof arg == "number")
		return true;
	
	if (typeof arg != "string")
		return false;
	
	// ensure string
	var s = arg + "";
	
	return (/^-?(\d+|\d*\.\d+)$/).test(s);
};


/**
 * @private
 *
 * @description Returns true if the argument represents a decimal integer.
 *
 * @param {Number|String} arg The argument to test.
 *
 * @returns {Boolean} true if the argument represents an integer, otherwise 
 *          false.
 */
jsworld._isInteger = function(arg) {

	if (typeof arg != "number" && typeof arg != "string")
		return false;

	// convert to string
	var s = arg + "";

	return (/^-?\d+$/).test(s);
};


/**
 * @private
 *
 * @description Returns true if the argument represents a decimal float.
 *
 * @param {Number|String} arg The argument to test.
 *
 * @returns {Boolean} true if the argument represents a float, otherwise false.
 */
jsworld._isFloat = function(arg) {

	if (typeof arg != "number" && typeof arg != "string")
		return false;
	
	// convert to string
	var s = arg + "";
	
	return (/^-?\.\d+?$/).test(s);
};


/** 
 * @private
 *
 * @description Checks if the specified formatting option is contained 
 * within the options string.
 * 
 * @param {String} option The option to search for.
 * @param {String} optionsString The options string.
 *
 * @returns {Boolean} true if the flag is found, else false
 */
jsworld._hasOption = function(option, optionsString) {

	if (typeof option != "string" || typeof optionsString != "string")
		return false;

	if (optionsString.indexOf(option) != -1)
		return true;
	else
		return false;
};


/**
 * @private
 *
 * @description String replacement function.
 *
 * @param {String} s The string to work on.
 * @param {String} target The string to search for.
 * @param {String} replacement The replacement.
 *
 * @returns {String} The new string.
 */
jsworld._stringReplaceAll = function(s, target, replacement) {

	var out;

	if (target.length == 1 && replacement.length == 1) {
		// simple char/char case somewhat faster
		out = "";
	
		for (var i = 0; i < s.length; i++) {
			
			if (s.charAt(i) == target.charAt(0))
				out = out + replacement.charAt(0);
			else
				out = out + s.charAt(i);
		}
		
		return out;
	}
	else {
		// longer target and replacement strings
		out = s;

		var index = out.indexOf(target);
		
		while (index != -1) {
		
			out = out.replace(target, replacement);
			
			index = out.indexOf(target);
		}

		return out;
	}
};


/**
 * @private
 *
 * @description Tests if a string starts with the specified substring.
 *
 * @param {String} testedString The string to test.
 * @param {String} sub The string to match.
 *
 * @returns {Boolean} true if the test succeeds.
 */
jsworld._stringStartsWith = function (testedString, sub) {
	
	if (testedString.length < sub.length)
		return false;
	
	for (var i = 0; i < sub.length; i++) {
		if (testedString.charAt(i) != sub.charAt(i))
			return false;
	}
	
	return true;
};


/** 
 * @private
 *
 * @description Gets the requested precision from an options string.
 *
 * <p>Example: ".3" returns 3 decimal places precision.
 *
 * @param {String} optionsString The options string.
 *
 * @returns {integer Number} The requested precision, -1 if not specified.
 */
jsworld._getPrecision = function (optionsString) {

	if (typeof optionsString != "string")
		return -1;

	var m = optionsString.match(/\.(\d)/);
	if (m)
		return parseInt(m[1], 10);
	else
		return -1;
};


/** 
 * @private
 *
 * @description Takes a decimal numeric amount (optionally as string) and 
 * returns its integer and fractional parts packed into an object.
 *
 * @param {Number|String} amount The amount, e.g. "123.45" or "-56.78"
 * 
 * @returns {object} Parsed amount object with properties:
 *         {String} integer  : the integer part
 *         {String} fraction : the fraction part
 */
jsworld._splitNumber = function (amount) {

	if (typeof amount == "number")
		amount = amount + "";

	var obj = {};

	// remove negative sign
	if (amount.charAt(0) == "-")
		amount = amount.substring(1);

	// split amount into integer and decimal parts
	var amountParts = amount.split(".");
	if (!amountParts[1])
		amountParts[1] = ""; // we need "" instead of null

	obj.integer = amountParts[0];
	obj.fraction = amountParts[1];

	return obj;
};


/** 
 * @private
 *
 * @description Formats the integer part using the specified grouping
 * and thousands separator.
 * 
 * @param {String} intPart The integer part of the amount, as string.
 * @param {String} grouping The grouping definition.
 * @param {String} thousandsSep The thousands separator.
 * 
 * @returns {String} The formatted integer part.
 */
jsworld._formatIntegerPart = function (intPart, grouping, thousandsSep) {

	// empty separator string? no grouping?
	// -> return immediately with no formatting!
	if (thousandsSep == "" || grouping == "-1")
		return intPart;

	// turn the semicolon-separated string of integers into an array
	var groupSizes = grouping.split(";");

	// the formatted output string
	var out = "";

	// the intPart string position to process next,
	// start at string end, e.g. "10000000<starts here"
	var pos = intPart.length;

	// process the intPart string backwards
	//     "1000000000"
	//            <---\ direction
	var size;
	
	while (pos > 0) {

		// get next group size (if any, otherwise keep last)
		if (groupSizes.length > 0)
			size = parseInt(groupSizes.shift(), 10);

		// int parse error?
		if (isNaN(size))
			throw "Error: Invalid grouping";

		// size is -1? -> no more grouping, so just copy string remainder
		if (size == -1) {
			out = intPart.substring(0, pos) + out;
			break;
		}

		pos -= size; // move to next sep. char. position

		// position underrun? -> just copy string remainder
		if (pos < 1) {
			out = intPart.substring(0, pos + size) + out;
			break;
		}

		// extract group and apply sep. char.
		out = thousandsSep + intPart.substring(pos, pos + size) + out;
	}

	return out;
};
	
	
/** 
 * @private
 *
 * @description Formats the fractional part to the specified decimal 
 * precision.
 *
 * @param {String} fracPart The fractional part of the amount
 * @param {integer Number} precision The desired decimal precision
 *
 * @returns {String} The formatted fractional part.
 */
jsworld._formatFractionPart = function (fracPart, precision) {

	// append zeroes up to precision if necessary
	for (var i=0; fracPart.length < precision; i++)
		fracPart = fracPart + "0";

	return fracPart;
};


/** 
 * @private 
 *
 * @desription Converts a number to string and pad it with leading zeroes if the
 * string is shorter than length.
 *
 * @param {integer Number} number The number value subjected to selective padding.
 * @param {integer Number} length If the number has fewer digits than this length
 *        apply padding.
 *
 * @returns {String} The formatted string.
 */
jsworld._zeroPad = function(number, length) {

	// ensure string
	var s = number + "";

	while (s.length < length)
		s = "0" + s;
	
	return s;
};


/** 
 * @private 
 * @description Converts a number to string and pads it with leading spaces if 
 * the string is shorter than length.
 *
 * @param {integer Number} number The number value subjected to selective padding.
 * @param {integer Number} length If the number has fewer digits than this length
 *        apply padding.
 *
 * @returns {String} The formatted string.
 */
jsworld._spacePad = function(number, length) {

	// ensure string
	var s = number + "";

	while (s.length < length)
		s = " " + s;
	
	return s;
};



/**
 * @class
 * Represents a POSIX-style locale with its numeric, monetary and date/time 
 * properties. Also provides a set of locale helper methods.
 *
 * <p>The locale properties follow the POSIX standards:
 *
 * <ul>
 *     <li><a href="http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap07.html#tag_07_03_04">POSIX LC_NUMERIC</a>
 *     <li><a href="http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html#tag_07_03_03">POSIX LC_MONETARY</a>
 *     <li><a href="http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap07.html#tag_07_03_05">POSIX LC_TIME</a>
 * </ul>
 *
 * @public
 * @constructor
 * @description Creates a new locale object (POSIX-style) with the specified
 * properties.
 *
 * @param {object} properties An object containing the raw locale properties:
 *
 *        @param {String} properties.decimal_point
 *
 *        A string containing the symbol that shall be used as the decimal
 *        delimiter (radix character) in numeric, non-monetary formatted
 *        quantities. This property cannot be omitted and cannot be set to the
 *        empty string.
 *
 *
 *        @param {String} properties.thousands_sep
 *
 *        A string containing the symbol that shall be used as a separator for
 *        groups of digits to the left of the decimal delimiter in numeric,
 *        non-monetary formatted monetary quantities.
 *
 *
 *        @param {String} properties.grouping
 *
 *        Defines the size of each group of digits in formatted non-monetary
 *        quantities. The operand is a sequence of integers separated by
 *        semicolons. Each integer specifies the number of digits in each group,
 *        with the initial integer defining the size of the group immediately
 *        preceding the decimal delimiter, and the following integers defining
 *        the preceding groups. If the last integer is not -1, then the size of
 *        the previous group (if any) shall be repeatedly used for the
 *        remainder of the digits. If the last integer is -1, then no further
 *        grouping shall be performed.
 *
 *
 *        @param {String} properties.int_curr_symbol
 *
 *        The first three letters signify the ISO-4217 currency code,
 *        the fourth letter is the international symbol separation character
 *        (normally a space).
 *
 *
 *        @param {String} properties.currency_symbol
 *
 *        The local shorthand currency symbol, e.g. "$" for the en_US locale
 *
 *
 *        @param {String} properties.mon_decimal_point
 *
 *        The symbol to be used as the decimal delimiter (radix character)
 *
 *
 *        @param {String} properties.mon_thousands_sep
 *
 *        The symbol to be used as a separator for groups of digits to the
 *        left of the decimal delimiter.
 *
 *
 *        @param {String} properties.mon_grouping
 *
 *        A string that defines the size of each group of digits. The
 *        operand is a sequence of integers separated by semicolons (";").
 *        Each integer specifies the number of digits in each group, with the
 *        initial integer defining the size of the group preceding the
 *        decimal delimiter, and the following integers defining the
 *        preceding groups. If the last integer is not -1, then the size of
 *        the previous group (if any) must be repeatedly used for the
 *        remainder of the digits. If the last integer is -1, then no
 *        further grouping is to be performed.
 *
 *
 *        @param {String} properties.positive_sign
 *
 *        The string to indicate a non-negative monetary amount.
 *
 *
 *        @param {String} properties.negative_sign
 *
 *        The string to indicate a negative monetary amount.
 *
 *
 *        @param {integer Number} properties.frac_digits
 *
 *        An integer representing the number of fractional digits (those to
 *        the right of the decimal delimiter) to be written in a formatted
 *        monetary quantity using currency_symbol.
 *
 *
 *        @param {integer Number} properties.int_frac_digits
 *
 *        An integer representing the number of fractional digits (those to
 *        the right of the decimal delimiter) to be written in a formatted
 *        monetary quantity using int_curr_symbol.
 *
 *
 *        @param {integer Number} properties.p_cs_precedes
 *
 *        An integer set to 1 if the currency_symbol precedes the value for a
 *        monetary quantity with a non-negative value, and set to 0 if the
 *        symbol succeeds the value.
 *
 *
 *        @param {integer Number} properties.n_cs_precedes
 *
 *        An integer set to 1 if the currency_symbol precedes the value for a
 *        monetary quantity with a negative value, and set to 0 if the symbol
 *        succeeds the value.
 *
 *
 *        @param {integer Number} properties.p_sep_by_space
 *
 *        Set to a value indicating the separation of the currency_symbol,
 *        the sign string, and the value for a non-negative formatted monetary
 *        quantity:
 *        
 *             <p>0 No space separates the currency symbol and value.</p>
 *
 *             <p>1 If the currency symbol and sign string are adjacent, a space
 *                  separates them from the value; otherwise, a space separates
 *                  the currency symbol from the value.</p>
 *
 *             <p>2 If the currency symbol and sign string are adjacent, a space
 *                  separates them; otherwise, a space separates the sign string
 *                  from the value.</p>
 *
 *
 *        @param {integer Number} properties.n_sep_by_space
 *
 *        Set to a value indicating the separation of the currency_symbol,
 *        the sign string, and the value for a negative formatted monetary
 *        quantity. Rules same as for p_sep_by_space.
 *
 *
 *        @param {integer Number} properties.p_sign_posn
 *
 *        An integer set to a value indicating the positioning of the
 *        positive_sign for a monetary quantity with a non-negative value:
 *	
 *	       <p>0 Parentheses enclose the quantity and the currency_symbol.</p>
 *
 *	       <p>1 The sign string precedes the quantity and the currency_symbol.</p>
 *
 *	       <p>2 The sign string succeeds the quantity and the currency_symbol.</p>
 *
 *	       <p>3 The sign string precedes the currency_symbol.</p>
 *
 *	       <p>4 The sign string succeeds the currency_symbol.</p>
 *
 *
 *	  @param {integer Number} properties.n_sign_posn
 *
 *	  An integer set to a value indicating the positioning of the
 *	  negative_sign for a negative formatted monetary quantity. Rules same
 *	  as for p_sign_posn.
 *
 *
 *	  @param {integer Number} properties.int_p_cs_precedes
 *
 *	  An integer set to 1 if the int_curr_symbol precedes the value for a
 *	  monetary quantity with a non-negative value, and set to 0 if the
 *	  symbol succeeds the value.
 *
 *
 *	  @param {integer Number} properties.int_n_cs_precedes
 *
 *	  An integer set to 1 if the int_curr_symbol precedes the value for a
 *	  monetary quantity with a negative value, and set to 0 if the symbol
 *	  succeeds the value.
 *
 *
 *	  @param {integer Number} properties.int_p_sep_by_space
 *
 *	  Set to a value indicating the separation of the int_curr_symbol,
 *	  the sign string, and the value for a non-negative internationally
 *	  formatted monetary quantity. Rules same as for p_sep_by_space.
 *
 *
 *	  @param {integer Number} properties.int_n_sep_by_space
 *
 *	  Set to a value indicating the separation of the int_curr_symbol,
 *	  the sign string, and the value for a negative internationally
 *	  formatted monetary quantity. Rules same as for p_sep_by_space.
 *
 *
 *	  @param {integer Number} properties.int_p_sign_posn
 *
 *	  An integer set to a value indicating the positioning of the
 *	  positive_sign for a positive monetary quantity formatted with the
 *	  international format. Rules same as for p_sign_posn.
 *
 *
 *	  @param {integer Number} properties.int_n_sign_posn
 *
 *	  An integer set to a value indicating the positioning of the
 *	  negative_sign for a negative monetary quantity formatted with the
 *	  international format. Rules same as for p_sign_posn.
 *
 *
 *        @param {String[] | String} properties.abday
 *
 *        The abbreviated weekday names, corresponding to the %a conversion
 *        specification. The property must be either an array of 7 strings or
 *        a string consisting of 7 semicolon-separated substrings, each 
 *        surrounded by double-quotes. The first must be the abbreviated name 
 *        of the day corresponding to Sunday, the second the abbreviated name 
 *        of the day corresponding to Monday, and so on.
 *        
 *
 *        @param {String[] | String} properties.day
 *
 *        The full weekday names, corresponding to the %A conversion
 *        specification. The property must be either an array of 7 strings or
 *        a string consisting of 7 semicolon-separated substrings, each 
 *        surrounded by double-quotes. The first must be the full name of the 
 *        day corresponding to Sunday, the second the full name of the day 
 *        corresponding to Monday, and so on.
 *        
 *
 *        @param {String[] | String} properties.abmon
 *
 *        The abbreviated month names, corresponding to the %b conversion
 *        specification. The property must be either an array of 12 strings or
 *        a string consisting of 12 semicolon-separated substrings, each 
 *        surrounded by double-quotes. The first must be the abbreviated name 
 *        of the first month of the year (January), the second the abbreviated 
 *        name of the second month, and so on.
 *        
 *
 *        @param {String[] | String} properties.mon
 *
 *        The full month names, corresponding to the %B conversion
 *        specification. The property must be either an array of 12 strings or
 *        a string consisting of 12 semicolon-separated substrings, each 
 *        surrounded by double-quotes. The first must be the full name of the 
 *        first month of the year (January), the second the full name of the second 
 *        month, and so on.
 *        
 *
 *        @param {String} properties.d_fmt
 *
 *        The appropriate date representation. The string may contain any
 *        combination of characters and conversion specifications (%<char>).
 *        
 *
 *        @param {String} properties.t_fmt
 *
 *        The appropriate time representation. The string may contain any
 *        combination of characters and conversion specifications (%<char>).
 *        
 *
 *        @param {String} properties.d_t_fmt
 *
 *        The appropriate date and time representation. The string may contain
 *        any combination of characters and conversion specifications (%<char>).
 *
 *
 *        @param {String[] | String} properties.am_pm
 *
 *        The appropriate representation of the ante-meridiem and post-meridiem
 *        strings, corresponding to the %p conversion specification. The property 
 *        must be either an array of 2 strings or a string consisting of 2 
 *        semicolon-separated substrings, each surrounded by double-quotes. 
 *        The first string must represent the ante-meridiem designation, the 
 *        last string the post-meridiem designation.
 *
 *
 * @throws @throws Error on a undefined or invalid locale property.
 */
jsworld.Locale = function(properties) {
	
	
	/**
	 * @private
	 *
	 * @description Identifies the class for internal library purposes.
	 */
	this._className = "jsworld.Locale";
	
	
	/** 
	 * @private 
	 *
	 * @description Parses a day or month name definition list, which
	 * could be a ready JS array, e.g. ["Mon", "Tue", "Wed"...] or
	 * it could be a string formatted according to the classic POSIX
	 * definition e.g. "Mon";"Tue";"Wed";...
	 *
	 * @param {String[] | String} namesAn array or string defining 
	 *        the week/month names.
	 * @param {integer Number} expectedItems The number of expected list
	 *        items, e.g. 7 for weekdays, 12 for months.
	 *
	 * @returns {String[]} The parsed (and checked) items.
	 * 
	 * @throws Error on missing definition, unexpected item count or
	 *         missing double-quotes.
	 */
	this._parseList = function(names, expectedItems) {
		
		var array = [];
		
		if (names == null) {
			throw "Names not defined";
		}
		else if (typeof names == "object") {
			// we got a ready array
			array = names;
		}
		else if (typeof names == "string") {
			// we got the names in the classic POSIX form, do parse
			array = names.split(";", expectedItems);
		
			for (var i = 0; i < array.length; i++) {
				// check for and strip double quotes
				if (array[i][0] == "\"" && array[i][array[i].length - 1] == "\"")
					array[i] = array[i].slice(1, -1);
				else
					throw "Missing double quotes";
			}
		}
		else {
			throw "Names must be an array or a string";
		}
		
		if (array.length != expectedItems)
			throw "Expected " + expectedItems + " items, got " + array.length;
		
		return array;
	};
	
	
	/**
	 * @private
	 *
	 * @description Validates a date/time format string, such as "H:%M:%S". 
	 * Checks that the argument is of type "string" and is not empty.
	 *
	 * @param {String} formatString The format string.
	 *
	 * @returns {String} The validated string.
	 *
	 * @throws Error on null or empty string.
	 */
	this._validateFormatString = function(formatString) {
		
		if (typeof formatString == "string" && formatString.length > 0)
			return formatString;
		else
			throw "Empty or no string";
	};
	
	
	// LC_NUMERIC

	if (properties == null || typeof properties != "object")
		throw "Error: Invalid/missing locale properties";
	
	
	if (typeof properties.decimal_point != "string")
		throw "Error: Invalid/missing decimal_point property";
	
	this.decimal_point = properties.decimal_point;
	
	
	if (typeof properties.thousands_sep != "string")
		throw "Error: Invalid/missing thousands_sep property";
	
	this.thousands_sep = properties.thousands_sep;
	
	
	if (typeof properties.grouping != "string")
		throw "Error: Invalid/missing grouping property";
	
	this.grouping = properties.grouping;
	
	
	// LC_MONETARY
	
	if (typeof properties.int_curr_symbol != "string")
		throw "Error: Invalid/missing int_curr_symbol property";
	
	if (! /[A-Za-z]{3}.?/.test(properties.int_curr_symbol))
		throw "Error: Invalid int_curr_symbol property";
	
	this.int_curr_symbol = properties.int_curr_symbol;
	

	if (typeof properties.currency_symbol != "string")
		throw "Error: Invalid/missing currency_symbol property";
	
	this.currency_symbol = properties.currency_symbol;
	
	
	if (typeof properties.frac_digits != "number" && properties.frac_digits < 0)
		throw "Error: Invalid/missing frac_digits property";
	
	this.frac_digits = properties.frac_digits;
	
	
	// may be empty string/null for currencies with no fractional part
	if (properties.mon_decimal_point === null || properties.mon_decimal_point == "") {
	
		if (this.frac_digits > 0)
			throw "Error: Undefined mon_decimal_point property";
		else
			properties.mon_decimal_point = "";
	}
	
	if (typeof properties.mon_decimal_point != "string")
		throw "Error: Invalid/missing mon_decimal_point property";
	
	this.mon_decimal_point = properties.mon_decimal_point;
	
	
	if (typeof properties.mon_thousands_sep != "string")
		throw "Error: Invalid/missing mon_thousands_sep property";
	
	this.mon_thousands_sep = properties.mon_thousands_sep;
	
	
	if (typeof properties.mon_grouping != "string")
		throw "Error: Invalid/missing mon_grouping property";
	
	this.mon_grouping = properties.mon_grouping;
	
	
	if (typeof properties.positive_sign != "string")
		throw "Error: Invalid/missing positive_sign property";
	
	this.positive_sign = properties.positive_sign;
	
	
	if (typeof properties.negative_sign != "string")
		throw "Error: Invalid/missing negative_sign property";
	
	this.negative_sign = properties.negative_sign;
	
	
	
	if (properties.p_cs_precedes !== 0 && properties.p_cs_precedes !== 1)
		throw "Error: Invalid/missing p_cs_precedes property, must be 0 or 1";
	
	this.p_cs_precedes = properties.p_cs_precedes;
	
	
	if (properties.n_cs_precedes !== 0 && properties.n_cs_precedes !== 1)
		throw "Error: Invalid/missing n_cs_precedes, must be 0 or 1";
	
	this.n_cs_precedes = properties.n_cs_precedes;
	

	if (properties.p_sep_by_space !== 0 &&
	    properties.p_sep_by_space !== 1 &&
	    properties.p_sep_by_space !== 2)
		throw "Error: Invalid/missing p_sep_by_space property, must be 0, 1 or 2";
	
	this.p_sep_by_space = properties.p_sep_by_space;
	

	if (properties.n_sep_by_space !== 0 &&
	    properties.n_sep_by_space !== 1 &&
	    properties.n_sep_by_space !== 2)
		throw "Error: Invalid/missing n_sep_by_space property, must be 0, 1, or 2";
	
	this.n_sep_by_space = properties.n_sep_by_space;
	

	if (properties.p_sign_posn !== 0 &&
	    properties.p_sign_posn !== 1 &&
	    properties.p_sign_posn !== 2 &&
	    properties.p_sign_posn !== 3 &&
	    properties.p_sign_posn !== 4)
		throw "Error: Invalid/missing p_sign_posn property, must be 0, 1, 2, 3 or 4";
	
	this.p_sign_posn = properties.p_sign_posn;


	if (properties.n_sign_posn !== 0 &&
	    properties.n_sign_posn !== 1 &&
	    properties.n_sign_posn !== 2 &&
	    properties.n_sign_posn !== 3 &&
	    properties.n_sign_posn !== 4)
		throw "Error: Invalid/missing n_sign_posn property, must be 0, 1, 2, 3 or 4";
	
	this.n_sign_posn = properties.n_sign_posn;


	if (typeof properties.int_frac_digits != "number" && properties.int_frac_digits < 0)
		throw "Error: Invalid/missing int_frac_digits property";

	this.int_frac_digits = properties.int_frac_digits;
	
	
	if (properties.int_p_cs_precedes !== 0 && properties.int_p_cs_precedes !== 1)
		throw "Error: Invalid/missing int_p_cs_precedes property, must be 0 or 1";
	
	this.int_p_cs_precedes = properties.int_p_cs_precedes;
	
	
	if (properties.int_n_cs_precedes !== 0 && properties.int_n_cs_precedes !== 1)
		throw "Error: Invalid/missing int_n_cs_precedes property, must be 0 or 1";
	
	this.int_n_cs_precedes = properties.int_n_cs_precedes;
	

	if (properties.int_p_sep_by_space !== 0 &&
	    properties.int_p_sep_by_space !== 1 &&
	    properties.int_p_sep_by_space !== 2)
		throw "Error: Invalid/missing int_p_sep_by_spacev, must be 0, 1 or 2";
		
	this.int_p_sep_by_space = properties.int_p_sep_by_space;


	if (properties.int_n_sep_by_space !== 0 &&
	    properties.int_n_sep_by_space !== 1 &&
	    properties.int_n_sep_by_space !== 2)
		throw "Error: Invalid/missing int_n_sep_by_space property, must be 0, 1, or 2";
	
	this.int_n_sep_by_space = properties.int_n_sep_by_space;
	

	if (properties.int_p_sign_posn !== 0 &&
	    properties.int_p_sign_posn !== 1 &&
	    properties.int_p_sign_posn !== 2 &&
	    properties.int_p_sign_posn !== 3 &&
	    properties.int_p_sign_posn !== 4)
		throw "Error: Invalid/missing int_p_sign_posn property, must be 0, 1, 2, 3 or 4";
	
	this.int_p_sign_posn = properties.int_p_sign_posn;
	
	
	if (properties.int_n_sign_posn !== 0 &&
	    properties.int_n_sign_posn !== 1 &&
	    properties.int_n_sign_posn !== 2 &&
	    properties.int_n_sign_posn !== 3 &&
	    properties.int_n_sign_posn !== 4)
		throw "Error: Invalid/missing int_n_sign_posn property, must be 0, 1, 2, 3 or 4";

	this.int_n_sign_posn = properties.int_n_sign_posn;
	
	
	// LC_TIME
	
	if (properties == null || typeof properties != "object")
		throw "Error: Invalid/missing time locale properties";
	
	
	// parse the supported POSIX LC_TIME properties
	
	// abday
	try  {
		this.abday = this._parseList(properties.abday, 7);
	}
	catch (error) {
		throw "Error: Invalid abday property: " + error;
	}
	
	// day
	try {
		this.day = this._parseList(properties.day, 7);
	}
	catch (error) {
		throw "Error: Invalid day property: " + error;
	}
	
	// abmon
	try {
		this.abmon = this._parseList(properties.abmon, 12);
	} catch (error) {
		throw "Error: Invalid abmon property: " + error;
	}
	
	// mon
	try {
		this.mon = this._parseList(properties.mon, 12);
	} catch (error) {
		throw "Error: Invalid mon property: " + error;
	}
	
	// d_fmt
	try {
		this.d_fmt = this._validateFormatString(properties.d_fmt);
	} catch (error) {
		throw "Error: Invalid d_fmt property: " + error;
	}
	
	// t_fmt
	try {
		this.t_fmt = this._validateFormatString(properties.t_fmt);
	} catch (error) {
		throw "Error: Invalid t_fmt property: " + error;
	}
	
	// d_t_fmt
	try {
		this.d_t_fmt = this._validateFormatString(properties.d_t_fmt);
	} catch (error) {
		throw "Error: Invalid d_t_fmt property: " + error;
	}
	
	// am_pm
	try {
		var am_pm_strings = this._parseList(properties.am_pm, 2);
		this.am = am_pm_strings[0];
		this.pm = am_pm_strings[1];
	} catch (error) {
		// ignore empty/null string errors
		this.am = "";
		this.pm = "";
	}
	
	
	/**
	 * @public
	 *
	 * @description Returns the abbreviated name of the specified weekday.
	 *
	 * @param {integer Number} [weekdayNum] An integer between 0 and 6. Zero 
	 *        corresponds to Sunday, one to Monday, etc. If omitted the
	 *        method will return an array of all abbreviated weekday 
	 *        names.
	 *
	 * @returns {String | String[]} The abbreviated name of the specified weekday
	 *          or an array of all abbreviated weekday names.
	 *
	 * @throws Error on invalid argument.
	 */
	this.getAbbreviatedWeekdayName = function(weekdayNum) {
	
		if (typeof weekdayNum == "undefined" || weekdayNum === null)
			return this.abday;
		
		if (! jsworld._isInteger(weekdayNum) || weekdayNum < 0 || weekdayNum > 6)
			throw "Error: Invalid weekday argument, must be an integer [0..6]";
			
		return this.abday[weekdayNum];
	};
	
	
	/**
	 * @public
	 *
	 * @description Returns the name of the specified weekday.
	 *
	 * @param {integer Number} [weekdayNum] An integer between 0 and 6. Zero 
	 *        corresponds to Sunday, one to Monday, etc. If omitted the
	 *        method will return an array of all weekday names.
	 *
	 * @returns {String | String[]} The name of the specified weekday or an 
	 *          array of all weekday names.
	 *
	 * @throws Error on invalid argument.
	 */
	this.getWeekdayName = function(weekdayNum) {
		
		if (typeof weekdayNum == "undefined" || weekdayNum === null)
			return this.day;
		
		if (! jsworld._isInteger(weekdayNum) || weekdayNum < 0 || weekdayNum > 6)
			throw "Error: Invalid weekday argument, must be an integer [0..6]";
			
		return this.day[weekdayNum];
	};
	
	
	/**
	 * @public
	 *
	 * @description Returns the abbreviated name of the specified month.
	 *
	 * @param {integer Number} [monthNum] An integer between 0 and 11. Zero 
	 *        corresponds to January, one to February, etc. If omitted the
	 *        method will return an array of all abbreviated month names.
	 *
	 * @returns {String | String[]} The abbreviated name of the specified month
	 *          or an array of all abbreviated month names.
	 *
	 * @throws Error on invalid argument.
	 */
	this.getAbbreviatedMonthName = function(monthNum) {
	
		if (typeof monthNum == "undefined" || monthNum === null)
			return this.abmon;
		
		if (! jsworld._isInteger(monthNum) || monthNum < 0 || monthNum > 11)
			throw "Error: Invalid month argument, must be an integer [0..11]";
		
		return this.abmon[monthNum];
	};
	
	
	/**
	 * @public
	 *
	 * @description Returns the name of the specified month.
	 *
	 * @param {integer Number} [monthNum] An integer between 0 and 11. Zero 
	 *        corresponds to January, one to February, etc. If omitted the
	 *        method will return an array of all month names.
	 *
	 * @returns {String | String[]} The name of the specified month or an array 
	 *          of all month names.
	 *
	 * @throws Error on invalid argument.
	 */
	this.getMonthName = function(monthNum) {
	
		if (typeof monthNum == "undefined" || monthNum === null)
			return this.mon;
		
		if (! jsworld._isInteger(monthNum) || monthNum < 0 || monthNum > 11)
			throw "Error: Invalid month argument, must be an integer [0..11]";
		
		return this.mon[monthNum];
	};
	
	
	
	/** 
	 * @public
	 *
	 * @description Gets the decimal delimiter (radix) character for
	 * numeric quantities.
	 *
	 * @returns {String} The radix character.
	 */
	this.getDecimalPoint = function() {
		
		return this.decimal_point;
	};
	
	
	/** 
	 * @public
	 *
	 * @description Gets the local shorthand currency symbol.
	 *
	 * @returns {String} The currency symbol.
	 */
	this.getCurrencySymbol = function() {
		
		return this.currency_symbol;
	};
	
	
	/**
	 * @public
	 *
	 * @description Gets the internaltion currency symbol (ISO-4217 code).
	 *
	 * @returns {String} The international currency symbol.
	 */
	this.getIntCurrencySymbol = function() {
	
		return this.int_curr_symbol.substring(0,3);
	};
	
	
	/** 
	 * @public
	 *
	 * @description Gets the position of the local (shorthand) currency 
	 * symbol relative to the amount. Assumes a non-negative amount.
	 *
	 * @returns {Boolean} True if the symbol precedes the amount, false if
	 * the symbol succeeds the amount.
	 */
	this.currencySymbolPrecedes = function() {
		
		if (this.p_cs_precedes == 1)
			return true;
		else
			return false;
	};
	
	
	/** 
	 * @public
	 *
	 * @description Gets the position of the international (ISO-4217 code) 
	 * currency symbol relative to the amount. Assumes a non-negative 
	 * amount.
	 *
	 * @returns {Boolean} True if the symbol precedes the amount, false if
	 * the symbol succeeds the amount.
	 */
	this.intCurrencySymbolPrecedes = function() {
		
		if (this.int_p_cs_precedes == 1)
			return true;
		else
			return false;

	};
	
	
	/** 
	 * @public
	 *
	 * @description Gets the decimal delimiter (radix) for monetary
	 * quantities.
	 *
	 * @returns {String} The radix character.
	 */
	this.getMonetaryDecimalPoint = function() {
		
		return this.mon_decimal_point;
	};
	
	
	/** 
	 * @public
	 *
	 * @description Gets the number of fractional digits for local
	 * (shorthand) symbol formatting.
	 *
	 * @returns {integer Number} The number of fractional digits.
	 */
	this.getFractionalDigits = function() {
		
		return this.frac_digits;
	};
	
	
	/** 
	 * @public
	 *
	 * @description Gets the number of fractional digits for
	 * international (ISO-4217 code) formatting.
	 *
	 * @returns {integer Number} The number of fractional digits.
	 */
	this.getIntFractionalDigits = function() {
		
		return this.int_frac_digits;
	};
};



/** 
 * @class 
 * Class for localised formatting of numbers.
 *
 * <p>See: <a href="http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap07.html#tag_07_03_04">
 * POSIX LC_NUMERIC</a>.
 *
 *
 * @public
 * @constructor 
 * @description Creates a new numeric formatter for the specified locale.
 *
 * @param {jsworld.Locale} locale A locale object specifying the required 
 *        POSIX LC_NUMERIC formatting properties.
 *
 * @throws Error on constructor failure.
 */
jsworld.NumericFormatter = function(locale) {

	if (typeof locale != "object" || locale._className != "jsworld.Locale")
		throw "Constructor error: You must provide a valid jsworld.Locale instance";
	
	this.lc = locale;
	
	
	/** 
	 * @public
	 * 
	 * @description Formats a decimal numeric value according to the preset
	 * locale.
	 *
	 * @param {Number|String} number The number to format.
	 * @param {String} [options] Options to modify the formatted output:
	 *        <ul>
	 *            <li>"^"  suppress grouping
	 *            <li>"+"  force positive sign for positive amounts
	 *            <li>"~"  suppress positive/negative sign
	 *            <li>".n" specify decimal precision 'n'
	 *        </ul>
	 *
	 * @returns {String} The formatted number.
	 *
	 * @throws "Error: Invalid input" on bad input.
	 */
	this.format = function(number, options) {
		
		if (typeof number == "string")
			number = jsworld._trim(number);
		
		if (! jsworld._isNumber(number))
			throw "Error: The input is not a number";
		
		var floatAmount = parseFloat(number, 10);
		
		// get the required precision
		var reqPrecision = jsworld._getPrecision(options);
		
		// round to required precision
		if (reqPrecision != -1)
			floatAmount = Math.round(floatAmount * Math.pow(10, reqPrecision)) / Math.pow(10, reqPrecision);
		
		
		// convert the float number to string and parse into
		// object with properties integer and fraction
		var parsedAmount = jsworld._splitNumber(String(floatAmount));
		
		// format integer part with grouping chars
		var formattedIntegerPart;
		
		if (floatAmount === 0)
			formattedIntegerPart = "0";
		else
			formattedIntegerPart = jsworld._hasOption("^", options) ?
				parsedAmount.integer :
				jsworld._formatIntegerPart(parsedAmount.integer, 
				                           this.lc.grouping, 
							   this.lc.thousands_sep);
		
		// format the fractional part
		var formattedFractionPart =
			reqPrecision != -1 ?
			jsworld._formatFractionPart(parsedAmount.fraction, reqPrecision) :
			parsedAmount.fraction;
		
		
		// join the integer and fraction parts using the decimal_point property
		var formattedAmount =
			formattedFractionPart.length ?
			formattedIntegerPart + this.lc.decimal_point + formattedFractionPart :
			formattedIntegerPart;
		
		// prepend sign?
		if (jsworld._hasOption("~", options) || floatAmount === 0) {
			// suppress both '+' and '-' signs, i.e. return abs value
			return formattedAmount; 
		}
		else {
			if (jsworld._hasOption("+", options) || floatAmount < 0) {
				if (floatAmount > 0)
					// force '+' sign for positive amounts
					return "+" + formattedAmount;
				else if (floatAmount < 0)
					// prepend '-' sign
					return "-" + formattedAmount;
				else
					// zero case
					return formattedAmount;
			}
			else {
				// positive amount with no '+' sign
				return formattedAmount;
			}
		}
	};
};


/** 
 * @class 
 * Class for localised formatting of dates and times.
 *
 * <p>See: <a href="http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap07.html#tag_07_03_05">
 * POSIX LC_TIME</a>.
 *
 * @public
 * @constructor
 * @description Creates a new date/time formatter for the specified locale.
 *
 * @param {jsworld.Locale} locale A locale object specifying the required 
 *        POSIX LC_TIME formatting properties.
 *
 * @throws Error on constructor failure.
 */
jsworld.DateTimeFormatter = function(locale) {
	
		
	if (typeof locale != "object" || locale._className != "jsworld.Locale")
		throw "Constructor error: You must provide a valid jsworld.Locale instance.";
	
	this.lc = locale;

	
	/** 
	 * @public 
	 *
	 * @description Formats a date according to the preset locale.
	 *
	 * @param {Date|String} date A valid Date object instance or a string
	 *        containing a valid ISO-8601 formatted date, e.g. "2010-31-03" 
	 *        or "2010-03-31 23:59:59".
	 *
	 * @returns {String} The formatted date
	 *
	 * @throws Error on invalid date argument
	 */
	this.formatDate = function(date) {
		
		var d = null;
		
		if (typeof date == "string") {
			// assume ISO-8601 date string
			try {
				d = jsworld.parseIsoDate(date);
			} catch (error) {
				// try full ISO-8601 date/time string
				d = jsworld.parseIsoDateTime(date);
			}
		}
		else if (date !== null && typeof date == "object") {
			// assume ready Date object
			d = date;
		}
		else {
			throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string";
		}
		
		return this._applyFormatting(d, this.lc.d_fmt);
	};
	
	
	/** 
	 * @public 
	 *
	 * @description Formats a time according to the preset locale.
	 *
	 * @param {Date|String} date A valid Date object instance or a string
	 *        containing a valid ISO-8601 formatted time, e.g. "23:59:59"
	 *        or "2010-03-31 23:59:59".
	 *
	 * @returns {String} The formatted time.
	 *
	 * @throws Error on invalid date argument.
	 */
	this.formatTime = function(date) {
		
		var d = null;
		
		if (typeof date == "string") {
			// assume ISO-8601 time string
			try {
				d = jsworld.parseIsoTime(date);
			} catch (error) {
				// try full ISO-8601 date/time string
				d = jsworld.parseIsoDateTime(date);
			}
		}
		else if (date !== null && typeof date == "object") {
			// assume ready Date object
			d = date;
		}
		else {
			throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string";
		}
		
		return this._applyFormatting(d, this.lc.t_fmt);
	};
	
	
	/** 
	 * @public 
	 *
	 * @description Formats a date/time value according to the preset 
	 * locale.
	 *
	 * @param {Date|String} date A valid Date object instance or a string
	 *        containing a valid ISO-8601 formatted date/time, e.g.
	 *        "2010-03-31 23:59:59".
	 *
	 * @returns {String} The formatted time.
	 *
	 * @throws Error on invalid argument.
	 */
	this.formatDateTime = function(date) {
		
		var d = null;
		
		if (typeof date == "string") {
			// assume ISO-8601 format
			d = jsworld.parseIsoDateTime(date);
		}
		else if (date !== null && typeof date == "object") {
			// assume ready Date object
			d = date;
		}
		else {
			throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string";
		}
		
		return this._applyFormatting(d, this.lc.d_t_fmt);
	};
	
	
	/** 
	 * @private 
	 *
	 * @description Apples formatting to the Date object according to the
	 * format string.
	 *
	 * @param {Date} d A valid Date instance.
	 * @param {String} s The formatting string with '%' placeholders.
	 *
	 * @returns {String} The formatted string.
	 */
	this._applyFormatting = function(d, s) {
		
		s = s.replace(/%%/g, '%');
		s = s.replace(/%a/g, this.lc.abday[d.getDay()]);
		s = s.replace(/%A/g, this.lc.day[d.getDay()]);
		s = s.replace(/%b/g, this.lc.abmon[d.getMonth()]);
		s = s.replace(/%B/g, this.lc.mon[d.getMonth()]);
		s = s.replace(/%d/g, jsworld._zeroPad(d.getDate(), 2));
		s = s.replace(/%e/g, jsworld._spacePad(d.getDate(), 2));
		s = s.replace(/%F/g, d.getFullYear() +
				         "-" +
					 jsworld._zeroPad(d.getMonth()+1, 2) +
					 "-" +
					 jsworld._zeroPad(d.getDate(), 2));
		s = s.replace(/%h/g, this.lc.abmon[d.getMonth()]); // same as %b
		s = s.replace(/%H/g, jsworld._zeroPad(d.getHours(), 2));
		s = s.replace(/%I/g, jsworld._zeroPad(this._hours12(d.getHours()), 2));
		s = s.replace(/%k/g, d.getHours());
		s = s.replace(/%l/g, this._hours12(d.getHours()));
		s = s.replace(/%m/g, jsworld._zeroPad(d.getMonth()+1, 2));
		s = s.replace(/%n/g, "\n");
		s = s.replace(/%M/g, jsworld._zeroPad(d.getMinutes(), 2));
		s = s.replace(/%p/g, this._getAmPm(d.getHours()));
		s = s.replace(/%P/g, this._getAmPm(d.getHours()).toLocaleLowerCase()); // safe?
		s = s.replace(/%R/g, jsworld._zeroPad(d.getHours(), 2) +
					":" +
					jsworld._zeroPad(d.getMinutes(), 2));
		s = s.replace(/%S/g, jsworld._zeroPad(d.getSeconds(), 2));
		s = s.replace(/%T/g, jsworld._zeroPad(d.getHours(), 2) +
					":" +
					jsworld._zeroPad(d.getMinutes(), 2) +
					":" +
					jsworld._zeroPad(d.getSeconds(), 2));
		s = s.replace(/%w/g, this.lc.day[d.getDay()]);
		s = s.replace(/%y/g, new String(d.getFullYear()).substring(2));
		s = s.replace(/%Y/g, d.getFullYear());
		
		s = s.replace(/%Z/g, ""); // to do: ignored until a reliable TMZ method found
		
		s = s.replace(/%[a-zA-Z]/g, ""); // ignore all other % sequences
		
		return s;
	};
	
	
	/** 
	 * @private 
	 *
	 * @description Does 24 to 12 hour conversion.
	 *
	 * @param {integer Number} hour24 Hour [0..23].
	 * 
	 * @returns {integer Number} Corresponding hour [1..12].
	 */
	this._hours12 = function(hour24) {
		
		if (hour24 === 0)
			return 12; // 00h is 12AM
			
		else if (hour24 > 12)
			return hour24 - 12; // 1PM to 11PM
		
		else
			return hour24; // 1AM to 12PM
	};
	
	
	/** 
	 * @private 
	 * 
	 * @description Gets the appropriate localised AM or PM string depending
	 * on the day hour. Special cases: midnight is 12AM, noon is 12PM.
	 *
	 * @param {integer Number} hour24 Hour [0..23].
	 * 
	 * @returns {String} The corresponding localised AM or PM string.
	 */
	this._getAmPm = function(hour24) {
		
		if (hour24 < 12)
			return this.lc.am;
		else
			return this.lc.pm;
	};
};



/** 
 * @class Class for localised formatting of currency amounts.
 *
 * <p>See: <a href="http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html#tag_07_03_03">
 * POSIX LC_MONETARY</a>.
 *
 * @public
 * @constructor
 * @description Creates a new monetary formatter for the specified locale.
 *
 * @param {jsworld.Locale} locale A locale object specifying the required 
 *        POSIX LC_MONETARY formatting properties.
 * @param {String} [currencyCode] Set the currency explicitly by
 *        passing its international ISO-4217 code, e.g. "USD", "EUR", "GBP".
 *        Use this optional parameter to override the default local currency
 * @param {String} [altIntSymbol] Non-local currencies are formatted
 *        with their international ISO-4217 code to prevent ambiguity.
 *        Use this optional argument to force a different symbol, such as the
 *        currency's shorthand sign. This is mostly useful when the shorthand
 *        sign is both internationally recognised and identifies the currency
 *        uniquely (e.g. the Euro sign).
 *
 * @throws Error on constructor failure.
 */
jsworld.MonetaryFormatter = function(locale, currencyCode, altIntSymbol) {
	
	if (typeof locale != "object" || locale._className != "jsworld.Locale")
		throw "Constructor error: You must provide a valid jsworld.Locale instance";
	
	this.lc = locale;
	
	/** 
	 * @private
	 * @description Lookup table to determine the fraction digits for a
	 * specific currency; most currencies subdivide at 1/100 (2 fractional
	 * digits), so we store only those that deviate from the default.
	 *
	 * <p>The data is from Unicode's CLDR version 1.7.0. The two currencies
	 * with non-decimal subunits (MGA and MRO) are marked as having no
	 * fractional digits as well as all currencies that have no subunits
	 * in circulation.
	 * 
	 * <p>It is "hard-wired" for referential convenience and is only looked
	 * up when an overriding currencyCode parameter is supplied.
	 */
	this.currencyFractionDigits = {
		"AFN" : 0, "ALL" : 0, "AMD" : 0, "BHD" : 3, "BIF" : 0,
		"BYR" : 0, "CLF" : 0, "CLP" : 0, "COP" : 0, "CRC" : 0, 
		"DJF" : 0, "GNF" : 0, "GYD" : 0, "HUF" : 0, "IDR" : 0, 
		"IQD" : 0, "IRR" : 0, "ISK" : 0, "JOD" : 3, "JPY" : 0, 
		"KMF" : 0, "KRW" : 0, "KWD" : 3, "LAK" : 0, "LBP" : 0,
		"LYD" : 3, "MGA" : 0, "MMK" : 0, "MNT" : 0, "MRO" : 0,
		"MUR" : 0, "OMR" : 3, "PKR" : 0, "PYG" : 0, "RSD" : 0, 
		"RWF" : 0, "SLL" : 0, "SOS" : 0, "STD" : 0, "SYP" : 0, 
		"TND" : 3, "TWD" : 0, "TZS" : 0, "UGX" : 0, "UZS" : 0, 
		"VND" : 0, "VUV" : 0, "XAF" : 0, "XOF" : 0, "XPF" : 0, 
		"YER" : 0, "ZMK" : 0
	};
	
	
	// optional currencyCode argument?
	if (typeof currencyCode == "string") {
		// user wanted to override the local currency
		this.currencyCode = currencyCode.toUpperCase();
		
		// must override the frac digits too, for some
		// currencies have 0, 2 or 3!
		var numDigits = this.currencyFractionDigits[this.currencyCode];
		if (typeof numDigits != "number")
			numDigits = 2; // default for most currencies
		this.lc.frac_digits = numDigits;
		this.lc.int_frac_digits = numDigits;
	}
	else {
		// use local currency
		this.currencyCode = this.lc.int_curr_symbol.substring(0,3).toUpperCase();
	}
	
	// extract intl. currency separator
	this.intSep = this.lc.int_curr_symbol.charAt(3);
	
	// flag local or intl. sign formatting?
	if (this.currencyCode == this.lc.int_curr_symbol.substring(0,3)) {
		// currency matches the local one? ->
		// formatting with local symbol and parameters
		this.internationalFormatting = false;
		this.curSym = this.lc.currency_symbol;
	}
	else {
		// currency doesn't match the local ->
		
		// do we have an overriding currency symbol?
		if (typeof altIntSymbol == "string") {
			// -> force formatting with local parameters, using alt symbol
			this.curSym = altIntSymbol;
			this.internationalFormatting = false;
		}
		else {
			// -> force formatting with intl. sign and parameters
			this.internationalFormatting = true;
		}
	}
	
	
	/** 
	 * @public
	 *
	 * @description Gets the currency symbol used in formatting.
	 *
	 * @returns {String} The currency symbol.
	 */
	this.getCurrencySymbol = function() {
		
		return this.curSym;
	};
	
	
	/** 
	 * @public
	 *
	 * @description Gets the position of the currency symbol relative to
	 * the amount. Assumes a non-negative amount and local formatting.
	 *
	 * @param {String} intFlag Optional flag to force international
	 * formatting by passing the string "i".
	 *
	 * @returns {Boolean} True if the symbol precedes the amount, false if
	 * the symbol succeeds the amount.
	 */
	this.currencySymbolPrecedes = function(intFlag) {
		
		if (typeof intFlag == "string" && intFlag == "i") {
			// international formatting was forced
			if (this.lc.int_p_cs_precedes == 1)
				return true;
			else
				return false;
			
		}
		else {
			// check whether local formatting is on or off
			if (this.internationalFormatting) {
				if (this.lc.int_p_cs_precedes == 1)
					return true;
				else
					return false;
			}
			else {
				if (this.lc.p_cs_precedes == 1)
					return true;
				else
					return false;
			}
		}
	};
	
	
	/** 
	 * @public
	 *
	 * @description Gets the decimal delimiter (radix) used in formatting.
	 *
	 * @returns {String} The radix character.
	 */
	this.getDecimalPoint = function() {
		
		return this.lc.mon_decimal_point;
	};
	
	
	/** 
	 * @public
	 *
	 * @description Gets the number of fractional digits. Assumes local
	 * formatting.
	 *
	 * @param {String} intFlag Optional flag to force international
	 *        formatting by passing the string "i".
	 *
	 * @returns {integer Number} The number of fractional digits.
	 */
	this.getFractionalDigits = function(intFlag) {
		
		if (typeof intFlag == "string" && intFlag == "i") {
			// international formatting was forced
			return this.lc.int_frac_digits;
		}
		else {
			// check whether local formatting is on or off
			if (this.internationalFormatting)
				return this.lc.int_frac_digits;
			else
				return this.lc.frac_digits;
		}
	};
	
	
	/** 
	 * @public
	 *
	 * @description Formats a monetary amount according to the preset 
	 * locale.
	 *
	 * <pre>
	 * For local currencies the native shorthand symbol will be used for
	 * formatting.
	 * Example:
	 *        locale is en_US
	 *        currency is USD
	 *        -> the "$" symbol will be used, e.g. $123.45
	 *        
	 * For non-local currencies the international ISO-4217 code will be
	 * used for formatting.
	 * Example:
	 *       locale is en_US (which has USD as currency)
	 *       currency is EUR
	 *       -> the ISO three-letter code will be used, e.g. EUR 123.45
	 *
	 * If the currency is non-local, but an alternative currency symbol was
	 * provided, this will be used instead.
	 * Example
	 *       locale is en_US (which has USD as currency)
	 *       currency is EUR
	 *       an alternative symbol is provided - "€"
	 *       -> the alternative symbol will be used, e.g. €123.45
	 * </pre>
	 * 
	 * @param {Number|String} amount The amount to format as currency.
	 * @param {String} [options] Options to modify the formatted output:
	 *       <ul>
	 *           <li>"^"  suppress grouping
	 *           <li>"!"  suppress the currency symbol
	 *           <li>"~"  suppress the currency symbol and the sign (positive or negative)
	 *           <li>"i"  force international sign (ISO-4217 code) formatting
	 *           <li>".n" specify decimal precision
	 *       
	 * @returns The formatted currency amount as string.
	 *
	 * @throws "Error: Invalid amount" on bad amount.
	 */
	this.format = function(amount, options) {
		
		// if the amount is passed as string, check that it parses to a float
		var floatAmount;
		
		if (typeof amount == "string") {
			amount = jsworld._trim(amount);
			floatAmount = parseFloat(amount);
			
			if (typeof floatAmount != "number" || isNaN(floatAmount))
				throw "Error: Amount string not a number";
		}
		else if (typeof amount == "number") {
			floatAmount = amount;
		}
		else {
			throw "Error: Amount not a number";
		}
		
		// get the required precision, ".n" option arg overrides default locale config
		var reqPrecision = jsworld._getPrecision(options);
		
		if (reqPrecision == -1) {
			if (this.internationalFormatting || jsworld._hasOption("i", options))
				reqPrecision = this.lc.int_frac_digits;
			else
				reqPrecision = this.lc.frac_digits;
		}
		
		// round
		floatAmount = Math.round(floatAmount * Math.pow(10, reqPrecision)) / Math.pow(10, reqPrecision);
		
		
		// convert the float amount to string and parse into
		// object with properties integer and fraction
		var parsedAmount = jsworld._splitNumber(String(floatAmount));
		
		// format integer part with grouping chars
		var formattedIntegerPart;
		
		if (floatAmount === 0)
			formattedIntegerPart = "0";
		else
			formattedIntegerPart = jsworld._hasOption("^", options) ?
				parsedAmount.integer :
				jsworld._formatIntegerPart(parsedAmount.integer, 
				                           this.lc.mon_grouping, 
							   this.lc.mon_thousands_sep);
		
		
		// format the fractional part
		var formattedFractionPart;
		
		if (reqPrecision == -1) {
			// pad fraction with trailing zeros accoring to default locale [int_]frac_digits
			if (this.internationalFormatting || jsworld._hasOption("i", options))
				formattedFractionPart =
					jsworld._formatFractionPart(parsedAmount.fraction, this.lc.int_frac_digits);
			else
				formattedFractionPart =
					jsworld._formatFractionPart(parsedAmount.fraction, this.lc.frac_digits);
		}
		else {
			// pad fraction with trailing zeros according to optional format parameter
			formattedFractionPart =
				jsworld._formatFractionPart(parsedAmount.fraction, reqPrecision);
		}
		
		
		// join integer and decimal parts using the mon_decimal_point property
		var quantity;
		
		if (this.lc.frac_digits > 0 || formattedFractionPart.length)
			quantity = formattedIntegerPart + this.lc.mon_decimal_point + formattedFractionPart;
		else
			quantity = formattedIntegerPart;
		
		
		// do final formatting with sign and symbol
		if (jsworld._hasOption("~", options)) {
			return quantity;
		}
		else {
			var suppressSymbol = jsworld._hasOption("!", options) ? true : false;
			
			var sign = floatAmount < 0 ? "-" : "+";
			
			if (this.internationalFormatting || jsworld._hasOption("i", options)) {
				
				// format with ISO-4217 code (suppressed or not)
				if (suppressSymbol)
					return this._formatAsInternationalCurrencyWithNoSym(sign, quantity);
				else
					return this._formatAsInternationalCurrency(sign, quantity);
			}
			else {
				// format with local currency code (suppressed or not)
				if (suppressSymbol)
					return this._formatAsLocalCurrencyWithNoSym(sign, quantity);
				else
					return this._formatAsLocalCurrency(sign, quantity);
			}
		}
	};
	
	
	/** 
	 * @private
	 *
	 * @description Assembles the final string with sign, separator and symbol as local
	 * currency.
	 *
	 * @param {String} sign The amount sign: "+" or "-".
	 * @param {String} q The formatted quantity (unsigned).
	 *
	 * @returns {String} The final formatted string.
	 */
	this._formatAsLocalCurrency = function (sign, q) {
		
		// assemble final formatted amount by going over all possible value combinations of:
		// sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1}
		if (sign == "+") {
			
			// parentheses
			if      (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) {
				return "(" + q + this.curSym + ")";
			}
			else if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) {
				return "(" + this.curSym + q + ")";
			}
			else if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) {
				return "(" + q + " " + this.curSym + ")";
			}
			else if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) {
				return "(" + this.curSym + " " + q + ")";
			}
			
			// sign before q + sym
			else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) {
				return this.lc.positive_sign + q + this.curSym;
			}
			else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + this.curSym + q;
			}
			else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) {
				return this.lc.positive_sign + q + " " + this.curSym;
			}
			else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + this.curSym + " " + q;
			}
			else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) {
				return this.lc.positive_sign + " " + q + this.curSym;
			}
			else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + " " + this.curSym + q;
			}
			
			// sign after q + sym
			else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) {
				return q + this.curSym + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) {
				return this.curSym + q + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) {
				return  q + " " + this.curSym + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) {
				return this.curSym + " " + q + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) {
				return q + this.curSym + " " + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) {
				return this.curSym + q + " " + this.lc.positive_sign;
			}
			
			// sign before sym
			else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) {
				return q + this.lc.positive_sign + this.curSym;
			}
			else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + this.curSym + q;
			}
			else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) {
				return q + " " + this.lc.positive_sign + this.curSym;
			}
			else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + this.curSym + " " + q;
			}
			else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) {
				return q + this.lc.positive_sign + " " + this.curSym;
			}
			else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + " " + this.curSym + q;
			}
			
			// sign after symbol
			else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) {
				return q + this.curSym + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) {
				return this.curSym + this.lc.positive_sign + q;
			}
			else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) {
				return  q + " " + this.curSym + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) {
				return this.curSym + this.lc.positive_sign + " " + q;
			}
			else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) {
				return q + this.curSym + " " + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) {
				return this.curSym + " " + this.lc.positive_sign + q;
			}
			
		}
		else if (sign == "-") {
			
			// parentheses enclose q + sym
			if      (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) {
				return "(" + q + this.curSym + ")";
			}
			else if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) {
				return "(" + this.curSym + q + ")";
			}
			else if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) {
				return "(" + q + " " + this.curSym + ")";
			}
			else if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) {
				return "(" + this.curSym + " " + q + ")";
			}
			
			// sign before q + sym
			else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) {
				return this.lc.negative_sign + q + this.curSym;
			}
			else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + this.curSym + q;
			}
			else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) {
				return this.lc.negative_sign + q + " " + this.curSym;
			}
			else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + this.curSym + " " + q;
			}
			else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) {
				return this.lc.negative_sign + " " + q + this.curSym;
			}
			else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + " " + this.curSym + q;
			}
			
			// sign after q + sym
			else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) {
				return q + this.curSym + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) {
				return this.curSym + q + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) {
				return  q + " " + this.curSym + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) {
				return this.curSym + " " + q + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) {
				return q + this.curSym + " " + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) {
				return this.curSym + q + " " + this.lc.negative_sign;
			}
			
			// sign before sym
			else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) {
				return q + this.lc.negative_sign + this.curSym;
			}
			else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + this.curSym + q;
			}
			else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) {
				return q + " " + this.lc.negative_sign + this.curSym;
			}
			else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + this.curSym + " " + q;
			}
			else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) {
				return q + this.lc.negative_sign + " " + this.curSym;
			}
			else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + " " + this.curSym + q;
			}
			
			// sign after symbol
			else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) {
				return q + this.curSym + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) {
				return this.curSym + this.lc.negative_sign + q;
			}
			else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) {
				return  q + " " + this.curSym + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) {
				return this.curSym + this.lc.negative_sign + " " + q;
			}
			else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) {
				return q + this.curSym + " " + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) {
				return this.curSym + " " + this.lc.negative_sign + q;
			}
		}
		
		// throw error if we fall through
		throw "Error: Invalid POSIX LC MONETARY definition";
	};
	
	
	/** 
	 * @private
	 *
	 * @description Assembles the final string with sign, separator and ISO-4217
	 * currency code.
	 *
	 * @param {String} sign The amount sign: "+" or "-".
	 * @param {String} q The formatted quantity (unsigned).
	 *
	 * @returns {String} The final formatted string.
	 */
	this._formatAsInternationalCurrency = function (sign, q) {
		
		// assemble the final formatted amount by going over all possible value combinations of:
		// sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1}
		
		if (sign == "+") {
			
			// parentheses
			if      (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) {
				return "(" + q + this.currencyCode + ")";
			}
			else if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) {
				return "(" + this.currencyCode + q + ")";
			}
			else if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) {
				return "(" + q + this.intSep + this.currencyCode + ")";
			}
			else if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) {
				return "(" + this.currencyCode + this.intSep + q + ")";
			}
			
			// sign before q + sym
			else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) {
				return this.lc.positive_sign + q + this.currencyCode;
			}
			else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + this.currencyCode + q;
			}
			else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) {
				return this.lc.positive_sign + q + this.intSep + this.currencyCode;
			}
			else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + this.currencyCode + this.intSep + q;
			}
			else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) {
				return this.lc.positive_sign + this.intSep + q + this.currencyCode;
			}
			else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + this.intSep + this.currencyCode + q;
			}
			
			// sign after q + sym
			else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) {
				return q + this.currencyCode + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) {
				return this.currencyCode + q + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) {
				return  q + this.intSep + this.currencyCode + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) {
				return this.currencyCode + this.intSep + q + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) {
				return q + this.currencyCode + this.intSep + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) {
				return this.currencyCode + q + this.intSep + this.lc.positive_sign;
			}
			
			// sign before sym
			else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) {
				return q + this.lc.positive_sign + this.currencyCode;
			}
			else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + this.currencyCode + q;
			}
			else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) {
				return q + this.intSep + this.lc.positive_sign + this.currencyCode;
			}
			else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + this.currencyCode + this.intSep + q;
			}
			else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) {
				return q + this.lc.positive_sign + this.intSep + this.currencyCode;
			}
			else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + this.intSep + this.currencyCode + q;
			}
			
			// sign after symbol
			else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) {
				return q + this.currencyCode + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) {
				return this.currencyCode + this.lc.positive_sign + q;
			}
			else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) {
				return  q + this.intSep + this.currencyCode + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) {
				return this.currencyCode + this.lc.positive_sign + this.intSep + q;
			}
			else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) {
				return q + this.currencyCode + this.intSep + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) {
				return this.currencyCode + this.intSep + this.lc.positive_sign + q;
			}
			
		}
		else if (sign == "-") {
			
			// parentheses enclose q + sym
			if      (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) {
				return "(" + q + this.currencyCode + ")";
			}
			else if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) {
				return "(" + this.currencyCode + q + ")";
			}
			else if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) {
				return "(" + q + this.intSep + this.currencyCode + ")";
			}
			else if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) {
				return "(" + this.currencyCode + this.intSep + q + ")";
			}
			
			// sign before q + sym
			else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) {
				return this.lc.negative_sign + q + this.currencyCode;
			}
			else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + this.currencyCode + q;
			}
			else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) {
				return this.lc.negative_sign + q + this.intSep + this.currencyCode;
			}
			else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + this.currencyCode + this.intSep + q;
			}
			else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) {
				return this.lc.negative_sign + this.intSep + q + this.currencyCode;
			}
			else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + this.intSep + this.currencyCode + q;
			}
			
			// sign after q + sym
			else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) {
				return q + this.currencyCode + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) {
				return this.currencyCode + q + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) {
				return  q + this.intSep + this.currencyCode + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) {
				return this.currencyCode + this.intSep + q + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) {
				return q + this.currencyCode + this.intSep + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) {
				return this.currencyCode + q + this.intSep + this.lc.negative_sign;
			}
			
			// sign before sym
			else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) {
				return q + this.lc.negative_sign + this.currencyCode;
			}
			else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + this.currencyCode + q;
			}
			else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) {
				return q + this.intSep + this.lc.negative_sign + this.currencyCode;
			}
			else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + this.currencyCode + this.intSep + q;
			}
			else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) {
				return q + this.lc.negative_sign + this.intSep + this.currencyCode;
			}
			else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + this.intSep + this.currencyCode + q;
			}
			
			// sign after symbol
			else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) {
				return q + this.currencyCode + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) {
				return this.currencyCode + this.lc.negative_sign + q;
			}
			else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) {
				return  q + this.intSep + this.currencyCode + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) {
				return this.currencyCode + this.lc.negative_sign + this.intSep + q;
			}
			else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) {
				return q + this.currencyCode + this.intSep + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) {
				return this.currencyCode + this.intSep + this.lc.negative_sign + q;
			}
		}
		
		// throw error if we fall through
		throw "Error: Invalid POSIX LC MONETARY definition";
	};
	
	
	/** 
	 * @private
	 *
	 * @description Assembles the final string with sign and separator, but suppress the
	 * local currency symbol.
	 *
	 * @param {String} sign The amount sign: "+" or "-".
	 * @param {String} q The formatted quantity (unsigned).
	 *
	 * @returns {String} The final formatted string
	 */
	this._formatAsLocalCurrencyWithNoSym = function (sign, q) {
		
		// assemble the final formatted amount by going over all possible value combinations of:
		// sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1}
		
		if (sign == "+") {
			
			// parentheses
			if      (this.lc.p_sign_posn === 0) {
				return "(" + q + ")";
			}
			
			// sign before q + sym
			else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) {
				return this.lc.positive_sign + q;
			}
			else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + q;
			}
			else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) {
				return this.lc.positive_sign + q;
			}
			else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + q;
			}
			else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) {
				return this.lc.positive_sign + " " + q;
			}
			else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + " " + q;
			}
			
			// sign after q + sym
			else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) {
				return q + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) {
				return q + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) {
				return  q + " " + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) {
				return q + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) {
				return q + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) {
				return q + " " + this.lc.positive_sign;
			}
			
			// sign before sym
			else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) {
				return q + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + q;
			}
			else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) {
				return q + " " + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + " " + q;
			}
			else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) {
				return q + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + " " + q;
			}
			
			// sign after symbol
			else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) {
				return q + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + q;
			}
			else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) {
				return  q + " " + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + " " + q;
			}
			else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) {
				return q + " " + this.lc.positive_sign;
			}
			else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) {
				return this.lc.positive_sign + q;
			}
			
		}
		else if (sign == "-") {
			
			// parentheses enclose q + sym
			if      (this.lc.n_sign_posn === 0) {
				return "(" + q + ")";
			}
			
			// sign before q + sym
			else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) {
				return this.lc.negative_sign + q;
			}
			else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + q;
			}
			else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) {
				return this.lc.negative_sign + q;
			}
			else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + " " + q;
			}
			else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) {
				return this.lc.negative_sign + " " + q;
			}
			else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + " " + q;
			}
			
			// sign after q + sym
			else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) {
				return q + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) {
				return q + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) {
				return  q + " " + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) {
				return q + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) {
				return q + " " + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) {
				return q + " " + this.lc.negative_sign;
			}
			
			// sign before sym
			else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) {
				return q + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + q;
			}
			else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) {
				return q + " " + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + " " + q;
			}
			else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) {
				return q + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + " " + q;
			}
			
			// sign after symbol
			else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) {
				return q + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + q;
			}
			else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) {
				return  q + " " + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + " " + q;
			}
			else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) {
				return q + " " + this.lc.negative_sign;
			}
			else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) {
				return this.lc.negative_sign + q;
			}
		}
		
		// throw error if we fall through
		throw "Error: Invalid POSIX LC MONETARY definition";
	};
	
	
	/** 
	 * @private
	 *
	 * @description Assembles the final string with sign and separator, but suppress
	 * the ISO-4217 currency code.
	 *
	 * @param {String} sign The amount sign: "+" or "-".
	 * @param {String} q The formatted quantity (unsigned).
	 *
	 * @returns {String} The final formatted string.
	 */
	this._formatAsInternationalCurrencyWithNoSym = function (sign, q) {
		
		// assemble the final formatted amount by going over all possible value combinations of:
		// sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1}
		
		if (sign == "+") {
			
			// parentheses
			if      (this.lc.int_p_sign_posn === 0) {
				return "(" + q + ")";
			}
			
			// sign before q + sym
			else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) {
				return this.lc.positive_sign + q;
			}
			else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + q;
			}
			else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) {
				return this.lc.positive_sign + q;
			}
			else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + this.intSep + q;
			}
			else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) {
				return this.lc.positive_sign + this.intSep + q;
			}
			else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + this.intSep + q;
			}
			
			// sign after q + sym
			else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) {
				return q + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) {
				return q + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) {
				return  q + this.intSep + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) {
				return q + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) {
				return q + this.intSep + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) {
				return q + this.intSep + this.lc.positive_sign;
			}
			
			// sign before sym
			else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) {
				return q + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + q;
			}
			else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) {
				return q + this.intSep + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + this.intSep + q;
			}
			else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) {
				return q + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + this.intSep + q;
			}
			
			// sign after symbol
			else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) {
				return q + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + q;
			}
			else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) {
				return  q + this.intSep + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + this.intSep + q;
			}
			else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) {
				return q + this.intSep + this.lc.positive_sign;
			}
			else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) {
				return this.lc.positive_sign + q;
			}
			
		}
		else if (sign == "-") {
			
			// parentheses enclose q + sym
			if      (this.lc.int_n_sign_posn === 0) {
				return "(" + q + ")";
			}
			
			// sign before q + sym
			else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) {
				return this.lc.negative_sign + q;
			}
			else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + q;
			}
			else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) {
				return this.lc.negative_sign + q;
			}
			else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + this.intSep + q;
			}
			else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) {
				return this.lc.negative_sign + this.intSep + q;
			}
			else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + this.intSep + q;
			}
			
			// sign after q + sym
			else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) {
				return q + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) {
				return q + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) {
				return  q + this.intSep + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) {
				return q + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) {
				return q + this.intSep + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) {
				return q + this.intSep + this.lc.negative_sign;
			}
			
			// sign before sym
			else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) {
				return q + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + q;
			}
			else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) {
				return q + this.intSep + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + this.intSep + q;
			}
			else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) {
				return q + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + this.intSep + q;
			}
			
			// sign after symbol
			else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) {
				return q + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + q;
			}
			else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) {
				return  q + this.intSep + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + this.intSep + q;
			}
			else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) {
				return q + this.intSep + this.lc.negative_sign;
			}
			else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) {
				return this.lc.negative_sign + q;
			}
		}
		
		// throw error if we fall through
		throw "Error: Invalid POSIX LC_MONETARY definition";
	};
};


/** 
 * @class 
 * Class for parsing localised number strings.
 *
 * @public