//
// Copyright ŠLogARun, Inc. All Rights Reserved
//
function GetDuration(timeStr)
{
 // Parse the string
 if(!timeStr) {return 0;}
 var tArr = timeStr.split(':');
 if(!tArr) {return 0;}
 
 // If there is not a semi-colon, then assume the time is in minutes rather than 
 // seconds
 if(tArr.length == 1 && new Number(tArr[0]) <= 120)
 {
	return tArr[0] * 60;
 }
 
 var end = tArr.length - 1;
 var sec = new Number(0);
 for(var i=end; i>=0; i--)
 {
  if(!isNaN(tArr[i]))
  {
   sec += (tArr[i] * Math.pow(60, end - i));
  }
 }
 if(sec == Number.NaN)
 {
  sec = null;
 }
 return sec;
}

function GetPace(distance, distanceType, duration, paceType)
{
	if (isNaN(distance)) { return "Unknown"; }
	var d = new Number(distance);
	var t = (duration.isNan) ? GetDuration(duration) : duration;
	
	if (!d || !t || d.isNan || t.isNan || d <= 0)
	{
		return "Unknown";
	}
	
	var suffix = "";

	// We have a valid
	var pt = parseInt(paceType);
	switch (pt)
	{
		case 1:
			suffix = "/mile"; break;
		case 2:
			suffix = "/km"; break;
		case 3:
			suffix = "mph"; break;
		case 4:
			suffix = "km/h"; break;
		case 5:
			suffix = "/100 yards"; break;
		case 6:
			suffix = "/100 meters"; break;
	}
	
	var paceStr = PrintPace(d, distanceType, t, suffix);
	if (paceStr == "")
	{
		return "unknown";
	}

	return paceStr + suffix;
}

function PrintPace(dist, distType, durationSeconds, suffix)
{
	var isKm = suffix == "/km" || suffix == "km/h";
	var isMile = suffix == "/mile" || suffix == "mph";
	var isYard = suffix == "/100 yards";
	var isMeter = suffix == "/100 meters";
	var convertedDist;
	if (isKm)
	{
		convertedDist = ConvertToKilometers(dist, distType);
	}
	else if (isMile)
	{
		convertedDist = ConvertToMiles2(dist, distType);
	}
	else if (isYard)
	{
		convertedDist = ConvertToYards(dist, distType);
	}
	else
	{
		convertedDist = ConvertToMeters(dist, distType);
	}

	// Now figure out if we're doing mph or min/mile
	var value;
	if (suffix == "/km" || suffix == "/mile")
	{
		value = GetMinPerDist(convertedDist, durationSeconds);
	}
	else if (suffix == "/100 yards" || suffix == "/100 meters")
	{
		value = GetMinPerDist(convertedDist / 100, durationSeconds);
	}
	else
	{
		value = GetDistPerHour(convertedDist, durationSeconds);
	}
	return value;
}

function GetMinPerDist(dist, seconds)
{
	if (seconds == 0)
	{
		return "00:00:00";
	}
	var seconds = seconds / dist;
	return GetTimeString(seconds);
}

function GetDistPerHour(dist, seconds)
{
	if (seconds == 0)
	{
		return "0";
	}

	var mph = (dist * 3600) / seconds;
	return mph.toFixed(2);
}

function CalculatePace(distance, duration)
{
 if(isNaN(distance)) {return "00:00:00";}
 var d = new Number(distance);
 var t = (duration.isNan) ? GetDuration(duration):duration;
 
 if(!d || !t || d.isNan) 
 {
	return "00:00:00";
 }
 
 // Distance = Rate * Time
 var rate = new Date((t*1000) / d);
 if(!rate || isNaN(rate))
 {
  return "00:00:00";
 }
 
 return (PadZeros(rate.getUTCHours(), 2) + ":" + 
  PadZeros(rate.getUTCMinutes(), 2) + ":" + 
  PadZeros(rate.getUTCSeconds(), 2));
}

function GetTimeString(seconds)
{
	if(isNaN(seconds)) {return "00:00:00";}
	var s = seconds % 60;
	var m = (seconds / 60) % 60;
	var h = (seconds / 3600);
	
	var str = new String.Builder();
	if(Math.floor(h)>0) 
	{
		str.append(PadZeros(Math.floor(h), 2) + ":");
	}
	str.appendFormat('{0}:{1}', PadZeros(Math.floor(m), 2), PadZeros(Math.floor(s), 2));
	return str.toString();
}

// This function returns a string padded with leading zeros
function PadZeros(num, totalLen) 
{
  var numStr = num.toString();
  var numZeros = totalLen - numStr.length;
  if (numZeros > 0) 
	{
		for(var i=1;i<=numZeros;i++)
		{
			numStr = "0" + numStr;
		}
  }
  return numStr;
}

function ConvertToMiles(km)
{
	var n = new Number(km);
	return n * 0.621371192;
}

function GetViewableDistanceType(exercise)
{
	if (!exercise)
	{
		return 0;
	}
	return DistanceTypePrefs[exercise.toLowerCase()];
}

function GetViewablePaceType(exercise)
{
	if (!exercise)
	{
		return 0;
	}
	return PaceTypePrefs[exercise.toLowerCase()];
}

function ConvertToDistance(origDist, origDistType, outDistType)
{
	var d = parseFloat(origDist);
	var origDT = parseInt(origDistType);
	var outDT = parseInt(outDistType);

	switch (outDT)
	{
		case 1:
			return ConvertToMiles2(d, origDT);
		case 2:
			return ConvertToKilometers(d, origDT);
		case 3:
			return ConvertToYards(d, origDT);
		case 4:
			return ConvertToMeters(d, origDT);
		default: return d;
	}
}

function ConvertToMiles2(dis, distanceType) 
{
	if (isNaN(dis)) 
	{
		return 0;
	}

	switch(parseInt(distanceType))
	{
		case 1: return dis;
		case 2: return dis * 0.621371192;
		case 3: return dis * 0.0005682;
		case 4: return dis * 0.0006214;
		default: return dis;
	}
}

function ConvertToKilometers(dis, distanceType)
{
	if (isNaN(dis))
	{
		return 0;
	}

	switch (parseInt(distanceType))
	{
		case 1: return dis * 1.609344;
		case 2: return dis;
		case 3: return dis * 0.0009144;
		case 4: return dis * 0.001;
		default: return dis;
	}
}

function ConvertToYards(dis, distanceType)
{
	if (isNaN(dis))
	{
		return 0;
	}

	switch (parseInt(distanceType))
	{
		case 1: return dis * 1760;
		case 2: return dis * 1093.6132983;
		case 3: return dis;
		case 4: return dis * 1.0936133;
		default: return dis;
	}
}

