// For use in property2.asp for searching and displaying property query results

// All property information is stored in an array ready to be regurgitated
// in response to events. Even though there could be a hundred records or
// so, it is still more efficient to do it this way than resubmitting the
// page each time and executing SQL against the text file (which causes
// lots of file locking, etc on Windoze).

// Naughty global variables used to keep track of what is selected on-screen.

var properties = new Array();
var searchResults;
var numMatches = 0;
var currSearchIdx = 0;
var currPropIdx = 1;
var defaultImg = 'p';
var thumbImg = 't';
var displayType = "AList";
var propsDir = "/properties/";
var imagesDir = propsDir + "images/";
var noThumb = imagesDir + "noPropThumb.png";
var noPic = imagesDir + "noPropPic.png";
var popWin = null;

// A Property object that holds all info we need about a particular property.
// An array of these objects (properties[]) contains all currently available properties.
function Property(pID, bID, pSt, pArea, pType, pConn, postcode, price, beds, heat, avail, desc, remarks, imgStr) {
	this.propID = pID;
	this.branchID = bID;
	this.propStreet = pSt;
	this.propArea = pArea;
	this.propType = pType;
	this.connected = pConn;
	this.postcode = postcode;
	this.price = price;
	this.numBedrooms = beds;
	this.heatType = heat;
	this.availCode = avail;
	this.descript = desc;
	this.remarks = remarks;
	this.imageStr = imgStr.split("+");
}

// This object holds info about a property that matches the given criteria
function MatchedProperty(ptr, price) {
	this.ptrToProp = ptr;
	this.price = price;
}

// Populate the searchable array.
// These doubly-indexed arrays (no searching required) hold the current list of
// property IDs that match the specified criteria (All = ".+" ).
// The next/prev/show property buttons, etc will just select entries
// from this list and retrieve the full set of info for the currently
// selected property from the properties() array.
// Note that the lo and hi Price values contain equality operators such as
// <= and >= signs... the whole expression can then have eval() applied
function setSearchResults() {
	reSpaces = / /g;

	// Initialise everything so there are no nasty entries lurking from last time
	numMatches = 0;
	searchResults = null;
	searchResults = new Array();

	// Find matching properties based on the given criteria and add them to the
	// searchResults array. The lack of end '$' in the reType is intentional
	var reArea = "properties[propIdx].propArea.match(/^.+/i)";
	var reType = "properties[propIdx].propType.match(/^.+/i)";
	var reConn = "properties[propIdx].connected.match(/^.+/i)";
	var reBeds = "properties[propIdx].numBedrooms.match(/^.+/i)";
	var priceStr = "properties[propIdx].price > 0";
	for ( propIdx in properties ) {
		if (eval(reArea) && eval(reConn) && eval(reType) && eval(reBeds) && eval(priceStr)) {
			searchResults[numMatches] = new MatchedProperty(propIdx, properties[propIdx].price);
			numMatches++;
		}
	}

	// Sort the searchResults by price
	searchResults.sort(comparePrices);

	// Create the list of matching properties and work out which div to show
	if (numMatches > 0) {
		currSearchIdx = 0;
		createPropertyList();
		displayType = getDisplayType();
		setDisplayType(displayType);
	} else {
		setDisplayType("NotFound");
	}
}

