/*
name: workspace.extensions
extends: workspace
*/
function Nz(value, def) {return value != null ? value : def }
function CNull(value) { return value ? value : null }

Boolean.prototype.toBoolean = function() {return this.valueOf() }
Boolean.prototype.toDate = function() { return this.valueOf() ? new Date() : new Date(0) }
Boolean.prototype.toNumber = function (){ return this.valueOf() ? -1 : 0 }
Boolean.prototype.isBoolean = true
Boolean.prototype.isEmpty = function() { return this == false; }
if (!Boolean.prototype._toString) Boolean.prototype._toString = Boolean.prototype.toString
Boolean.prototype.toString = function(s)
{
	if (s)
	{
		var asText = s.split(';')
		return this.valueOf() ? asText[0] : asText[1]
	}
	else
		return this._toString()
}

Number.prototype.toPaddedString = function(length, chr)
{
	var sTemp = Math.abs(this)._toString();
	sTemp = sTemp.padLeft(length, chr)
		
	return (this < 0) ? '-' + sTemp : sTemp;
}
Number.prototype.toBoolean = function() {return this != 0 }
Number.prototype.toDate = function() { return new Date(this) }
Number.prototype.toNumber = function() { return this.valueOf() }
Number.prototype.toFloat = Number.prototype.toNumber
Number.prototype.toInt = function() { return Math.round(this.valueOf()); }
Number.prototype.floor = function() { return Math.floor(this.valueOf()); }
Number.prototype.isNumber = true
Number.prototype.isEmpty = function() { return this == 0; }
Number.prototype._aHex = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
Number.prototype.toHex = function(pad)
{
	var aChars = []
	
	var l = Math.floor(Math.abs(this.valueOf()))
	
	while (l != 0) 
	{
		aChars.unshift(this._aHex[l%16])
		l = Math.floor(l / 16)
	}
	
	return aChars.join("").padLeft(pad, '0')
}
if (!Number.prototype._toString) Number.prototype._toString = Number.prototype.toString
Number.prototype.toString = function(s)
{
	var fmt
	
	if (s && s.isString)
	{
		var asFmt = s.split(';')
		if (asFmt.length > 1)
		{
			if (this < 0 && asFmt[1]) asFmt[0] = asFmt[1]
			if (this == 0 && asFmt[2]) asFmt[0] = asFmt[2]
		}
		fmt = new this.FormatInfo(asFmt[0])
	}
	else if (s)
	{
		if (this < 0 ) fmt = s.negativeFormat
		if (this == 0) fmt = s.zeroFormat
		if (this > 0) fmt = s.positiveFormat
	}
	
	if (fmt)
	{
		if (!fmt.literal)
		{
		
			var dblValue = Math.abs(this)
			if (fmt.percent) dblValue *= 100
			
			var sSign = (this < 0 && !fmt.isNegative) ? '-' : ''
			
			if (fmt.decimalScale > 1)
			{
				dblValue *= fmt.decimalScale
				dblValue /= fmt.scale
				dblValue = Math.round(dblValue)
				
				var lWhole = Math.floor(dblValue / fmt.decimalScale)
				var lFraction = Math.floor(dblValue - (lWhole * fmt.decimalScale))
				
				var sWhole = lWhole._toString().padLeft(fmt.zeroPadding, '0')
				if (fmt.group) sWhole = fnGroupDigits(sWhole)			
			
				var sFraction = lFraction._toString().padLeft(fmt.decimalPlaces, '0')
				
				return sSign + fmt.prefix + sWhole + '.' + sFraction + fmt.suffix
			}
			else
			{
				dblValue /= fmt.scale
				dblValue = Math.round(dblValue)
				
				var sWhole = dblValue._toString().padLeft(fmt.zeroPadding, '0')
				if (fmt.group) sWhole = fnGroupDigits(sWhole)
				
				return sSign + fmt.prefix + sWhole + fmt.suffix
			}			
			
		}
		else
		{
			return fmt.literal
		}
	}
	else
	{
		return this._toString();
	}
	
	function fnGroupDigits(s)
	{
		return s.length > 3 ? fnGroupDigits(s.slice(0, -3)) + ',' + s.slice(-3) : s
	}
}
Number.prototype.FormatInfo = function(s)
{					
	var asFmt = s.split(';')
	
	if (asFmt.length == 1)
	{
		format.call(this, asFmt[0]) 
		this.positiveFormat = this
	}
	else
	{
		this.positiveFormat = new format(asFmt[0], 'isPositive')
	}
		
	this.negativeFormat = (asFmt.length < 2) ? this.positiveFormat : new format(asFmt[1], 'isNegative')
	this.zeroFormat = (asFmt.length < 3) ? this.positiveFormat : new format(asFmt[2], 'isZero')
		
	function format(s, isName)
	{
		var oMatches = s.match(Number.prototype._rgxFormatParser)

		if (oMatches)
		{
			this.decimalPlaces = oMatches[11].length
			this.decimalScale = Math.pow(10, this.decimalPlaces)
			this.percent = oMatches[12].length > 0
			this.scale = Math.pow(1000, oMatches[10].length)
			this.group = (oMatches[4].length + oMatches[6].length + oMatches[8].length) > 0
			this.zeroPadding = oMatches[7].length + oMatches[9].length
			this.prefix = oMatches[1]
			this.suffix = oMatches[13]
			this[isName] = true	
		}	
		else
		{
			this.literal = s
		}	
		oMatches = null
		s = null
	}
	
	this.source = s
	asFmt = null
	s = null
}
Number.prototype._rgxFormatParser = /^([^#0,.-]*)?(-)?(#*)(,)?(#*)(,)?(0*)(,*)(0+)(,*)(?:\.(0+))?(%)?([^0]*)?$/


String.prototype.ltrim = function(chr)
{
	if (typeof(chr) == 'undefined')
		return this.replace(new RegExp('^\\s*'), '');
	else
		return this.replace(new RegExp('^' + chr + '*'), '');
}
String.prototype.rtrim = function(chr)
{ 
	if (typeof(chr) == 'undefined')
		return this.replace(new RegExp('\\s*$'), '');
	else
		return this.replace(new RegExp(chr + '*$'), ''); 
}
String.prototype.trim = function(chr)
{
	return this.rtrim(chr).ltrim(chr);
}
String.prototype.urlEncode = function()
{
	return escape(this).replace(/\+/g,'%2B')
}
String.prototype.padLeft = function(length, chr)
{
	if (typeof(chr) == 'undefined') chr = ' '
	var a = []
	length = length - this.length
	for (var i = 0; i < length; i++) a.push(chr)
	return a.join('') + this
}
String.prototype.padRight = function(length, chr)
{
	if (typeof(chr) == 'undefined') chr = ' '
	var a = []
	length = length - this.length
	for (var i = 0; i < length; i++) a.push(chr)
	return this + a.join('') 
}
String.prototype.toInt = function(radix) { return parseInt(this, radix); }
String.prototype.toFloat = function() { return parseFloat(this); }
String.prototype.toNumber = String.prototype.toFloat
String.prototype.toBoolean = function() { return (this.length != 0 && this != '0' && this.toLowerCase() != 'false'); }
String.prototype.toDate = function(s)
{ 
	if (s)
	{
		var parser = s.isString? new Date.prototype.Parser(s) : s
		return parser.parse(this)
	}
	else
		return new Date(this);
}
String.prototype.isString = true
String.prototype.isEmpty = function() { return this == ''; }
String.prototype.toProperCase = function()
{
	return this.replace(/\b\w/g, function(s) { return s.toUpperCase(); });
}
String.prototype.replaceText = function(search, replacement)
{
	return this.replace(this.getRegExpForText(search), replacement)
}
String.prototype.getRegExpForText = function(search)
{
	search = search.replace(/(\(|\)|\\|\^|\$|\||\?|\:|\[|\]|\.|\{|\}|\+|\*|\=|\!)/g,'\\$1')
	return new RegExp(search, "g")
}
String.prototype.replaceTokens = function(roData, startDelim, endDelim)
{
  startDelim = (startDelim == null) ? '' : startDelim.toString()
  endDelim =  (endDelim == null) ? startDelim : endDelim.toString()
	var midDelim = endDelim + '|' + startDelim

  var asKeys = []
  for (var key in roData) asKeys.push(key)

  var rgx = new RegExp( startDelim + asKeys.join(midDelim) + endDelim, 'g')

  return this.replace(rgx, fnToken)

  function fnToken(s) { return roData[s.slice(startDelim.length,-endDelim.length)]; }
}
String.prototype.test = function(rgx) { return rgx.test(this); }

Array.prototype.shellsort = function(comparator, thisObject)
{
	var i = 0 
	var j = 0 
	var distance = 1
	var tmpValue = null
	if (!comparator) comparator = this.comparator
	
  while (distance <= this.length)
		distance = distance * 3 + 1
  
  while (distance > 1)
  {
		distance = (distance - (distance % 3)) / 3
		for (i = distance; i < this.length; i++)
		{
			if(comparator.call(thisObject, this[i - distance], this[i], 0) == 1)
			{
				j = i
				tmpValue = this[j]
				
				do {
						this[j] = this[j - distance]
						j = j - distance
						if (j < distance) break;
				} while (comparator.call(thisObject, this[j - distance], tmpValue, 0) == 1)

				this[j] = tmpValue
			}
		}
  }     

	return tmpValue != null;
}
Array.prototype.addRange = function(range)
{
	for (var i = 0, length = range.length; i < length; i++)
		this.push(range[i]);
}
Array.prototype.binarySearch = function(key, comparator, thisObject)
{
  var lFirst = 0
  var lLast = this.length - 1
  if (!comparator) comparator = this.comparator
  
  while (lFirst <= lLast)
  {
    var lMiddle = (lFirst + lLast) 
    lMiddle = (lMiddle - (lMiddle % 2)) / 2
    switch (comparator.call(thisObject, this[lMiddle], key, 1))
    {
			case -1: lFirst = lMiddle + 1; break;
			case 1: lLast = lMiddle - 1; break;
			case 0: return lMiddle;
    }
  }

  return lFirst ? -lFirst : null
}
Array.prototype.comparatorType = {sort: 0, search: 1}
Array.prototype.forEach = function(callback, thisObject)
{
	for (var i = 0, length = this.length; i < length; i++)
		callback.call(thisObject, this[i], i, this)
}
Array.prototype.every = function(callback, thisObject)
{
	for (var i = 0, length = this.length; i < length; i++) 
	{
		if (!callback.call(thisObject, this[i], i, this)) return false
	}
	return true
}
Array.prototype.some = function(callback, thisObject)
{
	for (var i = 0, length = this.length; i < length; i++) 
	{
		if (callback.call(thisObject, this[i], i, this)) return true
	}
	return false
}
Array.prototype.map = function(callback, thisObject)
{
	var aRes = new Array()
	for (var i = 0, length = this.length; i < length; i++) 
	{
		aRes.push(callback.call(thisObject, this[i], i, this))
	}	
	return aRes
}
Array.prototype.filter = function(callback, thisObject)
{
	var aRes = new Array()
	for (var i = 0, length = this.length; i < length; i++) 
	{
		if (callback.call(thisObject, this[i], i, this))
			aRes.push(this[i])
	}	
	return aRes
}
Array.prototype.indexOf = function(value)
{
	var index = -1
	
	this.some(fnFind)
	
	return index
	
	function fnFind(arrayValue, arrayIndex)
	{
		if (arrayValue == value) index = arrayIndex
		return index != -1
	}
	
}
Array.prototype.isArray = true
Array.prototype.isEmpty = function() { return this.length == 0; }
Array.prototype.comparator = function(val1, val2) { return val1 == val2 ? 0 : val1 < val2 ? -1 : 1 }
Array.prototype.itemSortComparator = function(val1, val2) { return val1.key == val2.key ? 0 : val1.key < val2.key ? -1 : 1 }
Array.prototype.itemSearchComparator = function(val1, val2) { return val1.key == val2 ? 0 : val1.key < val2 ? -1 : 1 }
Array.prototype.getItem = function(key)
{
	var lPos = this.binarySearch(key, this.itemSearchComparator)
	if (lPos == 0 || lPos > 0) return this[lPos]
}
Array.prototype.putItem = function(key, value)
{
	var lPos = this.binarySearch(key, this.itemSearchComparator)
	if (lPos == null || lPos < 0)
	{
		lPos = -(lPos || 0)
		this.splice(lPos, 0, {key : key, value : value})
		return lPos
	}
	else
	{
		this[lPos].value = value
	}
}
Array.prototype.removeItem = function(key)
{
	var lPos = this.binarySearch(key, this.itemSearchComparator)
	if (lPos == 0 || lPos > 0) return this.splice(lPos, 1)[0]
}
Array.prototype.indexOfItem = function(key)
{
	var lPos = this.binarySearch(key, this.itemSearchComparator)
	return (lPos == 0 || lPos > 0) ? lPos : -1
}

Date.prototype.dayNames = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
Date.prototype.abbreviatedDayNames = ["Sun","Mon","Tues","Wed","Thu","Fri","Sat"];
Date.prototype.monthNames = ["January","February","March","April","May","June","July","August","September","October","November","December"];
Date.prototype.abbreviatedMonthNames = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
Date.prototype.AMDesignator = "AM";
Date.prototype.PMDesignator = "PM";
Date.prototype.shortDatePattern = 'dd/MM/yy'
Date.prototype.longDatePattern = 'dd MMMM yyyy'
Date.prototype.shortTimePattern = 'HH:mm'
Date.prototype.longTimePattern = 'HH:mm:ss'
Date.prototype.fullDateTimePattern = 'dddd d MMMM yyyy HH:mm:ss'
Date.prototype.monthDayPattern = 'd MMMM'
Date.prototype.yearMonthPattern = 'MMMM yyyy'
Date.prototype.isLeapYear = function()
{
 var y=this.getFullYear();
 return (y%4==0&&y%100!=0)||y%400==0;
}
Date.prototype.isWeekEnd = function() { return this.getDay()==0||this.getDay()==6; }
Date.prototype.isWeekDay = function() { return this.getDay()!=0&&this.getDay()!=6; }
Date.prototype.getDaysInMonth = function() { return [31,(this.isLeapYear()?29:28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][this.getMonth()]; }
Date.prototype.getDayName = function() { return this.dayNames[this.getDay()]; }
Date.prototype.toLongMonth = function() { return this.monthNames[this.getMonth()]; }
Date.prototype.toSystemDate = function() { return this.toString('dd MMM yyyy'); }
Date.prototype.toShortDate = function() { return this.toString('d'); }
Date.prototype.toLongDate = function() { return this.toString('D'); }
Date.prototype.truncateDate = function()
{
	var dat = this.toDate()
	dat.setHours(0,0,0,0)
	return dat
}
Date.prototype.truncateTime = function()
{ 
	var dat = this.toDate()
	dat.setFullYear(1970, 1, 1)
	return dat
}
Date.prototype.add = function()
{
	var dat = new Date(this)
	
	if (arguments[0].isTimeSpan) dat.setMilliseconds(dat.getMilliseconds() + arguments[0].totalMilliseconds)
	if (arguments.length == 2) fnAddDatePart.apply(this, arguments)
	
	return(dat);
	
	function fnAddDatePart(sDatePart, iOffset)
	{
		switch(sDatePart)
		{
			case 'Y':
			case 'yyyy': dat.setYear(dat.getYear() + iOffset);	break;
			case 'Q':
			case 'q': dat.setMonth(dat.getMonth() + iOffset * 3); break;
			case 'MM':
			case 'M':
			case 'm': dat.setMonth(dat.getMonth() + iOffset); break; 
			case 'W':
			case 'ww': dat.setDate(dat.getDate() + iOffset * 7); break;
			case 'D':
			case 'y':
			case 'd':
			case 'w': dat.setDate(dat.getDate() + iOffset); break;
			case 'h': dat.setHours(dat.getHours() + iOffset); break;
			case 'mm': dat.setMinutes(dat.getMinutes() + iOffset); break;
			case 'n': dat.setMinutes(dat.getMinutes() + iOffset); break;
			case 's': dat.setSeconds(dat.getSeconds() + iOffset); break;
			case 'ms': dat.setMilliseconds(dat.getMilliseconds() + iOffset);
		}
	}
}
Date.prototype.toString = function(s)
{
	
	if (!s) return this.toUTCString();
	var self = this;

	if (s.length = 1)
	{
		switch(s)
		{
			case "d": s = this.shortDatePattern; break;
			case "D": s = this.longDatePattern; break;
			case "f": s = this.longDatePattern + ' ' + this.shortTimePattern
			case "F": s = this.fullDateTimePattern; break;
			case "g": s = this.shortDatePattern + ' ' + this.shortTimePattern; break;
			case "G": s = this.shortDatePattern + ' ' + this.longTimePattern; break;
			case "m":
			case "M": s = this.monthDayPattern; break;
			case "r":
			case "R": return this.toGMTString();
			case "s": s = 'yyyy-MM-ddTHH:mm:ss'; break;
			case "t": s = this.shortTimePattern; break;
			case "T": s = this.longTimePattern; break;
			case "u": self = new Date(this.toGMTString().slice(0,-3)); s = 'yyyy-MM-dd HH:mm:ssZ'; break;
			case "U": self = new Date(this.toGMTString().slice(0,-3)); s = this.fullDateTimePattern; break;
			case "y":
			case "Y": s = this.yearMonthPattern; break;
		}
	}
	else
		s = s.replace(/\%/g,'')
		
	var fnTextForDatePart = function(s)
	{
		if (s.charAt(0) == '\\') return s.charAt(1)

		switch(s)
		{
			case "hh": return (self.getHours() % 12 ?  self.getHours() % 12 : 12).toString().padLeft(2,'0');
			case "h": return self.getHours() % 12 ?  self.getHours() % 12 : 12;
			case "HH": return self.getHours().toString().padLeft(2,'0');
			case "H":	return self.getHours();
			case "mm": return self.getMinutes().toString().padLeft(2,'0');
			case "m":	return self.getMinutes();
			case "ss": return self.getSeconds().toString().padLeft(2,'0');
			case "s":	return self.getSeconds();
			case "yyyy": return self.getFullYear();
			case "yy": return self.getFullYear().toString().substring(2,4);
			case "y": return self.getYear() % 100
			case "dddd": return self.getDayName();
			case "ddd":	return self.abbreviatedDayNames[self.getDay()];
			case "dd": return self.getDate().toString().padLeft(2,'0');
			case "d":	return self.getDate().toString();
			case "MMMM": return self.toLongMonth();
			case "MMM":	return self.abbreviatedMonthNames[self.getMonth()];
			case "MM": return (self.getMonth()+1).toString().padLeft(2,'0');
			case "M":	return self.getMonth()+1;
			case "t": return (self.getHours()<12?self.AMDesignator:self.PMDesignator).substr(0,1)
			case "tt": return self.getHours()<12?self.AMDesignator:self.PMDesignator;
			case "fff": return self.getMilliseconds();
			case "ff": return Math.floor(self.getMilliseconds() / 10)
			case "f": return Math.floor(self.getMilliseconds() / 100)
		}
	}
 
	return s.replace(/hh|h|HH|H|mm|m|ss|s|yyyy|yy|y|y|dddd|ddd|dd|d|MMMM|MMM|MM|M|tt|t|fff|ff|f|f|\\./g, fnTextForDatePart);
}
Date.prototype.subtract = function(substrahend)
{
	return substrahend ? new TimeSpan((this.valueOf() - substrahend.toDate().valueOf()) * TimeSpan.prototype.ticksPerMillisecond) : null
}
Date.prototype.toNumber = function() {return this.valueOf(); }
Date.prototype.toBoolean = function() { return this.valueOf() != 0; }
Date.prototype.toDate = function() { return new Date(this.valueOf()); }
Date.prototype.hasLocaleTime = function() { return (this.getHours() || this.getMinutes() || this.getSeconds() || this.getMilliseconds()); }
Date.prototype.isDate = true
Date.prototype.isEmpty = function() { return this.valueOf() == 0; }
Date.prototype.StrictParser = function(s)
{
	var msRegEx = '.*?'
	var moDatePart = {}
	var i = 0
	
	this.source = s
	
	s.replace(/hh|h|HH|H|mm|m|ss|s|yyyy|yy|y|y|dd|d|MMMM|MMM|MM|M/g, fnTextForDatePart);
	
	this.regExp = new RegExp(msRegEx, "i")
	msRegEx = null
	
	function parse(s)
	{		
		if (s)
		{
			var args = {Year:1970, Month:0, Date:1, Hours:0, Minutes:0, Seconds:0}
			
			var oMatches = s.match(this.regExp)
			if (oMatches)
			{
				for (var part in moDatePart)
				{
					switch (part) {
					case 'MonthName':
						args.Month = monthNum(oMatches[moDatePart.MonthName])
						break;
					case 'Month':
						args.Month = oMatches[moDatePart.Month].toInt(10) - 1
						break;
					case 'Year':
						args.Year = oMatches[moDatePart.Year].toInt(10)
						args.Year = args.Year < 100 ? (args.Year <= 30 ? args.Year + 2000 : args.Year + 1900) : args.Year
						break;
					default:
						args[part] = oMatches[moDatePart[part]].toInt(10)
					}
				}
				
				return new Date(args.Year, args.Month, args.Date, args.Hours, args.Minutes, args.Seconds)
			}
			return s.toDate()
		}
	}
	this.parse = parse
	
	function fnTextForDatePart(s)
	{
		switch(s)
		{
			case "hh":
			case "h":
			case "HH": 
			case "H": return fnAddPart('[0-9]{1,2}', 'Hours')
			case "mm": 
			case "m":	return fnAddPart('[0-9]{1,2}', 'Minutes')
			case "ss": 
			case "s":	return fnAddPart('[0-9]{1,2}', 'Seconds')
			case "yyyy": return fnAddPart('[0-9]{4,4}', 'Year')
			case "yy": 
			case "y": return fnAddPart('[0-9]{2,2}', 'Year')
			case "dd": 
			case "d":	return fnAddPart('[0-9]{1,2}', 'Date')
			case "MMMM":
			case "MMM":	return fnAddPart(Date.prototype.abbreviatedMonthNames.join('|'), 'MonthName')
			case "MM": 
			case "M":	return fnAddPart('[0-9]{1,2}', 'Month')
		}
	}
 
	function fnAddPart(sRegEx, partName)
	{
		msRegEx += '(' + sRegEx + ').*?'
		moDatePart[partName] = ++i
		return ''
	}
 
	function monthNum(month)
	{
		var month = month.substr(0,3).toLowerCase()
		
		var index = -1
	
		Date.prototype.abbreviatedMonthNames.some(fnFind)
		function fnFind(arrayMonth, arrayIndex)
		{
			if (arrayMonth.toLowerCase() == month.toLowerCase()) index = arrayIndex
			return index != -1
		}
		return index
	}
}
Date.prototype.Parser = Date.prototype.StrictParser
Date.prototype.LooseParser = function()
{
	this.parse = function(s)
	{
		var aParts = s.match(/[A-Za-z]+|\d+/g)

		var oParts = {}
		
		for (var i = 0; i < aParts.length; i++)
			if (/\d+/.test(aParts[i])) aParts[i] = parseInt(aParts[i].ltrim('0'))
		
		for (var i = 0; i < aParts.length; i++)
		{
			if (aParts[i].isNumber)
			{
				if (!attemptDate(aParts[i]))
				{
					if (!attemptMonth(aParts[i]))
					{
						if (!attemptYear(aParts[i])) return NaN
					}
				}
			}
			else
			{
				if (!attemptMonthName(aParts[i]))		
				{
					if (!attemptDayName(aParts[i])) return NaN
				}	
			}
		}  		
		
		return new Date(oParts.year, oParts.month, oParts.date)
		
		
		function attemptDate(value)
		{
			if (oParts.date == null && value > 0 && value <= 31 )
			{
					oParts.date = value
					return true
			}
		}
		
		function attemptMonth(value)
		{
			value -= 1
			
			if (oParts.month == null && value >= 0 && value < 12)
			{
				if ((new Date(2007, value, 1)).getDaysInMonth() < oParts.date)
				{
					if (oParts.year == null)
					{
						oParts.year = oParts.date
						oParts.date = null
						oParts.month = value
						return true
					}
				}
				else
				{
					oParts.month = value
					return true
				}
			}
		}
		
		function attemptYear(value)
		{
			if (oParts.year == null)
			{
					oParts.year = (value > 100) ? value : (value > 79) ? value + 1900 : value + 2000
					return true
			}		
		}
		
		function attemptMonthName(value)
		{
			if (oParts.monthName == null)
			{
				var i = indexOfName(Date.prototype.abbreviatedMonthNames, value)
				if (i == -1) i = indexOfName(Date.prototype.monthNames, value)
				if (i != -1)
				{
					if (oParts.month != null)
					{
						if (oParts.year == null)
						{
							oParts.year == oParts.month
							oParts.month == null
						}
					}
					
					if (attemptMonth(i+1))
					{					
						oParts.monthName == value
						return true
					}
				}	
			}
		}
		
		function attemptDayName(value)
		{
			if (oParts.dayName == null)
			{
				var i = indexOfName(Date.prototype.abbreviatedDayNames, value)
				if (i == -1) i = indexOfName(Date.prototype.dayNames, value)
				if (i != -1)
				{
					oParts.dayName == value
					return true
				}
			}
		}
		
		function indexOfName(aNames, value)
		{
			var index = -1

			aNames.some(fnFind)

			return index

			function fnFind(arrayValue, arrayIndex)
			{
				if (arrayValue.toLowerCase() == value) index = arrayIndex
				return index != -1
			}
		}	
	}
}

function TimeSpan()
{
	if (!arguments[0]) fnTicksConstructor.apply(this, [0])

	if (arguments[0].isString) fnStringConstructor.apply(this, arguments)
	if (arguments[0].isTimeSpan) fnCloneConstructor(arguments[0])
	
	if (this.ticks != null) return
	
	switch (arguments.length) {
		case 1: fnTicksConstructor.apply(this, arguments); break;
		case 2: throw "Unknown signature TimeSpan(arg1, arg2)"; break;
		case 3: fn3IntConstructor.apply(this, arguments); break;
		case 4: fn4IntConstructor.apply(this,arguments); break;
		default: fn5IntConstructor.apply(this,arguments); break;
	}
		
	function fnCloneConstructor(src)
	{
		this.ticks = src.ticks
		this.milliseconds = src.milliseconds
		this.seconds = src.seconds
		this.minutes = src.minutes
		this.hours = src.hours
		this.days = src.days
		
		this.totalMilliseconds = src.totalMilliseconds
		this.totalSeconds = src.totalSeconds
		this.totalMinutes = src.totalMinutes
		this.totalHours = src.totalHours
		this.totalDays = src.totalDays
	}
	
	function fnTicksConstructor(ticks)
	{
		this.ticks = ticks
		
		fnAssignTotals.call(this, ticks)
		
		this.milliseconds = Math.floor(this.totalMilliseconds) %  Math.floor(this.ticksPerSecond / this.ticksPerMillisecond)
		this.seconds = Math.floor(this.totalSeconds) %  Math.floor(this.ticksPerMinute / this.ticksPerSecond)
		this.minutes = Math.floor(this.totalMinutes) %  Math.floor(this.ticksPerHour / this.ticksPerMinute)
		this.hours = Math.floor(this.totalHours) % Math.floor(this.ticksPerDay / this.ticksPerHour)
		this.days = Math.floor(this.totalDays)
	}
	
	function fn3IntConstructor(hours, minutes, seconds)
		{ fn5IntConstructor.call(this, 0, hours, minutes, seconds, 0); }
	
	function fn4IntConstructor(days, hours, minutes, seconds)
		{ fn5Constructor.call(this, days, hours, minutes, seconds, 0) }
	
	function fn5IntConstructor(days, hours, minutes, seconds, milliseconds)
	{
		this.milliseconds = milliseconds
		this.seconds = seconds
		this.minutes = minutes
		this.hours = hours
		this.days = days
	
		this.ticks = milliseconds * this.ticksPerMillisecond
		this.ticks += seconds * this.ticksPerSecond
		this.ticks += minutes * this.ticksPerMinute
		this.ticks += hours * this.ticksPerHour
		this.ticks += days * this.ticksPerDay

		fnAssignTotals.call(this, this.ticks)
	}
	
	function fnAssignTotals(ticks)
	{
		this.totalMilliseconds = ticks / this.ticksPerMillisecond
		this.totalSeconds = ticks / this.ticksPerSecond
		this.totalMinutes = ticks /this.ticksPerMinute
		this.totalHours = ticks / this.ticksPerHour
		this.totalDays = ticks / this.ticksPerDay
	}
	
	function fnStringConstructor(sTimeSpan)
	{
		var oMatches = sTimeSpan.match(this.rgxParse)
		var args = new Array()
		
		if (!oMatches) throw sTimeSpan + " not a valid time span format"
		
		var lSign = oMatches[1] == '-' ? -1 : 1
		
		for (var i = 0 ; i < 5 ; i++)
			args[i] = oMatches[i + 2] ? parseInt(oMatches[i + 2]) * lSign : 0
		
		fn5IntConstructor.apply(this, args)		
	}
}
TimeSpan.prototype.isTimeSpan = true
TimeSpan.prototype.ticksPerMillisecond = 1
TimeSpan.prototype.ticksPerSecond   = 1000
TimeSpan.prototype.ticksPerMinute  = 60000
TimeSpan.prototype.ticksPerHour  = 3600000
TimeSpan.prototype.ticksPerDay  = 86400000
TimeSpan.prototype.valueOf = function() { return this.ticks; }
TimeSpan.prototype.defaultFormat = '-[d ]HH:mm:ss[.fff]'
TimeSpan.prototype.AMDesignator = "AM";
TimeSpan.prototype.PMDesignator = "PM";
TimeSpan.prototype.toString = function(sFmt)
{ 
	if (!sFmt) sFmt = this.defaultFormat
	var self = this
	
	var fnTextForDatePart = function(s)
	{
		if (s.charAt(0) == '\\') return s.charAt(1)
		
		switch(s)
		{
			case "d":	return self.days.toString();
			case "hh": return (self.hours % 12 ?  self.hours % 12 : 12).toString().padLeft(2,'0');
			case "h": return self.hours % 12 ?  self.hours % 12 : 12;
			case "HH": return self.hours.toString().padLeft(2,'0');
			case "H":	return self.hours;
			case "mm": return self.minutes.toString().padLeft(2,'0');
			case "m":	return self.minutes;
			case "ss": return self.seconds.toString().padLeft(2,'0');
			case "s":	return self.seconds;
			case "t": return (self.hours < 12 ? self.AMDesignator : self.PMDesignator).substr(0,1)
			case "tt": return self.hours() < 12 ? self.AMDesignator : self.PMDesignator;
			case "fff": return self.milliseconds;
			case "ff": return Math.floor(self.milliseconds / 10)
			case "f": return Math.floor(self.milliseconds / 100)
			case "-": return self.ticks < 0 ? '-' : ''
			case "+": return self.ticks < '0' ? '-' : '+'
		}
	}
	 
	var sRes = sFmt.replace(/d|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|f|-|\+|\\./g, fnTextForDatePart);
	return sRes.replace(/\[\D*0*\D*?\]/g, '')
}
TimeSpan.prototype.toDate = function() { new Date(this.totalMilliseconds); }
TimeSpan.prototype.isEmpty = function() { return this.ticks == 0; }
TimeSpan.prototype.rgxParse = /^\s*(-|\+)?(?:(\d+)\D+?)?((?:[0-1]\d)|(?:2[0-3])|\d):([0-5]\d|\d)(?::([0-5]\d|\d))?(?:\.(\d{1,3}))?\s*$/
TimeSpan.prototype.add = function(sDatePart, iOffset)
{
	if (arguments[0].isTimeSpan) return new TimeSpan(this.ticks + arguments[0].ticks)
	
	if (arguments[0].isDate) return arguments[0].add(this)

	if (arguments.length == 2) return fnDatePart.apply(this, arguments)
	
	function fnAddDatePart(sDatePart, iOffset)
	{
		var ts = new TimeSpan(this.ticks)
		
		switch(sDatePart)
		{
			case 'W':
			case 'w':
			case 'ww': ts.ticks += 7 * this.ticksPerDay * iOffset; break;
			case 'D':
			case 'd': ts.ticks += this.ticksPerDay * iOffset; break;
			case 'H':
			case 'h': ts.ticks += this.ticksPerHour * iOffset; break;
			case 'mm':
			case 'm':
			case 'M':  
			case 'n': ts.ticks += this.ticksPerMinute * iOffset; break;
			case 'S':
			case 's': ts.ticks += this.ticksPerSecond * iOffset; break;
			case 'ms': ts.ticks += this.ticksPerMillisecond * iOffset;
		}
		return(ts);
	}
}
TimeSpan.prototype.valueOf = function() { return this.ticks; }
TimeSpan.prototype.toTimeSpan = function()
{
	return new TimeSpan(this.days,
		this.hours,
		this.minutes,
		this.seconds,
		this.milliseconds)
}