function ConvertToMeters(dis, distanceType)
{
	if (isNaN(dis))
	{
		return 0;
	}

	switch (parseInt(distanceType))
	{
		case 1: return dis * 1609.344;
		case 2: return dis * 1000;
		case 3: return dis * 0.9144;
		case 4: return dis;
		default: return dis;
	}
}

/**************** DayList *********************/
function Log(username, startdate, enddate, useroptions, exercises, shoes, hasWriteAccess)
{
	this.UserName = username;
	this.HasWriteAccess = hasWriteAccess;
	this.StartDate = startdate;
	this.EndDate = enddate;
	this.UserOptions = useroptions;
	this.Exercises = exercises;
	this.Shoes = shoes;
	this.DayItems = new Array();
	this.ReadXml = LG_ReadXml;
	this.Add = LG_Add;
}

function LG_Add(dayItem)
{
	this.DayItems[dayItem.Date] = dayItem;
}

function LG_ReadXml(xmlDoc)
{
	var a = xmlDoc.getElementsByTagName('log')[0];
	this.UserName = a.getAttribute('userName');
	this.HasWriteAccess = a.getAttribute('hasWriteAccess') == 'true';
	this.StartDate = ParseDateXml(a.getAttribute('startDate'));
	this.EndDate = ParseDateXml(a.getAttribute('endDate'));
	
	this.UserOptions = new UserOptions();
	this.UserOptions.ReadXml(xmlDoc);
	
	this.Exercises = new ExerciseList();
	this.Exercises.ReadXml(xmlDoc);
	
	this.Shoes = new ShoeList();
	this.Shoes.ReadXml(xmlDoc);
	
	var days = a.getElementsByTagName('dayItem');
	for(var i=0;i<days.length;i++)
	{
		var di = new DayItem();
		di.Username = this.UserName;
		di.Exercises = this.Exercises;
		di.Shoes = this.Shoes;
		di.ReadXml(days[i]);
		this.Add(di);
	}
}

/**************** UserOptions *****************/
function UserOptions(firstday)
{
	this.FirstDay = firstday;
	this.Ops = new Array();
	this.Add = UsOps_Add;
	this.ReadXml = UsOps_ReadXml;
	this.Find = UsOps_Find;
	this.Count = 0;
}

function UsOps_Find(id)
{
	for(var i=0;i<this.Count; i++)
	{
		if(this.Ops[i][0] == id)
		{
			return this.Ops[i];
		}
	}
	return null;
}

function UsOps_Add(id, text)
{
	this.Ops[this.Count++] = new Array(id, text);
}

function UsOps_ReadXml(xmlDoc)
{
	var a = xmlDoc.getElementsByTagName('userOptions')[0];
	this.FirstDay = a.getAttribute('firstDay');
	
	var ops = a.getElementsByTagName('option');
	for(var i=0;i<ops.length;i++)
	{
		this.Add(ops[i].getAttribute('id'), ops[i].getAttribute('formatText'));
	}
}

/**************** DayItem *********************/
function DayItem(username, date, exercises, health, activities, notes, attachments)
{
	this.Username = username;
	this.Date = date;
	this.Exercises = exercises;
	this.Health = health;
	this.Activities = activities;
	this.Notes = notes;
	this.Attachments = attachments;
	this.Read = DI_Read;
	this.Render = DI_Render;
	this.ReadXml = DI_ReadXml;
	this.WriteXml = DI_WriteXml;
}

function DI_Read(doc)
{
	doc = doc || document;
	this.Health.Read(doc);
	this.Activities.ReadAll(doc);
	this.Notes.Read(doc);
}

function DI_Render(doc)
{
	this.Health.Render(doc);
	this.Exercises.Render(doc);
	this.Activities.Render(doc);
	this.Notes.Render(doc);
	this.Attachments.Render(doc);
}

function DI_ReadXml(xmlDoc)
{	
	// We need to figure out if this is a standalone form or within its parent
	var a;
	if(xmlDoc.firstChild.nodeName == 'runItem')
	{
		a = xmlDoc.getElementsByTagName('runItem')[0];
		this.Username = a.getAttribute('userName');
		this.Exercises = new ExerciseList();
		this.Exercises.ReadXml(a);
	}
	else
	{
		a = xmlDoc;
	}
	
	this.Date = ParseDateXml(a.getAttribute('date'));
	this.Health = new HealthItem();
	this.Health.ReadXml(a);
	this.Activities = new ActivityList();
	this.Activities.ReadXml(a, this.Exercises, this.Shoes);
	this.Notes = new NotesItem();
	this.Notes.ReadXml(a);
	this.Attachments = new AttachmentList();
	this.Attachments.ReadXml(a);
}

function DI_WriteXml(doc)
{
	doc = doc || document;
	this.Read(doc);
	var x = new String.Builder();
	var dt = this.Date;
	if(typeof(dt) != 'date') 
	{
		dt = new Date(dt);
	}
	x.appendFormat('<dayItem userName="{0}" date="{1}">', this.Username, dt.toShortDate());
	x.append(this.Health.WriteXml());
	x.append(this.Activities.WriteXml(this.Exercises));
	x.append(this.Notes.WriteXml());
	x.append('</dayItem>');
	return x.toString();
}

/**************** ExerciseList **********/
function ExerciseList()
{
	this.Items = new Array();
	this.Add = EC_Add;
	this.ReadXml = EC_ReadXml;
	this.Render = EC_Render;
	this.GetID = EC_GetID;
}

function EC_GetID(name)
{
	for(var i=1;i<=this.Items.length;i++)
	{
		if(this.Items[i].Name == name)
		{
			return this.Items[i].ID;
		}
	}
	return null;
}

function EC_Add(ex)
{
	this.Items[ex.ID] = ex;
}

function EC_ReadXml(xmlDoc)
{
	var e = xmlDoc.getElementsByTagName('exercises')[0].childNodes;
	for(var i=0;i<e.length;i++)
	{
		var ei = new ExerciseItem();
		ei.ReadXml(e[i]);
		this.Add(ei);
	}
}

function EC_Render(doc)
{
	doc = doc || document;
	var ul = doc.getElementById('acttypes');
	ul.innerHTML = '';
	var li = doc.createElement('li');
	li.innerHTML = 'Choose one:';
	ul.appendChild(li);
	for(var i=1;i<=this.Items.length;i++)
	{
		if(this.Items[i])
		{
			li = doc.createElement('li');
			li.innerHTML = "<a onclick=\"AddActivity(this, '"+this.Items[i].Type+"')\" href=\"#\">"+this.Items[i].Name+"</a>";
			ul.appendChild(li);
		}
	}
}

/*************** ExerciseItem *****************/
function ExerciseItem(id, name, type, value1, value2)
{
	this.ID = id;
	this.Name = name;
	this.Type = type;
	this.Value1 = value1;
	this.Value2 = value2;
	this.ReadXml = EI_ReadXml;
}