// Generate a full list of properties from the current search results
function createPropertyList() {
	// Locate the div element in the HTML to hold the list
	divNumMatches = document.getElementById("NumMatches");
	divPropList = document.getElementById("PropertyList");

	// Clear out the old nodes...
	nodeList = divPropList.childNodes;
	numNodes = nodeList.length;
	for ( idx = numNodes-1; idx >= 0; idx-- ) {
		divPropList.removeChild(nodeList[idx]);
	}

	// ... to make way for the new information
	theTable = document.createElement("TABLE");
	theTHead = document.createElement("THEAD");
	theTFoot = document.createElement("TFOOT");
	theTBody = document.createElement("TBODY");
	theTable.className = "propList";
	theTable.setAttribute("className", "propList");

	for ( idx = 0; idx < numMatches; idx++ ) {
		propPtr = searchResults[idx].ptrToProp;
		currentRow = document.createElement('TR');
		currentRow.id = idx;
		addEvent(currentRow, 'click', clickProperty);

		// Have to do this in both 'classic' and 'DOM' ways because of incomplete
		// browser standards implementation
		currentRow.className = "propDetails";
		currentRow.setAttribute("className", "propDetails");

		cell1 = document.createElement('TD');
		cell1.align = "center";

		// Create the image thumbnail if there is a 't' in the imgStr.
		// If not, the default "no image" thumbnail is shown
		thumbRE = '^'+thumbImg+'$';
		thumb = document.createElement('IMG');
		thumbArray = properties[searchResults[idx].ptrToProp].imageStr;
		fileNameSuffix = properties[searchResults[idx].ptrToProp].branchID + "_" + properties[searchResults[idx].ptrToProp].propID + ".jpg";

		if (searchArrayRE(thumbArray, thumbRE) >= 0) {
			thumbSrc = propsDir + thumbImg + fileNameSuffix;
		} else {
			thumbSrc = noThumb;
		}

		thumb.src = thumbSrc;

		cell1.appendChild(thumb);
		cell1.appendChild(document.createElement('BR'));
		cell1.appendChild(document.createTextNode("£" + properties[searchResults[idx].ptrToProp].price));

		cell2 = document.createElement('TD');

		if ( properties[searchResults[idx].ptrToProp].availCode.toUpperCase() == "SOLD STC" ) {
			cell2.className = "soldSTC";
			cell2.setAttribute("className", "soldSTC");
			soldText = document.createElement('DIV');
			soldText.appendChild(document.createTextNode('Sold STC'));
			soldText.className = "soldIndicator";
			soldText.setAttribute("className", "soldIndicator");
			cell1.appendChild(soldText);
			cell1.className = "soldSTC";
			cell1.setAttribute("className", "soldSTC");
		}

		if (properties[propPtr].numBedrooms > 0) {
			cell2.appendChild(document.createTextNode(properties[propPtr].numBedrooms + " bed "));
		}

		if (properties[propPtr].connected != '') {
			cell2.appendChild(document.createTextNode(properties[propPtr].connected + " "));
		}

		if (properties[propPtr].propType != '') {
			cell2.appendChild(document.createTextNode(properties[propPtr].propType + ", "));
		}

		cell2.appendChild(document.createTextNode(properties[propPtr].propStreet + ", " + properties[propPtr].propArea));

		// Create the 'excerpt'
		excerptWords = 24;
		fullDesc = properties[propPtr].descript.split(" ");
		excerptDesc = fullDesc.slice(0,excerptWords).join(" ");

		cell2.appendChild(document.createElement('BR'));
		descDiv = cell2.appendChild(document.createElement('DIV'));
		descDiv.appendChild(document.createTextNode(excerptDesc));
		descSpan = cell2.appendChild(document.createElement('SPAN'));
		descSpan.appendChild(document.createTextNode(" ... click for details"));
		descDiv.appendChild(descSpan);
 		cell2.appendChild(descDiv);

		currentRow.appendChild(cell1);
		currentRow.appendChild(cell2);
		theTBody.appendChild(currentRow);
	}

	theTable.appendChild(theTHead);
	theTable.appendChild(theTFoot);
	theTable.appendChild(theTBody);
	divPropList.appendChild(theTable);
	divNumMatches.childNodes.item(0).data = "Number of properties: " + numMatches;
	divNumMatches.className = 'boldText';

	// Set the currently-shown property to the first in the list
	changeProperty('0');
}

