var grid = null;
var MAX_DOWNLOAD_SIZE = 5 * 1024 * 1024; // 5 MB

// Committee Tree Data.
var oc_tpas_children = [
	{code : 'oc_tpas_cawg', name: 'Class 2002 Cost Allocation', resource: '/public/committees/oc_tpas_cawg/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'oc_tpas_iitf', name: 'Interconnection Issues TF', resource: '/public/committees/oc_tpas_iitf/meeting_materials/meeting_materials.nyiso.xml', children: null}
];

var oc_children = [
	{code : 'oc_cdas', name: 'Communication &amp; Data Advisory', resource: '/public/committees/oc_cdas/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_espwg', name: 'Electric System Planning WG', resource: '/public/committees/bic_espwg/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'oc_ipsac', name: 'Inter-area Plng Stkhldr Advis Com', resource: '/public/committees/oc_ipsac/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'oc_ipfswg', name: 'Intercon. Proj. Facilities Study WG', resource: '/public/committees/oc_ipfswg/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'oc_rwg', name: 'Restoration Working Group', resource: '/public/committees/oc_rwg/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'oc_rpwg', name: 'Reactive Power WG', resource: '/public/committees/oc_rpwg/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'oc_soas', name: 'System Operations Advisory', resource: '/public/committees/oc_soas/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'oc_spas', name: 'System Protection Advisory', resource: '/public/committees/oc_spas/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'oc_tpas', name: 'Transmission Planning Advisory', resource: '/public/committees/oc_tpas/meeting_materials/meeting_materials.nyiso.xml', children: oc_tpas_children}
];

var bic_spwg_children = [
	{code : 'bic_spwg_aitf', name: 'AMP/ICM Task Force', resource: '/public/committees/bic_spwg_aitf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_spwg_cptf', name: 'Credit Policy Working Group', resource: '/public/committees/bic_spwg_cptf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_spwg_gitf', name: 'Generation Issues TF', resource: '/public/committees/bic_spwg_gitf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_spwg_rtf', name: 'Reserves Task Force', resource: '/public/committees/bic_spwg_rtf/meeting_materials/meeting_materials.nyiso.xml', children: null}
];

var bic_mswg_children = [
	{code : 'bic_mswg_bstf', name: 'Black Start TF', resource: '/public/committees/bic_mswg_bstf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_mswg_crtf', name: 'Congestion Reduction TF', resource: '/public/committees/bic_mswg_crtf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_mswg_idtf', name: 'ICAP Deliverability TF', resource: '/public/committees/bic_mswg_idtf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_mswg_tsctf', name: 'TSC Task Force', resource: '/public/committees/bic_mswg_tsctf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_mswg_vsstf', name: 'Voltage Support Service TF', resource: '/public/committees/bic_mswg_vsstf/meeting_materials/meeting_materials.nyiso.xml', children: null}
];

var bic_icapwg_children = [
	{code : 'bic_icapwg_lftf', name: 'Load Forecasting TF', resource: '/public/committees/bic_icapwg_lftf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_icapwg_raitf', name: 'Resource Adequacy Issues TF', resource: '/public/committees/bic_icapwg_raitf/meeting_materials/meeting_materials.nyiso.xml', children: null}
];

var bic_bawg_children = [
	{code : 'bic_bawg_bdftf', name: 'Billing Data and Format TF', resource: '/public/committees/bic_bawg_bdftf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_bawg_bitf', name: 'Business Intelligence TF', resource: '/public/committees/bic_bawg_bitf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_bawg_fbtf', name: 'Final Bill Task Force', resource: '/public/committees/bic_bawg_fbtf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_bawg_mtf', name: 'Metering Task Force', resource: '/public/committees/bic_bawg_mtf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_bawg_wbrg', name: 'Web-Based Reconcilliation', resource: '/public/committees/bic_bawg_wbrg/meeting_materials/meeting_materials.nyiso.xml', children: null}
];

var bic_espwg_children = [
	{code : 'bic_espwg_iptf', name: 'Inter-regional Planning TF', resource: '/public/committees/bic_espwg_iptf/meeting_materials/meeting_materials.nyiso.xml', children: null}
];

var bic_children = [
	{code : 'bic_bawg', name: 'Billing &amp; Accounting WG', resource: '/public/committees/bic_bawg/meeting_materials/meeting_materials.nyiso.xml', children: bic_bawg_children},
	{code : 'bic_birt', name: 'Billing Issues Resolution Team', resource: '/public/committees/bic_birt/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_bpctf', name: 'Billing and Price Corrections TF', resource: '/public/committees/bic_bpctf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_egcwg', name: 'Electric Gas Coordination WG', resource: '/public/committees/bic_egcwg/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_espwg', name: 'Electric System Planning WG', resource: '/public/committees/bic_espwg/meeting_materials/meeting_materials.nyiso.xml', children: bic_espwg_children},
	{code : 'bic_icapwg', name: 'ICAP Working Group', resource: '/public/committees/bic_icapwg/meeting_materials/meeting_materials.nyiso.xml', children: bic_icapwg_children},
	{code : 'bic_iitf', name: 'Interconnection Issues TF', resource: '/public/committees/bic_iitf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_mdtf', name: 'Market Design Task Force', resource: '/public/committees/bic_mdtf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_miwg', name: 'Market Issues Working Group', resource: '/public/committees/bic_miwg/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_miwg_mmtf', name: 'Market Monitoring TF', resource: '/public/committees/bic_miwg_mmtf/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_mswg', name: 'Market Structure Working Group', resource: '/public/committees/bic_mswg/meeting_materials/meeting_materials.nyiso.xml', children: bic_mswg_children},
	{code : 'bic_prlwg', name: 'Price-Responsive Load WG', resource: '/public/committees/bic_prlwg/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_ram', name: 'Resource Adequacy Model WG', resource: '/public/committees/bic_ram/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'bic_spwg', name: 'Scheduling &amp; Pricing WG', resource: '/public/committees/bic_spwg/meeting_materials/meeting_materials.nyiso.xml', children: bic_spwg_children}
];

var mc_children = [
	{code : 'mc_appeals', name: 'Appeals to the MC', resource: '/public/committees/mc_appeals/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'mc_sector_meetings', name: 'Sector Meetings', resource: '/public/committees/mc_sector_meetings/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'mc_bpwg', name: 'Budget &amp; Priorities Working Group', resource: '/public/committees/mc_bpwg/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'mc_bsp', name: 'Budget Standards &amp; Performance', resource: '/public/committees/mc_bsp/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'mc_bls', name: 'By-Laws Subcommittee', resource: '/public/committees/mc_bls/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'mc_bss', name: 'Board Selection Subcommittee', resource: '/public/committees/mc_bss/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'mc_ls', name: 'Liaison Subcommittee', resource: '/public/committees/mc_ls/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'mc_mpaas', name: 'MP Audit Advisory Subcommittee', resource: '/public/committees/mc_mpaas/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'mc_ppt', name: 'Project Prioritization Team', resource: '/public/committees/mc_ppt/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'mc_srs', name: 'Stay Review Subcommittee', resource: '/public/committees/mc_srs/meeting_materials/meeting_materials.nyiso.xml', children: null},
	{code : 'mc_trs', name: 'Tariff Review Subcommittee', resource: '/public/committees/mc_trs/meeting_materials/meeting_materials.nyiso.xml', children: null}
];

var committeeTreeData = [
	{code : 'mc', name: 'Management Committee', resource: '/public/committees/mc/meeting_materials/meeting_materials.nyiso.xml', children: mc_children},
	{code : 'bic', name: 'Business Issues Committee', resource: '/public/committees/bic/meeting_materials/meeting_materials.nyiso.xml', children: bic_children},
	{code : 'oc', name: 'Operating Committee', resource: '/public/committees/oc/meeting_materials/meeting_materials.nyiso.xml', children: oc_children}
];

var committeeTreeNoOCData = [
	{code : 'mc', name: 'Management Committee', resource: '/public/committees/mc/meeting_materials/meeting_materials.nyiso.xml', children: mc_children},
	{code : 'bic', name: 'Business Issues Committee', resource: '/public/committees/bic/meeting_materials/meeting_materials.nyiso.xml', children: bic_children}
];

/**
 * Retrieves the committeeCode (on the query string as parameter com
 */
function getCommitteeCode() {
  return new queryString().get("com", null);
}

/**
 * Determines the resource given the code.  Recursively searches the committeeTreeData to 
 * find the resource with the specified code.
 */
function getResourceByCode(committeeCode, data) {
	if (committeeCode) {
	  if (data == null) {
	    data = committeeTreeData;
	  }
	  for (var i = 0; i < data.length; i++) {
	    if (data[i].code == committeeCode) {
	      return data[i].resource;
	    }
	    if (data[i].children != null) {
	      var val = getResourceByCode(committeeCode, data[i].children);
	      if (val != null) {
	        return val;
	      }
	    }
	  }
	}
  return null;
}

/**
 * Determines the name of this committee given the committee code by 
 * recursively searching the committeeTreeData.
 */
function getNameByCode(committeeCode, data){
	if (!committeeCode) {
		return null;	
	}
	if (data == null){
		data = committeeTreeData;
	}
	for (var i = 0; i < data.length; i++) {
		if (data[i].code == committeeCode) {
			return data[i].name;
		}
		if (data[i].children != null) {
			var val = getNameByCode(committeeCode, data[i].children); // recursive call
			if (val != null) {
				return val;
			}
		}
	}
	return null;
}  

 function outputOptions(cur, data, level) {
  var indent = "";
  for (var j = 0; j < level; j++) {
    indent += "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
  }
  for (var i = 0; i < data.length; i++) {      
    if (cur == data[i].code) {
      document.write('<option selected="true" value="' + data[i].code + '">' + indent + data[i].name + '</option>');
    } else {
      document.write('<option value="' + data[i].code + '">' + indent + data[i].name + '</option>');
    }     
    if (data[i].children != null) {
      outputOptions(cur, data[i].children, level + 1);
    }
  }
}

/**
 * Comes up with a semi-unique filename for the zip file to be downloaded, creates a form
 * and submits it.
 */
function zipAndDownload() {
	var selectedItems = grid.getSelectionModel().getSelections();	
	var totalSize = 0;
	for (var i = 0; i < selectedItems.length; i++) {
		if (needsSecure(selectedItems[i].get("url"))) {
			Ext.MessageBox.alert('Unable to zip and download', 'You have selected a secure resource.  Secure resources are not available for zip and download.  To access a secure resource, please use the document link.');
			return false;
		}
		/*
		if (!hasRights(selectedItems[i].get("url"))) {
			if (isLoggedIn()) {
				Ext.MessageBox.alert('Unable to zip and download', 'You have selected a secure resource to which you do not have rights.');
			} else {
				Ext.MessageBox.alert('Unable to zip and download', 'You have selected a secure resource.  You must be logged onto your myNYISO account to access this resource.');
			}		
			return false;
		}*/
		/*if (!hasRights(selectedItems[i].get("url"))) {
			Ext.MessageBox.alert('Insufficient Rights', 'You have selected a secure resource.  You must be logged in with special privileges to download this zip file.');			
			return false;
		} */
		totalSize += selectedItems[i].get("size");
	}
	// check the total size of the files and max sure it doesn't exceed the max
	if (totalSize > MAX_DOWNLOAD_SIZE) {
		Ext.MessageBox.alert('Exceeds Maximum Download Size', 'The files you have selected exceed the maximum download size of ' + Ext.util.Format.fileSize(MAX_DOWNLOAD_SIZE) + '. You currently have ' + Ext.util.Format.fileSize(totalSize) + ' selected. Please deselect some files and try again.');
		return false;
	}
		/*
	var zipForm = new Ext.form.BasicForm(null, {	
			url: '/ConnectDaily/zips/' + getCommitteeCode() + '.' + new Date().format("Y.m.d") + '.zip',
			el: 'download-zip',
			method: 'post',
			onSubmit: function() { alert("onsubmit"); },
			reader: 
	});*/
	
	var zipForm = new Ext.form.BasicForm("hidden-form", {
		onSubmit: Ext.emptyFn,
		standardSubmit: true,
		submit: function() { 		
			var dom = this.getEl().dom;	
			// Need to delete all exisiting form elements from previous submits
			if(dom.hasChildNodes()) {        
				while(dom.childNodes.length >= 1 ) {            
					dom.removeChild(dom.firstChild);               
				}     
			}
			for (var i = 0; i < selectedItems.length; i++) {
				var input = document.createElement('INPUT');
      			input.setAttribute('type', 'hidden');
        		input.setAttribute('name', 'url' + i);
        		input.setAttribute('value', selectedItems[i].get("url"));
      			dom.appendChild(input);			
			}
			dom.action = '/ConnectDaily/zips/' + getCommitteeCode() + '.' + new Date().format("Y.m.d") + '.zip';
			dom.submit();	
		}
	});
	zipForm.submit();
	grid.getSelectionModel().deselectAll();
	/*
	var params = {};
	
	for (var i = 0; i < selectedItems.length; i++) {
		params["url" + i] = selectedItems[i].get("url");
	}
			
	Ext.Ajax.request({
		url: '/ConnectDaily/zips/' + getCommitteeCode() + '.' + new Date().format("Y.m.d") + '.zip',
		success: function(resp) { alert("success!"); },
		failure: function() { alert("failure :("); },
		params: params
	});*/

}

function isLoggedIn() {
	return hasCookie("CTSESSION"); 
	// duplicate checks...
	// && hasCookie("ACTSESSION");
	// && window.location.href.indexOf("https") > -1;
}

function userLoggedInSecure() {
 	return isLoggedIn() && (window['isg'] === true);
}

function hasCookie(name) {
	return document.cookie.indexOf(name + "=") != -1;
}
	
function hasRights(url) {
	return (userLoggedInSecure() || !needsSecure(url));		
}

function downloadFile(url) {
	if (hasRights(url)) { 
  	//window.open(url, '_blank', 'height=480,width=640,resizable=yes,scrollbars=yes,toolbar=yes,location=yes', false);   
  	return true;
  } else {
		if (isLoggedIn()) {
			Ext.MessageBox.alert('Insufficient Rights', 'You have selected a secure resource.  You must be logged in to your myNYISO account in order to view this document.');
		} else {
			Ext.MessageBox.alert('Insufficient Rights', 'You have selected a secure resource which requires special permissions to access. Please login to your myNYISO account or contact NYISO to request access to this resource.');
		} 		     
	}
	return false;
}

function needsSecure(url) {
	return (url.indexOf('/secure/') > -1); // secure if it contains "/secure/" in url
}

/**
 * Converts from one date format to another.  If the date formats are not specified,
 * it is assumed that it is converting from Y-m-d to m/d/Y
 */
function convertDate(value, fromFormat, toFormat) {
  try {
	  return Date.parseDate(value, fromFormat? fromFormat : "Y-m-d").format(toFormat? toFormat : "m/d/Y");
	} catch (e) {
		return "N/A";  // invalid date format... shouldn't happen, but also shouldn't be caught here...		
  }
}

/**
 * Converts from one path to another.  Used when loading the document tree from
 * the xml files.  An example conversion "?com=mc&directory=2007-08-29" to 
 * "/public/committees/mc/meeting_materials/2007-08-29/index.nyiso.xml"
 */
function convertPath(value) {
	var nvpairs = value.split('&');
	var com = nvpairs[0].split('=')[1];  // mc
	var dir = nvpairs[1].split('=')[1];  // 2007-08-29

	return '/public/committees/' + com + '/meeting_materials/' + dir + '/index.nyiso.xml';
}

/**
 * Strips off the path portion to get everything between the last sets of "/" and convert to use
 * the mm/dd/yyyy date format.
 * For example, a value of /public/webdocs/committees/mc/meeting_materials/2008-01-09/mc_agenda_010908.pdf
 * would strip off everything, leaving 2008-01-09, which would then be converted to date format mm/dd/yyy
 */
function getDateFromURL(url) {
	// take off last "/" and everything following
	var name = url.substring(0, url.lastIndexOf('/')); // name = /public/webdocs/committees/mc/meeting_materials/2008-01-09
	// take everything after last "/"
	name = name.substring(name.lastIndexOf('/') + 1); // name = 2008-01-09	
	return Date.parseDate(name, "Y-m-d"); 	
}

/**
 * Builds the document stores and renders the document grid
 */
function buildDocumentGrid() {

	// Iterate through all of the nodes, get the array of URLs associated with those nodes.   
	var docDef = Ext.data.Record.create([
			{name: "group", mapping: "url", type: "date", convert: getDateFromURL},
			{name: "name", mapping: "name", sortable: true},
			{name: "upcase_name", mapping: "name", convert: Ext.util.Format.uppercase},
			{name: "upcase_desc", mapping: "para", convert: Ext.util.Format.uppercase},
			{name: "url", mapping: "url", sortable: true},
			{name: "modified", mapping: "date", sortable: true, type: "date", dateFormat: "Y-m-d"},
			{name: "type", mapping: "type", sortable: true},
			{name: "size", mapping: "size", sortable: true, type: 'int', convert: function(val) { return val * 1024; }},
			{name: "desc", mapping: "para"},
			{name: "checked"} /* dummy field */
	]);

	xmlReaderForGrid = new Ext.data.XmlReader({			
			record: "doc",           // The repeated element which contains row information   
			id: "id"                 // The element within the row that provides an ID for the record (optional)
	}, docDef);

	var store = new Ext.data.GroupingStore({ 
			reader: xmlReaderForGrid, 
			sortInfo: { field: "group", direction: "DESC" },
			groupField: "group"
	});		
	
	var gridView = new Ext.grid.GroupingView({
			forceFit: true,
			showGroupName: false,
			hideGroupedColumn: true,
			enableGroupingMenu: false,
			groupTextTpl: '<span title="Click to Select/Deselect all documents on {[values.rs[0].data["group"].format("m/d/Y")]}"> {[values.rs[0].data["group"].format("m/d/Y")]} ({[values.rs.length]} {[values.rs.length > 1 ? "Documents" : "Document"]})</span>'							
	});
	
	// function for building the document hyperlink... used in the following column model
	var renderDocumentLink = function(val, metadata, rec, rowIndex, colIndex, datastore) {	
		var link = '<div class="link';		
		if (needsSecure(rec.get("url"))) {
			if (userLoggedInSecure()) {
				link += ' unlocked title="Secured Resource';
			} else {
				link += ' locked" title="Secured Resource';
			}					
		} 
		link += '"><a  href="' + rec.get('url') + '" target="_blank" onclick="return downloadFile(\'' + rec.get('url') + '\');">';
		if (!val || val.length < 1) {
			val = rec.get("url");
			val = val.substring(val.lastIndexOf('/') + 1); 
		}
		link += Ext.util.Format.ellipsis(val, 65) + '</a></div><div class="desc" wrap="true">' + rec.get('desc') + '</div>';
		return link;
	};
	
  var selModel = new Ext.grid.CheckboxSelectionModel({
  		header: '<div class="x-grid3-hd-checker" id="checkHdr" title="Select/Deselect All"></div>',
  		singleSelect:false
 	});
  		
 	// trickery to suppress needless sorting and updating when all are checked/unchecked
 	selModel.notifyStartSelections = function() {
 		selModel.autoSelection = true;
 	}; 	
 	selModel.notifyEndSelections = function() {
 		selModel.autoSelection = null;
 	};
 	
 	// override the header checking portion
  selModel.onHdMouseDown = function(e, t) {
		if (t.className == 'x-grid3-hd-checker'){
			selModel.notifyStartSelections();
			e.stopEvent();
			var hd = Ext.fly(t.parentNode);
			var isChecked = hd.hasClass('x-grid3-hd-checker-on');
			if (isChecked) {
				hd.removeClass('x-grid3-hd-checker-on');		
				this.clearSelections(); 
			} else {
				hd.addClass('x-grid3-hd-checker-on');
				this.selectAll();
			}
			selModel.notifyEndSelections();     
			var groups = gridView.getGroups();
			for (var i = 0; i < groups.length; i++) {
				gridView.toggleGroupHeaderOnly(groups[i], isChecked);
			}
			grid.notifySelectionUpdate();
    }    
	};	
 	
 	selModel.deselectAll = function() {
 			this.notifyStartSelections();
			this.clearSelections(); 			
			this.notifyEndSelections();     
			var groups = gridView.getGroups();
			for (var i = 0; i < groups.length; i++) {
				gridView.toggleGroupHeaderOnly(groups[i], true);
			}
			this.toggleCheckboxHeader(false);
			grid.notifySelectionUpdate();
 	};

 	selModel.mySelectAll = function() {
 			this.notifyStartSelections();
			this.selectAll(); 			
			this.notifyEndSelections();     
			var groups = gridView.getGroups();
			for (var i = 0; i < groups.length; i++) {
				gridView.toggleGroupHeaderOnly(groups[i], false);
			}
			this.toggleCheckboxHeader(true);
			grid.notifySelectionUpdate();
 	};
 	
 	selModel.toggleCheckboxHeader = function(check) {
		var header = Ext.fly(Ext.getDom("checkHdr").parentNode);
		check = check !== undefined? check : header.hasClass('x-grid3-hd-checker-on');
		if (check == true) {
			header.addClass('x-grid3-hd-checker-on');
		} else {
		  header.removeClass('x-grid3-hd-checker-on');
		}
	}; 	
 	
	var colModel = new Ext.grid.ColumnModel([
			selModel,
			{header: "Group", hidden: true, dataIndex: "group"},
			{header: "Document Name", width: 280, dataIndex: "name", renderer: renderDocumentLink},
			{header: "Modified", width: 80, dataIndex: "modified", renderer: function(v) { return v.format("m/d/Y");}},
			{header: "Type", width: 30, dataIndex: "type", renderer: renderType},
			{header: "Size",  hidden: true, width: 50, dataIndex: "size", renderer: Ext.util.Format.fileSize, align: "right"}
	]);
	
	colModel.defaultSortable = true;
	var selectedFilesLabel = new Ext.Toolbar.TextItem("(No files selected)");
	var downloadFilesButton = new Ext.Toolbar.Button({ text: "Zip and Download", border: true, handler: zipAndDownload, disabled: true, iconCls: 'download-zip-icon' });
	var searchField = new SearchField({ store: store, width: 300 });
	var selectAllButton = new Ext.Toolbar.Button({ text: "Select All", border: true, handler: function() { selModel.mySelectAll(); }});
	var deselectAllButton = new Ext.Toolbar.Button({ text: "Deselect All", border: true, disabled: true, handler: function() { selModel.deselectAll(); }});
	grid = new Ext.grid.GridPanel({
			renderTo: 'committee-docs-grid',
			selModel: selModel,
			store: store,
			colModel: colModel, 
			view: gridView,
			enableColumnHide: false,
			enableColumnMove: false,
			enableDragDrop: false,
			height: 550,
			tbar: [ 'Filter: ', ' ', searchField, '-', new Ext.Toolbar.Fill(), downloadFilesButton, selectedFilesLabel ],
      bbar: [	selectAllButton, deselectAllButton ]
      			
	});

	var loadMask = new Ext.LoadMask(grid.getEl());

	// this will be used to cache docLists
	var docListCache = new Array();
	
	// shortcut to get the # of selected files...
	grid.getSelectionCount = function() {
		return selModel.getCount();
	};

	// used to update visual cues when selection changes
	grid.notifySelectionUpdate = function() {		
		var selectedItems = this.getSelectionCount();
		// check the select all checkbox if all are selected
		var allSelected = (selectedItems == gridView.getRows().length);
		selModel.toggleCheckboxHeader(allSelected);		
		if (allSelected && gridView.getRows().length > 0) {
			selectAllButton.disable();
		} else {
			selectAllButton.enable();
		}
		if (selectedItems == 0) {
			deselectAllButton.disable();
		} else {
			deselectAllButton.enable();
		}
		selectAllButton.toggle(!allSelected);
		deselectAllButton.toggle(allSelected);
		
		// see if any resources selected are locked
		/*
		var selectedRows = grid.getSelectionModel().getSelections();	
		for (var i = 0; i < selectedRows.length; i++) {
			if (!hasRights(selectedRows[i].get("url"))) {
				Ext.get(selectedFilesLabel.getEl()).update('Secured Resources must be downloaded individually (not available for zip).');
				downloadFilesButton.disable();
				return;		// ***EARLY RETURN!!
			}
		}*/
		
		// update the label indicated # of files selected
		switch (selectedItems) {
			case 0:
				Ext.get(selectedFilesLabel.getEl()).update('(No files selected)');
				downloadFilesButton.disable();
				break;
			case 1:
				Ext.get(selectedFilesLabel.getEl()).update('(1 file selected)');
				downloadFilesButton.enable();
				break;
			default:
				Ext.get(selectedFilesLabel.getEl()).update('(' + selectedItems + ' files selected)');
				downloadFilesButton.enable();
				break;
		}
	};
		
	/**
	 * This handles addition of a record to the grid
	 */
	grid.processDocList = function(record, options, success) {
		docListCache[options.url] = record; // cache this for later retrieval
		store.add(record);
		grid.signalUpdateComplete();
	};
	
	grid.signalUpdateStart = function(toBeLoaded) {
		grid.toBeLoaded = toBeLoaded;
		loadMask.show();		
	};
	
	grid.signalUpdateComplete = function() {
	  grid.toBeLoaded--;
	  if (grid.toBeLoaded <= 0) {
	  	searchField.onTrigger2Click(); // trigger the filter to run
	  	store.sort("group", "DESC"); // this should be automatic... bug in ext?
	  	loadMask.hide();	  	  	
	  	grid.notifySelectionUpdate();	
	 	}
	};
	  	
	/**
	 * Updates the document grid to either add or remove documents
	 * @param url - url to the list of documents
	 * @param add - true to add the documents to the grid, false to remove them from the grid
	 */
	grid.updateGrid = function(url, add) {				
		if (add) {
			if (docListCache[url] == null) {	
				var ds = new Ext.data.Store({url: url, reader: xmlReaderForGrid});
				ds.load({callback: this.processDocList, url: url});
			} else {
				store.add(docListCache[url]);
				grid.signalUpdateComplete();
			}
		} else {
			if (docListCache[url] != null) { // can only remove stuff that has been loaded prior
				var recs = docListCache[url];
				if (recs.length > 0) {
					// going to uncheck the selected state of the group in case it is added again
					gridView.toggleGroupHeaderOnly(Ext.get(gridView.getGroupId(recs[0].get("group"))), true);
					for (var v = 0; v < recs.length; v++) {
						store.remove(recs[v]);
					}
				}
			}
			grid.signalUpdateComplete();
		}		
	};
												
  // redefine the toggleGroup function for this particular GroupingView so that it 
	// checks or unchecks all child nodes.  Unfortunately, had to look at the guts of
	// extjs to make this hack...  in case of version upgrade, revisit and retest!!! 
	gridView.toggleGroupHeaderOnly = gridView.toggleGroup;
	gridView.toggleGroup = function(group, expanded) {		
		var deselect = expanded !== undefined? expanded : Ext.fly(Ext.getDom(group)).hasClass('x-grid-group-collapsed');
		this.toggleGroupHeaderOnly(group, expanded);		
		// no easy way to do this... was faster to iterate over indexes than to use query to string match
		// the group and then query for the index of each item
		var rows = store.getCount();
		selModel.notifyStartSelections();
		for (var r = 0; r < rows; r++) {	
		  var rec = store.getAt(r);	
		  if (group.id == gridView.getGroupId(rec.get("group"))) {
		    if (deselect) {
		    	selModel.deselectRow(r); // deselect row... calling directly could circular references		  
		    } else {	  
		    	selModel.selectRow(r, true); // select the row and keep existing selections 
				}	
		  }
		}	
		selModel.notifyEndSelections();
		grid.notifySelectionUpdate();
	};

	gridView.updateGroup = function(group, allSelected) {
		if (allSelected === undefined) {
			allSelected = true;
			var recs = store.query("group", group);
			for (var r = 0; r < recs.length && allSelected; r++) {
				if (recs.items[r].displayed) { // ignore those that aren't displayed
					allSelected = selModel.isIdSelected(recs.items[r].id);
				}
			}						
		}
		gridView.toggleGroupHeaderOnly(Ext.get(gridView.getGroupId(group)), !allSelected);
	};

/*  Enable this part if user wants to prevent the user from being able to check a secure resource for download
	selModel.on("beforerowselect", function(selModel, rowIndex, keepExisting, record) {
		// if the user doesn't have rights, cancel the action
		if (!hasRights(record.get("url"))) {	
			return false;	// cancel the checking...
		}
	});  */
	
	selModel.on("rowselect", function(selModel, rowIndex, record) {	
		if (!selModel.autoSelection) {		
			gridView.updateGroup(record.get("group"));  
			grid.notifySelectionUpdate();
		}
	});
	
	selModel.on("rowdeselect", function(selModel, rowIndex, record) {
		if (!selModel.autoSelection) {
			gridView.updateGroup(record.get("group"), false);  
			grid.notifySelectionUpdate();
		}
	});
	
	grid.render();					
		
}

/**
 * This function loads the tree of document names sorted by date from the xml
 * file located at url. 
 */
function populateTree(url) {

	var xmlReaderForTree = new Ext.data.XmlReader(
		{record: "para", id: "id"}, 
		Ext.data.Record.create([
			{name: "Date", mapping: "link", convert: function(value) { return convertDate(value, "F j, Y"); }},
			{name: "Url", mapping: "link/@url", convert: convertPath}
		])
	);	

	// Set up the tree panel...
	var treePanel = new Ext.tree.TreePanel({
			renderTo: 'committee-tree',
			root: new Ext.tree.TreeNode({text: 'Select Meeting Date(s):', allowDrag: false, allowDrop: false}),
			animate: false,
			enableDD: false,
			border: false,
			rootVisible: false,
			singleExpand: false,
			containerScroll: true,
			ddScroll: true,
			autoScroll: true,
			height: 350
	});

	// create a custom handler for node clicks
	treePanel.handleNodeClick = function(node) {
		if (node.ui.checkbox.checked) {
			node.expand();
		}
		if (node.firstChild != null) {	// has at least 1 child node
		  grid.signalUpdateStart(node.childNodes.length);
	  	// iterate over child nodes and check/uncheck them to match the parent
	  	for (var curnode = node.firstChild; curnode != null; curnode = curnode.nextSibling) {
	  		curnode.ui.checkbox.checked = node.ui.checkbox.checked;		
	  		grid.updateGrid(curnode.attributes.url, curnode.ui.checkbox.checked);
			}    
		} else { // no children, must be a leaf node
			grid.signalUpdateStart(1);
			grid.updateGrid(node.attributes.url, node.ui.checkbox.checked);
			var allChecked = node.ui.checkbox.checked;
			// if all of the children of this node's parent are checked, make sure the parent is checked
			for (var curnode = node.parentNode.firstChild; curnode != null && allChecked; curnode = curnode.nextSibling) {
				allChecked = curnode.ui.checkbox.checked;
			}
			node.parentNode.ui.checkbox.checked = allChecked;
		}
	};

	/**
	 * Opens the specified directory dir, or if dire is null or unable to find a matching node,
	 * opens the first directory in the tree
	 */
	treePanel.openDirectory = function(dir) {
		var parent = null;
		var node = null;
		if (dir) {
			if (dir.length == 4) { // assume year
				node = parent = treePanel.root.findChild("text", dir);				
			} else {
				var nodeName = convertDate(dir);	// directory is "yyyy-mm-dd", nodes are "mm/dd/yyyy"
				if (nodeName != "N/A") { // would occur if date were bad
					var year = nodeName.substring(6, 10); // get year
					parent = treePanel.root.findChild("text", year);
					if (parent) {	 // if we got the parent, look for the child!			
						node = parent.findChild("text", nodeName);
					}
				}					
			}
		}
		if (node == null) { // didn't find the directory or wasn't specified
			if (treePanel.root.firstChild) {
				parent = treePanel.root.firstChild;
				node = parent.firstChild;
			}
		}
		if (parent != null && node != null) {
			parent.expand();			
			node.ui.checkbox.checked = true;
			treePanel.handleNodeClick(node);
		}
	};
	
	/** 
	 * Converts records into nodes on the tree, grouped by year
	 */
	var buildNode = function(record) {
		var date = record.get('Date'); // get the date
		if (date == "N/A") {
			return;		// invalid date!
		}
		var url = record.get('Url'); // get the url
		var year = date.substring(6, 10); // get the year...

		var parentNode = treePanel.root.findChild("text", year);
		if (!parentNode) { // if folder for year doesn't exist, create it!!
		  parentNode = new Ext.tree.TreeNode({text: year, "url": url, checked: false});
			parentNode.addListener({'checkchange': treePanel.handleNodeClick, node: parentNode});
			treePanel.root.appendChild(parentNode);		
		}
		var childNode = new Ext.tree.TreeNode({text: date, url: url, checked: false});
		childNode.addListener({'checkchange': treePanel.handleNodeClick, node: childNode});
		parentNode.appendChild(childNode);
	};
	
	var ds = new Ext.data.Store({
			proxy: new Ext.data.HttpProxy({ url: url }),				
			reader: xmlReaderForTree
	});
	
	// when the data is loaded, add each node to the tree and build the document grid
	ds.load({callback: function (records) {	    		
			for (var i = 0; i < records.length; i++) {
				buildNode(records[i]);
			}
			treePanel.render();
			treePanel.root.expand();
			if (records.length == 0) {
				treePanel.root.setText("No Documents Available");
			}
			buildDocumentGrid();
			// opens either the directory specified, or if nothing is specified, opens the first directory
			treePanel.openDirectory(new queryString().get("directory", null));
  }});    
}

/**
 * Reusable SearchField widget :)
 */
SearchField = Ext.extend(Ext.form.TwinTriggerField, {
    initComponent : function(){
        SearchField.superclass.initComponent.call(this);
        this.on('specialkey', function(f, e){
            if(e.getKey() == e.ENTER){
                this.onTrigger2Click();
            }
        }, this);
    },

    validationEvent: false,
    validateOnBlur: false,
    trigger1Class: 'x-form-clear-trigger',
    trigger2Class: 'x-form-search-trigger',
    hideTrigger1: true,
    width:160,
    paramName: 'query',

		// called when the user clicks the "X" button to clear the search filter
    onTrigger1Click : function() {
        this.setRawValue("");
        this.store.clearFilter();
        this.store.each(function(record) {
        	record.displayed = true;
        });
        this.triggers[0].hide();      
    },

		// called when the user hits Enter or clicks the search button
    onTrigger2Click : function() {
        var v = Ext.util.Format.uppercase(this.getRawValue());
        if(v.length < 1) {
            this.onTrigger1Click();            
        } else {
	        this.store.filterBy(function(record) {     
	        	record.displayed = record.get('upcase_name').indexOf(v) != -1 
	        			|| record.get('upcase_desc').indexOf(v) != -1;
	        	return record.displayed;
	       	});       	
	        this.triggers[0].show();
	     	}
    }
});

Ext.onReady(function() {
	var committeeCode = getCommitteeCode();
	if (committeeCode && committeeCode.length > 0) {
  	populateTree(getResourceByCode(committeeCode));
  } 
});