function EI_ReadXml(a)
{
	this.ID = new Number(a.getAttribute('id'));
	this.Name = a.getAttribute('name');
	this.Type = a.getAttribute('type');
	this.Value1 = a.getAttribute('value1');
	this.Value2 = a.getAttribute('value2');
}

/**************** ShoeList *******************/
function ShoeList()
{
	this.Items = new Array();
	this.Count = 0;
	this.Add = SL_Add;
	this.ReadXml = SL_ReadXml;
	this.Render = SL_Render;
	this.WriteXml = function()
	{
		var s = new String.Builder();
		s.append("<shoes>");
		for(var i=0; i<this.Count; i++)
		{
			s.append(this.Items[i].WriteXml());
		}
		s.append("</shoes>");
		return s.toString();
	}
}

function SL_Add(shoe)
{
	this.Items[this.Count++] = shoe;
}

function SL_ReadXml(xmlDoc)
{
	var ele = xmlDoc.getElementsByTagName('shoes');
	if(ele && ele.length > 0)
	{
		var e = ele[0].childNodes;
		for(var i=0;i<e.length;i++)
		{
			var s = new ShoeItem();
			s.ReadXml(e[i]);
			this.Add(s);
		}
	}
}

function SL_Render(selected)
{
	var s = new String.Builder();
	selected = selected || "";
	for(var i=0;i<this.Count;i++)
	{
		if(!this.Items[i].Retired || this.Items[i].ID == selected)
		{
			var clss = '';
			if(this.Items[i].CurrDistance > (this.Items[i].MaxDistance * .8)) 
			{
				clss = 'shoealmost';
			}
			if(this.Items[i].CurrDistance > this.Items[i].MaxDistance) 
			{
				clss = 'shoeused';
			}
			s.appendFormat('<option {2} class="{3}" value="{0}">{1}</option>',
				this.Items[i].ID, this.Items[i].Name, 
				((selected=="" && (this.Items[i].Default))||(this.Items[i].ID.toString() == selected.toString()))?"selected":"",
				clss);
		}
	}
	return s.toString();
}

/**************** ShoeItem ******************/
function ShoeItem(id, name, maxDistance, def, retired, currDistance)
{
	this.ID = id;
	this.Name = name;
	this.PurchaseDate;
	this.CurrDistance = currDistance;
	this.CurrRuns;
	this.MaxDistance = maxDistance;
	this.Default = def;
	this.Retired = retired;
	this.IsDeleted = false;
	this.ReadXml = SI_ReadXml;
	this.WriteXml = function()
	{
		var s = new String.Builder();
		s.append("<shoe ");
		s.appendFormat('id="{0}" ', this.ID);
		s.appendFormat('name="{0}" ', this.Name);
		s.appendFormat('purchaseDate="{0}" ', this.PurchaseDate ? this.PurchaseDate.toShortDate() : '');
		s.appendFormat('maxDistance="{0}" ', this.MaxDistance);
		s.appendFormat('default="{0}" ', this.Default);
		s.appendFormat('retired="{0}" ', this.Retired);
		s.appendFormat('isDeleted="{0}"/>', this.IsDeleted);
		return s.toString();
	}
}

function SI_ReadXml(a)
{
	this.ID = new Number(a.getAttribute('id'));
	this.Name = a.getAttribute('name');
	this.MaxDistance = new Number(a.getAttribute('maxDistance'));
	this.PurchaseDate = ParseDateXml(a.getAttribute('purchaseDate'));
	var d = a.getAttribute('currDistance');
	this.CurrDistance = new Number(d || 0);
	d = a.getAttribute('currRuns');
	this.CurrRuns = new Number(d || 0);
	this.Default = a.getAttribute('default')=="1";
	this.Retired = a.getAttribute('retired')=="1";
}

/**************** HealthItem ******************/
function HealthItem(weight, fat, pulse, sleep)
{
	this.Weight = weight || 0;
	this.Fat = fat || 0;
	this.Pulse = pulse || 0;
	this.Sleep = sleep || 0;
	this.Read = HI_Read;
	this.Render = HI_Render;
	this.ReadXml = HI_ReadXml;
	this.WriteXml = HI_WriteXml;
	this.ToDayTitle = HI_ToDayTitle;
}

function HI_Read(doc)
{
	doc = doc || document;
	this.Weight = new Number(doc.getElementById('weight').value);
	this.Fat = new Number(doc.getElementById('bodyfat').value);
	this.Pulse = new Number(doc.getElementById('pulse').value);
	this.Sleep = new Number(doc.getElementById('sleep').value);
}

function HI_Render(doc)
{
	doc = doc || document;
	
	var weightUnitSpan = doc.getElementById('weight_unit');
	
	// kgs
	if (window.parent.WeightInLbs == 0) {
		// on a fresh page weight is in lbs; convert it to kgs
		if (window.parent.FreshPage) {
			var weightInKgs = this.Weight/2.20462262;
			doc.getElementById('weight').value = weightInKgs.toFixed(1);
		}
		// if not a fresh page, weight is in kgs
		else {
			doc.getElementById('weight').value = this.Weight.toFixed(1);
		}
		weightUnitSpan.innerHTML = '&nbsp;kgs';
	}
	else {
		doc.getElementById('weight').value = this.Weight.toFixed(1);
		weightUnitSpan.innerHTML = '&nbsp;lbs';
	}
		
	doc.getElementById('bodyfat').value = this.Fat;
	doc.getElementById('pulse').value = this.Pulse;
	doc.getElementById('sleep').value =  this.Sleep;
}

function HI_ReadXml(xmlDoc)
{
	var e = xmlDoc.getElementsByTagName('health')[0];
	if(e)
	{
		this.Weight = new Number(e.getAttribute('weight'));
		this.Fat = new Number(e.getAttribute('bodyFat'));
		this.Pulse = new Number(e.getAttribute('pulse'));
		this.Sleep = new Number(e.getAttribute('sleep'));
	}
}

function HI_WriteXml()
{
	//alert("window.parent.WeightInLbs: " + window.parent.WeightInLbs);
	var x = new String.Builder();
	x.appendFormat('<health weight="{0}" weightInLbs="{1}" bodyFat="{2}" pulse="{3}" sleep="{4}" />',
		this.Weight, window.parent.WeightInLbs, this.Fat, this.Pulse, this.Sleep);
	return x.toString();
}

function HI_ToDayTitle(isHtml)
{
	var s = new String.Builder();
	s.appendLine('Body Weight: '+this.Weight + (isHtml ? '<br/>' : ''));
	s.appendLine('Body Fat %: '+this.Fat + (isHtml ? '<br/>' : ''));
	s.appendLine('Resting HR: '+this.Pulse + (isHtml ? '<br/>' : ''));
	s.appendLine('Sleep (hrs): '+this.Sleep + (isHtml ? '<br/>' : ''));
	return s.toString();
}