// Display a different property. The 'direction' arg either specifies which property to
// display (an absolute positive value) or by how many properties to go forward
// (+value) or backwards (-value)
function changeProperty(direction) {
	// Locate each of the elements in the HTML below that require their values changing
	divPropNum = document.getElementById("propNum");
	divConnected = document.getElementById("Connected");
	divPropType= document.getElementById("PropType");
	divPostcode = document.getElementById("Postcode");
	divPrice = document.getElementById("Price");
	divShortAddr = document.getElementById("ShortAddress");
	divBedrooms = document.getElementById("Bedrooms");
	divDescript = document.getElementById("Descript");
	divRemarks = document.getElementById("Remarks");
	divSoldStamp = document.getElementById("SoldStamp");
	divImage = document.getElementById("ImageHere");
	divThumbs = document.getElementById("ImageThumbs");

	// Find which property to display next, based on the 'direction' value passed in
	if (direction.indexOf('-') > -1) {
		nextPropNum = ((currSearchIdx * 1) - (direction.substring(1)*1));
	} else if (direction.indexOf('+') > -1) {
		nextPropNum = ((currSearchIdx * 1) + (direction.substring(1)*1));
	} else {
		nextPropNum = direction * 1;
	}

	if ( nextPropNum < 0 ) nextPropNum = 0;
	if ( nextPropNum > ((numMatches-1)*1)) nextPropNum = (numMatches - 1)*1;
	currSearchIdx = nextPropNum;
	currPropIdx = searchResults[currSearchIdx].ptrToProp;

	// Change the values in the DIV elements to reflect the new property info
	divConnected.childNodes.item(0).data = properties[currPropIdx].connected;
	divConnected.className = 'propDetailsNL';
	divPropType.childNodes.item(0).data = properties[currPropIdx].propType;
	divPropType.className = 'propDetailsNL';
	divPropNumObj = divPropNum.childNodes.item(0);
	divPropNumObj.data = ((nextPropNum*1)+1) + " of " + numMatches;
	divPropNum.className = 'boldText';
	divPrice.childNodes.item(0).data = "£" + properties[currPropIdx].price;
	divPrice.className = 'propDetailsNL';
	divBedrooms.childNodes.item(0).data = "Bedrooms: " + properties[currPropIdx].numBedrooms;
	divBedrooms.className = 'propDetailsNL';
	divShortAddr.childNodes.item(0).data = properties[currPropIdx].propStreet + ", " + properties[currPropIdx].propArea + ", " + properties[currPropIdx].postcode;
	divShortAddr.className = 'propDetailsNL';
	divDescript.childNodes.item(0).data = properties[currPropIdx].descript;
	divDescript.className = 'normText';

	// Clear the old Remarks field
	while (divRemarks.hasChildNodes()) {
		divRemarks.removeChild(divRemarks.lastChild);
	}
	regex = /(W\.C|N\.B|[A-Z1-9\s*\/-]+:)+(.*?)/g;
	remarkItems = properties[currPropIdx].remarks.split(regex);

	firstWritten = false;
	for (var idx = 0; idx < remarkItems.length; idx++) {
		if (remarkItems[idx] == "") {
			continue;
		}
		divRemarks.appendChild(document.createTextNode(remarkItems[idx]));
		if (!firstWritten) {
			firstWritten = true;
		} else {
			divRemarks.appendChild(document.createElement('br'));
			divRemarks.appendChild(document.createElement('br'));
			firstWritten = false;
		}
	}
	divRemarks.className = 'normText';

	imgArray = properties[currPropIdx].imageStr;
	startImg = searchArrayRE(imgArray, '^'+defaultImg+'$');
	if (startImg >= 0) {
		currImgPrefix = defaultImg;
	} else {
		currImgPrefix = properties[currPropIdx].imageStr[0];
		startImg = 0;
	}

	// Now display all the images for the property, one under the other,
	// starting with currImgPrefix and cycling through the imageStr array,
	// looping back to the start if we reach the end (we ignore the
	// thumbnail entry 't').
	// First clear out the old nodes
	nodeList = divThumbs.childNodes;
	numNodes = nodeList.length;

	for ( idx = numNodes-1; idx >= 0; idx-- ) {
		removeEvent(divThumbs.childNodes.item(idx), 'click', enlargeThumb);
		divThumbs.removeChild(nodeList[idx]);
	}

	for (img = startImg; img < imgArray.length; img++) {
		if (imgArray[img] == thumbImg) continue;
		if (imgArray[img] == '') {
			addImgToDiv(divThumbs, '#');
			setDivImg(divImage, divSoldStamp, getImgURL('#'));
		} else {
			addImgToDiv(divThumbs, imgArray[img]);
			if (img == startImg) {
				setDivImg(divImage, divSoldStamp, getImgURL(imgArray[img]));
			}
		}
	}

	for (img = 0; img < startImg; img++) {
		if (imgArray[img] == thumbImg) continue;
		addImgToDiv(divThumbs, imgArray[img]);
	}

	// Add the Sold Stamp if the property is sold
	if ( properties[currPropIdx].availCode.toUpperCase() == "SOLD STC" ) {
		divSoldStamp.style.display = 'block';
	} else {
		divSoldStamp.style.display = 'none';
	}
}

