/**
* @fileoverview This file contains the user object
* @author Andrew Tourtellot, Sara Lin, Lisa Wang, Evan Sperling
* @version 0.1
*/

/**
* This is a floor logging object that records the original data and changes made it to
* @object
*/
var FloorLog = Class.create();

FloorLog.prototype = {
	/**
	* @constructor
	* @param {number} floor the current floor level
	*/
	initialize: function(floor) {
		this.floor = floor;
		/** 
		* indicates whether the administrator has uploaded a new floorplan image.
		* @type boolean 
		*/
		this.uploadImg = false;
		/** an array of newly added items */
		this.addedItems = null;
		/** an array of existing items that have been modified */
		this.changedItems = null;
	}
}

/**
* This is a user object.  The current User object contains both some admin sign in functionalities, tab transition functionalities, and administrator item coordinates editing functionalities.  This object should be separated into several smaller modules.  Currently, most of administrator editing functionalities has been shifted to AdminTab.
* @object
*/
var User = Class.create();

User.prototype = {
	/**
	* editMapSubject is an observer that is called whenever the admin leaves the page or floor when the editing has not been finished.
	* @type Subject
	*/
	editMapSubject: null,

	/**
	* initializes all event listeners
	*/
	initialize: function(element, event) {
		//array of all floor, each item corresponds to a floor and stores all changes made to it
		//ex: adminFloor[1].uploadImg==true iff admin has uploaded a new floor image
		this.adminFloor = new Array(6);
		thisAdminFloor = this.adminFloor;
		for (var index=0; index<6; index++) {
			thisAdminFloor[index] = new FloorLog(index);
		}
	
		//signin/out event handlers
		this.signIn = this._signIn.bindAsEventListener(this);
		this.signOut = this._signOut.bindAsEventListener(this);
		
		//page event handlers
		this.mapPage = this._mapPage.bindAsEventListener(this);
		this.listPage = this._listPage.bindAsEventListener(this);
		this.helpPage = this._helpPage.bindAsEventListener(this);
		
		//variables
		this.signedIn = false;
		this.signInHTML = null;
		this.tab = null;
		
		this.signOutLink = $('logout');
		this.showTab = this._showTab.bindAsEventListener(this);
		Event.observe(this.signOutLink, "click", this.signOut);
		
		this.coordEditButtonObserver = null;
		this.click2add = null;
		this.createItem = this._createItem.bindAsEventListener(this);
		this.clickAdder = this._clickAdder.bindAsEventListener(this);
		this.dropAdder = this._addPoint.bindAsEventListener(this);
		this.dropDeller = this._deletePoint.bindAsEventListener(this);
		this.hotSpotter = this._toggleHotSpot.bindAsEventListener(this);
		this.editItemCoords = this._editItemCoords.bindAsEventListener(this);
		this.coordEditingMode = 0; // 0 -> not editing, 1 -> editing coordinates, 2-> click-adding coordinates
		this.resetItemCoords = this._resetItemCoords.bindAsEventListener(this);
		this.deleteItem = this._deleteItem.bindAsEventListener(this);
		
		this.curEditItem = null;
		this.curDivs = new Array();
		this.curSpot = null;
		this.tempID = 0; // temporary unique item identifier for new items
		
		this.submitEverything = this._commitMapEdits.bindAsEventListener(this);
		
		Event.observe(element, event, this.showTab);
	},

	/**
	* loads the admin tab view file and displays the sign in form
	*/
	_showTab: function() {
		var tabResults = Main.tabs.loadTab('admin','Admin',true,true);
		if (tabResults.madeNew) {
			var thisObj = this;
			this.tab = tabResults.tab;
			var url = base_url+'admin/showAdminTab';
			new Ajax.Request(url, {
				onSuccess: function(response) {
					thisObj.signInHTML = response.responseText;
					thisObj.tab.tabElem.innerHTML = thisObj.signInHTML;
					Event.observe('signInForm', "submit", thisObj.signIn);
				}
			});
		}
		Main.tabs.showTab('admin');
	},
	
	/**
	* invoked when the administrator submits the sign in form.  Validates the user on the server, shows error message if inputs are not found on the admin database.  If another admin is currently logged in, then an error message would show the username of the logged in user and his/her email address.
	* @event
	*/
	_signIn: function(event) {
		var data = Event.element(event).serialize(true);
		//var username = data.username;
		//var password = data.password;
		var username = $('usr').value;
		var password = $('pwd').value;
		//hide all error messages
		$$('#signInForm .errorMsg span').each(function(e) {
			e.style.display='none';
		});
		$$('#signInForm .errorMsg')[0].style.display='none';
			
		if (username == "" || password == "") {
			$$('#signInForm .errorMsg .mandat')[0].style.display='';
			$$('#signInForm .errorMsg')[0].style.display='';
		} 
		else {	
			//validate username and password in server
			var thisObj = this;
			
			//encode the url
			var url = base_url+'admin/signIn';
			var queryArray = new Array(username,sha1Hash(password));
			url = encodeURL(url, queryArray);
			
			new Ajax.Request(url, {
				onSuccess: function(response) {
					var json = response.responseText.evalJSON();
					if (json.signedIn == "good") {	
						thisObj.signedIn = true;
						//clear and hide the form
						$('adminContent').innerHTML = "";
						//attach admin page event handlers
						thisObj.pageEvents(true);
				
						//show all elements with class "signedIn"
						$$('.signedIn').each(function(el) {
							el.style.display='';
						});
						
						thisObj._mapPage(null);
					}
					else if (json.signedIn == "bad") {
						$$('#signInForm .errorMsg .valid')[0].style.display='';
						$$('#signInForm .errorMsg')[0].style.display='';
						$('pwd').value = "";
					}
					else {
						$$('#signInForm .errorMsg .dblocked')[0].innerHTML = json.signedIn + " is currently logged in.  Send " + json.signedIn + " an <a href = mailto:" + json.email + ">email</a>?";
						$$('#signInForm .errorMsg .dblocked')[0].style.display='';
						$$('#signInForm .errorMsg')[0].style.display='';
						$('pwd').value = "";
					}
				}
			});
		}
	},

	/**
	* removing admin only contents and detach all admin event listeners.
	* @param {number} timeout
	*/
	prepareSignOut: function(timeout) {
		ItemTab.unloadEditTab();
	
		//hide the elements that shows only when user is logged in
		$$('.signedIn').each(function(el) {
			el.style.display='none';
		});
		
		//delete all elements that only shows when user is logged in
		$$('.admin').each(function(el) {
			el.parentNode.removeChild(el);
		});
		
		//show all original files before admin made changes
		$$('.original').each(function(el) {
			el.style.display='';
		});
		
		//if timed out, display time out error message
		if (timeout) {
			$$('#signInForm .errorMsg .timeout')[0].style.display='';
		}
		
		//this.signInHTML.style.display='';
		//$('logout').innerHTML = "(logout)";
		this.pageEvents(false);
	},
	
	/**
	* signs out.  prompts alert if the admin hasn't finish editing coordinates.
	*/
	_signOut: function() {
		// only sign out if we're good to go with coordinate editing
		if (!this.editMapSubject.notifyObservers(null))
			return false;
			
		this.signedIn = false;
		this.prepareSignOut(false);
	
		var thisObj = this;
		var url = base_url+'admin/signOut/';
		new Ajax.Request(url, {
			onSuccess: function(response) {
				thisObj.tab.tabElem.innerHTML = thisObj.signInHTML;
				Event.observe('signInForm', "submit", thisObj.signIn);
			}
		});
	},
	//END ADMIN VALIDATION FUNCTIONS//
	
	//BEGIN PAGE LOADING FUNCTIONS///////////////////////////////////////////////////////////////////////
	/**attach or detach page loading event handler 
	* @param {boolean} attach whether to attach or detach the event listeners
	*/
	pageEvents: function(attach) {
		if (attach) {
			Event.observe($('mapLink'), 'click', this.mapPage);
			Event.observe($('listLink'), 'click', this.listPage);
			Event.observe($('helpLink'), 'click', this.helpPage);
			//Event.observe($('accountLink'), 'click', this.accountPage);
		}
		else {
			Event.stopObserving($('mapLink'), 'click', this.mapPage);
			Event.stopObserving($('listLink'), 'click', this.listPage);
			Event.stopObserving($('helpLink'), 'click', this.helpPage);
			//Event.stopObserving($('accountLink'), 'click', this.accountPage);
		}
	},
	
	/** 
	* load map edit form, use AdminTab.uploadMap for upload functionalities, attach editMapSubject observers.
	* @event
	*/
	_mapPage: function(event) {
		if ($('editMapPage')==undefined) {
			thisObj = this;
			new Ajax.Request(base_url+'admin/getPage/map', {
				evalScripts: true,
				onSuccess: function(response) {	
					//insert and show signedIn buttons and html
					//new Insertion.Before(thisObj.signInHTML, response.responseText);
					$('adminContent').innerHTML = response.responseText;
					
					AdminTab.uploadMap.initialize();
					CoordToolbox.initialize();
					
					thisObj.editMapSubject = new Subject();
					thisObj.editMapSubject.attachObserver('uploadMap', AdminTab.uploadMap.promptReset);
					thisObj.editMapSubject.attachObserver('editCoord', thisObj._turnOffCoordEditing);
				}
			});
		}
			
		this.showPage('editMapPage');
	},
	
	/** 
	* load list page.  the functionalities for editing predefined lists is in AdminTab.editPredefinedLists
	* @event
	*/
	_listPage: function(event) {
		if (!this.editMapSubject.notifyObservers(null))
			return false;
			
		AdminTab.editPredefinedList.load();
		this.showPage('editListPage');
	},
	
	/** 
	* load help page.  the functionalities for editing predefined lists is in AdminTab.editHelp
	* @event
	*/
	_helpPage: function(event) {
		if (!this.editMapSubject.notifyObservers(null))
			return false;
			
		AdminTab.editHelp.load();
		this.showPage('editHelpPage');
	},
	
	/** 
	* hide all other pages, show only page with this pageId
	* @param {String} pageID such as "editHelpPage" or "editMapPage"
	*/
	showPage: function(pageId) {
		//get all page elements w/ className=="page"
		$$('#adminContent .page').each(function(page) {
			if (page.id==pageId) {
				page.style.display='';
			}
			else {
				page.style.display='none';
			}
		});
		
		var pageLink = pageId.substring(4, pageId.length-4).toLowerCase()+"Link";
		$$('#admin-tab .tabPages a').each(function(link) {
			if(link.id==pageLink) {
				link.addClassName('active');
			}
			else 
				link.removeClassName('active');
		});
	},
	//END PAGE LOADING FUNCTIONS//////////////////////////////////////////////////////////////////////////////////////
	
	// BEGIN COORDINATE EDITING SECTION
	
	/** sets up observers and contents for admin tab for editing coordinates */
	setUpAdminTab: function() {
		// observe coordinate edit toggle button
		var element = $('activateCoordEdit');
		if (element != null) {
			if (this.coordEditButtonObserver != null) {
				Event.stopObserving(element,"click",this.coordEditButtonObserver);
			}
			this.coordEditButtonObserver = this._editCoords.bindAsEventListener(this);
			Event.observe(element,"click",this.coordEditButtonObserver);
		}
		
		element = $('newItemForm');
		if (element) {
			Event.observe(element,"submit",this.createItem);
		}
		var elements = $$('.coordSubmit');
		var thisObj = this;
		// observe attach _commitMapEdits to all submit buttons
		elements.each(function(elem) {
			Event.observe(elem,"submit",thisObj.submitEverything);
		});
		elements = $$('.imageSubmit');
		elements.each(function(elem) {
			Event.observe(elem,"submit",thisObj.submitEverything);
		});
	},
	
	/** sets system up for coordinate editing
	* @event
	*/
	_editCoords: function(event) {
		this.coordEditingMode = 1;
		this.adminFloor[FloorManager.curFloor].addedItems = new Array();
		this.adminFloor[FloorManager.curFloor].changedItems = new Array();
		
		// set new click observers for map items to activate editItemCoordslement = $('activateCoordEdit');
		element = $('activateCoordEdit');
		if (element) {
			element.disabled = true;
			Event.stopObserving(element,"click",this.coordEditButtonObserver);
		var floor = FloorManager.getCurrentFloor();
		if (floor != null) {
			floor.setCoordEditObservers();
		}
		}
		this.coordEditButtonObserver = this._turnOffCoordEditing.bindAsEventListener(this);
		
		var elements = $$('.coordCancel');
		var thisObj = this;
		// attach cancel function to all cancel buttons
		elements.each(function(elem) {
			Event.observe(elem,"click",thisObj.coordEditButtonObserver);
		});

		this.setUpCoordEditToolbox();
		Main.tabs._minButton();
	},
	
	/** sets up floating toolbox for coordinate editing */
	setUpCoordEditToolbox: function() {
	
		var element = $('coordinateToolbox');
		var dropDown = $('itemDropDown');
		var newArray = this.adminFloor[FloorManager.curFloor].addedItems;
		var floor = FloorManager.getCurrentFloor();
		var optionStruct = {
			tag: 'option',
			children: '',
			value: ''
		};
		var optionElem;
			
		if (element) {
			element.style.display = '';
			if (dropDown != null) {
				Event.observe(dropDown,"change",this.editItemCoords);
				//dropDown.observe("change",this.editItemCoords);
				
				// attach items on map to dropdown menu
				if (floor != null) {
					floor.items.each(function(item) {
						if (item != null && item != undefined) {
							optionStruct.value = item.id; // create option element with item name and id
							optionStruct.children = item.name;
							optionStruct.tag = 'option';
							optionElem = $E(optionStruct);
							dropDown.appendChild(optionElem); // attach to dropdown menu
						}
					});
				}
				// attach items created during this session to the dropdown menu
				if (newArray != null) {
					newArray.each(function(item) {
						if (item != null) {
							optionStruct.value = item.tempID;
							optionStruct.children = item.name;
							optionStruct.tag = 'option';
							optionElem = $E(optionStruct);
							dropDown.appendChild(optionElem); // attach to dropdown menu
						}
					});
				}
			}
		}
		else {
			alert("HTML Problem: Could not find coordinate editing dropdown menu element");
		}
	},
	
	/** utility function to make certain toolbox elements disabled or enabled according to the current item's number of points and hotspot status (whether or not it has one).
		when fresh is true, resets all toolbox elements, otherwise uses goingUp to determine which ones need changing based on whether the number of points is going
		up or down */
	toolboxTransition: function(goingUp,fresh) {
		var addDrop = $('pointAdd');
		var addDropK = $('pointAddOK');
		var delDrop = $('pointDelete');
		var delDropK = $('pointDeleteOK');
		var deleteBut = $('deleteItem');
		var resetBut = $('resetCoords');
		var click2addBut = $('click2add');
		var labelCheck = $('dispLabelChBox');
		var hotSpotCheck = $('hotSpot');
		var numPts = -1;
		var isHotSpot = false;
		if (this.curEditItem != null) {
			numPts = this.curEditItem.ref.coordSet.xys.size();
			isHotSpot = this.curEditItem.ref.coordSet.hotSpot.size() > 0;
		}
		// for a new item or deselection, start by disabling entire toolbox
		if (this.curEditItem == null || fresh) {
			// remove all options from add and delete dropdowns
			if (addDrop) {
				if (addDrop.descendants().size() == 0) {
					addDrop.appendChild($E({
						tag: 'option',
						value: '-1',
						children: ''
					}));
				}
				else {
					addDrop.options[0].text = '';
					addDrop.options[0].value = '-1';
					addDrop.descendants().each(function(option) {
						if (option.value != '-1') {
							Element.remove(option);
						}
					});
				}
				addDrop.disabled = 'disabled';
			}
			if (delDrop) {
				if (delDrop.descendants().size() == 0) {
					delDrop.appendChild($E({
						tag: 'option',
						value: '-1',
						children: ''
					}));
				}
				else {
					delDrop.options[0].text = '';
					delDrop.options[0].value = '-1';
					delDrop.descendants().each(function(option) {
						if (option.value != '-1') {
							Element.remove(option);
						}
					});
				}
				delDrop.disabled = 'disabled';
			}
			// deactivate all buttons
			if (addDropK) {
				Event.stopObserving(addDropK,"click",this.dropAdder);
				//addDropK.stopObserving("click",this.dropAdder);
				addDropK.disabled = 'disabled';
			}
			if (delDropK) {
				Event.stopObserving(delDropK,"click",this.dropDeller);
				//delDropK.stopObserving("click",this.dropDeller);
				delDropK.disabled = 'disabled';
			}
			if (hotSpotCheck) {
				Event.stopObserving(hotSpotCheck,"change",this.hotSpotter);
				//hotSpotCheck.stopObserving("change",this.hotSpotter);
				hotSpotCheck.disabled = 'disabled';
				hotSpotCheck.checked = '';
			}
			if (deleteBut) {
				Event.stopObserving(deleteBut,"click",this.deleteItem);
				deleteBut.disabled = 'disabled';
			}
			if (resetBut) {
				Event.stopObserving(resetBut,"click",this.resetItemCoords);
				resetBut.disabled = 'disabled';
			}
			if (click2addBut) {
				if (this.click2add != null) {
					Event.stopObserving(click2addBut,"click",this.click2add);
					this.click2add = null;
				}
				click2addBut.disabled = 'disabled';
			}
			if (labelCheck) {
				labelCheck.disabled = 'disabled';
				labelCheck.checked = false;
			}
			// for a newly loaded item, calculate all toolbox stats from scratch
			if (fresh && this.curEditItem) {
				// reset button works as long as there is an item selected
				if (resetBut) {
					resetBut.disabled = '';
					Event.observe(resetBut,"click",this.resetItemCoords);
				}
				// click adder works as long as there is an item selected
				if (click2addBut) {
					click2addBut.disabled = '';
					this.click2add = this._click2add.bindAsEventListener(this);
					Event.observe(click2addBut,"click",this.click2add);
				}
				// so does delete and the label checkbox, as long as the item is not deleted already
				if (!(this.curEditItem.deleted)) {
					if (deleteBut) {
						deleteBut.disabled = '';
						Event.observe(deleteBut,"click",this.deleteItem);
					}
					if (labelCheck) {
						labelCheck.disabled = '';
						labelCheck.checked = this.curEditItem.ref.coordSet.showLabel;
					}
				}
				if (numPts > 0) {
					// Change first option in add dropdown from "No points" to point 0
					if (addDrop) {
						addDrop.options[0].text = '0';
						addDrop.options[0].value = '0';
						// can only add a point if there is at least one to use as a location basis
						addDrop.disabled = '';
					}
					if (addDropK) {
						addDropK.disabled = '';
						Event.observe(addDropK,"click",this.dropAdder);
					}
					// Change first option in delete dropdown from "No points" to point 0
					if (delDrop) {
						delDrop.options[0].text = '0';
						delDrop.options[0].value = '0';
						// can only delete a point if there is one
						delDrop.disabled = '';
					}
					if (delDropK) {
						delDropK.disabled = '';
						Event.observe(delDropK,"click",this.dropDeller);
					}
					// add point options to drop boxes for each point
					for (i = 1; i < numPts; i++) {
						if (addDrop) {
							addDrop.appendChild($E({
								tag: 'option',
								value: i.toString(),
								children: i.toString()
							}));
						}
						if (delDrop) {
							delDrop.appendChild($E({
								tag: 'option',
								value: i.toString(),
								children: i.toString()
							}));
						}
					}
					// can only create a hot spot if item has points
					if (hotSpotCheck) {
						hotSpotCheck.disabled = '';
						Event.observe(hotSpotCheck,"change",this.hotSpotter);
					}
				}
				// can also toggle hot spot if there is a hot spot to delete
				else if (isHotSpot && hotSpotCheck) {
					hotSpotCheck.disabled = '';
					Event.observe(hotSpotCheck,"change",this.hotSpotter);
				}
				if (isHotSpot && hotSpotCheck) {
					hotSpotCheck.checked = 'checked';
				}
			}
		}
		// for an item that is having a point added or removed
		else {
			if (goingUp) { // going UP in number of points
				if (addDrop) {
					addDrop.appendChild($E({
						tag: 'option',
						value: (numPts-1).toString(),
						children: (numPts-1).toString()
					}));
					// may need to remove former 'No points' option
					if (numPts == 1) {
						Element.remove(addDrop.descendants().first());
						addDrop.disabled = '';
					}
				}
				if (delDrop) {
					delDrop.appendChild($E({
						tag: 'option',
						value: (numPts-1).toString(),
						children: (numPts-1).toString()
					}));
					// may need to remove former 'No points' option
					if (numPts == 1) {
						Element.remove(delDrop.descendants().first());
						delDrop.disabled = '';
					}
				}
				if (numPts == 1) {
					// need to add listeners for hotspot toggle only if there was no hot spot before
					if (!isHotSpot && hotSpotCheck) {
						hotSpotCheck.disabled = '';
						Event.observe(hotSpotCheck,"change",this.hotSpotter);
					}
					if (delDropK) {
						delDropK.disabled = '';
						Event.observe(delDropK,"click",this.dropDeller);
						//delDropK.observe("click",this.dropDeller);
					}
					if (addDropK) {
						addDropK.disabled = '';
						Event.observe(addDropK,"click",this.dropAdder);
					}
				}
			}
			else { // going DOWN in number of points
				if (addDrop) {
					// if the last item was selected, move the selection to the next to last
					if (numPts > 1) {
						if (addDrop.options[numPts].selected) {
							addDrop.options[numPts].selected = false;
							addDrop.options[numPts-1].selected = true;
						}
					}
					// may need to insert "no points" option
					if (numPts == 0) {
						addDrop.options[0].text = '';
						addDrop.options[0].value = '-1';
						addDrop.disabled = 'disabled';
					}
					else {
						Element.remove(addDrop.descendants().last());
					}
				}
				if (delDrop) {
					// if the last item was selected, move the selection to the next to last
					if (numPts > 1) {
						if (delDrop.options[numPts].selected) {
							delDrop.options[numPts].selected = false;
							delDrop.options[numPts-1].selected = true;
						}
					}
					// may need to insert "no points" option
					if (numPts == 0) {
						delDrop.options[0].text = '';
						delDrop.options[0].value = '-1';
						delDrop.disabled = 'disabled';
					}
					else {
						Element.remove(delDrop.descendants().last());
					}
				}
				if (numPts == 0) {
					// need to remove listeners for hotspot toggle unless there is still a hot spot there to remove
					if (!isHotSpot && hotSpotCheck) {
						hotSpotCheck.disabled = 'disabled';
						Event.stopObserving(hotSpotCheck,"change",this.hotSpotter);
					}
					if (delDropK) {
						delDropK.disabled = 'disabled';
						Event.stopObserving(delDropK,"click",this.dropDeller);
					}
					if (addDropK) {
						addDropK.disabled = 'disabled';
						Event.stopObserving(addDropK,"click",this.dropAdder);
					}
				}
			}
		}
		$("pointAdd").style.display='';
	},
	
	/** sets up click2add mode for inputting new item's coordinates */
	_click2add: function(event) {
		$('coordEditPoint').hide();
	
		var map = $('map');
		var button = $('click2add');
		var cancelButton = $('noClk2add');
		var floor = FloorManager.getCurrentFloor();
		var dropDown = $('itemDropDown');
		var newName = $('newItemName');
		var newType = $('newItemType');
		if (map != null) {
			this.coordEditingMode = 2;
			if (floor != null) {
				floor.unsetJustCoordEditObservers();
			}
			if (this.clickAdder == null) {
				this.clickAdder = this._clickAdder.bindAsEventListener(this);
			}
			Event.observe(map,"mouseup",this.clickAdder);

			// change click2add button to _stopClick2add
			if (button) {
				Event.stopObserving(button,"click",this.click2add);
				button.style.display = 'none';
			}
			this.click2add = this._stopClick2add.bindAsEventListener(this);
			if (cancelButton) {
				Event.observe(cancelButton,"click",this.click2add);
				cancelButton.style.display = '';
			}
			else {
				this.click2add = this._stopClick2add.bindAsEventListener(this);
			}
			// observe dropdown menu for a new item selection; stop click2adding if detected
			if (dropDown != null) {
				Event.observe(dropDown,"change",this.click2add);
			}
		}
	},
	
	/** confirms a new item's creation; puts name/type into array of changed items, adds to dropdown. returns true if required input fields
		have been appropriately filled and the item was created, or false otherwise */
	_createItem: function(event) {
		if (this.coordEditingMode > 1) {
			this._stopClick2add(null);
		}
		if (this.coordEditingMode < 1) {
			this._editCoords(null);
		}
		var newName = $('newItemName');
		var newType = $('newItemType');
		var newArray = this.adminFloor[FloorManager.curFloor].addedItems;
		var dropDown = $('itemDropDown');
		var match = 0;
		if (newName != null && newType != null && dropDown != null) {
			if (newName.value != 'Enter new item name' && newName.value != '' && newType.value != 'none') {
				// create new log entry for this new item
				newEntry = {
					tempID: 'newItem'+this.tempID++,
					name: newName.value,
					type: newType.value,
					coordSet: {
						xys: new Array(),
						hotSpot: new Array(),
						showLabel: true
					}
				};
				newName.value = 'Enter new item name';
				newType.value = 'none';
				newArray[newArray.size()] = newEntry;
				// deselect all current items in dropdown box
				for (i = 0; i < dropDown.options.length; i++) {
					dropDown.options[i].selected = false;
				}
				// create new dropdown box option (selected) for this new item
				dropDown.appendChild($E({
					tag: 'option',
					value: newEntry.tempID,
					children: newEntry.name,
					selected: true
				}));
				this._editItemCoords(null);
				this._click2add(null);
			}
			else {
				alert("You must enter a name and type before creating a new item.");
			}
		}
	},
	
	/** stops click2add mode */
	_stopClick2add: function(event) {
		$('coordEditPoint').show();
		
		var map = $('map');
		var button = $('click2add');
		var cancelButton = $('noClk2add');
		var floor = FloorManager.getCurrentFloor();
		var dropDown = $('itemDropDown');
		if (map != null) {
			this.coordEditingMode = 1;
			if (this.clickAdder != null) {
				Event.stopObserving(map,"mouseup",this.clickAdder);
				//map.stopObserving("click",this.clickAdder);
			}
			if (floor != null) {
				floor.setJustCoordEditObservers();
			}
			// stop observing dropdown menu to stop click2adding
			if (dropDown != null) {
				Event.stopObserving(dropDown,"change",this.click2add);
				//dropDown.stopObserving("change",this.click2add);
			}
			// change click2add button back to _click2add
			if (cancelButton) {
				Event.stopObserving(cancelButton,"click",this.click2add);
				//cancelButton.stopObserving("click",this.click2add);
				cancelButton.style.display = 'none';
			}
			this.click2add = this._click2add.bindAsEventListener(this);
			if (button) {
				Event.observe(button,"click",this.click2add);
				//button.observe("click",this.click2add);
				button.style.display = '';
			}
			this.click2add = this._click2add.bindAsEventListener(this);
		}
	},
	
	/** saves locations of divs into coordinate array for current item */
	saveDivLocs: function() {
		var coords;
		var labelCheck = $('dispLabelChBox');
		if (this.curEditItem) {
			if (labelCheck) {
				this.curEditItem.ref.coordSet.showLabel = labelCheck.checked;
			}
			if (this.curDivs) {
				coords = this.curEditItem.ref.coordSet.xys;
				coords.clear();
				this.curDivs.each(function(div,index) {
					coords[index] = [parseInt(div.style.left.gsub('px','')),
									parseInt(div.style.top.gsub('px',''))];
				});
			}
			if (this.curSpot) {
				this.curEditItem.ref.coordSet.hotSpot[0] = parseInt(this.curSpot.style.left.gsub('px',''));
				this.curEditItem.ref.coordSet.hotSpot[1] = parseInt(this.curSpot.style.top.gsub('px',''));
			}
		}
	},
	
	/** removes the editing divs */
	removeEditingDivs: function() {
		this.curDivs.each(function(div) {
			Element.remove(div);
		});
		this.curDivs.clear();
		if (this.curSpot) {
			Element.remove(this.curSpot);
			this.curSpot = null;
		}
	},
	
	/** removes divs for current item, saves them into FloorLog, warns if number is less than 3, resets toolbox */
	deselectCurrentItem: function() {
		if (this.curEditItem && this.curDivs) {
			if (this.curDivs.size() < 3 && !(this.curEditItem.deleted)) {
				var updateOrAdded;
				var name;
				if (this.curEditItem.added) {
					updateOrAdded = 'added to';
					name = this.curEditItem.ref.name;
				}
				else {
					updateOrAdded = 'updated in';
					name = this.curEditItem.ref.ref.name;
				}
				alert('Warning: You currently have less than three points defining this item, ' + name +
					'. If you do not return to it and add more points before submitting, '+
					'this item will not be '+updateOrAdded+' the database.');
			}
			this.saveDivLocs();
			this.removeEditingDivs();
		}
		var dropDown = $('itemDropDown');
		if (dropDown) {
			dropDown.value = 'none';
		}
		this.curEditItem = null;
		this.toolboxTransition(false,false);
		this.curDivs.clear();
	},
	
	/**
	* for a selected item, create small div tags to represent the coordinates of the item.
	*/
	createDivsForCurItem: function() {
		var floorDiv = $('floor_'+FloorManager.curFloor);
		var thisObj = this;
		var hots = this.curEditItem.ref.coordSet.hotSpot;
		// clear current divs
		
		this.removeEditingDivs();
		if (floorDiv == null || this.curEditItem == null) {
			return;
		}
		
		this.curEditItem.ref.coordSet.xys.each(function(pair,index) {
			var lilDiv = $E({
				tag: 'div',
				className: 'editingDivDot draggable',
				children: index.toString()
			});

			thisObj.curDivs[index] = lilDiv;
			if (floorDiv != null) {
				floorDiv.appendChild(lilDiv);
			}

			pair = $(pair);
			lilDiv.style.left = pair[0]+'px';
			lilDiv.style.top = pair[1]+'px';

			var drag = new Draggable(lilDiv,{zIndex: .5});
			
			Main.draggables.push(drag);
		});
		
		if (hots.size() > 0) {
			this.curSpot = $E({
				tag: 'div',
				className: 'editingDivDot draggable',
				children: 'H'
			});
			if (floorDiv != null) {
				floorDiv.appendChild(this.curSpot);
			}
			var d = { 'left': hots[0]+'px', 'top': hots[1]+'px' };
			$(this.curSpot).setStyle(d);
			var drag = new Draggable(this.curSpot,{zIndex: .5});
			Main.draggables.push(drag);
		}
	},
	
	/** sets up coordinate editing on a specific item
	* @event
	*/
	_editItemCoords: function(event) {
		var dropDown = $('itemDropDown');
		var dropVal;
		var oldArray = this.adminFloor[FloorManager.curFloor].changedItems;
		var newArray = this.adminFloor[FloorManager.curFloor].addedItems;
		var arrayEntry = null;
		var floor = FloorManager.getCurrentFloor();
		var thisObj = this;
		if (dropDown != null) {
			dropVal = dropDown.value;
			this.deselectCurrentItem();
			dropDown.value = dropVal;
			// do nothing past deselecting if there is no new selected item
			if (dropVal == 'none') {
				return;
			}
			// search for item in array of new items
			newArray.each(function(entry) {
				if (thisObj.curEditItem == null && entry.tempID.toString() == dropVal.toString()) {
					thisObj.curEditItem = {
						added: true, // added item
						deleted: false,
						ref: entry
					};
					throw $break;
				}
			});
			// search for item in array of changed items
			if (this.curEditItem == null) {
				oldArray.each(function(entry) {
					if (thisObj.curEditItem == null && entry.id == dropVal) {
						thisObj.curEditItem = {
							added: false, // current (edited or deleted) item
							deleted: entry.deleted,
							ref: entry
						};
						throw $break;
					}
				});
			}
			// find item on entire floor
			if (this.curEditItem == null && floor != null) {
				floor.items.each(function(item) {
					if (item != undefined && item != null && thisObj.curEditItem == null) {
						if (item.id.toString() == dropVal) {
							arrayEntry = {
								id: item.id.toString(),
								ref: item,
								deleted: false,
								coordSet: {
									xys: new Array(),
									hotSpot: null,
									showLabel: item.label
								},
								oldCoordSet: {
									xys: new Array(),
									hotSpot: null,
									showLabel: item.label
								}
							};
							if (item.customHotSpot) {
								arrayEntry.coordSet.hotSpot = item.hotspot.clone();
								// oldCoordSet is only used for reading (when the item is reset) so we can just use the actual item object's array
								arrayEntry.oldCoordSet.hotSpot = item.hotspot;
							}
							else {
								arrayEntry.coordSet.hotSpot = new Array();
								arrayEntry.oldCoordSet.hotSpot = new Array();
							}
							// parse array of xs and ys from image map
							var itemElem = $('item_'+item.id);
							if (itemElem == null) {
								return;
							}
							var tempArray = itemElem.coords.split(',');
							for (i = 1; i < tempArray.size(); i=i+2) {
								arrayEntry.coordSet.xys.push([parseInt(tempArray[i-1]), parseInt(tempArray[i])]);
								arrayEntry.oldCoordSet.xys.push([parseInt(tempArray[i-1]), parseInt(tempArray[i])]);
							}
							oldArray.push(arrayEntry);
							thisObj.curEditItem = {
								added: false,
								deleted: false,
								ref: arrayEntry
							};
							throw $break;
						}
					}
				});
			}
			// fail if we could not find the item selected
			if (this.curEditItem == null) {
				return false;
			}
			this.createDivsForCurItem();
			this.toolboxTransition(false,true);
		}
	},
	
	/** selects given item in the toolbox, and then starts editing on it
	* @param {number} itemID
	*/
	editItemCoordsByID: function(itemID) {
		var dropDown = $('itemDropDown');
		var match = -1;
		if (dropDown != null) {
			for (i = 0; i < dropDown.options.length; i++) {
				if (dropDown.options[i].value == itemID) {
					match = i;
				}
				else {
					dropDown.options[i].selected = false;
				}
			}
			if (match > -1) {
				dropDown.options[match].selected = true;
				this._editItemCoords(null);
			}
		}
	},
	
	/** resets all points to their original values (from their initial values loaded from the database)
	* @event
	*/
	_resetItemCoords: function(event) {
		if (this.coordEditingMode > 1) {
			this._stopClick2add(null);
		}
		if (this.curEditItem != null) {
			// resetting added (new) items will lead to their deletion
			if (this.curEditItem.added) {
				if (confirm('Warning: This item has been added during editing. If you "reset" it, '+
							'it will be removed entirely, and you will have to re-add it from '+
							'scratch. Do you still want to remove it?')) {
					this._deleteItem(null);
				}
			}
			// resetting other items will reset all coordinates and un-delete it
			else {
				if (this.curEditItem.deleted) {
					this.curEditItem.deleted = false;
					this.curEditItem.ref.deleted = false;
				}
				this.curEditItem.ref.coordSet.xys = this.curEditItem.ref.oldCoordSet.xys.clone();
				this.curEditItem.ref.coordSet.hotSpot = this.curEditItem.ref.oldCoordSet.hotSpot.clone();
				this.curEditItem.ref.coordSet.showLabel = this.curEditItem.ref.oldCoordSet.showLabel;
				this.createDivsForCurItem();
				this.toolboxTransition(false,true);
			}
		}
	},
	
	/** deletes current item
	* @event
	*/
	_deleteItem: function(event) {
		var thisObj = this;
		if (this.coordEditingMode > 1) {
			this._stopClick2add(null);
		}
		var oldArray = this.adminFloor[FloorManager.curFloor].changedItems;
		var newArray = this.adminFloor[FloorManager.curFloor].addedItems;
		var dropDown = $('itemDropDown');
		if (this.curEditItem != null) {
			// deleting added (new) items is as simple as removing it from the array
			if (this.curEditItem.added) {
				this.adminFloor[FloorManager.curFloor].addedItems.without(this.curEditItem.ref);
				// remove from drop-down list
				if (dropDown != null) {
					dropDown.descendants().each(function(elem) {
						if (elem != null && elem != undefined) {
							if (elem.value == thisObj.curEditItem.ref.tempID) {
								Element.remove(elem);
							}
						}
					});
				}
				this.deselectCurrentItem();
			}
			// deleting existing items means clearing the coordinates and setting "deleted"
			else {
				this.curEditItem.ref.coordSet.xys.clear();
				this.curEditItem.ref.coordSet.hotSpot.clear();
				this.curEditItem.ref.coordSet.showLabel = false;
				this.curEditItem.deleted = true;
				this.curEditItem.ref.deleted = true;
				this.createDivsForCurItem();
				this.toolboxTransition(false,true);
			}
		}
	},
	
	/** toggles hot spot for an item
	* @event
	*/
	_toggleHotSpot: function(event) {
		this.saveDivLocs();
		var spotCheck = $('hotSpot');
		if (this.curEditItem && spotCheck) {
			var spot = this.curEditItem.ref.coordSet.hotSpot;
			var coords = this.curEditItem.ref.coordSet.xys;
			// if checkbox has been unchecked, remove the hot spot
			if (!spotCheck.checked) {
				spot.clear();
				// disabled toggle button if there are no points to make a new hot spot from
				if (coords.size() == 0) {
					Event.stopObserving(spotCheck,"change",this.hotSpotter);
					//spotCheck.stopObserving("change",this.hotSpotter);
					spotCheck.disabled = 'disabled';
				}
			}
			// otherwise calculate a new one from the current vertices' extremes
			else if (coords.size() > 0) {
				var xNeg = coords[0][0];
				var xPos = coords[0][0];
				var yNeg = coords[0][1];
				var yPos = coords[0][1];
				coords.each(function(pair) {
					if (pair[0] < xNeg) {
						xNeg = pair[0];
					}
					if (pair[0] > xPos) {
						xPos = pair[0];
					}
					if (pair[1] < yNeg) {
						yNeg = pair[1];
					}
					if (pair[1] > yPos) {
						yPos = pair[1];
					}
				});
				spot[0] = (xNeg+xPos)/2;
				spot[1] = (yNeg+yPos)/2;
			}
			this.createDivsForCurItem();
		}
	},
	
	/** 
	* deletes a given point of an item
	* @event
	*/
	_deletePoint: function(event) {
		var dropMenu = $('pointDelete');
		var divs;
		var index;
		if (dropMenu && this.curEditItem) {
			this.saveDivLocs();
			divs = this.curEditItem.ref.coordSet.xys;
			index = parseInt(dropMenu.value);
			if (index >= 0 && index < divs.size()) {
				divs.splice(index,1);
				this.createDivsForCurItem();
				this.toolboxTransition(false,false);
			}
		}
	},
	
	/** 
	* add div in between given point and next one (or, if last point, between this one and first one)
	* @event
	*/
	_addPoint: function(event) {
		var dropMenu = $('pointAdd');
		if (dropMenu && this.curEditItem) {
			var divs = this.curEditItem.ref.coordSet.xys;
			var index = parseInt(dropMenu.value);
			if (index >= 0 && index < divs.size()) {
				var source1 = divs[index];
				var source2;
				// if the last point is selected make a new one in between it and the first point
				if (index == divs.size()-1) {
					source2 = divs[0];
				}
				// otherwise make a new point between the selected one and the one after it
				else {
					source2 = divs[index+1];
				}
				var newX = (source1[0]+source2[0])/2;
				var newY = (source1[1]+source2[1])/2;
				this.pointAdder(index+1,newX,newY);
			}
		}
	},
	
	/** add point at coordinates clicked on on map */
	_clickAdder: function(event) {
		if (isDragging(Main.draggables))
			return;
		
		var pos_x = (event.layerX)? event.layerX : event.x;
		var pos_y = (event.layerY)? event.layerY : event.y;
		if (this.curEditItem) {
			this.pointAdder(this.curEditItem.ref.coordSet.xys.size(),pos_x,pos_y);
		}
	},
	
	/** add point with coordinates x,y to current item at index i.  PRECONDITION: this.curEditItem != null */
	pointAdder: function(i,x,y) {
		// items are automatically un-deleted when they have a point added
		if (this.curEditItem.deleted) {
			this.curEditItem.deleted = false;
			this.curEditItem.ref.deleted = false;
			var delBut = $('deleteItem');
			var labChk = $('dispLabelChBox');
			if (delBut) {
				Event.observe(delBut,"click",this.deleteItem);
				delBut.disabled = '';
			}
			if (labChk) {
				labChk.disabled = '';
			}
		}

		this.saveDivLocs();
		this.curEditItem.ref.coordSet.xys.splice(i,0,[x,y]);
		this.createDivsForCurItem();
		this.toolboxTransition(true,false);
	},
	
	/**
	* checks all modified items for bad input, such as less than three coordinate points for an item.  prompts user for modification.
	*/
	verifySubmit: function(event) {
		/** names of items that were deleted */
		var deletedItemNames = new Array(); 
		/** names of old items that do not have enough points and will not be updated */
		var notEnoughPtsOldNames = new Array(); 
		/** names of new items that do not have enough points and will not be created */
		var notEnoughPtsNewNames = new Array(); 
		var newArray = this.adminFloor[FloorManager.curFloor].addedItems;
		var oldArray = this.adminFloor[FloorManager.curFloor].changedItems;
		
		if (newArray) {
			newArray.each(function(item) {
				if (item.coordSet.xys.size() < 3) {
					notEnoughPtsNewNames.push(item.name);
				}
			});
		}
		if (oldArray) {
			oldArray.each(function(item) {
				if (item.deleted) {
					deletedItemNames.push(item.ref.name);
				}
				else if (item.coordSet.xys.size() < 3) {
					notEnoughPtsOldNames.push(item.ref.name);
				}
			});
		}
		var numBadNew = notEnoughPtsNewNames.size();
		var numBadOld = notEnoughPtsOldNames.size();
		var numDel = deletedItemNames.size();
		if (numBadNew + numBadOld > 0) {
			var prompt = 'Warning:';
			if (numBadNew > 0) {
				prompt = prompt + ' There are less than three points defining ' + notEnoughPtsNewNames[0];
				if (numBadNew == 1) {
					prompt = prompt + ', making it invalid. Since it is a new item, it';
				}
				else { // plural number of items
					if (numBadNew == 2) {
						prompt = prompt + ' and ' + notEnoughPtsNewNames[1];
					}
					else if (numBadNew == 3) {
						prompt = prompt + ', ' + notEnoughPtsNewNames[1] + ', and ' + notEnoughPtsNewNames[2];
					}
					else { // more than three items
						prompt = prompt + ', ' + notEnoughPtsNewNames[1] + ', ' + notEnoughPtsNewNames[2];
						prompt = prompt + ', and others';
					}
					prompt = prompt + ', making them invalid. Since they are new items, they';
				}
				prompt = prompt + ' will therefore not be added to the database at all.'
			}
			if (numBadOld > 0) {
				prompt = prompt + ' There are';
				if (numBadNew > 0) {
					prompt = prompt + ' also';
				}
				prompt = prompt + ' less than three points defining ' + notEnoughPtsOldNames[0];
				if (numBadOld == 1) {
					prompt = prompt + ', making it invalid. Since it is a preexisting item, its';
				}
				else { // plural number of items
					if (numBadOld == 2) {
						prompt = prompt + ' and ' + notEnoughPtsOldNames[1];
					}
					else if (numBadOld == 3) {
						prompt = prompt + ', ' + notEnoughPtsOldNames[1] + ', and ' + notEnoughPtsOldNames[2];
					}
					else { // more than three items
						prompt = prompt + ', ' + notEnoughPtsOldNames[1] + ', ' + notEnoughPtsOldNames[2];
						prompt = prompt + ', and others';
					}
					prompt = prompt + ', making them invalid. Since they are preexisting items, their';
				}
				prompt = prompt + ' coordinates will therefore remain as they were' +
					' in the database before editing.';
			}
			prompt = prompt + ' Do you wish to submit anyway?';
			if (!confirm(prompt)) {
				return false;
			}
		}
		if (numDel > 0) {
			prompt = 'Confirm Item Delete: ' + deletedItemNames[0];
			if (numDel == 1) {
				prompt = prompt + ' has been selected for deletion. This will remove ALL of its' +
					' properties and information from the database. Are you sure you wish to delete it?';
			}
			else { // plural number of items
				if (numDel == 2) {
					prompt = prompt + ' and ' + deletedItemNames[1];
				}
				else if (numDel == 3) {
					prompt = prompt + ', ' + deletedItemNames[1] + ', and ' + deletedItemNames[2];
				}
				else { // more than three items
					prompt = prompt + ', ' + deletedItemNames[1] + ', ' + deletedItemNames[2];
					prompt = prompt + ', and others';
				}
				prompt = prompt + ' have been selected for deletion. This will remove ALL of their properties' +
					' and information from the database. Are you sure you wish to delete them?';
			}
			if (!confirm(prompt)) {
				return false;
			}
		}
		return true;
	},
	
	/**
	* checks for deleted items and takes the invalid changes from the list of modifications made.
	*/
	compileDeletedItemsAndTrimInvalids: function() {
		if (this.coordEditingMode == 0) {
			return null;
		}
		var deleteds = new Array();
		var newArray = this.adminFloor[FloorManager.curFloor].addedItems;
		var newNewArray = new Array();
		var oldArray = this.adminFloor[FloorManager.curFloor].changedItems;
		var newOldArray = new Array();
		
		if (newArray) {
			newArray.each(function(item,index) {
				if (item.coordSet.xys.size() >= 3) {
					newNewArray.push(item);
				}
			});
			newArray.clear();
			this.adminFloor[FloorManager.curFloor].addedItems = newNewArray;
		}
		if (oldArray) {
			oldArray.each(function(item) {
				if (item.deleted) {
					deleteds.push(item);
				}
				else if (item.coordSet.xys.size() >= 3) {
					newOldArray.push(item);
				}
			});
			oldArray.clear();
			this.adminFloor[FloorManager.curFloor].changedItems = newOldArray;
		}
		if (deleteds.size() == 0) {
			return null;
		}
		return deleteds;
	},
	
	/** sends all PHP requests to commit updates.  this function updates all changes made to floorplan upldate, existing item coordinates and new items. 
	* However, floorplan updates are not updated using this.*/
	_commitMapEdits: function(event) {
		this.deselectCurrentItem();
		if (!this.verifySubmit()) {
			return;
		}
		var deletedItems = this.compileDeletedItemsAndTrimInvalids();
		var newStuff = this.adminFloor[FloorManager.curFloor].addedItems;
		var updatedStuff = this.adminFloor[FloorManager.curFloor].changedItems;
		var updatedMap = this.adminFloor[FloorManager.curFloor].uploadImg;
		var url;
		var paramObj;
		var parmString;
		var floor = FloorManager.getCurrentFloor();
		var totalRequests = 0;
		var progress = 0;
		if (updatedStuff) {
			totalRequests = totalRequests + updatedStuff.size();
		}
		if (newStuff) {
			totalRequests = totalRequests + newStuff.size();
		}
		if (deletedItems) {
			totalRequests = totalRequests + deletedItems.size();
		}
		if (updatedMap) {
			totalRequests++;
		}
		// submit all deleted items
		if (deletedItems) {
			deletedItems.each(function(item) {
				url = base_url+'itemEdit/delItem/'+item.id;
				new Ajax.Request(url, {
					onSuccess: function(response) {
						progress++;
						/* reload floor after edits are made */
						if (floor && progress >= totalRequests) {
							floor.uploadFloor();
						}
					}
				});
			});
		}
		// submit all changed but preexisting items
		if (updatedStuff) {
			updatedStuff.each(function (item) {
				url = base_url+'itemEdit/updateCoords/';
				paramObj = {
					id: parseInt(item.id),
					coordinates: item.coordSet.xys,
					hotspot: item.coordSet.hotSpot,
					label: item.coordSet.showLabel
				};
				paramString = Object.toJSON(paramObj);
				new Ajax.Request(url, {
					onSuccess: function(response) {
						progress++;
						/* reload floor after edits are made */
						if (floor && progress >= totalRequests) {
							floor.uploadFloor();
						}
					},
					method: 'post',
					parameters: {'itemData': paramString}
				});
			});
			updatedStuff.clear();
		}
		// submit all new items
		if (newStuff) {
			newStuff.each(function(item) {
				url = base_url+'itemEdit/addItem/';
				paramObj = {
					coordinates: item.coordSet.xys,
					hotspot: item.coordSet.hotSpot,
					label: item.coordSet.showLabel,
					floor: FloorManager.curFloor,
					itemTypeID: item.type,
					name: item.name
				};
				paramString = Object.toJSON(paramObj);
				new Ajax.Request(url, {
					onSuccess: function(response) {
						progress++;
						/* reload floor after edits are made */
						if (floor && progress >= totalRequests) {
							floor.uploadFloor();
						}
					},
					method: 'post',
					parameters: {'itemData': paramString}
				});
			});
			newStuff.clear();
		}
		if (updatedMap) {
			url = base_url+'admin/commitMapImg/'+FloorManager.curFloor;
			var thisObj = this;
			new Ajax.Request(url, {
				onSuccess: function(response) {
					progress++;
					/* reload floor after edits are made */
					if (floor && progress >= totalRequests) {
						floor.uploadFloor();
					}
					thisObj._cancelImgUpload(null);
				}
			});
		}
		/** TODO: display confirmation/error/"progress bar" (or just percentage) (pass in function to ajax request to update "progress bar") **/
		this.curEditItem = null;
		this.adminFloor[FloorManager.curFloor].uploadImg = false;
		this._turnOffCoordEditing(null);
	},
	
	/** turns off coordinate editing mode but prompts user to save changes if necessary. returns true if coordinate editing mode has been turned off, false otherwise */
	_turnOffCoordEditing: function(event) {
		if (Main.user.coordEditingMode == 0) {
			return true;
		}
		if (Main.user.coordEditingMode > 1) {
			Main.user._stopClick2add(null);
		}
		Main.user.deselectCurrentItem();
		if (Main.user.adminFloor[FloorManager.curFloor].addedItems.size() +
			Main.user.adminFloor[FloorManager.curFloor].changedItems.size() > 0) {
			if (!confirm('Warning: You have edited map item coordinates without submitting those edits '+
				'to the database. If you do not submit them, they will be discarded at this time. '+
				'Are you sure you wish to continue?')) {
				return false;
			}
		}
		Main.user.coordEditingMode = 0;
		
		Main.user.adminFloor[FloorManager.curFloor].addedItems = null;
		Main.user.adminFloor[FloorManager.curFloor].changedItems = null;
		
		var element = $('coordinateToolbox');
		if (element != null) {
			element.style.display = 'none';
		}
		$('newItemForm').enable();
		element = $('itemDropDown');
		if (element != null) {
			Event.stopObserving(element,"change",Main.user.editItemCoords);
			
			/* Remove options from the item select element */
			element.descendants().each(function(elem) {
				if (elem != null && elem != undefined) {
					if (elem.value != 'none') {
						Element.remove(elem);
					}
				}
			});
		}
			
		var floor = FloorManager.getCurrentFloor();
		if (floor != null) {
			floor.unsetCoordEditObservers();
		}
		
		element = $('activateCoordEdit');
		Main.user.coordEditButtonObserver = Main.user._editCoords.bindAsEventListener(Main.user);
		if (element) {
			Event.observe(element,"click",Main.user.coordEditButtonObserver);
			//element.observe("click",Main.user.coordEditButtonObserver);
			element.disabled=false;
		}
		Main.tabs._maxButton(null);
		return true;
	}
	// END COORDINATE EDITING SECTION
}