/********************** Totals *********************************/
function Totals()
{
	this.Items = new Array();
	this.Plus = function(act)
	{
		var itm = this.GetItm(act);
		itm[2] += ConvertToDistance(act.get_value1(), act.get_value4(), GetViewableDistanceType(act.Exercise));
		itm[3] += act.get_value2();
		if(!isNaN(act.get_value3()))
		{
			itm[4] += act.get_value3();
		}
		itm[5] += act.get_value4();
		itm[6] += act.get_value5();
	}
	this.GetItm = function(ex)
	{
		var itm = null;
		for(var i=0;i<this.Items.length;i++)
		{
			if(this.Items[i][0] == ex.Exercise)
			{
				itm = this.Items[i];
				break;
			}
		}
		if(!itm)
		{
			itm = new Array(ex.Exercise, ex.Type, 0, 0, 0, 0, 0);
			this.Items[this.Items.length] = itm;
		}
		return itm;
	}
}

/********************** Activity List ************************/
function ActivityList()
{
	this.Count = 0;
	this.Items = new Array();
	this.Shoes = new ShoeList();
	this.Add = AL_Add;
	this.Remove = AL_Remove;
	this.Render = AL_Render;
	this.ReadAll = AL_ReadAll;
	this.CalcAll = AL_CalcAll;
	this.ReadXml = AL_ReadXml;
	this.WriteXml = AL_WriteXml;
	this.GetRunFromID = AL_GetRunFromID;
	this.Set = AL_Set;
	this.ToMonthlyString = AL_ToMonthlyString;
	this.GetTotals = AL_Totals;
}

function AL_Totals(tot)
{
	for(var i=0;i<this.Count;i++)
	{
		tot.Plus(this.Items[i]);
	}
	return tot;
}

function AL_Set(item)
{
	for(var i=0; i<this.Count; i++)
	{
		if(this.Items[i].Type == item.Type && this.Items[i].ID == item.ID)
		{
			this.Items[i] = item;
			return;
		}
	}
}

function AL_GetRunFromID(id)
{
	if(this.Items)
	{
		for(var i=0;i<this.Count;i++)
		{
			if(this.Items[i].Type == "run" && this.Items[i].ID == id)
			{
				return this.Items[i];
			}
		}
	}
	return null;
}

function AL_Add(itm)
{
	if(itm.Shoes) 
	{
		itm.Shoes = this.Shoes;
	}
	this.Items[this.Count++] = itm;
}

function AL_Remove(id, type, doc)
{
	for(var i=0;i<this.Count;i++)
	{
		if(this.Items[i].Type == type && this.Items[i].ID == id)
		{
			this.Items[i].IsDeleted = true;
		}
	}
	this.Render(doc);
}

function AL_Render(doc)
{
	doc = doc || document;
	var ul = doc.getElementById('actlist');
	ul.innerHTML = "";
	if(this.Items)
	{
		for(var i=0; i<this.Count; i++)
		{
			if(!this.Items[i].IsDeleted)
			{
				var li = doc.createElement("li");
				li.innerHTML = this.Items[i].Render(doc);
				ul.appendChild(li);
				var ddl = doc.getElementById(this.Items[i].Type + ":distanceType" + this.Items[i].ID);
				if (ddl)
				{
					ddl.style.width = "60px";
					ddl.style.float = "left";
					var win = doc.parentWindow || doc.defaultView;
					BuildDropdown(ddl, win.DistanceTypes, this.Items[i].DistanceType);
				}
				li = null;
			}
		}
		this.CalcAll(doc);
	}
}

function AL_CalcAll(doc)
{
	doc = doc || document;
	if(this.Items)
	{
		for(var i=0;i<this.Count;i++)
		{
			if(this.Items[i].Calc && !this.Items[i].IsDeleted)
			{
				this.Items[i].Calc(doc);
			}
		}
	}
}

function AL_ReadAll(doc)
{
	for(var i=0;i<this.Count;i++)
	{
		this.Items[i].Read(doc);
	}
}

function AL_ReadXml(xmlDoc, exercises, shoes)
{
	var r = xmlDoc.getElementsByTagName('runs');
	if(r.length > 0) 
	{
		r = r[0].childNodes;
	}
	var a = xmlDoc.getElementsByTagName('activities');
	if(a.length > 0) 
	{
		a = a[0].childNodes;
	}
	
	if(shoes)
	{
		this.Shoes = shoes;
	}
	else
	{
		this.Shoes = new ShoeList();
		this.Shoes.ReadXml(xmlDoc);
	}
	
	for(var i=0;i<r.length;i++)
	{
		var ri = new RunItem();
		ri.ReadXml(r[i]);
		this.Add(ri);
	}
	for(var i=0;i<a.length;i++)
	{
		var type = exercises.Items[a[i].getAttribute('exerciseID')].Type;
		var ai;
		if(type == 'dsttm') 
		{
			ai = new DistTimeItem();
		}
		else 
		{
			ai = new SetsRepsItem();
		}
		ai.ReadXml(a[i], exercises);
		this.Add(ai);
	}
}

function AL_WriteXml(exercises)
{
	var a = new String.Builder('<activities>');
	for(var i=0;i<this.Count;i++)
	{
		if(!this.Items[i].IsDeleted)
		{
			a.append(this.Items[i].WriteXml(exercises));
		}
	}
	a.append('</activities>');
	return a.toString();
}