// Common helper function to generate an image filename based on the
// passed prefix (letter or number) and the global vars holding the remainder
// of the filename.
function getImgURL(imgPrefix) {
	imgSuffix = properties[currPropIdx].branchID + "_" + properties[currPropIdx].propID + ".jpg"

	if (isNum(imgPrefix)) {
		imgSep = "-";
	} else {
		imgSep = "";
	}
	if (imgPrefix == "#") {
		imgURL = noPic;
	} else {
		imgURL = propsDir + imgPrefix + imgSep + imgSuffix;
	}
	return imgURL;
}

// Show a slightly bigger representation of the thumbnail.
// Note that if the div holding the Sold stamp is "above" the image it does not bubble/transmit
// the click "through" to the image below. For this reason, the soldStamp must have an event
// added to it as well. Clicking the sold stamp would, however, mean that a larger version of
// the stamp would be displayed! For this reason, the longdesc property of both img tags are
// used to indicate the filename of the image to be displayed.
function setDivImg(divID,divSoldID,imgSrc) {
	removeEvent(divID.childNodes.item(0), 'click', viewLargeImage);
	removeEvent(divSoldID.childNodes.item(0), 'click', viewLargeImage);
	divID.childNodes.item(0).src = imgSrc;
	divID.childNodes.item(0).longdesc = imgSrc;
	addEvent(divID.childNodes.item(0), 'click', viewLargeImage);
	addEvent(divSoldID.childNodes.item(0), 'click', viewLargeImage);
	divSoldID.childNodes.item(0).longdesc = imgSrc;
}

// Add the given image to the passed DIV element.
// Annoyingly, the img prefix can be a number as well as a letter and the format
// changed depending on its type.
// If it's a letter, it is just prepended to the rest of the filename.
// If it's a number, it is separated from the rest of the filename by
// a hyphen.
function addImgToDiv(divID,imgPrefix) {
	domImg = document.createElement('IMG');
	domImg.src = getImgURL(imgPrefix);
	domImg.title = "Click to view larger image";
	addEvent(domImg, 'click', enlargeThumb);
	divID.appendChild(domImg);
}

function enlargeThumb(event) {
	evHandle = getEventHandle(event);
	objName = evHandle;
	imgURL = objName.src;
	divSoldStamp = document.getElementById("SoldStamp");
	divImage = document.getElementById("ImageHere");
	setDivImg(divImage, divSoldStamp, imgURL);
}

