// ------------------------------------------------
// DirApp.js
// Author:	jpotter@troupscreeksoftware.com
// Date:	Oct 26, 2006
// Desc:	Directory Application 
// Copyright 2006 Troups Creek Software LLC
// ------------------------------------------------
// Represents the TCS 'Directory' product application that contains a map, a set of 
// map markers, and two lists ('all' and 'current' [or 'now showing']). Users may
// select markers or items from either of the lists. As the map moves, the 'current'
// list changes to reflect the markers that are showing on the map.
// -----------------------------------------
// DirectoryApplication::DirectoryApplication
function DirectoryApplication()
{
	// -------------------------------------------------------------------------------------------
	// presentation-related data
	this.tvObj = null;				// DHTMLSuite Tab View
	this.accessObj = null;			// DHTMLSuite modal message object
	this.editConfigWindow = null;	// window used for editing site configuration
	this.itemEntryWindow = null;	// window used for adding/editing items
	this.uploadWindow = null;		// window used for uploading photos
	this.logWindow = null;			// (unseen) window used for logging clicks
	this.helpWindow = null;			// window used for displaying help text
	this.aboutWindow = null;		// window used for displaying about text
	// data processing-related data
	this.controller = null;			// our flow controller
	this.category = "";				// string of categories to show
	this.categoryAllIndex = 0		// index of the "All" category (i.e. last category)
	this.itemToOpen = 0;			// id for the marker item to open
	this.openedId = 0;				// id for the marker item that IS open
	this.uploadId = 0;				// id associated with image upload
	this.isEditOk = false;			// are we capable of edit item mode?
	// query string-related data
	this.isAdminMode = false;		// page to be loaded in admin mode?
	this.startLoc = "";				// page to be loaded at this starting location?
	// browser-related data
	this.isW3 = false;				// is it WC3 compliant?
	this.isIE5min = false;			// is it Internet Explorer V5 or better?
	this.isFF = false;				// is it Mozilla Firefox?
	// -------------------------------------------------------------------------------------------
	//            B A S I C   A P P L I C A T I O N   B E H A V I O R
	// -------------------------------------------------------------------------------------------
	// Methods, mostly utility-like, that support the basic application.

	// -----------------------------------------
	// DirectoryApplication::initApp
	this.initApp = function() 
	{	
		// initialize objects that we are going to need right away
		gMsg.init();									// messaging utility
		this.checkQuery();								// process the query string
		// throw up the default ads, if necessary
		this.showDefaultAds();
		// Marker List tabs
		this.tvObj = new DHTMLSuite.tabView();
		this.tvObj.setParentId('markerlist');
		this.tvObj.setTabTitles(Array('Selected','Showing'));
		this.tvObj.setIndexActiveTab(0);
		var innerWidth = document.getElementById("ListA").offsetWidth;
		var innerHeight = document.getElementById("ListA").offsetHeight;
		this.tvObj.setWidth(innerWidth + 10);
		this.tvObj.setHeight(innerHeight + 25);
		this.tvObj.init();
		// start up the flow controller
		this.controller = new DirectoryController();	// application flow controller
		// kick off the app flow
		this.triggerFlow(eNone);						// start the app
	}

	// -----------------------------------------
	// DirectoryApplication::checkQuery
	this.checkQuery = function() 
	{	// check for admin capability and starting location based on query string
		this.isAdminMode = false;		// init everything
		this.startLoc = "";
		var queryStr = document.location.search;
		if(queryStr != "")
		{	// query string exists
			var sdelim, edelim, value;
			var admin = queryStr.indexOf("admin",1);				// skip the leading "?"
			// admin mode is necessary to be able to access admin functions
			if(admin > -1)
			{	// admin mode specified
				sdelim = queryStr.indexOf("=",admin);				// first '=' after admin
				edelim = queryStr.indexOf("&",admin);				// first '&' after admin
				if(edelim < 0)
				{	// no end, just use length
					edelim = queryStr.length;
				}
				if(sdelim > 0 && edelim > 0 && edelim > sdelim)
				{
					value = queryStr.substring(sdelim+1, edelim);
					if(value == "on")
					{	// set admin mode on
						this.isAdminMode = true;
					}
				}
			}
			// start location is necessary to be able to start at a predetermined location other than
			// the default location set in admin mode
			var start = queryStr.indexOf("start",1);				// skip the leading "?"
			if(start > -1)
			{	// start location specified
				sdelim = queryStr.indexOf("=",start);				// first '=' after start
				edelim = queryStr.indexOf("&",start);				// first '&' after start
				if(edelim < 0)
				{	// no end, just use length
					edelim = queryStr.length;
				}
				if(sdelim > 0 && edelim > 0 && edelim > sdelim)
				{
					value = queryStr.substring(sdelim+1, edelim);
					this.startLoc = value;
				}
			}
		}
	}

	// -----------------------------------------
	// DirectoryApplication::checkBrowser
	this.checkBrowser = function()
	{	// check the browser environment
		var bV = 0;
		var bN = navigator.appName;
		var uA = navigator.userAgent;		// may contain "Firefox" for that browser
		if(bN == "Microsoft Internet Explorer")
		{	// check for IE version 5 or better
			var offset = uA.indexOf("MSIE");
			bV = parseFloat(uA.substring(offset+5, uA.indexOf(";",offset)));
			this.isIE5min = (bV >= 5) ? true : false;
		}
		else if(bN == "Netscape")
		{	// check for FireFox version 1.5 or better
			var offset = uA.indexOf("Firefox");
			this.isFF = (offset >= 0) ? true : false;
			if(this.isFF)
			{	// it *is* firefox, but what is the version? it has got to be 1.5 or better to qualify
				bV = parseFloat(uA.substring(offset+8, offset+11));
				this.isFF = (bV >= 1.5) ? true : false;
			}
		}
		this.isW3 = (document.getElementById) ? true : false;
		if(!this.isIE5min && !this.isFF)
		{
			gMsg.displayMsg(4,"This application may not work correctly with your browser version. It works best with Microsoft IE V5.0 or newer or with Firefox V1.5 or newer.");
		}
	}

	// -----------------------------------------
	// DirectoryApplication::showDefaultAds
	this.showDefaultAds = function()
	{	// test for ad elements and if found, display the default ad for each
		var testImgElement = document.getElementById("bannerimg");
		if(testImgElement)
			{testImgElement.src = "Images/default-f.gif";}
		testImgElement = document.getElementById("tileimg1");
		if(testImgElement)
			{testImgElement.src = "Images/default-r.gif";}
		testImgElement = document.getElementById("tileimg2");
		if(testImgElement)
			{testImgElement.src = "Images/default-r.gif";}
		testImgElement = document.getElementById("tileimg3");
		if(testImgElement)
			{testImgElement.src = "Images/default-r.gif";}
		testImgElement = document.getElementById("skyimg");
		if(testImgElement)
			{testImgElement.src = "Images/default-s.gif";}
		testImgElement = document.getElementById("cardimg1");
		if(testImgElement)
			{testImgElement.src = "Images/default-c.gif";}
		testImgElement = document.getElementById("cardimg2");
		if(testImgElement)
			{testImgElement.src = "Images/default-c.gif";}
	}
	
	// -----------------------------------------
	// DirectoryApplication::triggerFlow
	this.triggerFlow = function(ev) 
	{	// tell the flow controller what just happened & let it decide what to do next
		this.controller.triggerFlow(ev);
	}

	// -----------------------------------------
	// DirectoryApplication::unloadApp
	this.unloadApp = function() 
	{	// clean up the map
		gMap.unloadMap();
		// close any open windows
		if(this.editConfigWindow != null)
		{	// window exists
			if(this.editConfigWindow.closed == false)
			{	// and it is still open, so remove the config presentation 
				this.editConfigWindow.close();
			}
			this.editConfigWindow = null;
		}
		if(this.itemEntryWindow != null)
		{	// window exists
			if(this.itemEntryWindow.closed == false)
			{	// and it is still open, so remove the config presentation
				this.itemEntryWindow.close();
			}
			this.itemEntryWindow = null;
		}
		if(this.uploadWindow != null)
		{	// window exists
			if(this.uploadWindow.closed == false)
			{	// and it is still open, so remove the config presentation
				this.uploadWindow.close();
			}
			this.uploadWindow = null;
		}
		if(this.logWindow != null)
		{	// window exists
			if(this.logWindow.closed == false)
			{	// and it is still open, so remove the config presentation
				this.logWindow.close();
			}
			this.logWindow = null;
		}
		if(this.helpWindow != null)
		{	// window exists
			if(this.helpWindow.closed == false)
			{	// and it is still open, so remove the config presentation
				this.helpWindow.close();
			}
			this.helpWindow = null;
		}
		if(this.aboutWindow != null)
		{	// window exists
			if(this.aboutWindow.closed == false)
			{	// and it is still open, so remove the config presentation
				this.aboutWindow.close();
			}
			this.aboutWindow = null;
		}
	}
	// -------------------------------------------------------------------------------------------
	//            M E N U   B A R   M A N I P U L A T I O N
	// -------------------------------------------------------------------------------------------
	// Set of methods that interact with the menu toolbar.
	// -----------------------------------------
	this.menuBar = null;
	// values for menu id
	// ----- FILE MENU
	this.midFileTop = 100;		this.midFileSave = 110;		this.midFileCancel = 120;
	// ----- MAP MENU
	this.midMapTop = 200;		this.midMapZin = 210;		this.midMapZut = 220;		this.midMapUp = 230;
	this.midMapDown = 240;		this.midMapLeft = 250;		this.midMapRight = 260;		this.midMapArial = 270;
	this.midMapHybrid = 280;	this.midMapRoad = 290;
	// ----- VIEW MENU
	this.midViewTop = 300;		// submenu item ids are calculated!
	// ----- ADMIN MENU
	this.midAdminTop = 400;		this.midAdminLogin = 410;	this.midAdminNew = 420;		this.midAdminEdit = 430;
	this.midAdminPurge = 440;	this.midAdminSetup = 450;	this.midAdminLog = 460;
	// ---- HELP MENU
	this.midHelpTop = 500;		this.midHelpUser = 510;		this.midHelpAdmin = 520;	this.midHelpAbout = 530;
	// -----------------------------------------
	// DirectoryApplication::initMenu
	this.initMenu = function()
	{	// create our basic menu structure - everything is added and showing to start with
		var menuModel = new DHTMLSuite.menuModel();
		// addItem(id, itemText, itemIcon, menuURL, 0|parentId, toolTip, jsFunction, "top"|"sub", subMenuWidth)

		// ----- MAP MENU
		menuModel.addItem(this.midMapTop,"Map","images/_Icons/map.png","",0,"","","top",100);
		menuModel.addItem(this.midMapZin,"Zoom In","images/_Icons/zoom_in.png","",this.midMapTop,"Increase the zoom level","gApp.zoomMapIn()","sub");
		menuModel.addItem(this.midMapZut,"Zoom Out","images/_Icons/zoom_out.png","",this.midMapTop,"Decrease the zoom level","gApp.zoomMapOut()","sub");
		menuModel.addSeparator(this.midMapTop);
		menuModel.addItem(this.midMapUp,"Move Up","images/_Icons/arrow_up.png","",this.midMapTop,"Scroll the map north","gApp.moveMapNorth()","sub");
		menuModel.addItem(this.midMapDown,"Move Down","images/_Icons/arrow_down.png","",this.midMapTop,"Scroll the map south","gApp.moveMapSouth()","sub");
		menuModel.addItem(this.midMapLeft,"Move Left","images/_Icons/arrow_left.png","",this.midMapTop,"Pan the map west","gApp.moveMapWest()","sub");
		menuModel.addItem(this.midMapRight,"Move Right","images/_Icons/arrow_right.png","",this.midMapTop,"Pan the map east","gApp.moveMapEast()","sub");
		menuModel.addSeparator(this.midMapTop);
		menuModel.addItem(this.midMapArial,"Arial View","","",this.midMapTop,"Switch to a satellite map","gApp.switchMapTypeToArial()","sub");
		menuModel.addItem(this.midMapHybrid,"Hybrid View","","",this.midMapTop,"Switch to a combination map","gApp.switchMapTypeToHybrid()","sub");
		menuModel.addItem(this.midMapRoad,"Road View","","",this.midMapTop,"Switch to a road map","gApp.switchMapTypeToRoad()","sub");

		// ----- VIEW MENU
		menuModel.addItem(this.midViewTop,"View","images/_Icons/black-ab.gif","",0,"","","top",150); 
		var numCategories = gConfig.categories.length;
		if(numCategories > 0)
		{	// put 'em all in
			var id = 0;
			var val = "";
			var key = "";
			var iconPath = "";
			for(var c=0; c<numCategories; c++)
			{	// assign specifics to each, but no onclick handling yet
				id = this.midViewTop + ((c+1)*10);	// menu id
				val = gConfig.categories[c].name;	// internal name
				key = gConfig.categories[c].key;	// display name
				if(val == "all")
				{	// special handing for 'all' - assumes it is last in the gconfig categories list
					menuModel.addSeparator(this.midViewTop);
					menuModel.addItem(id,key,"images/_Icons/accept.png","",this.midViewTop,"Show/Hide all categories","gApp.categoryToggleAll()","sub");
					gConfig.categories[c].enabled = true;
					gConfig.categories[c].selected = true;
					gConfig.categories[c].mid = id;
					this.categoryAllIndex = c;
				}
				else
				{	// single category 
					iconPath = "images/" + gConfig.domainid + "/" + val + "-ab.gif";
					menuModel.addItem(id,key,iconPath,"",this.midViewTop,"Show/Hide this category","gApp.categoryToggle" + c + "()","sub");
					gConfig.categories[c].enabled = true;
					gConfig.categories[c].selected = true;
					gConfig.categories[c].mid = id;
				}
			}
		}

		// ----- ADMIN MENU
		menuModel.addItem(this.midAdminTop,"Administration","images/_Icons/user_suit.png","",0,"","","top",100);
		menuModel.addItem(this.midAdminLogin,"Login","images/_Icons/key.png","",this.midAdminTop,"Authenticate administrative privileges","gApp.accessAdmin()","sub");
		menuModel.addItem(this.midAdminNew,"New","images/_Icons/map_add.png","",this.midAdminTop,"Add a new marker to the map","gApp.addNewItem()","sub");
		menuModel.addItem(this.midAdminEdit,"Edit","images/_Icons/map_edit.png","",this.midAdminTop,"Modify an existing map marker","gApp.editItem()","sub");
		menuModel.addSeparator(this.midAdminTop);
		menuModel.addItem(this.midAdminPurge,"Purge","images/_Icons/map_delete.png","",this.midAdminTop,"Remove all inactive map markers","gApp.purgeItems()","sub");
		menuModel.addItem(this.midAdminSetup,"Setup","images/_Icons/wrench.png","",this.midAdminTop,"Modify the site setup","gApp.editConfig()","sub");
		menuModel.addItem(this.midAdminLog,"View Log","images/_Icons/page_excel.png","",this.midAdminTop,"View the click-thru log","gApp.showLog()","sub");

		// ---- HELP MENU
		menuModel.addItem(this.midHelpTop,"Help","images/_Icons/help.png","",0,"","","top",100);
		menuModel.addItem(this.midHelpUser,"User Help","images/_Icons/user_green.png","",this.midHelpTop,"View user help text","gApp.showHelp()","sub");
		menuModel.addItem(this.midHelpAdmin,"Admin Help","images/_Icons/user_suit.png","",this.midHelpTop,"View administrative help text","gApp.showHelp()","sub");
		menuModel.addItem(this.midHelpAbout,"About","images/_Icons/information.png","",this.midHelpTop,"About this application","gApp.showAbout()","sub");

		// finish up
		menuModel.init();
		this.menuBar = new DHTMLSuite.menuBar();
		this.menuBar.addMenuItems(menuModel);
		this.menuBar.setTarget("actionbar");
		this.menuBar.init();
	}
	
	// -----------------------------------------
	// DirectoryApplication::setupUI
	this.setupUI = function()
	{	// establish the presentation of the menu bar
		this.initMenu();
		
		// determine if we are setting up regular user or admin actions
		if(this.isAdminMode)
			this.setupForAdmin();
		else
			this.setupForUser();

		// establish the current map type
		this.setupMapType(gConfig.type);
		
		// now institute the category setting - we ALWAYS start with "all" selected			
		this.changeCategory(true, false);		// isAll = true, isUpdate = false
	}

	// -----------------------------------------
	// DirectoryApplication::setupForAdmin
	this.setupForAdmin = function()
	{	// set up the menu for administration mode (STARTUP)
		//		Map is fully functional
		//		View is fully functional
		//		Admin is only partially functional to start with (Login only)
		//		Help is partiall functional - user help disabled
		this.menuBar.hideMenuItem(this.midAdminNew);
		this.menuBar.hideMenuItem(this.midAdminEdit);
		this.menuBar.hideMenuItem(this.midAdminPurge);
		this.menuBar.hideMenuItem(this.midAdminSetup);
		this.menuBar.hideMenuItem(this.midAdminLog);
		this.menuBar.hideMenuItem(this.midHelpUser);
	}

	// -----------------------------------------
	// DirectoryApplication::setupForUser
	this.setupForUser = function()
	{	// set up for a regular user (STARTUP)
		//		Map is fully functional
		//		View is fully functional
		//		Admin is non-functional
		//		Help is partially functional - admin help disabled
		this.menuBar.hideMenuItem(this.midAdminTop);
		this.menuBar.hideMenuItem(this.midHelpAdmin);
	}

	// -----------------------------------------
	// DirectoryApplication::setupMapType
	this.setupMapType = function(type)
	{	// indicate the map type selected by putting the "accept" image next to the menu item
		switch(type)
		{
			case "road":
				this.menuBar.setIcon(this.midMapRoad,"images/_Icons/accept.png");	// selected
				this.menuBar.setIcon(this.midMapArial,"");					// deselected
				this.menuBar.setIcon(this.midMapHybrid,"");					// deselected
				break;
			case "arial":
				this.menuBar.setIcon(this.midMapRoad,"");					// deselected
				this.menuBar.setIcon(this.midMapArial,"images/_Icons/accept.png");	// selected
				this.menuBar.setIcon(this.midMapHybrid,"");					// deselected
				break;
			case "hybrid":
				this.menuBar.setIcon(this.midMapRoad,"");					// deselected
				this.menuBar.setIcon(this.midMapArial,"");					// deselected
				this.menuBar.setIcon(this.midMapHybrid,"images/_Icons/accept.png");// selected
				break;
		}
	}

	// -----------------------------------------
	// DirectoryApplication::setupAdmin
	this.setupAdmin = function(mode)
	{	// revamp the UI for admin post-login
		switch(mode)
		{
			case "ready":	// hide login, enable the rest (except edit)
				this.menuBar.hideMenuItem(this.midAdminLogin);
				this.menuBar.showMenuItem(this.midAdminNew);
				// this.menuBar.showMenuItem(this.midAdminEdit);
				this.menuBar.showMenuItem(this.midAdminPurge);
				this.menuBar.showMenuItem(this.midAdminSetup);
				this.menuBar.showMenuItem(this.midAdminLog);
				this.isEditOk = true;
				break;
			case "exit":	// show login, disable the rest [NOTE: obsolete!]
				this.menuBar.showMenuItem(this.midAdminLogin);
				this.menuBar.hideMenuItem(this.midAdminNew);
				this.menuBar.hideMenuItem(this.midAdminEdit);
				this.menuBar.hideMenuItem(this.midAdminPurge);
				this.menuBar.hideMenuItem(this.midAdminSetup);
				this.menuBar.hideMenuItem(this.midAdminLog);
				this.isEditOk = false;
				break;
		}
	}

	// -----------------------------------------
	// DirectoryApplication::setupUIAdd
	this.setupUIAdd = function(isEntry)
	{	// setup the UI so that conflicting admin features are hidden or shown
		if(isEntry)
		{	// hide other features - leave new active to focus the window
			this.menuBar.hideMenuItem(this.midAdminSetup);
			this.menuBar.hideMenuItem(this.midAdminPurge);
		}
		else
		{	// show other features
			this.menuBar.showMenuItem(this.midAdminSetup);
			this.menuBar.showMenuItem(this.midAdminPurge);
		}
	}
	
	// -----------------------------------------
	// DirectoryApplication::setupUIEditEntry
	this.setupUIEditEntry = function(isEntry)
	{	// setup the UI so that conflicting admin features are hidden or shown
		if(isEntry)
		{	// hide other features - leave edit active to focus the window
			this.menuBar.hideMenuItem(this.midAdminNew);
			this.menuBar.showMenuItem(this.midAdminEdit);
			this.menuBar.hideMenuItem(this.midAdminSetup);
			this.menuBar.hideMenuItem(this.midAdminPurge);
		}
		else
		{	// show other features and hide edit
			this.menuBar.showMenuItem(this.midAdminNew);
			this.menuBar.hideMenuItem(this.midAdminEdit);
			this.menuBar.showMenuItem(this.midAdminSetup);
			this.menuBar.showMenuItem(this.midAdminPurge);
		}
	}
	
	// -----------------------------------------
	// DirectoryApplication::setupUIEdit
	this.setupUIEdit = function(makeActive)
	{	// show the EDIT button, but as this is triggered from a map event, we must first
		// verify that we are in the proper mode before allowing edit
		if(makeActive && this.isEditOk)
			this.menuBar.showMenuItem(this.midAdminEdit);
		else
			this.menuBar.hideMenuItem(this.midAdminEdit);
	}
	
	// -----------------------------------------
	// DirectoryApplication::setupUISetup
	this.setupUISetup = function(isEntry)
	{	// setup the UI so that conflicting admin features are hidden or shown
		if(isEntry)
		{	// hide other features - leave setup active to focus the window
			this.menuBar.hideMenuItem(this.midAdminNew);
			this.menuBar.hideMenuItem(this.midAdminPurge);
		}
		else
		{	// show other features
			this.menuBar.showMenuItem(this.midAdminNew);
			this.menuBar.showMenuItem(this.midAdminPurge);
		}
	}

	// -------------------------------------------------------------------------------------------
	//           B U T T O N / E V E N T   H A N D L E R S
	// -------------------------------------------------------------------------------------------
	// Set of methods that handle user interaction events, e.g. button clicks.
	//
	//	NOTE: within the event handlers, the "this" reference does not necessarily refer to the application 
	//  object - it may refer to the element that triggered the event... hence, the use of the global reference
	
	// -----------------------------------------
	// DirectoryApplication::addNewItem
	// from the menu bar
	this.addNewItem = function()
	{	// add a new item button handler - create the itemEntryWindow, if needed
		if(!gApp.itemEntryWindow)
			{gApp.makeNewItemEntryWindow("new");} 
		else if(gApp.itemEntryWindow.closed) 
			{gApp.makeNewItemEntryWindow("new");}
		else
			{gApp.itemEntryWindow.focus();}
	}
	
	// -----------------------------------------
	// DirectoryApplication::saveChanges
	// from any of the data entry windows
	this.saveChanges = function()
	{	// save changes button handler - just tell our controller
		gApp.triggerFlow(eSave);
	}
	
	// -----------------------------------------
	// DirectoryApplication::cancelChanges
	// from any of the data entry windows
	this.cancelChanges = function()
	{	// cancel changes button handler - just tell our controller
		gApp.triggerFlow(eCancel);
	}
	
	// -----------------------------------------
	// DirectoryApplication::abortChanges
	// from any of the data entry windows
	this.abortChanges = function()
	{	// abort changes button handler - just tell our controller
		gApp.triggerFlow(eAbort);
	}

	// -----------------------------------------
	// DirectoryApplication::purgeItems
	// TODO: review confirmation text & consider alternatives for prompting
	// from the menu bar
	this.purgeItems = function()
	{	// purge all inactive items from the list
		if(confirm("Remove all inactive items from the list?"))
		{
			gSvc.purgeItems(gData.getItemListName());
		}
	}
	
	// -----------------------------------------
	// DirectoryApplication::editConfig
	// from the menu bar
	this.editConfig = function()
	{	// edit setup/configuration button handler - create the editConfigWindow, if needed
		if(!gApp.editConfigWindow)
			{gApp.makeNewEditConfigWindow();} 
		else if(gApp.editConfigWindow.closed) 
			{gApp.makeNewEditConfigWindow();}
		else
			{gApp.editConfigWindow.focus();}
	}
	
	// -----------------------------------------
	// DirectoryApplication::editItem
	// from the action bar
	this.editItem = function()
	{	// edit an existing item - create the itemEntryWindow, if needed
		if(!gApp.itemEntryWindow)
			{gApp.makeNewItemEntryWindow(gApp.openedId);} 
		else if(gApp.itemEntryWindow.closed) 
			{gApp.makeNewItemEntryWindow(gApp.openedId);}
		else
			{gApp.itemEntryWindow.focus();}
	}
	
	// -----------------------------------------
	// DirectoryApplication::accessAdmin
	// from the menu bar
	this.accessAdmin = function()
	{	// enter admin mode
		// display modal dialog to get the admin code
		if(this.accessObj == null)
		{	// initialize it one time
			this.accessObj = new DHTMLSuite.modalMessage();
			this.accessObj.setShadowOffset(5);
			this.accessObj.setSize(420,140);
			this.accessObj.setSource(false);	// no html source since we want to use a static message here.
			this.accessObj.setShadowDivVisible(true);	// Enable shadow for these boxes
			var htmlTxt = '<div><h5>Administrator Login</h5></div><DIV class="standard">';
			htmlTxt += '<TABLE cellSpacing="1" cellPadding="1" width="400" border="0">';
			htmlTxt += '<TR><TD colspan="3" class="detailList" style="WIDTH: 250px">Enter the Administrator	password:</TD>';
			htmlTxt += '<TD colspan="2">';
			htmlTxt += '<INPUT id="txtPW" type="password" name="pw" style="WIDTH: 140px; HEIGHT: 20px" maxLength="20" size="20">';
			htmlTxt += '</TD></TR><tr><td colspan="5">&nbsp;</td></tr><TR>';
			htmlTxt += '<TD colspan="3"></TD>';
			htmlTxt += '<TD style="WIDTH: 80px"><INPUT id="butOk" type="submit" value=" Ok " name="ok" onclick="gApp.accessOnOk()"></TD>';
			htmlTxt += '<TD><INPUT id="butCancel" type="button" value="Cancel" name="cancel" onclick="gApp.accessOnCancel()"></TD>';
			htmlTxt += '</TR></TABLE></DIV>';
			this.accessObj.setHtmlContent(htmlTxt);
			this.accessObj.setCssClassMessageBox("TcsMsg_level0");
		}
		this.accessObj.display();
	}
	
	// -----------------------------------------
	// DirectoryApplication::accessOnOk
	// from the access dialog
	this.accessOnOk = function()
	{
		this.testAdmin(document.getElementById("txtPW").value);
		this.accessObj.close();
	}
	
	// -----------------------------------------
	// DirectoryApplication::accessOnCancel
	// from the access dialog
	this.accessOnCancel = function()
	{
		this.accessObj.close();
	}
	
	// -----------------------------------------
	// DirectoryApplication::testAdmin
	this.testAdmin = function(accessCode)
	{	// see if the code is valid, but put it on a timer so that the access window
		// going out of scope doesn't screw up the call to the validation service
		var tid = setTimeout("gSvc.validateAdminAccess('" + accessCode + "')", 100);
	}
	
	// -----------------------------------------
	// DirectoryApplication::exitAdmin	[NOTE: obsolete!]
	// TODO: review use of confirm dialog
	// from the action bar
	this.exitAdmin = function()
	{	// leave admin mode
		if(confirm("Exit from Admin mode?"))
		{	// just tell our controller
			gApp.triggerFlow(eAdminExited);
		}
	}
	
	// -----------------------------------------
	// DirectoryApplication::switchMapTypeToArial
	// from the menu bar
	this.switchMapTypeToArial = function()
	{	// change the map display type & then change the map
		gApp.setupMapType("arial");
		gMap.setMapType("arial");
	}
	
	// -----------------------------------------
	// DirectoryApplication::switchMapTypeToHybrid
	// from the menu bar
	this.switchMapTypeToHybrid = function()
	{	// change the map display type & then change the map
		gApp.setupMapType("hybrid");
		gMap.setMapType("hybrid");
	}
	
	// -----------------------------------------
	// DirectoryApplication::switchMapTypeToRoad
	// from the menu bar
	this.switchMapTypeToRoad = function()
	{	// change the map display type & then change the map
		gApp.setupMapType("road");
		gMap.setMapType("road");
	}
	
	// -----------------------------------------
	// DirectoryApplication::showLog
	// from the menu bar
	this.showLog = function()
	{	// display our log file
		if(!gApp.logWindow)
			{gApp.makeNewLogWindow();} 
		else if(gApp.logWindow.closed) 
			{gApp.makeNewLogWindow();}
		else
			{gApp.logWindow.focus();}
	}
	
	// -----------------------------------------
	// DirectoryApplication::showHelp
	// from the menu bar
	this.showHelp = function()
	{	// display our help text
		if(!gApp.helpWindow)
			{gApp.makeNewHelpWindow();} 
		else if(gApp.helpWindow.closed) 
			{gApp.makeNewHelpWindow();}
		else
			{gApp.helpWindow.focus();}
	}
	
	// -----------------------------------------
	// DirectoryApplication::showAbout
	// from the menu bar
	this.showAbout = function()
	{	// display our help text
		if(!gApp.aboutWindow)
			{gApp.makeNewAboutWindow();} 
		else if(gApp.aboutWindow.closed) 
			{gApp.makeNewAboutWindow();}
		else
			{gApp.aboutWindow.focus();}
	}

	// -----------------------------------------
	// DirectoryApplication::moveMapEast
	// from the menu bar
	this.moveMapEast = function()
	{	// change the map
		gMap.moveEast();
	}
	
	// -----------------------------------------
	// DirectoryApplication::moveMapNorth
	// from the menu bar
	this.moveMapNorth = function()
	{	// change the map
		gMap.moveNorth();
	}
	
	// -----------------------------------------
	// DirectoryApplication::moveMapSouth
	// from the menu bar
	this.moveMapSouth = function()
	{	// change the map
		gMap.moveSouth();
	}
	
	// -----------------------------------------
	// DirectoryApplication::moveMapWest
	// from the menu bar
	this.moveMapWest = function()
	{	// change the map
		gMap.moveWest();
	}
	
	// -----------------------------------------
	// DirectoryApplication::zoomMapIn
	// from the menu bar
	this.zoomMapIn = function()
	{	// change the map
		gMap.zoomIn(false);
	}
	
	// -----------------------------------------
	// DirectoryApplication::zoomMapOut
	//	from the menu bar
	this.zoomMapOut = function()
	{	// change the map
		gMap.zoomOut();
	}
	
	// -----------------------------------------
	// DirectoryApplication::categoryToggleAll
	//	from the menu bar
	this.categoryToggleAll = function()
	{
		if(gConfig.categories[gApp.categoryAllIndex].enabled)
		{	// okay to toggle selection
			if(gConfig.categories[gApp.categoryAllIndex].selected)
			{	// it was selected, deselect it
				gConfig.categories[gApp.categoryAllIndex].selected = false;
				gApp.menuBar.setIcon(gConfig.categories[gApp.categoryAllIndex].mid, "");
			}
			else
			{	// is was NOT selected, so select it
				gConfig.categories[gApp.categoryAllIndex].selected = true;
				iconPath = "images/_Icons/accept.png";
				gApp.menuBar.setIcon(gConfig.categories[gApp.categoryAllIndex].mid, iconPath);
			}
			gApp.changeCategory(true,true);
		}
		else
			gMsg.displayMsg(2,"Category selection is currently disabled.");
	}
	
	// -----------------------------------------
	// DirectoryApplication::categoryToggle0
	//	from the menu bar
	this.categoryToggle0 = function()
	{
		if(gConfig.categories[0].enabled)
		{	// okay to toggle selection
			if(gConfig.categories[0].selected)
			{	// it was selected, deselect it
				gConfig.categories[0].selected = false;
				gApp.menuBar.setIcon(gConfig.categories[0].mid, "");
				// regardless of its previous state, 'all' should now be deselected
				gConfig.categories[gApp.categoryAllIndex].selected = false;
				gApp.menuBar.setIcon(gConfig.categories[gApp.categoryAllIndex].mid, "");
			}
			else
			{	// is was NOT selected, so select it
				gConfig.categories[0].selected = true;
				iconPath = "images/" + gConfig.domainid + "/" + gConfig.categories[0].name + "-ab.gif";
				gApp.menuBar.setIcon(gConfig.categories[0].mid, iconPath);
			}
			gApp.changeCategory(false,true);
		}
		else
			gMsg.displayMsg(2,"Category selection is currently disabled.");
	}
	
	// -----------------------------------------
	// DirectoryApplication::categoryToggle1
	//	from the menu bar
	this.categoryToggle1 = function()
	{
		if(gConfig.categories[1].enabled)
		{	// okay to toggle selection
			if(gConfig.categories[1].selected)
			{	// it was selected, deselect it
				gConfig.categories[1].selected = false;
				gApp.menuBar.setIcon(gConfig.categories[1].mid, "");
				// regardless of its previous state, 'all' should now be deselected
				gConfig.categories[gApp.categoryAllIndex].selected = false;
				gApp.menuBar.setIcon(gConfig.categories[gApp.categoryAllIndex].mid, "");
			}
			else
			{	// is was NOT selected, so select it
				gConfig.categories[1].selected = true;
				iconPath = "images/" + gConfig.domainid + "/" + gConfig.categories[1].name + "-ab.gif";
				gApp.menuBar.setIcon(gConfig.categories[1].mid, iconPath);
			}
			gApp.changeCategory(false,true);
		}
		else
			gMsg.displayMsg(2,"Category selection is currently disabled.");
	}
	
	// -----------------------------------------
	// DirectoryApplication::categoryToggle2
	//	from the menu bar
	this.categoryToggle2 = function()
	{
		if(gConfig.categories[2].enabled)
		{	// okay to toggle selection
			if(gConfig.categories[2].selected)
			{	// it was selected, deselect it
				gConfig.categories[2].selected = false;
				gApp.menuBar.setIcon(gConfig.categories[2].mid, "");
				// regardless of its previous state, 'all' should now be deselected
				gConfig.categories[gApp.categoryAllIndex].selected = false;
				gApp.menuBar.setIcon(gConfig.categories[gApp.categoryAllIndex].mid, "");
			}
			else
			{	// is was NOT selected, so select it
				gConfig.categories[2].selected = true;
				iconPath = "images/" + gConfig.domainid + "/" + gConfig.categories[2].name + "-ab.gif";
				gApp.menuBar.setIcon(gConfig.categories[2].mid, iconPath);
			}
			gApp.changeCategory(false,true);
		}
		else
			gMsg.displayMsg(2,"Category selection is currently disabled.");
	}
	
	// -----------------------------------------
	// DirectoryApplication::categoryToggle3
	//	from the menu bar
	this.categoryToggle3 = function()
	{
		if(gConfig.categories[3].enabled)
		{	// okay to toggle selection
			if(gConfig.categories[3].selected)
			{	// it was selected, deselect it
				gConfig.categories[3].selected = false;
				gApp.menuBar.setIcon(gConfig.categories[3].mid, "");
				// regardless of its previous state, 'all' should now be deselected
				gConfig.categories[gApp.categoryAllIndex].selected = false;
				gApp.menuBar.setIcon(gConfig.categories[gApp.categoryAllIndex].mid, "");
			}
			else
			{	// is was NOT selected, so select it
				gConfig.categories[3].selected = true;
				iconPath = "images/" + gConfig.domainid + "/" + gConfig.categories[3].name + "-ab.gif";
				gApp.menuBar.setIcon(gConfig.categories[3].mid, iconPath);
			}
			gApp.changeCategory(false,true);
		}
		else
			gMsg.displayMsg(2,"Category selection is currently disabled.");
	}
	
	// -----------------------------------------
	// DirectoryApplication::categoryToggle4
	//	from the menu bar
	this.categoryToggle4 = function()
	{
		if(gConfig.categories[4].enabled)
		{	// okay to toggle selection
			if(gConfig.categories[4].selected)
			{	// it was selected, deselect it
				gConfig.categories[4].selected = false;
				gApp.menuBar.setIcon(gConfig.categories[4].mid, "");
				// regardless of its previous state, 'all' should now be deselected
				gConfig.categories[gApp.categoryAllIndex].selected = false;
				gApp.menuBar.setIcon(gConfig.categories[gApp.categoryAllIndex].mid, "");
			}
			else
			{	// is was NOT selected, so select it
				gConfig.categories[4].selected = true;
				iconPath = "images/" + gConfig.domainid + "/" + gConfig.categories[4].name + "-ab.gif";
				gApp.menuBar.setIcon(gConfig.categories[4].mid, iconPath);
			}
			gApp.changeCategory(false,true);
		}
		else
			gMsg.displayMsg(2,"Category selection is currently disabled.");
	}
	
	// -----------------------------------------
	// DirectoryApplication::changeCategory
	this.changeCategory = function(isAll, isUpdate)
	{	// process a category change
		var iconPath = "";
		if(isAll)
		{	// the "all" category was clicked
			if(gConfig.categories[this.categoryAllIndex].selected)
			{	// show all -- all other categories should be selected too (and still enabled)
				for(var c=0; c < gConfig.categories.length; c++)
				{
					if(gConfig.categories[c].name != "all")
					{
						iconPath = "images/" + gConfig.domainid + "/" + gConfig.categories[c].name + "-ab.gif";
						this.menuBar.setIcon(gConfig.categories[c].mid, iconPath);
						gConfig.categories[c].selected = true;
						gConfig.categories[c].enabled = true;
					}
				}
			}
			else
			{	// hide all -- all other categories should be unselected (but still enabled)
				for(var c=0; c < gConfig.categories.length; c++)
				{
					if(gConfig.categories[c].name != "all")
					{
						this.menuBar.setIcon(gConfig.categories[c].mid, "");
						gConfig.categories[c].selected = false;
						gConfig.categories[c].enabled = true;
					}
				}
			}
		}
		else
		{	// a single category was selected, make sure "all" is deselected
			gConfig.categories[this.categoryAllIndex].selected = false;
			this.menuBar.setIcon(gConfig.categories[this.categoryAllIndex].mid, "");
		}
		
		// now go through all categories to see what is selected and not selected
		// building up the search criteria as we go...
		this.category = "";	// start from scratch each time
		for(var c=0; c < gConfig.categories.length; c++)
		{
			if(gConfig.categories[c].name != "all")
			{
				if(gConfig.categories[c].selected)
					this.category += "+" + gConfig.categories[c].name;
			}
		}

		// only process the update if asked
		if(isUpdate)
			{this.triggerFlow(eCategoryChange)};
	}
	
	// -----------------------------------------
	// DirectoryApplication::onClickAdType
	this.onClickAdType = function(adType)
	{	// handle ad type selection
		if(gApp.itemEntryWindow != null)
		{	// item entry is active
			switch(adType)
			{
				case "none":
					// disble all uploads
					gApp.itemEntryWindow.document.getElementById("aBan").disabled = true;
					gApp.itemEntryWindow.document.getElementById("aCar").disabled = true;
					gApp.itemEntryWindow.document.getElementById("aRec").disabled = true;
					gApp.itemEntryWindow.document.getElementById("aSky").disabled = true;
					break;
				case "banner":
					// disable all but banner uploads
					gApp.itemEntryWindow.document.getElementById("aBan").disabled = false;
					gApp.itemEntryWindow.document.getElementById("aCar").disabled = true;
					gApp.itemEntryWindow.document.getElementById("aRec").disabled = true;
					gApp.itemEntryWindow.document.getElementById("aSky").disabled = true;
					break;
				case "card":
					// disable all but business card uploads
					gApp.itemEntryWindow.document.getElementById("aBan").disabled = true;
					gApp.itemEntryWindow.document.getElementById("aCar").disabled = false;
					gApp.itemEntryWindow.document.getElementById("aRec").disabled = true;
					gApp.itemEntryWindow.document.getElementById("aSky").disabled = true;
					break;
				case "rect":
					// disable all but rectangle (tile) uploads
					gApp.itemEntryWindow.document.getElementById("aBan").disabled = true;
					gApp.itemEntryWindow.document.getElementById("aCar").disabled = true;
					gApp.itemEntryWindow.document.getElementById("aRec").disabled = false;
					gApp.itemEntryWindow.document.getElementById("aSky").disabled = true;
					break;
				case "sky":
					// disable all but skyscraper uploads
					gApp.itemEntryWindow.document.getElementById("aBan").disabled = true;
					gApp.itemEntryWindow.document.getElementById("aCar").disabled = true;
					gApp.itemEntryWindow.document.getElementById("aRec").disabled = true;
					gApp.itemEntryWindow.document.getElementById("aSky").disabled = false;
					break;
			}
		}	
	}
	
	// -----------------------------------------
	// DirectoryApplication::onListItemSelected
	this.onListItemSelected = function(itemId)
	{	// handle a list item selection
		var thisItem = gData.getItem(itemId);
		if(gApp.isW3 && (thisItem != null))
		{
			// save the id and reset the popup flag to ensure it will open even if the map doesn't move
			gMap.openedPopup = false;
			gApp.itemToOpen = itemId;
			// get the item location from our source data
			var markerData = thisItem.getElementsByTagName("marker")[0];
			var lat = parseFloat(markerData.getElementsByTagName("latitude")[0].firstChild.nodeValue);
			var lng = parseFloat(markerData.getElementsByTagName("longitude")[0].firstChild.nodeValue);
			// center the map on this item because it may or may not be already shown, 
			// and note that this will trigger an event (map moved) that will result in 
			// the current list being updated and the markers shown, and the marker for
			// the item selected will be opened - trust me, it's magic
			gMap.centerMap(lat,lng);
		}
	}

	// -------------------------------------------------------------------------------------------
	//            C O N F I G U R A T I O N / S E T U P   W I N D O W   
	// -------------------------------------------------------------------------------------------
	// Set of methods that manage the application behavior related to user interaction
	// applied to the site configuration data.
	
	// -----------------------------------------
	// DirectoryApplication::updateCategory
	// from the edit config window
	this.updateCategory = function(num)
	{	// review the category change
		var prev = num-1;
		var next = num+1;
		var allowed = false;
		if(gApp.editConfigWindow.document.getElementById("cbCat" + num).checked == true)
		{	// adding a category (checking the box)
			allowed = true;	// allow fields to be updated
			// next can now be checked too
			if(next <= 5)
				{gApp.editConfigWindow.document.getElementById("cbCat" + next).disabled = false;}
			// previous cannot be unchecked (out of order)
			if(prev >= 2)
				{gApp.editConfigWindow.document.getElementById("cbCat" + prev).disabled = true;}
		}
		else
		{	// removing a category (unchecking the box)
			allowed = false;	// fields can no longer be updated
			// next can not be checked now (out of order)
			if(next <= 5)
				{gApp.editConfigWindow.document.getElementById("cbCat" + next).disabled = true;}
			// previous can now be unchecked
			if(prev >= 2)
				{gApp.editConfigWindow.document.getElementById("cbCat" + prev).disabled = false;}
		}
		// default the internal name and do not allow the user to change it
		gApp.editConfigWindow.document.getElementById("txtCat" + num + "Name").disabled = true;	// !allowed;
		var ch = String.fromCharCode(64+num);
		gApp.editConfigWindow.document.getElementById("txtCat" + num + "Name").value = num + ch;
		gApp.editConfigWindow.document.getElementById("txtCat" + num + "Key").disabled = !allowed;
		gApp.editConfigWindow.document.getElementById("selCat" + num + "Icon").disabled = !allowed;
	}
	
	// -----------------------------------------
	// DirectoryApplication::cancelEditConfig
	this.cancelEditConfig = function(isAbort)
	{	// cancel the edit config capability
		if(isAbort)
		{	// window is closed & unavailable
			this.editConfigWindow = null;
		}
		else if(this.editConfigWindow != null)
		{	// we still have a window object
			if(this.editConfigWindow.closed == false)
			{	// and it is still open, so remove the config presentation
				this.editConfigWindow.close();
			}
			this.editConfigWindow = null;
		}
		// restore the UI
		this.setupUISetup(false);

		// remove the starting location marker
		gMap.removeLastMapMarker();
	}

	// -----------------------------------------
	// DirectoryApplication::loadEditConfig
	this.loadEditConfig = function()
	{	// display the edit config capability
		if(this.editConfigWindow != null)
		{
			// populate the readonly data fields
			this.editConfigWindow.document.getElementById("txtDomainid").value = gConfig.domainid;
			this.editConfigWindow.document.getElementById("txtProvider").value = gConfig.provider;
			// default values
			this.editConfigWindow.document.getElementById("txtContact").value = gConfig.contact;
			this.editConfigWindow.document.getElementById("txtLatitude").value = gConfig.defaultLat;
			this.editConfigWindow.document.getElementById("txtLongitude").value = gConfig.defaultLng;
			this.editConfigWindow.document.getElementById("selOvride").selectedIndex = parseInt(gConfig.locked);
			// Yahoo uses the reverse high-->low zoom values
			if(gConfig.provider == "Yahoo")
				{this.editConfigWindow.document.getElementById("selZoom").selectedIndex = 17 - gConfig.defaultZoom;}
			else
				{this.editConfigWindow.document.getElementById("selZoom").selectedIndex = gConfig.defaultZoom;}
			// map values
			var idx = 0;		// map type
			if(gConfig.type == "road") idx = 0;
			else if(gConfig.type == "arial") idx = 1;
			else if(gConfig.type == "hybrid") idx = 2;
			else idx = 0;
			this.editConfigWindow.document.getElementById("selType").selectedIndex = idx;
			//
			idx = 0;		// controls type
			if(gConfig.controls == "default") idx = 0;
			else if(gConfig.controls == "pan") idx = 1;
			else if(gConfig.controls == "type") idx = 2;
			else if(gConfig.controls == "small") idx = 3;
			else if(gConfig.controls == "large") idx = 4;
			else idx = 0;
			this.editConfigWindow.document.getElementById("selControls").selectedIndex = idx;
			//
			idx = 0;		// marker open trigger
			if(gConfig.markerOnOpen == "click") idx = 0;
			else if(gConfig.markerOnOpen == "mouseover") idx = 1;
			else idx = 0;
			this.editConfigWindow.document.getElementById("selOpenon").selectedIndex = idx;
			// zoom max ranges 0-17
			// Yahoo uses the reverse high-->low zoom values
			if(gConfig.provider == "Yahoo")
				{this.editConfigWindow.document.getElementById("selMaxzoom").selectedIndex = 17 - gConfig.zoommax;}
			else
				{this.editConfigWindow.document.getElementById("selMaxzoom").selectedIndex = gConfig.zoommax;}
			// max markers = 10, 20, ..., 100
			this.editConfigWindow.document.getElementById("selMaxmarkers").selectedIndex = parseInt(gConfig.markerMax / 10) - 1;
			// ad timer = 10,000, 15,000, ... 30,000
			this.editConfigWindow.document.getElementById("selAdTimer").selectedIndex = parseInt(gConfig.timer / 5000) - 2;
			// host score = 10, 20, ... , 100
			this.editConfigWindow.document.getElementById("selHostAd").selectedIndex = parseInt(gConfig.hostScore / 10) - 1;
			// if ads are not enabled, cannot change ad settings
			if(gConfig.ads == "false")
			{
				this.editConfigWindow.document.getElementById("selAdTimer").disabled = true;
				this.editConfigWindow.document.getElementById("selHostAd").disabled = true;
				this.editConfigWindow.document.getElementById("butBanner").disabled = true;
				this.editConfigWindow.document.getElementById("butCard").disabled = true;
				this.editConfigWindow.document.getElementById("butSky").disabled = true;
				this.editConfigWindow.document.getElementById("butTile").disabled = true;
			}
			else
			{
				this.editConfigWindow.document.getElementById("butBanner").disabled = !(gAds.banner.exists);
				this.editConfigWindow.document.getElementById("butCard").disabled = !(gAds.cards.exists);
				this.editConfigWindow.document.getElementById("butSky").disabled = !(gAds.sky.exists);
				this.editConfigWindow.document.getElementById("butTile").disabled = !(gAds.tiles.exists);
			}
			// load any existing categories
			var numCategories = gConfig.categories.length;
			if(numCategories > 0)
			{	// skip the last category, which is always "all"
				for(var j=0; j<(numCategories-1); j++)
				{
					idx = j+1;
					var next = idx+1;
					var prev = idx-1;
					this.editConfigWindow.document.getElementById("cbCat" + idx).checked = true;		// mark this checked
					if(prev >= 2)
						{this.editConfigWindow.document.getElementById("cbCat" + prev).disabled = true;}	// disabled previous
					if(next <= 5)
						{this.editConfigWindow.document.getElementById("cbCat" + next).disabled = false;}	// enable next
					// enable this category's fields
					this.editConfigWindow.document.getElementById("txtCat" + idx + "Name").disabled = true;	// was false;
					this.editConfigWindow.document.getElementById("txtCat" + idx + "Key").disabled = false;
					this.editConfigWindow.document.getElementById("selCat" + idx + "Icon").disabled = false;
					// load the values
					this.editConfigWindow.document.getElementById("txtCat" + idx + "Name").value = gConfig.categories[j].name;
					this.editConfigWindow.document.getElementById("txtCat" + idx + "Key").value = gConfig.categories[j].key;
					this.editConfigWindow.document.getElementById("selCat" + idx + "Icon").selectedIndex = gConfig.categories[j].icon;
				}
			}
			// load category icons using the app name
			this.editConfigWindow.document.getElementById("imgBlue").src = "Images/" + gConfig.domainid + "/Blue-ab.gif";
			this.editConfigWindow.document.getElementById("imgGreen").src = "Images/" + gConfig.domainid + "/Green-ab.gif";
			this.editConfigWindow.document.getElementById("imgTeal").src = "Images/" + gConfig.domainid + "/Teal-ab.gif";
			this.editConfigWindow.document.getElementById("imgBrown").src = "Images/" + gConfig.domainid + "/Brown-ab.gif";
			this.editConfigWindow.document.getElementById("imgViolet").src = "Images/" + gConfig.domainid + "/Violet-ab.gif";
			this.editConfigWindow.document.getElementById("imgRed").src = "Images/" + gConfig.domainid + "/Red-ab.gif";
			this.editConfigWindow.document.getElementById("imgBronze").src = "Images/" + gConfig.domainid + "/Bronze-ab.gif";
			this.editConfigWindow.document.getElementById("imgLilac").src = "Images/" + gConfig.domainid + "/Lilac-ab.gif";
			this.editConfigWindow.document.getElementById("imgGold").src = "Images/" + gConfig.domainid + "/Gold-ab.gif";
			this.editConfigWindow.document.getElementById("imgSilver").src = "Images/" + gConfig.domainid + "/Silver-ab.gif";

			// alter the UI
			this.setupUISetup(true);
			
			// tell our controller that we're ready to edit the config file
			this.triggerFlow(eEditConfig);
		}
	}

	// -----------------------------------------
	// DirectoryApplication::saveEditConfig
	// TODO: review confirm mechanism
	this.saveEditConfig = function()
	{	// save the config changes
		if(this.validateConfig())
		{	// it is valid, so put the item data into a structure for processing
			var cfg = "";
			cfg += "<contact>" + this.editConfigWindow.document.getElementById("txtContact").value + "</contact>";
			// lat/lng we save, but not the address
			cfg += "<latitude>" + this.editConfigWindow.document.getElementById("txtLatitude").value + "</latitude>";
			cfg += "<longitude>" + this.editConfigWindow.document.getElementById("txtLongitude").value + "</longitude>";
			cfg += "<locked>" + this.editConfigWindow.document.getElementById("selOvride").selectedIndex + "</locked>";
			// save the map control settings
			var zlVal = this.editConfigWindow.document.getElementById("selZoom").selectedIndex;
			var zmVal = this.editConfigWindow.document.getElementById("selMaxzoom").selectedIndex;
			// Yahoo uses the reverse high-->low zoom values
			if(gConfig.provider == "Yahoo")
			{	// Yahoo
				cfg += "<zoomlevel>" + (17 - zlVal) + "</zoomlevel>";
				cfg += "<zoommax>" + (17 - zmVal) + "</zoommax>";
			}
			else
			{	// Google & MSVE
				cfg += "<zoomlevel>" + zlVal + "</zoomlevel>";
				cfg += "<zoommax>" + zmVal + "</zoommax>";
			}
			cfg += "<type>" + this.editConfigWindow.document.getElementById("selType").options[this.editConfigWindow.document.getElementById("selType").selectedIndex].value + "</type>";
			cfg += "<controls>" + this.editConfigWindow.document.getElementById("selControls").options[this.editConfigWindow.document.getElementById("selControls").selectedIndex].value + "</controls>";
			cfg += "<maxmarkers>" + this.editConfigWindow.document.getElementById("selMaxmarkers").options[this.editConfigWindow.document.getElementById("selMaxmarkers").selectedIndex].value + "</maxmarkers>";
			cfg += "<markeropenon>" + this.editConfigWindow.document.getElementById("selOpenon").options[this.editConfigWindow.document.getElementById("selOpenon").selectedIndex].value + "</markeropenon>";
			var useThumb = "on";
			if(gConfig.useDefaultThumb == false) useThumb = "off";
			cfg += "<usedefaultthumb>" + useThumb + "</usedefaultthumb>";
			cfg += "<adenabled>" + gConfig.ads + "</adenabled>";		// can't change
			cfg += "<adtimer>" + this.editConfigWindow.document.getElementById("selAdTimer").options[this.editConfigWindow.document.getElementById("selAdTimer").selectedIndex].value + "</adtimer>";
			cfg += "<adhostscore>" + this.editConfigWindow.document.getElementById("selHostAd").options[this.editConfigWindow.document.getElementById("selHostAd").selectedIndex].value + "</adhostscore>";
			// category settings (five allowed)
			cfg += "<categories>";
			for(var n=1; n<6; n++)
			{
				if(this.editConfigWindow.document.getElementById("cbCat" + n).checked == true)
				{
					cfg += this.editConfigWindow.document.getElementById("txtCat" + n + "Name").value;
					cfg += "," + this.editConfigWindow.document.getElementById("txtCat" + n + "Key").value;
					cfg += "," + this.editConfigWindow.document.getElementById("selCat" + n + "Icon").selectedIndex;
					cfg += "," + this.editConfigWindow.document.getElementById("selCat" + n + "Icon").options[this.editConfigWindow.document.getElementById("selCat" + n + "Icon").selectedIndex].value;
					cfg += ";"
				}
			}
			cfg += "</categories>";
			// replace the config settings - returns immediately
			gSvc.updateConfig(gSiteConfig, cfg);
			// reset the presentation
			this.cancelEditConfig();
		}
	}

	// -----------------------------------------
	// DirectoryApplication::validateConfig
	this.validateConfig = function()
	{	// validate the new/edited item content
		var allDataValid = true;
		var errObj = null;
		var exceptionString = "This data is invalid because:<br>";
		if(this.editConfigWindow != null)
		{	// validate all text entry data fields
			// email is required
			errObj = gData.validateEmail("default contact e-mail",this.editConfigWindow.document.getElementById("txtContact").value,true);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
			// Latitude is required
			errObj = gData.validateNumber("default Latitude",this.editConfigWindow.document.getElementById("txtLatitude").value,true);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
			// Longitude is required
			errObj = gData.validateNumber("default Longitude",this.editConfigWindow.document.getElementById("txtLongitude").value,true);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
			// Category names (that exist) must be valid
			for(var n=1; n<6; n++)
			{
				if(this.editConfigWindow.document.getElementById("cbCat" + n).checked == true)
				{	// if it is checked, it must have a name & key value
					errObj = gData.validateText("Category " + n + " internal name",this.editConfigWindow.document.getElementById("txtCat" + n + "Name").value,true);
					if(errObj.isError)
					{
						allDataValid = false;
						exceptionString += "* " + errObj.errMsg + "<br>";
						errObj = null;
					}
					errObj = gData.validateText("Category " + n + " display name",this.editConfigWindow.document.getElementById("txtCat" + n + "Key").value,true);
					if(errObj.isError)
					{
						allDataValid = false;
						exceptionString += "* " + errObj.errMsg + "<br>";
						errObj = null;
					}
				}
			}
		}
		if(!allDataValid)
		{	// finish formatting the exception string and display it
			exceptionString += '<br>';
			gMsg.displayMsg(4,exceptionString);
		}
		return allDataValid;
	}

	// -------------------------------------------------------------------------------------------
	//            A D D / E D I T   I T E M   W I N D O W
	// -------------------------------------------------------------------------------------------
	// Set of methods that manage the application behavior related to user interaction
	// applied to new data or edited data.

	// -----------------------------------------
	// DirectoryApplication::cancelEditItem
	this.cancelEditItem = function(isAbort)
	{	// cancel the edit item capability
		if(isAbort)
		{	// window is closed and unavailable
			this.itemEntryWindow = null;
		}
		else if(this.itemEntryWindow != null)
		{	// the window object is available
			if(this.itemEntryWindow.closed == false)
			{	// remove the edit item presentation
				this.itemEntryWindow.close();
			}
			this.itemEntryWindow = null;
		}
		// alter the UI
		this.setupUIEditEntry(false);
		// remove the new item marker
		gMap.removeLastMapMarker();
	}

	// -----------------------------------------
	// DirectoryApplication::cancelNewItem
	this.cancelNewItem = function(isAbort)
	{	// cancel the add new item capability
		if(isAbort)
		{	// window is closed and unavailable
			this.itemEntryWindow = null;
		}
		else if(this.itemEntryWindow != null)
		{	// the window object is available
			if(this.itemEntryWindow.closed == false)
			{	// remove the new item presentation
				this.itemEntryWindow.close();
			}
			this.itemEntryWindow = null;
		}
		// alter the UI	
		this.setupUIAdd(false);
		// remove the new item marker
		gMap.removeLastMapMarker();
	}

	// -----------------------------------------
	// DirectoryApplication::loadEditItem
	this.loadEditItem = function(itemId)
	{	// display the edit existing item presentation - trim white space out of incoming data (Mozilla issue)
		if(this.itemEntryWindow != null)
		{	// retrieve the item data
			var item = gData.getItem(itemId);
			if(item != null)
			{	// populate the data fields 
				this.itemEntryWindow.document.getElementById("txtId").value = itemId;
				// administrative data
				var approvalNode = item.getElementsByTagName("approval")[0];
				// status is required, but check it anyway
				var status = "";
				var statusNode = approvalNode.getElementsByTagName("status")[0];
				if(statusNode && statusNode.firstChild != null)
					{status = statusNode.firstChild.nodeValue;}			// item.selectSingleNode("approval/status").text;
				if(status == "unapproved")
					{this.itemEntryWindow.document.getElementById("radUnapproved").checked = true;}
				else if(status == "inactive")
					{this.itemEntryWindow.document.getElementById("radInactive").checked = true;}
				else
					{this.itemEntryWindow.document.getElementById("radActive").checked = true;}
				// contact is required, but check it anyway
				var contact = "";
				var contactNode = approvalNode.getElementsByTagName("contact")[0];
				if(contactNode && contactNode.firstChild != null)
					{contact = contactNode.firstChild.nodeValue;}
				this.itemEntryWindow.document.getElementById("txtContact").value = contact.trimStr();
				// ------------------
				// advertisement data, always start with 'none'
				this.itemEntryWindow.document.getElementById("radNone").checked = true;
				if(gConfig.ads == "false")
				{	// disable all ad-related controls
					this.itemEntryWindow.document.getElementById("txtNavigate").disabled = true;
					// this.itemEntryWindow.document.getElementById("txtKeyword").disabled = true;
					this.itemEntryWindow.document.getElementById("selWeight").disabled = true;
					this.itemEntryWindow.document.getElementById("radNone").disabled = true;
					this.itemEntryWindow.document.getElementById("radBanner").disabled = true;
					this.itemEntryWindow.document.getElementById("aBan").disabled = true;
					this.itemEntryWindow.document.getElementById("radCard").disabled = true;
					this.itemEntryWindow.document.getElementById("aCar").disabled = true;
					this.itemEntryWindow.document.getElementById("radSkyscraper").disabled = true;
					this.itemEntryWindow.document.getElementById("aSky").disabled = true;
					this.itemEntryWindow.document.getElementById("radTile").disabled = true;
					this.itemEntryWindow.document.getElementById("aRec").disabled = true;
				}
				else
				{	// enable specific controls based on ad selected - item includes the path to images
					// but we don't want to error-out if an ad node is missing... but above all else,
					// make sure to enable/disable options based on what the ad rotator actually sees
					var adNode = approvalNode.getElementsByTagName("ad")[0];
					var grNode = adNode.getElementsByTagName("graphic")[0];
					var imgNode = null;
					var imgPath = "";
					// BANNER
					this.itemEntryWindow.document.getElementById("radBanner").disabled = !(gAds.banner.exists);
					this.itemEntryWindow.document.getElementById("aBan").disabled = true;
					if(gAds.banner.exists)
					{
						imgNode = grNode.getElementsByTagName("banner")[0];
						imgPath = "";
						if(imgNode && imgNode.firstChild != null)
						{	// exists
							imgPath = (imgNode.firstChild.nodeValue).trimStr();
							if(imgPath != "")
							{	// exists
								this.itemEntryWindow.document.getElementById("full").src = "Images/" + imgPath;
								this.itemEntryWindow.document.getElementById("radBanner").checked = true;
								this.itemEntryWindow.document.getElementById("aBan").disabled = false;
							}
						}
					}
					// CARDS
					this.itemEntryWindow.document.getElementById("radCard").disabled = !(gAds.cards.exists);
					this.itemEntryWindow.document.getElementById("aCar").disabled = true;
					if(gAds.cards.exists)
					{
						imgNode = grNode.getElementsByTagName("card")[0];
						if(imgNode && imgNode.firstChild != null)
						{	// exists
							imgPath = (imgNode.firstChild.nodeValue).trimStr();
							if(imgPath != "")
							{	// exists
								this.itemEntryWindow.document.getElementById("card").src = "Images/"  + imgPath;
								this.itemEntryWindow.document.getElementById("radCard").checked = true;
								this.itemEntryWindow.document.getElementById("aCar").disabled = false;
							}
						}
					}
					// SKYSCRAPER
					this.itemEntryWindow.document.getElementById("radSkyscraper").disabled = !(gAds.sky.exists);
					this.itemEntryWindow.document.getElementById("aSky").disabled = true;
					if(gAds.sky.exists)
					{
						imgNode = grNode.getElementsByTagName("skyscraper")[0];
						if(imgNode && imgNode.firstChild != null)
						{	// exists
							imgPath = (imgNode.firstChild.nodeValue).trimStr();
							if(imgPath != "")
							{	// exists
								this.itemEntryWindow.document.getElementById("sky").src = "Images/"  + imgPath;
								this.itemEntryWindow.document.getElementById("radSkyscraper").checked = true;
								this.itemEntryWindow.document.getElementById("aSky").disabled = false;
							}
						}
					}
					// TILES
					this.itemEntryWindow.document.getElementById("radTile").disabled = !(gAds.tiles.exists);
					this.itemEntryWindow.document.getElementById("aRec").disabled = true;
					if(gAds.tiles.exists)
					{
						imgNode = grNode.getElementsByTagName("tile")[0];
						if(imgNode && imgNode.firstChild != null)
						{	// exists
							imgPath = (imgNode.firstChild.nodeValue).trimStr();
							if(imgPath != "")
							{	// exists
								this.itemEntryWindow.document.getElementById("rect").src = "Images/"  + imgPath;
								this.itemEntryWindow.document.getElementById("radTile").checked = true;
								this.itemEntryWindow.document.getElementById("aRec").disabled = false;
							}
						}
					}
					// the remaining ad fields can be loaded up regardless
					var navig = "";
					var navigNode = adNode.getElementsByTagName("navigate")[0];
					if(navigNode && navigNode.firstChild != null)
						{navig = navigNode.firstChild.nodeValue;}
					this.itemEntryWindow.document.getElementById("txtNavigate").value = navig.trimStr();
					// this.itemEntryWindow.document.getElementById("txtKeyword").value = item.selectSingleNode("approval/ad/keyword").text;
					var wgt = "";
					var wgtNode = adNode.getElementsByTagName("weight")[0];
					if(wgtNode && wgtNode.firstChild != null)
						{wgt = wgtNode.firstChild.nodeValue;}
					this.itemEntryWindow.document.getElementById("selWeight").selectedIndex = parseInt(wgt);
				}
				// marker data - we'll use the lat/lng to update the item location (below)
				var markerNode = item.getElementsByTagName("marker")[0];
				// lat / lng are absolutely required!
				var lat = 0;
				var latNode = markerNode.getElementsByTagName("latitude")[0];
				if(latNode && latNode.firstChild != null)
					{lat = parseFloat(latNode.firstChild.nodeValue);}
				var lng = 0;
				var lngNode = markerNode.getElementsByTagName("longitude")[0];
				if(lngNode && lngNode.firstChild != null)
					{lng = parseFloat(lngNode.firstChild.nodeValue);}
				// address is optional
				var addr = "";
				var addrNode = markerNode.getElementsByTagName("address")[0];
				if(addrNode && addrNode.firstChild != null)
					{addr = addrNode.firstChild.nodeValue;}
				this.itemEntryWindow.document.getElementById("txtAddress").value = addr.trimStr();
				// phone is optional
				var phn = "";
				var phnNode = markerNode.getElementsByTagName("phone")[0];
				if(phnNode && phnNode.firstChild != null)
					{phn = phnNode.firstChild.nodeValue;}
				this.itemEntryWindow.document.getElementById("txtPhone").value = phn.trimStr();
				// email is optional
				var eml = "";
				var emlNode = markerNode.getElementsByTagName("email")[0];
				if(emlNode && emlNode.firstChild != null)
					{eml = emlNode.firstChild.nodeValue;}
				this.itemEntryWindow.document.getElementById("txtEmail").value = eml.trimStr();
				// url is optional
				var ul = "";
				var ulNode = markerNode.getElementsByTagName("url")[0];
				if(ulNode && ulNode.firstChild != null)
					{ul = ulNode.firstChild.nodeValue;}
				this.itemEntryWindow.document.getElementById("txtUrl").value = ul.trimStr();
				// the urltext element is a late addition - may not exist in older apps
				var ult = "";
				var ultNode = markerNode.getElementsByTagName("urltext")[0];
				if(ultNode && ultNode.firstChild != null)
					{ult = ultNode.firstChild.nodeValue;}
				this.itemEntryWindow.document.getElementById("txtLink").value = ult.trimStr();
				// content data
				var contentNode = item.getElementsByTagName("content")[0];
				// title is required, but check it anyway
				var ttl = "";
				var ttlNode = contentNode.getElementsByTagName("title")[0];
				if(ttlNode && ttlNode.firstChild != null)
					{ttl = ttlNode.firstChild.nodeValue;}
				this.itemEntryWindow.document.getElementById("txtTitle").value = ttl.trimStr();
				// summary is required, but check it anyway
				var sum = "";
				var sumNode = contentNode.getElementsByTagName("summary")[0];
				if(sumNode && sumNode.firstChild != null)
					{sum = sumNode.firstChild.nodeValue;}
				this.itemEntryWindow.document.getElementById("txtSummary").value = sum.trimStr();
				// this.itemEntryWindow.document.getElementById("txtDescription").value = item.selectSingleNode("content/description").text;
				// figure out the category - it's required
				var catTxt = "";
				var catNode = contentNode.getElementsByTagName("category")[0];
				if(catNode && catNode.firstChild != null)
					{catTxt = catNode.firstChild.nodeValue;}
				var selIdx = 0;
				// handle configurable categories
				var numCategories = gConfig.categories.length;
				if(numCategories > 0)
				{	// build up the category selection list
					var htmlTxt = '<select id="selCat" class="detail">\n';
					for(var i=0; i<numCategories; i++)
					{	// for each category
						var val = gConfig.categories[i].name;
						var key = gConfig.categories[i].key;
						if(val != "all")
						{	// don't put "all" in the item select list
							htmlTxt += '<option value="' + val + '">' + key + '</option>\n';
						}
						if(val == catTxt)
						{	// this is the one we want selected
							selIdx = i;
						}
					}
					htmlTxt += '</select>';
					this.itemEntryWindow.document.getElementById("selCategory").innerHTML = htmlTxt;
					this.itemEntryWindow.document.getElementById("selCat").selectedIndex = selIdx;
				}
				// subcategory is always "" (option 0) for this app
				// this.itemEntryWindow.document.getElementById("selSubCategory").selectedIndex = 0;
				// content url is always "" for this app - don't even bother with it
				// photo data, item contains path to images
				var photoNode = item.getElementsByTagName("photo")[0];
				if(photoNode)
				{
					imgNode = photoNode.getElementsByTagName("thumbnail")[0];
					if(imgNode && imgNode.firstChild != null)
					{
						imgPath = imgNode.firstChild.nodeValue;
						if(imgPath != "")
							{this.itemEntryWindow.document.getElementById("thumb").src = "Images/" + imgPath;}
					}
				}
				//	fullsize photo is always "" for this app, so don't bother
				// show the existing item location on the map
				this.updateItemLocation(false, true, lat, lng);
				// alter the UI
				this.setupUIEditEntry(true);
				// tell our controller that we're ready to edit this item
				this.triggerFlow(eEditClicked);
			}
		}
	}

	// -----------------------------------------
	// DirectoryApplication::loadNewItem
	this.loadNewItem = function()
	{	// display the add new item presentation
		if(this.itemEntryWindow != null)
		{	// populate the data fields
			this.itemEntryWindow.document.getElementById("txtId").value = gData.createUniqueId();
			this.itemEntryWindow.document.getElementById("radActive").checked = true;
			this.itemEntryWindow.document.getElementById("txtContact").value = gConfig.contact;
			// determine which thumbnail to use as the default value (pnas won't be displayed at runtime)
			if(gConfig.useDefaultThumb)
				{this.itemEntryWindow.document.getElementById("thumb").src = "Images/"  + gConfig.domainid + "/default-t.gif";}
			else
				{this.itemEntryWindow.document.getElementById("thumb").src = "Images/"  + gConfig.domainid + "/pna-t.jpg";}
			// for a new item, default to no ads, and set up elements based on what we know
			this.itemEntryWindow.document.getElementById("radNone").checked = true;
			if(gConfig.ads == "true")
			{	// enable/disable radio button options based on what the ad rotator actually sees, but disable
				// all of the upload options until an ad type selection is actually made
				this.itemEntryWindow.document.getElementById("radBanner").disabled = !(gAds.banner.exists);
				this.itemEntryWindow.document.getElementById("aBan").disabled = true;
				this.itemEntryWindow.document.getElementById("radCard").disabled = !(gAds.cards.exists);
				this.itemEntryWindow.document.getElementById("aCar").disabled = true;
				this.itemEntryWindow.document.getElementById("radSkyscraper").disabled = !(gAds.sky.exists);
				this.itemEntryWindow.document.getElementById("aSky").disabled = true;
				this.itemEntryWindow.document.getElementById("radTile").disabled = !(gAds.tiles.exists);
				this.itemEntryWindow.document.getElementById("aRec").disabled = true;
			}
			else
			{	// no ads! disable all ad-related controls
				this.itemEntryWindow.document.getElementById("txtNavigate").disabled = true;
				// this.itemEntryWindow.document.getElementById("txtKeyword").disabled = true;
				this.itemEntryWindow.document.getElementById("selWeight").disabled = true;
				this.itemEntryWindow.document.getElementById("radNone").disabled = true;
				this.itemEntryWindow.document.getElementById("radBanner").disabled = true;
				this.itemEntryWindow.document.getElementById("aBan").disabled = true;
				this.itemEntryWindow.document.getElementById("radCard").disabled = true;
				this.itemEntryWindow.document.getElementById("aCar").disabled = true;
				this.itemEntryWindow.document.getElementById("radSkyscraper").disabled = true;
				this.itemEntryWindow.document.getElementById("aSky").disabled = true;
				this.itemEntryWindow.document.getElementById("radTile").disabled = true;
				this.itemEntryWindow.document.getElementById("aRec").disabled = true;
			}
			// setup configurable categories
			var selIdx = 0;
			var numCategories = gConfig.categories.length;
			if(numCategories > 0)
			{	// build up the category selection list
				var htmlTxt = '<select id="selCat" class="detail">\n';
				for(var i=0; i<numCategories; i++)
				{	// for each category
					var val = gConfig.categories[i].name;
					var key = gConfig.categories[i].key;
					if(val != "all")
					{	// don't put "all" in the item select list
						htmlTxt += '<option value="' + val + '">' + key + '</option>\n';
					}
				}
				htmlTxt += '</select>';
				this.itemEntryWindow.document.getElementById("selCategory").innerHTML = htmlTxt;
				this.itemEntryWindow.document.getElementById("selCat").selectedIndex = selIdx;
			}
			// update the new item location on the map
			this.updateItemLocation(true, true);
			// alter the UI
			this.setupUIAdd(true);
			// tell our controller that we're ready to add a new item
			this.triggerFlow(eAddNewItem);
		}
	}

	// -----------------------------------------
	// DirectoryApplication::saveEditItem
	// TODO: review confirmation mechanism
	this.saveEditItem = function()
	{	// save the modified item
		if(this.validateItem())
		{	// it is valid, so put the item data into a structure for processing
			var itemStructure = this.structureItem();
			// request the service to add the item
			gSvc.replaceItem(gData.getItemListName(), itemStructure);
		}
	}

	// -----------------------------------------
	// DirectoryApplication::saveNewItem
	// TODO: review confirmation mechanism
	this.saveNewItem = function()
	{	// save the new item
		if(this.validateItem())
		{	// it is valid, so put the item data into a structure for processing
			var itemStructure = this.structureItem();
			// request the service to add the item
			gSvc.addItem(gData.getItemListName(), itemStructure);
		}
	}
	// -------------------------------------------------------------------------------------------
	//            I T E M   M A N I P U L A T I O N   
	// -------------------------------------------------------------------------------------------
	// Methods that process new and/or edited items in memory.

	// -----------------------------------------
	// DirectoryApplication::structureItem
	this.structureItem = function()
	{	// assemble the item data into a structure, be sure to trim whitespace from text entry fields
		var itm = "";
		if(this.itemEntryWindow != null)
		{	// gather the data together here, then let the service guy
			// figure out how to actually store it long-term
			itm += "<id>" + this.itemEntryWindow.document.getElementById("txtId").value + "</id>";
			// administrative data
			if(this.itemEntryWindow.document.getElementById("radUnapproved").checked)
				itm += "<approvalStatus>" + this.itemEntryWindow.document.getElementById("radUnapproved").value + "</approvalStatus>";
			if(this.itemEntryWindow.document.getElementById("radActive").checked)
				itm += "<approvalStatus>" + this.itemEntryWindow.document.getElementById("radActive").value + "</approvalStatus>";		
			if(this.itemEntryWindow.document.getElementById("radInactive").checked)
				itm += "<approvalStatus>" + this.itemEntryWindow.document.getElementById("radInactive").value + "</approvalStatus>";
			itm += "<approvalContact>" + (this.itemEntryWindow.document.getElementById("txtContact").value).trimStr() + "</approvalContact>";
			itm += "<approvalStartDate>" + "" + "</approvalStartDate>";
			itm += "<approvalEndDate>" + "" + "</approvalEndDate>";
			// advertisement data
			var adType = "";
			if(this.itemEntryWindow.document.getElementById("radNone").checked)
				adType = this.itemEntryWindow.document.getElementById("radNone").value;
			if(this.itemEntryWindow.document.getElementById("radBanner").checked)
				adType = this.itemEntryWindow.document.getElementById("radBanner").value;
			if(this.itemEntryWindow.document.getElementById("radCard").checked)
				adType = this.itemEntryWindow.document.getElementById("radCard").value;
			if(this.itemEntryWindow.document.getElementById("radSkyscraper").checked)
				adType = this.itemEntryWindow.document.getElementById("radSkyscraper").value;
			if(this.itemEntryWindow.document.getElementById("radTile").checked)
				adType = this.itemEntryWindow.document.getElementById("radTile").value;
			var graPath = "";
			var graName = "";
			switch(adType)
			{
				case "full":
					graPath = this.itemEntryWindow.document.getElementById("full").src;
					graName = graPath.substring(graPath.lastIndexOf("/") + 1, graPath.length);
					itm += "<approvalAdGraphicBanner>" + gConfig.domainid + "/" + graName + "</approvalAdGraphicBanner>";
					itm += "<approvalAdGraphicCard>" + "" + "</approvalAdGraphicCard>";
					itm += "<approvalAdGraphicSkyscraper>" + "" + "</approvalAdGraphicSkyscraper>";
					itm += "<approvalAdGraphicTile>" + "" + "</approvalAdGraphicTile>";
					break;
				case "card":
					graPath = this.itemEntryWindow.document.getElementById("card").src;
					graName = graPath.substring(graPath.lastIndexOf("/") + 1, graPath.length);
					itm += "<approvalAdGraphicBanner>" + "" + "</approvalAdGraphicBanner>";
					itm += "<approvalAdGraphicCard>" + gConfig.domainid + "/" + graName + "</approvalAdGraphicCard>";
					itm += "<approvalAdGraphicSkyscraper>" + "" + "</approvalAdGraphicSkyscraper>";
					itm += "<approvalAdGraphicTile>" + "" + "</approvalAdGraphicTile>";
					break;
				case "sky":
					graPath = this.itemEntryWindow.document.getElementById("sky").src;
					graName = graPath.substring(graPath.lastIndexOf("/") + 1, graPath.length);
					itm += "<approvalAdGraphicBanner>" + "" + "</approvalAdGraphicBanner>";
					itm += "<approvalAdGraphicCard>" + "" + "</approvalAdGraphicCard>";
					itm += "<approvalAdGraphicSkyscraper>" + gConfig.domainid + "/" + graName + "</approvalAdGraphicSkyscraper>";
					itm += "<approvalAdGraphicTile>" + "" + "</approvalAdGraphicTile>";
					break;
				case "rect":
					graPath = this.itemEntryWindow.document.getElementById("rect").src;
					graName = graPath.substring(graPath.lastIndexOf("/") + 1, graPath.length);
					itm += "<approvalAdGraphicBanner>" + "" + "</approvalAdGraphicBanner>";
					itm += "<approvalAdGraphicCard>" + "" + "</approvalAdGraphicCard>";
					itm += "<approvalAdGraphicSkyscraper>" + "" + "</approvalAdGraphicSkyscraper>";
					itm += "<approvalAdGraphicTile>" + gConfig.domainid + "/" + graName + "</approvalAdGraphicTile>";
					break;
				case "none":
					itm += "<approvalAdGraphicBanner>" + "" + "</approvalAdGraphicBanner>";
					itm += "<approvalAdGraphicCard>" + "" + "</approvalAdGraphicCard>";
					itm += "<approvalAdGraphicSkyscraper>" + "" + "</approvalAdGraphicSkyscraper>";
					itm += "<approvalAdGraphicTile>" + "" + "</approvalAdGraphicTile>";
					break;
			}
			itm += "<approvalAdNavigate>" + (this.itemEntryWindow.document.getElementById("txtNavigate").value).trimStr() + "</approvalAdNavigate>";
			itm += "<approvalAdKeyword>" + "" + "</approvalAdKeyword>";
			itm += "<approvalAdWeight>" + this.itemEntryWindow.document.getElementById("selWeight").options[this.itemEntryWindow.document.getElementById("selWeight").selectedIndex].value + "</approvalAdWeight>";
			// marker data
			itm += "<markerLatitude>" + (this.itemEntryWindow.document.getElementById("txtLatitude").value).trimStr() + "</markerLatitude>";
			itm += "<markerLongitude>" + (this.itemEntryWindow.document.getElementById("txtLongitude").value).trimStr() + "</markerLongitude>";
			itm += "<markerAddress>" + (this.itemEntryWindow.document.getElementById("txtAddress").value).trimStr() + "</markerAddress>";
			itm += "<markerPhone>" + (this.itemEntryWindow.document.getElementById("txtPhone").value).trimStr() + "</markerPhone>";
			itm += "<markerEmail>" + (this.itemEntryWindow.document.getElementById("txtEmail").value).trimStr() + "</markerEmail>";
			// handle the url/url text a little special
			var urlVal = (this.itemEntryWindow.document.getElementById("txtUrl").value).trimStr();
			var urlTextVal = (this.itemEntryWindow.document.getElementById("txtLink").value).trimStr();
			itm += "<markerUrl>" + urlVal + "</markerUrl>";
			if(urlVal == "")
			{	// no url - no link text will be saved either
				itm += "<markerUrl>" + "" + "</markerUrl>";
				itm += "<markerUrlText>" + "" + "</markerUrlText>";
			}
			else
			{	// we have a url, but do we have link text?
				if(urlTextVal != "")
				{	// url & link text both provided
					itm += "<markerUrl>" + urlVal + "</markerUrl>";
					itm += "<markerUrlText>" + urlTextVal + "</markerUrlText>";
				}
				else
				{	// url provided, but no link text... create it ourselves
					itm += "<markerUrl>" + urlVal + "</markerUrl>";
					var startAt = 7;
					var endAt = urlVal.indexOf("/",startAt+1);
					if(endAt <= startAt) endAt = urlVal.length;
					urlTextVal = urlVal.substring(startAt,endAt);
					itm += "<markerUrlText>" + urlTextVal + "</markerUrlText>";
				}
			}
			// content data
			itm += "<contentTitle>" + (this.itemEntryWindow.document.getElementById("txtTitle").value).trimStr() + "</contentTitle>";
			itm += "<contentSummary>" + (this.itemEntryWindow.document.getElementById("txtSummary").value).trimStr() + "</contentSummary>";
			itm += "<contentDescription>" + "" + "</contentDescription>";
			itm += "<contentCategory>" + this.itemEntryWindow.document.getElementById("selCat").options[this.itemEntryWindow.document.getElementById("selCat").selectedIndex].value + "</contentCategory>";
			itm += "<contentSubcategory>" + "" + "</contentSubcategory>";
			itm += "<contentUrl>" + "" + "</contentUrl>";
			// photo data
			var imgpath = this.itemEntryWindow.document.getElementById("thumb").src;
			var thbname = imgpath.substring(imgpath.lastIndexOf("/") + 1, imgpath.length);
			itm += "<contentPhotoThumbnail>" + gConfig.domainid + "/" + thbname + "</contentPhotoThumbnail>";
			itm += "<contentPhotoFullsize>" + "" + "</contentPhotoFullsize>";
		}
		return itm;
	}

	// -----------------------------------------
	// DirectoryApplication::validateItem
	// TODO: do additional checks for data validity
	this.validateItem = function()
	{	// validate the new/edited item content
		var allDataValid = true;
		var errObj = null;
		var exceptionString = "This data is invalid because:<br>";
		if(this.itemEntryWindow != null)
		{	// validate all text entry data fields
			// Latitude is required
			errObj = gData.validateNumber("Latitude",this.itemEntryWindow.document.getElementById("txtLatitude").value,true);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
			// Longitude is required
			errObj = gData.validateNumber("Longitude",this.itemEntryWindow.document.getElementById("txtLongitude").value,true);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
			// Admin contact/email
			errObj = gData.validateEmail("Admin e-mail",this.itemEntryWindow.document.getElementById("txtContact").value,false);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
			// business contact/email
			errObj = gData.validateEmail("Contact e-mail",this.itemEntryWindow.document.getElementById("txtEmail").value,false);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
			// url for ad navigation
			errObj = gData.validateURL("Ad URL",this.itemEntryWindow.document.getElementById("txtNavigate").value,false);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
			// url for business contact
			errObj = gData.validateURL("Contact URL",this.itemEntryWindow.document.getElementById("txtUrl").value,false);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
			// url text for business contact
			errObj = gData.validateText("Contact URL text",this.itemEntryWindow.document.getElementById("txtLink").value,false);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
			// title text
			errObj = gData.validateText("Title",this.itemEntryWindow.document.getElementById("txtTitle").value,true);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
			// summary text
			errObj = gData.validateText("Summary",this.itemEntryWindow.document.getElementById("txtSummary").value,true);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
			/*
			// description text
			errObj = gData.validateText("Description",this.itemEntryWindow.document.getElementById("txtDescription").value,false);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
			*/
			// contact street address
			errObj = gData.validateText("Address",this.itemEntryWindow.document.getElementById("txtAddress").value,false);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
			// contact phone number
			errObj = gData.validatePhone("Phone",this.itemEntryWindow.document.getElementById("txtPhone").value,false);
			if(errObj.isError)
			{
				allDataValid = false;
				exceptionString += "* " + errObj.errMsg + "<br>";
				errObj = null;
			}
		}
		if(!allDataValid)
		{	// finish formatting the exception string and display it
			exceptionString += '<br>';
			gMsg.displayMsg(4,exceptionString);
		}
		return allDataValid;
	}

	// -------------------------------------------------------------------------------------------
	//            L I S T   M A N I P U L A T I O N   
	// -------------------------------------------------------------------------------------------
	// Set of methods that manage the selectable lists of items, both 'all' and 'current', and
	// for both 'user' and 'admin' modes.

	// -----------------------------------------
	// DirectoryApplication::setupCurrentList
	this.setupCurrentList = function(clearMarkers)
	{	// set up our data to show the currently mapped items
		if(clearMarkers)
		{	// clear the map of current markers
			gMap.removeAllMapMarkers();
		}
		// get the bounds of the map and then trim the list down to the ones that might show
		var mapBounds = gMap.getMapBounds();
		gData.createCurrentItemList(mapBounds.maxLat, mapBounds.maxLng, mapBounds.minLat, mapBounds.minLng)
		// now update the map to show those items
		this.updateCurrentMap();
	}

	// -----------------------------------------
	// DirectoryApplication::showItemList
	this.showItemList = function()
	{	// show the item list...
		var xsl = "";	// pathname of xsl file
		// assign the xml source
		var xmlDoc = gData.sourceData;
		var xslPath = gData.getDataPath();
		if(this.isAdminMode)
		{	// in admin mode we show all selected categories, regardless of status
			xsl = xslPath + "/AdminItemList.xsl";
		}
		else
		{	// in user mode, we show only active items by category selected
			xsl = xslPath + "/ActiveCategoryItemList.xsl";
		}
		// do the transform based on the browser
		this.showList("ListA", xmlDoc, xsl);
	}

	// -----------------------------------------
	// DirectoryApplication::showCurrentList
	this.showCurrentList = function()
	{	// show the items currently mapped... it changes each time the map is manipulated
		var xsl = "";					// pathname of xsl file
		// assign the xml source
		var xmlDoc = gData.currentData;
		var xslPath = gData.getDataPath();
		// xsl-directed display varies by mode and by category
		if(this.isAdminMode)
		{	// in admin mode we show all selected categories, regardless of status.
			xsl = xslPath + "/AdminMappedItemList.xsl";
		}
		else
		{	// in user mode, we show only active items by category selected
			xsl = xslPath + "/ActiveCategoryMappedItemList.xsl";
		}
		// do the transform based on the browser
		this.showList("CurA", xmlDoc, xsl);
	}

	// -----------------------------------------
	// DirectoryApplication::showList
	this.showList = function(elemId, xmlDoc, xsl)
	{	// show the list in the proper location using the proper data with the proper transform
		var catList = "";				// current categories to show
		var xmlXform = null;			// loaded xsl
		var xslTemplate = null;			// IE xsl processor template
		var xslProcessor = null;		// IE xsl processor
		var ffXsltProcessor = null;		// mozilla xsl processor
		// determine our currently selected categories
		catVal = this.category;
		var catName = "cat";
		// do the transform based on the browser
		if(window.ActiveXObject)
		{	// IE specific processing
			// load the transform
			xmlXform = new ActiveXObject('MSXML2.FreeThreadedDOMDocument.3.0');	// was 5.0
			xmlXform.async = false;
			xmlXform.load(xsl);
			// load the template (for faster processing and to be able to pass parameters)
			xslTemplate = new ActiveXObject("Msxml2.XSLTemplate.3.0");			// was 5.0
			xslTemplate.stylesheet = xmlXform;
			// create an xslt processor, spec the parameter, and do it
			xslProcessor = xslTemplate.createProcessor();
			xslProcessor.input = xmlDoc;
			xslProcessor.addParameter(catName, catVal, "");
			xslProcessor.transform();
			// show the result
			document.getElementById(elemId).innerHTML = xslProcessor.output;
		}
		else if(document.implementation && document.implementation.createDocument && XSLTProcessor)
		{	// Mozilla, Firefox processing
			// load the transform
			xmlXform = document.implementation.createDocument("","",null);
			xmlXform.async = false;
			xmlXform.load(xsl);								// XSL
			// create an xslt processor, spec the parameter, and do it
			ffXsltProcessor = new XSLTProcessor();
			ffXsltProcessor.importStylesheet(xmlXform);
			ffXsltProcessor.setParameter(null, catName, catVal);
			var resultDoc = ffXsltProcessor.transformToDocument(xmlDoc);
			// create a results processor and transform the xmldoc to a string
			var resultPro = new XMLSerializer();
			var resultStr = resultPro.serializeToString(resultDoc);
			// show the result
			document.getElementById(elemId).innerHTML = resultStr;			
		}
		else
		{
			gMsg.displayMsg(3,"Your browser does not support XSLT processing.");
		}	
	}
	// -------------------------------------------------------------------------------------------
	//            M A P   M A N I P U L A T I O N   
	// -------------------------------------------------------------------------------------------
	// Set of methods that interact with the map to reflect recent changes and/or user interaction.

	// -----------------------------------------
	// DirectoryApplication::updateCurrentMap
	// NOTE: the logic here MUST match the xsl transform
	//		updateCurrentMap controls the marker display and the
	//		xsl transform controls the corresponding list display
	this.updateCurrentMap = function()
	{	// review the current list and update the map display with markers
		var mappedItems = gData.currentData;
		if(mappedItems != null)
		{
			var itemList = mappedItems.getElementsByTagName("item");		
			var itemCount = itemList.length;
			// if there are excess markers to be displayed, we must start over
			if(gMap.okToShowMarkers(itemCount))
			{	// for each item in the list, get the marker data and add the marker to the map
				for(var i=0; i<itemCount; i++)
				{
					var thisItem = itemList[i];
					var thisItemId = thisItem.getElementsByTagName("id")[0].firstChild.nodeValue;
					// get the marker data for this item
					var markerData = thisItem.getElementsByTagName("marker")[0];
					var lat = parseFloat(markerData.getElementsByTagName("latitude")[0].firstChild.nodeValue);
					var lng = parseFloat(markerData.getElementsByTagName("longitude")[0].firstChild.nodeValue);
					// set icon based on category
					var contentData = thisItem.getElementsByTagName("content")[0];
					var itemCategory = contentData.getElementsByTagName("category")[0].firstChild.nodeValue;
					var iconimage = itemCategory + ".gif";
					// determine if the marker should be displayed
					if(this.category.indexOf(itemCategory) != -1)
					{	// the item category is currently selected
 						var approvalData = thisItem.getElementsByTagName("approval")[0];
						var iconimageapproval = approvalData.getElementsByTagName("status")[0].firstChild.nodeValue;
						if(iconimageapproval == 'active')
						{	// always show active items
							gMap.addMapMarker(lat, lng, thisItem, gConfig.domainid + "/" + iconimage, (this.itemToOpen == thisItemId));
						}
						else
						{	// non-active items only display in admin mode
							if(this.isAdminMode)
							{	// show it after replacing the icon image
								iconimage = iconimageapproval + ".gif";
								gMap.addMapMarker(lat, lng, thisItem, gConfig.domainid + "/" + iconimage, (this.itemToOpen == thisItemId));
							}
						}
					}
				}
				// reset the item to open
				this.itemToOpen = 0;
				// tell the controller we are ready
				this.triggerFlow(eCurListOk);
			}
			else
			{	// too many markers to show - performance problem - tell the controller
				this.triggerFlow(eMaxMarkers);
			}
		}
	}

	// -----------------------------------------
	// DirectoryApplication::updateItemLocation
	this.updateItemLocation = function(isNew, isFirst, lat, lng)
	{	// update the new item location
		if(this.itemEntryWindow != null)
		{
			// if we've been here before, remove just the previous marker
			if(isFirst)
				{gMap.removeAllMapMarkers();}
			else
				{gMap.removeLastMapMarker();}
			var setting = gMap.getCurrentMapSettings();
			if(isNew)
			{	// use the current map settings
				this.itemEntryWindow.document.getElementById("txtLatitude").value = setting.lat;
				this.itemEntryWindow.document.getElementById("txtLongitude").value = setting.lng;
				gMap.addMapMarker(setting.lat, setting.lng, null, gConfig.domainid + "/newitem.gif", false);
			}
			else
			{	// use the lat/lng given to us
				this.itemEntryWindow.document.getElementById("txtLatitude").value = lat;
				this.itemEntryWindow.document.getElementById("txtLongitude").value = lng;
				gMap.addMapMarker(lat, lng, null, gConfig.domainid + "/newitem.gif", false);
			}
		}
	}

	// -----------------------------------------
	// DirectoryApplication::updateMapFeatures
	this.updateMapFeatures = function()
	{	// show any map features / permanent icons / corrections
		var allItems = gData.featureData;
		if(allItems != null)
		{	// as long as there are any features to plot
			var itemList = allItems.getElementsByTagName("item");		
			var itemCount = itemList.length;
			// process all features to add to the map
			for(var i=0; i<itemCount; i++)
			{
				var thisItem = itemList[i];
				var approvalData = thisItem.getElementsByTagName("approval")[0];
				var approvalStatus = approvalData.getElementsByTagName("status")[0].firstChild.nodeValue;
				// the item must be active in order to be displayed
				if(approvalStatus == "active")
				{
					var contentData = thisItem.getElementsByTagName("content")[0];
					var contentCategory = contentData.getElementsByTagName("category")[0].firstChild.nodeValue;
					// the category must start with "feature" or "correction" in order to be included
					if((contentCategory.indexOf("feature") >= 0) || (contentCategory.indexOf("correction") >= 0))
					{	// assign the name of the icon
						var iconimage = contentCategory + ".gif";
						// get the location data for this item
						var markerData = thisItem.getElementsByTagName("marker")[0];
						var lat = parseFloat(markerData.getElementsByTagName("latitude")[0].firstChild.nodeValue);
						var lng = parseFloat(markerData.getElementsByTagName("longitude")[0].firstChild.nodeValue);
						// add the feature / correction to the map
						gMap.addMapFeature(lat, lng, gConfig.domainid + "/" + iconimage);
					}
				}
			}
		}
	}

	// -----------------------------------------
	// DirectoryApplication::updateStartLocation
	this.updateStartLocation = function()
	{	// update the start location (from edit config)
		if(this.editConfigWindow != null)
		{
			var setting = gMap.getCurrentMapSettings();
			this.editConfigWindow.document.getElementById("txtLatitude").value = setting.lat;
			this.editConfigWindow.document.getElementById("txtLongitude").value = setting.lng;
			// this maybe should not be limited to Google!
			if(gConfig.provider == "Google")
			{	
				gMap.removeLastMapMarker();	// unnecessary the first time through?
				gMap.addMapMarker(setting.lat, setting.lng, null, gConfig.domainid + "/newitem.gif", false);
			}
			gMsg.displayMsg(1,"Starting coordinates updated.");
			this.editConfigWindow.focus();
		}
	}

	// -------------------------------------------------------------------------------------------
	//            P H O T O   U P L O A D   M A N I P U L A T I O N   
	// -------------------------------------------------------------------------------------------
	// Set of methods that manage image uploads, both from data entry (ads & photos)
	// and site setup (defaults). 

	// -----------------------------------------
	// DirectoryApplication::checkUploadComplete
	// TODO: review update of edit window status
	this.checkUploadComplete = function()
	{	// process the completion of the upload process
		var state = "";
		var baseWin = null;
		if(!this.uploadWindow.closed)
		{	// retrieve the upload state
			state = this.uploadWindow.document.getElementById("errState").value;
		}
		// check if we uploaded default or item-related images
		if(this.uploadId == "default" || this.uploadId != 0)
		{	// either a default upload or non-zero item upload
			if(this.uploadId == "default")
				{baseWin = this.editConfigWindow;}
			else
				{baseWin = this.itemEntryWindow;}
			switch(state)
			{
				case "init":
					// just started - wait a sec and try again
					baseWin.status = "Uploading...";
					var tid = setTimeout("gApp.checkUploadComplete()", 1000);
					break;
				case "ok":
					// upload successful - close the window
					baseWin.status = "Success";
					gMsg.displayMsg(1,"Upload successful.");
					// if successful, the upload process writes out a cookie with the new image name(s)
					// NOTE: if the cross-domain cookie isn't working (see defects) one might think this
					// would not work as well, but it does, because in admin mode, the page cannot be
					// integrated, so the cross-domain restriction is not in effect!
					var val = gData.getMapCookie(this.uploadId);
					if(val != null)
					{	// got the cookie!
						var typeOffset = 0;
						var extOffset = 0;
						var imgType = "";
						if(this.uploadId == "default")
						{	// if it is a default thumbnail, set the config guy 
							// then do the log entry, nothing gets displayed
							typeOffset = val.lastIndexOf("-");
							extOffset = val.lastIndexOf(".");
							imgType = val.substring(typeOffset+1, extOffset);
							if(imgType == "t") gConfig.useDefaultThumb = true;
							gSvc.addLogEntry(gConfig.domainid, "Uploaded " + val);
						}
						else
						{	// something was uploaded, figure out what it was so we can display it
							if(val.indexOf("&") >= 0)
							{	// a fullsize image file AND a thumbnail were created BUT full is n/a for Directory app
								// reset val to just thumb image name
								var thumbOffset = val.indexOf("&");
								val = val.substring(thumbOffset+1, val.length);
							}
							typeOffset = val.lastIndexOf("-");
							extOffset = val.lastIndexOf(".");
							imgType = val.substring(typeOffset+1, extOffset);
							// display the uploaded image
							var imgElement = "";
							if(imgType == "t") imgElement = "thumb";
							else if(imgType == "f") imgElement = "full";
							else if(imgType == "c") imgElement = "card";
							else if(imgType == "s") imgElement = "sky";
							else if(imgType == "r") imgElement = "rect";
							else imgElement = "";
							if(imgType != "" && imgElement != "")
							{	// show the uploaded image in the entry window
								baseWin.document.getElementById(imgElement).src = "Images/" + gConfig.domainid + "/" + val;
								gSvc.addLogEntry(gConfig.domainid, "Uploaded " + val);
							}
						}
					}
					this.uploadId = 0;
					this.uploadWindow.close();
					baseWin.focus();
					break;
				case "error":
					// failure - report the error
					this.uploadId = 0;
					baseWin.status = "Failed";
					gMsg.displayMsg(3, "Upload failed!");
					this.uploadWindow.close();
					baseWin.focus();
					break;
			}
		}
	}

	// -----------------------------------------
	// DirectoryApplication::uploadDefault
	this.uploadDefault = function(type)
	{	// process an image upload
		if(this.editConfigWindow != null)
		{	// set the item id
			this.uploadId = "default";
			// create the uploadWindow, if needed
			if(!this.uploadWindow)
				{this.makeNewUploadWindow(this.uploadId, type);} 
			else if(this.uploadWindow.closed) 
				{this.makeNewUploadWindow(this.uploadId, type);}
			else
				{this.uploadWindow.focus();}
		}
	}

	// -----------------------------------------
	// DirectoryApplication::uploadImage
	this.uploadImage = function(type)
	{	// process an image upload
		if(this.itemEntryWindow != null)
		{	// make sure the link is valid for the type
			var okayToUpload = false;
			if(type=="thumb")
				okayToUpload = true;
			else if(type=="full" && this.itemEntryWindow.document.getElementById("aBan").disabled == false)
				okayToUpload = true;
			else if(type=="card" && this.itemEntryWindow.document.getElementById("aCar").disabled == false)
				okayToUpload = true;
			else if(type=="rect" && this.itemEntryWindow.document.getElementById("aRec").disabled == false)
				okayToUpload = true;
			else if(type=="sky" && this.itemEntryWindow.document.getElementById("aSky").disabled == false)
				okayToUpload = true;
			if(okayToUpload)
			{	// set the item id
				this.uploadId = this.itemEntryWindow.document.getElementById("txtId").value;
				// create the uploadWindow, if needed
				if(!this.uploadWindow)
					{this.makeNewUploadWindow(this.uploadId, type);} 
				else if(this.uploadWindow.closed) 
					{this.makeNewUploadWindow(this.uploadId, type);}
				else
					{this.uploadWindow.focus();}
			}
		}
	}
	
	// -------------------------------------------------------------------------------------------
	//            W I N D O W   C R E A T I O N   
	// -------------------------------------------------------------------------------------------
	// Set of methods that create new window objects for data entry and data display

	// -----------------------------------------
	// DirectoryApplication::makeNewEditConfigWindow
	this.makeNewEditConfigWindow = function()
	{	// create a new window in which we can manage the edit config presentation
		var editConfigURL = "Entry_SiteConfig.htm";
		this.editConfigWindow = window.open(editConfigURL,"","toolbar=no,status=no,menubar=no,scrollbars=yes,resizable=yes,height=400,width=540")
	}

	// -----------------------------------------
	// DirectoryApplication::makeNewLogWindow
	this.makeNewLogWindow = function()
	{	// create a new window in which we can display our stats log
		var logURL = gData.logFilename;
		this.logWindow = window.open(logURL,"","toolbar=yes,status=yes,menubar=yes,scrollbars=yes,resizable=yes,height=600,width=800")
	}

	// -----------------------------------------
	// DirectoryApplication::makeNewHelpWindow
	this.makeNewHelpWindow = function()
	{	// create a new window in which we can display our help text
		var helpURL = "";
		if(this.isAdminMode)
			{helpURL = "Help_Admin.htm";}
		else
			{helpURL = "Help_User.htm";}
		this.helpWindow = window.open(helpURL,"","toolbar=no,status=no,menubar=no,scrollbars=yes,resizable=yes,height=600,width=600")
	}

	// -----------------------------------------
	// DirectoryApplication::makeNewAboutWindow
	this.makeNewAboutWindow = function()
	{	// create a new window in which we can display our about text
		var aboutURL = "Help_About.htm";
		this.aboutWindow = window.open(aboutURL,"","toolbar=no,status=yes,menubar=no,scrollbars=yes,resizable=yes,height=400,width=600")
	}
	
	// -----------------------------------------
	// DirectoryApplication::makeNewItemEntryWindow
	this.makeNewItemEntryWindow = function(parm)
	{	// create a new window in which we can manage the item entry presentation
		var entryURL = "Entry_MarkerItem.htm?" + parm;
		this.itemEntryWindow = window.open(entryURL,"","toolbar=no,status=no,menubar=no,scrollbars=yes,resizable=yes,height=440,width=680")
	}

	// -----------------------------------------
	// DirectoryApplication::makeNewUploadWindow
	this.makeNewUploadWindow = function(id, type)
	{	// create a new window in which we can manage photo & image uploads
		var uploadURL = "PhotoMgr.aspx?app=" + gConfig.domainid + "&id=" + id + "&type=" + type;
		this.uploadWindow = window.open(uploadURL,"","toolbar=no,status=no,menubar=no,scrollbars=no,resizable=no,height=300,width=500")
	}
}