function AL_ToMonthlyString(ops)
{
	function info(act, type)
	{
		this.act = act;
		this.type = type;
		this.val1 = '';
		this.val2 = '';
		this.val3 = '';
		this.val4 = '';
		this.tot1 = 0;
		this.tot2 = 0;
		this.tot3 = 0;
		this.tot4 = 0;
	}
	
	var infos = new Array();
	
	for(var i=0;i<this.Count;i++)
	{
		var a = null;
		for(var j=0;j<infos.length;j++)
		{
			if(infos[j].act == this.Items[i].Exercise)
			{
				a = infos[j];
				break;
			}
		}
		
		if(!a)
		{
			a = new info(this.Items[i].Exercise, this.Items[i].Type);
			infos[infos.length] = a;
		}
		if(this.Items[i].get_value1() > 0)
		{
			a.val1 += ('/' + this.Items[i].get_value1());
			if (this.Items[i].get_value4() > 0)
			{
				a.val1 += (' ' + DistanceTypeToShortName(this.Items[i].get_value4()));
			}
		}
		if(this.Items[i].get_value2() > 0)
		{
			a.val2 += ('/' + GetTimeString(this.Items[i].get_value2()));
		}
		if (this.Items[i].get_value3() > 0 || (this.Items[i].get_value3().length > 0 && this.Items[i].get_value3() != "Unknown"))
		{
			a.val3 += ('/' + this.Items[i].get_value3());
		}
		// Convert to viewable data type
		a.tot1 += ConvertToDistance(this.Items[i].get_value1(), this.Items[i].get_value4(), GetViewableDistanceType(a.act));
		a.tot2 += this.Items[i].get_value2();
		if(!isNaN(this.Items[i].get_value3()))
		{
			a.tot3 += this.Items[i].get_value3();
		}
		a.tot4 = this.Items[i].get_value4();
	}
	
	var s = new String.Builder();
	for(var i=0;i<infos.length;i++)
	{
		var inf = infos[i];
		if(ops.Find("TotalDistance") && (inf.type == 'run' || inf.type == 'dsttm') && inf.val1.length > 0)
		{
			s.appendFormat('&nbsp;{0} Dist: {1}', inf.act, inf.val1.substr(1));
			if (new Number(inf.val1.substr(1, inf.val1.indexOf(' ') - 1)) != inf.tot1)
			{
				s.appendFormat('={0} {1}', inf.tot1.toFixed(1), DistanceTypeToShortName(GetViewableDistanceType(inf.act)));
			}
			s.append('<br/>');
		}
		if(ops.Find("TotalDuration") && (inf.type == 'run' || inf.type == 'dsttm') && inf.val2.length > 0)
		{
			s.appendFormat('&nbsp;{0} Time: {1}', inf.act, inf.val2.substr(1));
			if(inf.val2.substr(1) != GetTimeString(inf.tot2))
			{
				s.appendFormat('={0}', GetTimeString(inf.tot2.toFixed(1)));
			}
			s.append('<br/>');
		}
		if(ops.Find("TotalPace") && (inf.type == 'run' || inf.type == 'dsttm') && inf.val3.length > 0)
		{
			s.appendFormat('&nbsp;{0} Pace: {1}<br/>', inf.act, inf.val3.substr(1));
		}
	}
	return s.toString();
}

/**************** Run Item ********************/
function RunItem(id, distance, duration, shoeID, splits, actualDistance)
{
	this.ID = id;
	this.Distance = distance || 0;
	this.Duration = duration || 0;
	this.ActualDistance = actualDistance || 0;
	this.Exercise = "Run";
	this.DistanceType = GetViewableDistanceType(this.Exercise);
	this.ShoeID = shoeID;
	this.Splits = splits;
	this.Type = "run";
	this.IsDeleted = false;
	this.Shoes = new ShoeList();
	this.Read = RI_Read;
	this.Render = RI_Render;
	this.Calc = RI_Calc;
	this.ReadXml = RI_ReadXml;
	this.WriteXml = RI_WriteXml;
	this.get_value1 = function(){return this.ActualDistance;}
	this.get_value2 = function(){return this.Duration;}
	this.get_value3 = function()
	{
		return GetPace(this.ActualDistance, this.DistanceType, this.Duration, GetViewablePaceType(this.Exercise));
	}
	this.get_value4 = function() { return this.DistanceType; }
	this.get_value5 = function() { return this.Distance; }
	this.ToDayTitle = RI_ToDayTitle;
}

function RI_Read(doc)
{
	doc = doc || document;
	var ds = doc.getElementById(this.Type+":distance"+this.ID);
	var tm = doc.getElementById(this.Type+":duration"+this.ID);
	var sh = doc.getElementById(this.Type + ":shoe" + this.ID);
	var dt = doc.getElementById(this.Type + ":distanceType" + this.ID);
	if(ds)
	{
		this.ActualDistance = new Number(ds.value);
		if (isNaN(this.ActualDistance))
		{
			this.ActualDistance = 0;
		}
		
		this.DistanceType = new Number(GetSelectedValue(dt));
		this.Distance = ConvertToMiles2(this.ActualDistance, this.DistanceType);
		this.Duration = GetDuration(tm.value);
	}
	if(sh && sh.selectedIndex >= 0)
	{
		if(sh.selectedIndex < 0) 
		{
			sh.selectedIndex = 0;
		}
		this.ShoeID = new Number(sh.options[sh.selectedIndex].value);
	}
}

function RI_Render()
{
	var str = new String.Builder('<div title="Enter the mileage in decimal form."><span class="hlt">');
	str.append(this.Exercise + '</span> Distance ');
	str.appendFormat('<input onchange="ActChg()" type="text" name="{2}:distance{0}" id="{2}:distance{0}" value="{1}"/></div>', this.ID, this.ActualDistance, this.Type);
	str.appendFormat('<div><select onchange="ActChg()" name="{1}:distanceType{0}" id="{1}:distanceType{0}"></select></div>', this.ID, this.Type);
	str.append('<div title="Enter your duration in an hh:mm:ss format">Duration ');
	str.appendFormat('<input onchange="ActChg()" type="text" name="{2}:duration{0}" id="{2}:duration{0}" value="{1}"/></div>', this.ID, GetTimeString(this.Duration), this.Type);
	if (this.Type == 'run' || this.Type == 'dsttm')
	{
		str.append('<div title="This is an approximate pace given your distance and duration" class="tx">');
		str.appendFormat('<span id="{1}:pace{0}"></span></div>', this.ID, this.Type);
		if (this.Type == 'run')
		{
			str.append('<div title="Click this link to track specific splits of a given workout and also running terrain" class="tx">');
			str.appendFormat('<a onclick="AddSplits({0})" href="#">Splits/Terrain</a></div>', this.ID);
			if (this.Shoes)
			{
				str.appendFormat('<div title="Track the wear on your shoes"><select id="{1}:shoe{0}" name="{1}:shoe{0}">', this.ID, this.Type);
				str.append(this.Shoes.Render(this.ShoeID));
				str.append('</select></div>');
			}
		}
	}
	str.appendFormat('<a onclick="RemoveItem({0},\'{1}\')" class="tx" title="Remove this item from your log" href="#">Remove</a>', this.ID, this.Type);
	return str.toString();
}

function RI_Calc(doc)
{
	doc = doc || document;
	var rt = doc.getElementById(this.Type+":pace"+this.ID);
	var d = this.ActualDistance;
	
	var tp = doc.getElementById(this.Type+":distanceType"+this.ID);
	// If this is a split item and it is in kilometers, there needs to be a conversion
//	if (tp && tp.selectedIndex)
//	{
//		d = ConvertToMiles(d);
//	}
	rt.innerHTML = GetPace(d, this.DistanceType, this.Duration, GetViewablePaceType(this.Exercise));
}

function DTI_Calc(doc)
{
	doc = doc || document;
	var rt = doc.getElementById(this.Type+":pace"+this.ID);
	var d = this.ActualDistance;
	
	rt.innerHTML = GetPace(d, this.DistanceType, this.Duration, GetViewablePaceType(this.Exercise));
}