// Display the passed image in a separate popup window, resizing it appropriately
// (well, as appropriately as possible given that different browsers return the width
// and height differently depending on scrollbars etc, hence the magic number kludges)
function viewLargeImage(event) {
	evHandle = getEventHandle(event);
	objName = evHandle;

	// Reading the 'src' property would mean (if the 'Sold' stamp was clicked) that it
	// would get displayed instead of the house image. The longdesc property is therefore
	// used because it has been forced (for both img and sold stamp) to point to the
	// real image filename to be displayed
	imgURL = objName.longdesc;

	popWin = popUp('', 'console', 320, 460);
	if (!popWin) { return true; }
	var docHandle = popWin.document;
	docHandle.write("<html>");
	docHandle.write("<head>");
	docHandle.write("<head><title>Larger Property View</title>");
	docHandle.write("<script type='text/javascript'>");
	docHandle.write("function fitPic() {\n");
	docHandle.write("iWidth = document.images[0].width;\n");
	docHandle.write("iHeight = document.images[0].height;\n");
	docHandle.write("window.resizeTo(iWidth, iHeight);\n");
	docHandle.write("docHTML = window.document.documentElement;\n");
	docHandle.write("docBODY = window.document.body;\n");
	docHandle.write("if (window.innerWidth) { rWidth = window.innerWidth; rHeight = window.innerHeight; }\n");
	docHandle.write("else if (docHTML && docHTML.clientWidth) { rWidth= docHTML.clientWidth; rHeight = docHTML.clientHeight; }\n");
	docHandle.write("else if (docBODY && docBODY.clientWidth) { rWidth= docBODY.clientWidth; rHeight = docBODY.clientHeight; }\n");
	docHandle.write("if (window.opera && !document.childNodes) { rWidth += 16; }\n");
	docHandle.write("window.resizeTo(iWidth + (iWidth - rWidth), iHeight + (iHeight - rHeight));\n");
	docHandle.write("}\n");
	docHandle.write("</script>");
	docHandle.write("</head>");
	docHandle.write("<body onClick='self.close();' onLoad='fitPic()';>");
	docHandle.write("<div style='position:absolute;left:0px;top:0px;'><img src='"+imgURL+"' alt='Click the image to close this window' onload='fitPic();'></div>");
	docHandle.write("</body></html>");
	docHandle.close();
}

// Generic popup handler. Normally called thus:
//<a href="popcontents.htm" onclick="popUp(this.href,'console',400,200);return false;" target="_blank">LinkName</a>
function popUp(popURL, popType, popHeight, popWidth) {
	if (popWin != null && !popWin.closed) {
		popWin.resizeTo(popWidth, popHeight);
	}

	var strOptions="";
	if (popType=="console") {
		strOptions="scrollbars=no,resizable,height=" + popHeight + ",width=" + popWidth;
	}

	if (popType=="fixed") {
		strOptions="status,height=" + popHeight + ",width=" + popWidth;
	}

	if (popType=="elastic") {
		strOptions="toolbar,menubar,scrollbars," + "resizable,location,height=" + popHeight+",width="+popWidth;
	}

	popWin = window.open(popURL, 'popWin', strOptions);
	setFocus(popWin);
	return popWin;
}

// Do all sorts of jiggery pokery to get IE/Safari to understand which containing
// element was clicked. In DOM-compliant browsers, 'this.id' is sufficient
function clickProperty(event) {
	evHandle = getEventHandle(event);
	objName = evHandle;

	// Manually 'bubble up' until the element containing the ID (table row in this case) is found
	while (objName.id == '') {
		objName = objName.parentNode;
	}

	changeProperty(getObjHandle(objName.id).id);
	setDisplayType('ByOne');
	window.location.hash = '#ListTop';
}

// Shows the div given by the ID in the displayType variable
function showPane() {
	hidePanes();
	divID = document.getElementById(displayType);
	divID.style.display = 'block';
}

// Hides all three <div> pane objects
function hidePanes() {
	divID = document.getElementById("AList");
	divID.style.display = 'none';
	divID = document.getElementById("ByOne");
	divID.style.display = 'none';
	divID = document.getElementById("NotFound");
	divID.style.display = 'none';
}

// Iterates over the radio buttons to find which is checked and returns its name
function getDisplayType() {
	var retType = displayType;
	var inputs = document.getElementsByTagName("input");

	for ( idx = 0; idx < inputs.length; idx++ ) {
		if (inputs[idx].type == "radio" && inputs[idx].checked == true) {
			retType = inputs[idx].value;
		}
	}
	return retType;
}

