		State = {
                        inSelection: false,
                        actionMode: 'reservation',
                        actionModeDisplay: 'provisional'
        	}

		NewReservationClass = Class.create();
        	NewReservationClass.prototype = {
                	bookingType: false,
        		displayType: false,
        		checkin: false,
        		checkinX: false,
        		checkinY: false,
        		checkout: false,
        		checkoutX: false,
        		checkoutY: false,
        		nights: false,

        		initialize: function(bookingType, displayType, checkin, checkinX, checkinY, checkout, checkoutX, checkoutY) {
                         	this.bookingType = bookingType;
                         	this.displayType = displayType;
                         	this.checkin = checkin;
                         	this.checkinX = checkinX;
                         	this.checkinY = checkinY;
                         	this.checkout = checkout;
                         	this.checkoutX = checkoutX;
                         	this.checkoutY = checkoutY;

                         	this.nights = checkoutX - checkinX;
                         	//a real reservation must have at least 1 night
                                if (this.nights < 1 && this.bookingType == 'reservation') {
                                	this.checkin = false;
					this.checkout = false;
					this.bookingType = false;
                         		this.displayType = false;
                         		Grid.init();

                         		return false;
                                }

                         	//turn off dragging
                                Event.stopObserving(document, 'mousedown', DraggerMouseStart, false);
				Event.stopObserving(document, 'mouseup', DraggerMouseEnd, false);
				//create the interface for defining the new reservation
                         	this.generatePopup();
              		},

                        generatePopup: function() {
                     		//populate and configure the popup that will contain our new reservation info
                     		Element.update('GHRpopup-new-text', 'You have selected ' + (this.nights) + ' nights for this reservation; <a href="https://www.pinelandfarms.org/guesthouses/reservation.php?checkin=' + this.checkin + '&checkout=' + this.checkout + '&reservable_id=' + GHRReservables[this.checkinY - 1] + '">Complete booking details</a>. Or <a href="javascript: NewReservation.teardown(); Grid.init()">cancel.</a>');

                               	//determine the location of the new popup on the screen
                                this.positionPopup();
                                Element.show('GHRpopup-new');
                     	},

                        positionPopup: function() {
                                //set the positional styles for popup, which should already be an existing element

                                //find out which cell the box pops up in
                                var cell = Grid.getCell(this.checkinX, this.checkinY).getElement();
                               	var popup = $('GHRpopup-new');

                                //get our default position
                                var x = Position.cumulativeOffset(cell)[0] + Element.getDimensions(cell).height / 2;
                                var y = Position.cumulativeOffset(cell)[1] + Element.getDimensions(cell).width / 2;

                                //decide exactly where to orient the popup. If the right side is short on space, shift left
                                var screenDim = Grid.availableScreen();
                                var spaceAvailable = screenDim[0] - x;
                                var popupWidth = Element.getDimensions(popup).width;
                                while (popupWidth > spaceAvailable && x > 0) {
                                    	x -= 5;

                                        screenDim = Grid.availableScreen();
                                        spaceAvailable = screenDim[0] - x;
                                }

                                Element.setStyle(popup,
					$H({
						left: x + 'px',
        					top: y + 'px'
        				})
        			);
                               	return true;
                        },


                     	teardown: function() {
                     		//reverse the stuff done at initialization
                                Element.hide('GHRpopup-new');

				this.checkin = false;
				this.checkout = false;
				this.bookingType = false;
                         	this.displayType = false;

				//reactivate dragging
                         	Event.observe(document, 'mousedown', DraggerMouseStart, false);
				Event.observe(document, 'mouseup', DraggerMouseEnd, false);
                     	}
        	}

        	CellClass = Class.create();
        	CellClass.prototype = {
        		x: false,
        		y: false,

                 	bookingType: false,
        		displayType: false,
        		dateString: false,
        		reservationIds: false,

                    	initialize: function(x, y, bookingType, displayType, dateString, reservationIds) {
                         	this.x = x;
                         	this.y = y;
                         	this.bookingType = bookingType;
                         	this.displayType = displayType;
                         	this.dateString = dateString;
                         	this.reservationIds = reservationIds;
              		},

              		selectable: function(newBookingType) {
              			//true if this cell can be selected for the given bookingType
              			if (this.bookingType == 'block') {
              				return false;
              			}

				if (State.inSelection && Dragger.startX < this.x) {
              				//the drag path cannot be blocked. so the cell to the left of this one must also
              				//be selectable in order for this one to be selectable
                                	if (this.x > 1) {
                                		lx = this.x-1;
                                        	lcell = GHRCells[lx-1][this.y-1];

                                        	if (!lcell.selectable(newBookingType)) {
                                        		return false;
                                        	}
                                	}
                                }

              			if (this.bookingType == 'reservation') {
              				if (GHRConfig.allowTransitionDays) {
              					if (newBookingType == 'reservation') {
              						//need to allow it if this is a potential transition day
                                                	ress = this.getReservations();
                                               	 	if (ress.length == 1) {
                                                		//there is 1 reservation in this cell. so it is possible it is a potential transition day
                                                		res = ress[0];
              							if (res.checkin == this.dateString || res.checkout == this.dateString) {
              								//this cell is a potential transition day, which can be included in a new reservation.
              								//But a checkin day must end a selection, and a checkout must begin it
                                                       			//If we're not selecting multiple days, then transition days cannot be selected
                                                        		if (State.inSelection) {
                                                                		if (res.checkout == this.dateString) {
                                                                			//this is a checkout. it has to be the start of the selection
                                                                        		if (Dragger.startX == this.x) {
                                                                        			return true;
                                                                        		}
                                                                		}

                                                                		if (res.checkin == this.dateString) {
                                                                			//this is a checkin. it has to be the end of the selection
                                                                        		if (Dragger.endX == this.x) {
                                                                        			return true;
                                                                        		}
                                                                		}
                                                        		}
              							}
              						}
              					}
              				}
                                        return false;
              			}

              			return true;
              		},

              		update: function(selected, newBookingType, newBookingDisplayType) {
              			var el = this.getElement();
                        	if (el) {
                        		if (selected && this.selectable(newBookingType)) {
                        			displayType = newBookingDisplayType;
                        		} else {
                        			displayType = this.displayType;
                        		}
                        		switch (displayType) {
                        			case "provisional":
                        				el.className = 'dateCellSelected';
                        				break;
                        			case "confirmed":
                        				el.className = 'dateCellBooked';
                        				break;
                        			default:
                        				el.className = 'dateCellFree';
                        				break;
                        		}
                        	}
                        	if (selected && this.selectable(newBookingType)) {
                        		return true;
                        	} else {
                        		return false;
                        	}
              		},

              		getElement: function() {
              			//return cell Element at coords x and y, or null if none there
                                return $(Grid.cellPrefix + this.x + '-' + this.y);
              		},

              		getReservations: function() {
              			//return array of reservation objects that this cell is a part of, if any
                                if (this.reservationIds) {
                                	ret = new Array();
                                	for (var i=0;i<this.reservationIds.length;i++) {
                                        	res = GHRReservations[this.reservationIds[i]];
                                        	if (res) {
                                        		ret[ret.length] = res;
                                        	}
                                        }
                                        return ret;
                                }
                                return [];
              		},

              		inspect: function() {
              			return this.x + '-' + this.y;
              		}
        	}

        	GridClass = Class.create();
                GridClass.prototype = {
                	width: false,
              		height: false,
              		cellPrefix: false,

			initialize: function(width, height, cellPrefix) {
                         	this.width = width;
                         	this.height = height;
                         	this.cellPrefix = cellPrefix;
              		},

              		getCellElement: function(x,y) {
              			//return cell Element at coords x and y, or null if none there
              			var cell = GHRCells[x-1][y-1];
                        	if (cell) {
                                	return cell.getElement();
                                }
                                return null;
              		},

              		getCell: function(x,y) {
              			//return cell Element at coords x and y, or null if none there
              			var cell = GHRCells[x-1][y-1];
                        	if (cell) {
                                	return cell;
                                }
                                return null;
              		},

              		updateCell: function(x,y,selected,newBookingType,newBookingDisplayType) {
              			var cell = GHRCells[x-1][y-1];
                        	if (cell) {
                        		return cell.update(selected,newBookingType,newBookingDisplayType);
                        	}
                        	return false;
        		},

        		cellSelectable: function(x,y,newBookingType) {
        			var cell = GHRCells[x-1][y-1];
                        	if (cell) {
                        		return cell.selectable(newBookingType);
                        	}
                        	return false;
        		},

              		init: function() {
                        	//reset table cell states
        			for (var a=1;a<=this.width;a++) {
                        		for (var b=1;b<=this.height;b++) {
                        			this.updateCell(a, b, false);
                        		}
                        	}
                        },

                        pointedCell: function(ev, mx, my) {
                         	//return array of coords for which cell the mouse is pointing at during event ev, or null if none
                         	//allow one or both mouse coords to be passed in
                         	if (mx == null) {
                                	mx = Event.pointerX(ev);
                         	}
                         	if (my == null) {
                                	my = Event.pointerY(ev);
                         	}
                                for (var x=1;x<=this.width;x++) {
                        		for (var y=1;y<=this.height;y++) {
                        			if (Position.within(this.getCellElement(x,y), mx, my)) {
                        				return [x,y];
                        			}
                        		}
                        	}
                        	return null;
                        },

                        positionPopup: function(resId) {
                        	//Position and size the popup for rservation with id ResId
                                if (res = GHRReservations[resId]) {
                                	//set the positional styles for popup, which should already be an existing element

                                	//find out which cell the box pops up in
                                	var resPopBox = this.reservationPopupCell(resId);
                                        var cell = this.getCell(resPopBox[0], resPopBox[1]).getElement();
                                        var popup = $('GHRpopup-' + resId);

                                        //get our default position
                                        var x = Position.cumulativeOffset(cell)[0] + Element.getDimensions(cell).height / 2;
                                        var y = Position.cumulativeOffset(cell)[1] + Element.getDimensions(cell).width / 2;

                                    	//decide exactly where to orient the popup. If the right side is short on space, shift left
                                    	var screenDim = this.availableScreen();
                                        var spaceAvailable = screenDim[0] - x;
                                        var popupWidth = Element.getDimensions(popup).width;
                                        while (popupWidth > spaceAvailable && x > 0) {
                                                x -= 5;

                                                screenDim = this.availableScreen();
                                                spaceAvailable = screenDim[0] - x;
                                        }

                                        Element.setStyle(popup,
						$H({
							left: x + 'px',
        						top: y + 'px'
        					})
        				);
                                        return true;
                                }
                                return false;
                        },

                        reservationPopupCell: function(resId) {
                        	//return array with [x,y] of the Grid box where reservation resId should orient its popup
                                if (res = GHRReservations[resId]) {
                                        return [res.popupX, res.popupY];
                                }
                                return null;
                        },

                        availableScreen: function() {
                        	//return array with width and height of currently viewed window
                        	var myWidth = 0, myHeight = 0;
 				if (typeof(window.innerWidth) == 'number') {
  					myWidth = window.innerWidth;
  					myHeight = window.innerHeight;
 				} else if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
  					myWidth = document.documentElement.clientWidth;
  					myHeight = document.documentElement.clientHeight;
 				} else if (document.body && (document.body.clientWidth || document.body.clientHeight)) {
  					myWidth = document.body.clientWidth;
  					myHeight = document.body.clientHeight;
 				}

 				return [myWidth, myHeight];
                        }
        	}

        	DraggerClass = Class.create();
              	DraggerClass.prototype = {
              		startX: false,
              		startY: false,
              		endX: false,
              		endY: false,
              		startMx: false,
              		startMy: false,

              		initialize: function() {
              		},

              		startMouseTrack: function(ev) {
        			//start tracking mouse movement if click happened in one of our tracked boxes
        			if (State.actionMode == 'none') {
        				return true;
        			}

        			mx = Event.pointerX(ev);
        			my = Event.pointerY(ev);
        			boxCoords = Grid.pointedCell(ev,null,null);
                        	if (boxCoords != null) {
                        		x = boxCoords[0];
                        		y = boxCoords[1];

                        		Grid.init();  //we can only create one new entry on the Grid per pageload
                                       	State.inSelection = true;
                                       	this.startX = x;
                                        this.startY = y;
                                        this.endX = x;
                                        this.endY = y;
                                        this.startMx = mx;
              				this.startMy = my;

					if (Grid.cellSelectable(x, y, State.actionMode)) {
						//Event.observe(document, 'selectstart', function(ev){return false;}, false);
                                        	if (Grid.updateCell(x, y, true, State.actionMode, State.actionModeDisplay)) {
                                        		Event.observe(document, 'mousemove', DraggerMouseDuring, false);
                                        		return true;
                                        	}
                                        	//Event.stopObserving(document, 'selectstart', function(ev){return false;}, false);
                                        }

                                        //failed to select first cell
                                        this.startX = false;
                                        this.startY = false;
                                        this.endX = false;
                                        this.endY = false;
                                        this.startMx = false;
              				this.startMy = false;
                                        State.inSelection = false;
                        	}

                        	return true;
        		},

        		duringMouseTrack: function(ev) {
        			//continue tracking mouse movement -- see if the mouse crosses into other boxes
        			mx = Event.pointerX(ev);
                		boxCoords = Grid.pointedCell(ev,null,this.startMy);
                        	if (boxCoords != null) {
                        		x = boxCoords[0];
                        		y = boxCoords[1];
                                        //Update elements if this box is to the right of the start box
                                        if (y == this.startY) {
                                                if (x >= this.startX) {
                                                	var oldEndX = this.endX;
                        				var oldEndY = this.endY;
                        				this.endX = x;
                                        		this.endY = y;
                                                	if (Grid.cellSelectable(x, y, State.actionMode)) {
                                                   		//update all elements in the interim
                                                        	for (var xx=1;xx<=Grid.width;xx++) {
                                                                	if (xx >= this.startX && xx <= x) {
                                                                		Grid.updateCell(xx, y, true, State.actionMode, State.actionModeDisplay);
                                                                	} else {
                                                                		Grid.updateCell(xx, y, false);
                                                                	}
                                                   		}
                                                   	} else {
                                                   		//backup the end point
                                                   		this.endX = oldEndX;
                                        			this.endY = oldEndY;
                                                   	}
                                                }
                                       	}
                        	}
                        	return true;
                        },

                        endMouseTrack: function(ev) {
                        	//finish tracking mouse movement
        			Event.stopObserving(document, 'mousemove', DraggerMouseDuring, false);
        			//Event.stopObserving(document, 'selectstart', function(ev){return false;}, false);
        			State.inSelection = false;

        			//create the new reservation
        			if (this.startX && this.startY && this.endX && this.endY) {
        				checkinCell = Grid.getCell(this.startX, this.startY);
        				checkoutCell = Grid.getCell(this.endX, this.endY);
                                	NewReservation = new NewReservationClass(State.actionMode, State.actionModeDisplay, checkinCell.dateString, this.startX, this.startY, checkoutCell.dateString, this.endX, this.endY);
                           	}

                           	//clear out the Dragger state
                        	this.startX = false;
                                this.startY = false;
                                this.endX = false;
                                this.endY = false;
                                this.startMx = false;
              			this.startMy = false;

              			return true;
                        }
        	}