function RI_ReadXml(a)
{
	this.ID = new Number(a.getAttribute('runNum'));
	this.Distance = new Number(a.getAttribute('distance'));
	this.Duration = GetDuration(a.getAttribute('duration'));
	this.ShoeID = new Number(a.getAttribute('shoeID'));
	this.ActualDistance = new Number(a.getAttribute('actualDistance'));
	this.DistanceType = new Number(a.getAttribute('distanceType'));
	this.Splits = new SplitsList();
	this.Splits.ReadXml(a.childNodes);
}

function RI_WriteXml()
{
	var x = new String.Builder();
	x.appendFormat('<{0} id="{1}" distance="{2}" duration="{3}" shoeID="{4}" actualDistance="{5}" distanceType="{6}">',
		this.Type, this.ID, this.Distance, this.Duration, this.ShoeID, this.ActualDistance, this.DistanceType);
	if(this.Splits)
	{
		x.append(this.Splits.WriteXml());
	}
	x.appendFormat('</{0}>', this.Type);
	return x.toString();
}

function RI_ToDayTitle(isHtml)
{
	var s = new String.Builder();
	s.appendLine(this.Exercise + ' Distance: ' + this.ActualDistance + ' ' + DistanceTypeToShortName(this.DistanceType) + (isHtml ? '<br/>' : ''));
	s.appendLine(this.Exercise + ' Time: ' + GetTimeString(this.Duration) + (isHtml ? '<br/>' : ''));
	s.appendLine(this.Exercise + ' Pace: ' + GetPace(this.ActualDistance, this.DistanceType, this.Duration, GetViewablePaceType(this.Exercise)) + (isHtml ? '<br/>' : ''));
	return s.toString();
}

function DTI_ToDayTitle(isHtml)
{
	var s = new String.Builder();
	s.appendLine(this.Exercise + ' Distance: ' + this.ActualDistance + ' ' + DistanceTypeToShortName(this.DistanceType) + 
		(isHtml ? '<br/>' : ''));
	s.appendLine(this.Exercise + ' Time: ' + GetTimeString(this.Duration) + (isHtml ? '<br/>' : ''));
	s.appendLine(this.Exercise + ' Pace: ' + GetPace(this.ActualDistance, this.DistanceType, this.Duration, GetViewablePaceType(this.Exercise)) + (isHtml ? '<br/>' : ''));
	return s.toString();
}

function DistanceTypeToShortName(dt)
{
	var names = [
	"unk", "mi", "km", "yd", "m"
	];
	return names[dt];
}

/********************** Splits List ************************/
function SplitsList()
{
	this.Count = 0;
	this.Items = new Array();
	this.Shoes = new ShoeList();
	this.Add = SPL_Add;
	this.Remove = AL_Remove;
	this.Render = SPL_Render;
	this.ReadAll = AL_ReadAll;
	this.CalcAll = AL_CalcAll;
	this.ReadXml = SPL_ReadXml;
	this.WriteXml = SPL_WriteXml;
	this.CalcTotals = SPL_CalcTotals;
}

function SPL_CalcTotals(doc)
{
	doc = doc || document;
	var win = doc.parentWindow || doc.defaultView;
	var d = 0, t = 0;
	for(var i = 0;i < this.Count; i++)
	{
		if(!this.Items[i].IsDeleted)
		{
			var tp = doc.getElementById(this.Items[i].Type+":distanceType"+this.Items[i].ID);
			// If this is a split item and it is in kilometers, there needs to be a conversion
			d += ConvertToDistance(this.Items[i].Distance, GetSelectedValue(tp), win.RunItem.DistanceType);
			t += this.Items[i].Duration;
		}
	}
	doc.getElementById('TotDist').innerHTML = d.toFixed(2);
	doc.getElementById('TotDistType').innerHTML = DistanceTypeToShortName(win.RunItem.DistanceType);
	doc.getElementById('TotTime').innerHTML = GetTimeString(t);
	doc.getElementById('TotPc').innerHTML = GetPace(d, win.RunItem.DistanceType, t, GetViewablePaceType(win.RunItem.Exercise));
}

function SPL_Add(split)
{
	this.Items[this.Count++] = split;
}

function SPL_ReadXml(children)
{
	if(children && children.length > 0)
	{
		var s = children[0].childNodes;
		for(var i=0;i<s.length;i++)
		{
			var si = new SplitItem();
			si.ReadXml(s[i]);
			this.Add(si);
		}
	}
}

function SPL_WriteXml()
{
	var a = '<splits>';
	for(var i=0;i<this.Count;i++)
	{
		if(!this.Items[i].IsDeleted)
		{
			a += this.Items[i].WriteXml();
		}
	}
	a += '</splits>';
	return a;
}

function SPL_Render(doc)
{
	doc = doc || document;
	var ul = doc.getElementById('splits');
	ul.innerHTML = "";
	if(this.Items)
	{
		for(var i=0; i<this.Count; i++)
		{
			if(!this.Items[i].IsDeleted)
			{
				var li = doc.createElement("li");
				li.innerHTML = 'Split ' + (i + 1) + ' ' + this.Items[i].Render(doc);
				if(i%2==1) 
				{
					li.className = 'hl';
				}
				ul.appendChild(li);
				var win = doc.parentWindow || doc.defaultView;
				var ddl = doc.getElementById("splits:distanceType" + this.Items[i].ID);
				BuildDropdown(ddl, win.DistanceTypes, this.Items[i].DistanceType);
				ddl = doc.getElementById("splits:trainingType" + this.Items[i].ID);
				BuildDropdown(ddl, win.SplitTypes, this.Items[i].SplitType);
				ddl = doc.getElementById("splits:terrain" + this.Items[i].ID);
				BuildDropdown(ddl, win.TerrainTypes, this.Items[i].TerrainType);
			}
		}
		this.CalcAll(doc);
		this.CalcTotals(doc);
	}
}

/********************** SplitItem ****************************/
function SplitItem(id, distance, duration, distanceType, terrainType, splitType)
{
	this.ID = id;
	this.Distance = distance || 0;
	this.ActualDistance = distance || 0;
	this.Duration = duration || 0;
	this.DistanceType = distanceType || 1;
	
	this.TerrainType = terrainType || 1;
	this.SplitType = splitType || 1;
	this.IsDeleted = false;
	this.Type = 'splits';
	
	this.Read = SPI_Read;
	this.Render = SPI_Render;
	this.Calc = function() { }
	this.ReadXml = SPI_ReadXml;
	this.WriteXml = SPI_WriteXml;
}