// Iterates over the radio buttons and checks the one with the name given in the arg 'toType'
function setDisplayType(toType) {
	var inputs = document.getElementsByTagName("input");

	for ( idx = 0; idx < inputs.length; idx++ ) {
		if (inputs[idx].type == "radio" && inputs[idx].value == toType) {
			inputs[idx].checked = true;
		}
	}

	// Set the global variable (boo, hiss) to indicate which type is currently selected
	if (numMatches > 0 ) {
		displayType = toType;
	} else {
		displayType = "NotFound";
	}
	showPane();
}

// Called when the price dropdown is changed - a little cheap logic to make sure that the
// low price never exceeds the high price
function adjustPrice(priceObj) {
	loPriceObj = document.getElementById("ddLoPrice");
	hiPriceObj = document.getElementById("ddHiPrice");
	if (priceObj.id.match('Lo') && (loPriceObj.selectedIndex >= hiPriceObj.selectedIndex )) {
		hiPriceObj.selectedIndex = loPriceObj.selectedIndex;
	} else if (priceObj.id.match('Hi') && (hiPriceObj.selectedIndex <= loPriceObj.selectedIndex )) {
		loPriceObj.selectedIndex = hiPriceObj.selectedIndex;
	}
	setSearchResults();
}

// Adds an event listener to the specified object. Courtesy of John Resig.
function addEvent( obj, type, fn ) {
	if ( obj.attachEvent ) {
		obj['e'+type+fn] = fn;
		obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
		obj.attachEvent( "on"+type, obj[type+fn] );
	} else {
		obj.addEventListener( type, fn, false );
	}
}

function removeEvent( obj, type, fn ) {
	if ( obj.detachEvent ) {
		if (obj[type+fn]) {
			obj.detachEvent( "on"+type, obj[type+fn] );
			obj[type+fn] = null;
		}
	} else {
		obj.removeEventListener( type, fn, false );
	}
}



// Gets a handle on the given event - guess why we need this? No, surely
// it couldn't be IE shunning the standards?
function getEventHandle(event) {
	if (event.srcElement) {
		// IE
		return event.srcElement;
	} else if (event.currentTarget) {
		// W3C DOM
		return event.currentTarget;
	}
}

// Simply uses the DOM to get a handle on the given object. Differentiates between being
// given a string and another object
function getObjHandle(objID) {
	if (document.getElementById) {
		if (typeof objID == "string") {
			return document.getElementById(objID);
		} else {
			return objID;
		}
	} else {
		return false;
	}
}

// Sort the searchResults by price
function comparePrices(obj1, obj2) {
	return obj1.price - obj2.price;
    //return obj1.price < obj2.price ? -1 : obj1.price == obj2.price ? 0 : 1;
}

function setFocus(objID) {
	objID.focus();
}

// Looks in the passed array for an element matching the
// regular expression reg. Returns the position of the
// first element found. -1 returned if not found
function searchArrayRE(arr, reg) {
	foundPos = -1;
	var regex = new RegExp(reg,"gi")

	for ( var idx = 0; idx < arr.length; idx++ ) {
		if ( regex.test(arr[idx]) ) {
			foundPos = idx;
			break;
		}
	}
	return foundPos;
}


// Function suite to test values for their type
var numb = '0123456789';
var lwr = 'abcdefghijklmnopqrstuvwxyz';
var upr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

function isValid(parm,val) {
  if (parm == "") return true;
  for (i=0; i<parm.length; i++) {
    if (val.indexOf(parm.charAt(i),0) == -1) return false;
  }
  return true;
}

function isNum(parm) {return isValid(parm,numb);}
function isLower(parm) {return isValid(parm,lwr);}
function isUpper(parm) {return isValid(parm,upr);}
function isAlpha(parm) {return isValid(parm,lwr+upr);}
function isAlphanum(parm) {return isValid(parm,lwr+upr+numb);}