function SPI_Render()
{
	var s = new String.Builder('Distance ');
	s.appendFormat('<input onchange="ActChg()" type="text" name="splits:distance{0}" id="splits:distance{0}" value="{1}"/>', this.ID, this.Distance);
	s.appendFormat('<select onchange="ActChg()" name="splits:distanceType{0}" id="splits:distanceType{0}"></select>', this.ID);
	s.appendFormat(' Time <input onchange="ActChg()" type="text" name="splits:duration{0}" id="splits:duration{0}" value="{1}"/>', this.ID, GetTimeString(this.Duration));
	s.appendFormat(' <span id="splits:pace{0}"></span><br/>', this.ID);
	s.append(' Training Type: ');
	s.appendFormat('<select onchange="ActChg()" name="splits:trainingType{0}" id="splits:trainingType{0}"></select>', this.ID);
	s.append(' Terrain: ');
	s.appendFormat('<select onchange="ActChg()" name="splits:terrain{0}" id="splits:terrain{0}"></select>', this.ID);
	s.appendFormat(' <a href="#" onclick="RemoveItem({0}, \'{1}\')">Remove</a>', this.ID, this.Type);
	return s.toString();
}

function SPI_Read(doc)
{
	doc = doc || document;
	var ds = doc.getElementById(this.Type+":distance"+this.ID);
	var tm = doc.getElementById(this.Type+":duration"+this.ID);
	var dt = doc.getElementById(this.Type+":distanceType"+this.ID);
	var tt = doc.getElementById(this.Type+":trainingType"+this.ID);
	var t = doc.getElementById(this.Type+":terrain"+this.ID);
	
	if(ds)
	{
		this.Distance = new Number(ds.value);
		this.ActualDistance = this.Distance;
		this.Duration = GetDuration(tm.value);
		this.DistanceType = new Number(dt.options[dt.selectedIndex].value);
		this.TerrainType = new Number(t.options[t.selectedIndex].value);
		this.SplitType = new Number(tt.options[tt.selectedIndex].value);
	}
}

function SPI_ReadXml(a)
{
	this.ID = new Number(a.getAttribute('id'));
	this.Distance = new Number(a.getAttribute('distance'));
	this.ActualDistance = this.Distance;
	this.Duration = GetDuration(a.getAttribute('duration'));
	this.DistanceType = new Number(a.getAttribute('distanceType'));
	this.TerrainType = new Number(a.getAttribute('terrainType'));
	this.SplitType = new Number(a.getAttribute('splitType'));
}

function SPI_WriteXml()
{
	var x = '<split id="' + this.ID + '" distance="' + this.Distance + '" duration="' + this.Duration;
	x += ('" distanceType="' + this.DistanceType + '" terrainType="' + this.TerrainType);
	x += ('" splitType="' + this.SplitType + '"/>');
	return x;
}

/********************** DistTimeItem *************************/
function DistTimeItem(id, exercise, distance, duration, actualDistance)
{
	this.ID = id;
	this.Exercise = exercise;
	this.Distance = distance || 0;
	this.Duration = duration || 0;
	this.ActualDistance = actualDistance || 0;
	this.DistanceType = GetViewableDistanceType(this.Exercise);
	this.Type = "dsttm";
	this.IsDeleted = false;
	this.Render = RI_Render;
	this.Read = RI_Read;
	this.Calc = DTI_Calc;
	this.ReadXml = DTI_ReadXml;
	this.WriteXml = DTI_WriteXml;
	this.get_value1 = function(){return this.ActualDistance;}
	this.get_value2 = function(){return this.Duration;}
	this.get_value3 = function()
	{
		return GetPace(this.ActualDistance, this.DistanceType, this.Duration, GetViewablePaceType(this.Exercise));
	}
	this.get_value4 = function() { return this.DistanceType; }
	this.get_value5 = function() { return this.Distance; }
	this.ToDayTitle = DTI_ToDayTitle;
}

function DTI_ReadXml(a, exercises)
{
	this.ID = new Number(a.getAttribute('id'));
	var e = new Number(a.getAttribute('exerciseID'));
	this.Exercise = exercises.Items[e].Name;
	this.Distance = new Number(a.getAttribute('value1'));
	this.Duration = GetDuration(a.getAttribute('value2'));
	this.ActualDistance = new Number(a.getAttribute('actualDistance'));
	this.DistanceType = new Number(a.getAttribute('distanceType'));
}

function DTI_WriteXml(exercises)
{
	var x = new String.Builder();
	x.appendFormat('<activity id="{0}" exerciseID="{1}" value1="{2}" value2="{3}" actualDistance="{4}" distanceType="{5}"/>',
		this.ID, exercises.GetID(this.Exercise), this.Distance, this.Duration, this.ActualDistance, this.DistanceType);
	return x.toString();
}

/********************** SetsRepsItem *************************/
function SetsRepsItem(id, exercise, sets, reps)
{
	this.ID = id;
	this.Exercise = exercise;
	this.Sets = sets || 0;
	this.Reps = reps || 0;
	this.Type = "setrep";
	this.IsDeleted = false;
	this.Read = SR_Read;
	this.Render = SR_Render;
	this.ReadXml = SR_ReadXml;
	this.WriteXml = SR_WriteXml;
	this.get_value1 = function(){return this.Sets;}
	this.get_value2 = function(){return this.Reps;}
	this.get_value3 = function() { return this.Sets * this.Reps; }
	this.get_value4 = function() { return 0; }
	this.get_value5 = function() { return 0; }
	this.ToDayTitle = SR_ToDayTitle;
}

function SR_Read(doc)
{
	doc = doc || document;
	var s = doc.getElementById(this.Type+":sets"+this.ID);
	var r = doc.getElementById(this.Type+":reps"+this.ID);
	if(s)
	{
		this.Sets = new Number(s.value);
		this.Reps = new Number(r.value);
	}
}

function SR_Render()
{
	var str = new String.Builder('<div title="Enter the number of sets you completed in decimal form"><span class="hlt">');
	str.append(this.Exercise + '</span> Sets ');
	str.appendFormat('<input type="text" name="{2}:sets{0}" id="{2}:sets{0}" value="{1}"/></div>', this.ID, this.Sets, this.Type);
	str.append('<div title="Enter the number of repetitions you completed in decimal form">Repetitions ');
	str.appendFormat('<input type="text" name="{2}:reps{0}" id="{2}:reps{0}" value="{1}"/></div>', this.ID, this.Reps, this.Type);
	str.appendFormat('<a onclick="RemoveItem({0}, \'{1}\')" class="tx" title="Remove this item from your log" href="#">Remove</a>', this.ID, this.Type);
	return str.toString();
}

function SR_ReadXml(a, exercises)
{
	this.ID = new Number(a.getAttribute('id'));
	var e = new Number(a.getAttribute('exerciseID'));
	this.Exercise = exercises.Items[e].Name;
	this.Sets = new Number(a.getAttribute('value1'));
	this.Reps = new Number(a.getAttribute('value2'));
}

function SR_WriteXml(exercises)
{
	var x = new String.Builder();
	x.appendFormat('<activity id="{0}" exerciseID="{1}" value1="{2}" value2="{3}"/>',
		this.ID, exercises.GetID(this.Exercise), this.Sets, this.Reps);
	return x.toString();	
}

function SR_ToDayTitle(isHtml)
{
	var s = new String.Builder();
	s.appendLine(this.Exercise + ' Sets: ' + this.Sets + (isHtml ? '<br/>' : ''));
	s.appendLine(this.Exercise + ' Reps: ' + this.Reps + (isHtml ? '<br/>' : ''));
	s.appendLine(this.Exercise + ' Total: ' + (this.Sets * this.Reps) + (isHtml ? '<br/>' : ''));
	return s.toString();
}

/********************* Notes Item **********************/
function NotesItem(dayTitle, selectedUser)
{
	this.DayTitle = dayTitle || '';
	this.Items = new Array();
	this.Count = 0;
	this.SelectedUser = selectedUser;
	this.Add = NI_Add;
	this.Set = NI_Set;
	this.Get = NI_Get;
	this.Read = NI_Read;
	this.Render = NI_Render;
	this.ReadXml = NI_ReadXml;
	this.WriteXml = NI_WriteXml;
	this.HasUser = NI_HasUser;
	this.ToDayTitle = NI_ToDayTitle;
	this.HasCoachNote = function()
	{
		return this.Count > 1;
	}
}

function NI_Add(user, display, note)
{
	this.Items[this.Count++] = new Notes(user.toLowerCase(), display, note);
}

function NI_Set(user, note)
{
	for(var i=0;i<this.Count;i++)
	{
		if(user.toLowerCase() == this.Items[i].Username)
		{
			this.Items[i].Note = note;
			return;
		}
	}
}

function NI_Get(user)
{
	for(var i=0;i<this.Count;i++)
	{
		if(user.toLowerCase() == this.Items[i].Username)
		{
			return this.Items[i].Note;
		}
	}
	return '';
}

function NI_HasUser(user)
{
	for(var i=0;i<this.Count;i++)
	{
		if(user.toLowerCase() == this.Items[i].Username)
		{
			return true;
		}
	}
	return false;
}

function NI_Read(doc)
{
	doc = doc || document;
	this.DayTitle = doc.getElementById('daytitle').value;
	this.Set(this.SelectedUser, doc.getElementById('notesTxt').value);
}

function NI_Render(doc)
{
	doc = doc || document;
	// This method needs to write the day title
	// Create the tabs
	// Populate textbox correctly.
	doc.getElementById('daytitle').value = this.DayTitle;
	var ul = doc.getElementById('noteUsers');
	ul.innerHTML = '';
	
	for(var i=0;i<this.Count;i++)
	{
		var li = doc.createElement('li');
		var s = '<a class="{1}" onclick="ChangeTab(\'{2}\')" href="#">{0}</a>';
		li.innerHTML = s.format(this.Items[i].DisplayName,
			(this.SelectedUser.toLowerCase() == this.Items[i].Username) ? "selected" : "",
			this.Items[i].Username);
		ul.appendChild(li);
	}
	
	doc.getElementById('notesTxt').value = this.Get(this.SelectedUser);
}

function NI_ReadXml(xmlDoc)
{
	var n = xmlDoc.getElementsByTagName('notes')[0];
	if(n)
	{
		this.DayTitle = n.getAttribute('dayTitle') || '';
	}
	
	// First make the first tab the requested user
	this.Add(RequestedUser, RequestedDisplayName, '');
	
	if(n && n.childNodes.length > 0)
	{
		n = n.childNodes;
		for(var i=0;i<n.length;i++)
		{
			var note = n[i].textContent || n[i].text;
			note = note == undefined ? '' : note;
			
			var author = n[i].getAttribute('author');
			// If the author is not the requested user add them to the list, otherwise, just set
			// the notes for the given author
			if(!this.HasUser(author))
			{
				this.Add(author, n[i].getAttribute('displayName'), note);
			}
			else
			{
				this.Set(author, note);
			}
		}
	}
	
	// Now we need to see if a user that has only write access to the notes field
	if(!this.HasUser(LoggedInUser) && HasWriteAccess)
	{
		this.Add(LoggedInUser, LoggedInUser, '');
	}
	
	if(!this.SelectedUser) 
	{
		this.SelectedUser = this.Items[0].Username;
	}
}

function NI_WriteXml()
{
	var s = new String.Builder('<notes');
	s.appendFormat(' dayTitle="{0}">', this.DayTitle.xmlEncode());
	for(var i=0;i<this.Count;i++)
	{
		s.appendFormat('<note author="{0}">{1}</note>', this.Items[i].Username.xmlEncode(), this.Items[i].Note.xmlEncode());
	}
	s.append('</notes>');
	return s.toString();
}

function NI_ToDayTitle(isHtml)
{
	var s = new String.Builder();
	for(var i=0;i<this.Count;i++)
	{
		var str = (this.Items[i].Note.length < 100 || isHtml) ? this.Items[i].Note : this.Items[i].Note.substr(0, 100);
		s.appendLine('['+this.Items[i].DisplayName+'] ' + str);
	}
	return s.toString();
}

function Notes(user, display, note)
{
	this.Username = user;
	this.DisplayName = display;
	this.Note = note;
}

/************ AttachementList *****************/
function AttachmentList()
{
	this.Name = name || '';
	this.Items = new Array();
	this.Count = 0;
	this.Add = ATI_Add;
	this.Render = ATI_Render;
	this.Remove = ATI_Remove;
	this.ReadXml = ATI_ReadXml;
}

function ATI_Add(name)
{
	this.Items[this.Count++] = name;
}

function ATI_Remove(name)
{
	this.Items.remove(name);
	this.Count--;
}

function ATI_Render(doc)
{
	doc = doc || document;
	var itm = doc.getElementById('attach');
	var s = new String.Builder();
	if(this.Count > 0)
	{
		s.append('Attachments: ');
	}
	
	var win = doc.parentWindow || doc.defaultView;
	for(var i=0; i<this.Count; i++)
	{
		s.appendFormat('<a href="./ImageView.aspx?username={0}&date={1}&name={2}" target="_blank">{3}</a>',
			RequestedUser.encodeURI(), win.RequestedDate, this.Items[i].encodeURI(), this.Items[i].xmlEncode());
	}
	itm.innerHTML = s.toString();
}

function ATI_ReadXml(xmlDoc)
{
	var n = xmlDoc.getElementsByTagName('image');
	
	if(n)
	{
		for(var i=0;i<n.length;i++)
		{
			var attach = n[i].textContent || n[i].text;
			attach = (attach == undefined) ? '' : attach;
			
			this.Add(attach);
		}
	}
}
